Merge branches 'clk-stm32mp1', 'clk-samsung', 'clk-uniphier-mpeg', 'clk-stratix10...
[sfrench/cifs-2.6.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                 kfree(fcheck);
197                 return ret;
198         }
199
200         entry->fs_fcheck = fcheck;
201         return 0;
202 }
203
204 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
205 {
206         if (!osb->osb_fc_ent.fs_fcheck)
207                 return;
208
209         kobject_del(&osb->osb_fc_ent.fs_kobj);
210         kobject_put(&osb->osb_fc_ent.fs_kobj);
211         wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
212         ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
213 }
214
215 static int
216 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
217                               unsigned int count);
218 static int
219 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
220                            unsigned int len)
221 {
222         int ret;
223
224         if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
225                 return -EINVAL;
226
227         spin_lock(&ent->fs_fcheck->fc_lock);
228         if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
229                 mlog(ML_NOTICE,
230                 "Cannot set online file check maximum entry number "
231                 "to %u due to too many pending entries(%u)\n",
232                 len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
233                 ret = -EBUSY;
234         } else {
235                 if (len < ent->fs_fcheck->fc_size)
236                         BUG_ON(!ocfs2_filecheck_erase_entries(ent,
237                                 ent->fs_fcheck->fc_size - len));
238
239                 ent->fs_fcheck->fc_max = len;
240                 ret = 0;
241         }
242         spin_unlock(&ent->fs_fcheck->fc_lock);
243
244         return ret;
245 }
246
247 #define OCFS2_FILECHECK_ARGS_LEN        24
248 static int
249 ocfs2_filecheck_args_get_long(const char *buf, size_t count,
250                               unsigned long *val)
251 {
252         char buffer[OCFS2_FILECHECK_ARGS_LEN];
253
254         memcpy(buffer, buf, count);
255         buffer[count] = '\0';
256
257         if (kstrtoul(buffer, 0, val))
258                 return 1;
259
260         return 0;
261 }
262
263 static int
264 ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
265 {
266         if (!strncmp(name, "fix", 4))
267                 *type = OCFS2_FILECHECK_TYPE_FIX;
268         else if (!strncmp(name, "check", 6))
269                 *type = OCFS2_FILECHECK_TYPE_CHK;
270         else if (!strncmp(name, "set", 4))
271                 *type = OCFS2_FILECHECK_TYPE_SET;
272         else
273                 return 1;
274
275         return 0;
276 }
277
278 static int
279 ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
280                            struct ocfs2_filecheck_args *args)
281 {
282         unsigned long val = 0;
283         unsigned int type;
284
285         /* too short/long args length */
286         if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
287                 return 1;
288
289         if (ocfs2_filecheck_type_parse(name, &type))
290                 return 1;
291         if (ocfs2_filecheck_args_get_long(buf, count, &val))
292                 return 1;
293
294         if (val <= 0)
295                 return 1;
296
297         args->fa_type = type;
298         if (type == OCFS2_FILECHECK_TYPE_SET)
299                 args->fa_len = (unsigned int)val;
300         else
301                 args->fa_ino = val;
302
303         return 0;
304 }
305
306 static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
307                                     struct kobj_attribute *attr,
308                                     char *buf)
309 {
310
311         ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
312         unsigned int type;
313         struct ocfs2_filecheck_entry *p;
314         struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
315                                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
316
317         if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
318                 return -EINVAL;
319
320         if (type == OCFS2_FILECHECK_TYPE_SET) {
321                 spin_lock(&ent->fs_fcheck->fc_lock);
322                 total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
323                 spin_unlock(&ent->fs_fcheck->fc_lock);
324                 goto exit;
325         }
326
327         ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
328         total += ret;
329         remain -= ret;
330         spin_lock(&ent->fs_fcheck->fc_lock);
331         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
332                 if (p->fe_type != type)
333                         continue;
334
335                 ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
336                                p->fe_ino, p->fe_done,
337                                ocfs2_filecheck_error(p->fe_status));
338                 if (ret < 0) {
339                         total = ret;
340                         break;
341                 }
342                 if (ret == remain) {
343                         /* snprintf() didn't fit */
344                         total = -E2BIG;
345                         break;
346                 }
347                 total += ret;
348                 remain -= ret;
349         }
350         spin_unlock(&ent->fs_fcheck->fc_lock);
351
352 exit:
353         return total;
354 }
355
356 static inline int
357 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
358                                 unsigned long ino)
359 {
360         struct ocfs2_filecheck_entry *p;
361
362         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
363                 if (!p->fe_done) {
364                         if (p->fe_ino == ino)
365                                 return 1;
366                 }
367         }
368
369         return 0;
370 }
371
372 static inline int
373 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
374 {
375         struct ocfs2_filecheck_entry *p;
376
377         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
378                 if (p->fe_done) {
379                         list_del(&p->fe_list);
380                         kfree(p);
381                         ent->fs_fcheck->fc_size--;
382                         ent->fs_fcheck->fc_done--;
383                         return 1;
384                 }
385         }
386
387         return 0;
388 }
389
390 static int
391 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
392                               unsigned int count)
393 {
394         unsigned int i = 0;
395         unsigned int ret = 0;
396
397         while (i++ < count) {
398                 if (ocfs2_filecheck_erase_entry(ent))
399                         ret++;
400                 else
401                         break;
402         }
403
404         return (ret == count ? 1 : 0);
405 }
406
407 static void
408 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
409                            struct ocfs2_filecheck_entry *entry)
410 {
411         spin_lock(&ent->fs_fcheck->fc_lock);
412         entry->fe_done = 1;
413         ent->fs_fcheck->fc_done++;
414         spin_unlock(&ent->fs_fcheck->fc_lock);
415 }
416
417 static unsigned int
418 ocfs2_filecheck_handle(struct ocfs2_super *osb,
419                        unsigned long ino, unsigned int flags)
420 {
421         unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
422         struct inode *inode = NULL;
423         int rc;
424
425         inode = ocfs2_iget(osb, ino, flags, 0);
426         if (IS_ERR(inode)) {
427                 rc = (int)(-(long)inode);
428                 if (rc >= OCFS2_FILECHECK_ERR_START &&
429                     rc < OCFS2_FILECHECK_ERR_END)
430                         ret = rc;
431                 else
432                         ret = OCFS2_FILECHECK_ERR_FAILED;
433         } else
434                 iput(inode);
435
436         return ret;
437 }
438
439 static void
440 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
441                              struct ocfs2_filecheck_entry *entry)
442 {
443         struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
444                                                 osb_fc_ent);
445
446         if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
447                 entry->fe_status = ocfs2_filecheck_handle(osb,
448                                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
449         else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
450                 entry->fe_status = ocfs2_filecheck_handle(osb,
451                                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
452         else
453                 entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
454
455         ocfs2_filecheck_done_entry(ent, entry);
456 }
457
458 static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
459                                      struct kobj_attribute *attr,
460                                      const char *buf, size_t count)
461 {
462         ssize_t ret = 0;
463         struct ocfs2_filecheck_args args;
464         struct ocfs2_filecheck_entry *entry;
465         struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
466                                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
467
468         if (count == 0)
469                 return count;
470
471         if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
472                 return -EINVAL;
473
474         if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
475                 ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
476                 goto exit;
477         }
478
479         entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
480         if (!entry) {
481                 ret = -ENOMEM;
482                 goto exit;
483         }
484
485         spin_lock(&ent->fs_fcheck->fc_lock);
486         if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
487                 ret = -EEXIST;
488                 kfree(entry);
489         } else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
490                 (ent->fs_fcheck->fc_done == 0)) {
491                 mlog(ML_NOTICE,
492                 "Cannot do more file check "
493                 "since file check queue(%u) is full now\n",
494                 ent->fs_fcheck->fc_max);
495                 ret = -EAGAIN;
496                 kfree(entry);
497         } else {
498                 if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
499                     (ent->fs_fcheck->fc_done > 0)) {
500                         /* Delete the oldest entry which was done,
501                          * make sure the entry size in list does
502                          * not exceed maximum value
503                          */
504                         BUG_ON(!ocfs2_filecheck_erase_entry(ent));
505                 }
506
507                 entry->fe_ino = args.fa_ino;
508                 entry->fe_type = args.fa_type;
509                 entry->fe_done = 0;
510                 entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
511                 list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
512                 ent->fs_fcheck->fc_size++;
513         }
514         spin_unlock(&ent->fs_fcheck->fc_lock);
515
516         if (!ret)
517                 ocfs2_filecheck_handle_entry(ent, entry);
518
519 exit:
520         return (!ret ? count : ret);
521 }