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
,
98 static void ocfs2_filecheck_release(struct kobject
*kobj
)
100 struct ocfs2_filecheck_sysfs_entry
*entry
= container_of(kobj
,
101 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
103 complete(&entry
->fs_kobj_unregister
);
107 ocfs2_filecheck_show(struct kobject
*kobj
, struct attribute
*attr
, char *buf
)
110 struct kobj_attribute
*kattr
= container_of(attr
,
111 struct kobj_attribute
, attr
);
115 ret
= kattr
->show(kobj
, kattr
, buf
);
121 ocfs2_filecheck_store(struct kobject
*kobj
, struct attribute
*attr
,
122 const char *buf
, size_t count
)
125 struct kobj_attribute
*kattr
= container_of(attr
,
126 struct kobj_attribute
, attr
);
130 ret
= kattr
->store(kobj
, kattr
, buf
, count
);
135 static const struct sysfs_ops ocfs2_filecheck_ops
= {
136 .show
= ocfs2_filecheck_show
,
137 .store
= ocfs2_filecheck_store
,
140 static struct kobj_type ocfs2_ktype_filecheck
= {
141 .default_attrs
= ocfs2_filecheck_attrs
,
142 .sysfs_ops
= &ocfs2_filecheck_ops
,
143 .release
= ocfs2_filecheck_release
,
147 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry
*entry
)
149 struct ocfs2_filecheck_entry
*p
;
151 spin_lock(&entry
->fs_fcheck
->fc_lock
);
152 while (!list_empty(&entry
->fs_fcheck
->fc_head
)) {
153 p
= list_first_entry(&entry
->fs_fcheck
->fc_head
,
154 struct ocfs2_filecheck_entry
, fe_list
);
155 list_del(&p
->fe_list
);
156 BUG_ON(!p
->fe_done
); /* To free a undone file check entry */
159 spin_unlock(&entry
->fs_fcheck
->fc_lock
);
161 kfree(entry
->fs_fcheck
);
162 entry
->fs_fcheck
= NULL
;
165 int ocfs2_filecheck_create_sysfs(struct ocfs2_super
*osb
)
168 struct ocfs2_filecheck
*fcheck
;
169 struct ocfs2_filecheck_sysfs_entry
*entry
= &osb
->osb_fc_ent
;
171 fcheck
= kmalloc(sizeof(struct ocfs2_filecheck
), GFP_NOFS
);
175 INIT_LIST_HEAD(&fcheck
->fc_head
);
176 spin_lock_init(&fcheck
->fc_lock
);
177 fcheck
->fc_max
= OCFS2_FILECHECK_MINSIZE
;
181 entry
->fs_kobj
.kset
= osb
->osb_dev_kset
;
182 init_completion(&entry
->fs_kobj_unregister
);
183 ret
= kobject_init_and_add(&entry
->fs_kobj
, &ocfs2_ktype_filecheck
,
186 kobject_put(&entry
->fs_kobj
);
191 entry
->fs_fcheck
= fcheck
;
195 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super
*osb
)
197 if (!osb
->osb_fc_ent
.fs_fcheck
)
200 kobject_del(&osb
->osb_fc_ent
.fs_kobj
);
201 kobject_put(&osb
->osb_fc_ent
.fs_kobj
);
202 wait_for_completion(&osb
->osb_fc_ent
.fs_kobj_unregister
);
203 ocfs2_filecheck_sysfs_free(&osb
->osb_fc_ent
);
207 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
210 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry
*ent
,
215 if ((len
< OCFS2_FILECHECK_MINSIZE
) || (len
> OCFS2_FILECHECK_MAXSIZE
))
218 spin_lock(&ent
->fs_fcheck
->fc_lock
);
219 if (len
< (ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
)) {
221 "Cannot set online file check maximum entry number "
222 "to %u due to too many pending entries(%u)\n",
223 len
, ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
);
226 if (len
< ent
->fs_fcheck
->fc_size
)
227 BUG_ON(!ocfs2_filecheck_erase_entries(ent
,
228 ent
->fs_fcheck
->fc_size
- len
));
230 ent
->fs_fcheck
->fc_max
= len
;
233 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
238 #define OCFS2_FILECHECK_ARGS_LEN 24
240 ocfs2_filecheck_args_get_long(const char *buf
, size_t count
,
243 char buffer
[OCFS2_FILECHECK_ARGS_LEN
];
245 memcpy(buffer
, buf
, count
);
246 buffer
[count
] = '\0';
248 if (kstrtoul(buffer
, 0, val
))
255 ocfs2_filecheck_type_parse(const char *name
, unsigned int *type
)
257 if (!strncmp(name
, "fix", 4))
258 *type
= OCFS2_FILECHECK_TYPE_FIX
;
259 else if (!strncmp(name
, "check", 6))
260 *type
= OCFS2_FILECHECK_TYPE_CHK
;
261 else if (!strncmp(name
, "set", 4))
262 *type
= OCFS2_FILECHECK_TYPE_SET
;
270 ocfs2_filecheck_args_parse(const char *name
, const char *buf
, size_t count
,
271 struct ocfs2_filecheck_args
*args
)
273 unsigned long val
= 0;
276 /* too short/long args length */
277 if ((count
< 1) || (count
>= OCFS2_FILECHECK_ARGS_LEN
))
280 if (ocfs2_filecheck_type_parse(name
, &type
))
282 if (ocfs2_filecheck_args_get_long(buf
, count
, &val
))
288 args
->fa_type
= type
;
289 if (type
== OCFS2_FILECHECK_TYPE_SET
)
290 args
->fa_len
= (unsigned int)val
;
297 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
298 struct kobj_attribute
*attr
,
302 ssize_t ret
= 0, total
= 0, remain
= PAGE_SIZE
;
304 struct ocfs2_filecheck_entry
*p
;
305 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
306 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
308 if (ocfs2_filecheck_type_parse(attr
->attr
.name
, &type
))
311 if (type
== OCFS2_FILECHECK_TYPE_SET
) {
312 spin_lock(&ent
->fs_fcheck
->fc_lock
);
313 total
= snprintf(buf
, remain
, "%u\n", ent
->fs_fcheck
->fc_max
);
314 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
318 ret
= snprintf(buf
, remain
, "INO\t\tDONE\tERROR\n");
321 spin_lock(&ent
->fs_fcheck
->fc_lock
);
322 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
323 if (p
->fe_type
!= type
)
326 ret
= snprintf(buf
+ total
, remain
, "%lu\t\t%u\t%s\n",
327 p
->fe_ino
, p
->fe_done
,
328 ocfs2_filecheck_error(p
->fe_status
));
330 /* snprintf() didn't fit */
337 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
344 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
347 struct ocfs2_filecheck_entry
*p
;
349 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
351 if (p
->fe_ino
== ino
)
360 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry
*ent
)
362 struct ocfs2_filecheck_entry
*p
;
364 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
366 list_del(&p
->fe_list
);
368 ent
->fs_fcheck
->fc_size
--;
369 ent
->fs_fcheck
->fc_done
--;
378 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
382 unsigned int ret
= 0;
384 while (i
++ < count
) {
385 if (ocfs2_filecheck_erase_entry(ent
))
391 return (ret
== count
? 1 : 0);
395 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
396 struct ocfs2_filecheck_entry
*entry
)
398 spin_lock(&ent
->fs_fcheck
->fc_lock
);
400 ent
->fs_fcheck
->fc_done
++;
401 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
405 ocfs2_filecheck_handle(struct ocfs2_super
*osb
,
406 unsigned long ino
, unsigned int flags
)
408 unsigned int ret
= OCFS2_FILECHECK_ERR_SUCCESS
;
409 struct inode
*inode
= NULL
;
412 inode
= ocfs2_iget(osb
, ino
, flags
, 0);
414 rc
= (int)(-(long)inode
);
415 if (rc
>= OCFS2_FILECHECK_ERR_START
&&
416 rc
< OCFS2_FILECHECK_ERR_END
)
419 ret
= OCFS2_FILECHECK_ERR_FAILED
;
427 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
428 struct ocfs2_filecheck_entry
*entry
)
430 struct ocfs2_super
*osb
= container_of(ent
, struct ocfs2_super
,
433 if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_CHK
)
434 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
435 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_CHK
);
436 else if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_FIX
)
437 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
438 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_FIX
);
440 entry
->fe_status
= OCFS2_FILECHECK_ERR_UNSUPPORTED
;
442 ocfs2_filecheck_done_entry(ent
, entry
);
445 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
446 struct kobj_attribute
*attr
,
447 const char *buf
, size_t count
)
450 struct ocfs2_filecheck_args args
;
451 struct ocfs2_filecheck_entry
*entry
;
452 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
453 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
458 if (ocfs2_filecheck_args_parse(attr
->attr
.name
, buf
, count
, &args
))
461 if (args
.fa_type
== OCFS2_FILECHECK_TYPE_SET
) {
462 ret
= ocfs2_filecheck_adjust_max(ent
, args
.fa_len
);
466 entry
= kmalloc(sizeof(struct ocfs2_filecheck_entry
), GFP_NOFS
);
472 spin_lock(&ent
->fs_fcheck
->fc_lock
);
473 if (ocfs2_filecheck_is_dup_entry(ent
, args
.fa_ino
)) {
476 } else if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
477 (ent
->fs_fcheck
->fc_done
== 0)) {
479 "Cannot do more file check "
480 "since file check queue(%u) is full now\n",
481 ent
->fs_fcheck
->fc_max
);
485 if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
486 (ent
->fs_fcheck
->fc_done
> 0)) {
487 /* Delete the oldest entry which was done,
488 * make sure the entry size in list does
489 * not exceed maximum value
491 BUG_ON(!ocfs2_filecheck_erase_entry(ent
));
494 entry
->fe_ino
= args
.fa_ino
;
495 entry
->fe_type
= args
.fa_type
;
497 entry
->fe_status
= OCFS2_FILECHECK_ERR_INPROGRESS
;
498 list_add_tail(&entry
->fe_list
, &ent
->fs_fcheck
->fc_head
);
499 ent
->fs_fcheck
->fc_size
++;
501 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
504 ocfs2_filecheck_handle_entry(ent
, entry
);
507 return (!ret
? count
: ret
);