xref: /openbmc/linux/fs/ocfs2/filecheck.c (revision 39ec3774e3e8ebd5e45b0a5d071531d36f67c158)
1a860f6ebSGang He /* -*- mode: c; c-basic-offset: 8; -*-
2a860f6ebSGang He  * vim: noexpandtab sw=8 ts=8 sts=0:
3a860f6ebSGang He  *
4a860f6ebSGang He  * filecheck.c
5a860f6ebSGang He  *
6a860f6ebSGang He  * Code which implements online file check.
7a860f6ebSGang He  *
8a860f6ebSGang He  * Copyright (C) 2016 SuSE.  All rights reserved.
9a860f6ebSGang He  *
10a860f6ebSGang He  * This program is free software; you can redistribute it and/or
11a860f6ebSGang He  * modify it under the terms of the GNU General Public
12a860f6ebSGang He  * License as published by the Free Software Foundation, version 2.
13a860f6ebSGang He  *
14a860f6ebSGang He  * This program is distributed in the hope that it will be useful,
15a860f6ebSGang He  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16a860f6ebSGang He  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17a860f6ebSGang He  * General Public License for more details.
18a860f6ebSGang He  */
19a860f6ebSGang He 
20a860f6ebSGang He #include <linux/list.h>
21a860f6ebSGang He #include <linux/spinlock.h>
22a860f6ebSGang He #include <linux/module.h>
23a860f6ebSGang He #include <linux/slab.h>
24a860f6ebSGang He #include <linux/kmod.h>
25a860f6ebSGang He #include <linux/fs.h>
26a860f6ebSGang He #include <linux/kobject.h>
27a860f6ebSGang He #include <linux/sysfs.h>
28a860f6ebSGang He #include <linux/sysctl.h>
29a860f6ebSGang He #include <cluster/masklog.h>
30a860f6ebSGang He 
31a860f6ebSGang He #include "ocfs2.h"
32a860f6ebSGang He #include "ocfs2_fs.h"
33a860f6ebSGang He #include "stackglue.h"
34a860f6ebSGang He #include "inode.h"
35a860f6ebSGang He 
36a860f6ebSGang He #include "filecheck.h"
37a860f6ebSGang He 
38a860f6ebSGang He 
39a860f6ebSGang He /* File check error strings,
40a860f6ebSGang He  * must correspond with error number in header file.
41a860f6ebSGang He  */
42a860f6ebSGang He static const char * const ocfs2_filecheck_errs[] = {
43a860f6ebSGang He 	"SUCCESS",
44a860f6ebSGang He 	"FAILED",
45a860f6ebSGang He 	"INPROGRESS",
46a860f6ebSGang He 	"READONLY",
47a860f6ebSGang He 	"INJBD",
48a860f6ebSGang He 	"INVALIDINO",
49a860f6ebSGang He 	"BLOCKECC",
50a860f6ebSGang He 	"BLOCKNO",
51a860f6ebSGang He 	"VALIDFLAG",
52a860f6ebSGang He 	"GENERATION",
53a860f6ebSGang He 	"UNSUPPORTED"
54a860f6ebSGang He };
55a860f6ebSGang He 
56a860f6ebSGang He struct ocfs2_filecheck_entry {
57a860f6ebSGang He 	struct list_head fe_list;
58a860f6ebSGang He 	unsigned long fe_ino;
59a860f6ebSGang He 	unsigned int fe_type;
60a860f6ebSGang He 	unsigned int fe_done:1;
61a860f6ebSGang He 	unsigned int fe_status:31;
62a860f6ebSGang He };
63a860f6ebSGang He 
64a860f6ebSGang He struct ocfs2_filecheck_args {
65a860f6ebSGang He 	unsigned int fa_type;
66a860f6ebSGang He 	union {
67a860f6ebSGang He 		unsigned long fa_ino;
68a860f6ebSGang He 		unsigned int fa_len;
69a860f6ebSGang He 	};
70a860f6ebSGang He };
71a860f6ebSGang He 
72a860f6ebSGang He static const char *
73a860f6ebSGang He ocfs2_filecheck_error(int errno)
74a860f6ebSGang He {
75a860f6ebSGang He 	if (!errno)
76a860f6ebSGang He 		return ocfs2_filecheck_errs[errno];
77a860f6ebSGang He 
78a860f6ebSGang He 	BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
79a860f6ebSGang He 	       errno > OCFS2_FILECHECK_ERR_END);
80a860f6ebSGang He 	return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
81a860f6ebSGang He }
82a860f6ebSGang He 
835f483c4aSGang He static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
84a860f6ebSGang He 					struct kobj_attribute *attr,
85a860f6ebSGang He 					char *buf);
865f483c4aSGang He static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
87a860f6ebSGang He 					struct kobj_attribute *attr,
88a860f6ebSGang He 					const char *buf, size_t count);
895f483c4aSGang He static struct kobj_attribute ocfs2_filecheck_attr_chk =
90a860f6ebSGang He 					__ATTR(check, S_IRUSR | S_IWUSR,
915f483c4aSGang He 					ocfs2_filecheck_attr_show,
925f483c4aSGang He 					ocfs2_filecheck_attr_store);
935f483c4aSGang He static struct kobj_attribute ocfs2_filecheck_attr_fix =
94a860f6ebSGang He 					__ATTR(fix, S_IRUSR | S_IWUSR,
955f483c4aSGang He 					ocfs2_filecheck_attr_show,
965f483c4aSGang He 					ocfs2_filecheck_attr_store);
975f483c4aSGang He static struct kobj_attribute ocfs2_filecheck_attr_set =
98a860f6ebSGang He 					__ATTR(set, S_IRUSR | S_IWUSR,
995f483c4aSGang He 					ocfs2_filecheck_attr_show,
1005f483c4aSGang He 					ocfs2_filecheck_attr_store);
1015f483c4aSGang He static struct attribute *ocfs2_filecheck_attrs[] = {
1025f483c4aSGang He 	&ocfs2_filecheck_attr_chk.attr,
1035f483c4aSGang He 	&ocfs2_filecheck_attr_fix.attr,
1045f483c4aSGang He 	&ocfs2_filecheck_attr_set.attr,
1055f483c4aSGang He 	NULL
1065f483c4aSGang He };
1075f483c4aSGang He 
1085f483c4aSGang He static void ocfs2_filecheck_release(struct kobject *kobj)
1095f483c4aSGang He {
1105f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *entry = container_of(kobj,
1115f483c4aSGang He 				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
1125f483c4aSGang He 
1135f483c4aSGang He 	complete(&entry->fs_kobj_unregister);
1145f483c4aSGang He }
1155f483c4aSGang He 
1165f483c4aSGang He static ssize_t
1175f483c4aSGang He ocfs2_filecheck_show(struct kobject *kobj, struct attribute *attr, char *buf)
1185f483c4aSGang He {
1195f483c4aSGang He 	ssize_t ret = -EIO;
1205f483c4aSGang He 	struct kobj_attribute *kattr = container_of(attr,
1215f483c4aSGang He 					struct kobj_attribute, attr);
1225f483c4aSGang He 
1235f483c4aSGang He 	kobject_get(kobj);
1245f483c4aSGang He 	if (kattr->show)
1255f483c4aSGang He 		ret = kattr->show(kobj, kattr, buf);
1265f483c4aSGang He 	kobject_put(kobj);
1275f483c4aSGang He 	return ret;
1285f483c4aSGang He }
1295f483c4aSGang He 
1305f483c4aSGang He static ssize_t
1315f483c4aSGang He ocfs2_filecheck_store(struct kobject *kobj, struct attribute *attr,
1325f483c4aSGang He 			const char *buf, size_t count)
1335f483c4aSGang He {
1345f483c4aSGang He 	ssize_t ret = -EIO;
1355f483c4aSGang He 	struct kobj_attribute *kattr = container_of(attr,
1365f483c4aSGang He 					struct kobj_attribute, attr);
1375f483c4aSGang He 
1385f483c4aSGang He 	kobject_get(kobj);
1395f483c4aSGang He 	if (kattr->store)
1405f483c4aSGang He 		ret = kattr->store(kobj, kattr, buf, count);
1415f483c4aSGang He 	kobject_put(kobj);
1425f483c4aSGang He 	return ret;
1435f483c4aSGang He }
1445f483c4aSGang He 
1455f483c4aSGang He static const struct sysfs_ops ocfs2_filecheck_ops = {
1465f483c4aSGang He 	.show = ocfs2_filecheck_show,
1475f483c4aSGang He 	.store = ocfs2_filecheck_store,
1485f483c4aSGang He };
1495f483c4aSGang He 
1505f483c4aSGang He static struct kobj_type ocfs2_ktype_filecheck = {
1515f483c4aSGang He 	.default_attrs = ocfs2_filecheck_attrs,
1525f483c4aSGang He 	.sysfs_ops = &ocfs2_filecheck_ops,
1535f483c4aSGang He 	.release = ocfs2_filecheck_release,
1545f483c4aSGang He };
155a860f6ebSGang He 
156a860f6ebSGang He static void
157a860f6ebSGang He ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
158a860f6ebSGang He {
159a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
160a860f6ebSGang He 
161a860f6ebSGang He 	spin_lock(&entry->fs_fcheck->fc_lock);
162a860f6ebSGang He 	while (!list_empty(&entry->fs_fcheck->fc_head)) {
163a860f6ebSGang He 		p = list_first_entry(&entry->fs_fcheck->fc_head,
164a860f6ebSGang He 				     struct ocfs2_filecheck_entry, fe_list);
165a860f6ebSGang He 		list_del(&p->fe_list);
166a860f6ebSGang He 		BUG_ON(!p->fe_done); /* To free a undone file check entry */
167a860f6ebSGang He 		kfree(p);
168a860f6ebSGang He 	}
169a860f6ebSGang He 	spin_unlock(&entry->fs_fcheck->fc_lock);
170a860f6ebSGang He 
171a860f6ebSGang He 	kfree(entry->fs_fcheck);
1725f483c4aSGang He 	entry->fs_fcheck = NULL;
173a860f6ebSGang He }
174a860f6ebSGang He 
1755f483c4aSGang He int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
176a860f6ebSGang He {
1775f483c4aSGang He 	int ret;
1785f483c4aSGang He 	struct ocfs2_filecheck *fcheck;
1795f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent;
180a860f6ebSGang He 
181a860f6ebSGang He 	fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
1825f483c4aSGang He 	if (!fcheck)
1835f483c4aSGang He 		return -ENOMEM;
1845f483c4aSGang He 
185a860f6ebSGang He 	INIT_LIST_HEAD(&fcheck->fc_head);
186a860f6ebSGang He 	spin_lock_init(&fcheck->fc_lock);
187a860f6ebSGang He 	fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
188a860f6ebSGang He 	fcheck->fc_size = 0;
189a860f6ebSGang He 	fcheck->fc_done = 0;
190a860f6ebSGang He 
1915f483c4aSGang He 	entry->fs_kobj.kset = osb->osb_dev_kset;
1925f483c4aSGang He 	init_completion(&entry->fs_kobj_unregister);
1935f483c4aSGang He 	ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck,
1945f483c4aSGang He 					NULL, "filecheck");
1955f483c4aSGang He 	if (ret) {
196a860f6ebSGang He 		kfree(fcheck);
197a860f6ebSGang He 		return ret;
198a860f6ebSGang He 	}
199a860f6ebSGang He 
2005f483c4aSGang He 	entry->fs_fcheck = fcheck;
2015f483c4aSGang He 	return 0;
2025f483c4aSGang He }
2035f483c4aSGang He 
2045f483c4aSGang He void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
205a860f6ebSGang He {
2065f483c4aSGang He 	if (!osb->osb_fc_ent.fs_fcheck)
2075f483c4aSGang He 		return;
2085f483c4aSGang He 
2095f483c4aSGang He 	kobject_del(&osb->osb_fc_ent.fs_kobj);
2105f483c4aSGang He 	kobject_put(&osb->osb_fc_ent.fs_kobj);
2115f483c4aSGang He 	wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
2125f483c4aSGang He 	ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
213a860f6ebSGang He }
214a860f6ebSGang He 
215a860f6ebSGang He static int
216a860f6ebSGang He ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
217a860f6ebSGang He 			      unsigned int count);
218a860f6ebSGang He static int
219a860f6ebSGang He ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
220a860f6ebSGang He 			   unsigned int len)
221a860f6ebSGang He {
222a860f6ebSGang He 	int ret;
223a860f6ebSGang He 
224a860f6ebSGang He 	if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
225a860f6ebSGang He 		return -EINVAL;
226a860f6ebSGang He 
227a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
228a860f6ebSGang He 	if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
2298fc2cb4bSGang He 		mlog(ML_NOTICE,
230a860f6ebSGang He 		"Cannot set online file check maximum entry number "
231a860f6ebSGang He 		"to %u due to too many pending entries(%u)\n",
232a860f6ebSGang He 		len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
233a860f6ebSGang He 		ret = -EBUSY;
234a860f6ebSGang He 	} else {
235a860f6ebSGang He 		if (len < ent->fs_fcheck->fc_size)
236a860f6ebSGang He 			BUG_ON(!ocfs2_filecheck_erase_entries(ent,
237a860f6ebSGang He 				ent->fs_fcheck->fc_size - len));
238a860f6ebSGang He 
239a860f6ebSGang He 		ent->fs_fcheck->fc_max = len;
240a860f6ebSGang He 		ret = 0;
241a860f6ebSGang He 	}
242a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
243a860f6ebSGang He 
244a860f6ebSGang He 	return ret;
245a860f6ebSGang He }
246a860f6ebSGang He 
247a860f6ebSGang He #define OCFS2_FILECHECK_ARGS_LEN	24
248a860f6ebSGang He static int
249a860f6ebSGang He ocfs2_filecheck_args_get_long(const char *buf, size_t count,
250a860f6ebSGang He 			      unsigned long *val)
251a860f6ebSGang He {
252a860f6ebSGang He 	char buffer[OCFS2_FILECHECK_ARGS_LEN];
253a860f6ebSGang He 
254a860f6ebSGang He 	memcpy(buffer, buf, count);
255a860f6ebSGang He 	buffer[count] = '\0';
256a860f6ebSGang He 
257a860f6ebSGang He 	if (kstrtoul(buffer, 0, val))
258a860f6ebSGang He 		return 1;
259a860f6ebSGang He 
260a860f6ebSGang He 	return 0;
261a860f6ebSGang He }
262a860f6ebSGang He 
263a860f6ebSGang He static int
264a860f6ebSGang He ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
265a860f6ebSGang He {
266a860f6ebSGang He 	if (!strncmp(name, "fix", 4))
267a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_FIX;
268a860f6ebSGang He 	else if (!strncmp(name, "check", 6))
269a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_CHK;
270a860f6ebSGang He 	else if (!strncmp(name, "set", 4))
271a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_SET;
272a860f6ebSGang He 	else
273a860f6ebSGang He 		return 1;
274a860f6ebSGang He 
275a860f6ebSGang He 	return 0;
276a860f6ebSGang He }
277a860f6ebSGang He 
278a860f6ebSGang He static int
279a860f6ebSGang He ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
280a860f6ebSGang He 			   struct ocfs2_filecheck_args *args)
281a860f6ebSGang He {
282a860f6ebSGang He 	unsigned long val = 0;
283a860f6ebSGang He 	unsigned int type;
284a860f6ebSGang He 
285a860f6ebSGang He 	/* too short/long args length */
286a860f6ebSGang He 	if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
287a860f6ebSGang He 		return 1;
288a860f6ebSGang He 
289a860f6ebSGang He 	if (ocfs2_filecheck_type_parse(name, &type))
290a860f6ebSGang He 		return 1;
291a860f6ebSGang He 	if (ocfs2_filecheck_args_get_long(buf, count, &val))
292a860f6ebSGang He 		return 1;
293a860f6ebSGang He 
294a860f6ebSGang He 	if (val <= 0)
295a860f6ebSGang He 		return 1;
296a860f6ebSGang He 
297a860f6ebSGang He 	args->fa_type = type;
298a860f6ebSGang He 	if (type == OCFS2_FILECHECK_TYPE_SET)
299a860f6ebSGang He 		args->fa_len = (unsigned int)val;
300a860f6ebSGang He 	else
301a860f6ebSGang He 		args->fa_ino = val;
302a860f6ebSGang He 
303a860f6ebSGang He 	return 0;
304a860f6ebSGang He }
305a860f6ebSGang He 
3065f483c4aSGang He static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
307a860f6ebSGang He 				    struct kobj_attribute *attr,
308a860f6ebSGang He 				    char *buf)
309a860f6ebSGang He {
310a860f6ebSGang He 
311a860f6ebSGang He 	ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
312a860f6ebSGang He 	unsigned int type;
313a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
3145f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
3155f483c4aSGang He 				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
316a860f6ebSGang He 
317a860f6ebSGang He 	if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
318a860f6ebSGang He 		return -EINVAL;
319a860f6ebSGang He 
320a860f6ebSGang He 	if (type == OCFS2_FILECHECK_TYPE_SET) {
321a860f6ebSGang He 		spin_lock(&ent->fs_fcheck->fc_lock);
322a860f6ebSGang He 		total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
323a860f6ebSGang He 		spin_unlock(&ent->fs_fcheck->fc_lock);
324a860f6ebSGang He 		goto exit;
325a860f6ebSGang He 	}
326a860f6ebSGang He 
327a860f6ebSGang He 	ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
328a860f6ebSGang He 	total += ret;
329a860f6ebSGang He 	remain -= ret;
330a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
331a860f6ebSGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
332a860f6ebSGang He 		if (p->fe_type != type)
333a860f6ebSGang He 			continue;
334a860f6ebSGang He 
335a860f6ebSGang He 		ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
336a860f6ebSGang He 			       p->fe_ino, p->fe_done,
337a860f6ebSGang He 			       ocfs2_filecheck_error(p->fe_status));
338a860f6ebSGang He 		if (ret < 0) {
339a860f6ebSGang He 			total = ret;
340a860f6ebSGang He 			break;
341a860f6ebSGang He 		}
342a860f6ebSGang He 		if (ret == remain) {
343a860f6ebSGang He 			/* snprintf() didn't fit */
344a860f6ebSGang He 			total = -E2BIG;
345a860f6ebSGang He 			break;
346a860f6ebSGang He 		}
347a860f6ebSGang He 		total += ret;
348a860f6ebSGang He 		remain -= ret;
349a860f6ebSGang He 	}
350a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
351a860f6ebSGang He 
352a860f6ebSGang He exit:
353a860f6ebSGang He 	return total;
354a860f6ebSGang He }
355a860f6ebSGang He 
3565f483c4aSGang He static inline int
357*39ec3774SGang He ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
358*39ec3774SGang He 				unsigned long ino)
359*39ec3774SGang He {
360*39ec3774SGang He 	struct ocfs2_filecheck_entry *p;
361*39ec3774SGang He 
362*39ec3774SGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
363*39ec3774SGang He 		if (!p->fe_done) {
364*39ec3774SGang He 			if (p->fe_ino == ino)
365*39ec3774SGang He 				return 1;
366*39ec3774SGang He 		}
367*39ec3774SGang He 	}
368*39ec3774SGang He 
369*39ec3774SGang He 	return 0;
370*39ec3774SGang He }
371*39ec3774SGang He 
372*39ec3774SGang He static inline int
373a860f6ebSGang He ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
374a860f6ebSGang He {
375a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
376a860f6ebSGang He 
377a860f6ebSGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
378a860f6ebSGang He 		if (p->fe_done) {
379a860f6ebSGang He 			list_del(&p->fe_list);
380a860f6ebSGang He 			kfree(p);
381a860f6ebSGang He 			ent->fs_fcheck->fc_size--;
382a860f6ebSGang He 			ent->fs_fcheck->fc_done--;
383a860f6ebSGang He 			return 1;
384a860f6ebSGang He 		}
385a860f6ebSGang He 	}
386a860f6ebSGang He 
387a860f6ebSGang He 	return 0;
388a860f6ebSGang He }
389a860f6ebSGang He 
390a860f6ebSGang He static int
391a860f6ebSGang He ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
392a860f6ebSGang He 			      unsigned int count)
393a860f6ebSGang He {
394a860f6ebSGang He 	unsigned int i = 0;
395a860f6ebSGang He 	unsigned int ret = 0;
396a860f6ebSGang He 
397a860f6ebSGang He 	while (i++ < count) {
398a860f6ebSGang He 		if (ocfs2_filecheck_erase_entry(ent))
399a860f6ebSGang He 			ret++;
400a860f6ebSGang He 		else
401a860f6ebSGang He 			break;
402a860f6ebSGang He 	}
403a860f6ebSGang He 
404a860f6ebSGang He 	return (ret == count ? 1 : 0);
405a860f6ebSGang He }
406a860f6ebSGang He 
407a860f6ebSGang He static void
408a860f6ebSGang He ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
409a860f6ebSGang He 			   struct ocfs2_filecheck_entry *entry)
410a860f6ebSGang He {
411a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
4128fc2cb4bSGang He 	entry->fe_done = 1;
413a860f6ebSGang He 	ent->fs_fcheck->fc_done++;
414a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
415a860f6ebSGang He }
416a860f6ebSGang He 
417a860f6ebSGang He static unsigned int
4185f483c4aSGang He ocfs2_filecheck_handle(struct ocfs2_super *osb,
419a860f6ebSGang He 		       unsigned long ino, unsigned int flags)
420a860f6ebSGang He {
421a860f6ebSGang He 	unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
422a860f6ebSGang He 	struct inode *inode = NULL;
423a860f6ebSGang He 	int rc;
424a860f6ebSGang He 
4255f483c4aSGang He 	inode = ocfs2_iget(osb, ino, flags, 0);
426a860f6ebSGang He 	if (IS_ERR(inode)) {
427a860f6ebSGang He 		rc = (int)(-(long)inode);
428a860f6ebSGang He 		if (rc >= OCFS2_FILECHECK_ERR_START &&
429a860f6ebSGang He 		    rc < OCFS2_FILECHECK_ERR_END)
430a860f6ebSGang He 			ret = rc;
431a860f6ebSGang He 		else
432a860f6ebSGang He 			ret = OCFS2_FILECHECK_ERR_FAILED;
433a860f6ebSGang He 	} else
434a860f6ebSGang He 		iput(inode);
435a860f6ebSGang He 
436a860f6ebSGang He 	return ret;
437a860f6ebSGang He }
438a860f6ebSGang He 
439a860f6ebSGang He static void
440a860f6ebSGang He ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
441a860f6ebSGang He 			     struct ocfs2_filecheck_entry *entry)
442a860f6ebSGang He {
4435f483c4aSGang He 	struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
4445f483c4aSGang He 						osb_fc_ent);
4455f483c4aSGang He 
446a860f6ebSGang He 	if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
4475f483c4aSGang He 		entry->fe_status = ocfs2_filecheck_handle(osb,
448a860f6ebSGang He 				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
449a860f6ebSGang He 	else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
4505f483c4aSGang He 		entry->fe_status = ocfs2_filecheck_handle(osb,
451a860f6ebSGang He 				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
452a860f6ebSGang He 	else
453a860f6ebSGang He 		entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
454a860f6ebSGang He 
455a860f6ebSGang He 	ocfs2_filecheck_done_entry(ent, entry);
456a860f6ebSGang He }
457a860f6ebSGang He 
4585f483c4aSGang He static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
459a860f6ebSGang He 				     struct kobj_attribute *attr,
460a860f6ebSGang He 				     const char *buf, size_t count)
461a860f6ebSGang He {
4625f483c4aSGang He 	ssize_t ret = 0;
463a860f6ebSGang He 	struct ocfs2_filecheck_args args;
464a860f6ebSGang He 	struct ocfs2_filecheck_entry *entry;
4655f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
4665f483c4aSGang He 				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
467a860f6ebSGang He 
468a860f6ebSGang He 	if (count == 0)
469a860f6ebSGang He 		return count;
470a860f6ebSGang He 
4715f483c4aSGang He 	if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
472a860f6ebSGang He 		return -EINVAL;
473a860f6ebSGang He 
474a860f6ebSGang He 	if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
475a860f6ebSGang He 		ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
476a860f6ebSGang He 		goto exit;
477a860f6ebSGang He 	}
478a860f6ebSGang He 
479a860f6ebSGang He 	entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
480a860f6ebSGang He 	if (!entry) {
481a860f6ebSGang He 		ret = -ENOMEM;
482a860f6ebSGang He 		goto exit;
483a860f6ebSGang He 	}
484a860f6ebSGang He 
485a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
486*39ec3774SGang He 	if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
487*39ec3774SGang He 		ret = -EEXIST;
488*39ec3774SGang He 		kfree(entry);
489*39ec3774SGang He 	} else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
490a860f6ebSGang He 		(ent->fs_fcheck->fc_done == 0)) {
4918fc2cb4bSGang He 		mlog(ML_NOTICE,
492a860f6ebSGang He 		"Cannot do more file check "
493a860f6ebSGang He 		"since file check queue(%u) is full now\n",
494a860f6ebSGang He 		ent->fs_fcheck->fc_max);
4958fc2cb4bSGang He 		ret = -EAGAIN;
496a860f6ebSGang He 		kfree(entry);
497a860f6ebSGang He 	} else {
498a860f6ebSGang He 		if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
499a860f6ebSGang He 		    (ent->fs_fcheck->fc_done > 0)) {
500a860f6ebSGang He 			/* Delete the oldest entry which was done,
501a860f6ebSGang He 			 * make sure the entry size in list does
502a860f6ebSGang He 			 * not exceed maximum value
503a860f6ebSGang He 			 */
504a860f6ebSGang He 			BUG_ON(!ocfs2_filecheck_erase_entry(ent));
505a860f6ebSGang He 		}
506a860f6ebSGang He 
507a860f6ebSGang He 		entry->fe_ino = args.fa_ino;
508a860f6ebSGang He 		entry->fe_type = args.fa_type;
509a860f6ebSGang He 		entry->fe_done = 0;
510a860f6ebSGang He 		entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
511a860f6ebSGang He 		list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
512a860f6ebSGang He 		ent->fs_fcheck->fc_size++;
513a860f6ebSGang He 	}
514a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
515a860f6ebSGang He 
516a860f6ebSGang He 	if (!ret)
517a860f6ebSGang He 		ocfs2_filecheck_handle_entry(ent, entry);
518a860f6ebSGang He 
519a860f6ebSGang He exit:
520a860f6ebSGang He 	return (!ret ? count : ret);
521a860f6ebSGang He }
522