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