All of lore.kernel.org
 help / color / mirror / Atom feed
From: Zhang Yi <yi.zhang@huawei.com>
To: <linux-ext4@vger.kernel.org>
Cc: <tytso@mit.edu>, <adilger.kernel@dilger.ca>, <jack@suse.cz>,
	<yi.zhang@huawei.com>, <yukuai3@huawei.com>
Subject: [PATCH 02/12] ext4: introduce fault injection facility
Date: Tue, 8 Nov 2022 22:46:07 +0800	[thread overview]
Message-ID: <20221108144617.4159381-3-yi.zhang@huawei.com> (raw)
In-Reply-To: <20221108144617.4159381-1-yi.zhang@huawei.com>

Introduce fault injection feature for ext4, it depends on the standard
fault-injection (CONFIG_FAULT_INJECTION) facility. User could test and
reinforce ext4 by introduce errors like checksum error, metadata I/O
error, journal error, etc. We could also inject precision fault by set
filters, such as group, inode, logical block of an inode, physical
block of filesystem, and so on.

This patch just add fault injection frame and 6 debugfs interfaces, does
not introduce any concrete faults, later patch will do this
step-by-step. Lists of debugfs interfaces:

 - available_faults: show available faults that we can inject.
 - inject_faults: set faults, can set multiple at one time.
 - inject_inode: set the inode filter, matches all inodes if not set.
 - inject_group: set the block group filter, similar to inject_inode.
 - inject_logical_block: set the logical block filter for one inode.
 - inject_physical_block: set the physical block filter for the fs.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
 fs/ext4/Kconfig |   9 +++
 fs/ext4/ext4.h  |  98 ++++++++++++++++++++++++++++++++
 fs/ext4/sysfs.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 255 insertions(+)

diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 86699c8cab28..2c01c9b335c3 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -101,6 +101,15 @@ config EXT4_DEBUG
 	  If you select Y here, then you will be able to turn on debugging
 	  using dynamic debug control for mb_debug() / ext_debug() msgs.
 
+config EXT4_FAULT_INJECTION
+	bool "Ext4 fault injection support"
+	depends on EXT4_DEBUG && FAULT_INJECTION_DEBUG_FS
+	help
+	  Enables fault injecton facility. Allow test ext4 by injecting
+	  failures like checksum error, EIO, etc. The injection could be
+	  filtered by block group, inode, logical block of file, pyhsical
+	  block, and so on.
+
 config EXT4_KUNIT_TESTS
 	tristate "KUnit tests for ext4" if !KUNIT_ALL_TESTS
 	depends on EXT4_FS && KUNIT
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 53099ffe307f..7a030b0b51c7 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -37,6 +37,7 @@
 #include <linux/falloc.h>
 #include <linux/percpu-rwsem.h>
 #include <linux/fiemap.h>
+#include <linux/fault-inject.h>
 #ifdef __KERNEL__
 #include <linux/compat.h>
 #endif
@@ -1504,6 +1505,100 @@ struct ext4_orphan_info {
 						 * file blocks */
 };
 
