Merge tag 'drm-msm-fixes-2018-04-25' of git://people.freedesktop.org/~robclark/linux...
[linux-2.6-block.git] / fs / ocfs2 / filecheck.c
CommitLineData
a860f6eb
GH
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 */
42static 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
a860f6eb
GH
56struct 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
64struct ocfs2_filecheck_args {
65 unsigned int fa_type;
66 union {
67 unsigned long fa_ino;
68 unsigned int fa_len;
69 };
70};
71
72static const char *
73ocfs2_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
5f483c4a
GH
83static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
84 struct kobj_attribute *attr,
85 char *buf);
86static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
87 struct kobj_attribute *attr,
88 const char *buf, size_t count);
89static struct kobj_attribute ocfs2_filecheck_attr_chk =
a860f6eb 90 __ATTR(check, S_IRUSR | S_IWUSR,
5f483c4a
GH
91 ocfs2_filecheck_attr_show,
92 ocfs2_filecheck_attr_store);
93static struct kobj_attribute ocfs2_filecheck_attr_fix =
a860f6eb 94 __ATTR(fix, S_IRUSR | S_IWUSR,
5f483c4a
GH
95 ocfs2_filecheck_attr_show,
96 ocfs2_filecheck_attr_store);
97static struct kobj_attribute ocfs2_filecheck_attr_set =
a860f6eb 98 __ATTR(set, S_IRUSR | S_IWUSR,
5f483c4a
GH
99 ocfs2_filecheck_attr_show,
100 ocfs2_filecheck_attr_store);
101static 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
108static 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
116static ssize_t
117ocfs2_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
130static ssize_t
131ocfs2_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
145static const struct sysfs_ops ocfs2_filecheck_ops = {
146 .show = ocfs2_filecheck_show,
147 .store = ocfs2_filecheck_store,
148};
149
150static struct kobj_type ocfs2_ktype_filecheck = {
151 .default_attrs = ocfs2_filecheck_attrs,
152 .sysfs_ops = &ocfs2_filecheck_ops,
153 .release = ocfs2_filecheck_release,
154};
a860f6eb 155
a860f6eb
GH
156static void
157ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
158{
159 struct ocfs2_filecheck_entry *p;
160
a860f6eb
GH
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
a860f6eb 171 kfree(entry->fs_fcheck);
5f483c4a 172 entry->fs_fcheck = NULL;
a860f6eb
GH
173}
174
5f483c4a 175int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
a860f6eb 176{
5f483c4a
GH
177 int ret;
178 struct ocfs2_filecheck *fcheck;
179 struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent;
a860f6eb
GH
180
181 fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
5f483c4a
GH
182 if (!fcheck)
183 return -ENOMEM;
a860f6eb 184
5f483c4a
GH
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;
a860f6eb
GH
198 }
199
5f483c4a 200 entry->fs_fcheck = fcheck;
a860f6eb 201 return 0;
a860f6eb
GH
202}
203
5f483c4a 204void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
a860f6eb 205{
5f483c4a
GH
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);
a860f6eb
GH
213}
214
215static int
216ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
217 unsigned int count);
218static int
219ocfs2_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)) {
8fc2cb4b 229 mlog(ML_NOTICE,
a860f6eb
GH
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
248static int
249ocfs2_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
263static int
264ocfs2_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
278static int
279ocfs2_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
5f483c4a 306static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
a860f6eb
GH
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;
5f483c4a
GH
314 struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
315 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
a860f6eb
GH
316
317 if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
318 return -EINVAL;
319
a860f6eb
GH
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
352exit:
a860f6eb
GH
353 return total;
354}
355
39ec3774
GH
356static inline int
357ocfs2_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
5f483c4a 372static inline int
a860f6eb
GH
373ocfs2_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
390static int
391ocfs2_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
407static void
408ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
409 struct ocfs2_filecheck_entry *entry)
410{
a860f6eb 411 spin_lock(&ent->fs_fcheck->fc_lock);
8fc2cb4b 412 entry->fe_done = 1;
a860f6eb
GH
413 ent->fs_fcheck->fc_done++;
414 spin_unlock(&ent->fs_fcheck->fc_lock);
415}
416
417static unsigned int
5f483c4a 418ocfs2_filecheck_handle(struct ocfs2_super *osb,
a860f6eb
GH
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
5f483c4a 425 inode = ocfs2_iget(osb, ino, flags, 0);
a860f6eb
GH
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
439static void
440ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
441 struct ocfs2_filecheck_entry *entry)
442{
5f483c4a
GH
443 struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
444 osb_fc_ent);
445
a860f6eb 446 if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
5f483c4a 447 entry->fe_status = ocfs2_filecheck_handle(osb,
a860f6eb
GH
448 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
449 else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
5f483c4a 450 entry->fe_status = ocfs2_filecheck_handle(osb,
a860f6eb
GH
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
5f483c4a 458static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
a860f6eb
GH
459 struct kobj_attribute *attr,
460 const char *buf, size_t count)
461{
5f483c4a 462 ssize_t ret = 0;
a860f6eb
GH
463 struct ocfs2_filecheck_args args;
464 struct ocfs2_filecheck_entry *entry;
5f483c4a
GH
465 struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
466 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
a860f6eb
GH
467
468 if (count == 0)
469 return count;
470
5f483c4a 471 if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
a860f6eb 472 return -EINVAL;
a860f6eb
GH
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);
39ec3774
GH
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) &&
5f483c4a 490 (ent->fs_fcheck->fc_done == 0)) {
8fc2cb4b 491 mlog(ML_NOTICE,
a860f6eb
GH
492 "Cannot do more file check "
493 "since file check queue(%u) is full now\n",
494 ent->fs_fcheck->fc_max);
8fc2cb4b 495 ret = -EAGAIN;
a860f6eb
GH
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
519exit:
a860f6eb
GH
520 return (!ret ? count : ret);
521}