When there are errors in the ocfs2 filesystem, they are usually accompanied by the inode number which caused the error. This inode number would be the input to fixing the file. One of these options could be considered: A file in the sys filesytem which would accept inode numbers. This could be used to communication back what has to be fixed or is fixed. You could write: $# echo "CHECK <inode>" > /sys/fs/ocfs2/devname/filecheck or $# echo "FIX <inode>" > /sys/fs/ocfs2/devname/filecheck Compare with first version, I use strncasecmp instead of double strncmp functions. Second, update the source file contribution vendor. Gang He (4): ocfs2: export ocfs2_kset for online file check ocfs2: sysfile interfaces for online file check ocfs2: create/remove sysfile for online file check ocfs2: check/fix inode block for online file check fs/ocfs2/Makefile | 3 +- fs/ocfs2/filecheck.c | 566 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/filecheck.h | 48 +++++ fs/ocfs2/inode.c | 196 ++++++++++++++++- fs/ocfs2/inode.h | 3 + fs/ocfs2/ocfs2_trace.h | 2 + fs/ocfs2/stackglue.c | 3 +- fs/ocfs2/stackglue.h | 2 + fs/ocfs2/super.c | 5 + 9 files changed, 820 insertions(+), 8 deletions(-) create mode 100644 fs/ocfs2/filecheck.c create mode 100644 fs/ocfs2/filecheck.h -- 2.1.2
Gang He
2015-Oct-28 06:25 UTC
[Ocfs2-devel] [PATCH v2 1/4] ocfs2: export ocfs2_kset for online file check
Export ocfs2_kset object from ocfs2_stackglue kernel module, then online file check code will create the related sysfiles under ocfs2_kset object. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/stackglue.c | 3 ++- fs/ocfs2/stackglue.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c index 5d965e8..13219ed 100644 --- a/fs/ocfs2/stackglue.c +++ b/fs/ocfs2/stackglue.c @@ -629,7 +629,8 @@ static struct attribute_group ocfs2_attr_group = { .attrs = ocfs2_attrs, }; -static struct kset *ocfs2_kset; +struct kset *ocfs2_kset; +EXPORT_SYMBOL_GPL(ocfs2_kset); static void ocfs2_sysfs_exit(void) { diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h index 66334a3..f2dce10 100644 --- a/fs/ocfs2/stackglue.h +++ b/fs/ocfs2/stackglue.h @@ -298,4 +298,6 @@ void ocfs2_stack_glue_set_max_proto_version(struct ocfs2_protocol_version *max_p int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin); void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin); +extern struct kset *ocfs2_kset; + #endif /* STACKGLUE_H */ -- 2.1.2
Gang He
2015-Oct-28 06:25 UTC
[Ocfs2-devel] [PATCH v2 2/4] ocfs2: sysfile interfaces for online file check
Implement online file check sysfile interfaces, e.g. how to create the related sysfile according to device name, how to display/handle file check request from the sysfile. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/Makefile | 3 +- fs/ocfs2/filecheck.c | 566 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/filecheck.h | 48 +++++ fs/ocfs2/inode.h | 3 + 4 files changed, 619 insertions(+), 1 deletion(-) create mode 100644 fs/ocfs2/filecheck.c create mode 100644 fs/ocfs2/filecheck.h diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile index ce210d4..e27e652 100644 --- a/fs/ocfs2/Makefile +++ b/fs/ocfs2/Makefile @@ -41,7 +41,8 @@ ocfs2-objs := \ quota_local.o \ quota_global.o \ xattr.o \ - acl.o + acl.o \ + filecheck.o ocfs2_stackglue-objs := stackglue.o ocfs2_stack_o2cb-objs := stack_o2cb.o diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c new file mode 100644 index 0000000..f12ed1f --- /dev/null +++ b/fs/ocfs2/filecheck.c @@ -0,0 +1,566 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * filecheck.c + * + * Code which implements online file check. + * + * Copyright (C) 2015 Novell. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/fs.h> +#include <linux/kobject.h> +#include <linux/sysfs.h> +#include <linux/sysctl.h> +#include <cluster/masklog.h> + +#include "ocfs2.h" +#include "ocfs2_fs.h" +#include "stackglue.h" +#include "inode.h" + +#include "filecheck.h" + + +/* File check error strings, + * must correspond with error number in header file. + */ +static const char * const ocfs2_filecheck_errs[] = { + "SUCCESS", + "FAILED", + "INPROGRESS", + "READONLY", + "INVALIDINO", + "BLOCKECC", + "BLOCKNO", + "VALIDFLAG", + "GENERATION", + "UNSUPPORTED" +}; + +static DEFINE_SPINLOCK(ocfs2_filecheck_sysfs_lock); +static LIST_HEAD(ocfs2_filecheck_sysfs_list); + +struct ocfs2_filecheck { + struct list_head fc_head; /* File check entry list head */ + spinlock_t fc_lock; + unsigned int fc_max; /* Maximum number of entry in list */ + unsigned int fc_size; /* Current entry count in list */ + unsigned int fc_done; /* File check entries are done in list */ +}; + +struct ocfs2_filecheck_sysfs_entry { + struct list_head fs_list; + atomic_t fs_count; + struct super_block *fs_sb; + struct kset *fs_kset; + struct ocfs2_filecheck *fs_fcheck; +}; + +#define OCFS2_FILECHECK_MAXSIZE 100 +#define OCFS2_FILECHECK_MINSIZE 10 + +/* File check operation type */ +enum { + OCFS2_FILECHECK_TYPE_CHK = 0, /* Check a file */ + OCFS2_FILECHECK_TYPE_FIX, /* Fix a file */ + OCFS2_FILECHECK_TYPE_SET = 100 /* Set file check options */ +}; + +struct ocfs2_filecheck_entry { + struct list_head fe_list; + unsigned long fe_ino; + unsigned int fe_type; + unsigned short fe_done:1; + unsigned short fe_status:15; +}; + +struct ocfs2_filecheck_args { + unsigned int fa_type; + union { + unsigned long fa_ino; + unsigned int fa_len; + }; +}; + +static const char * +ocfs2_filecheck_error(int errno) +{ + if (!errno) + return ocfs2_filecheck_errs[errno]; + + BUG_ON(errno < OCFS2_FILECHECK_ERR_START || + errno > OCFS2_FILECHECK_ERR_END); + return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1]; +} + +static ssize_t ocfs2_filecheck_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf); +static ssize_t ocfs2_filecheck_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); +static struct kobj_attribute ocfs2_attr_filecheck + __ATTR(filecheck, S_IRUSR | S_IWUSR, + ocfs2_filecheck_show, + ocfs2_filecheck_store); + +static int ocfs2_filecheck_sysfs_wait(atomic_t *p) +{ + schedule(); + return 0; +} + +static void +ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry) +{ + struct ocfs2_filecheck_entry *p; + + if (!atomic_dec_and_test(&entry->fs_count)) + wait_on_atomic_t(&entry->fs_count, ocfs2_filecheck_sysfs_wait, + TASK_UNINTERRUPTIBLE); + + spin_lock(&entry->fs_fcheck->fc_lock); + while (!list_empty(&entry->fs_fcheck->fc_head)) { + p = list_first_entry(&entry->fs_fcheck->fc_head, + struct ocfs2_filecheck_entry, fe_list); + list_del(&p->fe_list); + BUG_ON(!p->fe_done); /* To free a undone file check entry */ + kfree(p); + } + spin_unlock(&entry->fs_fcheck->fc_lock); + + kset_unregister(entry->fs_kset); + kfree(entry->fs_fcheck); + kfree(entry); +} + +static void +ocfs2_filecheck_sysfs_add(struct ocfs2_filecheck_sysfs_entry *entry) +{ + spin_lock(&ocfs2_filecheck_sysfs_lock); + list_add_tail(&entry->fs_list, &ocfs2_filecheck_sysfs_list); + spin_unlock(&ocfs2_filecheck_sysfs_lock); +} + +static int ocfs2_filecheck_sysfs_del(const char *devname) +{ + struct ocfs2_filecheck_sysfs_entry *p; + + spin_lock(&ocfs2_filecheck_sysfs_lock); + list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) { + if (!strcmp(p->fs_sb->s_id, devname)) { + list_del(&p->fs_list); + spin_unlock(&ocfs2_filecheck_sysfs_lock); + ocfs2_filecheck_sysfs_free(p); + return 0; + } + } + spin_unlock(&ocfs2_filecheck_sysfs_lock); + return 1; +} + +static void +ocfs2_filecheck_sysfs_put(struct ocfs2_filecheck_sysfs_entry *entry) +{ + if (atomic_dec_and_test(&entry->fs_count)) + wake_up_atomic_t(&entry->fs_count); +} + +static struct ocfs2_filecheck_sysfs_entry * +ocfs2_filecheck_sysfs_get(const char *devname) +{ + struct ocfs2_filecheck_sysfs_entry *p = NULL; + + spin_lock(&ocfs2_filecheck_sysfs_lock); + list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) { + if (!strcmp(p->fs_sb->s_id, devname)) { + atomic_inc(&p->fs_count); + spin_unlock(&ocfs2_filecheck_sysfs_lock); + return p; + } + } + spin_unlock(&ocfs2_filecheck_sysfs_lock); + return NULL; +} + +int ocfs2_filecheck_create_sysfs(struct super_block *sb) +{ + int ret = 0; + struct kset *ocfs2_filecheck_kset = NULL; + struct ocfs2_filecheck *fcheck = NULL; + struct ocfs2_filecheck_sysfs_entry *entry = NULL; + struct attribute **attrs = NULL; + struct attribute_group attrgp; + + if (!ocfs2_kset) + return -ENOMEM; + + attrs = kmalloc(sizeof(struct attribute *) * 2, GFP_NOFS); + if (!attrs) { + ret = -ENOMEM; + goto error; + } else { + attrs[0] = &ocfs2_attr_filecheck.attr; + attrs[1] = NULL; + memset(&attrgp, 0, sizeof(attrgp)); + attrgp.attrs = attrs; + } + + fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS); + if (!fcheck) { + ret = -ENOMEM; + goto error; + } else { + INIT_LIST_HEAD(&fcheck->fc_head); + spin_lock_init(&fcheck->fc_lock); + fcheck->fc_max = OCFS2_FILECHECK_MINSIZE; + fcheck->fc_size = 0; + fcheck->fc_done = 0; + } + + if (strlen(sb->s_id) <= 0) { + mlog(ML_ERROR, + "Cannot get device basename when create filecheck sysfs\n"); + ret = -ENODEV; + goto error; + } + + ocfs2_filecheck_kset = kset_create_and_add(sb->s_id, NULL, + &ocfs2_kset->kobj); + if (!ocfs2_filecheck_kset) { + ret = -ENOMEM; + goto error; + } + + ret = sysfs_create_group(&ocfs2_filecheck_kset->kobj, &attrgp); + if (ret) + goto error; + + entry = kmalloc(sizeof(struct ocfs2_filecheck_sysfs_entry), GFP_NOFS); + if (!entry) { + ret = -ENOMEM; + goto error; + } else { + atomic_set(&entry->fs_count, 1); + entry->fs_sb = sb; + entry->fs_kset = ocfs2_filecheck_kset; + entry->fs_fcheck = fcheck; + ocfs2_filecheck_sysfs_add(entry); + } + + kfree(attrs); + return 0; + +error: + kfree(attrs); + kfree(entry); + kfree(fcheck); + kset_unregister(ocfs2_filecheck_kset); + return ret; +} + +int ocfs2_filecheck_remove_sysfs(struct super_block *sb) +{ + return ocfs2_filecheck_sysfs_del(sb->s_id); +} + +static int +ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, + unsigned int count); +static int +ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent, + unsigned int len) +{ + int ret; + + if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE)) + return -EINVAL; + + spin_lock(&ent->fs_fcheck->fc_lock); + if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) { + mlog(ML_ERROR, + "Cannot set online file check maximum entry number " + "to %u due to too much pending entries(%u)\n", + len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done); + ret = -EBUSY; + } else { + if (len < ent->fs_fcheck->fc_size) + BUG_ON(!ocfs2_filecheck_erase_entries(ent, + ent->fs_fcheck->fc_size - len)); + + ent->fs_fcheck->fc_max = len; + ret = 0; + } + spin_unlock(&ent->fs_fcheck->fc_lock); + + return ret; +} + +#define OCFS2_FILECHECK_ARGS_LEN 32 +static int +ocfs2_filecheck_args_get_long(const char *buf, size_t count, + unsigned long *val) +{ + char buffer[OCFS2_FILECHECK_ARGS_LEN]; + + if (count < 1) + return 1; + + memcpy(buffer, buf, count); + buffer[count] = '\0'; + + if (kstrtoul(buffer, 0, val)) + return 1; + + return 0; +} + +static int +ocfs2_filecheck_args_parse(const char *buf, size_t count, + struct ocfs2_filecheck_args *args) +{ + unsigned long val = 0; + + /* too short/long args length */ + if ((count < 5) || (count > OCFS2_FILECHECK_ARGS_LEN)) + return 1; + + if (!strncasecmp(buf, "FIX ", 4)) { + if (ocfs2_filecheck_args_get_long(buf + 4, count - 4, &val)) + return 1; + + args->fa_type = OCFS2_FILECHECK_TYPE_FIX; + args->fa_ino = val; + return 0; + } else if ((count > 6) && !strncasecmp(buf, "CHECK ", 6)) { + if (ocfs2_filecheck_args_get_long(buf + 6, count - 6, &val)) + return 1; + + args->fa_type = OCFS2_FILECHECK_TYPE_CHK; + args->fa_ino = val; + return 0; + } else if (!strncasecmp(buf, "SET ", 4)) { + if (ocfs2_filecheck_args_get_long(buf + 4, count - 4, &val)) + return 1; + + args->fa_type = OCFS2_FILECHECK_TYPE_SET; + args->fa_len = (unsigned int)val; + return 0; + } else { /* invalid args */ + return 1; + } +} + +static ssize_t ocfs2_filecheck_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + + ssize_t ret = 0, total = 0, remain = PAGE_SIZE; + struct ocfs2_filecheck_entry *p; + struct ocfs2_filecheck_sysfs_entry *ent; + + ent = ocfs2_filecheck_sysfs_get(kobj->name); + if (!ent) { + mlog(ML_ERROR, + "Cannot get the corresponding entry via device basename %s\n", + kobj->name); + return -ENODEV; + } + + spin_lock(&ent->fs_fcheck->fc_lock); + ret = snprintf(buf, remain, "INO\t\tTYPE\tDONE\tERROR\n"); + total += ret; + remain -= ret; + + list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { + ret = snprintf(buf + total, remain, "%lu\t\t%u\t%u\t%s\n", + p->fe_ino, p->fe_type, p->fe_done, + ocfs2_filecheck_error(p->fe_status)); + if (ret < 0) { + total = ret; + break; + } + if (ret == remain) { + /* snprintf() didn't fit */ + total = -E2BIG; + break; + } + total += ret; + remain -= ret; + } + spin_unlock(&ent->fs_fcheck->fc_lock); + + ocfs2_filecheck_sysfs_put(ent); + return total; +} + +static int +ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent) +{ + struct ocfs2_filecheck_entry *p; + + list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { + if (p->fe_done) { + list_del(&p->fe_list); + kfree(p); + ent->fs_fcheck->fc_size--; + ent->fs_fcheck->fc_done--; + return 1; + } + } + + return 0; +} + +static int +ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, + unsigned int count) +{ + unsigned int i = 0; + unsigned int ret = 0; + + while (i++ < count) { + if (ocfs2_filecheck_erase_entry(ent)) + ret++; + else + break; + } + + return (ret == count ? 1 : 0); +} + +static void +ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent, + struct ocfs2_filecheck_entry *entry) +{ + entry->fe_done = 1; + spin_lock(&ent->fs_fcheck->fc_lock); + ent->fs_fcheck->fc_done++; + spin_unlock(&ent->fs_fcheck->fc_lock); +} + +static unsigned short +ocfs2_filecheck_handle(struct super_block *sb, + unsigned long ino, unsigned int flags) +{ + unsigned short ret = OCFS2_FILECHECK_ERR_SUCCESS; + struct inode *inode = NULL; + int rc; + + inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0); + if (IS_ERR(inode)) { + rc = (int)(-(long)inode); + if (rc >= OCFS2_FILECHECK_ERR_START && + rc < OCFS2_FILECHECK_ERR_END) + ret = rc; + else + ret = OCFS2_FILECHECK_ERR_FAILED; + } else + iput(inode); + + return ret; +} + +static void +ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent, + struct ocfs2_filecheck_entry *entry) +{ + if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK) + entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, + entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK); + else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX) + entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, + entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX); + else + entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED; + + ocfs2_filecheck_done_entry(ent, entry); +} + +static ssize_t ocfs2_filecheck_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct ocfs2_filecheck_args args; + struct ocfs2_filecheck_entry *entry = NULL; + struct ocfs2_filecheck_sysfs_entry *ent; + ssize_t ret = 0; + + if (count == 0) + return count; + + if (ocfs2_filecheck_args_parse(buf, count, &args)) { + mlog(ML_ERROR, "Invalid arguments for online file check\n"); + return -EINVAL; + } + + ent = ocfs2_filecheck_sysfs_get(kobj->name); + if (!ent) { + mlog(ML_ERROR, + "Cannot get the corresponding entry via device basename %s\n", + kobj->name); + return -ENODEV; + } + + if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { + ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); + ocfs2_filecheck_sysfs_put(ent); + return (!ret ? count : ret); + } + + spin_lock(&ent->fs_fcheck->fc_lock); + if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && + (ent->fs_fcheck->fc_done == 0)) { + mlog(ML_ERROR, + "Online file check queue(%u) is full\n", + ent->fs_fcheck->fc_max); + ret = -EBUSY; + } else { + if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && + (ent->fs_fcheck->fc_done > 0)) { + /* Delete the oldest entry which was done, + * make sure the entry size in list does + * not exceed maximum value + */ + BUG_ON(!ocfs2_filecheck_erase_entry(ent)); + } + + entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); + if (entry) { + entry->fe_ino = args.fa_ino; + entry->fe_type = args.fa_type; + entry->fe_done = 0; + entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS; + list_add_tail(&entry->fe_list, + &ent->fs_fcheck->fc_head); + + ent->fs_fcheck->fc_size++; + ret = count; + } else { + ret = -ENOMEM; + } + } + spin_unlock(&ent->fs_fcheck->fc_lock); + + if (entry) + ocfs2_filecheck_handle_entry(ent, entry); + + ocfs2_filecheck_sysfs_put(ent); + return ret; +} diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h new file mode 100644 index 0000000..5ec331b --- /dev/null +++ b/fs/ocfs2/filecheck.h @@ -0,0 +1,48 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * filecheck.h + * + * Online file check. + * + * Copyright (C) 2015 Novell. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + + +#ifndef FILECHECK_H +#define FILECHECK_H + +#include <linux/types.h> +#include <linux/list.h> + + +/* File check errno */ +enum { + OCFS2_FILECHECK_ERR_SUCCESS = 0, /* Success */ + OCFS2_FILECHECK_ERR_FAILED = 1000, /* Other failure */ + OCFS2_FILECHECK_ERR_INPROGRESS, /* In progress */ + OCFS2_FILECHECK_ERR_READONLY, /* Read only */ + OCFS2_FILECHECK_ERR_INVALIDINO, /* Invalid ino */ + OCFS2_FILECHECK_ERR_BLOCKECC, /* Block ecc */ + OCFS2_FILECHECK_ERR_BLOCKNO, /* Block number */ + OCFS2_FILECHECK_ERR_VALIDFLAG, /* Inode valid flag */ + OCFS2_FILECHECK_ERR_GENERATION, /* Inode generation */ + OCFS2_FILECHECK_ERR_UNSUPPORTED /* Unsupported */ +}; + +#define OCFS2_FILECHECK_ERR_START OCFS2_FILECHECK_ERR_FAILED +#define OCFS2_FILECHECK_ERR_END OCFS2_FILECHECK_ERR_UNSUPPORTED + +int ocfs2_filecheck_create_sysfs(struct super_block *sb); +int ocfs2_filecheck_remove_sysfs(struct super_block *sb); + +#endif /* FILECHECK_H */ diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 5e86b24..abd1018 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -139,6 +139,9 @@ int ocfs2_drop_inode(struct inode *inode); /* Flags for ocfs2_iget() */ #define OCFS2_FI_FLAG_SYSFILE 0x1 #define OCFS2_FI_FLAG_ORPHAN_RECOVERY 0x2 +#define OCFS2_FI_FLAG_FILECHECK_CHK 0x4 +#define OCFS2_FI_FLAG_FILECHECK_FIX 0x8 + struct inode *ocfs2_ilookup(struct super_block *sb, u64 feoff); struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, unsigned flags, int sysfile_type); -- 2.1.2
Gang He
2015-Oct-28 06:26 UTC
[Ocfs2-devel] [PATCH v2 3/4] ocfs2: create/remove sysfile for online file check
Create online file check sysfile when ocfs2 mount, remove the related sysfile when ocfs2 umount. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/super.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 403c566..7213a94 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -74,6 +74,7 @@ #include "suballoc.h" #include "buffer_head_io.h" +#include "filecheck.h" static struct kmem_cache *ocfs2_inode_cachep; struct kmem_cache *ocfs2_dquot_cachep; @@ -1202,6 +1203,9 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) /* Start this when the mount is almost sure of being successful */ ocfs2_orphan_scan_start(osb); + /* Create filecheck sysfile /sys/fs/ocfs2/<devname>/filecheck */ + ocfs2_filecheck_create_sysfs(sb); + return status; read_super_error: @@ -1658,6 +1662,7 @@ static void ocfs2_put_super(struct super_block *sb) ocfs2_sync_blockdev(sb); ocfs2_dismount_volume(sb, 0); + ocfs2_filecheck_remove_sysfs(sb); } static int ocfs2_statfs(struct dentry *dentry, struct kstatfs *buf) -- 2.1.2
Gang He
2015-Oct-28 06:26 UTC
[Ocfs2-devel] [PATCH v2 4/4] ocfs2: check/fix inode block for online file check
Implement online check or fix inode block during reading a inode block to memory. Signed-off-by: Gang He <ghe at suse.com> --- fs/ocfs2/inode.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/ocfs2/ocfs2_trace.h | 2 + 2 files changed, 192 insertions(+), 6 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index b254416..d811698 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -53,6 +53,7 @@ #include "xattr.h" #include "refcounttree.h" #include "ocfs2_trace.h" +#include "filecheck.h" #include "buffer_head_io.h" @@ -74,6 +75,13 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb, struct inode *inode, struct buffer_head *fe_bh); +static int ocfs2_filecheck_read_inode_block_full(struct inode *inode, + struct buffer_head **bh, int flags, int type); +static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, + struct buffer_head *bh); +static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, + struct buffer_head *bh); + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -127,6 +135,7 @@ struct inode *ocfs2_ilookup(struct super_block *sb, u64 blkno) struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, unsigned flags, int sysfile_type) { + int rc = 0; struct inode *inode = NULL; struct super_block *sb = osb->sb; struct ocfs2_find_inode_args args; @@ -161,12 +170,17 @@ struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 blkno, unsigned flags, } trace_ocfs2_iget5_locked(inode->i_state); if (inode->i_state & I_NEW) { - ocfs2_read_locked_inode(inode, &args); + rc = ocfs2_read_locked_inode(inode, &args); unlock_new_inode(inode); } if (is_bad_inode(inode)) { iput(inode); - inode = ERR_PTR(-ESTALE); + if ((flags & OCFS2_FI_FLAG_FILECHECK_CHK) || + (flags & OCFS2_FI_FLAG_FILECHECK_FIX)) + /* Return OCFS2_FILECHECK_ERR_XXX related errno */ + inode = ERR_PTR(rc); + else + inode = ERR_PTR(-ESTALE); goto bail; } @@ -494,16 +508,32 @@ static int ocfs2_read_locked_inode(struct inode *inode, } if (can_lock) { - status = ocfs2_read_inode_block_full(inode, &bh, - OCFS2_BH_IGNORE_CACHE); + if (args->fi_flags & OCFS2_FI_FLAG_FILECHECK_CHK) + status = ocfs2_filecheck_read_inode_block_full(inode, + &bh, OCFS2_BH_IGNORE_CACHE, 0); + else if (args->fi_flags & OCFS2_FI_FLAG_FILECHECK_FIX) + status = ocfs2_filecheck_read_inode_block_full(inode, + &bh, OCFS2_BH_IGNORE_CACHE, 1); + else + status = ocfs2_read_inode_block_full(inode, + &bh, OCFS2_BH_IGNORE_CACHE); } else { status = ocfs2_read_blocks_sync(osb, args->fi_blkno, 1, &bh); /* * If buffer is in jbd, then its checksum may not have been * computed as yet. */ - if (!status && !buffer_jbd(bh)) - status = ocfs2_validate_inode_block(osb->sb, bh); + if (!status && !buffer_jbd(bh)) { + if (args->fi_flags & OCFS2_FI_FLAG_FILECHECK_CHK) + status = ocfs2_filecheck_validate_inode_block( + osb->sb, bh); + else if (args->fi_flags & OCFS2_FI_FLAG_FILECHECK_FIX) + status = ocfs2_filecheck_repair_inode_block( + osb->sb, bh); + else + status = ocfs2_validate_inode_block( + osb->sb, bh); + } } if (status < 0) { mlog_errno(status); @@ -531,6 +561,14 @@ static int ocfs2_read_locked_inode(struct inode *inode, BUG_ON(args->fi_blkno != le64_to_cpu(fe->i_blkno)); + if (buffer_dirty(bh)) { + status = ocfs2_write_block(osb, bh, INODE_CACHE(inode)); + if (status < 0) { + mlog_errno(status); + goto bail; + } + } + status = 0; bail: @@ -1385,6 +1423,152 @@ bail: return rc; } +static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, + struct buffer_head *bh) +{ + int rc = 0; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; + + trace_ocfs2_filecheck_validate_inode_block( + (unsigned long long)bh->b_blocknr); + + BUG_ON(!buffer_uptodate(bh)); + + if (!OCFS2_IS_VALID_DINODE(di)) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: signature = %.*s\n", + (unsigned long long)bh->b_blocknr, 7, di->i_signature); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + rc = ocfs2_validate_meta_ecc(sb, bh->b_data, &di->i_check); + if (rc) { + mlog(ML_ERROR, + "Filecheck: checksum failed for dinode %llu\n", + (unsigned long long)bh->b_blocknr); + rc = -OCFS2_FILECHECK_ERR_BLOCKECC; + goto bail; + } + + if (le64_to_cpu(di->i_blkno) != bh->b_blocknr) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: i_blkno is %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_blkno)); + rc = -OCFS2_FILECHECK_ERR_BLOCKNO; + goto bail; + } + + if (!(di->i_flags & cpu_to_le32(OCFS2_VALID_FL))) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: OCFS2_VALID_FL not set\n", + (unsigned long long)bh->b_blocknr); + rc = -OCFS2_FILECHECK_ERR_VALIDFLAG; + goto bail; + } + + if (le32_to_cpu(di->i_fs_generation) !+ OCFS2_SB(sb)->fs_generation) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: fs_generation is %u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(di->i_fs_generation)); + rc = -OCFS2_FILECHECK_ERR_GENERATION; + goto bail; + } + +bail: + return rc; +} + +static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, + struct buffer_head *bh) +{ + int rc; + int changed = 0; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; + + rc = ocfs2_filecheck_validate_inode_block(sb, bh); + /* Can't fix invalid inode block */ + if (!rc || rc == -OCFS2_FILECHECK_ERR_INVALIDINO) + return rc; + + trace_ocfs2_filecheck_repair_inode_block( + (unsigned long long)bh->b_blocknr); + + if (ocfs2_is_hard_readonly(OCFS2_SB(sb)) || + ocfs2_is_soft_readonly(OCFS2_SB(sb))) { + mlog(ML_ERROR, + "Filecheck: try to repair dinode #%llu on readonly filesystem\n", + (unsigned long long)bh->b_blocknr); + return -OCFS2_FILECHECK_ERR_READONLY; + } + + if (le64_to_cpu(di->i_blkno) != bh->b_blocknr) { + di->i_blkno = cpu_to_le64(bh->b_blocknr); + changed = 1; + mlog(ML_ERROR, + "Filecheck: reset dinode #%llu: i_blkno to %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_blkno)); + } + + if (!(di->i_flags & cpu_to_le32(OCFS2_VALID_FL))) { + di->i_flags |= cpu_to_le32(OCFS2_VALID_FL); + changed = 1; + mlog(ML_ERROR, + "Filecheck: reset dinode #%llu: OCFS2_VALID_FL is set\n", + (unsigned long long)bh->b_blocknr); + } + + if (le32_to_cpu(di->i_fs_generation) !+ OCFS2_SB(sb)->fs_generation) { + di->i_fs_generation = cpu_to_le32(OCFS2_SB(sb)->fs_generation); + changed = 1; + mlog(ML_ERROR, + "Filecheck: reset dinode #%llu: fs_generation to %u\n", + (unsigned long long)bh->b_blocknr, + le32_to_cpu(di->i_fs_generation)); + } + + if (changed || + ocfs2_validate_meta_ecc(sb, bh->b_data, &di->i_check)) { + ocfs2_compute_meta_ecc(sb, bh->b_data, &di->i_check); + mark_buffer_dirty(bh); + mlog(ML_ERROR, + "Filecheck: reset dinode #%llu: compute meta ecc\n", + (unsigned long long)bh->b_blocknr); + } + + return 0; +} + +static int +ocfs2_filecheck_read_inode_block_full(struct inode *inode, + struct buffer_head **bh, int flags, int type) +{ + int rc; + struct buffer_head *tmp = *bh; + + if (!type) /* Check inode block */ + rc = ocfs2_read_blocks(INODE_CACHE(inode), + OCFS2_I(inode)->ip_blkno, + 1, &tmp, flags, + ocfs2_filecheck_validate_inode_block); + else /* Repair inode block */ + rc = ocfs2_read_blocks(INODE_CACHE(inode), + OCFS2_I(inode)->ip_blkno, + 1, &tmp, flags, + ocfs2_filecheck_repair_inode_block); + + /* If ocfs2_read_blocks() got us a new bh, pass it up. */ + if (!rc && !*bh) + *bh = tmp; + + return rc; +} + int ocfs2_read_inode_block_full(struct inode *inode, struct buffer_head **bh, int flags) { diff --git a/fs/ocfs2/ocfs2_trace.h b/fs/ocfs2/ocfs2_trace.h index 6cb019b..d9205e0 100644 --- a/fs/ocfs2/ocfs2_trace.h +++ b/fs/ocfs2/ocfs2_trace.h @@ -1540,6 +1540,8 @@ DEFINE_OCFS2_ULL_INT_EVENT(ocfs2_read_locked_inode); DEFINE_OCFS2_INT_INT_EVENT(ocfs2_check_orphan_recovery_state); DEFINE_OCFS2_ULL_EVENT(ocfs2_validate_inode_block); +DEFINE_OCFS2_ULL_EVENT(ocfs2_filecheck_validate_inode_block); +DEFINE_OCFS2_ULL_EVENT(ocfs2_filecheck_repair_inode_block); TRACE_EVENT(ocfs2_inode_is_valid_to_delete, TP_PROTO(void *task, void *dc_task, unsigned long long ino, -- 2.1.2
Srinivas Eeda
2015-Oct-28 16:34 UTC
[Ocfs2-devel] [PATCH v2 0/4] Add online file check feature
Hi Gang, thank you for implementing this. I would like to understand this better on where and how it helps ... would you mind sharing couple examples(real scenarios). Thanks, --Srini On 10/27/2015 11:25 PM, Gang He wrote:> When there are errors in the ocfs2 filesystem, > they are usually accompanied by the inode number which caused the error. > This inode number would be the input to fixing the file. > One of these options could be considered: > A file in the sys filesytem which would accept inode numbers. > This could be used to communication back what has to be fixed or is fixed. > You could write: > $# echo "CHECK <inode>" > /sys/fs/ocfs2/devname/filecheck > or > $# echo "FIX <inode>" > /sys/fs/ocfs2/devname/filecheck > > Compare with first version, I use strncasecmp instead of double strncmp > functions. Second, update the source file contribution vendor. > > Gang He (4): > ocfs2: export ocfs2_kset for online file check > ocfs2: sysfile interfaces for online file check > ocfs2: create/remove sysfile for online file check > ocfs2: check/fix inode block for online file check > > fs/ocfs2/Makefile | 3 +- > fs/ocfs2/filecheck.c | 566 +++++++++++++++++++++++++++++++++++++++++++++++++ > fs/ocfs2/filecheck.h | 48 +++++ > fs/ocfs2/inode.c | 196 ++++++++++++++++- > fs/ocfs2/inode.h | 3 + > fs/ocfs2/ocfs2_trace.h | 2 + > fs/ocfs2/stackglue.c | 3 +- > fs/ocfs2/stackglue.h | 2 + > fs/ocfs2/super.c | 5 + > 9 files changed, 820 insertions(+), 8 deletions(-) > create mode 100644 fs/ocfs2/filecheck.c > create mode 100644 fs/ocfs2/filecheck.h >
Junxiao Bi
2015-Nov-03 07:12 UTC
[Ocfs2-devel] [PATCH v2 4/4] ocfs2: check/fix inode block for online file check
Hi Gang, This is not like a right patch. First, online file check only checks inode's block number, valid flag, fs generation value, and meta ecc. I never see a real corruption happened only on this field, if these fields are corrupted, that means something bad may happen on other place. So fix this field may not help and even cause corruption more hard. Second, the repair way is wrong. In ocfs2_filecheck_repair_inode_block(), if these fields in disk don't match the ones in memory, the ones in memory are used to update the disk fields. The question is how do you know these field in memory are right(they may be the real corrupted ones)? Thanks, Junxiao. On 10/28/2015 02:26 PM, Gang He wrote:> +static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, > + struct buffer_head *bh) > +{ > + int rc; > + int changed = 0; > + struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; > + > + rc = ocfs2_filecheck_validate_inode_block(sb, bh); > + /* Can't fix invalid inode block */ > + if (!rc || rc == -OCFS2_FILECHECK_ERR_INVALIDINO) > + return rc; > + > + trace_ocfs2_filecheck_repair_inode_block( > + (unsigned long long)bh->b_blocknr); > + > + if (ocfs2_is_hard_readonly(OCFS2_SB(sb)) || > + ocfs2_is_soft_readonly(OCFS2_SB(sb))) { > + mlog(ML_ERROR, > + "Filecheck: try to repair dinode #%llu on readonly filesystem\n", > + (unsigned long long)bh->b_blocknr); > + return -OCFS2_FILECHECK_ERR_READONLY; > + } > + > + if (le64_to_cpu(di->i_blkno) != bh->b_blocknr) { > + di->i_blkno = cpu_to_le64(bh->b_blocknr); > + changed = 1; > + mlog(ML_ERROR, > + "Filecheck: reset dinode #%llu: i_blkno to %llu\n", > + (unsigned long long)bh->b_blocknr, > + (unsigned long long)le64_to_cpu(di->i_blkno)); > + } > + > + if (!(di->i_flags & cpu_to_le32(OCFS2_VALID_FL))) { > + di->i_flags |= cpu_to_le32(OCFS2_VALID_FL); > + changed = 1; > + mlog(ML_ERROR, > + "Filecheck: reset dinode #%llu: OCFS2_VALID_FL is set\n", > + (unsigned long long)bh->b_blocknr); > + } > + > + if (le32_to_cpu(di->i_fs_generation) !> + OCFS2_SB(sb)->fs_generation) { > + di->i_fs_generation = cpu_to_le32(OCFS2_SB(sb)->fs_generation); > + changed = 1; > + mlog(ML_ERROR, > + "Filecheck: reset dinode #%llu: fs_generation to %u\n", > + (unsigned long long)bh->b_blocknr, > + le32_to_cpu(di->i_fs_generation)); > + } > + > + if (changed || > + ocfs2_validate_meta_ecc(sb, bh->b_data, &di->i_check)) { > + ocfs2_compute_meta_ecc(sb, bh->b_data, &di->i_check); > + mark_buffer_dirty(bh); > + mlog(ML_ERROR, > + "Filecheck: reset dinode #%llu: compute meta ecc\n", > + (unsigned long long)bh->b_blocknr); > + } > + > + return 0; > +}
Junxiao Bi
2015-Nov-03 07:20 UTC
[Ocfs2-devel] [PATCH v2 2/4] ocfs2: sysfile interfaces for online file check
Hi Gang, I didn't see a need to add a sysfs file for the check and repair. This leaves a hard problem for customer to decide. How they decide whether they should repair the bad inode since this may cause corruption even harder? I think the error should be fixed by this feature automaticlly if repair helps, of course this can be done only when error=continue is enabled or add some mount option for it. Thanks, Junxiao. On 10/28/2015 02:25 PM, Gang He wrote:> Implement online file check sysfile interfaces, e.g. > how to create the related sysfile according to device name, > how to display/handle file check request from the sysfile. > > Signed-off-by: Gang He <ghe at suse.com> > --- > fs/ocfs2/Makefile | 3 +- > fs/ocfs2/filecheck.c | 566 +++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/ocfs2/filecheck.h | 48 +++++ > fs/ocfs2/inode.h | 3 + > 4 files changed, 619 insertions(+), 1 deletion(-) > create mode 100644 fs/ocfs2/filecheck.c > create mode 100644 fs/ocfs2/filecheck.h > > diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile > index ce210d4..e27e652 100644 > --- a/fs/ocfs2/Makefile > +++ b/fs/ocfs2/Makefile > @@ -41,7 +41,8 @@ ocfs2-objs := \ > quota_local.o \ > quota_global.o \ > xattr.o \ > - acl.o > + acl.o \ > + filecheck.o > > ocfs2_stackglue-objs := stackglue.o > ocfs2_stack_o2cb-objs := stack_o2cb.o > diff --git a/fs/ocfs2/filecheck.c b/fs/ocfs2/filecheck.c > new file mode 100644 > index 0000000..f12ed1f > --- /dev/null > +++ b/fs/ocfs2/filecheck.c > @@ -0,0 +1,566 @@ > +/* -*- mode: c; c-basic-offset: 8; -*- > + * vim: noexpandtab sw=8 ts=8 sts=0: > + * > + * filecheck.c > + * > + * Code which implements online file check. > + * > + * Copyright (C) 2015 Novell. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public > + * License as published by the Free Software Foundation, version 2. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include <linux/list.h> > +#include <linux/spinlock.h> > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/kmod.h> > +#include <linux/fs.h> > +#include <linux/kobject.h> > +#include <linux/sysfs.h> > +#include <linux/sysctl.h> > +#include <cluster/masklog.h> > + > +#include "ocfs2.h" > +#include "ocfs2_fs.h" > +#include "stackglue.h" > +#include "inode.h" > + > +#include "filecheck.h" > + > + > +/* File check error strings, > + * must correspond with error number in header file. > + */ > +static const char * const ocfs2_filecheck_errs[] = { > + "SUCCESS", > + "FAILED", > + "INPROGRESS", > + "READONLY", > + "INVALIDINO", > + "BLOCKECC", > + "BLOCKNO", > + "VALIDFLAG", > + "GENERATION", > + "UNSUPPORTED" > +}; > + > +static DEFINE_SPINLOCK(ocfs2_filecheck_sysfs_lock); > +static LIST_HEAD(ocfs2_filecheck_sysfs_list); > + > +struct ocfs2_filecheck { > + struct list_head fc_head; /* File check entry list head */ > + spinlock_t fc_lock; > + unsigned int fc_max; /* Maximum number of entry in list */ > + unsigned int fc_size; /* Current entry count in list */ > + unsigned int fc_done; /* File check entries are done in list */ > +}; > + > +struct ocfs2_filecheck_sysfs_entry { > + struct list_head fs_list; > + atomic_t fs_count; > + struct super_block *fs_sb; > + struct kset *fs_kset; > + struct ocfs2_filecheck *fs_fcheck; > +}; > + > +#define OCFS2_FILECHECK_MAXSIZE 100 > +#define OCFS2_FILECHECK_MINSIZE 10 > + > +/* File check operation type */ > +enum { > + OCFS2_FILECHECK_TYPE_CHK = 0, /* Check a file */ > + OCFS2_FILECHECK_TYPE_FIX, /* Fix a file */ > + OCFS2_FILECHECK_TYPE_SET = 100 /* Set file check options */ > +}; > + > +struct ocfs2_filecheck_entry { > + struct list_head fe_list; > + unsigned long fe_ino; > + unsigned int fe_type; > + unsigned short fe_done:1; > + unsigned short fe_status:15; > +}; > + > +struct ocfs2_filecheck_args { > + unsigned int fa_type; > + union { > + unsigned long fa_ino; > + unsigned int fa_len; > + }; > +}; > + > +static const char * > +ocfs2_filecheck_error(int errno) > +{ > + if (!errno) > + return ocfs2_filecheck_errs[errno]; > + > + BUG_ON(errno < OCFS2_FILECHECK_ERR_START || > + errno > OCFS2_FILECHECK_ERR_END); > + return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1]; > +} > + > +static ssize_t ocfs2_filecheck_show(struct kobject *kobj, > + struct kobj_attribute *attr, > + char *buf); > +static ssize_t ocfs2_filecheck_store(struct kobject *kobj, > + struct kobj_attribute *attr, > + const char *buf, size_t count); > +static struct kobj_attribute ocfs2_attr_filecheck > + __ATTR(filecheck, S_IRUSR | S_IWUSR, > + ocfs2_filecheck_show, > + ocfs2_filecheck_store); > + > +static int ocfs2_filecheck_sysfs_wait(atomic_t *p) > +{ > + schedule(); > + return 0; > +} > + > +static void > +ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry) > +{ > + struct ocfs2_filecheck_entry *p; > + > + if (!atomic_dec_and_test(&entry->fs_count)) > + wait_on_atomic_t(&entry->fs_count, ocfs2_filecheck_sysfs_wait, > + TASK_UNINTERRUPTIBLE); > + > + spin_lock(&entry->fs_fcheck->fc_lock); > + while (!list_empty(&entry->fs_fcheck->fc_head)) { > + p = list_first_entry(&entry->fs_fcheck->fc_head, > + struct ocfs2_filecheck_entry, fe_list); > + list_del(&p->fe_list); > + BUG_ON(!p->fe_done); /* To free a undone file check entry */ > + kfree(p); > + } > + spin_unlock(&entry->fs_fcheck->fc_lock); > + > + kset_unregister(entry->fs_kset); > + kfree(entry->fs_fcheck); > + kfree(entry); > +} > + > +static void > +ocfs2_filecheck_sysfs_add(struct ocfs2_filecheck_sysfs_entry *entry) > +{ > + spin_lock(&ocfs2_filecheck_sysfs_lock); > + list_add_tail(&entry->fs_list, &ocfs2_filecheck_sysfs_list); > + spin_unlock(&ocfs2_filecheck_sysfs_lock); > +} > + > +static int ocfs2_filecheck_sysfs_del(const char *devname) > +{ > + struct ocfs2_filecheck_sysfs_entry *p; > + > + spin_lock(&ocfs2_filecheck_sysfs_lock); > + list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) { > + if (!strcmp(p->fs_sb->s_id, devname)) { > + list_del(&p->fs_list); > + spin_unlock(&ocfs2_filecheck_sysfs_lock); > + ocfs2_filecheck_sysfs_free(p); > + return 0; > + } > + } > + spin_unlock(&ocfs2_filecheck_sysfs_lock); > + return 1; > +} > + > +static void > +ocfs2_filecheck_sysfs_put(struct ocfs2_filecheck_sysfs_entry *entry) > +{ > + if (atomic_dec_and_test(&entry->fs_count)) > + wake_up_atomic_t(&entry->fs_count); > +} > + > +static struct ocfs2_filecheck_sysfs_entry * > +ocfs2_filecheck_sysfs_get(const char *devname) > +{ > + struct ocfs2_filecheck_sysfs_entry *p = NULL; > + > + spin_lock(&ocfs2_filecheck_sysfs_lock); > + list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) { > + if (!strcmp(p->fs_sb->s_id, devname)) { > + atomic_inc(&p->fs_count); > + spin_unlock(&ocfs2_filecheck_sysfs_lock); > + return p; > + } > + } > + spin_unlock(&ocfs2_filecheck_sysfs_lock); > + return NULL; > +} > + > +int ocfs2_filecheck_create_sysfs(struct super_block *sb) > +{ > + int ret = 0; > + struct kset *ocfs2_filecheck_kset = NULL; > + struct ocfs2_filecheck *fcheck = NULL; > + struct ocfs2_filecheck_sysfs_entry *entry = NULL; > + struct attribute **attrs = NULL; > + struct attribute_group attrgp; > + > + if (!ocfs2_kset) > + return -ENOMEM; > + > + attrs = kmalloc(sizeof(struct attribute *) * 2, GFP_NOFS); > + if (!attrs) { > + ret = -ENOMEM; > + goto error; > + } else { > + attrs[0] = &ocfs2_attr_filecheck.attr; > + attrs[1] = NULL; > + memset(&attrgp, 0, sizeof(attrgp)); > + attrgp.attrs = attrs; > + } > + > + fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS); > + if (!fcheck) { > + ret = -ENOMEM; > + goto error; > + } else { > + INIT_LIST_HEAD(&fcheck->fc_head); > + spin_lock_init(&fcheck->fc_lock); > + fcheck->fc_max = OCFS2_FILECHECK_MINSIZE; > + fcheck->fc_size = 0; > + fcheck->fc_done = 0; > + } > + > + if (strlen(sb->s_id) <= 0) { > + mlog(ML_ERROR, > + "Cannot get device basename when create filecheck sysfs\n"); > + ret = -ENODEV; > + goto error; > + } > + > + ocfs2_filecheck_kset = kset_create_and_add(sb->s_id, NULL, > + &ocfs2_kset->kobj); > + if (!ocfs2_filecheck_kset) { > + ret = -ENOMEM; > + goto error; > + } > + > + ret = sysfs_create_group(&ocfs2_filecheck_kset->kobj, &attrgp); > + if (ret) > + goto error; > + > + entry = kmalloc(sizeof(struct ocfs2_filecheck_sysfs_entry), GFP_NOFS); > + if (!entry) { > + ret = -ENOMEM; > + goto error; > + } else { > + atomic_set(&entry->fs_count, 1); > + entry->fs_sb = sb; > + entry->fs_kset = ocfs2_filecheck_kset; > + entry->fs_fcheck = fcheck; > + ocfs2_filecheck_sysfs_add(entry); > + } > + > + kfree(attrs); > + return 0; > + > +error: > + kfree(attrs); > + kfree(entry); > + kfree(fcheck); > + kset_unregister(ocfs2_filecheck_kset); > + return ret; > +} > + > +int ocfs2_filecheck_remove_sysfs(struct super_block *sb) > +{ > + return ocfs2_filecheck_sysfs_del(sb->s_id); > +} > + > +static int > +ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, > + unsigned int count); > +static int > +ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent, > + unsigned int len) > +{ > + int ret; > + > + if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE)) > + return -EINVAL; > + > + spin_lock(&ent->fs_fcheck->fc_lock); > + if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) { > + mlog(ML_ERROR, > + "Cannot set online file check maximum entry number " > + "to %u due to too much pending entries(%u)\n", > + len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done); > + ret = -EBUSY; > + } else { > + if (len < ent->fs_fcheck->fc_size) > + BUG_ON(!ocfs2_filecheck_erase_entries(ent, > + ent->fs_fcheck->fc_size - len)); > + > + ent->fs_fcheck->fc_max = len; > + ret = 0; > + } > + spin_unlock(&ent->fs_fcheck->fc_lock); > + > + return ret; > +} > + > +#define OCFS2_FILECHECK_ARGS_LEN 32 > +static int > +ocfs2_filecheck_args_get_long(const char *buf, size_t count, > + unsigned long *val) > +{ > + char buffer[OCFS2_FILECHECK_ARGS_LEN]; > + > + if (count < 1) > + return 1; > + > + memcpy(buffer, buf, count); > + buffer[count] = '\0'; > + > + if (kstrtoul(buffer, 0, val)) > + return 1; > + > + return 0; > +} > + > +static int > +ocfs2_filecheck_args_parse(const char *buf, size_t count, > + struct ocfs2_filecheck_args *args) > +{ > + unsigned long val = 0; > + > + /* too short/long args length */ > + if ((count < 5) || (count > OCFS2_FILECHECK_ARGS_LEN)) > + return 1; > + > + if (!strncasecmp(buf, "FIX ", 4)) { > + if (ocfs2_filecheck_args_get_long(buf + 4, count - 4, &val)) > + return 1; > + > + args->fa_type = OCFS2_FILECHECK_TYPE_FIX; > + args->fa_ino = val; > + return 0; > + } else if ((count > 6) && !strncasecmp(buf, "CHECK ", 6)) { > + if (ocfs2_filecheck_args_get_long(buf + 6, count - 6, &val)) > + return 1; > + > + args->fa_type = OCFS2_FILECHECK_TYPE_CHK; > + args->fa_ino = val; > + return 0; > + } else if (!strncasecmp(buf, "SET ", 4)) { > + if (ocfs2_filecheck_args_get_long(buf + 4, count - 4, &val)) > + return 1; > + > + args->fa_type = OCFS2_FILECHECK_TYPE_SET; > + args->fa_len = (unsigned int)val; > + return 0; > + } else { /* invalid args */ > + return 1; > + } > +} > + > +static ssize_t ocfs2_filecheck_show(struct kobject *kobj, > + struct kobj_attribute *attr, > + char *buf) > +{ > + > + ssize_t ret = 0, total = 0, remain = PAGE_SIZE; > + struct ocfs2_filecheck_entry *p; > + struct ocfs2_filecheck_sysfs_entry *ent; > + > + ent = ocfs2_filecheck_sysfs_get(kobj->name); > + if (!ent) { > + mlog(ML_ERROR, > + "Cannot get the corresponding entry via device basename %s\n", > + kobj->name); > + return -ENODEV; > + } > + > + spin_lock(&ent->fs_fcheck->fc_lock); > + ret = snprintf(buf, remain, "INO\t\tTYPE\tDONE\tERROR\n"); > + total += ret; > + remain -= ret; > + > + list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { > + ret = snprintf(buf + total, remain, "%lu\t\t%u\t%u\t%s\n", > + p->fe_ino, p->fe_type, p->fe_done, > + ocfs2_filecheck_error(p->fe_status)); > + if (ret < 0) { > + total = ret; > + break; > + } > + if (ret == remain) { > + /* snprintf() didn't fit */ > + total = -E2BIG; > + break; > + } > + total += ret; > + remain -= ret; > + } > + spin_unlock(&ent->fs_fcheck->fc_lock); > + > + ocfs2_filecheck_sysfs_put(ent); > + return total; > +} > + > +static int > +ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent) > +{ > + struct ocfs2_filecheck_entry *p; > + > + list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { > + if (p->fe_done) { > + list_del(&p->fe_list); > + kfree(p); > + ent->fs_fcheck->fc_size--; > + ent->fs_fcheck->fc_done--; > + return 1; > + } > + } > + > + return 0; > +} > + > +static int > +ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, > + unsigned int count) > +{ > + unsigned int i = 0; > + unsigned int ret = 0; > + > + while (i++ < count) { > + if (ocfs2_filecheck_erase_entry(ent)) > + ret++; > + else > + break; > + } > + > + return (ret == count ? 1 : 0); > +} > + > +static void > +ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent, > + struct ocfs2_filecheck_entry *entry) > +{ > + entry->fe_done = 1; > + spin_lock(&ent->fs_fcheck->fc_lock); > + ent->fs_fcheck->fc_done++; > + spin_unlock(&ent->fs_fcheck->fc_lock); > +} > + > +static unsigned short > +ocfs2_filecheck_handle(struct super_block *sb, > + unsigned long ino, unsigned int flags) > +{ > + unsigned short ret = OCFS2_FILECHECK_ERR_SUCCESS; > + struct inode *inode = NULL; > + int rc; > + > + inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0); > + if (IS_ERR(inode)) { > + rc = (int)(-(long)inode); > + if (rc >= OCFS2_FILECHECK_ERR_START && > + rc < OCFS2_FILECHECK_ERR_END) > + ret = rc; > + else > + ret = OCFS2_FILECHECK_ERR_FAILED; > + } else > + iput(inode); > + > + return ret; > +} > + > +static void > +ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent, > + struct ocfs2_filecheck_entry *entry) > +{ > + if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK) > + entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, > + entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK); > + else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX) > + entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb, > + entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX); > + else > + entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED; > + > + ocfs2_filecheck_done_entry(ent, entry); > +} > + > +static ssize_t ocfs2_filecheck_store(struct kobject *kobj, > + struct kobj_attribute *attr, > + const char *buf, size_t count) > +{ > + struct ocfs2_filecheck_args args; > + struct ocfs2_filecheck_entry *entry = NULL; > + struct ocfs2_filecheck_sysfs_entry *ent; > + ssize_t ret = 0; > + > + if (count == 0) > + return count; > + > + if (ocfs2_filecheck_args_parse(buf, count, &args)) { > + mlog(ML_ERROR, "Invalid arguments for online file check\n"); > + return -EINVAL; > + } > + > + ent = ocfs2_filecheck_sysfs_get(kobj->name); > + if (!ent) { > + mlog(ML_ERROR, > + "Cannot get the corresponding entry via device basename %s\n", > + kobj->name); > + return -ENODEV; > + } > + > + if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { > + ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); > + ocfs2_filecheck_sysfs_put(ent); > + return (!ret ? count : ret); > + } > + > + spin_lock(&ent->fs_fcheck->fc_lock); > + if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && > + (ent->fs_fcheck->fc_done == 0)) { > + mlog(ML_ERROR, > + "Online file check queue(%u) is full\n", > + ent->fs_fcheck->fc_max); > + ret = -EBUSY; > + } else { > + if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && > + (ent->fs_fcheck->fc_done > 0)) { > + /* Delete the oldest entry which was done, > + * make sure the entry size in list does > + * not exceed maximum value > + */ > + BUG_ON(!ocfs2_filecheck_erase_entry(ent)); > + } > + > + entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); > + if (entry) { > + entry->fe_ino = args.fa_ino; > + entry->fe_type = args.fa_type; > + entry->fe_done = 0; > + entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS; > + list_add_tail(&entry->fe_list, > + &ent->fs_fcheck->fc_head); > + > + ent->fs_fcheck->fc_size++; > + ret = count; > + } else { > + ret = -ENOMEM; > + } > + } > + spin_unlock(&ent->fs_fcheck->fc_lock); > + > + if (entry) > + ocfs2_filecheck_handle_entry(ent, entry); > + > + ocfs2_filecheck_sysfs_put(ent); > + return ret; > +} > diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h > new file mode 100644 > index 0000000..5ec331b > --- /dev/null > +++ b/fs/ocfs2/filecheck.h > @@ -0,0 +1,48 @@ > +/* -*- mode: c; c-basic-offset: 8; -*- > + * vim: noexpandtab sw=8 ts=8 sts=0: > + * > + * filecheck.h > + * > + * Online file check. > + * > + * Copyright (C) 2015 Novell. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public > + * License as published by the Free Software Foundation, version 2. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > + > +#ifndef FILECHECK_H > +#define FILECHECK_H > + > +#include <linux/types.h> > +#include <linux/list.h> > + > + > +/* File check errno */ > +enum { > + OCFS2_FILECHECK_ERR_SUCCESS = 0, /* Success */ > + OCFS2_FILECHECK_ERR_FAILED = 1000, /* Other failure */ > + OCFS2_FILECHECK_ERR_INPROGRESS, /* In progress */ > + OCFS2_FILECHECK_ERR_READONLY, /* Read only */ > + OCFS2_FILECHECK_ERR_INVALIDINO, /* Invalid ino */ > + OCFS2_FILECHECK_ERR_BLOCKECC, /* Block ecc */ > + OCFS2_FILECHECK_ERR_BLOCKNO, /* Block number */ > + OCFS2_FILECHECK_ERR_VALIDFLAG, /* Inode valid flag */ > + OCFS2_FILECHECK_ERR_GENERATION, /* Inode generation */ > + OCFS2_FILECHECK_ERR_UNSUPPORTED /* Unsupported */ > +}; > + > +#define OCFS2_FILECHECK_ERR_START OCFS2_FILECHECK_ERR_FAILED > +#define OCFS2_FILECHECK_ERR_END OCFS2_FILECHECK_ERR_UNSUPPORTED > + > +int ocfs2_filecheck_create_sysfs(struct super_block *sb); > +int ocfs2_filecheck_remove_sysfs(struct super_block *sb); > + > +#endif /* FILECHECK_H */ > diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h > index 5e86b24..abd1018 100644 > --- a/fs/ocfs2/inode.h > +++ b/fs/ocfs2/inode.h > @@ -139,6 +139,9 @@ int ocfs2_drop_inode(struct inode *inode); > /* Flags for ocfs2_iget() */ > #define OCFS2_FI_FLAG_SYSFILE 0x1 > #define OCFS2_FI_FLAG_ORPHAN_RECOVERY 0x2 > +#define OCFS2_FI_FLAG_FILECHECK_CHK 0x4 > +#define OCFS2_FI_FLAG_FILECHECK_FIX 0x8 > + > struct inode *ocfs2_ilookup(struct super_block *sb, u64 feoff); > struct inode *ocfs2_iget(struct ocfs2_super *osb, u64 feoff, unsigned flags, > int sysfile_type); >
Mark Fasheh
2015-Nov-24 21:47 UTC
[Ocfs2-devel] [PATCH v2 1/4] ocfs2: export ocfs2_kset for online file check
On Wed, Oct 28, 2015 at 02:25:58PM +0800, Gang He wrote:> Export ocfs2_kset object from ocfs2_stackglue kernel module, > then online file check code will create the related sysfiles > under ocfs2_kset object. > > Signed-off-by: Gang He <ghe at suse.com>Reviewed-by: Mark Fasheh <mfasheh at suse.de> -- Mark Fasheh
Mark Fasheh
2015-Nov-24 21:52 UTC
[Ocfs2-devel] [PATCH v2 2/4] ocfs2: sysfile interfaces for online file check
On Wed, Oct 28, 2015 at 02:25:59PM +0800, Gang He wrote:> Implement online file check sysfile interfaces, e.g. > how to create the related sysfile according to device name, > how to display/handle file check request from the sysfile. > > Signed-off-by: Gang He <ghe at suse.com>FYI, This looks generally fine to me however we should address Junxiao's concerns before it goes any further. --Mark -- Mark Fasheh
Mark Fasheh
2015-Nov-24 21:53 UTC
[Ocfs2-devel] [PATCH v2 3/4] ocfs2: create/remove sysfile for online file check
On Wed, Oct 28, 2015 at 02:26:00PM +0800, Gang He wrote:> Create online file check sysfile when ocfs2 mount, > remove the related sysfile when ocfs2 umount. > > Signed-off-by: Gang He <ghe at suse.com>Reviewed-by: Mark Fasheh <mfasheh at suse.de> --Mark -- Mark Fasheh
Pavel Machek
2015-Dec-02 18:20 UTC
[Ocfs2-devel] [PATCH v2 0/4] Add online file check feature
On Wed 2015-10-28 14:25:57, Gang He wrote:> When there are errors in the ocfs2 filesystem, > they are usually accompanied by the inode number which caused the error. > This inode number would be the input to fixing the file. > One of these options could be considered: > A file in the sys filesytem which would accept inode numbers. > This could be used to communication back what has to be fixed or is fixed. > You could write: > $# echo "CHECK <inode>" > /sys/fs/ocfs2/devname/filecheck > or > $# echo "FIX <inode>" > /sys/fs/ocfs2/devname/filecheck >Are you sure this is reasonable interface? I mean.... sysfs is supposed to be one value per file. And I don't think its suitable for running commands. ...or returning back results. Pavel -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html