xref: /openbmc/linux/fs/ocfs2/filecheck.c (revision 3805e6a1)
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 }
607