68f6c35dce3678b515bbfb0eb8adaeda80b03c2c
[muen/linux.git] / fs / ocfs2 / filecheck.c
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_entry {
60         struct list_head fe_list;
61         unsigned long fe_ino;
62         unsigned int fe_type;
63         unsigned int fe_done:1;
64         unsigned int fe_status:31;
65 };
66
67 struct ocfs2_filecheck_args {
68         unsigned int fa_type;
69         union {
70                 unsigned long fa_ino;
71                 unsigned int fa_len;
72         };
73 };
74
75 static const char *
76 ocfs2_filecheck_error(int errno)
77 {
78         if (!errno)
79                 return ocfs2_filecheck_errs[errno];
80
81         BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
82                errno > OCFS2_FILECHECK_ERR_END);
83         return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
84 }
85
86 static ssize_t ocfs2_filecheck_show(struct kobject *kobj,
87                                     struct kobj_attribute *attr,
88                                     char *buf);
89 static ssize_t ocfs2_filecheck_store(struct kobject *kobj,
90                                      struct kobj_attribute *attr,
91                                      const char *buf, size_t count);
92 static struct kobj_attribute ocfs2_attr_filecheck_chk =
93                                         __ATTR(check, S_IRUSR | S_IWUSR,
94                                         ocfs2_filecheck_show,
95                                         ocfs2_filecheck_store);
96 static struct kobj_attribute ocfs2_attr_filecheck_fix =
97                                         __ATTR(fix, S_IRUSR | S_IWUSR,
98                                         ocfs2_filecheck_show,
99                                         ocfs2_filecheck_store);
100 static struct kobj_attribute ocfs2_attr_filecheck_set =
101                                         __ATTR(set, S_IRUSR | S_IWUSR,
102                                         ocfs2_filecheck_show,
103                                         ocfs2_filecheck_store);
104
105 static void
106 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
107 {
108         struct ocfs2_filecheck_entry *p;
109
110         if (!atomic_dec_and_test(&entry->fs_count)) {
111                 wait_var_event(&entry->fs_count,
112                                !atomic_read(&entry->fs_count));
113         }
114
115         spin_lock(&entry->fs_fcheck->fc_lock);
116         while (!list_empty(&entry->fs_fcheck->fc_head)) {
117                 p = list_first_entry(&entry->fs_fcheck->fc_head,
118                                      struct ocfs2_filecheck_entry, fe_list);
119                 list_del(&p->fe_list);
120                 BUG_ON(!p->fe_done); /* To free a undone file check entry */
121                 kfree(p);
122         }
123         spin_unlock(&entry->fs_fcheck->fc_lock);
124
125         kset_unregister(entry->fs_fcheckkset);
126         kset_unregister(entry->fs_devicekset);
127         kfree(entry->fs_fcheck);
128         kfree(entry);
129 }
130
131 static void
132 ocfs2_filecheck_sysfs_add(struct ocfs2_filecheck_sysfs_entry *entry)
133 {
134         spin_lock(&ocfs2_filecheck_sysfs_lock);
135         list_add_tail(&entry->fs_list, &ocfs2_filecheck_sysfs_list);
136         spin_unlock(&ocfs2_filecheck_sysfs_lock);
137 }
138
139 static int ocfs2_filecheck_sysfs_del(const char *devname)
140 {
141         struct ocfs2_filecheck_sysfs_entry *p;
142
143         spin_lock(&ocfs2_filecheck_sysfs_lock);
144         list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) {
145                 if (!strcmp(p->fs_sb->s_id, devname)) {
146                         list_del(&p->fs_list);
147                         spin_unlock(&ocfs2_filecheck_sysfs_lock);
148                         ocfs2_filecheck_sysfs_free(p);
149                         return 0;
150                 }
151         }
152         spin_unlock(&ocfs2_filecheck_sysfs_lock);
153         return 1;
154 }
155
156 static void
157 ocfs2_filecheck_sysfs_put(struct ocfs2_filecheck_sysfs_entry *entry)
158 {
159         if (atomic_dec_and_test(&entry->fs_count))
160                 wake_up_var(&entry->fs_count);
161 }
162
163 static struct ocfs2_filecheck_sysfs_entry *
164 ocfs2_filecheck_sysfs_get(const char *devname)
165 {
166         struct ocfs2_filecheck_sysfs_entry *p = NULL;
167
168         spin_lock(&ocfs2_filecheck_sysfs_lock);
169         list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) {
170                 if (!strcmp(p->fs_sb->s_id, devname)) {
171                         atomic_inc(&p->fs_count);
172                         spin_unlock(&ocfs2_filecheck_sysfs_lock);
173                         return p;
174                 }
175         }
176         spin_unlock(&ocfs2_filecheck_sysfs_lock);
177         return NULL;
178 }
179
180 int ocfs2_filecheck_create_sysfs(struct super_block *sb)
181 {
182         int ret = 0;
183         struct kset *device_kset = NULL;
184         struct kset *fcheck_kset = NULL;
185         struct ocfs2_filecheck *fcheck = NULL;
186         struct ocfs2_filecheck_sysfs_entry *entry = NULL;
187         struct attribute **attrs = NULL;
188         struct attribute_group attrgp;
189
190         if (!ocfs2_kset)
191                 return -ENOMEM;
192
193         attrs = kmalloc(sizeof(struct attribute *) * 4, GFP_NOFS);
194         if (!attrs) {
195                 ret = -ENOMEM;
196                 goto error;
197         } else {
198                 attrs[0] = &ocfs2_attr_filecheck_chk.attr;
199                 attrs[1] = &ocfs2_attr_filecheck_fix.attr;
200                 attrs[2] = &ocfs2_attr_filecheck_set.attr;
201                 attrs[3] = NULL;
202                 memset(&attrgp, 0, sizeof(attrgp));
203                 attrgp.attrs = attrs;
204         }
205
206         fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
207         if (!fcheck) {
208                 ret = -ENOMEM;
209                 goto error;
210         } else {
211                 INIT_LIST_HEAD(&fcheck->fc_head);
212                 spin_lock_init(&fcheck->fc_lock);
213                 fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
214                 fcheck->fc_size = 0;
215                 fcheck->fc_done = 0;
216         }
217
218         if (strlen(sb->s_id) <= 0) {
219                 mlog(ML_ERROR,
220                 "Cannot get device basename when create filecheck sysfs\n");
221                 ret = -ENODEV;
222                 goto error;
223         }
224
225         device_kset = kset_create_and_add(sb->s_id, NULL, &ocfs2_kset->kobj);
226         if (!device_kset) {
227                 ret = -ENOMEM;
228                 goto error;
229         }
230
231         fcheck_kset = kset_create_and_add("filecheck", NULL,
232                                           &device_kset->kobj);
233         if (!fcheck_kset) {
234                 ret = -ENOMEM;
235                 goto error;
236         }
237
238         ret = sysfs_create_group(&fcheck_kset->kobj, &attrgp);
239         if (ret)
240                 goto error;
241
242         entry = kmalloc(sizeof(struct ocfs2_filecheck_sysfs_entry), GFP_NOFS);
243         if (!entry) {
244                 ret = -ENOMEM;
245                 goto error;
246         } else {
247                 atomic_set(&entry->fs_count, 1);
248                 entry->fs_sb = sb;
249                 entry->fs_devicekset = device_kset;
250                 entry->fs_fcheckkset = fcheck_kset;
251                 entry->fs_fcheck = fcheck;
252                 ocfs2_filecheck_sysfs_add(entry);
253         }
254
255         kfree(attrs);
256         return 0;
257
258 error:
259         kfree(attrs);
260         kfree(entry);
261         kfree(fcheck);
262         kset_unregister(fcheck_kset);
263         kset_unregister(device_kset);
264         return ret;
265 }
266
267 int ocfs2_filecheck_remove_sysfs(struct super_block *sb)
268 {
269         return ocfs2_filecheck_sysfs_del(sb->s_id);
270 }
271
272 static int
273 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
274                               unsigned int count);
275 static int
276 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
277                            unsigned int len)
278 {
279         int ret;
280
281         if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
282                 return -EINVAL;
283
284         spin_lock(&ent->fs_fcheck->fc_lock);
285         if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
286                 mlog(ML_ERROR,
287                 "Cannot set online file check maximum entry number "
288                 "to %u due to too many pending entries(%u)\n",
289                 len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
290                 ret = -EBUSY;
291         } else {
292                 if (len < ent->fs_fcheck->fc_size)
293                         BUG_ON(!ocfs2_filecheck_erase_entries(ent,
294                                 ent->fs_fcheck->fc_size - len));
295
296                 ent->fs_fcheck->fc_max = len;
297                 ret = 0;
298         }
299         spin_unlock(&ent->fs_fcheck->fc_lock);
300
301         return ret;
302 }
303
304 #define OCFS2_FILECHECK_ARGS_LEN        24
305 static int
306 ocfs2_filecheck_args_get_long(const char *buf, size_t count,
307                               unsigned long *val)
308 {
309         char buffer[OCFS2_FILECHECK_ARGS_LEN];
310
311         memcpy(buffer, buf, count);
312         buffer[count] = '\0';
313
314         if (kstrtoul(buffer, 0, val))
315                 return 1;
316
317         return 0;
318 }
319
320 static int
321 ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
322 {
323         if (!strncmp(name, "fix", 4))
324                 *type = OCFS2_FILECHECK_TYPE_FIX;
325         else if (!strncmp(name, "check", 6))
326                 *type = OCFS2_FILECHECK_TYPE_CHK;
327         else if (!strncmp(name, "set", 4))
328                 *type = OCFS2_FILECHECK_TYPE_SET;
329         else
330                 return 1;
331
332         return 0;
333 }
334
335 static int
336 ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
337                            struct ocfs2_filecheck_args *args)
338 {
339         unsigned long val = 0;
340         unsigned int type;
341
342         /* too short/long args length */
343         if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
344                 return 1;
345
346         if (ocfs2_filecheck_type_parse(name, &type))
347                 return 1;
348         if (ocfs2_filecheck_args_get_long(buf, count, &val))
349                 return 1;
350
351         if (val <= 0)
352                 return 1;
353
354         args->fa_type = type;
355         if (type == OCFS2_FILECHECK_TYPE_SET)
356                 args->fa_len = (unsigned int)val;
357         else
358                 args->fa_ino = val;
359
360         return 0;
361 }
362
363 static ssize_t ocfs2_filecheck_show(struct kobject *kobj,
364                                     struct kobj_attribute *attr,
365                                     char *buf)
366 {
367
368         ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
369         unsigned int type;
370         struct ocfs2_filecheck_entry *p;
371         struct ocfs2_filecheck_sysfs_entry *ent;
372
373         if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
374                 return -EINVAL;
375
376         ent = ocfs2_filecheck_sysfs_get(kobj->parent->name);
377         if (!ent) {
378                 mlog(ML_ERROR,
379                 "Cannot get the corresponding entry via device basename %s\n",
380                 kobj->name);
381                 return -ENODEV;
382         }
383
384         if (type == OCFS2_FILECHECK_TYPE_SET) {
385                 spin_lock(&ent->fs_fcheck->fc_lock);
386                 total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
387                 spin_unlock(&ent->fs_fcheck->fc_lock);
388                 goto exit;
389         }
390
391         ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
392         total += ret;
393         remain -= ret;
394         spin_lock(&ent->fs_fcheck->fc_lock);
395         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
396                 if (p->fe_type != type)
397                         continue;
398
399                 ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
400                                p->fe_ino, p->fe_done,
401                                ocfs2_filecheck_error(p->fe_status));
402                 if (ret < 0) {
403                         total = ret;
404                         break;
405                 }
406                 if (ret == remain) {
407                         /* snprintf() didn't fit */
408                         total = -E2BIG;
409                         break;
410                 }
411                 total += ret;
412                 remain -= ret;
413         }
414         spin_unlock(&ent->fs_fcheck->fc_lock);
415
416 exit:
417         ocfs2_filecheck_sysfs_put(ent);
418         return total;
419 }
420
421 static int
422 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
423 {
424         struct ocfs2_filecheck_entry *p;
425
426         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
427                 if (p->fe_done) {
428                         list_del(&p->fe_list);
429                         kfree(p);
430                         ent->fs_fcheck->fc_size--;
431                         ent->fs_fcheck->fc_done--;
432                         return 1;
433                 }
434         }
435
436         return 0;
437 }
438
439 static int
440 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
441                               unsigned int count)
442 {
443         unsigned int i = 0;
444         unsigned int ret = 0;
445
446         while (i++ < count) {
447                 if (ocfs2_filecheck_erase_entry(ent))
448                         ret++;
449                 else
450                         break;
451         }
452
453         return (ret == count ? 1 : 0);
454 }
455
456 static void
457 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
458                            struct ocfs2_filecheck_entry *entry)
459 {
460         entry->fe_done = 1;
461         spin_lock(&ent->fs_fcheck->fc_lock);
462         ent->fs_fcheck->fc_done++;
463         spin_unlock(&ent->fs_fcheck->fc_lock);
464 }
465
466 static unsigned int
467 ocfs2_filecheck_handle(struct super_block *sb,
468                        unsigned long ino, unsigned int flags)
469 {
470         unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
471         struct inode *inode = NULL;
472         int rc;
473
474         inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
475         if (IS_ERR(inode)) {
476                 rc = (int)(-(long)inode);
477                 if (rc >= OCFS2_FILECHECK_ERR_START &&
478                     rc < OCFS2_FILECHECK_ERR_END)
479                         ret = rc;
480                 else
481                         ret = OCFS2_FILECHECK_ERR_FAILED;
482         } else
483                 iput(inode);
484
485         return ret;
486 }
487
488 static void
489 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
490                              struct ocfs2_filecheck_entry *entry)
491 {
492         if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
493                 entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
494                                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
495         else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
496                 entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
497                                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
498         else
499                 entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
500
501         ocfs2_filecheck_done_entry(ent, entry);
502 }
503
504 static ssize_t ocfs2_filecheck_store(struct kobject *kobj,
505                                      struct kobj_attribute *attr,
506                                      const char *buf, size_t count)
507 {
508         struct ocfs2_filecheck_args args;
509         struct ocfs2_filecheck_entry *entry;
510         struct ocfs2_filecheck_sysfs_entry *ent;
511         ssize_t ret = 0;
512
513         if (count == 0)
514                 return count;
515
516         if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) {
517                 mlog(ML_ERROR, "Invalid arguments for online file check\n");
518                 return -EINVAL;
519         }
520
521         ent = ocfs2_filecheck_sysfs_get(kobj->parent->name);
522         if (!ent) {
523                 mlog(ML_ERROR,
524                 "Cannot get the corresponding entry via device basename %s\n",
525                 kobj->parent->name);
526                 return -ENODEV;
527         }
528
529         if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
530                 ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
531                 goto exit;
532         }
533
534         entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
535         if (!entry) {
536                 ret = -ENOMEM;
537                 goto exit;
538         }
539
540         spin_lock(&ent->fs_fcheck->fc_lock);
541         if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
542             (ent->fs_fcheck->fc_done == 0)) {
543                 mlog(ML_ERROR,
544                 "Cannot do more file check "
545                 "since file check queue(%u) is full now\n",
546                 ent->fs_fcheck->fc_max);
547                 ret = -EBUSY;
548                 kfree(entry);
549         } else {
550                 if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
551                     (ent->fs_fcheck->fc_done > 0)) {
552                         /* Delete the oldest entry which was done,
553                          * make sure the entry size in list does
554                          * not exceed maximum value
555                          */
556                         BUG_ON(!ocfs2_filecheck_erase_entry(ent));
557                 }
558
559                 entry->fe_ino = args.fa_ino;
560                 entry->fe_type = args.fa_type;
561                 entry->fe_done = 0;
562                 entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
563                 list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
564                 ent->fs_fcheck->fc_size++;
565         }
566         spin_unlock(&ent->fs_fcheck->fc_lock);
567
568         if (!ret)
569                 ocfs2_filecheck_handle_entry(ent, entry);
570
571 exit:
572         ocfs2_filecheck_sysfs_put(ent);
573         return (!ret ? count : ret);
574 }