xref: /openbmc/linux/fs/ocfs2/filecheck.c (revision a860f6eb4c6a8bb0ca6860d9472f424bad9af9cf)
1*a860f6ebSGang He /* -*- mode: c; c-basic-offset: 8; -*-
2*a860f6ebSGang He  * vim: noexpandtab sw=8 ts=8 sts=0:
3*a860f6ebSGang He  *
4*a860f6ebSGang He  * filecheck.c
5*a860f6ebSGang He  *
6*a860f6ebSGang He  * Code which implements online file check.
7*a860f6ebSGang He  *
8*a860f6ebSGang He  * Copyright (C) 2016 SuSE.  All rights reserved.
9*a860f6ebSGang He  *
10*a860f6ebSGang He  * This program is free software; you can redistribute it and/or
11*a860f6ebSGang He  * modify it under the terms of the GNU General Public
12*a860f6ebSGang He  * License as published by the Free Software Foundation, version 2.
13*a860f6ebSGang He  *
14*a860f6ebSGang He  * This program is distributed in the hope that it will be useful,
15*a860f6ebSGang He  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16*a860f6ebSGang He  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17*a860f6ebSGang He  * General Public License for more details.
18*a860f6ebSGang He  */
19*a860f6ebSGang He 
20*a860f6ebSGang He #include <linux/list.h>
21*a860f6ebSGang He #include <linux/spinlock.h>
22*a860f6ebSGang He #include <linux/module.h>
23*a860f6ebSGang He #include <linux/slab.h>
24*a860f6ebSGang He #include <linux/kmod.h>
25*a860f6ebSGang He #include <linux/fs.h>
26*a860f6ebSGang He #include <linux/kobject.h>
27*a860f6ebSGang He #include <linux/sysfs.h>
28*a860f6ebSGang He #include <linux/sysctl.h>
29*a860f6ebSGang He #include <cluster/masklog.h>
30*a860f6ebSGang He 
31*a860f6ebSGang He #include "ocfs2.h"
32*a860f6ebSGang He #include "ocfs2_fs.h"
33*a860f6ebSGang He #include "stackglue.h"
34*a860f6ebSGang He #include "inode.h"
35*a860f6ebSGang He 
36*a860f6ebSGang He #include "filecheck.h"
37*a860f6ebSGang He 
38*a860f6ebSGang He 
39*a860f6ebSGang He /* File check error strings,
40*a860f6ebSGang He  * must correspond with error number in header file.
41*a860f6ebSGang He  */
42*a860f6ebSGang He static const char * const ocfs2_filecheck_errs[] = {
43*a860f6ebSGang He 	"SUCCESS",
44*a860f6ebSGang He 	"FAILED",
45*a860f6ebSGang He 	"INPROGRESS",
46*a860f6ebSGang He 	"READONLY",
47*a860f6ebSGang He 	"INJBD",
48*a860f6ebSGang He 	"INVALIDINO",
49*a860f6ebSGang He 	"BLOCKECC",
50*a860f6ebSGang He 	"BLOCKNO",
51*a860f6ebSGang He 	"VALIDFLAG",
52*a860f6ebSGang He 	"GENERATION",
53*a860f6ebSGang He 	"UNSUPPORTED"
54*a860f6ebSGang He };
55*a860f6ebSGang He 
56*a860f6ebSGang He static DEFINE_SPINLOCK(ocfs2_filecheck_sysfs_lock);
57*a860f6ebSGang He static LIST_HEAD(ocfs2_filecheck_sysfs_list);
58*a860f6ebSGang He 
59*a860f6ebSGang He struct ocfs2_filecheck {
60*a860f6ebSGang He 	struct list_head fc_head;	/* File check entry list head */
61*a860f6ebSGang He 	spinlock_t fc_lock;
62*a860f6ebSGang He 	unsigned int fc_max;	/* Maximum number of entry in list */
63*a860f6ebSGang He 	unsigned int fc_size;	/* Current entry count in list */
64*a860f6ebSGang He 	unsigned int fc_done;	/* Finished entry count in list */
65*a860f6ebSGang He };
66*a860f6ebSGang He 
67*a860f6ebSGang He struct ocfs2_filecheck_sysfs_entry {	/* sysfs entry per mounting */
68*a860f6ebSGang He 	struct list_head fs_list;
69*a860f6ebSGang He 	atomic_t fs_count;
70*a860f6ebSGang He 	struct super_block *fs_sb;
71*a860f6ebSGang He 	struct kset *fs_devicekset;
72*a860f6ebSGang He 	struct kset *fs_fcheckkset;
73*a860f6ebSGang He 	struct ocfs2_filecheck *fs_fcheck;
74*a860f6ebSGang He };
75*a860f6ebSGang He 
76*a860f6ebSGang He #define OCFS2_FILECHECK_MAXSIZE		100
77*a860f6ebSGang He #define OCFS2_FILECHECK_MINSIZE		10
78*a860f6ebSGang He 
79*a860f6ebSGang He /* File check operation type */
80*a860f6ebSGang He enum {
81*a860f6ebSGang He 	OCFS2_FILECHECK_TYPE_CHK = 0,	/* Check a file(inode) */
82*a860f6ebSGang He 	OCFS2_FILECHECK_TYPE_FIX,	/* Fix a file(inode) */
83*a860f6ebSGang He 	OCFS2_FILECHECK_TYPE_SET = 100	/* Set entry list maximum size */
84*a860f6ebSGang He };
85*a860f6ebSGang He 
86*a860f6ebSGang He struct ocfs2_filecheck_entry {
87*a860f6ebSGang He 	struct list_head fe_list;
88*a860f6ebSGang He 	unsigned long fe_ino;
89*a860f6ebSGang He 	unsigned int fe_type;
90*a860f6ebSGang He 	unsigned int fe_done:1;
91*a860f6ebSGang He 	unsigned int fe_status:31;
92*a860f6ebSGang He };
93*a860f6ebSGang He 
94*a860f6ebSGang He struct ocfs2_filecheck_args {
95*a860f6ebSGang He 	unsigned int fa_type;
96*a860f6ebSGang He 	union {
97*a860f6ebSGang He 		unsigned long fa_ino;
98*a860f6ebSGang He 		unsigned int fa_len;
99*a860f6ebSGang He 	};
100*a860f6ebSGang He };
101*a860f6ebSGang He 
102*a860f6ebSGang He static const char *
103*a860f6ebSGang He ocfs2_filecheck_error(int errno)
104*a860f6ebSGang He {
105*a860f6ebSGang He 	if (!errno)
106*a860f6ebSGang He 		return ocfs2_filecheck_errs[errno];
107*a860f6ebSGang He 
108*a860f6ebSGang He 	BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
109*a860f6ebSGang He 	       errno > OCFS2_FILECHECK_ERR_END);
110*a860f6ebSGang He 	return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
111*a860f6ebSGang He }
112*a860f6ebSGang He 
113*a860f6ebSGang He static ssize_t ocfs2_filecheck_show(struct kobject *kobj,
114*a860f6ebSGang He 				    struct kobj_attribute *attr,
115*a860f6ebSGang He 				    char *buf);
116*a860f6ebSGang He static ssize_t ocfs2_filecheck_store(struct kobject *kobj,
117*a860f6ebSGang He 				     struct kobj_attribute *attr,
118*a860f6ebSGang He 				     const char *buf, size_t count);
119*a860f6ebSGang He static struct kobj_attribute ocfs2_attr_filecheck_chk =
120*a860f6ebSGang He 					__ATTR(check, S_IRUSR | S_IWUSR,
121*a860f6ebSGang He 					ocfs2_filecheck_show,
122*a860f6ebSGang He 					ocfs2_filecheck_store);
123*a860f6ebSGang He static struct kobj_attribute ocfs2_attr_filecheck_fix =
124*a860f6ebSGang He 					__ATTR(fix, S_IRUSR | S_IWUSR,
125*a860f6ebSGang He 					ocfs2_filecheck_show,
126*a860f6ebSGang He 					ocfs2_filecheck_store);
127*a860f6ebSGang He static struct kobj_attribute ocfs2_attr_filecheck_set =
128*a860f6ebSGang He 					__ATTR(set, S_IRUSR | S_IWUSR,
129*a860f6ebSGang He 					ocfs2_filecheck_show,
130*a860f6ebSGang He 					ocfs2_filecheck_store);
131*a860f6ebSGang He 
132*a860f6ebSGang He static int ocfs2_filecheck_sysfs_wait(atomic_t *p)
133*a860f6ebSGang He {
134*a860f6ebSGang He 	schedule();
135*a860f6ebSGang He 	return 0;
136*a860f6ebSGang He }
137*a860f6ebSGang He 
138*a860f6ebSGang He static void
139*a860f6ebSGang He ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
140*a860f6ebSGang He {
141*a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
142*a860f6ebSGang He 
143*a860f6ebSGang He 	if (!atomic_dec_and_test(&entry->fs_count))
144*a860f6ebSGang He 		wait_on_atomic_t(&entry->fs_count, ocfs2_filecheck_sysfs_wait,
145*a860f6ebSGang He 				 TASK_UNINTERRUPTIBLE);
146*a860f6ebSGang He 
147*a860f6ebSGang He 	spin_lock(&entry->fs_fcheck->fc_lock);
148*a860f6ebSGang He 	while (!list_empty(&entry->fs_fcheck->fc_head)) {
149*a860f6ebSGang He 		p = list_first_entry(&entry->fs_fcheck->fc_head,
150*a860f6ebSGang He 				     struct ocfs2_filecheck_entry, fe_list);
151*a860f6ebSGang He 		list_del(&p->fe_list);
152*a860f6ebSGang He 		BUG_ON(!p->fe_done); /* To free a undone file check entry */
153*a860f6ebSGang He 		kfree(p);
154*a860f6ebSGang He 	}
155*a860f6ebSGang He 	spin_unlock(&entry->fs_fcheck->fc_lock);
156*a860f6ebSGang He 
157*a860f6ebSGang He 	kset_unregister(entry->fs_fcheckkset);
158*a860f6ebSGang He 	kset_unregister(entry->fs_devicekset);
159*a860f6ebSGang He 	kfree(entry->fs_fcheck);
160*a860f6ebSGang He 	kfree(entry);
161*a860f6ebSGang He }
162*a860f6ebSGang He 
163*a860f6ebSGang He static void
164*a860f6ebSGang He ocfs2_filecheck_sysfs_add(struct ocfs2_filecheck_sysfs_entry *entry)
165*a860f6ebSGang He {
166*a860f6ebSGang He 	spin_lock(&ocfs2_filecheck_sysfs_lock);
167*a860f6ebSGang He 	list_add_tail(&entry->fs_list, &ocfs2_filecheck_sysfs_list);
168*a860f6ebSGang He 	spin_unlock(&ocfs2_filecheck_sysfs_lock);
169*a860f6ebSGang He }
170*a860f6ebSGang He 
171*a860f6ebSGang He static int ocfs2_filecheck_sysfs_del(const char *devname)
172*a860f6ebSGang He {
173*a860f6ebSGang He 	struct ocfs2_filecheck_sysfs_entry *p;
174*a860f6ebSGang He 
175*a860f6ebSGang He 	spin_lock(&ocfs2_filecheck_sysfs_lock);
176*a860f6ebSGang He 	list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) {
177*a860f6ebSGang He 		if (!strcmp(p->fs_sb->s_id, devname)) {
178*a860f6ebSGang He 			list_del(&p->fs_list);
179*a860f6ebSGang He 			spin_unlock(&ocfs2_filecheck_sysfs_lock);
180*a860f6ebSGang He 			ocfs2_filecheck_sysfs_free(p);
181*a860f6ebSGang He 			return 0;
182*a860f6ebSGang He 		}
183*a860f6ebSGang He 	}
184*a860f6ebSGang He 	spin_unlock(&ocfs2_filecheck_sysfs_lock);
185*a860f6ebSGang He 	return 1;
186*a860f6ebSGang He }
187*a860f6ebSGang He 
188*a860f6ebSGang He static void
189*a860f6ebSGang He ocfs2_filecheck_sysfs_put(struct ocfs2_filecheck_sysfs_entry *entry)
190*a860f6ebSGang He {
191*a860f6ebSGang He 	if (atomic_dec_and_test(&entry->fs_count))
192*a860f6ebSGang He 		wake_up_atomic_t(&entry->fs_count);
193*a860f6ebSGang He }
194*a860f6ebSGang He 
195*a860f6ebSGang He static struct ocfs2_filecheck_sysfs_entry *
196*a860f6ebSGang He ocfs2_filecheck_sysfs_get(const char *devname)
197*a860f6ebSGang He {
198*a860f6ebSGang He 	struct ocfs2_filecheck_sysfs_entry *p = NULL;
199*a860f6ebSGang He 
200*a860f6ebSGang He 	spin_lock(&ocfs2_filecheck_sysfs_lock);
201*a860f6ebSGang He 	list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) {
202*a860f6ebSGang He 		if (!strcmp(p->fs_sb->s_id, devname)) {
203*a860f6ebSGang He 			atomic_inc(&p->fs_count);
204*a860f6ebSGang He 			spin_unlock(&ocfs2_filecheck_sysfs_lock);
205*a860f6ebSGang He 			return p;
206*a860f6ebSGang He 		}
207*a860f6ebSGang He 	}
208*a860f6ebSGang He 	spin_unlock(&ocfs2_filecheck_sysfs_lock);
209*a860f6ebSGang He 	return NULL;
210*a860f6ebSGang He }
211*a860f6ebSGang He 
212*a860f6ebSGang He int ocfs2_filecheck_create_sysfs(struct super_block *sb)
213*a860f6ebSGang He {
214*a860f6ebSGang He 	int ret = 0;
215*a860f6ebSGang He 	struct kset *device_kset = NULL;
216*a860f6ebSGang He 	struct kset *fcheck_kset = NULL;
217*a860f6ebSGang He 	struct ocfs2_filecheck *fcheck = NULL;
218*a860f6ebSGang He 	struct ocfs2_filecheck_sysfs_entry *entry = NULL;
219*a860f6ebSGang He 	struct attribute **attrs = NULL;
220*a860f6ebSGang He 	struct attribute_group attrgp;
221*a860f6ebSGang He 
222*a860f6ebSGang He 	if (!ocfs2_kset)
223*a860f6ebSGang He 		return -ENOMEM;
224*a860f6ebSGang He 
225*a860f6ebSGang He 	attrs = kmalloc(sizeof(struct attribute *) * 4, GFP_NOFS);
226*a860f6ebSGang He 	if (!attrs) {
227*a860f6ebSGang He 		ret = -ENOMEM;
228*a860f6ebSGang He 		goto error;
229*a860f6ebSGang He 	} else {
230*a860f6ebSGang He 		attrs[0] = &ocfs2_attr_filecheck_chk.attr;
231*a860f6ebSGang He 		attrs[1] = &ocfs2_attr_filecheck_fix.attr;
232*a860f6ebSGang He 		attrs[2] = &ocfs2_attr_filecheck_set.attr;
233*a860f6ebSGang He 		attrs[3] = NULL;
234*a860f6ebSGang He 		memset(&attrgp, 0, sizeof(attrgp));
235*a860f6ebSGang He 		attrgp.attrs = attrs;
236*a860f6ebSGang He 	}
237*a860f6ebSGang He 
238*a860f6ebSGang He 	fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
239*a860f6ebSGang He 	if (!fcheck) {
240*a860f6ebSGang He 		ret = -ENOMEM;
241*a860f6ebSGang He 		goto error;
242*a860f6ebSGang He 	} else {
243*a860f6ebSGang He 		INIT_LIST_HEAD(&fcheck->fc_head);
244*a860f6ebSGang He 		spin_lock_init(&fcheck->fc_lock);
245*a860f6ebSGang He 		fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
246*a860f6ebSGang He 		fcheck->fc_size = 0;
247*a860f6ebSGang He 		fcheck->fc_done = 0;
248*a860f6ebSGang He 	}
249*a860f6ebSGang He 
250*a860f6ebSGang He 	if (strlen(sb->s_id) <= 0) {
251*a860f6ebSGang He 		mlog(ML_ERROR,
252*a860f6ebSGang He 		"Cannot get device basename when create filecheck sysfs\n");
253*a860f6ebSGang He 		ret = -ENODEV;
254*a860f6ebSGang He 		goto error;
255*a860f6ebSGang He 	}
256*a860f6ebSGang He 
257*a860f6ebSGang He 	device_kset = kset_create_and_add(sb->s_id, NULL, &ocfs2_kset->kobj);
258*a860f6ebSGang He 	if (!device_kset) {
259*a860f6ebSGang He 		ret = -ENOMEM;
260*a860f6ebSGang He 		goto error;
261*a860f6ebSGang He 	}
262*a860f6ebSGang He 
263*a860f6ebSGang He 	fcheck_kset = kset_create_and_add("filecheck", NULL,
264*a860f6ebSGang He 					  &device_kset->kobj);
265*a860f6ebSGang He 	if (!fcheck_kset) {
266*a860f6ebSGang He 		ret = -ENOMEM;
267*a860f6ebSGang He 		goto error;
268*a860f6ebSGang He 	}
269*a860f6ebSGang He 
270*a860f6ebSGang He 	ret = sysfs_create_group(&fcheck_kset->kobj, &attrgp);
271*a860f6ebSGang He 	if (ret)
272*a860f6ebSGang He 		goto error;
273*a860f6ebSGang He 
274*a860f6ebSGang He 	entry = kmalloc(sizeof(struct ocfs2_filecheck_sysfs_entry), GFP_NOFS);
275*a860f6ebSGang He 	if (!entry) {
276*a860f6ebSGang He 		ret = -ENOMEM;
277*a860f6ebSGang He 		goto error;
278*a860f6ebSGang He 	} else {
279*a860f6ebSGang He 		atomic_set(&entry->fs_count, 1);
280*a860f6ebSGang He 		entry->fs_sb = sb;
281*a860f6ebSGang He 		entry->fs_devicekset = device_kset;
282*a860f6ebSGang He 		entry->fs_fcheckkset = fcheck_kset;
283*a860f6ebSGang He 		entry->fs_fcheck = fcheck;
284*a860f6ebSGang He 		ocfs2_filecheck_sysfs_add(entry);
285*a860f6ebSGang He 	}
286*a860f6ebSGang He 
287*a860f6ebSGang He 	kfree(attrs);
288*a860f6ebSGang He 	return 0;
289*a860f6ebSGang He 
290*a860f6ebSGang He error:
291*a860f6ebSGang He 	kfree(attrs);
292*a860f6ebSGang He 	kfree(entry);
293*a860f6ebSGang He 	kfree(fcheck);
294*a860f6ebSGang He 	kset_unregister(fcheck_kset);
295*a860f6ebSGang He 	kset_unregister(device_kset);
296*a860f6ebSGang He 	return ret;
297*a860f6ebSGang He }
298*a860f6ebSGang He 
299*a860f6ebSGang He int ocfs2_filecheck_remove_sysfs(struct super_block *sb)
300*a860f6ebSGang He {
301*a860f6ebSGang He 	return ocfs2_filecheck_sysfs_del(sb->s_id);
302*a860f6ebSGang He }
303*a860f6ebSGang He 
304*a860f6ebSGang He static int
305*a860f6ebSGang He ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
306*a860f6ebSGang He 			      unsigned int count);
307*a860f6ebSGang He static int
308*a860f6ebSGang He ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
309*a860f6ebSGang He 			   unsigned int len)
310*a860f6ebSGang He {
311*a860f6ebSGang He 	int ret;
312*a860f6ebSGang He 
313*a860f6ebSGang He 	if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
314*a860f6ebSGang He 		return -EINVAL;
315*a860f6ebSGang He 
316*a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
317*a860f6ebSGang He 	if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
318*a860f6ebSGang He 		mlog(ML_ERROR,
319*a860f6ebSGang He 		"Cannot set online file check maximum entry number "
320*a860f6ebSGang He 		"to %u due to too many pending entries(%u)\n",
321*a860f6ebSGang He 		len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
322*a860f6ebSGang He 		ret = -EBUSY;
323*a860f6ebSGang He 	} else {
324*a860f6ebSGang He 		if (len < ent->fs_fcheck->fc_size)
325*a860f6ebSGang He 			BUG_ON(!ocfs2_filecheck_erase_entries(ent,
326*a860f6ebSGang He 				ent->fs_fcheck->fc_size - len));
327*a860f6ebSGang He 
328*a860f6ebSGang He 		ent->fs_fcheck->fc_max = len;
329*a860f6ebSGang He 		ret = 0;
330*a860f6ebSGang He 	}
331*a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
332*a860f6ebSGang He 
333*a860f6ebSGang He 	return ret;
334*a860f6ebSGang He }
335*a860f6ebSGang He 
336*a860f6ebSGang He #define OCFS2_FILECHECK_ARGS_LEN	24
337*a860f6ebSGang He static int
338*a860f6ebSGang He ocfs2_filecheck_args_get_long(const char *buf, size_t count,
339*a860f6ebSGang He 			      unsigned long *val)
340*a860f6ebSGang He {
341*a860f6ebSGang He 	char buffer[OCFS2_FILECHECK_ARGS_LEN];
342*a860f6ebSGang He 
343*a860f6ebSGang He 	memcpy(buffer, buf, count);
344*a860f6ebSGang He 	buffer[count] = '\0';
345*a860f6ebSGang He 
346*a860f6ebSGang He 	if (kstrtoul(buffer, 0, val))
347*a860f6ebSGang He 		return 1;
348*a860f6ebSGang He 
349*a860f6ebSGang He 	return 0;
350*a860f6ebSGang He }
351*a860f6ebSGang He 
352*a860f6ebSGang He static int
353*a860f6ebSGang He ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
354*a860f6ebSGang He {
355*a860f6ebSGang He 	if (!strncmp(name, "fix", 4))
356*a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_FIX;
357*a860f6ebSGang He 	else if (!strncmp(name, "check", 6))
358*a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_CHK;
359*a860f6ebSGang He 	else if (!strncmp(name, "set", 4))
360*a860f6ebSGang He 		*type = OCFS2_FILECHECK_TYPE_SET;
361*a860f6ebSGang He 	else
362*a860f6ebSGang He 		return 1;
363*a860f6ebSGang He 
364*a860f6ebSGang He 	return 0;
365*a860f6ebSGang He }
366*a860f6ebSGang He 
367*a860f6ebSGang He static int
368*a860f6ebSGang He ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
369*a860f6ebSGang He 			   struct ocfs2_filecheck_args *args)
370*a860f6ebSGang He {
371*a860f6ebSGang He 	unsigned long val = 0;
372*a860f6ebSGang He 	unsigned int type;
373*a860f6ebSGang He 
374*a860f6ebSGang He 	/* too short/long args length */
375*a860f6ebSGang He 	if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
376*a860f6ebSGang He 		return 1;
377*a860f6ebSGang He 
378*a860f6ebSGang He 	if (ocfs2_filecheck_type_parse(name, &type))
379*a860f6ebSGang He 		return 1;
380*a860f6ebSGang He 	if (ocfs2_filecheck_args_get_long(buf, count, &val))
381*a860f6ebSGang He 		return 1;
382*a860f6ebSGang He 
383*a860f6ebSGang He 	if (val <= 0)
384*a860f6ebSGang He 		return 1;
385*a860f6ebSGang He 
386*a860f6ebSGang He 	args->fa_type = type;
387*a860f6ebSGang He 	if (type == OCFS2_FILECHECK_TYPE_SET)
388*a860f6ebSGang He 		args->fa_len = (unsigned int)val;
389*a860f6ebSGang He 	else
390*a860f6ebSGang He 		args->fa_ino = val;
391*a860f6ebSGang He 
392*a860f6ebSGang He 	return 0;
393*a860f6ebSGang He }
394*a860f6ebSGang He 
395*a860f6ebSGang He static ssize_t ocfs2_filecheck_show(struct kobject *kobj,
396*a860f6ebSGang He 				    struct kobj_attribute *attr,
397*a860f6ebSGang He 				    char *buf)
398*a860f6ebSGang He {
399*a860f6ebSGang He 
400*a860f6ebSGang He 	ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
401*a860f6ebSGang He 	unsigned int type;
402*a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
403*a860f6ebSGang He 	struct ocfs2_filecheck_sysfs_entry *ent;
404*a860f6ebSGang He 
405*a860f6ebSGang He 	if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
406*a860f6ebSGang He 		return -EINVAL;
407*a860f6ebSGang He 
408*a860f6ebSGang He 	ent = ocfs2_filecheck_sysfs_get(kobj->parent->name);
409*a860f6ebSGang He 	if (!ent) {
410*a860f6ebSGang He 		mlog(ML_ERROR,
411*a860f6ebSGang He 		"Cannot get the corresponding entry via device basename %s\n",
412*a860f6ebSGang He 		kobj->name);
413*a860f6ebSGang He 		return -ENODEV;
414*a860f6ebSGang He 	}
415*a860f6ebSGang He 
416*a860f6ebSGang He 	if (type == OCFS2_FILECHECK_TYPE_SET) {
417*a860f6ebSGang He 		spin_lock(&ent->fs_fcheck->fc_lock);
418*a860f6ebSGang He 		total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
419*a860f6ebSGang He 		spin_unlock(&ent->fs_fcheck->fc_lock);
420*a860f6ebSGang He 		goto exit;
421*a860f6ebSGang He 	}
422*a860f6ebSGang He 
423*a860f6ebSGang He 	ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
424*a860f6ebSGang He 	total += ret;
425*a860f6ebSGang He 	remain -= ret;
426*a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
427*a860f6ebSGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
428*a860f6ebSGang He 		if (p->fe_type != type)
429*a860f6ebSGang He 			continue;
430*a860f6ebSGang He 
431*a860f6ebSGang He 		ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
432*a860f6ebSGang He 			       p->fe_ino, p->fe_done,
433*a860f6ebSGang He 			       ocfs2_filecheck_error(p->fe_status));
434*a860f6ebSGang He 		if (ret < 0) {
435*a860f6ebSGang He 			total = ret;
436*a860f6ebSGang He 			break;
437*a860f6ebSGang He 		}
438*a860f6ebSGang He 		if (ret == remain) {
439*a860f6ebSGang He 			/* snprintf() didn't fit */
440*a860f6ebSGang He 			total = -E2BIG;
441*a860f6ebSGang He 			break;
442*a860f6ebSGang He 		}
443*a860f6ebSGang He 		total += ret;
444*a860f6ebSGang He 		remain -= ret;
445*a860f6ebSGang He 	}
446*a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
447*a860f6ebSGang He 
448*a860f6ebSGang He exit:
449*a860f6ebSGang He 	ocfs2_filecheck_sysfs_put(ent);
450*a860f6ebSGang He 	return total;
451*a860f6ebSGang He }
452*a860f6ebSGang He 
453*a860f6ebSGang He static int
454*a860f6ebSGang He ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
455*a860f6ebSGang He {
456*a860f6ebSGang He 	struct ocfs2_filecheck_entry *p;
457*a860f6ebSGang He 
458*a860f6ebSGang He 	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
459*a860f6ebSGang He 		if (p->fe_done) {
460*a860f6ebSGang He 			list_del(&p->fe_list);
461*a860f6ebSGang He 			kfree(p);
462*a860f6ebSGang He 			ent->fs_fcheck->fc_size--;
463*a860f6ebSGang He 			ent->fs_fcheck->fc_done--;
464*a860f6ebSGang He 			return 1;
465*a860f6ebSGang He 		}
466*a860f6ebSGang He 	}
467*a860f6ebSGang He 
468*a860f6ebSGang He 	return 0;
469*a860f6ebSGang He }
470*a860f6ebSGang He 
471*a860f6ebSGang He static int
472*a860f6ebSGang He ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
473*a860f6ebSGang He 			      unsigned int count)
474*a860f6ebSGang He {
475*a860f6ebSGang He 	unsigned int i = 0;
476*a860f6ebSGang He 	unsigned int ret = 0;
477*a860f6ebSGang He 
478*a860f6ebSGang He 	while (i++ < count) {
479*a860f6ebSGang He 		if (ocfs2_filecheck_erase_entry(ent))
480*a860f6ebSGang He 			ret++;
481*a860f6ebSGang He 		else
482*a860f6ebSGang He 			break;
483*a860f6ebSGang He 	}
484*a860f6ebSGang He 
485*a860f6ebSGang He 	return (ret == count ? 1 : 0);
486*a860f6ebSGang He }
487*a860f6ebSGang He 
488*a860f6ebSGang He static void
489*a860f6ebSGang He ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
490*a860f6ebSGang He 			   struct ocfs2_filecheck_entry *entry)
491*a860f6ebSGang He {
492*a860f6ebSGang He 	entry->fe_done = 1;
493*a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
494*a860f6ebSGang He 	ent->fs_fcheck->fc_done++;
495*a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
496*a860f6ebSGang He }
497*a860f6ebSGang He 
498*a860f6ebSGang He static unsigned int
499*a860f6ebSGang He ocfs2_filecheck_handle(struct super_block *sb,
500*a860f6ebSGang He 		       unsigned long ino, unsigned int flags)
501*a860f6ebSGang He {
502*a860f6ebSGang He 	unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
503*a860f6ebSGang He 	struct inode *inode = NULL;
504*a860f6ebSGang He 	int rc;
505*a860f6ebSGang He 
506*a860f6ebSGang He 	inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
507*a860f6ebSGang He 	if (IS_ERR(inode)) {
508*a860f6ebSGang He 		rc = (int)(-(long)inode);
509*a860f6ebSGang He 		if (rc >= OCFS2_FILECHECK_ERR_START &&
510*a860f6ebSGang He 		    rc < OCFS2_FILECHECK_ERR_END)
511*a860f6ebSGang He 			ret = rc;
512*a860f6ebSGang He 		else
513*a860f6ebSGang He 			ret = OCFS2_FILECHECK_ERR_FAILED;
514*a860f6ebSGang He 	} else
515*a860f6ebSGang He 		iput(inode);
516*a860f6ebSGang He 
517*a860f6ebSGang He 	return ret;
518*a860f6ebSGang He }
519*a860f6ebSGang He 
520*a860f6ebSGang He static void
521*a860f6ebSGang He ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
522*a860f6ebSGang He 			     struct ocfs2_filecheck_entry *entry)
523*a860f6ebSGang He {
524*a860f6ebSGang He 	if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
525*a860f6ebSGang He 		entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
526*a860f6ebSGang He 				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
527*a860f6ebSGang He 	else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
528*a860f6ebSGang He 		entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
529*a860f6ebSGang He 				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
530*a860f6ebSGang He 	else
531*a860f6ebSGang He 		entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
532*a860f6ebSGang He 
533*a860f6ebSGang He 	ocfs2_filecheck_done_entry(ent, entry);
534*a860f6ebSGang He }
535*a860f6ebSGang He 
536*a860f6ebSGang He static ssize_t ocfs2_filecheck_store(struct kobject *kobj,
537*a860f6ebSGang He 				     struct kobj_attribute *attr,
538*a860f6ebSGang He 				     const char *buf, size_t count)
539*a860f6ebSGang He {
540*a860f6ebSGang He 	struct ocfs2_filecheck_args args;
541*a860f6ebSGang He 	struct ocfs2_filecheck_entry *entry;
542*a860f6ebSGang He 	struct ocfs2_filecheck_sysfs_entry *ent;
543*a860f6ebSGang He 	ssize_t ret = 0;
544*a860f6ebSGang He 
545*a860f6ebSGang He 	if (count == 0)
546*a860f6ebSGang He 		return count;
547*a860f6ebSGang He 
548*a860f6ebSGang He 	if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) {
549*a860f6ebSGang He 		mlog(ML_ERROR, "Invalid arguments for online file check\n");
550*a860f6ebSGang He 		return -EINVAL;
551*a860f6ebSGang He 	}
552*a860f6ebSGang He 
553*a860f6ebSGang He 	ent = ocfs2_filecheck_sysfs_get(kobj->parent->name);
554*a860f6ebSGang He 	if (!ent) {
555*a860f6ebSGang He 		mlog(ML_ERROR,
556*a860f6ebSGang He 		"Cannot get the corresponding entry via device basename %s\n",
557*a860f6ebSGang He 		kobj->parent->name);
558*a860f6ebSGang He 		return -ENODEV;
559*a860f6ebSGang He 	}
560*a860f6ebSGang He 
561*a860f6ebSGang He 	if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
562*a860f6ebSGang He 		ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
563*a860f6ebSGang He 		goto exit;
564*a860f6ebSGang He 	}
565*a860f6ebSGang He 
566*a860f6ebSGang He 	entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
567*a860f6ebSGang He 	if (!entry) {
568*a860f6ebSGang He 		ret = -ENOMEM;
569*a860f6ebSGang He 		goto exit;
570*a860f6ebSGang He 	}
571*a860f6ebSGang He 
572*a860f6ebSGang He 	spin_lock(&ent->fs_fcheck->fc_lock);
573*a860f6ebSGang He 	if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
574*a860f6ebSGang He 	    (ent->fs_fcheck->fc_done == 0)) {
575*a860f6ebSGang He 		mlog(ML_ERROR,
576*a860f6ebSGang He 		"Cannot do more file check "
577*a860f6ebSGang He 		"since file check queue(%u) is full now\n",
578*a860f6ebSGang He 		ent->fs_fcheck->fc_max);
579*a860f6ebSGang He 		ret = -EBUSY;
580*a860f6ebSGang He 		kfree(entry);
581*a860f6ebSGang He 	} else {
582*a860f6ebSGang He 		if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
583*a860f6ebSGang He 		    (ent->fs_fcheck->fc_done > 0)) {
584*a860f6ebSGang He 			/* Delete the oldest entry which was done,
585*a860f6ebSGang He 			 * make sure the entry size in list does
586*a860f6ebSGang He 			 * not exceed maximum value
587*a860f6ebSGang He 			 */
588*a860f6ebSGang He 			BUG_ON(!ocfs2_filecheck_erase_entry(ent));
589*a860f6ebSGang He 		}
590*a860f6ebSGang He 
591*a860f6ebSGang He 		entry->fe_ino = args.fa_ino;
592*a860f6ebSGang He 		entry->fe_type = args.fa_type;
593*a860f6ebSGang He 		entry->fe_done = 0;
594*a860f6ebSGang He 		entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
595*a860f6ebSGang He 		list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
596*a860f6ebSGang He 		ent->fs_fcheck->fc_size++;
597*a860f6ebSGang He 	}
598*a860f6ebSGang He 	spin_unlock(&ent->fs_fcheck->fc_lock);
599*a860f6ebSGang He 
600*a860f6ebSGang He 	if (!ret)
601*a860f6ebSGang He 		ocfs2_filecheck_handle_entry(ent, entry);
602*a860f6ebSGang He 
603*a860f6ebSGang He exit:
604*a860f6ebSGang He 	ocfs2_filecheck_sysfs_put(ent);
605*a860f6ebSGang He 	return (!ret ? count : ret);
606*a860f6ebSGang He }
607