diff options
author | Gang He <ghe@suse.com> | 2016-03-22 17:24:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-22 18:36:02 -0400 |
commit | a860f6eb4c6a8bb0ca6860d9472f424bad9af9cf (patch) | |
tree | 645318e29103186075c10b2321014a8ae8082343 | |
parent | 9dde5e4f3383c3459a67ab63786ca58d645d6b5e (diff) |
ocfs2: sysfile interfaces for online file check
Implement online file check sysfile interfaces, e.g. how to create the
related sysfile according to device name, how to display/handle file
check request from the sysfile.
Signed-off-by: Gang He <ghe@suse.com>
Reviewed-by: Mark Fasheh <mfasheh@suse.de>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Joseph Qi <joseph.qi@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/ocfs2/Makefile | 3 | ||||
-rw-r--r-- | fs/ocfs2/filecheck.c | 606 | ||||
-rw-r--r-- | fs/ocfs2/filecheck.h | 49 | ||||
-rw-r--r-- | fs/ocfs2/inode.h | 3 |
4 files changed, 660 insertions, 1 deletions
diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile index ce210d4951a1..e27e6527912b 100644 --- a/fs/ocfs2/Makefile +++ b/fs/ocfs2/Makefile | |||
@@ -41,7 +41,8 @@ ocfs2-objs := \ | |||
41 | quota_local.o \ | 41 | quota_local.o \ |
42 | quota_global.o \ | 42 | quota_global.o \ |
43 | xattr.o \ | 43 | xattr.o \ |
44 | acl.o | 44 | acl.o \ |
45 | filecheck.o | ||
45 | 46 | ||
46 | ocfs2_stackglue-objs := stackglue.o | 47 | ocfs2_stackglue-objs := stackglue.o |
47 | ocfs2_stack_o2cb-objs := stack_o2cb.o | 48 | ocfs2_stack_o2cb-objs := stack_o2cb.o |
diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c new file mode 100644 index 000000000000..2cabbcf2f28e --- /dev/null +++ b/fs/ocfs2/filecheck.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8; -*- | ||
2 | * vim: noexpandtab sw=8 ts=8 sts=0: | ||
3 | * | ||
4 | * filecheck.c | ||
5 | * | ||
6 | * Code which implements online file check. | ||
7 | * | ||
8 | * Copyright (C) 2016 SuSE. All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public | ||
12 | * License as published by the Free Software Foundation, version 2. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #include <linux/list.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/kmod.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/kobject.h> | ||
27 | #include <linux/sysfs.h> | ||
28 | #include <linux/sysctl.h> | ||
29 | #include <cluster/masklog.h> | ||
30 | |||
31 | #include "ocfs2.h" | ||
32 | #include "ocfs2_fs.h" | ||
33 | #include "stackglue.h" | ||
34 | #include "inode.h" | ||
35 | |||
36 | #include "filecheck.h" | ||
37 | |||
38 | |||
39 | /* File check error strings, | ||
40 | * must correspond with error number in header file. | ||
41 | */ | ||
42 | static const char * const ocfs2_filecheck_errs[] = { | ||
43 | "SUCCESS", | ||
44 | "FAILED", | ||
45 | "INPROGRESS", | ||
46 | "READONLY", | ||
47 | "INJBD", | ||
48 | "INVALIDINO", | ||
49 | "BLOCKECC", | ||
50 | "BLOCKNO", | ||
51 | "VALIDFLAG", | ||
52 | "GENERATION", | ||
53 | "UNSUPPORTED" | ||
54 | }; | ||
55 | |||
56 | static DEFINE_SPINLOCK(ocfs2_filecheck_sysfs_lock); | ||
57 | static LIST_HEAD(ocfs2_filecheck_sysfs_list); | ||
58 | |||
59 | struct ocfs2_filecheck { | ||
60 | struct list_head fc_head; /* File check entry list head */ | ||
61 | spinlock_t fc_lock; | ||
62 | unsigned int fc_max; /* Maximum number of entry in list */ | ||
63 | unsigned int fc_size; /* Current entry count in list */ | ||
64 | unsigned int fc_done; /* Finished entry count in list */ | ||
65 | }; | ||
66 | |||
67 | struct ocfs2_filecheck_sysfs_entry { /* sysfs entry per mounting */ | ||
68 | struct list_head fs_list; | ||
69 | atomic_t fs_count; | ||
70 | struct super_block *fs_sb; | ||
71 | struct kset *fs_devicekset; | ||
72 | struct kset *fs_fcheckkset; | ||
73 | struct ocfs2_filecheck *fs_fcheck; | ||
74 | }; | ||
75 | |||
76 | #define OCFS2_FILECHECK_MAXSIZE 100 | ||
77 | #define OCFS2_FILECHECK_MINSIZE 10 | ||
78 | |||
79 | /* File check operation type */ | ||
80 | enum { | ||
81 | OCFS2_FILECHECK_TYPE_CHK = 0, /* Check a file(inode) */ | ||
82 | OCFS2_FILECHECK_TYPE_FIX, /* Fix a file(inode) */ | ||
83 | OCFS2_FILECHECK_TYPE_SET = 100 /* Set entry list maximum size */ | ||
84 | }; | ||
85 | |||
86 | struct ocfs2_filecheck_entry { | ||
87 | struct list_head fe_list; | ||
88 | unsigned long fe_ino; | ||
89 | unsigned int fe_type; | ||
90 | unsigned int fe_done:1; | ||
91 | unsigned int fe_status:31; | ||
92 | }; | ||
93 | |||
94 | struct ocfs2_filecheck_args { | ||
95 | unsigned int fa_type; | ||
96 | union { | ||
97 | unsigned long fa_ino; | ||
98 | unsigned int fa_len; | ||
99 | }; | ||
100 | }; | ||
101 | |||
102 | static const char * | ||
103 | ocfs2_filecheck_error(int errno) | ||
104 | { | ||
105 | if (!errno) | ||
106 | return ocfs2_filecheck_errs[errno]; | ||
107 | |||
108 | BUG_ON(errno < OCFS2_FILECHECK_ERR_START || | ||
109 | errno > OCFS2_FILECHECK_ERR_END); | ||
110 | return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1]; | ||
111 | } | ||
112 | |||
113 | static ssize_t ocfs2_filecheck_show(struct kobject *kobj, | ||
114 | struct kobj_attribute *attr, | ||
115 | char *buf); | ||
116 | static ssize_t ocfs2_filecheck_store(struct kobject *kobj, | ||
117 | struct kobj_attribute *attr, | ||
118 | const char *buf, size_t count); | ||
119 | static struct kobj_attribute ocfs2_attr_filecheck_chk = | ||
120 | __ATTR(check, S_IRUSR | S_IWUSR, | ||
121 | ocfs2_filecheck_show, | ||
122 | ocfs2_filecheck_store); | ||
123 | static struct kobj_attribute ocfs2_attr_filecheck_fix = | ||
124 | __ATTR(fix, S_IRUSR | S_IWUSR, | ||
125 | ocfs2_filecheck_show, | ||
126 | ocfs2_filecheck_store); | ||
127 | static struct kobj_attribute ocfs2_attr_filecheck_set = | ||
128 | __ATTR(set, S_IRUSR | S_IWUSR, | ||
129 | ocfs2_filecheck_show, | ||
130 | ocfs2_filecheck_store); | ||
131 | |||
132 | static int ocfs2_filecheck_sysfs_wait(atomic_t *p) | ||
133 | { | ||
134 | schedule(); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry) | ||
140 | { | ||
141 | struct ocfs2_filecheck_entry *p; | ||
142 | |||
143 | if (!atomic_dec_and_test(&entry->fs_count)) | ||
144 | wait_on_atomic_t(&entry->fs_count, ocfs2_filecheck_sysfs_wait, | ||
145 | TASK_UNINTERRUPTIBLE); | ||
146 | |||
147 | spin_lock(&entry->fs_fcheck->fc_lock); | ||
148 | while (!list_empty(&entry->fs_fcheck->fc_head)) { | ||
149 | p = list_first_entry(&entry->fs_fcheck->fc_head, | ||
150 | struct ocfs2_filecheck_entry, fe_list); | ||
151 | list_del(&p->fe_list); | ||
152 | BUG_ON(!p->fe_done); /* To free a undone file check entry */ | ||
153 | kfree(p); | ||
154 | } | ||
155 | spin_unlock(&entry->fs_fcheck->fc_lock); | ||
156 | |||
157 | kset_unregister(entry->fs_fcheckkset); | ||
158 | kset_unregister(entry->fs_devicekset); | ||
159 | kfree(entry->fs_fcheck); | ||
160 | kfree(entry); | ||
161 | } | ||
162 | |||
163 | static void | ||
164 | ocfs2_filecheck_sysfs_add(struct ocfs2_filecheck_sysfs_entry *entry) | ||
165 | { | ||
166 | spin_lock(&ocfs2_filecheck_sysfs_lock); | ||
167 | list_add_tail(&entry->fs_list, &ocfs2_filecheck_sysfs_list); | ||
168 | spin_unlock(&ocfs2_filecheck_sysfs_lock); | ||
169 | } | ||
170 | |||
171 | static int ocfs2_filecheck_sysfs_del(const char *devname) | ||
172 | { | ||
173 | struct ocfs2_filecheck_sysfs_entry *p; | ||
174 | |||
175 | spin_lock(&ocfs2_filecheck_sysfs_lock); | ||
176 | list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) { | ||
177 | if (!strcmp(p->fs_sb->s_id, devname)) { | ||
178 | list_del(&p->fs_list); | ||
179 | spin_unlock(&ocfs2_filecheck_sysfs_lock); | ||
180 | ocfs2_filecheck_sysfs_free(p); | ||
181 | return 0; | ||
182 | } | ||
183 | } | ||
184 | spin_unlock(&ocfs2_filecheck_sysfs_lock); | ||
185 | return 1; | ||
186 | } | ||
187 | |||
188 | static void | ||
189 | ocfs2_filecheck_sysfs_put(struct ocfs2_filecheck_sysfs_entry *entry) | ||
190 | { | ||
191 | if (atomic_dec_and_test(&entry->fs_count)) | ||
192 | wake_up_atomic_t(&entry->fs_count); | ||
193 | } | ||
194 | |||
195 | static struct ocfs2_filecheck_sysfs_entry * | ||
196 | ocfs2_filecheck_sysfs_get(const char *devname) | ||
197 | { | ||
198 | struct ocfs2_filecheck_sysfs_entry *p = NULL; | ||
199 | |||
200 | spin_lock(&ocfs2_filecheck_sysfs_lock); | ||
201 | list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) { | ||
202 | if (!strcmp(p->fs_sb->s_id, devname)) { | ||
203 | atomic_inc(&p->fs_count); | ||
204 | spin_unlock(&ocfs2_filecheck_sysfs_lock); | ||
205 | return p; | ||
206 | } | ||
207 | } | ||
208 | spin_unlock(&ocfs2_filecheck_sysfs_lock); | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
212 | int ocfs2_filecheck_create_sysfs(struct super_block *sb) | ||
213 | { | ||
214 | int ret = 0; | ||
215 | struct kset *device_kset = NULL; | ||
216 | struct kset *fcheck_kset = NULL; | ||
217 | struct ocfs2_filecheck *fcheck = NULL; | ||
218 | struct ocfs2_filecheck_sysfs_entry *entry = NULL; | ||
219 | struct attribute **attrs = NULL; | ||
220 | struct attribute_group attrgp; | ||
221 | |||
222 | if (!ocfs2_kset) | ||
223 | return -ENOMEM; | ||
224 | |||
225 | attrs = kmalloc(sizeof(struct attribute *) * 4, GFP_NOFS); | ||
226 | if (!attrs) { | ||
227 | ret = -ENOMEM; | ||
228 | goto error; | ||
229 | } else { | ||
230 | attrs[0] = &ocfs2_attr_filecheck_chk.attr; | ||
231 | attrs[1] = &ocfs2_attr_filecheck_fix.attr; | ||
232 | attrs[2] = &ocfs2_attr_filecheck_set.attr; | ||
233 | attrs[3] = NULL; | ||
234 | memset(&attrgp, 0, sizeof(attrgp)); | ||
235 | attrgp.attrs = attrs; | ||
236 | } | ||
237 | |||
238 | fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS); | ||
239 | if (!fcheck) { | ||
240 | ret = -ENOMEM; | ||
241 | goto error; | ||
242 | } else { | ||
243 | INIT_LIST_HEAD(&fcheck->fc_head); | ||
244 | spin_lock_init(&fcheck->fc_lock); | ||
245 | fcheck->fc_max = OCFS2_FILECHECK_MINSIZE; | ||
246 | fcheck->fc_size = 0; | ||
247 | fcheck->fc_done = 0; | ||
248 | } | ||
249 | |||
250 | if (strlen(sb->s_id) <= 0) { | ||
251 | mlog(ML_ERROR, | ||
252 | "Cannot get device basename when create filecheck sysfs\n"); | ||
253 | ret = -ENODEV; | ||
254 | goto error; | ||
255 | } | ||
256 | |||
257 | device_kset = kset_create_and_add(sb->s_id, NULL, &ocfs2_kset->kobj); | ||
258 | if (!device_kset) { | ||
259 | ret = -ENOMEM; | ||
260 | goto error; | ||
261 | } | ||
262 | |||
263 | fcheck_kset = kset_create_and_add("filecheck", NULL, | ||
264 | &device_kset->kobj); | ||
265 | if (!fcheck_kset) { | ||
266 | ret = -ENOMEM; | ||
267 | goto error; | ||
268 | } | ||
269 | |||
270 | ret = sysfs_create_group(&fcheck_kset->kobj, &attrgp); | ||
271 | if (ret) | ||
272 | goto error; | ||
273 | |||
274 | entry = kmalloc(sizeof(struct ocfs2_filecheck_sysfs_entry), GFP_NOFS); | ||
275 | if (!entry) { | ||
276 | ret = -ENOMEM; | ||
277 | goto error; | ||
278 | } else { | ||
279 | atomic_set(&entry->fs_count, 1); | ||
280 | entry->fs_sb = sb; | ||
281 | entry->fs_devicekset = device_kset; | ||
282 | entry->fs_fcheckkset = fcheck_kset; | ||
283 | entry->fs_fcheck = fcheck; | ||
284 | ocfs2_filecheck_sysfs_add(entry); | ||
285 | } | ||
286 | |||
287 | kfree(attrs); | ||
288 | return 0; | ||
289 | |||
290 | error: | ||
291 | kfree(attrs); | ||
292 | kfree(entry); | ||
293 | kfree(fcheck); | ||
294 | kset_unregister(fcheck_kset); | ||
295 | kset_unregister(device_kset); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | int ocfs2_filecheck_remove_sysfs(struct super_block *sb) | ||
300 | { | ||
301 | return ocfs2_filecheck_sysfs_del(sb->s_id); | ||
302 | } | ||
303 | |||
304 | static int | ||
305 | ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, | ||
306 | unsigned int count); | ||
307 | static int | ||
308 | ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent, | ||
309 | unsigned int len) | ||
310 | { | ||
311 | int ret; | ||
312 | |||
313 | if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE)) | ||
314 | return -EINVAL; | ||
315 | |||
316 | spin_lock(&ent->fs_fcheck->fc_lock); | ||
317 | if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) { | ||
318 | mlog(ML_ERROR, | ||
319 | "Cannot set online file check maximum entry number " | ||
320 | "to %u due to too many pending entries(%u)\n", | ||
321 | len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done); | ||
322 | ret = -EBUSY; | ||
323 | } else { | ||
324 | if (len < ent->fs_fcheck->fc_size) | ||
325 | BUG_ON(!ocfs2_filecheck_erase_entries(ent, | ||
326 | ent->fs_fcheck->fc_size - len)); | ||
327 | |||
328 | ent->fs_fcheck->fc_max = len; | ||
329 | ret = 0; | ||
330 | } | ||
331 | spin_unlock(&ent->fs_fcheck->fc_lock); | ||
332 | |||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | #define OCFS2_FILECHECK_ARGS_LEN 24 | ||
337 | static int | ||
338 | ocfs2_filecheck_args_get_long(const char *buf, size_t count, | ||
339 | unsigned long *val) | ||
340 | { | ||
341 | char buffer[OCFS2_FILECHECK_ARGS_LEN]; | ||
342 | |||
343 | memcpy(buffer, buf, count); | ||
344 | buffer[count] = '\0'; | ||
345 | |||
346 | if (kstrtoul(buffer, 0, val)) | ||
347 | return 1; | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static int | ||
353 | ocfs2_filecheck_type_parse(const char *name, unsigned int *type) | ||
354 | { | ||
355 | if (!strncmp(name, "fix", 4)) | ||
356 | *type = OCFS2_FILECHECK_TYPE_FIX; | ||
357 | else if (!strncmp(name, "check", 6)) | ||
358 | *type = OCFS2_FILECHECK_TYPE_CHK; | ||
359 | else if (!strncmp(name, "set", 4)) | ||
360 | *type = OCFS2_FILECHECK_TYPE_SET; | ||
361 | else | ||
362 | return 1; | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static int | ||
368 | ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count, | ||
369 | struct ocfs2_filecheck_args *args) | ||
370 | { | ||
371 | unsigned long val = 0; | ||
372 | unsigned int type; | ||
373 | |||
374 | /* too short/long args length */ | ||
375 | if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN)) | ||
376 | return 1; | ||
377 | |||
378 | if (ocfs2_filecheck_type_parse(name, &type)) | ||
379 | return 1; | ||
380 | if (ocfs2_filecheck_args_get_long(buf, count, &val)) | ||
381 | return 1; | ||
382 | |||
383 | if (val <= 0) | ||
384 | return 1; | ||
385 | |||
386 | args->fa_type = type; | ||
387 | if (type == OCFS2_FILECHECK_TYPE_SET) | ||
388 | args->fa_len = (unsigned int)val; | ||
389 | else | ||
390 | args->fa_ino = val; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static ssize_t ocfs2_filecheck_show(struct kobject *kobj, | ||
396 | struct kobj_attribute *attr, | ||
397 | char *buf) | ||
398 | { | ||
399 | |||
400 | ssize_t ret = 0, total = 0, remain = PAGE_SIZE; | ||
401 | unsigned int type; | ||
402 | struct ocfs2_filecheck_entry *p; | ||
403 | struct ocfs2_filecheck_sysfs_entry *ent; | ||
404 | |||
405 | if (ocfs2_filecheck_type_parse(attr->attr.name, &type)) | ||
406 | return -EINVAL; | ||
407 | |||
408 | ent = ocfs2_filecheck_sysfs_get(kobj->parent->name); | ||
409 | if (!ent) { | ||
410 | mlog(ML_ERROR, | ||
411 | "Cannot get the corresponding entry via device basename %s\n", | ||
412 | kobj->name); | ||
413 | return -ENODEV; | ||
414 | } | ||
415 | |||
416 | if (type == OCFS2_FILECHECK_TYPE_SET) { | ||
417 | spin_lock(&ent->fs_fcheck->fc_lock); | ||
418 | total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max); | ||
419 | spin_unlock(&ent->fs_fcheck->fc_lock); | ||
420 | goto exit; | ||
421 | } | ||
422 | |||
423 | ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n"); | ||
424 | total += ret; | ||
425 | remain -= ret; | ||
426 | spin_lock(&ent->fs_fcheck->fc_lock); | ||
427 | list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { | ||
428 | if (p->fe_type != type) | ||
429 | continue; | ||
430 | |||
431 | ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n", | ||
432 | p->fe_ino, p->fe_done, | ||
433 | ocfs2_filecheck_error(p->fe_status)); | ||
434 | if (ret < 0) { | ||
435 | total = ret; | ||
436 | break; | ||
437 | } | ||
438 | if (ret == remain) { | ||
439 | /* snprintf() didn't fit */ | ||
440 | total = -E2BIG; | ||
441 | break; | ||
442 | } | ||
443 | total += ret; | ||
444 | remain -= ret; | ||
445 | } | ||
446 | spin_unlock(&ent->fs_fcheck->fc_lock); | ||
447 | |||
448 | exit: | ||
449 | ocfs2_filecheck_sysfs_put(ent); | ||
450 | return total; | ||
451 | } | ||
452 | |||
453 | static int | ||
454 | ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent) | ||
455 | { | ||
456 | struct ocfs2_filecheck_entry *p; | ||
457 | |||
458 | list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { | ||
459 | if (p->fe_done) { | ||
460 | list_del(&p->fe_list); | ||
461 | kfree(p); | ||
462 | ent->fs_fcheck->fc_size--; | ||
463 | ent->fs_fcheck->fc_done--; | ||
464 | return 1; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | static int | ||
472 | ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, | ||
473 | unsigned int count) | ||
474 | { | ||
475 | unsigned int i = 0; | ||
476 | unsigned int ret = 0; | ||
477 | |||
478 | while (i++ < count) { | ||
479 | if (ocfs2_filecheck_erase_entry(ent)) | ||
480 | ret++; | ||
481 | else | ||
482 | break; | ||
483 | } | ||
484 | |||
485 | return (ret == count ? 1 : 0); | ||
486 | } | ||
487 | |||
488 | static void | ||
489 | ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent, | ||
490 | struct ocfs2_filecheck_entry *entry) | ||
491 | { | ||
492 | entry->fe_done = 1; | ||
493 | spin_lock(&ent->fs_fcheck->fc_lock); | ||
494 | ent->fs_fcheck->fc_done++; | ||
495 | spin_unlock(&ent->fs_fcheck->fc_lock); | ||
496 | } | ||
497 | |||
498 | static unsigned int | ||
499 | ocfs2_filecheck_handle(struct super_block *sb, | ||
500 | unsigned long ino, unsigned int flags) | ||
501 | { | ||
502 | unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS; | ||
503 | struct inode *inode = NULL; | ||
504 | int rc; | ||
505 | |||
506 | inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0); | ||
507 | if (IS_ERR(inode)) { | ||
508 | rc = (int)(-(long)inode); | ||
509 | if (rc >= OCFS2_FILECHECK_ERR_START && | ||
510 | rc < OCFS2_FILECHECK_ERR_END) | ||
511 | ret = rc; | ||
512 | else | ||
513 | ret = OCFS2_FILECHECK_ERR_FAILED; | ||
514 | } else | ||
515 | iput(inode); | ||
516 | |||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | static void | ||
521 | ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent, | ||
522 | struct ocfs2_filecheck_entry *entry) | ||
523 | { | ||
524 | if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK) | ||
525 | entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, | ||
526 | entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK); | ||
527 | else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX) | ||
528 | entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, | ||
529 | entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX); | ||
530 | else | ||
531 | entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED; | ||
532 | |||
533 | ocfs2_filecheck_done_entry(ent, entry); | ||
534 | } | ||
535 | |||
536 | static ssize_t ocfs2_filecheck_store(struct kobject *kobj, | ||
537 | struct kobj_attribute *attr, | ||
538 | const char *buf, size_t count) | ||
539 | { | ||
540 | struct ocfs2_filecheck_args args; | ||
541 | struct ocfs2_filecheck_entry *entry; | ||
542 | struct ocfs2_filecheck_sysfs_entry *ent; | ||
543 | ssize_t ret = 0; | ||
544 | |||
545 | if (count == 0) | ||
546 | return count; | ||
547 | |||
548 | if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) { | ||
549 | mlog(ML_ERROR, "Invalid arguments for online file check\n"); | ||
550 | return -EINVAL; | ||
551 | } | ||
552 | |||
553 | ent = ocfs2_filecheck_sysfs_get(kobj->parent->name); | ||
554 | if (!ent) { | ||
555 | mlog(ML_ERROR, | ||
556 | "Cannot get the corresponding entry via device basename %s\n", | ||
557 | kobj->parent->name); | ||
558 | return -ENODEV; | ||
559 | } | ||
560 | |||
561 | if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { | ||
562 | ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); | ||
563 | goto exit; | ||
564 | } | ||
565 | |||
566 | entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); | ||
567 | if (!entry) { | ||
568 | ret = -ENOMEM; | ||
569 | goto exit; | ||
570 | } | ||
571 | |||
572 | spin_lock(&ent->fs_fcheck->fc_lock); | ||
573 | if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && | ||
574 | (ent->fs_fcheck->fc_done == 0)) { | ||
575 | mlog(ML_ERROR, | ||
576 | "Cannot do more file check " | ||
577 | "since file check queue(%u) is full now\n", | ||
578 | ent->fs_fcheck->fc_max); | ||
579 | ret = -EBUSY; | ||
580 | kfree(entry); | ||
581 | } else { | ||
582 | if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && | ||
583 | (ent->fs_fcheck->fc_done > 0)) { | ||
584 | /* Delete the oldest entry which was done, | ||
585 | * make sure the entry size in list does | ||
586 | * not exceed maximum value | ||
587 | */ | ||
588 | BUG_ON(!ocfs2_filecheck_erase_entry(ent)); | ||
589 | } | ||
590 | |||
591 | entry->fe_ino = args.fa_ino; | ||
592 | entry->fe_type = args.fa_type; | ||
593 | entry->fe_done = 0; | ||
594 | entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS; | ||
595 | list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head); | ||
596 | ent->fs_fcheck->fc_size++; | ||
597 | } | ||
598 | spin_unlock(&ent->fs_fcheck->fc_lock); | ||
599 | |||
600 | if (!ret) | ||
601 | ocfs2_filecheck_handle_entry(ent, entry); | ||
602 | |||
603 | exit: | ||
604 | ocfs2_filecheck_sysfs_put(ent); | ||
605 | return (!ret ? count : ret); | ||
606 | } | ||
diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h new file mode 100644 index 000000000000..e5cd002a2c09 --- /dev/null +++ b/fs/ocfs2/filecheck.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8; -*- | ||
2 | * vim: noexpandtab sw=8 ts=8 sts=0: | ||
3 | * | ||
4 | * filecheck.h | ||
5 | * | ||
6 | * Online file check. | ||
7 | * | ||
8 | * Copyright (C) 2016 SuSE. All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public | ||
12 | * License as published by the Free Software Foundation, version 2. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | |||
21 | #ifndef FILECHECK_H | ||
22 | #define FILECHECK_H | ||
23 | |||
24 | #include <linux/types.h> | ||
25 | #include <linux/list.h> | ||
26 | |||
27 | |||
28 | /* File check errno */ | ||
29 | enum { | ||
30 | OCFS2_FILECHECK_ERR_SUCCESS = 0, /* Success */ | ||
31 | OCFS2_FILECHECK_ERR_FAILED = 1000, /* Other failure */ | ||
32 | OCFS2_FILECHECK_ERR_INPROGRESS, /* In progress */ | ||
33 | OCFS2_FILECHECK_ERR_READONLY, /* Read only */ | ||
34 | OCFS2_FILECHECK_ERR_INJBD, /* Buffer in jbd */ | ||
35 | OCFS2_FILECHECK_ERR_INVALIDINO, /* Invalid ino */ | ||
36 | OCFS2_FILECHECK_ERR_BLOCKECC, /* Block ecc */ | ||
37 | OCFS2_FILECHECK_ERR_BLOCKNO, /* Block number */ | ||
38 | OCFS2_FILECHECK_ERR_VALIDFLAG, /* Inode valid flag */ | ||
39 | OCFS2_FILECHECK_ERR_GENERATION, /* Inode generation */ | ||
40 | OCFS2_FILECHECK_ERR_UNSUPPORTED /* Unsupported */ | ||
41 | }; | ||
42 | |||
43 | #define OCFS2_FILECHECK_ERR_START OCFS2_FILECHECK_ERR_FAILED | ||
44 | #define OCFS2_FILECHECK_ERR_END OCFS2_FILECHECK_ERR_UNSUPPORTED | ||
45 | |||
46 | int ocfs2_filecheck_create_sysfs(struct super_block *sb); | ||
47 | int ocfs2_filecheck_remove_sysfs(struct super_block *sb); | ||
48 | |||
49 | #endif /* FILECHECK_H */ | ||
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index aac8b86f312e..01635e016b3e 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h | |||
@@ -139,6 +139,9 @@ int ocfs2_drop_inode(struct inode *inode); | |||
139 | /* Flags for ocfs2_iget() */ | 139 | /* Flags for ocfs2_iget() */ |
140 | #define OCFS2_FI_FLAG_SYSFILE 0x1 | 140 | #define OCFS2_FI_FLAG_SYSFILE 0x1 |
141 | #define OCFS2_FI_FLAG_ORPHAN_RECOVERY 0x2 | 141 | #define OCFS2_FI_FLAG_ORPHAN_RECOVERY 0x2 |
142 | #define OCFS2_FI_FLAG_FILECHECK_CHK 0x4 | ||
143 | #define OCFS2_FI_FLAG_FILECHECK_FIX 0x8 | ||
144 | |||
142 | struct inode *ocfs2_ilookup(struct super_block *sb, u64 feoff); | 145 | struct inode *ocfs2_ilookup(struct super_block *sb, u64 feoff); |
143 | struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, unsigned flags, | 146 | struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, unsigned flags, |
144 | int sysfile_type); | 147 | int sysfile_type); |