Merge tag 'wireless-drivers-for-davem-2016-04-13' of git://git.kernel.org/pub/scm...
[cascardo/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 {
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 }