+#ifdef CONFIG_EXT4_FAULT_INJECTION
+#define FAULT_NOTSET	(U64_MAX)
+
+enum ext4_fault_bits {
+	EXT4_FAULT_MAX
+};
+
+struct ext4_fault_attr {
+	struct fault_attr fa_attr;
+	struct dentry *fa_dir;
+	/* filter config */
+	u64 fa_group;			/* group number */
+	u64 fa_ino;			/* inode number */
+	u64 fa_lblock;			/* logical block number */
+	u64 fa_pblock;			/* pyhsical block number */
+	/* inject fault operations bitmap */
+	DECLARE_BITMAP(fail_ops, EXT4_FAULT_MAX);
+};
+
+extern void ext4_init_fault_inject(struct super_block *sb);
+extern bool ext4_should_fail(struct super_block *sb, unsigned int bit,
+			     u64 group, u64 ino, u64 lblock, u64 pblock);
+
+#define EXT4_FAULT_FN(bit, name, errno)						\
+static inline int ext4_fault_##name(struct super_block *sb)			\
+{										\
+	bool ret = ext4_should_fail(sb, EXT4_FAULT_##bit, FAULT_NOTSET,		\
+				    FAULT_NOTSET, FAULT_NOTSET, FAULT_NOTSET);	\
+	return (ret && errno) ? (int)errno : (int)ret;				\
+}
+#define EXT4_FAULT_GRP_FN(bit, name, errno)					\
+static inline int ext4_fault_##name(struct super_block *sb, ext4_group_t group)	\
+{										\
+	bool ret = ext4_should_fail(sb, EXT4_FAULT_##bit, group,		\
+				    FAULT_NOTSET, FAULT_NOTSET, FAULT_NOTSET);	\
+	return (ret && errno) ? (int)errno : (int)ret;				\
+}
+#define EXT4_FAULT_INODE_FN(bit, name, errno)					\
+static inline int ext4_fault_##name(struct super_block *sb, unsigned long ino)	\
+{										\
+	bool ret = ext4_should_fail(sb, EXT4_FAULT_##bit, FAULT_NOTSET,		\
+				    ino ? : FAULT_NOTSET, FAULT_NOTSET,		\
+				    FAULT_NOTSET);				\
+	return (ret && errno) ? (int)errno : (int)ret;				\
+}
+#define EXT4_FAULT_INODE_LBLOCK_FN(bit, name, errno)				\
+static inline int ext4_fault_##name(struct inode *inode, ext4_lblk_t lblock)	\
+{										\
+	bool ret = ext4_should_fail(inode->i_sb, EXT4_FAULT_##bit, FAULT_NOTSET,\
+				    inode->i_ino, lblock, FAULT_NOTSET);	\
+	return (ret && errno) ? (int)errno : (int)ret;				\
+}
+#define EXT4_FAULT_INODE_PBLOCK_FN(bit, name, errno)				\
+static inline int ext4_fault_##name(struct super_block *sb, unsigned long ino,	\
+				    ext4_fsblk_t pblock)			\
+{										\
+	bool ret = ext4_should_fail(sb, EXT4_FAULT_##bit, FAULT_NOTSET,		\
+				    ino ? : FAULT_NOTSET, FAULT_NOTSET, pblock);\
+	return (ret && errno) ? (int)errno : (int)ret;				\
+}
+
+#else
+static inline void ext4_init_fault_inject(struct super_block *sb)
+{
+}
+#define EXT4_FAULT_FN(bit, name, errno)						\
+static inline int ext4_fault_##name(struct super_block *sb)			\
+{										\
+	return 0;								\
+}
+#define EXT4_FAULT_GRP_FN(bit, name, errno)					\
+static inline int ext4_fault_##name(struct super_block *sb, ext4_group_t group)	\
+{										\
+	return 0;								\
+}
+#define EXT4_FAULT_INODE_FN(bit, name, errno)					\
+static inline int ext4_fault_##name(struct super_block *sb, unsigned long ino)	\
+{										\
+	return 0;								\
+}
+#define EXT4_FAULT_INODE_LBLOCK_FN(bit, name, errno)				\
+static inline int ext4_fault_##name(struct inode *inode, ext4_lblk_t lblock)	\
+{										\
+	return 0;								\
+}
+#define EXT4_FAULT_INODE_PBLOCK_FN(bit, name, errno)				\
+static inline int ext4_fault_##name(struct super_block *sb, unsigned long ino,	\
+				    ext4_fsblk_t pblock)			\
+{										\
+	return 0;								\
+}
+
+#endif /* CONFIG_EXT4_FAULT_INJECTION */
+
 /*
  * fourth extended-fs super-block data in memory
  */
@@ -1710,6 +1805,9 @@ struct ext4_sb_info {
 	u64 s_dax_part_off;
 #ifdef CONFIG_EXT4_DEBUG
 	unsigned long s_simulate_fail;
+#endif
+#ifdef CONFIG_EXT4_FAULT_INJECTION
+	struct ext4_fault_attr s_fault_attr;
 #endif
 	/* Record the errseq of the backing block device */
 	errseq_t s_bdev_wb_err;
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index f3e4049ec50e..634768ebea2c 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -553,6 +553,8 @@ int ext4_register_sysfs(struct super_block *sb)
 	}
 	if (ext4_debugfs_root)
 		sbi->s_debug = debugfs_create_dir(sb->s_id, ext4_debugfs_root);
