1 /* -*- mode: c; c-basic-offset: 8; -*-
2 * vim: noexpandtab sw=8 ts=8 sts=0:
6 * Code which implements online file check.
8 * Copyright (C) 2016 SuSE. All rights reserved.
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public
12 * License as published by the Free Software Foundation, version 2.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
20 #include <linux/list.h>
21 #include <linux/spinlock.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
24 #include <linux/kmod.h>
26 #include <linux/kobject.h>
27 #include <linux/sysfs.h>
28 #include <linux/sysctl.h>
29 #include <cluster/masklog.h>
33 #include "stackglue.h"
36 #include "filecheck.h"
39 /* File check error strings,
40 * must correspond with error number in header file.
42 static const char * const ocfs2_filecheck_errs
[] = {
56 struct ocfs2_filecheck_entry
{
57 struct list_head fe_list
;
60 unsigned int fe_done
:1;
61 unsigned int fe_status
:31;
64 struct ocfs2_filecheck_args
{
73 ocfs2_filecheck_error(int errno
)
76 return ocfs2_filecheck_errs
[errno
];
78 BUG_ON(errno
< OCFS2_FILECHECK_ERR_START
||
79 errno
> OCFS2_FILECHECK_ERR_END
);
80 return ocfs2_filecheck_errs
[errno
- OCFS2_FILECHECK_ERR_START
+ 1];
83 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
84 struct kobj_attribute
*attr
,
86 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
87 struct kobj_attribute
*attr
,
88 const char *buf
, size_t count
);
89 static struct kobj_attribute ocfs2_filecheck_attr_chk
=
90 __ATTR(check
, S_IRUSR
| S_IWUSR
,
91 ocfs2_filecheck_attr_show
,
92 ocfs2_filecheck_attr_store
);
93 static struct kobj_attribute ocfs2_filecheck_attr_fix
=
94 __ATTR(fix
, S_IRUSR
| S_IWUSR
,
95 ocfs2_filecheck_attr_show
,
96 ocfs2_filecheck_attr_store
);
97 static struct kobj_attribute ocfs2_filecheck_attr_set
=
98 __ATTR(set
, S_IRUSR
| S_IWUSR
,
99 ocfs2_filecheck_attr_show
,
100 ocfs2_filecheck_attr_store
);
101 static struct attribute
*ocfs2_filecheck_attrs
[] = {
102 &ocfs2_filecheck_attr_chk
.attr
,
103 &ocfs2_filecheck_attr_fix
.attr
,
104 &ocfs2_filecheck_attr_set
.attr
,
108 static void ocfs2_filecheck_release(struct kobject
*kobj
)
110 struct ocfs2_filecheck_sysfs_entry
*entry
= container_of(kobj
,
111 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
113 complete(&entry
->fs_kobj_unregister
);
117 ocfs2_filecheck_show(struct kobject
*kobj
, struct attribute
*attr
, char *buf
)
120 struct kobj_attribute
*kattr
= container_of(attr
,
121 struct kobj_attribute
, attr
);
125 ret
= kattr
->show(kobj
, kattr
, buf
);
131 ocfs2_filecheck_store(struct kobject
*kobj
, struct attribute
*attr
,
132 const char *buf
, size_t count
)
135 struct kobj_attribute
*kattr
= container_of(attr
,
136 struct kobj_attribute
, attr
);
140 ret
= kattr
->store(kobj
, kattr
, buf
, count
);
145 static const struct sysfs_ops ocfs2_filecheck_ops
= {
146 .show
= ocfs2_filecheck_show
,
147 .store
= ocfs2_filecheck_store
,
150 static struct kobj_type ocfs2_ktype_filecheck
= {
151 .default_attrs
= ocfs2_filecheck_attrs
,
152 .sysfs_ops
= &ocfs2_filecheck_ops
,
153 .release
= ocfs2_filecheck_release
,
157 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry
*entry
)
159 struct ocfs2_filecheck_entry
*p
;
161 spin_lock(&entry
->fs_fcheck
->fc_lock
);
162 while (!list_empty(&entry
->fs_fcheck
->fc_head
)) {
163 p
= list_first_entry(&entry
->fs_fcheck
->fc_head
,
164 struct ocfs2_filecheck_entry
, fe_list
);
165 list_del(&p
->fe_list
);
166 BUG_ON(!p
->fe_done
); /* To free a undone file check entry */
169 spin_unlock(&entry
->fs_fcheck
->fc_lock
);
171 kfree(entry
->fs_fcheck
);
172 entry
->fs_fcheck
= NULL
;
175 int ocfs2_filecheck_create_sysfs(struct ocfs2_super
*osb
)
178 struct ocfs2_filecheck
*fcheck
;
179 struct ocfs2_filecheck_sysfs_entry
*entry
= &osb
->osb_fc_ent
;
181 fcheck
= kmalloc(sizeof(struct ocfs2_filecheck
), GFP_NOFS
);
185 INIT_LIST_HEAD(&fcheck
->fc_head
);
186 spin_lock_init(&fcheck
->fc_lock
);
187 fcheck
->fc_max
= OCFS2_FILECHECK_MINSIZE
;
191 entry
->fs_kobj
.kset
= osb
->osb_dev_kset
;
192 init_completion(&entry
->fs_kobj_unregister
);
193 ret
= kobject_init_and_add(&entry
->fs_kobj
, &ocfs2_ktype_filecheck
,
196 kobject_put(&entry
->fs_kobj
);
201 entry
->fs_fcheck
= fcheck
;
205 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super
*osb
)
207 if (!osb
->osb_fc_ent
.fs_fcheck
)
210 kobject_del(&osb
->osb_fc_ent
.fs_kobj
);
211 kobject_put(&osb
->osb_fc_ent
.fs_kobj
);
212 wait_for_completion(&osb
->osb_fc_ent
.fs_kobj_unregister
);
213 ocfs2_filecheck_sysfs_free(&osb
->osb_fc_ent
);
217 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
220 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry
*ent
,
225 if ((len
< OCFS2_FILECHECK_MINSIZE
) || (len
> OCFS2_FILECHECK_MAXSIZE
))
228 spin_lock(&ent
->fs_fcheck
->fc_lock
);
229 if (len
< (ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
)) {
231 "Cannot set online file check maximum entry number "
232 "to %u due to too many pending entries(%u)\n",
233 len
, ent
->fs_fcheck
->fc_size
- ent
->fs_fcheck
->fc_done
);
236 if (len
< ent
->fs_fcheck
->fc_size
)
237 BUG_ON(!ocfs2_filecheck_erase_entries(ent
,
238 ent
->fs_fcheck
->fc_size
- len
));
240 ent
->fs_fcheck
->fc_max
= len
;
243 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
248 #define OCFS2_FILECHECK_ARGS_LEN 24
250 ocfs2_filecheck_args_get_long(const char *buf
, size_t count
,
253 char buffer
[OCFS2_FILECHECK_ARGS_LEN
];
255 memcpy(buffer
, buf
, count
);
256 buffer
[count
] = '\0';
258 if (kstrtoul(buffer
, 0, val
))
265 ocfs2_filecheck_type_parse(const char *name
, unsigned int *type
)
267 if (!strncmp(name
, "fix", 4))
268 *type
= OCFS2_FILECHECK_TYPE_FIX
;
269 else if (!strncmp(name
, "check", 6))
270 *type
= OCFS2_FILECHECK_TYPE_CHK
;
271 else if (!strncmp(name
, "set", 4))
272 *type
= OCFS2_FILECHECK_TYPE_SET
;
280 ocfs2_filecheck_args_parse(const char *name
, const char *buf
, size_t count
,
281 struct ocfs2_filecheck_args
*args
)
283 unsigned long val
= 0;
286 /* too short/long args length */
287 if ((count
< 1) || (count
>= OCFS2_FILECHECK_ARGS_LEN
))
290 if (ocfs2_filecheck_type_parse(name
, &type
))
292 if (ocfs2_filecheck_args_get_long(buf
, count
, &val
))
298 args
->fa_type
= type
;
299 if (type
== OCFS2_FILECHECK_TYPE_SET
)
300 args
->fa_len
= (unsigned int)val
;
307 static ssize_t
ocfs2_filecheck_attr_show(struct kobject
*kobj
,
308 struct kobj_attribute
*attr
,
312 ssize_t ret
= 0, total
= 0, remain
= PAGE_SIZE
;
314 struct ocfs2_filecheck_entry
*p
;
315 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
316 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
318 if (ocfs2_filecheck_type_parse(attr
->attr
.name
, &type
))
321 if (type
== OCFS2_FILECHECK_TYPE_SET
) {
322 spin_lock(&ent
->fs_fcheck
->fc_lock
);
323 total
= snprintf(buf
, remain
, "%u\n", ent
->fs_fcheck
->fc_max
);
324 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
328 ret
= snprintf(buf
, remain
, "INO\t\tDONE\tERROR\n");
331 spin_lock(&ent
->fs_fcheck
->fc_lock
);
332 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
333 if (p
->fe_type
!= type
)
336 ret
= snprintf(buf
+ total
, remain
, "%lu\t\t%u\t%s\n",
337 p
->fe_ino
, p
->fe_done
,
338 ocfs2_filecheck_error(p
->fe_status
));
344 /* snprintf() didn't fit */
351 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
358 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
361 struct ocfs2_filecheck_entry
*p
;
363 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
365 if (p
->fe_ino
== ino
)
374 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry
*ent
)
376 struct ocfs2_filecheck_entry
*p
;
378 list_for_each_entry(p
, &ent
->fs_fcheck
->fc_head
, fe_list
) {
380 list_del(&p
->fe_list
);
382 ent
->fs_fcheck
->fc_size
--;
383 ent
->fs_fcheck
->fc_done
--;
392 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry
*ent
,
396 unsigned int ret
= 0;
398 while (i
++ < count
) {
399 if (ocfs2_filecheck_erase_entry(ent
))
405 return (ret
== count
? 1 : 0);
409 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
410 struct ocfs2_filecheck_entry
*entry
)
412 spin_lock(&ent
->fs_fcheck
->fc_lock
);
414 ent
->fs_fcheck
->fc_done
++;
415 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
419 ocfs2_filecheck_handle(struct ocfs2_super
*osb
,
420 unsigned long ino
, unsigned int flags
)
422 unsigned int ret
= OCFS2_FILECHECK_ERR_SUCCESS
;
423 struct inode
*inode
= NULL
;
426 inode
= ocfs2_iget(osb
, ino
, flags
, 0);
428 rc
= (int)(-(long)inode
);
429 if (rc
>= OCFS2_FILECHECK_ERR_START
&&
430 rc
< OCFS2_FILECHECK_ERR_END
)
433 ret
= OCFS2_FILECHECK_ERR_FAILED
;
441 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry
*ent
,
442 struct ocfs2_filecheck_entry
*entry
)
444 struct ocfs2_super
*osb
= container_of(ent
, struct ocfs2_super
,
447 if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_CHK
)
448 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
449 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_CHK
);
450 else if (entry
->fe_type
== OCFS2_FILECHECK_TYPE_FIX
)
451 entry
->fe_status
= ocfs2_filecheck_handle(osb
,
452 entry
->fe_ino
, OCFS2_FI_FLAG_FILECHECK_FIX
);
454 entry
->fe_status
= OCFS2_FILECHECK_ERR_UNSUPPORTED
;
456 ocfs2_filecheck_done_entry(ent
, entry
);
459 static ssize_t
ocfs2_filecheck_attr_store(struct kobject
*kobj
,
460 struct kobj_attribute
*attr
,
461 const char *buf
, size_t count
)
464 struct ocfs2_filecheck_args args
;
465 struct ocfs2_filecheck_entry
*entry
;
466 struct ocfs2_filecheck_sysfs_entry
*ent
= container_of(kobj
,
467 struct ocfs2_filecheck_sysfs_entry
, fs_kobj
);
472 if (ocfs2_filecheck_args_parse(attr
->attr
.name
, buf
, count
, &args
))
475 if (args
.fa_type
== OCFS2_FILECHECK_TYPE_SET
) {
476 ret
= ocfs2_filecheck_adjust_max(ent
, args
.fa_len
);
480 entry
= kmalloc(sizeof(struct ocfs2_filecheck_entry
), GFP_NOFS
);
486 spin_lock(&ent
->fs_fcheck
->fc_lock
);
487 if (ocfs2_filecheck_is_dup_entry(ent
, args
.fa_ino
)) {
490 } else if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
491 (ent
->fs_fcheck
->fc_done
== 0)) {
493 "Cannot do more file check "
494 "since file check queue(%u) is full now\n",
495 ent
->fs_fcheck
->fc_max
);
499 if ((ent
->fs_fcheck
->fc_size
>= ent
->fs_fcheck
->fc_max
) &&
500 (ent
->fs_fcheck
->fc_done
> 0)) {
501 /* Delete the oldest entry which was done,
502 * make sure the entry size in list does
503 * not exceed maximum value
505 BUG_ON(!ocfs2_filecheck_erase_entry(ent
));
508 entry
->fe_ino
= args
.fa_ino
;
509 entry
->fe_type
= args
.fa_type
;
511 entry
->fe_status
= OCFS2_FILECHECK_ERR_INPROGRESS
;
512 list_add_tail(&entry
->fe_list
, &ent
->fs_fcheck
->fc_head
);
513 ent
->fs_fcheck
->fc_size
++;
515 spin_unlock(&ent
->fs_fcheck
->fc_lock
);
518 ocfs2_filecheck_handle_entry(ent
, entry
);
521 return (!ret
? count
: ret
);