1 // SPDX-License-Identifier: GPL-2.0-only
5 * Code which implements online file check.
7 * Copyright (C) 2016 SuSE. All rights reserved.
10 #include <linux/list.h>
11 #include <linux/spinlock.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/kmod.h>
16 #include <linux/kobject.h>
17 #include <linux/sysfs.h>
18 #include <linux/sysctl.h>
19 #include <cluster/masklog.h>
23 #include "stackglue.h"
26 #include "filecheck.h"
29 /* File check error strings,
30 * must correspond with error number in header file.
32 static const char * const ocfs2_filecheck_errs
[] = {
46 struct ocfs2_filecheck_entry
{
47 struct list_head fe_list
;
50 unsigned int fe_done
:1;
51 unsigned int fe_status
:31;
54 struct ocfs2_filecheck_args
{
63 ocfs2_filecheck_error(int errno
)
66 return ocfs2_filecheck_errs
[errno
];
68 BUG_ON(errno
< OCFS2_FILECHECK_ERR_START
||
69 errno
> OCFS2_FILECHECK_ERR_END
);
70 return ocfs2_filecheck_errs
[errno
- OCFS2_FILECHECK_ERR_START
+ 1];
73 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
74 struct kobj_attribute
*attr
,
76 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
77 struct kobj_attribute
*attr
,
78 const char *buf
, size_t count
);
79 static struct kobj_attribute ocfs2_filecheck_attr_chk
=
80 __ATTR(check
, S_IRUSR
| S_IWUSR
,
81 ocfs2_filecheck_attr_show
,
82 ocfs2_filecheck_attr_store
);
83 static struct kobj_attribute ocfs2_filecheck_attr_fix
=
84 __ATTR(fix
, S_IRUSR
| S_IWUSR
,
85 ocfs2_filecheck_attr_show
,
86 ocfs2_filecheck_attr_store
);
87 static struct kobj_attribute ocfs2_filecheck_attr_set
=
88 __ATTR(set
, S_IRUSR
| S_IWUSR
,
89 ocfs2_filecheck_attr_show
,
90 ocfs2_filecheck_attr_store
);
91 static struct attribute
*ocfs2_filecheck_attrs
[] = {
92 &ocfs2_filecheck_attr_chk
.attr
,
93 &ocfs2_filecheck_attr_fix
.attr
,
94 &ocfs2_filecheck_attr_set
.attr
,
97 ATTRIBUTE_GROUPS(ocfs2_filecheck
);
99 static void ocfs2_filecheck_release(struct kobject
*kobj
)
101 struct ocfs2_filecheck_sysfs_entry
*entry
= container_of(kobj
,
102 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
104 complete(&entry
->fs_kobj_unregister
);
108 ocfs2_filecheck_show(struct kobject
*kobj
, struct attribute
*attr
, char *buf
)
111 struct kobj_attribute
*kattr
= container_of(attr
,
112 struct kobj_attribute
, attr
);
116 ret
= kattr
->show(kobj
, kattr
, buf
);
122 ocfs2_filecheck_store(struct kobject
*kobj
, struct attribute
*attr
,
123 const char *buf
, size_t count
)
126 struct kobj_attribute
*kattr
= container_of(attr
,
127 struct kobj_attribute
, attr
);
131 ret
= kattr
->store(kobj
, kattr
, buf
, count
);
136 static const struct sysfs_ops ocfs2_filecheck_ops
= {
137 .show
= ocfs2_filecheck_show
,
138 .store
= ocfs2_filecheck_store
,
141 static struct kobj_type ocfs2_ktype_filecheck
= {
142 .default_groups
= ocfs2_filecheck_groups
,
143 .sysfs_ops
= &ocfs2_filecheck_ops
,
144 .release
= ocfs2_filecheck_release
,
148 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry
*entry
)
150 struct ocfs2_filecheck_entry
*p
;
152 spin_lock(&entry
->fs_fcheck
->fc_lock
);
153 while (!list_empty(&entry
->fs_fcheck
->fc_head
)) {
154 p
= list_first_entry(&entry
->fs_fcheck
->fc_head
,
155 struct ocfs2_filecheck_entry
, fe_list
);
156 list_del(&p
->fe_list
);
157 BUG_ON(!p
->fe_done
); /* To free a undone file check entry */
160 spin_unlock(&entry
->fs_fcheck
->fc_lock
);
162 kfree(entry
->fs_fcheck
);
163 entry
->fs_fcheck
= NULL
;
166 int ocfs2_filecheck_create_sysfs(struct ocfs2_super
*osb
)
169 struct ocfs2_filecheck
*fcheck
;
170 struct ocfs2_filecheck_sysfs_entry
*entry
= &osb
->osb_fc_ent
;
172 fcheck
= kmalloc(sizeof(struct ocfs2_filecheck
), GFP_NOFS
);
176 INIT_LIST_HEAD(&fcheck
->fc_head
);
177 spin_lock_init(&fcheck
->fc_lock
);
178 fcheck
->fc_max
= OCFS2_FILECHECK_MINSIZE
;
182 entry
->fs_kobj
.kset
= osb
->osb_dev_kset
;
183 init_completion(&entry
->fs_kobj_unregister
);
184 ret
= kobject_init_and_add(&entry
->fs_kobj
, &ocfs2_ktype_filecheck
,
187 kobject_put(&entry
->fs_kobj
);
192 entry
->fs_fcheck
= fcheck
;
196 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super
*osb
)
198 if (!osb
->osb_fc_ent
.fs_fcheck
)
201 kobject_del(&osb
->osb_fc_ent
.fs_kobj
);
202 kobject_put(&osb
->osb_fc_ent
.fs_kobj
);
203 wait_for_completion(&osb
->osb_fc_ent
.fs_kobj_unregister
);
204 ocfs2_filecheck_sysfs_free(&osb
->osb_fc_ent
);
208 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
211 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry
*ent
,
216 if ((len
< OCFS2_FILECHECK_MINSIZE
) || (len
> OCFS2_FILECHECK_MAXSIZE
))
219 spin_lock(&ent
->fs_fcheck
->fc_lock
);
220 if (len
< (ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
)) {
222 "Cannot set online file check maximum entry number "
223 "to %u due to too many pending entries(%u)\n",
224 len
, ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
);
227 if (len
< ent
->fs_fcheck
->fc_size
)
228 BUG_ON(!ocfs2_filecheck_erase_entries(ent
,
229 ent
->fs_fcheck
->fc_size
- len
));
231 ent
->fs_fcheck
->fc_max
= len
;
234 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
239 #define OCFS2_FILECHECK_ARGS_LEN 24
241 ocfs2_filecheck_args_get_long(const char *buf
, size_t count
,
244 char buffer
[OCFS2_FILECHECK_ARGS_LEN
];
246 memcpy(buffer
, buf
, count
);
247 buffer
[count
] = '\0';
249 if (kstrtoul(buffer
, 0, val
))
256 ocfs2_filecheck_type_parse(const char *name
, unsigned int *type
)
258 if (!strncmp(name
, "fix", 4))
259 *type
= OCFS2_FILECHECK_TYPE_FIX
;
260 else if (!strncmp(name
, "check", 6))
261 *type
= OCFS2_FILECHECK_TYPE_CHK
;
262 else if (!strncmp(name
, "set", 4))
263 *type
= OCFS2_FILECHECK_TYPE_SET
;
271 ocfs2_filecheck_args_parse(const char *name
, const char *buf
, size_t count
,
272 struct ocfs2_filecheck_args
*args
)
274 unsigned long val
= 0;
277 /* too short/long args length */
278 if ((count
< 1) || (count
>= OCFS2_FILECHECK_ARGS_LEN
))
281 if (ocfs2_filecheck_type_parse(name
, &type
))
283 if (ocfs2_filecheck_args_get_long(buf
, count
, &val
))
289 args
->fa_type
= type
;
290 if (type
== OCFS2_FILECHECK_TYPE_SET
)
291 args
->fa_len
= (unsigned int)val
;
298 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
299 struct kobj_attribute
*attr
,
303 ssize_t ret
= 0, total
= 0, remain
= PAGE_SIZE
;
305 struct ocfs2_filecheck_entry
*p
;
306 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
307 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
309 if (ocfs2_filecheck_type_parse(attr
->attr
.name
, &type
))
312 if (type
== OCFS2_FILECHECK_TYPE_SET
) {
313 spin_lock(&ent
->fs_fcheck
->fc_lock
);
314 total
= snprintf(buf
, remain
, "%u\n", ent
->fs_fcheck
->fc_max
);
315 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
319 ret
= snprintf(buf
, remain
, "INO\t\tDONE\tERROR\n");
322 spin_lock(&ent
->fs_fcheck
->fc_lock
);
323 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
324 if (p
->fe_type
!= type
)
327 ret
= snprintf(buf
+ total
, remain
, "%lu\t\t%u\t%s\n",
328 p
->fe_ino
, p
->fe_done
,
329 ocfs2_filecheck_error(p
->fe_status
));
331 /* snprintf() didn't fit */
338 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
345 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
348 struct ocfs2_filecheck_entry
*p
;
350 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
352 if (p
->fe_ino
== ino
)
361 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry
*ent
)
363 struct ocfs2_filecheck_entry
*p
;
365 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
367 list_del(&p
->fe_list
);
369 ent
->fs_fcheck
->fc_size
--;
370 ent
->fs_fcheck
->fc_done
--;
379 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
383 unsigned int ret
= 0;
385 while (i
++ < count
) {
386 if (ocfs2_filecheck_erase_entry(ent
))
392 return (ret
== count
? 1 : 0);
396 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
397 struct ocfs2_filecheck_entry
*entry
)
399 spin_lock(&ent
->fs_fcheck
->fc_lock
);
401 ent
->fs_fcheck
->fc_done
++;
402 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
406 ocfs2_filecheck_handle(struct ocfs2_super
*osb
,
407 unsigned long ino
, unsigned int flags
)
409 unsigned int ret
= OCFS2_FILECHECK_ERR_SUCCESS
;
410 struct inode
*inode
= NULL
;
413 inode
= ocfs2_iget(osb
, ino
, flags
, 0);
415 rc
= (int)(-(long)inode
);
416 if (rc
>= OCFS2_FILECHECK_ERR_START
&&
417 rc
< OCFS2_FILECHECK_ERR_END
)
420 ret
= OCFS2_FILECHECK_ERR_FAILED
;
428 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
429 struct ocfs2_filecheck_entry
*entry
)
431 struct ocfs2_super
*osb
= container_of(ent
, struct ocfs2_super
,
434 if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_CHK
)
435 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
436 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_CHK
);
437 else if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_FIX
)
438 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
439 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_FIX
);
441 entry
->fe_status
= OCFS2_FILECHECK_ERR_UNSUPPORTED
;
443 ocfs2_filecheck_done_entry(ent
, entry
);
446 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
447 struct kobj_attribute
*attr
,
448 const char *buf
, size_t count
)
451 struct ocfs2_filecheck_args args
;
452 struct ocfs2_filecheck_entry
*entry
;
453 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
454 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
459 if (ocfs2_filecheck_args_parse(attr
->attr
.name
, buf
, count
, &args
))
462 if (args
.fa_type
== OCFS2_FILECHECK_TYPE_SET
) {
463 ret
= ocfs2_filecheck_adjust_max(ent
, args
.fa_len
);
467 entry
= kmalloc(sizeof(struct ocfs2_filecheck_entry
), GFP_NOFS
);
473 spin_lock(&ent
->fs_fcheck
->fc_lock
);
474 if (ocfs2_filecheck_is_dup_entry(ent
, args
.fa_ino
)) {
477 } else if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
478 (ent
->fs_fcheck
->fc_done
== 0)) {
480 "Cannot do more file check "
481 "since file check queue(%u) is full now\n",
482 ent
->fs_fcheck
->fc_max
);
486 if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
487 (ent
->fs_fcheck
->fc_done
> 0)) {
488 /* Delete the oldest entry which was done,
489 * make sure the entry size in list does
490 * not exceed maximum value
492 BUG_ON(!ocfs2_filecheck_erase_entry(ent
));
495 entry
->fe_ino
= args
.fa_ino
;
496 entry
->fe_type
= args
.fa_type
;
498 entry
->fe_status
= OCFS2_FILECHECK_ERR_INPROGRESS
;
499 list_add_tail(&entry
->fe_list
, &ent
->fs_fcheck
->fc_head
);
500 ent
->fs_fcheck
->fc_size
++;
502 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
505 ocfs2_filecheck_handle_entry(ent
, entry
);
508 return (!ret
? count
: ret
);