+	if (sbi->s_debug)
+		ext4_init_fault_inject(sb);
 	return 0;
 }
 
@@ -566,6 +568,152 @@ void ext4_unregister_sysfs(struct super_block *sb)
 	kobject_del(&sbi->s_kobj);
 }
 
+#ifdef CONFIG_EXT4_FAULT_INJECTION
+char *ext4_fault_names[EXT4_FAULT_MAX] = {
+	/* empty */
+};
+
+static int ext4_fault_available_show(struct seq_file *m, void *v)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ext4_fault_names); i++)
+		seq_printf(m, "%s\n", ext4_fault_names[i]);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ext4_fault_available);
+
+static int ext4_fault_ops_show(struct seq_file *m, void *v)
+{
+	struct super_block *sb = m->private;
+	struct ext4_fault_attr *attr = &EXT4_SB(sb)->s_fault_attr;
+	int bit = 0;
+
+	for_each_set_bit(bit, attr->fail_ops, EXT4_FAULT_MAX)
+		seq_printf(m, "%s\n", ext4_fault_names[bit]);
+
+	return 0;
+}
+
+static int ext4_fault_ops_open(struct inode *inode, struct file *file)
+{
+	struct super_block *sb = inode->i_private;
+	struct ext4_fault_attr *attr = &EXT4_SB(sb)->s_fault_attr;
+	int ret;
+
+	ret = single_open(file, ext4_fault_ops_show, sb);
+	if (ret)
+		return ret;
+
+	if (file->f_flags & O_TRUNC)
+		bitmap_zero(attr->fail_ops, EXT4_FAULT_MAX);
+	return ret;
+}
+
+static int ext4_fault_ops_release(struct inode *inode, struct file *file)
+{
+	return single_release(inode, file);
+}
+
+static ssize_t ext4_fault_ops_write(struct file *file, const char __user *buffer,
+				    size_t count, loff_t *ppos)
+{
+	struct seq_file *m = file->private_data;
+	struct super_block *sb = m->private;
+	struct ext4_fault_attr *attr = &EXT4_SB(sb)->s_fault_attr;
+	char fault_buf[32] = { };
+	char *fault_op;
+	int i;
+
+	if (count >= sizeof(fault_buf)) {
+		ext4_msg(sb, KERN_ERR, "fault operation too long %lu", count);
+		return -EINVAL;
+	}
+	if (copy_from_user(fault_buf, buffer, count))
+		return -EFAULT;
+
+	fault_op = strstrip(fault_buf);
+	for (i = 0; i < ARRAY_SIZE(ext4_fault_names); i++) {
+		if (!strcmp(fault_op, ext4_fault_names[i])) {
+			__set_bit(i, attr->fail_ops);
+			break;
+		}
+	}
+	*ppos += count;
+	return count;
+}
+
+static const struct file_operations ext4_fault_ops_fops = {
+	.open = ext4_fault_ops_open,
+	.read = seq_read,
+	.write = ext4_fault_ops_write,
+	.llseek = seq_lseek,
+	.release = ext4_fault_ops_release,
+};
+
+
+/*
+ * Inject fault injection for one operation, it could be filtered by the
+ * group, inode, logical block and physical block. Return true if we should
+ * inject fault.
+ */
+bool ext4_should_fail(struct super_block *sb, unsigned int bit,
+		      u64 group, u64 ino, u64 lblock, u64 pblock)
+{
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	struct ext4_fault_attr *attr = &sbi->s_fault_attr;
+
+	if (!test_bit(bit, attr->fail_ops))
+		return false;
+
+#define EXT4_FAIL_FILTER_MATCH(conf, check)		\
+	((conf == FAULT_NOTSET) || (check == FAULT_NOTSET) || (conf == check))
+
+	if (!EXT4_FAIL_FILTER_MATCH(attr->fa_group, group))
+		return false;
+	if (!EXT4_FAIL_FILTER_MATCH(attr->fa_ino, ino))
+		return false;
+	if (!EXT4_FAIL_FILTER_MATCH(attr->fa_lblock, lblock))
+		return false;
+	if (!EXT4_FAIL_FILTER_MATCH(attr->fa_pblock, pblock))
+		return false;
+
+	return should_fail(&attr->fa_attr, 1);
+}
+
+void ext4_init_fault_inject(struct super_block *sb)
+{
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	struct ext4_fault_attr *attr = &sbi->s_fault_attr;
+	struct dentry *parent = sbi->s_debug;
+	struct dentry *dir;
+
+	attr->fa_attr = (struct fault_attr) FAULT_ATTR_INITIALIZER;
+	attr->fa_ino = FAULT_NOTSET;
+	attr->fa_group = FAULT_NOTSET;
+	attr->fa_lblock = FAULT_NOTSET;
+	attr->fa_pblock = FAULT_NOTSET;
+	memset(attr->fail_ops, 0, sizeof(attr->fail_ops));
+
+	dir = fault_create_debugfs_attr("fault_inject", parent, &attr->fa_attr);
+	if (IS_ERR(dir)) {
+		ext4_msg(sb, KERN_ERR, "failed to initialize fault_injection %ld",
+			 PTR_ERR(dir));
+		return;
+	}
+	attr->fa_dir = dir;
+	debugfs_create_file("available_faults", 0400, dir, sb,
+			    &ext4_fault_available_fops);
+	debugfs_create_file("inject_faults", 0600, dir, sb,
+			    &ext4_fault_ops_fops);
+	debugfs_create_x64("inject_inode", 0600, dir, &attr->fa_ino);
+	debugfs_create_x64("inject_group", 0600, dir, &attr->fa_group);
+	debugfs_create_x64("inject_logical_block", 0600, dir, &attr->fa_lblock);
+	debugfs_create_x64("inject_physical_block", 0600, dir, &attr->fa_pblock);
+}
+#endif /* CONFIG_EXT4_FAULT_INJECTION */
+
 int __init ext4_init_sysfs(void)
 {
 	int ret;
-- 
2.31.1


  parent reply	other threads:[~2022-11-08 14:26 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-08 14:46 [PATCH 00/12] ext4: enhance simulate fail facility Zhang Yi
2022-11-08 14:46 ` [PATCH 01/12] ext4: add debugfs interface Zhang Yi
2022-11-08 14:46 ` Zhang Yi [this message]
2022-11-08 18:07   ` [PATCH 02/12] ext4: introduce fault injection facility kernel test robot
2022-11-08 14:46 ` [PATCH 03/12] ext4: add several checksum fault injection Zhang Yi
2022-11-08 14:46 ` [PATCH 04/12] ext4: add bitmaps I/O " Zhang Yi
2022-11-08 14:46 ` [PATCH 05/12] ext4: add inode " Zhang Yi
2022-11-08 14:46 ` [PATCH 06/12] ext4: add extent block " Zhang Yi
2022-11-08 14:46 ` [PATCH 07/12] ext4: add dirblock " Zhang Yi
2022-11-08 14:46 ` [PATCH 08/12] ext4: call ext4_xattr_get_block() when getting xattr block Zhang Yi
2022-11-08 14:46 ` [PATCH 09/12] ext4: add xattr block I/O fault injection Zhang Yi
2022-11-08 14:46 ` [PATCH 10/12] ext4: add symlink " Zhang Yi
2022-11-08 14:46 ` [PATCH 11/12] ext4: add journal related " Zhang Yi
2022-11-08 14:46 ` [PATCH 12/12] ext4: remove simulate fail facility Zhang Yi

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=20221108144617.4159381-3-yi.zhang@huawei.com \
    --to=yi.zhang@huawei.com \
    --cc=adilger.kernel@dilger.ca \
    --cc=jack@suse.cz \
    --cc=linux-ext4@vger.kernel.org \
    --cc=tytso@mit.edu \
    --cc=yukuai3@huawei.com \
    /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.