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