All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Ren <zren@suse.com>
To: Gang He <ghe@suse.com>, mfasheh@suse.com, rgoldwyn@suse.de
Cc: linux-kernel@vger.kernel.org, ocfs2-devel@oss.oracle.com
Subject: [Ocfs2-devel] [PATCH v4 2/5] ocfs2: sysfile interfaces for online file check
Date: Wed, 9 Mar 2016 13:40:38 +0800	[thread overview]
Message-ID: <56DFB756.2090305@suse.com> (raw)
In-Reply-To: <1456723082-13838-3-git-send-email-ghe@suse.com>



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);

WARNING: multiple messages have this Message-ID (diff)
From: Eric Ren <zren@suse.com>
To: Gang He <ghe@suse.com>, mfasheh@suse.com, rgoldwyn@suse.de
Cc: linux-kernel@vger.kernel.org, ocfs2-devel@oss.oracle.com
Subject: Re: [Ocfs2-devel] [PATCH v4 2/5] ocfs2: sysfile interfaces for online file check
Date: Wed, 9 Mar 2016 13:40:38 +0800	[thread overview]
Message-ID: <56DFB756.2090305@suse.com> (raw)
In-Reply-To: <1456723082-13838-3-git-send-email-ghe@suse.com>



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);

  reply	other threads:[~2016-03-09  5:40 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-02-29  5:17 [Ocfs2-devel] [PATCH v4 0/5] Add online file check feature Gang He
2016-02-29  5:17 ` Gang He
2016-02-29  5:17 ` [Ocfs2-devel] [PATCH v4 1/5] ocfs2: export ocfs2_kset for online file check Gang He
2016-02-29  5:17   ` Gang He
2016-02-29  5:17 ` [Ocfs2-devel] [PATCH v4 2/5] ocfs2: sysfile interfaces " Gang He
2016-02-29  5:17   ` Gang He
2016-03-09  5:40   ` Eric Ren [this message]
2016-03-09  5:40     ` [Ocfs2-devel] " Eric Ren
2016-03-21 22:57   ` Mark Fasheh
2016-03-21 22:57     ` Mark Fasheh
2016-03-21 23:05     ` [Ocfs2-devel] " Andrew Morton
2016-03-21 23:05       ` Andrew Morton
2016-03-21 23:38       ` [Ocfs2-devel] " Mark Fasheh
2016-03-21 23:38         ` Mark Fasheh
2016-03-22  1:01         ` Andrew Morton
2016-03-22  1:01           ` Andrew Morton
2016-03-22  2:15           ` Gang He
2016-03-22  2:15             ` Gang He
2016-02-29  5:18 ` [Ocfs2-devel] [PATCH v4 3/5] ocfs2: create/remove sysfile " Gang He
2016-02-29  5:18   ` Gang He
2016-02-29  5:18 ` [Ocfs2-devel] [PATCH v4 4/5] ocfs2: check/fix inode block " Gang He
2016-02-29  5:18   ` Gang He
2016-03-09  5:41   ` [Ocfs2-devel] " Eric Ren
2016-03-09  5:41     ` Eric Ren
2016-03-21 22:16   ` Mark Fasheh
2016-03-21 22:16     ` Mark Fasheh
2016-02-29  5:18 ` [Ocfs2-devel] [PATCH v4 5/5] ocfs2: add feature document " Gang He
2016-02-29  5:18   ` Gang He

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56DFB756.2090305@suse.com \
    --to=zren@suse.com \
    --cc=ghe@suse.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mfasheh@suse.com \
    --cc=ocfs2-devel@oss.oracle.com \
    --cc=rgoldwyn@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.