staging: erofs: introduce error injection infrastructure
authorChao Yu <yuchao0@huawei.com>
Thu, 26 Jul 2018 12:21:54 +0000 (20:21 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Jul 2018 15:24:08 +0000 (17:24 +0200)
This patch introduces error injection infrastructure, with it, we can
inject error in any kernel exported common functions which erofs used,
so that it can force erofs running into error paths, it turns out that
tests can cover real rare paths more easily to find bugs.

Reviewed-by: Gao Xiang <gaoxiang25@huawei.com>
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/erofs/Kconfig
drivers/staging/erofs/inode.c
drivers/staging/erofs/internal.h
drivers/staging/erofs/super.c

index 1a5ec1bb5cda67e743ff2d8cb9de20261ada9fc6..edda055b3c64e294adc8a20d00f9008a0afe56ce 100644 (file)
@@ -71,3 +71,9 @@ config EROFS_FS_USE_VM_MAP_RAM
 
          If you don't know what these are, say N.
 
+config EROFS_FAULT_INJECTION
+       bool "EROFS fault injection facility"
+       depends on EROFS_FS
+       help
+         Test EROFS to inject faults such as ENOMEM, EIO, and so on.
+         If unsure, say N.
index c01181153d90b4f94de7b48b276b46eb5d3fb38a..a6d3e129708ee028d1a7beec01da803c5306ce6a 100644 (file)
@@ -113,6 +113,7 @@ static int read_inode(struct inode *inode, void *data)
 static int fill_inline_data(struct inode *inode, void *data, unsigned m_pofs)
 {
        struct erofs_vnode *vi = EROFS_V(inode);
+       struct erofs_sb_info *sbi = EROFS_I_SB(inode);
        int mode = vi->data_mapping_mode;
 
        DBG_BUGON(mode >= EROFS_INODE_LAYOUT_MAX);
@@ -123,7 +124,7 @@ static int fill_inline_data(struct inode *inode, void *data, unsigned m_pofs)
 
        /* fast symlink (following ext4) */
        if (S_ISLNK(inode->i_mode) && inode->i_size < PAGE_SIZE) {
-               char *lnk = kmalloc(inode->i_size + 1, GFP_KERNEL);
+               char *lnk = erofs_kmalloc(sbi, inode->i_size + 1, GFP_KERNEL);
 
                if (unlikely(lnk == NULL))
                        return -ENOMEM;
index 5862705c5caea480c23588591d8f31d5c22035fe..ca224866282f712e3504fb3691c1d3353e1a5923 100644 (file)
 #define DBG_BUGON(...)          ((void)0)
 #endif
 
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+enum {
+       FAULT_KMALLOC,
+       FAULT_MAX,
+};
+
+extern char *erofs_fault_name[FAULT_MAX];
+#define IS_FAULT_SET(fi, type) ((fi)->inject_type & (1 << (type)))
+
+struct erofs_fault_info {
+       atomic_t inject_ops;
+       unsigned int inject_rate;
+       unsigned int inject_type;
+};
+#endif
+
 /* EROFS_SUPER_MAGIC_V1 to represent the whole file system */
 #define EROFS_SUPER_MAGIC   EROFS_SUPER_MAGIC_V1
 
@@ -70,14 +86,55 @@ struct erofs_sb_info {
        char *dev_name;
 
        unsigned int mount_opt;
+
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+       struct erofs_fault_info fault_info;     /* For fault injection */
+#endif
 };
 
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+#define erofs_show_injection_info(type)                                        \
+       infoln("inject %s in %s of %pS", erofs_fault_name[type],        \
+               __func__, __builtin_return_address(0))
+
+static inline bool time_to_inject(struct erofs_sb_info *sbi, int type)
+{
+       struct erofs_fault_info *ffi = &sbi->fault_info;
+
+       if (!ffi->inject_rate)
+               return false;
+
+       if (!IS_FAULT_SET(ffi, type))
+               return false;
+
+       atomic_inc(&ffi->inject_ops);
+       if (atomic_read(&ffi->inject_ops) >= ffi->inject_rate) {
+               atomic_set(&ffi->inject_ops, 0);
+               return true;
+       }
+       return false;
+}
+#endif
+
+static inline void *erofs_kmalloc(struct erofs_sb_info *sbi,
+                                       size_t size, gfp_t flags)
+{
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+       if (time_to_inject(sbi, FAULT_KMALLOC)) {
+               erofs_show_injection_info(FAULT_KMALLOC);
+               return NULL;
+       }
+#endif
+       return kmalloc(size, flags);
+}
+
 #define EROFS_SB(sb) ((struct erofs_sb_info *)(sb)->s_fs_info)
 #define EROFS_I_SB(inode) ((struct erofs_sb_info *)(inode)->i_sb->s_fs_info)
 
 /* Mount flags set via mount options or defaults */
 #define EROFS_MOUNT_XATTR_USER         0x00000010
 #define EROFS_MOUNT_POSIX_ACL          0x00000020
+#define EROFS_MOUNT_FAULT_INJECTION    0x00000040
 
 #define clear_opt(sbi, option) ((sbi)->mount_opt &= ~EROFS_MOUNT_##option)
 #define set_opt(sbi, option)   ((sbi)->mount_opt |= EROFS_MOUNT_##option)
index 4a8a2664c80b289c289a589fef9e4266be5ec4f2..701425fed049b7cd82ce707f49e639c7aa17d971 100644 (file)
@@ -129,6 +129,26 @@ out:
        return ret;
 }
 
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+char *erofs_fault_name[FAULT_MAX] = {
+       [FAULT_KMALLOC]         = "kmalloc",
+};
+
+static void erofs_build_fault_attr(struct erofs_sb_info *sbi,
+                                               unsigned int rate)
+{
+       struct erofs_fault_info *ffi = &sbi->fault_info;
+
+       if (rate) {
+               atomic_set(&ffi->inject_ops, 0);
+               ffi->inject_rate = rate;
+               ffi->inject_type = (1 << FAULT_MAX) - 1;
+       } else {
+               memset(ffi, 0, sizeof(struct erofs_fault_info));
+       }
+}
+#endif
+
 static void default_options(struct erofs_sb_info *sbi)
 {
 #ifdef CONFIG_EROFS_FS_XATTR
@@ -145,6 +165,7 @@ enum {
        Opt_nouser_xattr,
        Opt_acl,
        Opt_noacl,
+       Opt_fault_injection,
        Opt_err
 };
 
@@ -153,6 +174,7 @@ static match_table_t erofs_tokens = {
        {Opt_nouser_xattr, "nouser_xattr"},
        {Opt_acl, "acl"},
        {Opt_noacl, "noacl"},
+       {Opt_fault_injection, "fault_injection=%u"},
        {Opt_err, NULL}
 };
 
@@ -160,6 +182,7 @@ static int parse_options(struct super_block *sb, char *options)
 {
        substring_t args[MAX_OPT_ARGS];
        char *p;
+       int arg = 0;
 
        if (!options)
                return 0;
@@ -204,6 +227,16 @@ static int parse_options(struct super_block *sb, char *options)
                        infoln("noacl options not supported");
                        break;
 #endif
+               case Opt_fault_injection:
+                       if (args->from && match_int(args, &arg))
+                               return -EINVAL;
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+                       erofs_build_fault_attr(EROFS_SB(sb), arg);
+                       set_opt(EROFS_SB(sb), FAULT_INJECTION);
+#else
+                       infoln("FAULT_INJECTION was not selected");
+#endif
+                       break;
                default:
                        errln("Unrecognized mount option \"%s\" "
                                        "or missing value", p);
@@ -452,6 +485,11 @@ static int erofs_show_options(struct seq_file *seq, struct dentry *root)
                seq_puts(seq, ",acl");
        else
                seq_puts(seq, ",noacl");
+#endif
+#ifdef CONFIG_EROFS_FAULT_INJECTION
+       if (test_opt(sbi, FAULT_INJECTION))
+               seq_printf(seq, ",fault_injection=%u",
+                               sbi->fault_info.inject_rate);
 #endif
        return 0;
 }