xfs: add debug knob to slow down writeback for fun
authorDarrick J. Wong <djwong@kernel.org>
Tue, 29 Nov 2022 01:24:35 +0000 (17:24 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 29 Nov 2022 01:24:35 +0000 (17:24 -0800)
Add a new error injection knob so that we can arbitrarily slow down
writeback to test for race conditions and aberrant reclaim behavior if
the writeback mechanisms are slow to issue writeback.  This will enable
functional testing for the ifork sequence counters introduced in commit
745b3f76d1c8 ("xfs: maintain a sequence count for inode fork
manipulations").

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/libxfs/xfs_errortag.h
fs/xfs/xfs_aops.c
fs/xfs/xfs_error.c
fs/xfs/xfs_error.h
fs/xfs/xfs_trace.c
fs/xfs/xfs_trace.h

index 580ccbd5aadc2cf18f29d69b692ce32f71e6c9cf..f5f629174ecad6b686f55d2d82cfc1e1cc4b6398 100644 (file)
@@ -61,7 +61,8 @@
 #define XFS_ERRTAG_LARP                                        39
 #define XFS_ERRTAG_DA_LEAF_SPLIT                       40
 #define XFS_ERRTAG_ATTR_LEAF_TO_NODE                   41
-#define XFS_ERRTAG_MAX                                 42
+#define XFS_ERRTAG_WB_DELAY_MS                         42
+#define XFS_ERRTAG_MAX                                 43
 
 /*
  * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
 #define XFS_RANDOM_LARP                                        1
 #define XFS_RANDOM_DA_LEAF_SPLIT                       1
 #define XFS_RANDOM_ATTR_LEAF_TO_NODE                   1
+#define XFS_RANDOM_WB_DELAY_MS                         3000
 
 #endif /* __XFS_ERRORTAG_H_ */
index a22d90af40c85e97bc62ea1971c905358b274cc7..41734202796f4ccb1bbee62ce5ccef0991073a9d 100644 (file)
@@ -17,6 +17,8 @@
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_reflink.h"
+#include "xfs_errortag.h"
+#include "xfs_error.h"
 
 struct xfs_writepage_ctx {
        struct iomap_writepage_ctx ctx;
@@ -217,11 +219,17 @@ xfs_imap_valid(
         * checked (and found nothing at this offset) could have added
         * overlapping blocks.
         */
-       if (XFS_WPC(wpc)->data_seq != READ_ONCE(ip->i_df.if_seq))
+       if (XFS_WPC(wpc)->data_seq != READ_ONCE(ip->i_df.if_seq)) {
+               trace_xfs_wb_data_iomap_invalid(ip, &wpc->iomap,
+                               XFS_WPC(wpc)->data_seq, XFS_DATA_FORK);
                return false;
+       }
        if (xfs_inode_has_cow_data(ip) &&
-           XFS_WPC(wpc)->cow_seq != READ_ONCE(ip->i_cowfp->if_seq))
+           XFS_WPC(wpc)->cow_seq != READ_ONCE(ip->i_cowfp->if_seq)) {
+               trace_xfs_wb_cow_iomap_invalid(ip, &wpc->iomap,
+                               XFS_WPC(wpc)->cow_seq, XFS_COW_FORK);
                return false;
+       }
        return true;
 }
 
@@ -285,6 +293,8 @@ xfs_map_blocks(
        if (xfs_is_shutdown(mp))
                return -EIO;
 
+       XFS_ERRORTAG_DELAY(mp, XFS_ERRTAG_WB_DELAY_MS);
+
        /*
         * COW fork blocks can overlap data fork blocks even if the blocks
         * aren't shared.  COW I/O always takes precedent, so we must always
index dea3c0649d2f7f4ca504efeaa60d9887284e7845..2d6e3c718e03d08d97b6a55e6cdba199dff3633f 100644 (file)
@@ -60,6 +60,7 @@ static unsigned int xfs_errortag_random_default[] = {
        XFS_RANDOM_LARP,
        XFS_RANDOM_DA_LEAF_SPLIT,
        XFS_RANDOM_ATTR_LEAF_TO_NODE,
+       XFS_RANDOM_WB_DELAY_MS,
 };
 
 struct xfs_errortag_attr {
@@ -175,6 +176,7 @@ XFS_ERRORTAG_ATTR_RW(ag_resv_fail, XFS_ERRTAG_AG_RESV_FAIL);
 XFS_ERRORTAG_ATTR_RW(larp,             XFS_ERRTAG_LARP);
 XFS_ERRORTAG_ATTR_RW(da_leaf_split,    XFS_ERRTAG_DA_LEAF_SPLIT);
 XFS_ERRORTAG_ATTR_RW(attr_leaf_to_node,        XFS_ERRTAG_ATTR_LEAF_TO_NODE);
+XFS_ERRORTAG_ATTR_RW(wb_delay_ms,      XFS_ERRTAG_WB_DELAY_MS);
 
 static struct attribute *xfs_errortag_attrs[] = {
        XFS_ERRORTAG_ATTR_LIST(noerror),
@@ -218,6 +220,7 @@ static struct attribute *xfs_errortag_attrs[] = {
        XFS_ERRORTAG_ATTR_LIST(larp),
        XFS_ERRORTAG_ATTR_LIST(da_leaf_split),
        XFS_ERRORTAG_ATTR_LIST(attr_leaf_to_node),
+       XFS_ERRORTAG_ATTR_LIST(wb_delay_ms),
        NULL,
 };
 ATTRIBUTE_GROUPS(xfs_errortag);
@@ -267,6 +270,19 @@ xfs_errortag_valid(
        return true;
 }
 
+bool
+xfs_errortag_enabled(
+       struct xfs_mount        *mp,
+       unsigned int            tag)
+{
+       if (!mp->m_errortag)
+               return false;
+       if (!xfs_errortag_valid(tag))
+               return false;
+
+       return mp->m_errortag[tag] != 0;
+}
+
 bool
 xfs_errortag_test(
        struct xfs_mount        *mp,
index 5191e9145e5520565a51295b90e5d883b52bdfe0..dbe6c37dc697514160a886246d8c2f7c968d05a2 100644 (file)
@@ -45,6 +45,18 @@ extern bool xfs_errortag_test(struct xfs_mount *mp, const char *expression,
                const char *file, int line, unsigned int error_tag);
 #define XFS_TEST_ERROR(expr, mp, tag)          \
        ((expr) || xfs_errortag_test((mp), #expr, __FILE__, __LINE__, (tag)))
+bool xfs_errortag_enabled(struct xfs_mount *mp, unsigned int tag);
+#define XFS_ERRORTAG_DELAY(mp, tag)            \
+       do { \
+               might_sleep(); \
+               if (!xfs_errortag_enabled((mp), (tag))) \
+                       break; \
+               xfs_warn_ratelimited((mp), \
+"Injecting %ums delay at file %s, line %d, on filesystem \"%s\"", \
+                               (mp)->m_errortag[(tag)], __FILE__, __LINE__, \
+                               (mp)->m_super->s_id); \
+               mdelay((mp)->m_errortag[(tag)]); \
+       } while (0)
 
 extern int xfs_errortag_get(struct xfs_mount *mp, unsigned int error_tag);
 extern int xfs_errortag_set(struct xfs_mount *mp, unsigned int error_tag,
@@ -55,6 +67,7 @@ extern int xfs_errortag_clearall(struct xfs_mount *mp);
 #define xfs_errortag_init(mp)                  (0)
 #define xfs_errortag_del(mp)
 #define XFS_TEST_ERROR(expr, mp, tag)          (expr)
+#define XFS_ERRORTAG_DELAY(mp, tag)            ((void)0)
 #define xfs_errortag_set(mp, tag, val)         (ENOSYS)
 #define xfs_errortag_add(mp, tag)              (ENOSYS)
 #define xfs_errortag_clearall(mp)              (ENOSYS)
index d269ef57ff0166c0ef17d0c9b58b06e59932c673..8a5dc1538aa826aedd5c0fb97b02517052f91e28 100644 (file)
@@ -34,6 +34,8 @@
 #include "xfs_ag.h"
 #include "xfs_ag_resv.h"
 #include "xfs_error.h"
+#include <linux/iomap.h>
+#include "xfs_iomap.h"
 
 /*
  * We include this last to have the helpers above available for the trace
index 372d871bccc5eba84881e8b2b5e22078f05c45ac..c9ada9577a4a7a35110853097cfede903a1792ff 100644 (file)
@@ -3352,6 +3352,50 @@ DEFINE_EVENT(xfs_inode_irec_class, name, \
        TP_PROTO(struct xfs_inode *ip, struct xfs_bmbt_irec *irec), \
        TP_ARGS(ip, irec))
 
+/* inode iomap invalidation events */
+DECLARE_EVENT_CLASS(xfs_wb_invalid_class,
+       TP_PROTO(struct xfs_inode *ip, const struct iomap *iomap, unsigned int wpcseq, int whichfork),
+       TP_ARGS(ip, iomap, wpcseq, whichfork),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(u64, addr)
+               __field(loff_t, pos)
+               __field(u64, len)
+               __field(u16, type)
+               __field(u16, flags)
+               __field(u32, wpcseq)
+               __field(u32, forkseq)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->addr = iomap->addr;
+               __entry->pos = iomap->offset;
+               __entry->len = iomap->length;
+               __entry->type = iomap->type;
+               __entry->flags = iomap->flags;
+               __entry->wpcseq = wpcseq;
+               __entry->forkseq = READ_ONCE(xfs_ifork_ptr(ip, whichfork)->if_seq);
+       ),
+       TP_printk("dev %d:%d ino 0x%llx pos 0x%llx addr 0x%llx bytecount 0x%llx type 0x%x flags 0x%x wpcseq 0x%x forkseq 0x%x",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->pos,
+                 __entry->addr,
+                 __entry->len,
+                 __entry->type,
+                 __entry->flags,
+                 __entry->wpcseq,
+                 __entry->forkseq)
+);
+#define DEFINE_WB_INVALID_EVENT(name) \
+DEFINE_EVENT(xfs_wb_invalid_class, name, \
+       TP_PROTO(struct xfs_inode *ip, const struct iomap *iomap, unsigned int wpcseq, int whichfork), \
+       TP_ARGS(ip, iomap, wpcseq, whichfork))
+DEFINE_WB_INVALID_EVENT(xfs_wb_cow_iomap_invalid);
+DEFINE_WB_INVALID_EVENT(xfs_wb_data_iomap_invalid);
+
 /* refcount/reflink tracepoint definitions */
 
 /* reflink tracepoints */