xref: /openbmc/linux/fs/ocfs2/filecheck.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
150acfb2bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2fa60ce2cSMasahiro Yamada /*
3a860f6ebSGang He  * filecheck.c
4a860f6ebSGang He  *
5a860f6ebSGang He  * Code which implements online file check.
6a860f6ebSGang He  *
7a860f6ebSGang He  * Copyright (C) 2016 SuSE.  All rights reserved.
8a860f6ebSGang He  */
9a860f6ebSGang He 
10a860f6ebSGang He #include <linux/list.h>
11a860f6ebSGang He #include <linux/spinlock.h>
12a860f6ebSGang He #include <linux/module.h>
13a860f6ebSGang He #include <linux/slab.h>
14a860f6ebSGang He #include <linux/kmod.h>
15a860f6ebSGang He #include <linux/fs.h>
16a860f6ebSGang He #include <linux/kobject.h>
17a860f6ebSGang He #include <linux/sysfs.h>
18a860f6ebSGang He #include <linux/sysctl.h>
19a860f6ebSGang He #include <cluster/masklog.h>
20a860f6ebSGang He 
21a860f6ebSGang He #include "ocfs2.h"
22a860f6ebSGang He #include "ocfs2_fs.h"
23a860f6ebSGang He #include "stackglue.h"
24a860f6ebSGang He #include "inode.h"
25a860f6ebSGang He 
26a860f6ebSGang He #include "filecheck.h"
27a860f6ebSGang He 
28a860f6ebSGang He 
29a860f6ebSGang He /* File check error strings,
30a860f6ebSGang He  * must correspond with error number in header file.
31a860f6ebSGang He  */
32a860f6ebSGang He static const char * const ocfs2_filecheck_errs[] = {
33a860f6ebSGang He 	"SUCCESS",
34a860f6ebSGang He 	"FAILED",
35a860f6ebSGang He 	"INPROGRESS",
36a860f6ebSGang He 	"READONLY",
37a860f6ebSGang He 	"INJBD",
38a860f6ebSGang He 	"INVALIDINO",
39a860f6ebSGang He 	"BLOCKECC",
40a860f6ebSGang He 	"BLOCKNO",
41a860f6ebSGang He 	"VALIDFLAG",
42a860f6ebSGang He 	"GENERATION",
43a860f6ebSGang He 	"UNSUPPORTED"
44a860f6ebSGang He };
45a860f6ebSGang He 
46a860f6ebSGang He struct ocfs2_filecheck_entry {
47a860f6ebSGang He 	struct list_head fe_list;
48a860f6ebSGang He 	unsigned long fe_ino;
49a860f6ebSGang He 	unsigned int fe_type;
50a860f6ebSGang He 	unsigned int fe_done:1;
51a860f6ebSGang He 	unsigned int fe_status:31;
52a860f6ebSGang He };
53a860f6ebSGang He 
54a860f6ebSGang He struct ocfs2_filecheck_args {
55a860f6ebSGang He 	unsigned int fa_type;
56a860f6ebSGang He 	union {
57a860f6ebSGang He 		unsigned long fa_ino;
58a860f6ebSGang He 		unsigned int fa_len;
59a860f6ebSGang He 	};
60a860f6ebSGang He };
61a860f6ebSGang He 
62a860f6ebSGang He static const char *
ocfs2_filecheck_error(int errno)63a860f6ebSGang He ocfs2_filecheck_error(int errno)
64a860f6ebSGang He {
65a860f6ebSGang He 	if (!errno)
66a860f6ebSGang He 		return ocfs2_filecheck_errs[errno];
67a860f6ebSGang He 
68a860f6ebSGang He 	BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
69a860f6ebSGang He 	       errno > OCFS2_FILECHECK_ERR_END);
70a860f6ebSGang He 	return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
71a860f6ebSGang He }
72a860f6ebSGang He 
735f483c4aSGang He static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
74a860f6ebSGang He 					struct kobj_attribute *attr,
75a860f6ebSGang He 					char *buf);
765f483c4aSGang He static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
77a860f6ebSGang He 					struct kobj_attribute *attr,
78a860f6ebSGang He 					const char *buf, size_t count);
795f483c4aSGang He static struct kobj_attribute ocfs2_filecheck_attr_chk =
80a860f6ebSGang He 					__ATTR(check, S_IRUSR | S_IWUSR,
815f483c4aSGang He 					ocfs2_filecheck_attr_show,
825f483c4aSGang He 					ocfs2_filecheck_attr_store);
835f483c4aSGang He static struct kobj_attribute ocfs2_filecheck_attr_fix =
84a860f6ebSGang He 					__ATTR(fix, S_IRUSR | S_IWUSR,
855f483c4aSGang He 					ocfs2_filecheck_attr_show,
865f483c4aSGang He 					ocfs2_filecheck_attr_store);
875f483c4aSGang He static struct kobj_attribute ocfs2_filecheck_attr_set =
88a860f6ebSGang He 					__ATTR(set, S_IRUSR | S_IWUSR,
895f483c4aSGang He 					ocfs2_filecheck_attr_show,
905f483c4aSGang He 					ocfs2_filecheck_attr_store);
915f483c4aSGang He static struct attribute *ocfs2_filecheck_attrs[] = {
925f483c4aSGang He 	&ocfs2_filecheck_attr_chk.attr,
935f483c4aSGang He 	&ocfs2_filecheck_attr_fix.attr,
945f483c4aSGang He 	&ocfs2_filecheck_attr_set.attr,
955f483c4aSGang He 	NULL
965f483c4aSGang He };
97*59430cc1SGreg Kroah-Hartman ATTRIBUTE_GROUPS(ocfs2_filecheck);
985f483c4aSGang He 
ocfs2_filecheck_release(struct kobject * kobj)995f483c4aSGang He static void ocfs2_filecheck_release(struct kobject *kobj)
1005f483c4aSGang He {
1015f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *entry = container_of(kobj,
1025f483c4aSGang He 				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
1035f483c4aSGang He 
1045f483c4aSGang He 	complete(&entry->fs_kobj_unregister);
1055f483c4aSGang He }
1065f483c4aSGang He 
1075f483c4aSGang He static ssize_t
ocfs2_filecheck_show(struct kobject * kobj,struct attribute * attr,char * buf)1085f483c4aSGang He ocfs2_filecheck_show(struct kobject *kobj, struct attribute *attr, char *buf)
1095f483c4aSGang He {
1105f483c4aSGang He 	ssize_t ret = -EIO;
1115f483c4aSGang He 	struct kobj_attribute *kattr = container_of(attr,
1125f483c4aSGang He 					struct kobj_attribute, attr);
1135f483c4aSGang He 
1145f483c4aSGang He 	kobject_get(kobj);
1155f483c4aSGang He 	if (kattr->show)
1165f483c4aSGang He 		ret = kattr->show(kobj, kattr, buf);
1175f483c4aSGang He 	kobject_put(kobj);
1185f483c4aSGang He 	return ret;
1195f483c4aSGang He }
1205f483c4aSGang He 
1215f483c4aSGang He static ssize_t
ocfs2_filecheck_store(struct kobject * kobj,struct attribute * attr,const char * buf,size_t count)1225f483c4aSGang He ocfs2_filecheck_store(struct kobject *kobj, struct attribute *attr,
1235f483c4aSGang He 			const char *buf, size_t count)
1245f483c4aSGang He {
1255f483c4aSGang He 	ssize_t ret = -EIO;
1265f483c4aSGang He 	struct kobj_attribute *kattr = container_of(attr,
1275f483c4aSGang He 					struct kobj_attribute, attr);
1285f483c4aSGang He 
1295f483c4aSGang He 	kobject_get(kobj);
1305f483c4aSGang He 	if (kattr->store)
1315f483c4aSGang He 		ret = kattr->store(kobj, kattr, buf, count);
1325f483c4aSGang He 	kobject_put(kobj);
1335f483c4aSGang He 	return ret;
1345f483c4aSGang He }
1355f483c4aSGang He 
1365f483c4aSGang He static const struct sysfs_ops ocfs2_filecheck_ops = {
1375f483c4aSGang He 	.show = ocfs2_filecheck_show,
1385f483c4aSGang He 	.store = ocfs2_filecheck_store,
1395f483c4aSGang He };
1405f483c4aSGang He 
1415f483c4aSGang He static struct kobj_type ocfs2_ktype_filecheck = {
142*59430cc1SGreg Kroah-Hartman 	.default_groups = ocfs2_filecheck_groups,
1435f483c4aSGang He 	.sysfs_ops = &ocfs2_filecheck_ops,
1445f483c4aSGang He 	.release = ocfs2_filecheck_release,
1455f483c4aSGang He };
146a860f6ebSGang He 
147a860f6ebSGang He static void
ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry * entry)148a860f6ebSGang He ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
149a860f6ebSGang He {
150a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
151a860f6ebSGang He 
152a860f6ebSGang He 	spin_lock(&entry->fs_fcheck->fc_lock);
153a860f6ebSGang He 	while (!list_empty(&entry->fs_fcheck->fc_head)) {
154a860f6ebSGang He 		p = list_first_entry(&entry->fs_fcheck->fc_head,
155a860f6ebSGang He 				     struct ocfs2_filecheck_entry, fe_list);
156a860f6ebSGang He 		list_del(&p->fe_list);
157a860f6ebSGang He 		BUG_ON(!p->fe_done); /* To free a undone file check entry */
158a860f6ebSGang He 		kfree(p);
159a860f6ebSGang He 	}
160a860f6ebSGang He 	spin_unlock(&entry->fs_fcheck->fc_lock);
161a860f6ebSGang He 
162a860f6ebSGang He 	kfree(entry->fs_fcheck);
1635f483c4aSGang He 	entry->fs_fcheck = NULL;
164a860f6ebSGang He }
165a860f6ebSGang He 
ocfs2_filecheck_create_sysfs(struct ocfs2_super * osb)1665f483c4aSGang He int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
167a860f6ebSGang He {
1685f483c4aSGang He 	int ret;
1695f483c4aSGang He 	struct ocfs2_filecheck *fcheck;
1705f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent;
171a860f6ebSGang He 
172a860f6ebSGang He 	fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
1735f483c4aSGang He 	if (!fcheck)
1745f483c4aSGang He 		return -ENOMEM;
1755f483c4aSGang He 
176a860f6ebSGang He 	INIT_LIST_HEAD(&fcheck->fc_head);
177a860f6ebSGang He 	spin_lock_init(&fcheck->fc_lock);
178a860f6ebSGang He 	fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
179a860f6ebSGang He 	fcheck->fc_size = 0;
180a860f6ebSGang He 	fcheck->fc_done = 0;
181a860f6ebSGang He 
1825f483c4aSGang He 	entry->fs_kobj.kset = osb->osb_dev_kset;
1835f483c4aSGang He 	init_completion(&entry->fs_kobj_unregister);
1845f483c4aSGang He 	ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck,
1855f483c4aSGang He 					NULL, "filecheck");
1865f483c4aSGang He 	if (ret) {
187b9fba67bSTobin C. Harding 		kobject_put(&entry->fs_kobj);
188a860f6ebSGang He 		kfree(fcheck);
189a860f6ebSGang He 		return ret;
190a860f6ebSGang He 	}
191a860f6ebSGang He 
1925f483c4aSGang He 	entry->fs_fcheck = fcheck;
1935f483c4aSGang He 	return 0;
1945f483c4aSGang He }
1955f483c4aSGang He 
ocfs2_filecheck_remove_sysfs(struct ocfs2_super * osb)1965f483c4aSGang He void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
197a860f6ebSGang He {
1985f483c4aSGang He 	if (!osb->osb_fc_ent.fs_fcheck)
1995f483c4aSGang He 		return;
2005f483c4aSGang He 
2015f483c4aSGang He 	kobject_del(&osb->osb_fc_ent.fs_kobj);
2025f483c4aSGang He 	kobject_put(&osb->osb_fc_ent.fs_kobj);
2035f483c4aSGang He 	wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
2045f483c4aSGang He 	ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
205a860f6ebSGang He }
206a860f6ebSGang He 
207a860f6ebSGang He static int
208a860f6ebSGang He ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
209a860f6ebSGang He 			      unsigned int count);
210a860f6ebSGang He static int
ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry * ent,unsigned int len)211a860f6ebSGang He ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
212a860f6ebSGang He 			   unsigned int len)
213a860f6ebSGang He {
214a860f6ebSGang He 	int ret;
215a860f6ebSGang He 
216a860f6ebSGang He 	if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
217a860f6ebSGang He 		return -EINVAL;
218a860f6ebSGang He 
219a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
220a860f6ebSGang He 	if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
2218fc2cb4bSGang He 		mlog(ML_NOTICE,
222a860f6ebSGang He 		"Cannot set online file check maximum entry number "
223a860f6ebSGang He 		"to %u due to too many pending entries(%u)\n",
224a860f6ebSGang He 		len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
225a860f6ebSGang He 		ret = -EBUSY;
226a860f6ebSGang He 	} else {
227a860f6ebSGang He 		if (len < ent->fs_fcheck->fc_size)
228a860f6ebSGang He 			BUG_ON(!ocfs2_filecheck_erase_entries(ent,
229a860f6ebSGang He 				ent->fs_fcheck->fc_size - len));
230a860f6ebSGang He 
231a860f6ebSGang He 		ent->fs_fcheck->fc_max = len;
232a860f6ebSGang He 		ret = 0;
233a860f6ebSGang He 	}
234a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
235a860f6ebSGang He 
236a860f6ebSGang He 	return ret;
237a860f6ebSGang He }
238a860f6ebSGang He 
239a860f6ebSGang He #define OCFS2_FILECHECK_ARGS_LEN	24
240a860f6ebSGang He static int
ocfs2_filecheck_args_get_long(const char * buf,size_t count,unsigned long * val)241a860f6ebSGang He ocfs2_filecheck_args_get_long(const char *buf, size_t count,
242a860f6ebSGang He 			      unsigned long *val)
243a860f6ebSGang He {
244a860f6ebSGang He 	char buffer[OCFS2_FILECHECK_ARGS_LEN];
245a860f6ebSGang He 
246a860f6ebSGang He 	memcpy(buffer, buf, count);
247a860f6ebSGang He 	buffer[count] = '\0';
248a860f6ebSGang He 
249a860f6ebSGang He 	if (kstrtoul(buffer, 0, val))
250a860f6ebSGang He 		return 1;
251a860f6ebSGang He 
252a860f6ebSGang He 	return 0;
253a860f6ebSGang He }
254a860f6ebSGang He 
255a860f6ebSGang He static int
ocfs2_filecheck_type_parse(const char * name,unsigned int * type)256a860f6ebSGang He ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
257a860f6ebSGang He {
258a860f6ebSGang He 	if (!strncmp(name, "fix", 4))
259a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_FIX;
260a860f6ebSGang He 	else if (!strncmp(name, "check", 6))
261a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_CHK;
262a860f6ebSGang He 	else if (!strncmp(name, "set", 4))
263a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_SET;
264a860f6ebSGang He 	else
265a860f6ebSGang He 		return 1;
266a860f6ebSGang He 
267a860f6ebSGang He 	return 0;
268a860f6ebSGang He }
269a860f6ebSGang He 
270a860f6ebSGang He static int
ocfs2_filecheck_args_parse(const char * name,const char * buf,size_t count,struct ocfs2_filecheck_args * args)271a860f6ebSGang He ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
272a860f6ebSGang He 			   struct ocfs2_filecheck_args *args)
273a860f6ebSGang He {
274a860f6ebSGang He 	unsigned long val = 0;
275a860f6ebSGang He 	unsigned int type;
276a860f6ebSGang He 
277a860f6ebSGang He 	/* too short/long args length */
278a860f6ebSGang He 	if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
279a860f6ebSGang He 		return 1;
280a860f6ebSGang He 
281a860f6ebSGang He 	if (ocfs2_filecheck_type_parse(name, &type))
282a860f6ebSGang He 		return 1;
283a860f6ebSGang He 	if (ocfs2_filecheck_args_get_long(buf, count, &val))
284a860f6ebSGang He 		return 1;
285a860f6ebSGang He 
286a860f6ebSGang He 	if (val <= 0)
287a860f6ebSGang He 		return 1;
288a860f6ebSGang He 
289a860f6ebSGang He 	args->fa_type = type;
290a860f6ebSGang He 	if (type == OCFS2_FILECHECK_TYPE_SET)
291a860f6ebSGang He 		args->fa_len = (unsigned int)val;
292a860f6ebSGang He 	else
293a860f6ebSGang He 		args->fa_ino = val;
294a860f6ebSGang He 
295a860f6ebSGang He 	return 0;
296a860f6ebSGang He }
297a860f6ebSGang He 
ocfs2_filecheck_attr_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)2985f483c4aSGang He static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
299a860f6ebSGang He 				    struct kobj_attribute *attr,
300a860f6ebSGang He 				    char *buf)
301a860f6ebSGang He {
302a860f6ebSGang He 
303a860f6ebSGang He 	ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
304a860f6ebSGang He 	unsigned int type;
305a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
3065f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
3075f483c4aSGang He 				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
308a860f6ebSGang He 
309a860f6ebSGang He 	if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
310a860f6ebSGang He 		return -EINVAL;
311a860f6ebSGang He 
312a860f6ebSGang He 	if (type == OCFS2_FILECHECK_TYPE_SET) {
313a860f6ebSGang He 		spin_lock(&ent->fs_fcheck->fc_lock);
314a860f6ebSGang He 		total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
315a860f6ebSGang He 		spin_unlock(&ent->fs_fcheck->fc_lock);
316a860f6ebSGang He 		goto exit;
317a860f6ebSGang He 	}
318a860f6ebSGang He 
319a860f6ebSGang He 	ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
320a860f6ebSGang He 	total += ret;
321a860f6ebSGang He 	remain -= ret;
322a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
323a860f6ebSGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
324a860f6ebSGang He 		if (p->fe_type != type)
325a860f6ebSGang He 			continue;
326a860f6ebSGang He 
327a860f6ebSGang He 		ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
328a860f6ebSGang He 			       p->fe_ino, p->fe_done,
329a860f6ebSGang He 			       ocfs2_filecheck_error(p->fe_status));
33054e948c6SDan Carpenter 		if (ret >= remain) {
331a860f6ebSGang He 			/* snprintf() didn't fit */
332a860f6ebSGang He 			total = -E2BIG;
333a860f6ebSGang He 			break;
334a860f6ebSGang He 		}
335a860f6ebSGang He 		total += ret;
336a860f6ebSGang He 		remain -= ret;
337a860f6ebSGang He 	}
338a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
339a860f6ebSGang He 
340a860f6ebSGang He exit:
341a860f6ebSGang He 	return total;
342a860f6ebSGang He }
343a860f6ebSGang He 
3445f483c4aSGang He static inline int
ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry * ent,unsigned long ino)34539ec3774SGang He ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
34639ec3774SGang He 				unsigned long ino)
34739ec3774SGang He {
34839ec3774SGang He 	struct ocfs2_filecheck_entry *p;
34939ec3774SGang He 
35039ec3774SGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
35139ec3774SGang He 		if (!p->fe_done) {
35239ec3774SGang He 			if (p->fe_ino == ino)
35339ec3774SGang He 				return 1;
35439ec3774SGang He 		}
35539ec3774SGang He 	}
35639ec3774SGang He 
35739ec3774SGang He 	return 0;
35839ec3774SGang He }
35939ec3774SGang He 
36039ec3774SGang He static inline int
ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry * ent)361a860f6ebSGang He ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
362a860f6ebSGang He {
363a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
364a860f6ebSGang He 
365a860f6ebSGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
366a860f6ebSGang He 		if (p->fe_done) {
367a860f6ebSGang He 			list_del(&p->fe_list);
368a860f6ebSGang He 			kfree(p);
369a860f6ebSGang He 			ent->fs_fcheck->fc_size--;
370a860f6ebSGang He 			ent->fs_fcheck->fc_done--;
371a860f6ebSGang He 			return 1;
372a860f6ebSGang He 		}
373a860f6ebSGang He 	}
374a860f6ebSGang He 
375a860f6ebSGang He 	return 0;
376a860f6ebSGang He }
377a860f6ebSGang He 
378a860f6ebSGang He static int
ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry * ent,unsigned int count)379a860f6ebSGang He ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
380a860f6ebSGang He 			      unsigned int count)
381a860f6ebSGang He {
382a860f6ebSGang He 	unsigned int i = 0;
383a860f6ebSGang He 	unsigned int ret = 0;
384a860f6ebSGang He 
385a860f6ebSGang He 	while (i++ < count) {
386a860f6ebSGang He 		if (ocfs2_filecheck_erase_entry(ent))
387a860f6ebSGang He 			ret++;
388a860f6ebSGang He 		else
389a860f6ebSGang He 			break;
390a860f6ebSGang He 	}
391a860f6ebSGang He 
392a860f6ebSGang He 	return (ret == count ? 1 : 0);
393a860f6ebSGang He }
394a860f6ebSGang He 
395a860f6ebSGang He static void
ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry * ent,struct ocfs2_filecheck_entry * entry)396a860f6ebSGang He ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
397a860f6ebSGang He 			   struct ocfs2_filecheck_entry *entry)
398a860f6ebSGang He {
399a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
4008fc2cb4bSGang He 	entry->fe_done = 1;
401a860f6ebSGang He 	ent->fs_fcheck->fc_done++;
402a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
403a860f6ebSGang He }
404a860f6ebSGang He 
405a860f6ebSGang He static unsigned int
ocfs2_filecheck_handle(struct ocfs2_super * osb,unsigned long ino,unsigned int flags)4065f483c4aSGang He ocfs2_filecheck_handle(struct ocfs2_super *osb,
407a860f6ebSGang He 		       unsigned long ino, unsigned int flags)
408a860f6ebSGang He {
409a860f6ebSGang He 	unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
410a860f6ebSGang He 	struct inode *inode = NULL;
411a860f6ebSGang He 	int rc;
412a860f6ebSGang He 
4135f483c4aSGang He 	inode = ocfs2_iget(osb, ino, flags, 0);
414a860f6ebSGang He 	if (IS_ERR(inode)) {
415a860f6ebSGang He 		rc = (int)(-(long)inode);
416a860f6ebSGang He 		if (rc >= OCFS2_FILECHECK_ERR_START &&
417a860f6ebSGang He 		    rc < OCFS2_FILECHECK_ERR_END)
418a860f6ebSGang He 			ret = rc;
419a860f6ebSGang He 		else
420a860f6ebSGang He 			ret = OCFS2_FILECHECK_ERR_FAILED;
421a860f6ebSGang He 	} else
422a860f6ebSGang He 		iput(inode);
423a860f6ebSGang He 
424a860f6ebSGang He 	return ret;
425a860f6ebSGang He }
426a860f6ebSGang He 
427a860f6ebSGang He static void
ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry * ent,struct ocfs2_filecheck_entry * entry)428a860f6ebSGang He ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
429a860f6ebSGang He 			     struct ocfs2_filecheck_entry *entry)
430a860f6ebSGang He {
4315f483c4aSGang He 	struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
4325f483c4aSGang He 						osb_fc_ent);
4335f483c4aSGang He 
434a860f6ebSGang He 	if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
4355f483c4aSGang He 		entry->fe_status = ocfs2_filecheck_handle(osb,
436a860f6ebSGang He 				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
437a860f6ebSGang He 	else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
4385f483c4aSGang He 		entry->fe_status = ocfs2_filecheck_handle(osb,
439a860f6ebSGang He 				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
440a860f6ebSGang He 	else
441a860f6ebSGang He 		entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
442a860f6ebSGang He 
443a860f6ebSGang He 	ocfs2_filecheck_done_entry(ent, entry);
444a860f6ebSGang He }
445a860f6ebSGang He 
ocfs2_filecheck_attr_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)4465f483c4aSGang He static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
447a860f6ebSGang He 				     struct kobj_attribute *attr,
448a860f6ebSGang He 				     const char *buf, size_t count)
449a860f6ebSGang He {
4505f483c4aSGang He 	ssize_t ret = 0;
451a860f6ebSGang He 	struct ocfs2_filecheck_args args;
452a860f6ebSGang He 	struct ocfs2_filecheck_entry *entry;
4535f483c4aSGang He 	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
4545f483c4aSGang He 				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
455a860f6ebSGang He 
456a860f6ebSGang He 	if (count == 0)
457a860f6ebSGang He 		return count;
458a860f6ebSGang He 
4595f483c4aSGang He 	if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
460a860f6ebSGang He 		return -EINVAL;
461a860f6ebSGang He 
462a860f6ebSGang He 	if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
463a860f6ebSGang He 		ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
464a860f6ebSGang He 		goto exit;
465a860f6ebSGang He 	}
466a860f6ebSGang He 
467a860f6ebSGang He 	entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
468a860f6ebSGang He 	if (!entry) {
469a860f6ebSGang He 		ret = -ENOMEM;
470a860f6ebSGang He 		goto exit;
471a860f6ebSGang He 	}
472a860f6ebSGang He 
473a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
47439ec3774SGang He 	if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
47539ec3774SGang He 		ret = -EEXIST;
47639ec3774SGang He 		kfree(entry);
47739ec3774SGang He 	} else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
478a860f6ebSGang He 		(ent->fs_fcheck->fc_done == 0)) {
4798fc2cb4bSGang He 		mlog(ML_NOTICE,
480a860f6ebSGang He 		"Cannot do more file check "
481a860f6ebSGang He 		"since file check queue(%u) is full now\n",
482a860f6ebSGang He 		ent->fs_fcheck->fc_max);
4838fc2cb4bSGang He 		ret = -EAGAIN;
484a860f6ebSGang He 		kfree(entry);
485a860f6ebSGang He 	} else {
486a860f6ebSGang He 		if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
487a860f6ebSGang He 		    (ent->fs_fcheck->fc_done > 0)) {
488a860f6ebSGang He 			/* Delete the oldest entry which was done,
489a860f6ebSGang He 			 * make sure the entry size in list does
490a860f6ebSGang He 			 * not exceed maximum value
491a860f6ebSGang He 			 */
492a860f6ebSGang He 			BUG_ON(!ocfs2_filecheck_erase_entry(ent));
493a860f6ebSGang He 		}
494a860f6ebSGang He 
495a860f6ebSGang He 		entry->fe_ino = args.fa_ino;
496a860f6ebSGang He 		entry->fe_type = args.fa_type;
497a860f6ebSGang He 		entry->fe_done = 0;
498a860f6ebSGang He 		entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
499a860f6ebSGang He 		list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
500a860f6ebSGang He 		ent->fs_fcheck->fc_size++;
501a860f6ebSGang He 	}
502a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
503a860f6ebSGang He 
504a860f6ebSGang He 	if (!ret)
505a860f6ebSGang He 		ocfs2_filecheck_handle_entry(ent, entry);
506a860f6ebSGang He 
507a860f6ebSGang He exit:
508a860f6ebSGang He 	return (!ret ? count : ret);
509a860f6ebSGang He }
510