1 // SPDX-License-Identifier: GPL-2.0-only
2 /* -*- mode: c; c-basic-offset: 8; -*-
3 * vim: noexpandtab sw=8 ts=8 sts=0:
7 * Code which implements online file check.
9 * Copyright (C) 2016 SuSE. All rights reserved.
12 #include <linux/list.h>
13 #include <linux/spinlock.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/kmod.h>
18 #include <linux/kobject.h>
19 #include <linux/sysfs.h>
20 #include <linux/sysctl.h>
21 #include <cluster/masklog.h>
25 #include "stackglue.h"
28 #include "filecheck.h"
31 /* File check error strings,
32 * must correspond with error number in header file.
34 static const char * const ocfs2_filecheck_errs
[] = {
48 struct ocfs2_filecheck_entry
{
49 struct list_head fe_list
;
52 unsigned int fe_done
:1;
53 unsigned int fe_status
:31;
56 struct ocfs2_filecheck_args
{
65 ocfs2_filecheck_error(int errno
)
68 return ocfs2_filecheck_errs
[errno
];
70 BUG_ON(errno
< OCFS2_FILECHECK_ERR_START
||
71 errno
> OCFS2_FILECHECK_ERR_END
);
72 return ocfs2_filecheck_errs
[errno
- OCFS2_FILECHECK_ERR_START
+ 1];
75 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
76 struct kobj_attribute
*attr
,
78 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
79 struct kobj_attribute
*attr
,
80 const char *buf
, size_t count
);
81 static struct kobj_attribute ocfs2_filecheck_attr_chk
=
82 __ATTR(check
, S_IRUSR
| S_IWUSR
,
83 ocfs2_filecheck_attr_show
,
84 ocfs2_filecheck_attr_store
);
85 static struct kobj_attribute ocfs2_filecheck_attr_fix
=
86 __ATTR(fix
, S_IRUSR
| S_IWUSR
,
87 ocfs2_filecheck_attr_show
,
88 ocfs2_filecheck_attr_store
);
89 static struct kobj_attribute ocfs2_filecheck_attr_set
=
90 __ATTR(set
, S_IRUSR
| S_IWUSR
,
91 ocfs2_filecheck_attr_show
,
92 ocfs2_filecheck_attr_store
);
93 static struct attribute
*ocfs2_filecheck_attrs
[] = {
94 &ocfs2_filecheck_attr_chk
.attr
,
95 &ocfs2_filecheck_attr_fix
.attr
,
96 &ocfs2_filecheck_attr_set
.attr
,
100 static void ocfs2_filecheck_release(struct kobject
*kobj
)
102 struct ocfs2_filecheck_sysfs_entry
*entry
= container_of(kobj
,
103 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
105 complete(&entry
->fs_kobj_unregister
);
109 ocfs2_filecheck_show(struct kobject
*kobj
, struct attribute
*attr
, char *buf
)
112 struct kobj_attribute
*kattr
= container_of(attr
,
113 struct kobj_attribute
, attr
);
117 ret
= kattr
->show(kobj
, kattr
, buf
);
123 ocfs2_filecheck_store(struct kobject
*kobj
, struct attribute
*attr
,
124 const char *buf
, size_t count
)
127 struct kobj_attribute
*kattr
= container_of(attr
,
128 struct kobj_attribute
, attr
);
132 ret
= kattr
->store(kobj
, kattr
, buf
, count
);
137 static const struct sysfs_ops ocfs2_filecheck_ops
= {
138 .show
= ocfs2_filecheck_show
,
139 .store
= ocfs2_filecheck_store
,
142 static struct kobj_type ocfs2_ktype_filecheck
= {
143 .default_attrs
= ocfs2_filecheck_attrs
,
144 .sysfs_ops
= &ocfs2_filecheck_ops
,
145 .release
= ocfs2_filecheck_release
,
149 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry
*entry
)
151 struct ocfs2_filecheck_entry
*p
;
153 spin_lock(&entry
->fs_fcheck
->fc_lock
);
154 while (!list_empty(&entry
->fs_fcheck
->fc_head
)) {
155 p
= list_first_entry(&entry
->fs_fcheck
->fc_head
,
156 struct ocfs2_filecheck_entry
, fe_list
);
157 list_del(&p
->fe_list
);
158 BUG_ON(!p
->fe_done
); /* To free a undone file check entry */
161 spin_unlock(&entry
->fs_fcheck
->fc_lock
);
163 kfree(entry
->fs_fcheck
);
164 entry
->fs_fcheck
= NULL
;
167 int ocfs2_filecheck_create_sysfs(struct ocfs2_super
*osb
)
170 struct ocfs2_filecheck
*fcheck
;
171 struct ocfs2_filecheck_sysfs_entry
*entry
= &osb
->osb_fc_ent
;
173 fcheck
= kmalloc(sizeof(struct ocfs2_filecheck
), GFP_NOFS
);
177 INIT_LIST_HEAD(&fcheck
->fc_head
);
178 spin_lock_init(&fcheck
->fc_lock
);
179 fcheck
->fc_max
= OCFS2_FILECHECK_MINSIZE
;
183 entry
->fs_kobj
.kset
= osb
->osb_dev_kset
;
184 init_completion(&entry
->fs_kobj_unregister
);
185 ret
= kobject_init_and_add(&entry
->fs_kobj
, &ocfs2_ktype_filecheck
,
188 kobject_put(&entry
->fs_kobj
);
193 entry
->fs_fcheck
= fcheck
;
197 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super
*osb
)
199 if (!osb
->osb_fc_ent
.fs_fcheck
)
202 kobject_del(&osb
->osb_fc_ent
.fs_kobj
);
203 kobject_put(&osb
->osb_fc_ent
.fs_kobj
);
204 wait_for_completion(&osb
->osb_fc_ent
.fs_kobj_unregister
);
205 ocfs2_filecheck_sysfs_free(&osb
->osb_fc_ent
);
209 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
212 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry
*ent
,
217 if ((len
< OCFS2_FILECHECK_MINSIZE
) || (len
> OCFS2_FILECHECK_MAXSIZE
))
220 spin_lock(&ent
->fs_fcheck
->fc_lock
);
221 if (len
< (ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
)) {
223 "Cannot set online file check maximum entry number "
224 "to %u due to too many pending entries(%u)\n",
225 len
, ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
);
228 if (len
< ent
->fs_fcheck
->fc_size
)
229 BUG_ON(!ocfs2_filecheck_erase_entries(ent
,
230 ent
->fs_fcheck
->fc_size
- len
));
232 ent
->fs_fcheck
->fc_max
= len
;
235 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
240 #define OCFS2_FILECHECK_ARGS_LEN 24
242 ocfs2_filecheck_args_get_long(const char *buf
, size_t count
,
245 char buffer
[OCFS2_FILECHECK_ARGS_LEN
];
247 memcpy(buffer
, buf
, count
);
248 buffer
[count
] = '\0';
250 if (kstrtoul(buffer
, 0, val
))
257 ocfs2_filecheck_type_parse(const char *name
, unsigned int *type
)
259 if (!strncmp(name
, "fix", 4))
260 *type
= OCFS2_FILECHECK_TYPE_FIX
;
261 else if (!strncmp(name
, "check", 6))
262 *type
= OCFS2_FILECHECK_TYPE_CHK
;
263 else if (!strncmp(name
, "set", 4))
264 *type
= OCFS2_FILECHECK_TYPE_SET
;
272 ocfs2_filecheck_args_parse(const char *name
, const char *buf
, size_t count
,
273 struct ocfs2_filecheck_args
*args
)
275 unsigned long val
= 0;
278 /* too short/long args length */
279 if ((count
< 1) || (count
>= OCFS2_FILECHECK_ARGS_LEN
))
282 if (ocfs2_filecheck_type_parse(name
, &type
))
284 if (ocfs2_filecheck_args_get_long(buf
, count
, &val
))
290 args
->fa_type
= type
;
291 if (type
== OCFS2_FILECHECK_TYPE_SET
)
292 args
->fa_len
= (unsigned int)val
;
299 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
300 struct kobj_attribute
*attr
,
304 ssize_t ret
= 0, total
= 0, remain
= PAGE_SIZE
;
306 struct ocfs2_filecheck_entry
*p
;
307 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
308 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
310 if (ocfs2_filecheck_type_parse(attr
->attr
.name
, &type
))
313 if (type
== OCFS2_FILECHECK_TYPE_SET
) {
314 spin_lock(&ent
->fs_fcheck
->fc_lock
);
315 total
= snprintf(buf
, remain
, "%u\n", ent
->fs_fcheck
->fc_max
);
316 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
320 ret
= snprintf(buf
, remain
, "INO\t\tDONE\tERROR\n");
323 spin_lock(&ent
->fs_fcheck
->fc_lock
);
324 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
325 if (p
->fe_type
!= type
)
328 ret
= snprintf(buf
+ total
, remain
, "%lu\t\t%u\t%s\n",
329 p
->fe_ino
, p
->fe_done
,
330 ocfs2_filecheck_error(p
->fe_status
));
336 /* snprintf() didn't fit */
343 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
350 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
353 struct ocfs2_filecheck_entry
*p
;
355 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
357 if (p
->fe_ino
== ino
)
366 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry
*ent
)
368 struct ocfs2_filecheck_entry
*p
;
370 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
372 list_del(&p
->fe_list
);
374 ent
->fs_fcheck
->fc_size
--;
375 ent
->fs_fcheck
->fc_done
--;
384 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
388 unsigned int ret
= 0;
390 while (i
++ < count
) {
391 if (ocfs2_filecheck_erase_entry(ent
))
397 return (ret
== count
? 1 : 0);
401 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
402 struct ocfs2_filecheck_entry
*entry
)
404 spin_lock(&ent
->fs_fcheck
->fc_lock
);
406 ent
->fs_fcheck
->fc_done
++;
407 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
411 ocfs2_filecheck_handle(struct ocfs2_super
*osb
,
412 unsigned long ino
, unsigned int flags
)
414 unsigned int ret
= OCFS2_FILECHECK_ERR_SUCCESS
;
415 struct inode
*inode
= NULL
;
418 inode
= ocfs2_iget(osb
, ino
, flags
, 0);
420 rc
= (int)(-(long)inode
);
421 if (rc
>= OCFS2_FILECHECK_ERR_START
&&
422 rc
< OCFS2_FILECHECK_ERR_END
)
425 ret
= OCFS2_FILECHECK_ERR_FAILED
;
433 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
434 struct ocfs2_filecheck_entry
*entry
)
436 struct ocfs2_super
*osb
= container_of(ent
, struct ocfs2_super
,
439 if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_CHK
)
440 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
441 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_CHK
);
442 else if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_FIX
)
443 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
444 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_FIX
);
446 entry
->fe_status
= OCFS2_FILECHECK_ERR_UNSUPPORTED
;
448 ocfs2_filecheck_done_entry(ent
, entry
);
451 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
452 struct kobj_attribute
*attr
,
453 const char *buf
, size_t count
)
456 struct ocfs2_filecheck_args args
;
457 struct ocfs2_filecheck_entry
*entry
;
458 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
459 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
464 if (ocfs2_filecheck_args_parse(attr
->attr
.name
, buf
, count
, &args
))
467 if (args
.fa_type
== OCFS2_FILECHECK_TYPE_SET
) {
468 ret
= ocfs2_filecheck_adjust_max(ent
, args
.fa_len
);
472 entry
= kmalloc(sizeof(struct ocfs2_filecheck_entry
), GFP_NOFS
);
478 spin_lock(&ent
->fs_fcheck
->fc_lock
);
479 if (ocfs2_filecheck_is_dup_entry(ent
, args
.fa_ino
)) {
482 } else if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
483 (ent
->fs_fcheck
->fc_done
== 0)) {
485 "Cannot do more file check "
486 "since file check queue(%u) is full now\n",
487 ent
->fs_fcheck
->fc_max
);
491 if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
492 (ent
->fs_fcheck
->fc_done
> 0)) {
493 /* Delete the oldest entry which was done,
494 * make sure the entry size in list does
495 * not exceed maximum value
497 BUG_ON(!ocfs2_filecheck_erase_entry(ent
));
500 entry
->fe_ino
= args
.fa_ino
;
501 entry
->fe_type
= args
.fa_type
;
503 entry
->fe_status
= OCFS2_FILECHECK_ERR_INPROGRESS
;
504 list_add_tail(&entry
->fe_list
, &ent
->fs_fcheck
->fc_head
);
505 ent
->fs_fcheck
->fc_size
++;
507 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
510 ocfs2_filecheck_handle_entry(ent
, entry
);
513 return (!ret
? count
: ret
);