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