xfs: create helpers to scrub a metadata btree
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 18 Oct 2017 04:37:37 +0000 (21:37 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Thu, 26 Oct 2017 22:38:24 +0000 (15:38 -0700)
Create helper functions and tracepoints to deal with errors while
scrubbing a metadata btree.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/Makefile
fs/xfs/scrub/btree.c [new file with mode: 0644]
fs/xfs/scrub/btree.h [new file with mode: 0644]
fs/xfs/scrub/trace.c
fs/xfs/scrub/trace.h

index 924ad45e7442e97251f26a9ff8c894495f011137..363961576194b9d6e87008613f926d0f4c465e3d 100644 (file)
@@ -143,6 +143,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y)
 
 xfs-y                          += $(addprefix scrub/, \
                                   trace.o \
+                                  btree.o \
                                   common.o \
                                   scrub.o \
                                   )
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c
new file mode 100644 (file)
index 0000000..2853908
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/btree.h"
+#include "scrub/trace.h"
+
+/* btree scrubbing */
+
+/*
+ * Check for btree operation errors.  See the section about handling
+ * operational errors in common.c.
+ */
+bool
+xfs_scrub_btree_process_error(
+       struct xfs_scrub_context        *sc,
+       struct xfs_btree_cur            *cur,
+       int                             level,
+       int                             *error)
+{
+       if (*error == 0)
+               return true;
+
+       switch (*error) {
+       case -EDEADLOCK:
+               /* Used to restart an op with deadlock avoidance. */
+               trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error);
+               break;
+       case -EFSBADCRC:
+       case -EFSCORRUPTED:
+               /* Note the badness but don't abort. */
+               sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+               *error = 0;
+               /* fall through */
+       default:
+               if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+                       trace_xfs_scrub_ifork_btree_op_error(sc, cur, level,
+                                       *error, __return_address);
+               else
+                       trace_xfs_scrub_btree_op_error(sc, cur, level,
+                                       *error, __return_address);
+               break;
+       }
+       return false;
+}
+
+/* Record btree block corruption. */
+void
+xfs_scrub_btree_set_corrupt(
+       struct xfs_scrub_context        *sc,
+       struct xfs_btree_cur            *cur,
+       int                             level)
+{
+       sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
+
+       if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+               trace_xfs_scrub_ifork_btree_error(sc, cur, level,
+                               __return_address);
+       else
+               trace_xfs_scrub_btree_error(sc, cur, level,
+                               __return_address);
+}
+
+/*
+ * Visit all nodes and leaves of a btree.  Check that all pointers and
+ * records are in order, that the keys reflect the records, and use a callback
+ * so that the caller can verify individual records.  The callback is the same
+ * as the one for xfs_btree_query_range, so therefore this function also
+ * returns XFS_BTREE_QUERY_RANGE_ABORT, zero, or a negative error code.
+ */
+int
+xfs_scrub_btree(
+       struct xfs_scrub_context        *sc,
+       struct xfs_btree_cur            *cur,
+       xfs_scrub_btree_rec_fn          scrub_fn,
+       struct xfs_owner_info           *oinfo,
+       void                            *private)
+{
+       int                             error = -EOPNOTSUPP;
+
+       xfs_scrub_btree_process_error(sc, cur, 0, &error);
+       return error;
+}
diff --git a/fs/xfs/scrub/btree.h b/fs/xfs/scrub/btree.h
new file mode 100644 (file)
index 0000000..4de825a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_SCRUB_BTREE_H__
+#define __XFS_SCRUB_BTREE_H__
+
+/* btree scrub */
+
+/* Check for btree operation errors. */
+bool xfs_scrub_btree_process_error(struct xfs_scrub_context *sc,
+               struct xfs_btree_cur *cur, int level, int *error);
+
+/* Check for btree corruption. */
+void xfs_scrub_btree_set_corrupt(struct xfs_scrub_context *sc,
+               struct xfs_btree_cur *cur, int level);
+
+struct xfs_scrub_btree;
+typedef int (*xfs_scrub_btree_rec_fn)(
+       struct xfs_scrub_btree  *bs,
+       union xfs_btree_rec     *rec);
+
+struct xfs_scrub_btree {
+       /* caller-provided scrub state */
+       struct xfs_scrub_context        *sc;
+       struct xfs_btree_cur            *cur;
+       xfs_scrub_btree_rec_fn          scrub_rec;
+       struct xfs_owner_info           *oinfo;
+       void                            *private;
+
+       /* internal scrub state */
+       union xfs_btree_rec             lastrec;
+       bool                            firstrec;
+       union xfs_btree_key             lastkey[XFS_BTREE_MAXLEVELS];
+       bool                            firstkey[XFS_BTREE_MAXLEVELS];
+       struct list_head                to_check;
+};
+int xfs_scrub_btree(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur,
+                   xfs_scrub_btree_rec_fn scrub_fn,
+                   struct xfs_owner_info *oinfo, void *private);
+
+#endif /* __XFS_SCRUB_BTREE_H__ */
index 88b5ccb5df07f35c1a24e082816c099478384d95..472080e757887957b5cd7baf9f12a10d6b27ae71 100644 (file)
 #include "xfs_inode.h"
 #include "xfs_btree.h"
 #include "xfs_trans.h"
