Message ID | 1456723082-13838-3-git-send-email-ghe@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 02/29/2016 01:17 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@suse.com> Tested-by: Eric Ren <zren@suse.com> > --- > fs/ocfs2/Makefile | 3 +- > fs/ocfs2/filecheck.c | 606 +++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/ocfs2/filecheck.h | 49 +++++ > fs/ocfs2/inode.h | 3 + > 4 files changed, 660 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..2cabbcf > --- /dev/null > +++ b/fs/ocfs2/filecheck.c > @@ -0,0 +1,606 @@ > +/* -*- mode: c; c-basic-offset: 8; -*- > + * vim: noexpandtab sw=8 ts=8 sts=0: > + * > + * filecheck.c > + * > + * Code which implements online file check. > + * > + * Copyright (C) 2016 SuSE. 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", > + "INJBD", > + "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; /* Finished entry count in list */ > +}; > + > +struct ocfs2_filecheck_sysfs_entry { /* sysfs entry per mounting */ > + struct list_head fs_list; > + atomic_t fs_count; > + struct super_block *fs_sb; > + struct kset *fs_devicekset; > + struct kset *fs_fcheckkset; > + 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(inode) */ > + OCFS2_FILECHECK_TYPE_FIX, /* Fix a file(inode) */ > + OCFS2_FILECHECK_TYPE_SET = 100 /* Set entry list maximum size */ > +}; > + > +struct ocfs2_filecheck_entry { > + struct list_head fe_list; > + unsigned long fe_ino; > + unsigned int fe_type; > + unsigned int fe_done:1; > + unsigned int fe_status:31; > +}; > + > +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_chk = > + __ATTR(check, S_IRUSR | S_IWUSR, > + ocfs2_filecheck_show, > + ocfs2_filecheck_store); > +static struct kobj_attribute ocfs2_attr_filecheck_fix = > + __ATTR(fix, S_IRUSR | S_IWUSR, > + ocfs2_filecheck_show, > + ocfs2_filecheck_store); > +static struct kobj_attribute ocfs2_attr_filecheck_set = > + __ATTR(set, 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_fcheckkset); > + kset_unregister(entry->fs_devicekset); > + 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 *device_kset = NULL; > + struct kset *fcheck_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 *) * 4, GFP_NOFS); > + if (!attrs) { > + ret = -ENOMEM; > + goto error; > + } else { > + attrs[0] = &ocfs2_attr_filecheck_chk.attr; > + attrs[1] = &ocfs2_attr_filecheck_fix.attr; > + attrs[2] = &ocfs2_attr_filecheck_set.attr; > + attrs[3] = 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; > + } > + > + device_kset = kset_create_and_add(sb->s_id, NULL, &ocfs2_kset->kobj); > + if (!device_kset) { > + ret = -ENOMEM; > + goto error; > + } > + > + fcheck_kset = kset_create_and_add("filecheck", NULL, > + &device_kset->kobj); > + if (!fcheck_kset) { > + ret = -ENOMEM; > + goto error; > + } > + > + ret = sysfs_create_group(&fcheck_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_devicekset = device_kset; > + entry->fs_fcheckkset = fcheck_kset; > + entry->fs_fcheck = fcheck; > + ocfs2_filecheck_sysfs_add(entry); > + } > + > + kfree(attrs); > + return 0; > + > +error: > + kfree(attrs); > + kfree(entry); > + kfree(fcheck); > + kset_unregister(fcheck_kset); > + kset_unregister(device_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 many 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 24 > +static int > +ocfs2_filecheck_args_get_long(const char *buf, size_t count, > + unsigned long *val) > +{ > + char buffer[OCFS2_FILECHECK_ARGS_LEN]; > + > + memcpy(buffer, buf, count); > + buffer[count] = '\0'; > + > + if (kstrtoul(buffer, 0, val)) > + return 1; > + > + return 0; > +} > + > +static int > +ocfs2_filecheck_type_parse(const char *name, unsigned int *type) > +{ > + if (!strncmp(name, "fix", 4)) > + *type = OCFS2_FILECHECK_TYPE_FIX; > + else if (!strncmp(name, "check", 6)) > + *type = OCFS2_FILECHECK_TYPE_CHK; > + else if (!strncmp(name, "set", 4)) > + *type = OCFS2_FILECHECK_TYPE_SET; > + else > + return 1; > + > + return 0; > +} > + > +static int > +ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count, > + struct ocfs2_filecheck_args *args) > +{ > + unsigned long val = 0; > + unsigned int type; > + > + /* too short/long args length */ > + if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN)) > + return 1; > + > + if (ocfs2_filecheck_type_parse(name, &type)) > + return 1; > + if (ocfs2_filecheck_args_get_long(buf, count, &val)) > + return 1; > + > + if (val <= 0) > + return 1; > + > + args->fa_type = type; > + if (type == OCFS2_FILECHECK_TYPE_SET) > + args->fa_len = (unsigned int)val; > + else > + args->fa_ino = val; > + > + return 0; > +} > + > +static ssize_t ocfs2_filecheck_show(struct kobject *kobj, > + struct kobj_attribute *attr, > + char *buf) > +{ > + > + ssize_t ret = 0, total = 0, remain = PAGE_SIZE; > + unsigned int type; > + struct ocfs2_filecheck_entry *p; > + struct ocfs2_filecheck_sysfs_entry *ent; > + > + if (ocfs2_filecheck_type_parse(attr->attr.name, &type)) > + return -EINVAL; > + > + ent = ocfs2_filecheck_sysfs_get(kobj->parent->name); > + if (!ent) { > + mlog(ML_ERROR, > + "Cannot get the corresponding entry via device basename %s\n", > + kobj->name); > + return -ENODEV; > + } > + > + if (type == OCFS2_FILECHECK_TYPE_SET) { > + spin_lock(&ent->fs_fcheck->fc_lock); > + total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max); > + spin_unlock(&ent->fs_fcheck->fc_lock); > + goto exit; > + } > + > + ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n"); > + total += ret; > + remain -= ret; > + spin_lock(&ent->fs_fcheck->fc_lock); > + list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { > + if (p->fe_type != type) > + continue; > + > + ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n", > + p->fe_ino, 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); > + > +exit: > + 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 int > +ocfs2_filecheck_handle(struct super_block *sb, > + unsigned long ino, unsigned int flags) > +{ > + unsigned int 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; > + struct ocfs2_filecheck_sysfs_entry *ent; > + ssize_t ret = 0; > + > + if (count == 0) > + return count; > + > + if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) { > + mlog(ML_ERROR, "Invalid arguments for online file check\n"); > + return -EINVAL; > + } > + > + ent = ocfs2_filecheck_sysfs_get(kobj->parent->name); > + if (!ent) { > + mlog(ML_ERROR, > + "Cannot get the corresponding entry via device basename %s\n", > + kobj->parent->name); > + return -ENODEV; > + } > + > + if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { > + ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); > + goto exit; > + } > + > + entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); > + if (!entry) { > + ret = -ENOMEM; > + goto exit; > + } > + > + 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, > + "Cannot do more file check " > + "since file check queue(%u) is full now\n", > + ent->fs_fcheck->fc_max); > + ret = -EBUSY; > + kfree(entry); > + } 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->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++; > + } > + spin_unlock(&ent->fs_fcheck->fc_lock); > + > + if (!ret) > + ocfs2_filecheck_handle_entry(ent, entry); > + > +exit: > + ocfs2_filecheck_sysfs_put(ent); > + return (!ret ? count : ret); > +} > diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h > new file mode 100644 > index 0000000..e5cd002 > --- /dev/null > +++ b/fs/ocfs2/filecheck.h > @@ -0,0 +1,49 @@ > +/* -*- mode: c; c-basic-offset: 8; -*- > + * vim: noexpandtab sw=8 ts=8 sts=0: > + * > + * filecheck.h > + * > + * Online file check. > + * > + * Copyright (C) 2016 SuSE. 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_INJBD, /* Buffer in jbd */ > + 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 aac8b86..01635e0 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);
On Mon, Feb 29, 2016 at 01:17: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@suse.com> Reviewed-by: Mark Fasheh <mfasheh@suse.de> -- Mark Fasheh
On Mon, 21 Mar 2016 15:57:23 -0700 Mark Fasheh <mfasheh@suse.de> wrote: > On Mon, Feb 29, 2016 at 01:17: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@suse.com> > Reviewed-by: Mark Fasheh <mfasheh@suse.de> Thanks. So all of ocfs2-export-ocfs2_kset-for-online-file-check.patch ocfs2-sysfile-interfaces-for-online-file-check.patch ocfs2-sysfile-interfaces-for-online-file-check-v4.patch ocfs2-create-remove-sysfile-for-online-file-check.patch ocfs2-check-fix-inode-block-for-online-file-check.patch ocfs2-check-fix-inode-block-for-online-file-check-v4.patch ocfs2-add-feature-document-for-online-file-check.patch have your reviewed-by. I'll send them on to Linus.
On Mon, Mar 21, 2016 at 04:05:47PM -0700, Andrew Morton wrote: > On Mon, 21 Mar 2016 15:57:23 -0700 Mark Fasheh <mfasheh@suse.de> wrote: > > > On Mon, Feb 29, 2016 at 01:17: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@suse.com> > > Reviewed-by: Mark Fasheh <mfasheh@suse.de> > > Thanks. So all of > > ocfs2-export-ocfs2_kset-for-online-file-check.patch > ocfs2-sysfile-interfaces-for-online-file-check.patch > ocfs2-sysfile-interfaces-for-online-file-check-v4.patch > ocfs2-create-remove-sysfile-for-online-file-check.patch > ocfs2-check-fix-inode-block-for-online-file-check.patch > ocfs2-check-fix-inode-block-for-online-file-check-v4.patch > ocfs2-add-feature-document-for-online-file-check.patch > > have your reviewed-by. I'll send them on to Linus. I'm curious, are the '-V4' patches built on top of the non '-V4' versions? Otherwise though this generally sounds good to me - I have reviewed the entire online fsck patch series. --Mark -- Mark Fasheh
On Mon, 21 Mar 2016 16:38:02 -0700 Mark Fasheh <mfasheh@suse.de> wrote: > On Mon, Mar 21, 2016 at 04:05:47PM -0700, Andrew Morton wrote: > > On Mon, 21 Mar 2016 15:57:23 -0700 Mark Fasheh <mfasheh@suse.de> wrote: > > > > > On Mon, Feb 29, 2016 at 01:17: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@suse.com> > > > Reviewed-by: Mark Fasheh <mfasheh@suse.de> > > > > Thanks. So all of > > > > ocfs2-export-ocfs2_kset-for-online-file-check.patch > > ocfs2-sysfile-interfaces-for-online-file-check.patch > > ocfs2-sysfile-interfaces-for-online-file-check-v4.patch > > ocfs2-create-remove-sysfile-for-online-file-check.patch > > ocfs2-check-fix-inode-block-for-online-file-check.patch > > ocfs2-check-fix-inode-block-for-online-file-check-v4.patch > > ocfs2-add-feature-document-for-online-file-check.patch > > > > have your reviewed-by. I'll send them on to Linus. > > I'm curious, are the '-V4' patches built on top of the non '-V4' versions? yes, the -v4 patches bring the base patch from whatever-version-i-originally-merged up to v4. > Otherwise though this generally sounds good to me - I have reviewed the > entire online fsck patch series. Cool.
Hello Mark and Andrew, Thank for taking the time in reviewing the patches, move the thing forward. Thanks Gang >>> > On Mon, 21 Mar 2016 16:38:02 -0700 Mark Fasheh <mfasheh@suse.de> wrote: > >> On Mon, Mar 21, 2016 at 04:05:47PM -0700, Andrew Morton wrote: >> > On Mon, 21 Mar 2016 15:57:23 -0700 Mark Fasheh <mfasheh@suse.de> wrote: >> > >> > > On Mon, Feb 29, 2016 at 01:17: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@suse.com> >> > > Reviewed-by: Mark Fasheh <mfasheh@suse.de> >> > >> > Thanks. So all of >> > >> > ocfs2-export-ocfs2_kset-for-online-file-check.patch >> > ocfs2-sysfile-interfaces-for-online-file-check.patch >> > ocfs2-sysfile-interfaces-for-online-file-check-v4.patch >> > ocfs2-create-remove-sysfile-for-online-file-check.patch >> > ocfs2-check-fix-inode-block-for-online-file-check.patch >> > ocfs2-check-fix-inode-block-for-online-file-check-v4.patch >> > ocfs2-add-feature-document-for-online-file-check.patch >> > >> > have your reviewed-by. I'll send them on to Linus. >> >> I'm curious, are the '-V4' patches built on top of the non '-V4' versions? > > yes, the -v4 patches bring the base patch from > whatever-version-i-originally-merged up to v4. > >> Otherwise though this generally sounds good to me - I have reviewed the >> entire online fsck patch series. > > Cool. > > _______________________________________________ > Ocfs2-devel mailing list > Ocfs2-devel@oss.oracle.com > https://oss.oracle.com/mailman/listinfo/ocfs2-devel
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..2cabbcf --- /dev/null +++ b/fs/ocfs2/filecheck.c @@ -0,0 +1,606 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * filecheck.c + * + * Code which implements online file check. + * + * Copyright (C) 2016 SuSE. 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", + "INJBD", + "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; /* Finished entry count in list */ +}; + +struct ocfs2_filecheck_sysfs_entry { /* sysfs entry per mounting */ + struct list_head fs_list; + atomic_t fs_count; + struct super_block *fs_sb; + struct kset *fs_devicekset; + struct kset *fs_fcheckkset; + 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(inode) */ + OCFS2_FILECHECK_TYPE_FIX, /* Fix a file(inode) */ + OCFS2_FILECHECK_TYPE_SET = 100 /* Set entry list maximum size */ +}; + +struct ocfs2_filecheck_entry { + struct list_head fe_list; + unsigned long fe_ino; + unsigned int fe_type; + unsigned int fe_done:1; + unsigned int fe_status:31; +}; + +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_chk = + __ATTR(check, S_IRUSR | S_IWUSR, + ocfs2_filecheck_show, + ocfs2_filecheck_store); +static struct kobj_attribute ocfs2_attr_filecheck_fix = + __ATTR(fix, S_IRUSR | S_IWUSR, + ocfs2_filecheck_show, + ocfs2_filecheck_store); +static struct kobj_attribute ocfs2_attr_filecheck_set = + __ATTR(set, 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_fcheckkset); + kset_unregister(entry->fs_devicekset); + 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 *device_kset = NULL; + struct kset *fcheck_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 *) * 4, GFP_NOFS); + if (!attrs) { + ret = -ENOMEM; + goto error; + } else { + attrs[0] = &ocfs2_attr_filecheck_chk.attr; + attrs[1] = &ocfs2_attr_filecheck_fix.attr; + attrs[2] = &ocfs2_attr_filecheck_set.attr; + attrs[3] = 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; + } + + device_kset = kset_create_and_add(sb->s_id, NULL, &ocfs2_kset->kobj); + if (!device_kset) { + ret = -ENOMEM; + goto error; + } + + fcheck_kset = kset_create_and_add("filecheck", NULL, + &device_kset->kobj); + if (!fcheck_kset) { + ret = -ENOMEM; + goto error; + } + + ret = sysfs_create_group(&fcheck_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_devicekset = device_kset; + entry->fs_fcheckkset = fcheck_kset; + entry->fs_fcheck = fcheck; + ocfs2_filecheck_sysfs_add(entry); + } + + kfree(attrs); + return 0; + +error: + kfree(attrs); + kfree(entry); + kfree(fcheck); + kset_unregister(fcheck_kset); + kset_unregister(device_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 many 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 24 +static int +ocfs2_filecheck_args_get_long(const char *buf, size_t count, + unsigned long *val) +{ + char buffer[OCFS2_FILECHECK_ARGS_LEN]; + + memcpy(buffer, buf, count); + buffer[count] = '\0'; + + if (kstrtoul(buffer, 0, val)) + return 1; + + return 0; +} + +static int +ocfs2_filecheck_type_parse(const char *name, unsigned int *type) +{ + if (!strncmp(name, "fix", 4)) + *type = OCFS2_FILECHECK_TYPE_FIX; + else if (!strncmp(name, "check", 6)) + *type = OCFS2_FILECHECK_TYPE_CHK; + else if (!strncmp(name, "set", 4)) + *type = OCFS2_FILECHECK_TYPE_SET; + else + return 1; + + return 0; +} + +static int +ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count, + struct ocfs2_filecheck_args *args) +{ + unsigned long val = 0; + unsigned int type; + + /* too short/long args length */ + if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN)) + return 1; + + if (ocfs2_filecheck_type_parse(name, &type)) + return 1; + if (ocfs2_filecheck_args_get_long(buf, count, &val)) + return 1; + + if (val <= 0) + return 1; + + args->fa_type = type; + if (type == OCFS2_FILECHECK_TYPE_SET) + args->fa_len = (unsigned int)val; + else + args->fa_ino = val; + + return 0; +} + +static ssize_t ocfs2_filecheck_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + + ssize_t ret = 0, total = 0, remain = PAGE_SIZE; + unsigned int type; + struct ocfs2_filecheck_entry *p; + struct ocfs2_filecheck_sysfs_entry *ent; + + if (ocfs2_filecheck_type_parse(attr->attr.name, &type)) + return -EINVAL; + + ent = ocfs2_filecheck_sysfs_get(kobj->parent->name); + if (!ent) { + mlog(ML_ERROR, + "Cannot get the corresponding entry via device basename %s\n", + kobj->name); + return -ENODEV; + } + + if (type == OCFS2_FILECHECK_TYPE_SET) { + spin_lock(&ent->fs_fcheck->fc_lock); + total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max); + spin_unlock(&ent->fs_fcheck->fc_lock); + goto exit; + } + + ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n"); + total += ret; + remain -= ret; + spin_lock(&ent->fs_fcheck->fc_lock); + list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { + if (p->fe_type != type) + continue; + + ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n", + p->fe_ino, 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); + +exit: + 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 int +ocfs2_filecheck_handle(struct super_block *sb, + unsigned long ino, unsigned int flags) +{ + unsigned int 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; + struct ocfs2_filecheck_sysfs_entry *ent; + ssize_t ret = 0; + + if (count == 0) + return count; + + if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) { + mlog(ML_ERROR, "Invalid arguments for online file check\n"); + return -EINVAL; + } + + ent = ocfs2_filecheck_sysfs_get(kobj->parent->name); + if (!ent) { + mlog(ML_ERROR, + "Cannot get the corresponding entry via device basename %s\n", + kobj->parent->name); + return -ENODEV; + } + + if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { + ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); + goto exit; + } + + entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); + if (!entry) { + ret = -ENOMEM; + goto exit; + } + + 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, + "Cannot do more file check " + "since file check queue(%u) is full now\n", + ent->fs_fcheck->fc_max); + ret = -EBUSY; + kfree(entry); + } 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->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++; + } + spin_unlock(&ent->fs_fcheck->fc_lock); + + if (!ret) + ocfs2_filecheck_handle_entry(ent, entry); + +exit: + ocfs2_filecheck_sysfs_put(ent); + return (!ret ? count : ret); +} diff --git a/fs/ocfs2/filecheck.h b/fs/ocfs2/filecheck.h new file mode 100644 index 0000000..e5cd002 --- /dev/null +++ b/fs/ocfs2/filecheck.h @@ -0,0 +1,49 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * filecheck.h + * + * Online file check. + * + * Copyright (C) 2016 SuSE. 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_INJBD, /* Buffer in jbd */ + 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 aac8b86..01635e0 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);
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@suse.com> --- fs/ocfs2/Makefile | 3 +- fs/ocfs2/filecheck.c | 606 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ocfs2/filecheck.h | 49 +++++ fs/ocfs2/inode.h | 3 + 4 files changed, 660 insertions(+), 1 deletion(-) create mode 100644 fs/ocfs2/filecheck.c create mode 100644 fs/ocfs2/filecheck.h