+#include "xfs_bit.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
 #include "scrub/common.h"
 
+/* Figure out which block the btree cursor was pointing to. */
+static inline xfs_fsblock_t
+xfs_scrub_btree_cur_fsbno(
+       struct xfs_btree_cur            *cur,
+       int                             level)
+{
+       if (level < cur->bc_nlevels && cur->bc_bufs[level])
+               return XFS_DADDR_TO_FSB(cur->bc_mp, cur->bc_bufs[level]->b_bn);
+       else if (level == cur->bc_nlevels - 1 &&
+                cur->bc_flags & XFS_BTREE_LONG_PTRS)
+               return XFS_INO_TO_FSB(cur->bc_mp, cur->bc_private.b.ip->i_ino);
+       else if (!(cur->bc_flags & XFS_BTREE_LONG_PTRS))
+               return XFS_AGB_TO_FSB(cur->bc_mp, cur->bc_private.a.agno, 0);
+       return NULLFSBLOCK;
+}
+
 /*
  * We include this last to have the helpers above available for the trace
  * event implementations.
index d9706593e937b9e281a63d6177a63c5803c62c1e..147ea0bcbdbdfb3d8f05c47e68aaafc76143e11a 100644 (file)
@@ -283,6 +283,169 @@ TRACE_EVENT(xfs_scrub_incomplete,
                  __entry->ret_ip)
 );
 
+TRACE_EVENT(xfs_scrub_btree_op_error,
+       TP_PROTO(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur,
+                int level, int error, void *ret_ip),
+       TP_ARGS(sc, cur, level, error, ret_ip),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(unsigned int, type)
+               __field(xfs_btnum_t, btnum)
+               __field(int, level)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, bno)
+               __field(int, ptr);
+               __field(int, error)
+               __field(void *, ret_ip)
+       ),
+       TP_fast_assign(
+               xfs_fsblock_t fsbno = xfs_scrub_btree_cur_fsbno(cur, level);
+
+               __entry->dev = sc->mp->m_super->s_dev;
+               __entry->type = sc->sm->sm_type;
+               __entry->btnum = cur->bc_btnum;
+               __entry->level = level;
+               __entry->agno = XFS_FSB_TO_AGNO(cur->bc_mp, fsbno);
+               __entry->bno = XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno);
+               __entry->ptr = cur->bc_ptrs[level];
+               __entry->error = error;
+               __entry->ret_ip = ret_ip;
+       ),
+       TP_printk("dev %d:%d type %u btnum %d level %d ptr %d agno %u agbno %u error %d ret_ip %pF",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->type,
+                 __entry->btnum,
+                 __entry->level,
+                 __entry->ptr,
+                 __entry->agno,
+                 __entry->bno,
+                 __entry->error,
+                 __entry->ret_ip)
+);
+
+TRACE_EVENT(xfs_scrub_ifork_btree_op_error,
+       TP_PROTO(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur,
+                int level, int error, void *ret_ip),
+       TP_ARGS(sc, cur, level, error, ret_ip),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(int, whichfork)
+               __field(unsigned int, type)
+               __field(xfs_btnum_t, btnum)
+               __field(int, level)
+               __field(int, ptr)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, bno)
+               __field(int, error)
+               __field(void *, ret_ip)
+       ),
+       TP_fast_assign(
+               xfs_fsblock_t fsbno = xfs_scrub_btree_cur_fsbno(cur, level);
+               __entry->dev = sc->mp->m_super->s_dev;
+               __entry->ino = sc->ip->i_ino;
+               __entry->whichfork = cur->bc_private.b.whichfork;
+               __entry->type = sc->sm->sm_type;
+               __entry->btnum = cur->bc_btnum;
+               __entry->level = level;
+               __entry->ptr = cur->bc_ptrs[level];
+               __entry->agno = XFS_FSB_TO_AGNO(cur->bc_mp, fsbno);
+               __entry->bno = XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno);
+               __entry->error = error;
+               __entry->ret_ip = ret_ip;
+       ),
+       TP_printk("dev %d:%d ino %llu fork %d type %u btnum %d level %d ptr %d agno %u agbno %u error %d ret_ip %pF",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->whichfork,
+                 __entry->type,
+                 __entry->btnum,
+                 __entry->level,
+                 __entry->ptr,
+                 __entry->agno,
+                 __entry->bno,
+                 __entry->error,
+                 __entry->ret_ip)
+);
+
+TRACE_EVENT(xfs_scrub_btree_error,
+       TP_PROTO(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur,
+                int level, void *ret_ip),
+       TP_ARGS(sc, cur, level, ret_ip),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(unsigned int, type)
+               __field(xfs_btnum_t, btnum)
+               __field(int, level)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, bno)
+               __field(int, ptr);
+               __field(void *, ret_ip)
+       ),
+       TP_fast_assign(
+               xfs_fsblock_t fsbno = xfs_scrub_btree_cur_fsbno(cur, level);
+               __entry->dev = sc->mp->m_super->s_dev;
+               __entry->type = sc->sm->sm_type;
+               __entry->btnum = cur->bc_btnum;
+               __entry->level = level;
+               __entry->agno = XFS_FSB_TO_AGNO(cur->bc_mp, fsbno);
+               __entry->bno = XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno);
+               __entry->ptr = cur->bc_ptrs[level];
+               __entry->ret_ip = ret_ip;
+       ),
+       TP_printk("dev %d:%d type %u btnum %d level %d ptr %d agno %u agbno %u ret_ip %pF",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->type,
+                 __entry->btnum,
+                 __entry->level,
+                 __entry->ptr,
+                 __entry->agno,
+                 __entry->bno,
+                 __entry->ret_ip)
+);
+
+TRACE_EVENT(xfs_scrub_ifork_btree_error,
+       TP_PROTO(struct xfs_scrub_context *sc, struct xfs_btree_cur *cur,
+                int level, void *ret_ip),
+       TP_ARGS(sc, cur, level, ret_ip),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(int, whichfork)
+               __field(unsigned int, type)
+               __field(xfs_btnum_t, btnum)
+               __field(int, level)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, bno)
+               __field(int, ptr);
+               __field(void *, ret_ip)
+       ),
+       TP_fast_assign(
+               xfs_fsblock_t fsbno = xfs_scrub_btree_cur_fsbno(cur, level);
+               __entry->dev = sc->mp->m_super->s_dev;
+               __entry->ino = sc->ip->i_ino;
+               __entry->whichfork = cur->bc_private.b.whichfork;
+               __entry->type = sc->sm->sm_type;
+               __entry->btnum = cur->bc_btnum;
+               __entry->level = level;
+               __entry->agno = XFS_FSB_TO_AGNO(cur->bc_mp, fsbno);
+               __entry->bno = XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno);
+               __entry->ptr = cur->bc_ptrs[level];
+               __entry->ret_ip = ret_ip;
+       ),
+       TP_printk("dev %d:%d ino %llu fork %d type %u btnum %d level %d ptr %d agno %u agbno %u ret_ip %pF",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->whichfork,
+                 __entry->type,
+                 __entry->btnum,
+                 __entry->level,
+                 __entry->ptr,
+                 __entry->agno,
+                 __entry->bno,
+                 __entry->ret_ip)
+);
+
 #endif /* _TRACE_XFS_SCRUB_TRACE_H */
 
 #undef TRACE_INCLUDE_PATH