* [PATCH v8 00/25] xfs: online scrub support
@ 2016-08-25 23:40 Darrick J. Wong
2016-08-25 23:40 ` [PATCH 01/25] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
` (24 more replies)
0 siblings, 25 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Hi all,
This is the eighth revision of a patchset that adds to XFS kernel
support for mapping multiple file logical blocks to the same physical
block (reflink/deduplication), implements the beginnings of online
metadata scrubbing and preening, and implements reverse mapping for
the realtime device. There shouldn't be any incompatible on-disk
format changes, pending a thorough review of the patches within.
Online scrub support consists of three pieces -- first, an ioctl that
maps physical extents to their owners; second, various in-kernel
metadata scrubbing ioctls to examine metadata records and
cross-reference them with other filesystem metadata; and third, a
userspace component to initiate kernel scrubbing, walk all inodes and
the directory tree, and scrub data extents.
The first few patches in this series implements the GETFSMAP ioctl
that maps a device number and physical extent either to filesystem
metadata or to a range of file blocks. The initial implementation
uses the reverse-mapping B+tree to supply the mapping information,
however a fallback implementation based on the free space btrees is
also provided. The flexibility of having both implementations is
important when it comes to the userspace tool -- even without the
owner/offset data, we still have enough information to set up a read
verification.
The bulk of the patches implement in-kernel scrubbing. This is
implemented as a new ioctl. Pass in a metadata type and a control
number (when applicable); the kernel will examine each record in that
metadata structure looking for obvious logical errors. External
corruption should be discoverable via the checksum embedded in each
(v5) filesystem metadata block. When applicable, the metadata record
will be cross-referenced with the other metadata structures to look
for discrepancies. Should any errors be found, an error code is
returned to userspace, which should take the filesystem offline and
repair it.
The final patch in the series enables xfs_scrub to query the per-AG
block reservations so that the summary counters can be sanity-checked.
If you're going to start using this mess, you probably ought to just
pull from my github trees for kernel[1], xfsprogs[2], xfstests[3],
xfs-docs[4], and man-pages[5]. The kernel patches in the git trees
should apply to 4.8-rc3; xfsprogs patches to for-next; and xfstest to
master.
The patches have been xfstested with x64, ppc64, and armhf; all tests
in the clone and rmap groups pass. AFAICT they don't cause any new
failures for the 'auto' group.
This is an extraordinary way to eat your data. Enjoy!
Comments and questions are, as always, welcome.
--D
[1] https://github.com/djwong/linux/tree/djwong-devel
[2] https://github.com/djwong/xfsprogs/tree/djwong-devel
[3] https://github.com/djwong/xfstests/tree/djwong-devel
[4] https://github.com/djwong/xfs-documentation/tree/djwong-devel
[5] https://github.com/djwong/man-pages/tree/djwong-devel
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 01/25] xfs: introduce the XFS_IOC_GETFSMAP ioctl
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:40 ` [PATCH 02/25] xfs: report shared extents in getfsmapx Darrick J. Wong
` (23 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Introduce a new ioctl that uses the reverse mapping btree to return
information about the physical layout of the filesystem.
v2: shorten the device field to u32 since that's all we need for
dev_t. Support reporting reverse mapping information for all the
devices that XFS supports (data, log).
v3: don't call the function that reports the journalling log rmap if
we don't have an external device, since the regular rmapbt will take
care of that.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_fs.h | 65 +++++
fs/xfs/xfs_fsmap.c | 571 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_fsmap.h | 29 ++
fs/xfs/xfs_ioctl.c | 77 ++++++
fs/xfs/xfs_ioctl32.c | 1
fs/xfs/xfs_trace.h | 85 +++++++
7 files changed, 829 insertions(+)
create mode 100644 fs/xfs/xfs_fsmap.c
create mode 100644 fs/xfs/xfs_fsmap.h
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 26ef195..5c90f82 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -79,6 +79,7 @@ xfs-y += xfs_aops.o \
xfs_extent_busy.o \
xfs_file.o \
xfs_filestream.o \
+ xfs_fsmap.o \
xfs_fsops.o \
xfs_globals.o \
xfs_icache.o \
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 10ebf99..58e14b14e 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -93,6 +93,70 @@ struct getbmapx {
#define BMV_OF_SHARED 0x8 /* segment shared with another file */
/*
+ * Structure for XFS_IOC_GETFSMAP.
+ *
+ * Similar to XFS_IOC_GETBMAPX, the first two elements in the array are
+ * used to constrain the output. The first element in the array should
+ * represent the lowest disk address that the user wants to learn about.
+ * The second element in the array should represent the highest disk
+ * address to query. Subsequent array elements will be filled out by the
+ * command.
+ *
+ * The fmv_iflags field is only used in the first structure. The
+ * fmv_oflags field is filled in for each returned structure after the
+ * second structure. The fmv_unused1 fields in the first two array
+ * elements must be zero.
+ *
+ * The fmv_count, fmv_entries, and fmv_iflags fields in the second array
+ * element must be zero.
+ *
+ * fmv_block, fmv_offset, and fmv_length are expressed in units of 512
+ * byte sectors.
+ */
+#ifndef HAVE_GETFSMAP
+struct getfsmap {
+ __u32 fmv_device; /* device id */
+ __u32 fmv_unused1; /* future use, must be zero */
+ __u64 fmv_block; /* starting block */
+ __u64 fmv_owner; /* owner id */
+ __u64 fmv_offset; /* file offset of segment */
+ __u64 fmv_length; /* length of segment, blocks */
+ __u32 fmv_oflags; /* mapping flags */
+ __u32 fmv_iflags; /* control flags (1st structure) */
+ __u32 fmv_count; /* # of entries in array incl. input */
+ __u32 fmv_entries; /* # of entries filled in (output). */
+ __u64 fmv_unused2; /* future use, must be zero */
+};
+#endif
+
+/* fmv_iflags values - set by XFS_IOC_GETFSMAP caller in the header. */
+/* no flags defined yet */
+#define FMV_HIF_VALID 0
+
+/* fmv_oflags values - returned in the header segment only. */
+#define FMV_HOF_DEV_T 0x1 /* fmv_device values will be dev_t */
+
+/* fmv_flags values - returned for each non-header segment */
+#define FMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
+#define FMV_OF_ATTR_FORK 0x2 /* segment = attribute fork */
+#define FMV_OF_EXTENT_MAP 0x4 /* segment = extent map */
+#define FMV_OF_SHARED 0x8 /* segment = shared with another file */
+#define FMV_OF_SPECIAL_OWNER 0x10 /* owner is a special value */
+#define FMV_OF_LAST 0x20 /* segment is the last in the FS */
+
+/* fmv_owner special values */
+#define FMV_OWN_FREE (-1ULL) /* free space */
+#define FMV_OWN_UNKNOWN (-2ULL) /* unknown owner */
+#define FMV_OWN_FS (-3ULL) /* static fs metadata */
+#define FMV_OWN_LOG (-4ULL) /* journalling log */
+#define FMV_OWN_AG (-5ULL) /* per-AG metadata */
+#define FMV_OWN_INOBT (-6ULL) /* inode btree blocks */
+#define FMV_OWN_INODES (-7ULL) /* inodes */
+#define FMV_OWN_REFC (-8ULL) /* refcount tree */
+#define FMV_OWN_COW (-9ULL) /* cow staging */
+#define FMV_OWN_DEFECTIVE (-10ULL) /* bad blocks */
+
+/*
* Structure for XFS_IOC_FSSETDM.
* For use by backup and restore programs to set the XFS on-disk inode
* fields di_dmevmask and di_dmstate. These must be set to exactly and
@@ -502,6 +566,7 @@ typedef struct xfs_swapext
#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap)
#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64)
#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks)
+#define XFS_IOC_GETFSMAP _IOWR('X', 59, struct getfsmap)
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c
new file mode 100644
index 0000000..a167acb
--- /dev/null
+++ b/fs/xfs/xfs_fsmap.c
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2016 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_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_error.h"
+#include "xfs_btree.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_rmap.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_fsmap.h"
+
+/* getfsmap query state */
+struct xfs_getfsmap_info {
+ struct getfsmap *fmv; /* vector header */
+ xfs_fsmap_format_t formatter; /* formatting fn */
+ void *format_arg; /* format buffer */
+ bool last; /* last extent? */
+ xfs_daddr_t next_daddr; /* next daddr we expect */
+ u32 dev; /* device id */
+ u64 missing_owner; /* owner of holes */
+
+ xfs_agnumber_t agno; /* AG number, if applicable */
+ struct xfs_buf *agbp; /* AGF, for refcount queries */
+ struct xfs_rmap_irec low; /* low rmap key */
+ struct xfs_rmap_irec high; /* high rmap key */
+};
+
+/* Associate a device with a getfsmap handler. */
+struct xfs_getfsmap_dev {
+ u32 dev;
+ int (*fn)(struct xfs_mount *mp,
+ struct getfsmap *keys,
+ struct xfs_getfsmap_info *info);
+};
+
+/* Compare two getfsmap device handlers. */
+static int
+xfs_getfsmap_dev_compare(
+ const void *p1,
+ const void *p2)
+{
+ const struct xfs_getfsmap_dev *d1 = p1;
+ const struct xfs_getfsmap_dev *d2 = p2;
+
+ return d1->dev - d2->dev;
+}
+
+/* Compare a record against our starting point */
+static bool
+xfs_getfsmap_rec_before_low_key(
+ struct xfs_getfsmap_info *info,
+ struct xfs_rmap_irec *rec)
+{
+ uint64_t x, y;
+
+ if (rec->rm_startblock < info->low.rm_startblock)
+ return true;
+ if (rec->rm_startblock > info->low.rm_startblock)
+ return false;
+
+ if (rec->rm_owner < info->low.rm_owner)
+ return true;
+ if (rec->rm_owner > info->low.rm_owner)
+ return false;
+
+ x = xfs_rmap_irec_offset_pack(rec);
+ y = xfs_rmap_irec_offset_pack(&info->low);
+ if (x < y)
+ return true;
+ return false;
+}
+
+/*
+ * Format a reverse mapping for getfsmap, having translated rm_startblock
+ * into the appropriate daddr units.
+ */
+STATIC int
+xfs_getfsmap_helper(
+ struct xfs_mount *mp,
+ struct xfs_getfsmap_info *info,
+ struct xfs_rmap_irec *rec,
+ xfs_daddr_t rec_daddr)
+{
+ struct getfsmap fmv;
+ xfs_daddr_t key_end;
+ int error;
+
+ /*
+ * Filter out records that start before our startpoint, if the
+ * caller requested that.
+ */
+ if (info->fmv->fmv_length &&
+ xfs_getfsmap_rec_before_low_key(info, rec)) {
+ rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
+ if (info->next_daddr < rec_daddr)
+ info->next_daddr = rec_daddr;
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
+ }
+
+ /*
+ * If the caller passed in a length with the low record and
+ * the record represents a file data extent, we incremented
+ * the offset in the low key by the length in the hopes of
+ * finding reverse mappings for the physical blocks we just
+ * saw. We did /not/ increment next_daddr by the length
+ * because the range query would not be able to find shared
+ * extents within the same physical block range.
+ *
+ * However, the extent we've been fed could have a startblock
+ * past the passed-in low record. If this is the case,
+ * advance next_daddr to the end of the passed-in low record
+ * so we don't report the extent prior to this extent as
+ * free.
+ */
+ key_end = info->fmv->fmv_block + info->fmv->fmv_length;
+ if (info->dev == info->fmv->fmv_device &&
+ info->next_daddr < key_end && rec_daddr >= key_end)
+ info->next_daddr = key_end;
+
+ /* Are we just counting mappings? */
+ if (info->fmv->fmv_count == 2) {
+ if (rec_daddr > info->next_daddr)
+ info->fmv->fmv_entries++;
+
+ if (info->last)
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
+
+ info->fmv->fmv_entries++;
+
+ rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
+ if (info->next_daddr < rec_daddr)
+ info->next_daddr = rec_daddr;
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
+ }
+
+ /*
+ * If the record starts past the last physical block we saw,
+ * then we've found some free space. Report that too.
+ */
+ if (rec_daddr > info->next_daddr) {
+ if (info->fmv->fmv_entries >= info->fmv->fmv_count - 2)
+ return XFS_BTREE_QUERY_RANGE_ABORT;
+
+ trace_xfs_fsmap_mapping(mp, info->dev, info->agno,
+ XFS_DADDR_TO_FSB(mp, info->next_daddr),
+ XFS_DADDR_TO_FSB(mp, rec_daddr -
+ info->next_daddr),
+ info->missing_owner, 0);
+
+ fmv.fmv_device = info->dev;
+ fmv.fmv_block = info->next_daddr;
+ fmv.fmv_owner = info->missing_owner;
+ fmv.fmv_offset = 0;
+ fmv.fmv_length = rec_daddr - info->next_daddr;
+ fmv.fmv_oflags = FMV_OF_SPECIAL_OWNER;
+ fmv.fmv_count = 0;
+ fmv.fmv_entries = 0;
+ fmv.fmv_unused1 = 0;
+ fmv.fmv_unused2 = 0;
+ error = info->formatter(&fmv, info->format_arg);
+ if (error)
+ return error;
+ info->fmv->fmv_entries++;
+ }
+
+ if (info->last)
+ goto out;
+
+ /* Fill out the extent we found */
+ if (info->fmv->fmv_entries >= info->fmv->fmv_count - 2)
+ return XFS_BTREE_QUERY_RANGE_ABORT;
+
+ trace_xfs_fsmap_mapping(mp, info->dev, info->agno,
+ rec->rm_startblock, rec->rm_blockcount, rec->rm_owner,
+ rec->rm_offset);
+
+ fmv.fmv_device = info->dev;
+ fmv.fmv_block = rec_daddr;
+ fmv.fmv_owner = rec->rm_owner;
+ fmv.fmv_offset = XFS_FSB_TO_BB(mp, rec->rm_offset);
+ fmv.fmv_length = XFS_FSB_TO_BB(mp, rec->rm_blockcount);
+ fmv.fmv_oflags = 0;
+ fmv.fmv_count = 0;
+ fmv.fmv_entries = 0;
+ fmv.fmv_unused1 = 0;
+ fmv.fmv_unused2 = 0;
+ if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner))
+ fmv.fmv_oflags |= FMV_OF_SPECIAL_OWNER;
+ if (rec->rm_flags & XFS_RMAP_UNWRITTEN)
+ fmv.fmv_oflags |= FMV_OF_PREALLOC;
+ if (rec->rm_flags & XFS_RMAP_ATTR_FORK)
+ fmv.fmv_oflags |= FMV_OF_ATTR_FORK;
+ if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
+ fmv.fmv_oflags |= FMV_OF_EXTENT_MAP;
+ error = info->formatter(&fmv, info->format_arg);
+ if (error)
+ return error;
+ info->fmv->fmv_entries++;
+
+out:
+ rec_daddr += XFS_FSB_TO_BB(mp, rec->rm_blockcount);
+ if (info->next_daddr < rec_daddr)
+ info->next_daddr = rec_daddr;
+ return XFS_BTREE_QUERY_RANGE_CONTINUE;
+}
+
+/* Transform a rmapbt irec into a fsmap */
+STATIC int
+xfs_getfsmap_datadev_helper(
+ struct xfs_btree_cur *cur,
+ struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ struct xfs_getfsmap_info *info = priv;
+ xfs_fsblock_t fsb;
+ xfs_daddr_t rec_daddr;
+
+ fsb = XFS_AGB_TO_FSB(mp, cur->bc_private.a.agno,
+ rec->rm_startblock);
+ rec_daddr = XFS_FSB_TO_DADDR(mp, fsb);
+
+ return xfs_getfsmap_helper(mp, info, rec, rec_daddr);
+}
+
+/* Transform a absolute-startblock rmap (rtdev, logdev) into a fsmap */
+STATIC int
+xfs_getfsmap_rtdev_helper(
+ struct xfs_btree_cur *cur,
+ struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ struct xfs_getfsmap_info *info = priv;
+ xfs_daddr_t rec_daddr;
+
+ rec_daddr = XFS_FSB_TO_BB(mp, rec->rm_startblock);
+
+ return xfs_getfsmap_helper(mp, info, rec, rec_daddr);
+}
+
+/* Set rmap flags based on the getfsmap flags */
+static void
+xfs_getfsmap_set_irec_flags(
+ struct xfs_rmap_irec *irec,
+ struct getfsmap *fmv)
+{
+ irec->rm_flags = 0;
+ if (fmv->fmv_oflags & FMV_OF_ATTR_FORK)
+ irec->rm_flags |= XFS_RMAP_ATTR_FORK;
+ if (fmv->fmv_oflags & FMV_OF_EXTENT_MAP)
+ irec->rm_flags |= XFS_RMAP_BMBT_BLOCK;
+ if (fmv->fmv_oflags & FMV_OF_PREALLOC)
+ irec->rm_flags |= XFS_RMAP_UNWRITTEN;
+}
+
+/* Execute a getfsmap query against the log device. */
+STATIC int
+xfs_getfsmap_logdev(
+ struct xfs_mount *mp,
+ struct getfsmap *keys,
+ struct xfs_getfsmap_info *info)
+{
+ struct xfs_btree_cur cur;
+ struct getfsmap *lowkey = keys;
+ struct xfs_rmap_irec rmap;
+
+ /* Set up search keys */
+ info->low.rm_startblock = XFS_BB_TO_FSBT(mp, lowkey->fmv_block);
+ info->low.rm_offset = XFS_BB_TO_FSBT(mp, lowkey->fmv_offset);
+ info->low.rm_owner = lowkey->fmv_owner;
+ info->low.rm_blockcount = 0;
+ xfs_getfsmap_set_irec_flags(&info->low, lowkey);
+
+ info->high.rm_startblock = -1U;
+ info->high.rm_owner = ULLONG_MAX;
+ info->high.rm_offset = ULLONG_MAX;
+ info->high.rm_blockcount = 0;
+ info->high.rm_flags = XFS_RMAP_KEY_FLAGS | XFS_RMAP_REC_FLAGS;
+ info->missing_owner = FMV_OWN_FREE;
+
+ trace_xfs_fsmap_low_key(mp, info->dev, info->agno,
+ info->low.rm_startblock,
+ info->low.rm_blockcount,
+ info->low.rm_owner,
+ info->low.rm_offset);
+
+ trace_xfs_fsmap_high_key(mp, info->dev, info->agno,
+ info->high.rm_startblock,
+ info->high.rm_blockcount,
+ info->high.rm_owner,
+ info->high.rm_offset);
+
+
+ if (lowkey->fmv_block > 0)
+ return 0;
+
+ rmap.rm_startblock = 0;
+ rmap.rm_blockcount = mp->m_sb.sb_logblocks;
+ rmap.rm_owner = XFS_RMAP_OWN_LOG;
+ rmap.rm_offset = 0;
+ rmap.rm_flags = 0;
+
+ cur.bc_mp = mp;
+ return xfs_getfsmap_rtdev_helper(&cur, &rmap, info);
+}
+
+/* Execute a getfsmap query against the regular data device. */
+STATIC int
+xfs_getfsmap_datadev(
+ struct xfs_mount *mp,
+ struct getfsmap *keys,
+ struct xfs_getfsmap_info *info)
+{
+ struct xfs_btree_cur *bt_cur = NULL;
+ struct getfsmap *lowkey;
+ struct getfsmap *highkey;
+ xfs_fsblock_t start_fsb;
+ xfs_fsblock_t end_fsb;
+ xfs_agnumber_t start_ag;
+ xfs_agnumber_t end_ag;
+ xfs_daddr_t eofs;
+ int error = 0;
+
+ lowkey = keys;
+ highkey = keys + 1;
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+ if (lowkey->fmv_block >= eofs)
+ return 0;
+ if (highkey->fmv_block >= eofs)
+ highkey->fmv_block = eofs - 1;
+ start_fsb = XFS_DADDR_TO_FSB(mp, lowkey->fmv_block);
+ end_fsb = XFS_DADDR_TO_FSB(mp, highkey->fmv_block);
+
+ /* Set up search keys */
+ info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb);
+ info->low.rm_offset = XFS_BB_TO_FSBT(mp, lowkey->fmv_offset);
+ info->low.rm_owner = lowkey->fmv_owner;
+ info->low.rm_blockcount = 0;
+ xfs_getfsmap_set_irec_flags(&info->low, lowkey);
+
+ info->high.rm_startblock = -1U;
+ info->high.rm_owner = ULLONG_MAX;
+ info->high.rm_offset = ULLONG_MAX;
+ info->high.rm_blockcount = 0;
+ info->high.rm_flags = XFS_RMAP_KEY_FLAGS | XFS_RMAP_REC_FLAGS;
+ info->missing_owner = FMV_OWN_FREE;
+
+ start_ag = XFS_FSB_TO_AGNO(mp, start_fsb);
+ end_ag = XFS_FSB_TO_AGNO(mp, end_fsb);
+
+ /* Query each AG */
+ for (info->agno = start_ag; info->agno <= end_ag; info->agno++) {
+ if (info->agno == end_ag) {
+ info->high.rm_startblock = XFS_FSB_TO_AGBNO(mp,
+ end_fsb);
+ info->high.rm_offset = XFS_BB_TO_FSBT(mp,
+ highkey->fmv_offset);
+ info->high.rm_owner = highkey->fmv_owner;
+ xfs_getfsmap_set_irec_flags(&info->high, highkey);
+ }
+
+ if (bt_cur) {
+ xfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR);
+ xfs_buf_relse(info->agbp);
+ bt_cur = NULL;
+ info->agbp = NULL;
+ }
+
+ error = xfs_alloc_read_agf(mp, NULL, info->agno, 0,
+ &info->agbp);
+ if (error)
+ goto err;
+
+ trace_xfs_fsmap_low_key(mp, info->dev, info->agno,
+ info->low.rm_startblock,
+ info->low.rm_blockcount,
+ info->low.rm_owner,
+ info->low.rm_offset);
+
+ trace_xfs_fsmap_high_key(mp, info->dev, info->agno,
+ info->high.rm_startblock,
+ info->high.rm_blockcount,
+ info->high.rm_owner,
+ info->high.rm_offset);
+
+ bt_cur = xfs_rmapbt_init_cursor(mp, NULL, info->agbp,
+ info->agno);
+ error = xfs_rmap_query_range(bt_cur, &info->low, &info->high,
+ xfs_getfsmap_datadev_helper, info);
+ if (error)
+ goto err;
+
+ if (info->agno == start_ag) {
+ info->low.rm_startblock = 0;
+ info->low.rm_owner = 0;
+ info->low.rm_offset = 0;
+ info->low.rm_flags = 0;
+ }
+ }
+
+ /* Report any free space at the end of the AG */
+ info->last = true;
+ error = xfs_getfsmap_datadev_helper(bt_cur, &info->high, info);
+ if (error)
+ goto err;
+
+err:
+ if (bt_cur)
+ xfs_btree_del_cursor(bt_cur, error < 0 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ if (info->agbp) {
+ xfs_buf_relse(info->agbp);
+ info->agbp = NULL;
+ }
+
+ return error;
+}
+
+/* Do we recognize the device? */
+STATIC bool
+xfs_getfsmap_is_valid_device(
+ struct xfs_mount *mp,
+ struct getfsmap *fmv)
+{
+ if (fmv->fmv_device == 0 || fmv->fmv_device == UINT_MAX ||
+ fmv->fmv_device == new_encode_dev(mp->m_ddev_targp->bt_dev))
+ return true;
+ if (mp->m_logdev_targp &&
+ fmv->fmv_device == new_encode_dev(mp->m_logdev_targp->bt_dev))
+ return true;
+ return false;
+}
+
+#define XFS_GETFSMAP_DEVS 3
+/*
+ * Get filesystem's extents as described in fmv, and format for
+ * output. Calls formatter to fill the user's buffer until all
+ * extents are mapped, until the passed-in fmv->fmv_count slots have
+ * been filled, or until the formatter short-circuits the loop, if it
+ * is tracking filled-in extents on its own.
+ */
+int
+xfs_getfsmap(
+ struct xfs_mount *mp,
+ struct getfsmap *fmv_low,
+ xfs_fsmap_format_t formatter,
+ void *arg)
+{
+ struct getfsmap *fmv_high;
+ struct getfsmap keys[2];
+ struct xfs_getfsmap_dev handlers[XFS_GETFSMAP_DEVS];
+ struct xfs_getfsmap_info info;
+ int i;
+ int error = 0;
+
+ if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+ return -EOPNOTSUPP;
+ if (fmv_low->fmv_count < 2)
+ return -EINVAL;
+ if (fmv_low->fmv_iflags & (~FMV_HIF_VALID))
+ return -EINVAL;
+ fmv_high = fmv_low + 1;
+ if (!xfs_getfsmap_is_valid_device(mp, fmv_low) ||
+ !xfs_getfsmap_is_valid_device(mp, fmv_high) ||
+ fmv_high->fmv_iflags || fmv_high->fmv_count ||
+ fmv_high->fmv_length || fmv_high->fmv_entries ||
+ fmv_high->fmv_unused1 || fmv_low->fmv_unused1 ||
+ fmv_high->fmv_unused2 || fmv_low->fmv_unused2)
+ return -EINVAL;
+
+ fmv_low->fmv_entries = 0;
+
+ /* Set up our device handlers. */
+ memset(handlers, 0, sizeof(handlers));
+ handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
+ handlers[0].fn = xfs_getfsmap_datadev;
+ if (mp->m_logdev_targp != mp->m_ddev_targp) {
+ handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
+ handlers[1].fn = xfs_getfsmap_logdev;
+ }
+
+ xfs_sort(handlers, XFS_GETFSMAP_DEVS, sizeof(struct xfs_getfsmap_dev),
+ xfs_getfsmap_dev_compare);
+
+ /*
+ * Since we allow the user to copy the last fmv item from a previous
+ * call into the low key slot, we have to advance the low key by
+ * whatever the reported length is. If the offset field doesn't apply,
+ * move up the start block to the next extent and start over with the
+ * lowest owner/offset possible; otherwise it's file data, so move up
+ * the offset only.
+ */
+ keys[0] = *fmv_low;
+ if (keys[0].fmv_oflags & (FMV_OF_SPECIAL_OWNER | FMV_OF_EXTENT_MAP)) {
+ keys[0].fmv_block += fmv_low->fmv_length;
+ keys[0].fmv_owner = 0;
+ keys[0].fmv_offset = 0;
+ } else
+ keys[0].fmv_offset += fmv_low->fmv_length;
+ memset(keys + 1, 0xFF, sizeof(struct getfsmap));
+
+ memset(&info, 0, sizeof(info));
+ info.fmv = fmv_low;
+ info.formatter = formatter;
+ info.format_arg = arg;
+
+ /* For each device we support... */
+ for (i = 0; i < XFS_GETFSMAP_DEVS; i++) {
+ /* Is this device within the range the user asked for? */
+ if (!handlers[i].fn)
+ continue;
+ if (fmv_low->fmv_device > handlers[i].dev)
+ continue;
+ if (fmv_high->fmv_device < handlers[i].dev)
+ break;
+
+ /*
+ * If this device number matches the high key, we have
+ * to pass the high key to the handler to limit the
+ * query results. If the device number exceeds the
+ * low key, zero out the low key so that we get
+ * everything from the beginning.
+ */
+ if (handlers[i].dev == fmv_high->fmv_device)
+ keys[1] = *fmv_high;
+ if (handlers[i].dev > fmv_low->fmv_device)
+ memset(keys, 0, sizeof(struct getfsmap));
+
+ info.next_daddr = keys[0].fmv_block;
+ info.dev = handlers[i].dev;
+ info.last = false;
+ info.agno = NULLAGNUMBER;
+ error = handlers[i].fn(mp, keys, &info);
+ if (error)
+ break;
+
+ }
+
+ fmv_low->fmv_oflags = FMV_HOF_DEV_T;
+ return error;
+}
diff --git a/fs/xfs/xfs_fsmap.h b/fs/xfs/xfs_fsmap.h
new file mode 100644
index 0000000..d1d0549
--- /dev/null
+++ b/fs/xfs/xfs_fsmap.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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_FSMAP_H__
+#define __XFS_FSMAP_H__
+
+/* fsmap to userspace formatter - copy to user & advance pointer */
+typedef int (*xfs_fsmap_format_t)(struct getfsmap *, void *);
+
+int xfs_getfsmap(struct xfs_mount *mp, struct getfsmap *fmv,
+ xfs_fsmap_format_t formatter, void *arg);
+
+#endif /* __XFS_FSMAP_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 7007a36..936cb45 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -42,6 +42,8 @@
#include "xfs_pnfs.h"
#include "xfs_acl.h"
#include "xfs_reflink.h"
+#include "xfs_btree.h"
+#include "xfs_fsmap.h"
#include <linux/capability.h>
#include <linux/dcache.h>
@@ -1614,6 +1616,76 @@ xfs_ioc_getbmapx(
return 0;
}
+struct getfsmap_info {
+ struct xfs_mount *mp;
+ struct getfsmap __user *data;
+ __s64 last_flags;
+};
+
+STATIC int
+xfs_getfsmap_format(struct getfsmap *fmv, void *priv)
+{
+ struct getfsmap_info *info = priv;
+
+ trace_xfs_getfsmap_mapping(info->mp, fmv->fmv_device, fmv->fmv_block,
+ fmv->fmv_length, fmv->fmv_owner,
+ fmv->fmv_offset, fmv->fmv_oflags);
+
+ info->last_flags = fmv->fmv_oflags;
+ if (copy_to_user(info->data, fmv, sizeof(struct getfsmap)))
+ return -EFAULT;
+
+ info->data++;
+ return 0;
+}
+
+STATIC int
+xfs_ioc_getfsmap(
+ struct xfs_inode *ip,
+ void __user *arg)
+{
+ struct getfsmap_info info;
+ struct getfsmap fmx[2];
+ bool aborted = false;
+ int error;
+
+ if (copy_from_user(&fmx, arg, 2 * sizeof(struct getfsmap)))
+ return -EFAULT;
+
+ trace_xfs_getfsmap_low_key(ip->i_mount, fmx[0].fmv_device,
+ fmx[0].fmv_block, fmx[0].fmv_length, fmx[0].fmv_owner,
+ fmx[0].fmv_offset, fmx[0].fmv_oflags);
+
+ trace_xfs_getfsmap_high_key(ip->i_mount, fmx[1].fmv_device,
+ fmx[1].fmv_block, fmx[1].fmv_length, fmx[1].fmv_owner,
+ fmx[1].fmv_offset, fmx[1].fmv_oflags);
+
+ info.mp = ip->i_mount;
+ info.data = (__force struct getfsmap *)arg + 2;
+ error = xfs_getfsmap(ip->i_mount, fmx, xfs_getfsmap_format, &info);
+ if (error == XFS_BTREE_QUERY_RANGE_ABORT) {
+ error = 0;
+ aborted = true;
+ }
+ if (error)
+ return error;
+
+ /* If we didn't abort, set the "last" flag in the last fmx */
+ if (!aborted && fmx[0].fmv_entries) {
+ info.data--;
+ info.last_flags |= FMV_OF_LAST;
+ if (copy_to_user(&info.data->fmv_oflags, &info.last_flags,
+ sizeof(info.last_flags)))
+ return -EFAULT;
+ }
+
+ /* copy back header */
+ if (copy_to_user(arg, fmx, 2 * sizeof(struct getfsmap)))
+ return -EFAULT;
+
+ return 0;
+}
+
int
xfs_ioc_swapext(
xfs_swapext_t *sxp)
@@ -1794,6 +1866,11 @@ xfs_file_ioctl(
case XFS_IOC_GETBMAPX:
return xfs_ioc_getbmapx(ip, arg);
+ case XFS_IOC_GETFSMAP:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return xfs_ioc_getfsmap(ip, arg);
+
case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE:
case XFS_IOC_PATH_TO_FSHANDLE: {
diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
index 321f577..9491bc8 100644
--- a/fs/xfs/xfs_ioctl32.c
+++ b/fs/xfs/xfs_ioctl32.c
@@ -554,6 +554,7 @@ xfs_file_compat_ioctl(
case XFS_IOC_GOINGDOWN:
case XFS_IOC_ERROR_INJECTION:
case XFS_IOC_ERROR_CLEARALL:
+ case XFS_IOC_GETFSMAP:
return xfs_file_ioctl(filp, cmd, p);
#ifndef BROKEN_X86_ALIGNMENT
/* These are handled fine if no alignment issues */
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index f980cca..03b5505 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3350,6 +3350,91 @@ DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap);
DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap_piece);
DEFINE_INODE_ERROR_EVENT(xfs_swap_extent_rmap_error);
+/* fsmap traces */
+DECLARE_EVENT_CLASS(xfs_fsmap_class,
+ TP_PROTO(struct xfs_mount *mp, u32 keydev, xfs_agnumber_t agno,
+ xfs_fsblock_t bno, xfs_filblks_t len, __uint64_t owner,
+ __uint64_t offset),
+ TP_ARGS(mp, keydev, agno, bno, len, owner, offset),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(dev_t, keydev)
+ __field(xfs_agnumber_t, agno)
+ __field(xfs_fsblock_t, bno)
+ __field(xfs_filblks_t, len)
+ __field(__uint64_t, owner)
+ __field(__uint64_t, offset)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp->m_super->s_dev;
+ __entry->keydev = new_decode_dev(keydev);
+ __entry->agno = agno;
+ __entry->bno = bno;
+ __entry->len = len;
+ __entry->owner = owner;
+ __entry->offset = offset;
+ ),
+ TP_printk("dev %d:%d keydev %d:%d agno %u bno %llu len %llu owner %lld offset 0x%llx\n",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ MAJOR(__entry->keydev), MINOR(__entry->keydev),
+ __entry->agno,
+ __entry->bno,
+ __entry->len,
+ __entry->owner,
+ __entry->offset)
+)
+#define DEFINE_FSMAP_EVENT(name) \
+DEFINE_EVENT(xfs_fsmap_class, name, \
+ TP_PROTO(struct xfs_mount *mp, u32 keydev, xfs_agnumber_t agno, \
+ xfs_fsblock_t bno, xfs_filblks_t len, __uint64_t owner, \
+ __uint64_t offset), \
+ TP_ARGS(mp, keydev, agno, bno, len, owner, offset))
+DEFINE_FSMAP_EVENT(xfs_fsmap_low_key);
+DEFINE_FSMAP_EVENT(xfs_fsmap_high_key);
+DEFINE_FSMAP_EVENT(xfs_fsmap_mapping);
+
+DECLARE_EVENT_CLASS(xfs_getfsmap_class,
+ TP_PROTO(struct xfs_mount *mp, u32 keydev, xfs_daddr_t block,
+ xfs_daddr_t len, __uint64_t owner, __uint64_t offset,
+ __uint64_t flags),
+ TP_ARGS(mp, keydev, block, len, owner, offset, flags),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(dev_t, keydev)
+ __field(xfs_daddr_t, block)
+ __field(xfs_daddr_t, len)
+ __field(__uint64_t, owner)
+ __field(__uint64_t, offset)
+ __field(__uint64_t, flags)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp->m_super->s_dev;
+ __entry->keydev = new_decode_dev(keydev);
+ __entry->block = block;
+ __entry->len = len;
+ __entry->owner = owner;
+ __entry->offset = offset;
+ __entry->flags = flags;
+ ),
+ TP_printk("dev %d:%d keydev %d:%d block %llu len %llu owner %lld offset %llu flags 0x%llx\n",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ MAJOR(__entry->keydev), MINOR(__entry->keydev),
+ __entry->block,
+ __entry->len,
+ __entry->owner,
+ __entry->offset,
+ __entry->flags)
+)
+#define DEFINE_GETFSMAP_EVENT(name) \
+DEFINE_EVENT(xfs_getfsmap_class, name, \
+ TP_PROTO(struct xfs_mount *mp, u32 keydev, xfs_daddr_t block, \
+ xfs_daddr_t len, __uint64_t owner, __uint64_t offset, \
+ __uint64_t flags), \
+ TP_ARGS(mp, keydev, block, len, owner, offset, flags))
+DEFINE_GETFSMAP_EVENT(xfs_getfsmap_low_key);
+DEFINE_GETFSMAP_EVENT(xfs_getfsmap_high_key);
+DEFINE_GETFSMAP_EVENT(xfs_getfsmap_mapping);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 02/25] xfs: report shared extents in getfsmapx
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
2016-08-25 23:40 ` [PATCH 01/25] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:40 ` [PATCH 03/25] xfs: have getfsmap fall back to the freesp btrees when rmap is not present Darrick J. Wong
` (22 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Cross-reference the reverse mapping data with the refcount btree to find
out which extents are shared.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/xfs_fsmap.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c
index a167acb..2eca7b9 100644
--- a/fs/xfs/xfs_fsmap.c
+++ b/fs/xfs/xfs_fsmap.c
@@ -37,6 +37,8 @@
#include "xfs_alloc.h"
#include "xfs_bit.h"
#include "xfs_fsmap.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
/* getfsmap query state */
struct xfs_getfsmap_info {
@@ -99,6 +101,42 @@ xfs_getfsmap_rec_before_low_key(
return false;
}
+/* Decide if this mapping is shared. */
+STATIC int
+xfs_getfsmap_is_shared(
+ struct xfs_mount *mp,
+ struct xfs_getfsmap_info *info,
+ struct xfs_rmap_irec *rec,
+ bool *stat)
+{
+ struct xfs_btree_cur *cur;
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
+ int error;
+
+ *stat = false;
+ if (!xfs_sb_version_hasreflink(&mp->m_sb))
+ return 0;
+ /* rt files will have agno set to NULLAGNUMBER */
+ if (info->agno == NULLAGNUMBER)
+ return 0;
+
+ /* Are there any shared blocks here? */
+ flen = 0;
+ cur = xfs_refcountbt_init_cursor(mp, NULL, info->agbp,
+ info->agno, NULL);
+
+ error = xfs_refcount_find_shared(cur, rec->rm_startblock,
+ rec->rm_blockcount, &fbno, &flen, false);
+
+ xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ if (error)
+ return error;
+
+ *stat = flen > 0;
+ return 0;
+}
+
/*
* Format a reverse mapping for getfsmap, having translated rm_startblock
* into the appropriate daddr units.
@@ -112,6 +150,7 @@ xfs_getfsmap_helper(
{
struct getfsmap fmv;
xfs_daddr_t key_end;
+ bool shared;
int error;
/*
@@ -221,6 +260,13 @@ xfs_getfsmap_helper(
fmv.fmv_oflags |= FMV_OF_ATTR_FORK;
if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
fmv.fmv_oflags |= FMV_OF_EXTENT_MAP;
+ if (fmv.fmv_oflags == 0) {
+ error = xfs_getfsmap_is_shared(mp, info, rec, &shared);
+ if (error)
+ return error;
+ if (shared)
+ fmv.fmv_oflags |= FMV_OF_SHARED;
+ }
error = info->formatter(&fmv, info->format_arg);
if (error)
return error;
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 03/25] xfs: have getfsmap fall back to the freesp btrees when rmap is not present
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
2016-08-25 23:40 ` [PATCH 01/25] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
2016-08-25 23:40 ` [PATCH 02/25] xfs: report shared extents in getfsmapx Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:40 ` [PATCH 04/25] xfs: getfsmap should fall back to rtbitmap when rtrmapbt " Darrick J. Wong
` (21 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
If the reverse-mapping btree isn't available, fall back to the
free space btrees to provide partial reverse mapping information.
The online scrub tool can make use of even partial information to
speed up the data block scan.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_alloc.c | 42 +++++++++++++
fs/xfs/libxfs/xfs_alloc.h | 10 +++
fs/xfs/xfs_fsmap.c | 151 ++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 200 insertions(+), 3 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 6e81b27..8b3e6b3 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -2932,3 +2932,45 @@ err:
xfs_trans_brelse(tp, agbp);
return error;
}
+
+struct xfs_alloc_query_range_info {
+ xfs_alloc_query_range_fn fn;
+ void *priv;
+};
+
+/* Format btree record and pass to our callback. */
+STATIC int
+xfs_alloc_query_range_helper(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ void *priv)
+{
+ struct xfs_alloc_query_range_info *query = priv;
+ struct xfs_alloc_rec_incore irec;
+
+ irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
+ irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
+ return query->fn(cur, &irec, query->priv);
+}
+
+/* Find all rmaps between two keys. */
+int
+xfs_alloc_query_range(
+ struct xfs_btree_cur *cur,
+ struct xfs_alloc_rec_incore *low_rec,
+ struct xfs_alloc_rec_incore *high_rec,
+ xfs_alloc_query_range_fn fn,
+ void *priv)
+{
+ union xfs_btree_irec low_brec;
+ union xfs_btree_irec high_brec;
+ struct xfs_alloc_query_range_info query;
+
+ ASSERT(cur->bc_btnum == XFS_BTNUM_BNO);
+ low_brec.a = *low_rec;
+ high_brec.a = *high_rec;
+ query.priv = priv;
+ query.fn = fn;
+ return xfs_btree_query_range(cur, &low_brec, &high_brec,
+ xfs_alloc_query_range_helper, &query);
+}
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index f7c5201..0b00de0 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -210,4 +210,14 @@ int xfs_free_extent_fix_freelist(struct xfs_trans *tp, xfs_agnumber_t agno,
xfs_extlen_t xfs_prealloc_blocks(struct xfs_mount *mp);
+typedef int (*xfs_alloc_query_range_fn)(
+ struct xfs_btree_cur *cur,
+ struct xfs_alloc_rec_incore *rec,
+ void *priv);
+
+int xfs_alloc_query_range(struct xfs_btree_cur *cur,
+ struct xfs_alloc_rec_incore *low_rec,
+ struct xfs_alloc_rec_incore *high_rec,
+ xfs_alloc_query_range_fn fn, void *priv);
+
#endif /* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c
index 2eca7b9..eb54884 100644
--- a/fs/xfs/xfs_fsmap.c
+++ b/fs/xfs/xfs_fsmap.c
@@ -39,6 +39,7 @@
#include "xfs_fsmap.h"
#include "xfs_refcount.h"
#include "xfs_refcount_btree.h"
+#include "xfs_alloc_btree.h"
/* getfsmap query state */
struct xfs_getfsmap_info {
@@ -314,6 +315,32 @@ xfs_getfsmap_rtdev_helper(
return xfs_getfsmap_helper(mp, info, rec, rec_daddr);
}
+/* Transform a bnobt irec into a fsmap */
+STATIC int
+xfs_getfsmap_datadev_bnobt_helper(
+ struct xfs_btree_cur *cur,
+ struct xfs_alloc_rec_incore *rec,
+ void *priv)
+{
+ struct xfs_mount *mp = cur->bc_mp;
+ struct xfs_getfsmap_info *info = priv;
+ struct xfs_rmap_irec irec;
+ xfs_fsblock_t fsb;
+ xfs_daddr_t rec_daddr;
+
+ fsb = XFS_AGB_TO_FSB(mp, cur->bc_private.a.agno,
+ rec->ar_startblock);
+ rec_daddr = XFS_FSB_TO_DADDR(mp, fsb);
+
+ irec.rm_startblock = rec->ar_startblock;
+ irec.rm_blockcount = rec->ar_blockcount;
+ irec.rm_owner = XFS_RMAP_OWN_NULL; /* "free" */
+ irec.rm_offset = 0;
+ irec.rm_flags = 0;
+
+ return xfs_getfsmap_helper(mp, info, &irec, rec_daddr);
+}
+
/* Set rmap flags based on the getfsmap flags */
static void
xfs_getfsmap_set_irec_flags(
@@ -492,6 +519,123 @@ err:
return error;
}
+/* Execute a getfsmap query against the regular data device's bnobt. */
+STATIC int
+xfs_getfsmap_datadev_bnobt(
+ struct xfs_mount *mp,
+ struct getfsmap *keys,
+ struct xfs_getfsmap_info *info)
+{
+ struct xfs_btree_cur *bt_cur = NULL;
+ struct getfsmap *lowkey;
+ struct getfsmap *highkey;
+ struct xfs_alloc_rec_incore alow;
+ struct xfs_alloc_rec_incore ahigh;
+ xfs_fsblock_t start_fsb;
+ xfs_fsblock_t end_fsb;
+ xfs_agnumber_t start_ag;
+ xfs_agnumber_t end_ag;
+ xfs_daddr_t eofs;
+ int error = 0;
+
+ lowkey = keys;
+ highkey = keys + 1;
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+ if (lowkey->fmv_block >= eofs)
+ return 0;
+ if (highkey->fmv_block >= eofs)
+ highkey->fmv_block = eofs - 1;
+ start_fsb = XFS_DADDR_TO_FSB(mp, lowkey->fmv_block);
+ end_fsb = XFS_DADDR_TO_FSB(mp, highkey->fmv_block);
+
+ /* Set up search keys */
+ info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb);
+ info->low.rm_offset = XFS_BB_TO_FSBT(mp, lowkey->fmv_offset);
+ info->low.rm_owner = lowkey->fmv_owner;
+ info->low.rm_blockcount = 0;
+ xfs_getfsmap_set_irec_flags(&info->low, lowkey);
+
+ info->high.rm_startblock = -1U;
+ info->high.rm_owner = ULLONG_MAX;
+ info->high.rm_offset = ULLONG_MAX;
+ info->high.rm_blockcount = 0;
+ info->high.rm_flags = XFS_RMAP_KEY_FLAGS | XFS_RMAP_REC_FLAGS;
+
+ info->missing_owner = FMV_OWN_UNKNOWN;
+
+ start_ag = XFS_FSB_TO_AGNO(mp, start_fsb);
+ end_ag = XFS_FSB_TO_AGNO(mp, end_fsb);
+
+ /* Query each AG */
+ for (info->agno = start_ag; info->agno <= end_ag; info->agno++) {
+ if (info->agno == end_ag) {
+ info->high.rm_startblock = XFS_FSB_TO_AGBNO(mp,
+ end_fsb);
+ info->high.rm_offset = XFS_BB_TO_FSBT(mp,
+ highkey->fmv_offset);
+ info->high.rm_owner = highkey->fmv_owner;
+ xfs_getfsmap_set_irec_flags(&info->high, highkey);
+ }
+
+ if (bt_cur) {
+ xfs_btree_del_cursor(bt_cur, XFS_BTREE_NOERROR);
+ xfs_buf_relse(info->agbp);
+ bt_cur = NULL;
+ info->agbp = NULL;
+ }
+
+ error = xfs_alloc_read_agf(mp, NULL, info->agno, 0,
+ &info->agbp);
+ if (error)
+ goto err;
+
+ trace_xfs_fsmap_low_key(mp, info->dev, info->agno,
+ info->low.rm_startblock,
+ info->low.rm_blockcount,
+ info->low.rm_owner,
+ info->low.rm_offset);
+
+ trace_xfs_fsmap_high_key(mp, info->dev, info->agno,
+ info->high.rm_startblock,
+ info->high.rm_blockcount,
+ info->high.rm_owner,
+ info->high.rm_offset);
+
+ bt_cur = xfs_allocbt_init_cursor(mp, NULL, info->agbp,
+ info->agno, XFS_BTNUM_BNO);
+ alow.ar_startblock = info->low.rm_startblock;
+ ahigh.ar_startblock = info->high.rm_startblock;
+ error = xfs_alloc_query_range(bt_cur, &alow, &ahigh,
+ xfs_getfsmap_datadev_bnobt_helper, info);
+ if (error)
+ goto err;
+
+ if (info->agno == start_ag) {
+ info->low.rm_startblock = 0;
+ info->low.rm_owner = 0;
+ info->low.rm_offset = 0;
+ info->low.rm_flags = 0;
+ }
+ }
+
+ /* Report any free space at the end of the AG */
+ info->last = true;
+ error = xfs_getfsmap_datadev_bnobt_helper(bt_cur, &ahigh, info);
+ if (error)
+ goto err;
+
+err:
+ if (bt_cur)
+ xfs_btree_del_cursor(bt_cur, error < 0 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ if (info->agbp) {
+ xfs_buf_relse(info->agbp);
+ info->agbp = NULL;
+ }
+
+ return error;
+}
+
/* Do we recognize the device? */
STATIC bool
xfs_getfsmap_is_valid_device(
@@ -529,8 +673,6 @@ xfs_getfsmap(
int i;
int error = 0;
- if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
- return -EOPNOTSUPP;
if (fmv_low->fmv_count < 2)
return -EINVAL;
if (fmv_low->fmv_iflags & (~FMV_HIF_VALID))
@@ -549,7 +691,10 @@ xfs_getfsmap(
/* Set up our device handlers. */
memset(handlers, 0, sizeof(handlers));
handlers[0].dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
- handlers[0].fn = xfs_getfsmap_datadev;
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+ handlers[0].fn = xfs_getfsmap_datadev;
+ else
+ handlers[0].fn = xfs_getfsmap_datadev_bnobt;
if (mp->m_logdev_targp != mp->m_ddev_targp) {
handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
handlers[1].fn = xfs_getfsmap_logdev;
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 04/25] xfs: getfsmap should fall back to rtbitmap when rtrmapbt not present
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (2 preceding siblings ...)
2016-08-25 23:40 ` [PATCH 03/25] xfs: have getfsmap fall back to the freesp btrees when rmap is not present Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:40 ` [PATCH 05/25] xfs: add scrub tracepoints Darrick J. Wong
` (20 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/xfs_fsmap.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c
index eb54884..1c5ce01 100644
--- a/fs/xfs/xfs_fsmap.c
+++ b/fs/xfs/xfs_fsmap.c
@@ -40,6 +40,7 @@
#include "xfs_refcount.h"
#include "xfs_refcount_btree.h"
#include "xfs_alloc_btree.h"
+#include "xfs_rtalloc.h"
/* getfsmap query state */
struct xfs_getfsmap_info {
@@ -315,6 +316,29 @@ xfs_getfsmap_rtdev_helper(
return xfs_getfsmap_helper(mp, info, rec, rec_daddr);
}
+/* Transform a rtbitmap "record" into a fsmap */
+STATIC int
+xfs_getfsmap_rtdev_rtbitmap_helper(
+ struct xfs_mount *mp,
+ xfs_rtblock_t start,
+ xfs_rtblock_t end,
+ void *priv)
+{
+ struct xfs_getfsmap_info *info = priv;
+ struct xfs_rmap_irec irec;
+ xfs_daddr_t rec_daddr;
+
+ rec_daddr = XFS_FSB_TO_BB(mp, start);
+
+ irec.rm_startblock = start;
+ irec.rm_blockcount = end - start + 1;
+ irec.rm_owner = XFS_RMAP_OWN_NULL; /* "free" */
+ irec.rm_offset = 0;
+ irec.rm_flags = 0;
+
+ return xfs_getfsmap_helper(mp, info, &irec, rec_daddr);
+}
+
/* Transform a bnobt irec into a fsmap */
STATIC int
xfs_getfsmap_datadev_bnobt_helper(
@@ -407,6 +431,103 @@ xfs_getfsmap_logdev(
return xfs_getfsmap_rtdev_helper(&cur, &rmap, info);
}
+/* Execute a getfsmap query against the realtime data device (rtbitmap). */
+STATIC int
+xfs_getfsmap_rtdev_rtbitmap(
+ struct xfs_mount *mp,
+ struct getfsmap *keys,
+ struct xfs_getfsmap_info *info)
+{
+ struct getfsmap *lowkey;
+ struct getfsmap *highkey;
+ xfs_fsblock_t start_fsb;
+ xfs_fsblock_t end_fsb;
+ xfs_rtblock_t rtstart;
+ xfs_rtblock_t rtend;
+ xfs_rtblock_t rem;
+ xfs_daddr_t eofs;
+ int is_free;
+ int error = 0;
+
+ lowkey = keys;
+ highkey = keys + 1;
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
+ if (lowkey->fmv_block >= eofs)
+ return 0;
+ if (highkey->fmv_block >= eofs)
+ highkey->fmv_block = eofs - 1;
+ start_fsb = XFS_BB_TO_FSBT(mp, lowkey->fmv_block);
+ end_fsb = XFS_BB_TO_FSB(mp, highkey->fmv_block);
+
+ /* Set up search keys */
+ info->low.rm_startblock = start_fsb;
+ info->low.rm_owner = lowkey->fmv_owner;
+ info->low.rm_offset = XFS_BB_TO_FSBT(mp, lowkey->fmv_offset);
+ info->low.rm_blockcount = 0;
+ xfs_getfsmap_set_irec_flags(&info->low, lowkey);
+
+ info->high.rm_startblock = end_fsb;
+ info->high.rm_owner = highkey->fmv_owner;
+ info->high.rm_offset = XFS_BB_TO_FSBT(mp, highkey->fmv_offset);
+ info->high.rm_blockcount = 0;
+ xfs_getfsmap_set_irec_flags(&info->high, highkey);
+
+ info->missing_owner = FMV_OWN_UNKNOWN;
+
+ trace_xfs_fsmap_low_key(mp, info->dev, info->agno,
+ info->low.rm_startblock,
+ info->low.rm_blockcount,
+ info->low.rm_owner,
+ info->low.rm_offset);
+
+ trace_xfs_fsmap_high_key(mp, info->dev, info->agno,
+ info->high.rm_startblock,
+ info->high.rm_blockcount,
+ info->high.rm_owner,
+ info->high.rm_offset);
+
+ xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+
+ /* Iterate the bitmap, looking for discrepancies. */
+ rtstart = 0;
+ rem = mp->m_sb.sb_rblocks;
+ while (rem) {
+ /* Is the first block free? */
+ error = xfs_rtcheck_range(mp, NULL, rtstart, 1, 1, &rtend,
+ &is_free);
+ if (error)
+ goto out_unlock;
+
+ /* How long does the extent go for? */
+ error = xfs_rtfind_forw(mp, NULL, rtstart,
+ mp->m_sb.sb_rblocks - 1, &rtend);
+ if (error)
+ goto out_unlock;
+
+ if (is_free) {
+ error = xfs_getfsmap_rtdev_rtbitmap_helper(mp,
+ rtstart, rtend, info);
+ if (error)
+ goto out_unlock;
+ }
+
+ rem -= rtend - rtstart + 1;
+ rtstart = rtend + 1;
+ }
+
+out_unlock:
+ xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+
+ /* Report any free space at the end of the rtdev */
+ info->last = true;
+ error = xfs_getfsmap_rtdev_rtbitmap_helper(mp, end_fsb, 0, info);
+ if (error)
+ goto err;
+
+err:
+ return error;
+}
+
/* Execute a getfsmap query against the regular data device. */
STATIC int
xfs_getfsmap_datadev(
@@ -699,6 +820,11 @@ xfs_getfsmap(
handlers[1].dev = new_encode_dev(mp->m_logdev_targp->bt_dev);
handlers[1].fn = xfs_getfsmap_logdev;
}
+ if (mp->m_rtdev_targp) {
+ handlers[2].dev = new_encode_dev(mp->m_rtdev_targp->bt_dev);
+ if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+ handlers[2].fn = xfs_getfsmap_rtdev_rtbitmap;
+ }
xfs_sort(handlers, XFS_GETFSMAP_DEVS, sizeof(struct xfs_getfsmap_dev),
xfs_getfsmap_dev_compare);
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 05/25] xfs: add scrub tracepoints
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (3 preceding siblings ...)
2016-08-25 23:40 ` [PATCH 04/25] xfs: getfsmap should fall back to rtbitmap when rtrmapbt " Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:40 ` [PATCH 06/25] xfs: generic functions to scrub metadata and btrees Darrick J. Wong
` (19 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/xfs_trace.h | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 03b5505..74963a1 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3435,6 +3435,83 @@ DEFINE_GETFSMAP_EVENT(xfs_getfsmap_low_key);
DEFINE_GETFSMAP_EVENT(xfs_getfsmap_high_key);
DEFINE_GETFSMAP_EVENT(xfs_getfsmap_mapping);
+/* scrub */
+DECLARE_EVENT_CLASS(xfs_scrub_class,
+ TP_PROTO(struct xfs_inode *ip, int type, unsigned long long control,
+ unsigned int flags, int error),
+ TP_ARGS(ip, type, control, flags, error),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(int, type)
+ __field(unsigned long long, control)
+ __field(unsigned int, flags)
+ __field(int, error)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ __entry->type = type;
+ __entry->control = control;
+ __entry->flags = flags;
+ __entry->error = error;
+ ),
+ TP_printk("dev %d:%d ino %llu type %u ctl %llu flags 0x%x error %d\n",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->type,
+ __entry->control,
+ __entry->flags,
+ __entry->error)
+)
+#define DEFINE_SCRUB_EVENT(name) \
+DEFINE_EVENT(xfs_scrub_class, name, \
+ TP_PROTO(struct xfs_inode *ip, int type, unsigned long long control, \
+ unsigned int flags, int error), \
+ TP_ARGS(ip, type, control, flags, error))
+
+DECLARE_EVENT_CLASS(xfs_scrub_sbtree_class,
+ TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t bno,
+ xfs_btnum_t btnum, int level, int nlevels, int ptr),
+ TP_ARGS(mp, agno, bno, btnum, level, nlevels, ptr),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_btnum_t, btnum)
+ __field(xfs_agnumber_t, agno)
+ __field(xfs_agblock_t, bno)
+ __field(int, level)
+ __field(int, nlevels)
+ __field(int, ptr)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp->m_super->s_dev;
+ __entry->agno = agno;
+ __entry->btnum = btnum;
+ __entry->bno = bno;
+ __entry->level = level;
+ __entry->nlevels = nlevels;
+ __entry->ptr = ptr;
+ ),
+ TP_printk("dev %d:%d agno %u agbno %u btnum %d level %d nlevels %d ptr %d\n",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->agno,
+ __entry->bno,
+ __entry->btnum,
+ __entry->level,
+ __entry->nlevels,
+ __entry->ptr)
+)
+#define DEFINE_SCRUB_SBTREE_EVENT(name) \
+DEFINE_EVENT(xfs_scrub_sbtree_class, name, \
+ TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t bno, \
+ xfs_btnum_t btnum, int level, int nlevels, int ptr), \
+ TP_ARGS(mp, agno, bno, btnum, level, nlevels, ptr))
+
+DEFINE_SCRUB_EVENT(xfs_scrub);
+DEFINE_SCRUB_EVENT(xfs_scrub_done);
+DEFINE_SCRUB_SBTREE_EVENT(xfs_scrub_btree_rec);
+DEFINE_SCRUB_SBTREE_EVENT(xfs_scrub_btree_key);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 06/25] xfs: generic functions to scrub metadata and btrees
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (4 preceding siblings ...)
2016-08-25 23:40 ` [PATCH 05/25] xfs: add scrub tracepoints Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:40 ` [PATCH 07/25] xfs: create an ioctl to scrub AG metadata Darrick J. Wong
` (18 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Create a function that walks a btree, checking the integrity of each
btree block (headers, keys, records) and calling back to the caller
to perform further checks on the records. Add some helper functions
so that we report detailed scrub errors in a uniform manner in dmesg.
These are helper functions for subsequent patches.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_btree.c | 41 ++-
fs/xfs/libxfs/xfs_btree.h | 17 +
fs/xfs/libxfs/xfs_format.h | 2
fs/xfs/xfs_scrub.c | 705 ++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_scrub.h | 25 ++
6 files changed, 782 insertions(+), 9 deletions(-)
create mode 100644 fs/xfs/xfs_scrub.c
create mode 100644 fs/xfs/xfs_scrub.h
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 5c90f82..a903bd3 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -92,6 +92,7 @@ xfs-y += xfs_aops.o \
xfs_mount.o \
xfs_mru_cache.o \
xfs_reflink.o \
+ xfs_scrub.o \
xfs_stats.o \
xfs_super.o \
xfs_symlink.o \
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 2552c03..a926c54 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -552,7 +552,7 @@ xfs_btree_ptr_offset(
/*
* Return a pointer to the n-th record in the btree block.
*/
-STATIC union xfs_btree_rec *
+union xfs_btree_rec *
xfs_btree_rec_addr(
struct xfs_btree_cur *cur,
int n,
@@ -565,7 +565,7 @@ xfs_btree_rec_addr(
/*
* Return a pointer to the n-th key in the btree block.
*/
-STATIC union xfs_btree_key *
+union xfs_btree_key *
xfs_btree_key_addr(
struct xfs_btree_cur *cur,
int n,
@@ -578,7 +578,7 @@ xfs_btree_key_addr(
/*
* Return a pointer to the n-th high key in the btree block.
*/
-STATIC union xfs_btree_key *
+union xfs_btree_key *
xfs_btree_high_key_addr(
struct xfs_btree_cur *cur,
int n,
@@ -591,7 +591,7 @@ xfs_btree_high_key_addr(
/*
* Return a pointer to the n-th block pointer in the btree block.
*/
-STATIC union xfs_btree_ptr *
+union xfs_btree_ptr *
xfs_btree_ptr_addr(
struct xfs_btree_cur *cur,
int n,
@@ -625,7 +625,7 @@ xfs_btree_get_iroot(
* Retrieve the block pointer from the cursor at the given level.
* This may be an inode btree root or from a buffer.
*/
-STATIC struct xfs_btree_block * /* generic btree block pointer */
+struct xfs_btree_block * /* generic btree block pointer */
xfs_btree_get_block(
struct xfs_btree_cur *cur, /* btree cursor */
int level, /* level in btree */
@@ -1736,7 +1736,7 @@ error0:
return error;
}
-STATIC int
+int
xfs_btree_lookup_get_block(
struct xfs_btree_cur *cur, /* btree cursor */
int level, /* level in the btree */
@@ -4852,3 +4852,32 @@ xfs_btree_count_blocks(
return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper,
blocks);
}
+
+/* If there's an extent, we're done. */
+STATIC int
+xfs_btree_has_record_helper(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *rec,
+ void *priv)
+{
+ return XFS_BTREE_QUERY_RANGE_ABORT;
+}
+
+/* Is there a record covering a given range of keys? */
+int
+xfs_btree_has_record(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_irec *low,
+ union xfs_btree_irec *high,
+ bool *exists)
+{
+ int error;
+
+ error = xfs_btree_query_range(cur, low, high,
+ &xfs_btree_has_record_helper, NULL);
+ if (error && error != XFS_BTREE_QUERY_RANGE_ABORT)
+ return error;
+ *exists = error == XFS_BTREE_QUERY_RANGE_ABORT;
+
+ return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index eb20376..f81b2a8 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -197,7 +197,6 @@ struct xfs_btree_ops {
const struct xfs_buf_ops *buf_ops;
-#if defined(DEBUG) || defined(XFS_WARN)
/* check that k1 is lower than k2 */
int (*keys_inorder)(struct xfs_btree_cur *cur,
union xfs_btree_key *k1,
@@ -207,7 +206,6 @@ struct xfs_btree_ops {
int (*recs_inorder)(struct xfs_btree_cur *cur,
union xfs_btree_rec *r1,
union xfs_btree_rec *r2);
-#endif
};
/*
@@ -537,4 +535,19 @@ int xfs_btree_visit_blocks(struct xfs_btree_cur *cur,
int xfs_btree_count_blocks(struct xfs_btree_cur *cur, xfs_extlen_t *blocks);
+union xfs_btree_rec *xfs_btree_rec_addr(struct xfs_btree_cur *cur, int n,
+ struct xfs_btree_block *block);
+union xfs_btree_key *xfs_btree_key_addr(struct xfs_btree_cur *cur, int n,
+ struct xfs_btree_block *block);
+union xfs_btree_key *xfs_btree_high_key_addr(struct xfs_btree_cur *cur, int n,
+ struct xfs_btree_block *block);
+union xfs_btree_ptr *xfs_btree_ptr_addr(struct xfs_btree_cur *cur, int n,
+ struct xfs_btree_block *block);
+int xfs_btree_lookup_get_block(struct xfs_btree_cur *cur, int level,
+ union xfs_btree_ptr *pp, struct xfs_btree_block **blkp);
+struct xfs_btree_block *xfs_btree_get_block(struct xfs_btree_cur *cur,
+ int level, struct xfs_buf **bpp);
+int xfs_btree_has_record(struct xfs_btree_cur *cur, union xfs_btree_irec *low,
+ union xfs_btree_irec *high, bool *exists);
+
#endif /* __XFS_BTREE_H__ */
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index bf40fa8..a3aa5e9 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -518,7 +518,7 @@ static inline int xfs_sb_version_hasftype(struct xfs_sb *sbp)
(sbp->sb_features2 & XFS_SB_VERSION2_FTYPE));
}
-static inline int xfs_sb_version_hasfinobt(xfs_sb_t *sbp)
+static inline bool xfs_sb_version_hasfinobt(xfs_sb_t *sbp)
{
return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) &&
(sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_FINOBT);
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
new file mode 100644
index 0000000..13bea55
--- /dev/null
+++ b/fs/xfs/xfs_scrub.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) 2016 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_trace.h"
+#include "xfs_scrub.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_rtalloc.h"
+
+/* Report a scrub corruption in dmesg. */
+STATIC void
+xfs_scrub_error(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp,
+ const char *type,
+ const char *func,
+ int line,
+ const char *check)
+{
+ xfs_fsblock_t fsbno;
+
+ fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+ xfs_alert(mp, "scrub: %s corruption in block %u/%u: %s, func: %s, line: %d",
+ type,
+ XFS_FSB_TO_AGNO(mp, fsbno),
+ XFS_FSB_TO_AGBNO(mp, fsbno),
+ check, func, line);
+}
+
+#define XFS_SCRUB_CHECK(mp, bp, type, fs_ok) \
+ if (!(fs_ok)) { \
+ xfs_scrub_error((mp), (bp), (type), __func__, __LINE__, #fs_ok); \
+ error = -EFSCORRUPTED; \
+ }
+#define XFS_SCRUB_GOTO(mp, bp, type, fs_ok, label) \
+ if (!(fs_ok)) { \
+ xfs_scrub_error((mp), (bp), (type), __func__, __LINE__, #fs_ok); \
+ error = -EFSCORRUPTED; \
+ goto label; \
+ }
+
+/* Report a scrub corruption in dmesg. */
+STATIC void
+xfs_scrub_ino_error(
+ struct xfs_inode *ip,
+ struct xfs_buf *bp,
+ const char *type,
+ const char *func,
+ int line,
+ const char *check)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_fsblock_t fsbno;
+
+ if (!bp) {
+ xfs_alert(mp, "scrub: inode %llu %s corruption: %s, "
+ "func: %s, line: %d",
+ ip->i_ino,
+ type,
+ check, func, line);
+ return;
+ }
+
+ fsbno = XFS_DADDR_TO_FSB(mp, bp->b_bn);
+ xfs_alert(mp, "scrub: inode %llu %s corruption in block %u/%u: %s, "
+ "func: %s, line: %d",
+ ip->i_ino,
+ type,
+ XFS_FSB_TO_AGNO(mp, fsbno),
+ XFS_FSB_TO_AGBNO(mp, fsbno),
+ check, func, line);
+}
+
+#define XFS_INO_SCRUB_CHECK(ip, bp, type, fs_ok) \
+ if (!(fs_ok)) { \
+ xfs_scrub_ino_error((ip), (bp), (type), __func__, __LINE__, #fs_ok); \
+ error = -EFSCORRUPTED; \
+ }
+#define XFS_INO_SCRUB_GOTO(ip, bp, type, fs_ok, label) \
+ if (!(fs_ok)) { \
+ xfs_scrub_ino_error((ip), (bp), (type), __func__, __LINE__, #fs_ok); \
+ error = -EFSCORRUPTED; \
+ goto label; \
+ }
+
+/* btree scrubbing */
+
+static const char * const btree_types[] = {
+ [XFS_BTNUM_BNO] = "bnobt",
+ [XFS_BTNUM_CNT] = "cntbt",
+ [XFS_BTNUM_RMAP] = "rmapbt",
+ [XFS_BTNUM_BMAP] = "bmapbt",
+ [XFS_BTNUM_INO] = "inobt",
+ [XFS_BTNUM_FINO] = "finobt",
+ [XFS_BTNUM_REFC] = "refcountbt",
+};
+
+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_btree_cur *cur;
+ xfs_scrub_btree_rec_fn scrub_rec;
+ struct xfs_buf *agi_bp;
+ struct xfs_buf *agf_bp;
+ struct xfs_buf *agfl_bp;
+ struct xfs_owner_info oinfo;
+
+ /* internal scrub state */
+ union xfs_btree_rec lastrec;
+ bool firstrec;
+ union xfs_btree_key lastkey[XFS_BTREE_MAXLEVELS];
+ bool firstkey[XFS_BTREE_MAXLEVELS];
+ struct xfs_btree_cur *bno_cur;
+ struct xfs_btree_cur *cnt_cur;
+ struct xfs_btree_cur *ino_cur;
+ struct xfs_btree_cur *fino_cur;
+ struct xfs_btree_cur *rmap_cur;
+ struct xfs_btree_cur *refc_cur;
+ struct list_head to_check;
+ int error;
+};
+
+/* Report a scrub corruption in dmesg. */
+STATIC void
+xfs_scrub_btree_error(
+ struct xfs_btree_cur *cur,
+ int level,
+ const char *func,
+ int line,
+ const char *check)
+{
+ char buf[24];
+ char descr[48];
+ const char *type;
+ struct xfs_buf *bp;
+ struct xfs_btree_block *block;
+ xfs_fsblock_t fsbno;
+
+ switch (cur->bc_btnum) {
+ case XFS_BTNUM_BMAP:
+ switch (cur->bc_private.b.whichfork) {
+ case XFS_DATA_FORK:
+ type = "data";
+ break;
+ case XFS_ATTR_FORK:
+ type = "attr";
+ break;
+ case XFS_COW_FORK:
+ type = "CoW";
+ break;
+ }
+ snprintf(descr, 48, "inode %llu %s fork",
+ (unsigned long long)cur->bc_private.b.ip->i_ino,
+ type);
+ type = descr;
+ break;
+ default:
+ type = btree_types[cur->bc_btnum];
+ break;
+ }
+
+ if (level < cur->bc_nlevels && cur->bc_ptrs[level] >= 1) {
+ block = xfs_btree_get_block(cur, level, &bp);
+ snprintf(buf, 24, " %s %d/%d", level == 0 ? "rec" : "ptr",
+ cur->bc_ptrs[level],
+ be16_to_cpu(block->bb_numrecs));
+ } else
+ buf[0] = 0;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ level >= cur->bc_nlevels - 1) {
+ xfs_alert(cur->bc_mp, "scrub: %s btree corruption in inode "
+ "%llu root%s: %s, func: %s, line: %d",
+ type, cur->bc_private.b.ip->i_ino,
+ buf, check, func, line);
+ } else if (!cur->bc_bufs[level]) {
+ xfs_alert(cur->bc_mp, "scrub: %s btree corruption, "
+ "func: %s, line: %d",
+ type, func, line);
+ } else {
+ fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, cur->bc_bufs[level]->b_bn);
+ xfs_alert(cur->bc_mp, "scrub: %s btree corruption in block "
+ "%u/%u%s: %s, func: %s, line: %d",
+ type,
+ XFS_FSB_TO_AGNO(cur->bc_mp, fsbno),
+ XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno),
+ buf, check, func, line);
+ }
+}
+
+#define XFS_BTREC_SCRUB_CHECK(bs, fs_ok) \
+ if (!(fs_ok)) { \
+ xfs_scrub_btree_error((bs)->cur, 0, __func__, __LINE__, #fs_ok); \
+ (bs)->error = -EFSCORRUPTED; \
+ }
+#define XFS_BTREC_SCRUB_GOTO(bs, fs_ok, label) \
+ if (!(fs_ok)) { \
+ xfs_scrub_btree_error((bs)->cur, 0, __func__, __LINE__, #fs_ok); \
+ (bs)->error = -EFSCORRUPTED; \
+ goto label; \
+ }
+#define XFS_BTKEY_SCRUB_CHECK(bs, level, fs_ok) \
+ if (!(fs_ok)) { \
+ xfs_scrub_btree_error((bs)->cur, (level), __func__, __LINE__, #fs_ok); \
+ (bs)->error = -EFSCORRUPTED; \
+ }
+#define XFS_BTKEY_SCRUB_GOTO(bs, level, fs_ok, label) \
+ if (!(fs_ok)) { \
+ xfs_scrub_btree_error((bs)->cur, (level), __func__, __LINE__, #fs_ok); \
+ (bs)->error = -EFSCORRUPTED; \
+ goto label; \
+ }
+
+/*
+ * Make sure this record is in order and doesn't stray outside of the parent
+ * keys.
+ */
+STATIC int
+xfs_scrub_btree_rec(
+ struct xfs_scrub_btree *bs)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ union xfs_btree_rec *rec;
+ union xfs_btree_key key;
+ union xfs_btree_key hkey;
+ union xfs_btree_key *keyp;
+ struct xfs_btree_block *block;
+ struct xfs_btree_block *keyblock;
+ struct xfs_buf *bp;
+
+ block = xfs_btree_get_block(cur, 0, &bp);
+ rec = xfs_btree_rec_addr(cur, cur->bc_ptrs[0], block);
+
+ if (bp)
+ trace_xfs_scrub_btree_rec(cur->bc_mp,
+ XFS_FSB_TO_AGNO(cur->bc_mp,
+ XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn)),
+ XFS_FSB_TO_AGBNO(cur->bc_mp,
+ XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn)),
+ cur->bc_btnum, 0, cur->bc_nlevels,
+ cur->bc_ptrs[0]);
+ else if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+ trace_xfs_scrub_btree_rec(cur->bc_mp,
+ XFS_INO_TO_AGNO(cur->bc_mp,
+ cur->bc_private.b.ip->i_ino),
+ XFS_INO_TO_AGBNO(cur->bc_mp,
+ cur->bc_private.b.ip->i_ino),
+ cur->bc_btnum, 0, cur->bc_nlevels,
+ cur->bc_ptrs[0]);
+ else
+ trace_xfs_scrub_btree_rec(cur->bc_mp,
+ NULLAGNUMBER, NULLAGBLOCK,
+ cur->bc_btnum, 0, cur->bc_nlevels,
+ cur->bc_ptrs[0]);
+
+ /* If this isn't the first record, are they in order? */
+ XFS_BTREC_SCRUB_CHECK(bs, bs->firstrec ||
+ cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec));
+ bs->firstrec = false;
+ bs->lastrec = *rec;
+
+ if (cur->bc_nlevels == 1)
+ return 0;
+
+ /* Is this at least as large as the parent low key? */
+ cur->bc_ops->init_key_from_rec(&key, rec);
+ keyblock = xfs_btree_get_block(cur, 1, &bp);
+ keyp = xfs_btree_key_addr(cur, cur->bc_ptrs[1], keyblock);
+ XFS_BTKEY_SCRUB_CHECK(bs, 0,
+ cur->bc_ops->diff_two_keys(cur, &key, keyp) >= 0);
+
+ if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
+ return 0;
+
+ /* Is this no larger than the parent high key? */
+ cur->bc_ops->init_high_key_from_rec(&hkey, rec);
+ keyp = xfs_btree_high_key_addr(cur, cur->bc_ptrs[1], keyblock);
+ XFS_BTKEY_SCRUB_CHECK(bs, 0,
+ cur->bc_ops->diff_two_keys(cur, keyp, &hkey) >= 0);
+
+ return 0;
+}
+
+/*
+ * Make sure this key is in order and doesn't stray outside of the parent
+ * keys.
+ */
+STATIC int
+xfs_scrub_btree_key(
+ struct xfs_scrub_btree *bs,
+ int level)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ union xfs_btree_key *key;
+ union xfs_btree_key *keyp;
+ struct xfs_btree_block *block;
+ struct xfs_btree_block *keyblock;
+ struct xfs_buf *bp;
+
+ block = xfs_btree_get_block(cur, level, &bp);
+ key = xfs_btree_key_addr(cur, cur->bc_ptrs[level], block);
+
+ if (bp)
+ trace_xfs_scrub_btree_key(cur->bc_mp,
+ XFS_FSB_TO_AGNO(cur->bc_mp,
+ XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn)),
+ XFS_FSB_TO_AGBNO(cur->bc_mp,
+ XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn)),
+ cur->bc_btnum, level, cur->bc_nlevels,
+ cur->bc_ptrs[level]);
+ else if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+ trace_xfs_scrub_btree_key(cur->bc_mp,
+ XFS_INO_TO_AGNO(cur->bc_mp,
+ cur->bc_private.b.ip->i_ino),
+ XFS_INO_TO_AGBNO(cur->bc_mp,
+ cur->bc_private.b.ip->i_ino),
+ cur->bc_btnum, level, cur->bc_nlevels,
+ cur->bc_ptrs[level]);
+ else
+ trace_xfs_scrub_btree_key(cur->bc_mp,
+ NULLAGNUMBER, NULLAGBLOCK,
+ cur->bc_btnum, level, cur->bc_nlevels,
+ cur->bc_ptrs[level]);
+
+ /* If this isn't the first key, are they in order? */
+ XFS_BTKEY_SCRUB_CHECK(bs, level, bs->firstkey[level] ||
+ cur->bc_ops->keys_inorder(cur, &bs->lastkey[level],
+ key));
+ bs->firstkey[level] = false;
+ bs->lastkey[level] = *key;
+
+ if (level + 1 >= cur->bc_nlevels)
+ return 0;
+
+ /* Is this at least as large as the parent low key? */
+ keyblock = xfs_btree_get_block(cur, level + 1, &bp);
+ keyp = xfs_btree_key_addr(cur, cur->bc_ptrs[level + 1], keyblock);
+ XFS_BTKEY_SCRUB_CHECK(bs, level,
+ cur->bc_ops->diff_two_keys(cur, key, keyp) >= 0);
+
+ if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
+ return 0;
+
+ /* Is this no larger than the parent high key? */
+ key = xfs_btree_high_key_addr(cur, cur->bc_ptrs[level], block);
+ keyp = xfs_btree_high_key_addr(cur, cur->bc_ptrs[level + 1], keyblock);
+ XFS_BTKEY_SCRUB_CHECK(bs, level,
+ cur->bc_ops->diff_two_keys(cur, keyp, key) >= 0);
+
+ return 0;
+}
+
+/*
+ * For scrub, grab the AGI and the AGF headers, in that order.
+ * Locking order requires us to get the AGI before the AGF.
+ */
+STATIC int
+xfs_scrub_get_ag_headers(
+ struct xfs_mount *mp,
+ xfs_agnumber_t agno,
+ struct xfs_buf **agi_bpp,
+ struct xfs_buf **agf_bpp)
+{
+ int error;
+
+ error = xfs_read_agi(mp, NULL, agno, agi_bpp);
+ if (error)
+ return error;
+
+ error = xfs_read_agf(mp, NULL, agno, 0, agf_bpp);
+ if (error) {
+ xfs_buf_relse(*agi_bpp);
+ *agi_bpp = NULL;
+ }
+
+ return error;
+}
+
+/*
+ * Release the AGF/AGI buffers.
+ */
+STATIC void
+xfs_scrub_put_ag_headers(
+ struct xfs_buf **agi_bpp,
+ struct xfs_buf **agf_bpp)
+{
+ if (*agf_bpp)
+ xfs_buf_relse(*agf_bpp);
+ if (*agi_bpp)
+ xfs_buf_relse(*agi_bpp);
+ *agi_bpp = *agf_bpp = NULL;
+}
+
+/*
+ * For scrub, grab the AGI and the AGF headers, in that order.
+ * Locking order requires us to get the AGI before the AGF.
+ */
+STATIC int
+xfs_scrub_btree_get_ag_headers(
+ struct xfs_mount *mp,
+ struct xfs_scrub_btree *bs,
+ xfs_agnumber_t agno)
+{
+ return xfs_scrub_get_ag_headers(mp, agno, &bs->agi_bp, &bs->agf_bp);
+}
+
+/*
+ * Release the AGF/AGI buffers.
+ */
+STATIC void
+xfs_scrub_btree_put_ag_headers(
+ struct xfs_scrub_btree *bs)
+{
+ xfs_scrub_put_ag_headers(&bs->agi_bp, &bs->agf_bp);
+}
+
+/* Check a btree pointer. */
+static int
+xfs_scrub_btree_ptr(
+ struct xfs_scrub_btree *bs,
+ int level,
+ union xfs_btree_ptr *ptr)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ xfs_daddr_t daddr;
+ xfs_daddr_t eofs;
+ int error = 0;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
+ level == cur->bc_nlevels) {
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ XFS_BTKEY_SCRUB_GOTO(bs, level, ptr->l == 0, out);
+ } else {
+ XFS_BTKEY_SCRUB_GOTO(bs, level, ptr->s == 0, out);
+ }
+ goto out;
+ }
+
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ XFS_BTKEY_SCRUB_GOTO(bs, level,
+ ptr->l != cpu_to_be64(NULLFSBLOCK), out);
+
+ daddr = XFS_FSB_TO_DADDR(cur->bc_mp, be64_to_cpu(ptr->l));
+ } else {
+ XFS_BTKEY_SCRUB_GOTO(bs, level,
+ cur->bc_private.a.agno != NULLAGNUMBER, out);
+ XFS_BTKEY_SCRUB_GOTO(bs, level,
+ ptr->s != cpu_to_be32(NULLAGBLOCK), out);
+
+ daddr = XFS_AGB_TO_DADDR(cur->bc_mp, cur->bc_private.a.agno,
+ be32_to_cpu(ptr->s));
+ }
+ eofs = XFS_FSB_TO_BB(cur->bc_mp, cur->bc_mp->m_sb.sb_dblocks);
+ XFS_BTKEY_SCRUB_GOTO(bs, level, daddr != 0, out);
+ XFS_BTKEY_SCRUB_GOTO(bs, level, daddr < eofs, out);
+
+out:
+ return error;
+}
+
+/* Should we end the scrub early? */
+static bool
+xfs_scrub_should_terminate(
+ int *error)
+{
+ if (fatal_signal_pending(current)) {
+ if (*error == 0)
+ *error = -EAGAIN;
+ return true;
+ }
+ return false;
+}
+
+/*
+ * 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.
+ */
+STATIC int
+xfs_scrub_btree(
+ struct xfs_scrub_btree *bs)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ union xfs_btree_ptr ptr;
+ union xfs_btree_ptr *pp;
+ union xfs_btree_rec *recp;
+ struct xfs_btree_block *block;
+ int level;
+ struct xfs_buf *bp;
+ int i;
+ int error = 0;
+
+ /* No such thing as a zero-level tree. */
+ XFS_BTREC_SCRUB_GOTO(bs, cur->bc_nlevels > 0, out_badcursor);
+
+ /* Make sure the root isn't in the superblock. */
+ cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+ error = xfs_scrub_btree_ptr(bs, cur->bc_nlevels, &ptr);
+ if (error)
+ goto out_badcursor;
+
+ /* Finish filling out the scrub state */
+ bs->error = 0;
+ bs->firstrec = true;
+ for (i = 0; i < XFS_BTREE_MAXLEVELS; i++)
+ bs->firstkey[i] = true;
+ bs->bno_cur = bs->cnt_cur = bs->ino_cur = bs->fino_cur = NULL;
+ bs->rmap_cur = bs->refc_cur = NULL;
+ if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ bs->agi_bp = NULL;
+ bs->agf_bp = NULL;
+ }
+ INIT_LIST_HEAD(&bs->to_check);
+
+ /* Grab cursors to the other AGs for cross-referencing. */
+ if (!(cur->bc_flags & XFS_BTREE_LONG_PTRS)) {
+ /* Set up a bnobt cursor for cross-referencing. */
+ if (bs->cur->bc_btnum != XFS_BTNUM_BNO)
+ bs->bno_cur = xfs_allocbt_init_cursor(cur->bc_mp, NULL,
+ bs->agf_bp, bs->cur->bc_private.a.agno,
+ XFS_BTNUM_BNO);
+ /* Set up a cntbt cursor for cross-referencing. */
+ if (bs->cur->bc_btnum != XFS_BTNUM_CNT)
+ bs->cnt_cur = xfs_allocbt_init_cursor(cur->bc_mp, NULL,
+ bs->agf_bp, bs->cur->bc_private.a.agno,
+ XFS_BTNUM_CNT);
+ /* Set up a inobt cursor for cross-referencing. */
+ if (bs->cur->bc_btnum != XFS_BTNUM_INO)
+ bs->ino_cur = xfs_inobt_init_cursor(cur->bc_mp, NULL,
+ bs->agi_bp, bs->cur->bc_private.a.agno,
+ XFS_BTNUM_INO);
+ /* Set up a finobt cursor for cross-referencing. */
+ if (bs->cur->bc_btnum != XFS_BTNUM_FINO &&
+ xfs_sb_version_hasfinobt(&cur->bc_mp->m_sb))
+ bs->fino_cur = xfs_inobt_init_cursor(cur->bc_mp, NULL,
+ bs->agi_bp, bs->cur->bc_private.a.agno,
+ XFS_BTNUM_FINO);
+ /* Set up a rmapbt cursor for cross-referencing. */
+ if (bs->cur->bc_btnum != XFS_BTNUM_RMAP &&
+ xfs_sb_version_hasrmapbt(&cur->bc_mp->m_sb))
+ bs->rmap_cur = xfs_rmapbt_init_cursor(cur->bc_mp, NULL,
+ bs->agf_bp, bs->cur->bc_private.a.agno);
+ /* Set up a refcountbt cursor for cross-referencing. */
+ if (bs->cur->bc_btnum != XFS_BTNUM_REFC &&
+ xfs_sb_version_hasreflink(&cur->bc_mp->m_sb))
+ bs->refc_cur = xfs_refcountbt_init_cursor(cur->bc_mp,
+ NULL, bs->agf_bp,
+ bs->cur->bc_private.a.agno, NULL);
+ }
+
+ /* Load the root of the btree. */
+ level = cur->bc_nlevels - 1;
+ cur->bc_ops->init_ptr_from_cur(cur, &ptr);
+ error = xfs_btree_lookup_get_block(cur, level, &ptr, &block);
+ if (error)
+ goto out;
+
+ xfs_btree_get_block(cur, level, &bp);
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ goto out;
+
+ cur->bc_ptrs[level] = 1;
+
+ while (level < cur->bc_nlevels) {
+ block = xfs_btree_get_block(cur, level, &bp);
+
+ if (level == 0) {
+ /* End of leaf, pop back towards the root. */
+ if (cur->bc_ptrs[level] >
+ be16_to_cpu(block->bb_numrecs)) {
+ if (level < cur->bc_nlevels - 1)
+ cur->bc_ptrs[level + 1]++;
+ level++;
+ continue;
+ }
+
+ /* Records in order for scrub? */
+ error = xfs_scrub_btree_rec(bs);
+ if (error)
+ goto out;
+ recp = xfs_btree_rec_addr(cur, cur->bc_ptrs[0], block);
+ error = bs->scrub_rec(bs, recp);
+ if (error < 0 ||
+ error == XFS_BTREE_QUERY_RANGE_ABORT)
+ break;
+ if (xfs_scrub_should_terminate(&error))
+ break;
+
+ cur->bc_ptrs[level]++;
+ continue;
+ }
+
+ /* End of node, pop back towards the root. */
+ if (cur->bc_ptrs[level] > be16_to_cpu(block->bb_numrecs)) {
+ if (level < cur->bc_nlevels - 1)
+ cur->bc_ptrs[level + 1]++;
+ level++;
+ continue;
+ }
+
+ /* Keys in order for scrub? */
+ error = xfs_scrub_btree_key(bs, level);
+ if (error)
+ goto out;
+
+ /* Drill another level deeper. */
+ pp = xfs_btree_ptr_addr(cur, cur->bc_ptrs[level], block);
+ error = xfs_scrub_btree_ptr(bs, level, pp);
+ if (error)
+ goto out;
+ level--;
+ error = xfs_btree_lookup_get_block(cur, level, pp, &block);
+ if (error)
+ goto out;
+
+ xfs_btree_get_block(cur, level, &bp);
+ error = xfs_btree_check_block(cur, block, level, bp);
+ if (error)
+ goto out;
+
+ cur->bc_ptrs[level] = 1;
+ }
+
+out:
+ /*
+ * If we don't end this function with the cursor pointing at a record
+ * block, a subsequent non-error cursor deletion will not release
+ * node-level buffers, causing a buffer leak. This is quite possible
+ * with a zero-results range query, so release the buffers if we
+ * failed to return any results.
+ */
+ if (cur->bc_bufs[0] == NULL) {
+ for (i = 0; i < cur->bc_nlevels; i++) {
+ if (cur->bc_bufs[i]) {
+ xfs_trans_brelse(cur->bc_tp, cur->bc_bufs[i]);
+ cur->bc_bufs[i] = NULL;
+ cur->bc_ptrs[i] = 0;
+ cur->bc_ra[i] = 0;
+ }
+ }
+ }
+
+ if (bs->refc_cur)
+ xfs_btree_del_cursor(bs->refc_cur, XFS_BTREE_ERROR);
+ if (bs->rmap_cur && bs->rmap_cur != bs->cur)
+ xfs_btree_del_cursor(bs->rmap_cur, XFS_BTREE_ERROR);
+ if (bs->fino_cur)
+ xfs_btree_del_cursor(bs->fino_cur, XFS_BTREE_ERROR);
+ if (bs->ino_cur)
+ xfs_btree_del_cursor(bs->ino_cur, XFS_BTREE_ERROR);
+ if (bs->cnt_cur)
+ xfs_btree_del_cursor(bs->cnt_cur, XFS_BTREE_ERROR);
+ if (bs->bno_cur && bs->bno_cur != bs->cur)
+ xfs_btree_del_cursor(bs->bno_cur, XFS_BTREE_ERROR);
+
+ if (!error && bs->error)
+ error = bs->error;
+
+out_badcursor:
+ return error;
+}
diff --git a/fs/xfs/xfs_scrub.h b/fs/xfs/xfs_scrub.h
new file mode 100644
index 0000000..474df7e
--- /dev/null
+++ b/fs/xfs/xfs_scrub.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 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_H__
+#define __XFS_SCRUB_H__
+
+/* Functions to come later. */
+
+#endif /* __XFS_SCRUB_H__ */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 07/25] xfs: create an ioctl to scrub AG metadata
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (5 preceding siblings ...)
2016-08-25 23:40 ` [PATCH 06/25] xfs: generic functions to scrub metadata and btrees Darrick J. Wong
@ 2016-08-25 23:40 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 08/25] xfs: scrub the backup superblocks Darrick J. Wong
` (17 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:40 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Create an ioctl that can be used to scrub internal filesystem
metadata. The new ioctl takes the metadata type, an (optional)
AG number, and a flags argument. This will be used by the
upcoming XFS online scrub tool.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 16 ++++++++++++++++
fs/xfs/xfs_ioctl.c | 20 ++++++++++++++++++++
fs/xfs/xfs_scrub.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_scrub.h | 2 +-
4 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 58e14b14e..22559ab 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -524,6 +524,21 @@ typedef struct xfs_swapext
#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */
#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */
+/* metadata scrubbing */
+struct xfs_scrub_metadata {
+ __u32 type; /* What to check? */
+ __u32 flags; /* Flags; none defined right now. */
+ __u64 control; /* AG or inode number */
+ __u64 reserved[6]; /* Must be zero. */
+};
+
+/*
+ * Metadata types and flags for scrub operation.
+ */
+#define XFS_SCRUB_TYPE_MAX 0
+
+#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
+
/*
* ioctl limits
*/
@@ -567,6 +582,7 @@ typedef struct xfs_swapext
#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64)
#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks)
#define XFS_IOC_GETFSMAP _IOWR('X', 59, struct getfsmap)
+#define XFS_IOC_SCRUB_METADATA _IOR ('X', 60, struct xfs_scrub_metadata)
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 936cb45..65f0c03 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -44,6 +44,7 @@
#include "xfs_reflink.h"
#include "xfs_btree.h"
#include "xfs_fsmap.h"
+#include "xfs_scrub.h"
#include <linux/capability.h>
#include <linux/dcache.h>
@@ -1686,6 +1687,22 @@ xfs_ioc_getfsmap(
return 0;
}
+STATIC int
+xfs_ioc_scrub_metadata(
+ struct xfs_inode *ip,
+ void __user *arg)
+{
+ struct xfs_scrub_metadata scrub;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&scrub, arg, sizeof(scrub)))
+ return -EFAULT;
+
+ return xfs_scrub_metadata(ip, &scrub);
+}
+
int
xfs_ioc_swapext(
xfs_swapext_t *sxp)
@@ -1871,6 +1888,9 @@ xfs_file_ioctl(
return -EPERM;
return xfs_ioc_getfsmap(ip, arg);
+ case XFS_IOC_SCRUB_METADATA:
+ return xfs_ioc_scrub_metadata(ip, arg);
+
case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE:
case XFS_IOC_PATH_TO_FSHANDLE: {
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 13bea55..fd24af7 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -703,3 +703,47 @@ out:
out_badcursor:
return error;
}
+
+/* Scrubbing dispatch. */
+
+struct xfs_scrub_meta_fns {
+ int (*scrub_fn)(struct xfs_inode *, struct xfs_scrub_metadata *);
+ bool (*has_fn)(struct xfs_sb *);
+};
+
+static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
+};
+
+/* Dispatch metadata scrubbing. */
+int
+xfs_scrub_metadata(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ const struct xfs_scrub_meta_fns *fns;
+ int i;
+ int error = 0;
+
+ trace_xfs_scrub(ip, sm->type, sm->control, sm->flags, error);
+
+ error = -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(sm->reserved); i++)
+ if (sm->reserved[i])
+ goto out;
+ if (sm->type > XFS_SCRUB_TYPE_MAX)
+ goto out;
+ if (sm->flags & ~XFS_SCRUB_FLAGS_ALL)
+ goto out;
+
+ fns = &meta_scrub_fns[sm->type];
+ if (fns->has_fn && !fns->has_fn(&mp->m_sb)) {
+ error = -ENOENT;
+ goto out;
+ }
+ error = fns->scrub_fn(ip, sm);
+
+out:
+ trace_xfs_scrub_done(ip, sm->type, sm->control, sm->flags, error);
+ return error;
+}
diff --git a/fs/xfs/xfs_scrub.h b/fs/xfs/xfs_scrub.h
index 474df7e..f4bb021 100644
--- a/fs/xfs/xfs_scrub.h
+++ b/fs/xfs/xfs_scrub.h
@@ -20,6 +20,6 @@
#ifndef __XFS_SCRUB_H__
#define __XFS_SCRUB_H__
-/* Functions to come later. */
+int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm);
#endif /* __XFS_SCRUB_H__ */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 08/25] xfs: scrub the backup superblocks
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (6 preceding siblings ...)
2016-08-25 23:40 ` [PATCH 07/25] xfs: create an ioctl to scrub AG metadata Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 09/25] xfs: scrub AGF and AGFL Darrick J. Wong
` (16 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Ensure that the geometry presented in the backup superblocks matches
the primary superblock so that repair can recover the filesystem if
that primary gets corrupted.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 1 +
fs/xfs/xfs_scrub.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 22559ab..8d58061 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -535,6 +535,7 @@ struct xfs_scrub_metadata {
/*
* Metadata types and flags for scrub operation.
*/
+#define XFS_SCRUB_TYPE_SB 0 /* superblock */
#define XFS_SCRUB_TYPE_MAX 0
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index fd24af7..3def216 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -704,6 +704,80 @@ out_badcursor:
return error;
}
+/* Metadata scrubbers */
+
+/* Scrub the filesystem superblock. */
+STATIC int
+xfs_scrub_sb(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_buf *bp;
+ struct xfs_sb sb;
+ xfs_agnumber_t agno;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp,
+ &xfs_sb_buf_ops);
+ if (error)
+ return error;
+
+ if (agno == 0)
+ goto out;
+
+ xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp));
+
+ /* Verify the geometries match. */
+#define XFS_SCRUB_SB_FIELD(fn) \
+ XFS_SCRUB_CHECK(mp, bp, "superblock", \
+ sb.sb_##fn == mp->m_sb.sb_##fn)
+ XFS_SCRUB_SB_FIELD(blocksize);
+ XFS_SCRUB_SB_FIELD(dblocks);
+ XFS_SCRUB_SB_FIELD(rblocks);
+ XFS_SCRUB_SB_FIELD(rextents);
+ XFS_SCRUB_SB_FIELD(logstart);
+ XFS_SCRUB_SB_FIELD(rextsize);
+ XFS_SCRUB_SB_FIELD(agblocks);
+ XFS_SCRUB_SB_FIELD(agcount);
+ XFS_SCRUB_SB_FIELD(rbmblocks);
+ XFS_SCRUB_SB_FIELD(logblocks);
+ XFS_SCRUB_SB_FIELD(sectsize);
+ XFS_SCRUB_SB_FIELD(inodesize);
+#undef XFS_SCRUB_SB_FIELD
+
+#define XFS_SCRUB_SB_FEAT(fn) \
+ XFS_SCRUB_CHECK(mp, bp, "superblock", \
+ xfs_sb_version_has##fn(&sb) == xfs_sb_version_has##fn(&mp->m_sb))
+ XFS_SCRUB_SB_FEAT(align);
+ XFS_SCRUB_SB_FEAT(dalign);
+ XFS_SCRUB_SB_FEAT(logv2);
+ XFS_SCRUB_SB_FEAT(extflgbit);
+ XFS_SCRUB_SB_FEAT(sector);
+ XFS_SCRUB_SB_FEAT(asciici);
+ XFS_SCRUB_SB_FEAT(morebits);
+ XFS_SCRUB_SB_FEAT(lazysbcount);
+ XFS_SCRUB_SB_FEAT(crc);
+ XFS_SCRUB_SB_FEAT(_pquotino);
+ XFS_SCRUB_SB_FEAT(ftype);
+ XFS_SCRUB_SB_FEAT(finobt);
+ XFS_SCRUB_SB_FEAT(sparseinodes);
+ XFS_SCRUB_SB_FEAT(metauuid);
+ XFS_SCRUB_SB_FEAT(rmapbt);
+ XFS_SCRUB_SB_FEAT(reflink);
+#undef XFS_SCRUB_SB_FEAT
+
+out:
+ xfs_buf_relse(bp);
+ return error;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -712,6 +786,7 @@ struct xfs_scrub_meta_fns {
};
static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
+ {xfs_scrub_sb, NULL},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 09/25] xfs: scrub AGF and AGFL
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (7 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 08/25] xfs: scrub the backup superblocks Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 10/25] xfs: scrub the AGI Darrick J. Wong
` (15 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Check the block references in the AGF and AGFL headers to make sure
they make sense.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_alloc.c | 2 -
fs/xfs/libxfs/xfs_alloc.h | 2 +
fs/xfs/libxfs/xfs_fs.h | 4 +
fs/xfs/xfs_scrub.c | 133 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 8b3e6b3..37782a1 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -629,7 +629,7 @@ const struct xfs_buf_ops xfs_agfl_buf_ops = {
/*
* Read in the allocation group free block array.
*/
-STATIC int /* error */
+int /* error */
xfs_alloc_read_agfl(
xfs_mount_t *mp, /* mount point structure */
xfs_trans_t *tp, /* transaction pointer */
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index 0b00de0..c3ada6b 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -204,6 +204,8 @@ xfs_alloc_get_rec(
int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
+int xfs_alloc_read_agfl(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_agnumber_t agno, struct xfs_buf **bpp);
int xfs_alloc_fix_freelist(struct xfs_alloc_arg *args, int flags);
int xfs_free_extent_fix_freelist(struct xfs_trans *tp, xfs_agnumber_t agno,
struct xfs_buf **agbp);
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 8d58061..8249ae0 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -536,7 +536,9 @@ struct xfs_scrub_metadata {
* Metadata types and flags for scrub operation.
*/
#define XFS_SCRUB_TYPE_SB 0 /* superblock */
-#define XFS_SCRUB_TYPE_MAX 0
+#define XFS_SCRUB_TYPE_AGF 1 /* AG free header */
+#define XFS_SCRUB_TYPE_AGFL 2 /* AG free list */
+#define XFS_SCRUB_TYPE_MAX 2
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 3def216..c04a097 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -778,6 +778,137 @@ out:
return error;
}
+/* Scrub the AGF. */
+STATIC int
+xfs_scrub_agf(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_agf *agf;
+ struct xfs_buf *agi_bp = NULL;
+ struct xfs_buf *agf_bp = NULL;
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ xfs_agblock_t eoag;
+ xfs_daddr_t daddr;
+ xfs_daddr_t eofs;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ /* Let the verifier check most of the AGF fields. */
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
+ return error;
+
+ agf = XFS_BUF_TO_AGF(agf_bp);
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+
+ /* Check the AG length */
+ eoag = be32_to_cpu(agf->agf_length);
+ if (agno == mp->m_sb.sb_agcount - 1) {
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+ eoag <= mp->m_sb.sb_agblocks);
+ } else {
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+ eoag == mp->m_sb.sb_agblocks);
+ }
+ daddr = XFS_AGB_TO_DADDR(mp, agno, eoag);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr <= eofs);
+
+ /* Check the AGF btree roots */
+ agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]);
+ daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+
+ agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]);
+ daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+ agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]);
+ daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+ agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+ }
+
+ if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+ agbno = be32_to_cpu(agf->agf_refcount_root);
+ daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF",
+ agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", agbno < eoag);
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
+ }
+
+ xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+ return error;
+}
+
+/* Scrub the AGFL. */
+STATIC int
+xfs_scrub_agfl(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_agf *agf;
+ struct xfs_buf *agi_bp = NULL;
+ struct xfs_buf *agf_bp = NULL;
+ struct xfs_buf *agfl_bp;
+ __be32 *agfl_bno;
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ xfs_agblock_t eoag;
+ xfs_daddr_t eofs;
+ int i;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ /* Let the verifier check most of the AGF fields. */
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
+ return error;
+
+ error = xfs_alloc_read_agfl(mp, NULL, agno, &agfl_bp);
+ if (error)
+ goto err_no_agfl;
+
+ agf = XFS_BUF_TO_AGF(agf_bp);
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+ eoag = be32_to_cpu(agf->agf_length);
+
+ agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
+ for (i = be32_to_cpu(agf->agf_flfirst);
+ i <= be32_to_cpu(agf->agf_fllast);
+ i++) {
+ agbno = be32_to_cpu(agfl_bno[i]);
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+ XFS_AGB_TO_DADDR(mp, agno, agbno) < eofs);
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+ agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+ agbno < eoag);
+ }
+
+ xfs_buf_relse(agfl_bp);
+err_no_agfl:
+ xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+ return error;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -787,6 +918,8 @@ struct xfs_scrub_meta_fns {
static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_sb, NULL},
+ {xfs_scrub_agf, NULL},
+ {xfs_scrub_agfl, NULL},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 10/25] xfs: scrub the AGI
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (8 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 09/25] xfs: scrub AGF and AGFL Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 11/25] xfs: support scrubbing free space btrees Darrick J. Wong
` (14 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Add a forgotten check to the AGI verifier, then wire up the scrub
infrastructure to check the AGI contents.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 3 ++-
fs/xfs/libxfs/xfs_ialloc.c | 5 ++++
fs/xfs/xfs_scrub.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 8249ae0..2d320a7 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -538,7 +538,8 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_SB 0 /* superblock */
#define XFS_SCRUB_TYPE_AGF 1 /* AG free header */
#define XFS_SCRUB_TYPE_AGFL 2 /* AG free list */
-#define XFS_SCRUB_TYPE_MAX 2
+#define XFS_SCRUB_TYPE_AGI 3 /* AG inode header */
+#define XFS_SCRUB_TYPE_MAX 3
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 60e1a67..1240064 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2514,6 +2514,11 @@ xfs_agi_verify(
if (be32_to_cpu(agi->agi_level) > XFS_BTREE_MAXLEVELS)
return false;
+
+ if (xfs_sb_version_hasfinobt(&mp->m_sb) &&
+ be32_to_cpu(agi->agi_free_level) > XFS_BTREE_MAXLEVELS)
+ return false;
+
/*
* during growfs operations, the perag is not fully initialised,
* so we can't use it for any useful checking. growfs ensures we can't
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index c04a097..2b1d669 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -909,6 +909,56 @@ err_no_agfl:
return error;
}
+/* Scrub the AGI. */
+STATIC int
+xfs_scrub_agi(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_agi *agi;
+ struct xfs_agf *agf;
+ struct xfs_buf *agi_bp = NULL;
+ struct xfs_buf *agf_bp = NULL;
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ xfs_agblock_t eoag;
+ xfs_daddr_t daddr;
+ xfs_daddr_t eofs;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
+ return error;
+
+ agi = XFS_BUF_TO_AGI(agi_bp);
+ agf = XFS_BUF_TO_AGF(agf_bp);
+ eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+ eoag = be32_to_cpu(agf->agf_length);
+
+ agbno = be32_to_cpu(agi->agi_root);
+ daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", agbno < eoag);
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", daddr < eofs);
+
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ agbno = be32_to_cpu(agi->agi_free_root);
+ daddr = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI",
+ agbno < mp->m_sb.sb_agblocks);
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", agbno < eoag);
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", daddr < eofs);
+ }
+
+ xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+ return error;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -920,6 +970,7 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_sb, NULL},
{xfs_scrub_agf, NULL},
{xfs_scrub_agfl, NULL},
+ {xfs_scrub_agi, NULL},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 11/25] xfs: support scrubbing free space btrees
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (9 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 10/25] xfs: scrub the AGI Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 12/25] xfs: support scrubbing inode btrees Darrick J. Wong
` (13 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Plumb in the pieces necessary to check the free space btrees.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_alloc_btree.c | 156 +++++++++++++++++++++++++++++----------
fs/xfs/libxfs/xfs_fs.h | 4 +
fs/xfs/xfs_scrub.c | 80 ++++++++++++++++++++
3 files changed, 197 insertions(+), 43 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c
index 5ba2dac..d6d81cb 100644
--- a/fs/xfs/libxfs/xfs_alloc_btree.c
+++ b/fs/xfs/libxfs/xfs_alloc_btree.c
@@ -205,19 +205,28 @@ xfs_allocbt_init_key_from_rec(
union xfs_btree_key *key,
union xfs_btree_rec *rec)
{
- ASSERT(rec->alloc.ar_startblock != 0);
-
key->alloc.ar_startblock = rec->alloc.ar_startblock;
key->alloc.ar_blockcount = rec->alloc.ar_blockcount;
}
STATIC void
+xfs_bnobt_init_high_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ __u32 x;
+
+ x = be32_to_cpu(rec->alloc.ar_startblock);
+ x += be32_to_cpu(rec->alloc.ar_blockcount) - 1;
+ key->alloc.ar_startblock = cpu_to_be32(x);
+ key->alloc.ar_blockcount = 0;
+}
+
+STATIC void
xfs_allocbt_init_rec_from_cur(
struct xfs_btree_cur *cur,
union xfs_btree_rec *rec)
{
- ASSERT(cur->bc_rec.a.ar_startblock != 0);
-
rec->alloc.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock);
rec->alloc.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount);
}
@@ -236,18 +245,24 @@ xfs_allocbt_init_ptr_from_cur(
}
STATIC __int64_t
-xfs_allocbt_key_diff(
+xfs_bnobt_key_diff(
struct xfs_btree_cur *cur,
union xfs_btree_key *key)
{
xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
xfs_alloc_key_t *kp = &key->alloc;
- __int64_t diff;
- if (cur->bc_btnum == XFS_BTNUM_BNO) {
- return (__int64_t)be32_to_cpu(kp->ar_startblock) -
- rec->ar_startblock;
- }
+ return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
+}
+
+STATIC __int64_t
+xfs_cntbt_key_diff(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *key)
+{
+ xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
+ xfs_alloc_key_t *kp = &key->alloc;
+ __int64_t diff;
diff = (__int64_t)be32_to_cpu(kp->ar_blockcount) - rec->ar_blockcount;
if (diff)
@@ -256,6 +271,33 @@ xfs_allocbt_key_diff(
return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
}
+STATIC __int64_t
+xfs_bnobt_diff_two_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return (__int64_t)be32_to_cpu(k1->alloc.ar_startblock) -
+ be32_to_cpu(k2->alloc.ar_startblock);
+}
+
+STATIC __int64_t
+xfs_cntbt_diff_two_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ __int64_t diff;
+
+ diff = be32_to_cpu(k1->alloc.ar_blockcount) -
+ be32_to_cpu(k2->alloc.ar_blockcount);
+ if (diff)
+ return diff;
+
+ return be32_to_cpu(k1->alloc.ar_startblock) -
+ be32_to_cpu(k2->alloc.ar_startblock);
+}
+
static bool
xfs_allocbt_verify(
struct xfs_buf *bp)
@@ -344,46 +386,76 @@ const struct xfs_buf_ops xfs_allocbt_buf_ops = {
};
-#if defined(DEBUG) || defined(XFS_WARN)
STATIC int
-xfs_allocbt_keys_inorder(
+xfs_bnobt_keys_inorder(
struct xfs_btree_cur *cur,
union xfs_btree_key *k1,
union xfs_btree_key *k2)
{
- if (cur->bc_btnum == XFS_BTNUM_BNO) {
- return be32_to_cpu(k1->alloc.ar_startblock) <
- be32_to_cpu(k2->alloc.ar_startblock);
- } else {
- return be32_to_cpu(k1->alloc.ar_blockcount) <
- be32_to_cpu(k2->alloc.ar_blockcount) ||
- (k1->alloc.ar_blockcount == k2->alloc.ar_blockcount &&
- be32_to_cpu(k1->alloc.ar_startblock) <
- be32_to_cpu(k2->alloc.ar_startblock));
- }
+ return be32_to_cpu(k1->alloc.ar_startblock) <
+ be32_to_cpu(k2->alloc.ar_startblock);
}
STATIC int
-xfs_allocbt_recs_inorder(
+xfs_bnobt_recs_inorder(
struct xfs_btree_cur *cur,
union xfs_btree_rec *r1,
union xfs_btree_rec *r2)
{
- if (cur->bc_btnum == XFS_BTNUM_BNO) {
- return be32_to_cpu(r1->alloc.ar_startblock) +
- be32_to_cpu(r1->alloc.ar_blockcount) <=
- be32_to_cpu(r2->alloc.ar_startblock);
- } else {
- return be32_to_cpu(r1->alloc.ar_blockcount) <
- be32_to_cpu(r2->alloc.ar_blockcount) ||
- (r1->alloc.ar_blockcount == r2->alloc.ar_blockcount &&
- be32_to_cpu(r1->alloc.ar_startblock) <
- be32_to_cpu(r2->alloc.ar_startblock));
- }
+ return be32_to_cpu(r1->alloc.ar_startblock) +
+ be32_to_cpu(r1->alloc.ar_blockcount) <=
+ be32_to_cpu(r2->alloc.ar_startblock);
}
-#endif /* DEBUG */
-static const struct xfs_btree_ops xfs_allocbt_ops = {
+STATIC int
+xfs_cntbt_keys_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return be32_to_cpu(k1->alloc.ar_blockcount) <
+ be32_to_cpu(k2->alloc.ar_blockcount) ||
+ (k1->alloc.ar_blockcount == k2->alloc.ar_blockcount &&
+ be32_to_cpu(k1->alloc.ar_startblock) <
+ be32_to_cpu(k2->alloc.ar_startblock));
+}
+
+STATIC int
+xfs_cntbt_recs_inorder(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_rec *r1,
+ union xfs_btree_rec *r2)
+{
+ return be32_to_cpu(r1->alloc.ar_blockcount) <
+ be32_to_cpu(r2->alloc.ar_blockcount) ||
+ (r1->alloc.ar_blockcount == r2->alloc.ar_blockcount &&
+ be32_to_cpu(r1->alloc.ar_startblock) <
+ be32_to_cpu(r2->alloc.ar_startblock));
+}
+
+static const struct xfs_btree_ops xfs_bnobt_ops = {
+ .rec_len = sizeof(xfs_alloc_rec_t),
+ .key_len = sizeof(xfs_alloc_key_t),
+
+ .dup_cursor = xfs_allocbt_dup_cursor,
+ .set_root = xfs_allocbt_set_root,
+ .alloc_block = xfs_allocbt_alloc_block,
+ .free_block = xfs_allocbt_free_block,
+ .update_lastrec = xfs_allocbt_update_lastrec,
+ .get_minrecs = xfs_allocbt_get_minrecs,
+ .get_maxrecs = xfs_allocbt_get_maxrecs,
+ .init_key_from_rec = xfs_allocbt_init_key_from_rec,
+ .init_high_key_from_rec = xfs_bnobt_init_high_key_from_rec,
+ .init_rec_from_cur = xfs_allocbt_init_rec_from_cur,
+ .init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur,
+ .key_diff = xfs_bnobt_key_diff,
+ .buf_ops = &xfs_allocbt_buf_ops,
+ .diff_two_keys = xfs_bnobt_diff_two_keys,
+ .keys_inorder = xfs_bnobt_keys_inorder,
+ .recs_inorder = xfs_bnobt_recs_inorder,
+};
+
+static const struct xfs_btree_ops xfs_cntbt_ops = {
.rec_len = sizeof(xfs_alloc_rec_t),
.key_len = sizeof(xfs_alloc_key_t),
@@ -397,12 +469,11 @@ static const struct xfs_btree_ops xfs_allocbt_ops = {
.init_key_from_rec = xfs_allocbt_init_key_from_rec,
.init_rec_from_cur = xfs_allocbt_init_rec_from_cur,
.init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur,
- .key_diff = xfs_allocbt_key_diff,
+ .key_diff = xfs_cntbt_key_diff,
.buf_ops = &xfs_allocbt_buf_ops,
-#if defined(DEBUG) || defined(XFS_WARN)
- .keys_inorder = xfs_allocbt_keys_inorder,
- .recs_inorder = xfs_allocbt_recs_inorder,
-#endif
+ .diff_two_keys = xfs_cntbt_diff_two_keys,
+ .keys_inorder = xfs_cntbt_keys_inorder,
+ .recs_inorder = xfs_cntbt_recs_inorder,
};
/*
@@ -427,12 +498,13 @@ xfs_allocbt_init_cursor(
cur->bc_mp = mp;
cur->bc_btnum = btnum;
cur->bc_blocklog = mp->m_sb.sb_blocklog;
- cur->bc_ops = &xfs_allocbt_ops;
if (btnum == XFS_BTNUM_CNT) {
+ cur->bc_ops = &xfs_cntbt_ops;
cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]);
cur->bc_flags = XFS_BTREE_LASTREC_UPDATE;
} else {
+ cur->bc_ops = &xfs_bnobt_ops;
cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]);
}
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 2d320a7..2cb47e2 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -539,7 +539,9 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_AGF 1 /* AG free header */
#define XFS_SCRUB_TYPE_AGFL 2 /* AG free list */
#define XFS_SCRUB_TYPE_AGI 3 /* AG inode header */
-#define XFS_SCRUB_TYPE_MAX 3
+#define XFS_SCRUB_TYPE_BNOBT 4 /* freesp by block btree */
+#define XFS_SCRUB_TYPE_CNTBT 5 /* freesp by length btree */
+#define XFS_SCRUB_TYPE_MAX 5
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 2b1d669..b93dedb 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -959,6 +959,84 @@ xfs_scrub_agi(
return error;
}
+/* Free space btree scrubber. */
+
+/* Scrub a bnobt/cntbt record. */
+STATIC int
+xfs_scrub_allocbt_helper(
+ struct xfs_scrub_btree *bs,
+ union xfs_btree_rec *rec)
+{
+ struct xfs_mount *mp = bs->cur->bc_mp;
+ struct xfs_agf *agf;
+ xfs_agblock_t bno;
+ xfs_extlen_t len;
+ int error = 0;
+
+ bno = be32_to_cpu(rec->alloc.ar_startblock);
+ len = be32_to_cpu(rec->alloc.ar_blockcount);
+ agf = XFS_BUF_TO_AGF(bs->agf_bp);
+
+ XFS_BTREC_SCRUB_CHECK(bs, bno < mp->m_sb.sb_agblocks);
+ XFS_BTREC_SCRUB_CHECK(bs, bno < be32_to_cpu(agf->agf_length));
+ XFS_BTREC_SCRUB_CHECK(bs, bno < bno + len);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
+ mp->m_sb.sb_agblocks);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
+ be32_to_cpu(agf->agf_length));
+
+ return error;
+}
+
+/* Scrub the freespace btrees for some AG. */
+STATIC int
+xfs_scrub_allocbt(
+ struct xfs_mount *mp,
+ struct xfs_scrub_metadata *sm,
+ xfs_btnum_t which)
+{
+ struct xfs_scrub_btree bs;
+ xfs_agnumber_t agno;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ error = xfs_scrub_btree_get_ag_headers(mp, &bs, agno);
+ if (error)
+ return error;
+
+ bs.cur = xfs_allocbt_init_cursor(mp, NULL, bs.agf_bp, agno, which);
+ bs.scrub_rec = xfs_scrub_allocbt_helper;
+ xfs_rmap_ag_owner(&bs.oinfo, XFS_RMAP_OWN_AG);
+ error = xfs_scrub_btree(&bs);
+ xfs_btree_del_cursor(bs.cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ xfs_scrub_btree_put_ag_headers(&bs);
+
+ if (!error && bs.error)
+ error = bs.error;
+
+ return error;
+}
+
+STATIC int
+xfs_scrub_bnobt(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ return xfs_scrub_allocbt(ip->i_mount, sm, XFS_BTNUM_BNO);
+}
+
+STATIC int
+xfs_scrub_cntbt(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ return xfs_scrub_allocbt(ip->i_mount, sm, XFS_BTNUM_CNT);
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -971,6 +1049,8 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_agf, NULL},
{xfs_scrub_agfl, NULL},
{xfs_scrub_agi, NULL},
+ {xfs_scrub_bnobt, NULL},
+ {xfs_scrub_cntbt, NULL},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 12/25] xfs: support scrubbing inode btrees
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (10 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 11/25] xfs: support scrubbing free space btrees Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 13/25] xfs: support scrubbing rmap btree Darrick J. Wong
` (12 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Plumb in the pieces necessary to check the inode btrees.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 4 +
fs/xfs/libxfs/xfs_ialloc.c | 41 +++++++-----
fs/xfs/libxfs/xfs_ialloc.h | 3 +
fs/xfs/libxfs/xfs_ialloc_btree.c | 32 ++++++++-
fs/xfs/xfs_scrub.c | 129 ++++++++++++++++++++++++++++++++++++++
5 files changed, 186 insertions(+), 23 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 2cb47e2..6b8ff75 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -541,7 +541,9 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_AGI 3 /* AG inode header */
#define XFS_SCRUB_TYPE_BNOBT 4 /* freesp by block btree */
#define XFS_SCRUB_TYPE_CNTBT 5 /* freesp by length btree */
-#define XFS_SCRUB_TYPE_MAX 5
+#define XFS_SCRUB_TYPE_INOBT 6 /* inode btree */
+#define XFS_SCRUB_TYPE_FINOBT 7 /* free inode btree */
+#define XFS_SCRUB_TYPE_MAX 7
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 1240064..ab05f63 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -99,24 +99,14 @@ xfs_inobt_update(
return xfs_btree_update(cur, &rec);
}
-/*
- * Get the data from the pointed-to record.
- */
-int /* error */
-xfs_inobt_get_rec(
- struct xfs_btree_cur *cur, /* btree cursor */
- xfs_inobt_rec_incore_t *irec, /* btree record */
- int *stat) /* output: success/failure */
+void
+xfs_inobt_btrec_to_irec(
+ struct xfs_mount *mp,
+ union xfs_btree_rec *rec,
+ struct xfs_inobt_rec_incore *irec)
{
- union xfs_btree_rec *rec;
- int error;
-
- error = xfs_btree_get_rec(cur, &rec, stat);
- if (error || *stat == 0)
- return error;
-
irec->ir_startino = be32_to_cpu(rec->inobt.ir_startino);
- if (xfs_sb_version_hassparseinodes(&cur->bc_mp->m_sb)) {
+ if (xfs_sb_version_hassparseinodes(&mp->m_sb)) {
irec->ir_holemask = be16_to_cpu(rec->inobt.ir_u.sp.ir_holemask);
irec->ir_count = rec->inobt.ir_u.sp.ir_count;
irec->ir_freecount = rec->inobt.ir_u.sp.ir_freecount;
@@ -131,6 +121,25 @@ xfs_inobt_get_rec(
be32_to_cpu(rec->inobt.ir_u.f.ir_freecount);
}
irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_inobt_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_inobt_rec_incore_t *irec, /* btree record */
+ int *stat) /* output: success/failure */
+{
+ union xfs_btree_rec *rec;
+ int error;
+
+ error = xfs_btree_get_rec(cur, &rec, stat);
+ if (error || *stat == 0)
+ return error;
+
+ xfs_inobt_btrec_to_irec(cur->bc_mp, rec, irec);
return 0;
}
diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h
index 0bb8966..8e5861d 100644
--- a/fs/xfs/libxfs/xfs_ialloc.h
+++ b/fs/xfs/libxfs/xfs_ialloc.h
@@ -168,5 +168,8 @@ int xfs_ialloc_inode_init(struct xfs_mount *mp, struct xfs_trans *tp,
int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_agnumber_t agno, struct xfs_buf **bpp);
+union xfs_btree_rec;
+void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, union xfs_btree_rec *rec,
+ struct xfs_inobt_rec_incore *irec);
#endif /* __XFS_IALLOC_H__ */
diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c
index eab68ae..f09ec06 100644
--- a/fs/xfs/libxfs/xfs_ialloc_btree.c
+++ b/fs/xfs/libxfs/xfs_ialloc_btree.c
@@ -152,6 +152,18 @@ xfs_inobt_init_key_from_rec(
}
STATIC void
+xfs_inobt_init_high_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ __u32 x;
+
+ x = be32_to_cpu(rec->inobt.ir_startino);
+ x += XFS_INODES_PER_CHUNK - 1;
+ key->inobt.ir_startino = cpu_to_be32(x);
+}
+
+STATIC void
xfs_inobt_init_rec_from_cur(
struct xfs_btree_cur *cur,
union xfs_btree_rec *rec)
@@ -205,6 +217,16 @@ xfs_inobt_key_diff(
cur->bc_rec.i.ir_startino;
}
+STATIC __int64_t
+xfs_inobt_diff_two_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return (__int64_t)be32_to_cpu(k1->inobt.ir_startino) -
+ be32_to_cpu(k2->inobt.ir_startino);
+}
+
static int
xfs_inobt_verify(
struct xfs_buf *bp)
@@ -279,7 +301,6 @@ const struct xfs_buf_ops xfs_inobt_buf_ops = {
.verify_write = xfs_inobt_write_verify,
};
-#if defined(DEBUG) || defined(XFS_WARN)
STATIC int
xfs_inobt_keys_inorder(
struct xfs_btree_cur *cur,
@@ -299,7 +320,6 @@ xfs_inobt_recs_inorder(
return be32_to_cpu(r1->inobt.ir_startino) + XFS_INODES_PER_CHUNK <=
be32_to_cpu(r2->inobt.ir_startino);
}
-#endif /* DEBUG */
static const struct xfs_btree_ops xfs_inobt_ops = {
.rec_len = sizeof(xfs_inobt_rec_t),
@@ -312,14 +332,14 @@ static const struct xfs_btree_ops xfs_inobt_ops = {
.get_minrecs = xfs_inobt_get_minrecs,
.get_maxrecs = xfs_inobt_get_maxrecs,
.init_key_from_rec = xfs_inobt_init_key_from_rec,
+ .init_high_key_from_rec = xfs_inobt_init_high_key_from_rec,
.init_rec_from_cur = xfs_inobt_init_rec_from_cur,
.init_ptr_from_cur = xfs_inobt_init_ptr_from_cur,
.key_diff = xfs_inobt_key_diff,
.buf_ops = &xfs_inobt_buf_ops,
-#if defined(DEBUG) || defined(XFS_WARN)
+ .diff_two_keys = xfs_inobt_diff_two_keys,
.keys_inorder = xfs_inobt_keys_inorder,
.recs_inorder = xfs_inobt_recs_inorder,
-#endif
};
static const struct xfs_btree_ops xfs_finobt_ops = {
@@ -333,14 +353,14 @@ static const struct xfs_btree_ops xfs_finobt_ops = {
.get_minrecs = xfs_inobt_get_minrecs,
.get_maxrecs = xfs_inobt_get_maxrecs,
.init_key_from_rec = xfs_inobt_init_key_from_rec,
+ .init_high_key_from_rec = xfs_inobt_init_high_key_from_rec,
.init_rec_from_cur = xfs_inobt_init_rec_from_cur,
.init_ptr_from_cur = xfs_finobt_init_ptr_from_cur,
.key_diff = xfs_inobt_key_diff,
.buf_ops = &xfs_inobt_buf_ops,
-#if defined(DEBUG) || defined(XFS_WARN)
+ .diff_two_keys = xfs_inobt_diff_two_keys,
.keys_inorder = xfs_inobt_keys_inorder,
.recs_inorder = xfs_inobt_recs_inorder,
-#endif
};
/*
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index b93dedb..81d24f5 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -1037,6 +1037,133 @@ xfs_scrub_cntbt(
return xfs_scrub_allocbt(ip->i_mount, sm, XFS_BTNUM_CNT);
}
+/* Inode btree scrubber. */
+
+/* Scrub an inobt/finobt record. */
+STATIC int
+xfs_scrub_iallocbt_helper(
+ struct xfs_scrub_btree *bs,
+ union xfs_btree_rec *rec)
+{
+ struct xfs_mount *mp = bs->cur->bc_mp;
+ struct xfs_agf *agf;
+ struct xfs_inobt_rec_incore irec;
+ __uint16_t holemask;
+ xfs_agino_t agino;
+ xfs_agblock_t bno;
+ xfs_agblock_t eoag;
+ xfs_extlen_t len;
+ int holecount;
+ int i;
+ int error = 0;
+ uint64_t holes;
+
+ xfs_inobt_btrec_to_irec(mp, rec, &irec);
+
+ XFS_BTREC_SCRUB_CHECK(bs, irec.ir_count <= XFS_INODES_PER_CHUNK);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.ir_freecount <= XFS_INODES_PER_CHUNK);
+ agino = irec.ir_startino;
+ agf = XFS_BUF_TO_AGF(bs->agf_bp);
+ eoag = be32_to_cpu(agf->agf_length);
+
+ /* Handle non-sparse inodes */
+ if (!xfs_inobt_issparse(irec.ir_holemask)) {
+ len = XFS_B_TO_FSB(mp,
+ XFS_INODES_PER_CHUNK * mp->m_sb.sb_inodesize);
+ bno = XFS_AGINO_TO_AGBNO(mp, agino);
+
+ XFS_BTREC_SCRUB_CHECK(bs, bno < mp->m_sb.sb_agblocks)
+ XFS_BTREC_SCRUB_CHECK(bs, bno < eoag);
+ XFS_BTREC_SCRUB_CHECK(bs, bno < bno + len);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
+ mp->m_sb.sb_agblocks);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
+ eoag);
+ return error;
+ }
+
+ /* Check each chunk of a sparse inode cluster. */
+ holemask = irec.ir_holemask;
+ holecount = 0;
+ len = XFS_B_TO_FSB(mp,
+ XFS_INODES_PER_HOLEMASK_BIT * mp->m_sb.sb_inodesize);
+ holes = ~xfs_inobt_irec_to_allocmask(&irec);
+ XFS_BTREC_SCRUB_CHECK(bs, (holes & irec.ir_free) == holes);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.ir_freecount <= irec.ir_count);
+
+ for (i = 0; i < XFS_INOBT_HOLEMASK_BITS; holemask >>= 1,
+ i++, agino += XFS_INODES_PER_HOLEMASK_BIT) {
+ if (holemask & 1) {
+ holecount += XFS_INODES_PER_HOLEMASK_BIT;
+ continue;
+ }
+ bno = XFS_AGINO_TO_AGBNO(mp, agino);
+
+ XFS_BTREC_SCRUB_CHECK(bs, bno < mp->m_sb.sb_agblocks)
+ XFS_BTREC_SCRUB_CHECK(bs, bno < eoag);
+ XFS_BTREC_SCRUB_CHECK(bs, bno < bno + len);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
+ mp->m_sb.sb_agblocks);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
+ eoag);
+ }
+
+ XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
+ XFS_BTREC_SCRUB_CHECK(bs, holecount + irec.ir_count ==
+ XFS_INODES_PER_CHUNK);
+
+ return error;
+}
+
+/* Scrub the inode btrees for some AG. */
+STATIC int
+xfs_scrub_iallocbt(
+ struct xfs_mount *mp,
+ struct xfs_scrub_metadata *sm,
+ xfs_btnum_t which)
+{
+ struct xfs_scrub_btree bs;
+ xfs_agnumber_t agno;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ error = xfs_scrub_btree_get_ag_headers(mp, &bs, agno);
+ if (error)
+ return error;
+
+ bs.cur = xfs_inobt_init_cursor(mp, NULL, bs.agi_bp, agno, which);
+ bs.scrub_rec = xfs_scrub_iallocbt_helper;
+ xfs_rmap_ag_owner(&bs.oinfo, XFS_RMAP_OWN_INOBT);
+ error = xfs_scrub_btree(&bs);
+ xfs_btree_del_cursor(bs.cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ xfs_scrub_btree_put_ag_headers(&bs);
+
+ if (!error && bs.error)
+ error = bs.error;
+
+ return error;
+}
+
+STATIC int
+xfs_scrub_inobt(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ return xfs_scrub_iallocbt(ip->i_mount, sm, XFS_BTNUM_INO);
+}
+
+STATIC int
+xfs_scrub_finobt(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ return xfs_scrub_iallocbt(ip->i_mount, sm, XFS_BTNUM_FINO);
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -1051,6 +1178,8 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_agi, NULL},
{xfs_scrub_bnobt, NULL},
{xfs_scrub_cntbt, NULL},
+ {xfs_scrub_inobt, NULL},
+ {xfs_scrub_finobt, xfs_sb_version_hasfinobt},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 13/25] xfs: support scrubbing rmap btree
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (11 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 12/25] xfs: support scrubbing inode btrees Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 14/25] xfs: support scrubbing refcount btree Darrick J. Wong
` (11 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Plumb in the pieces necessary to check the rmap btree.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 3 +
fs/xfs/libxfs/xfs_rmap.c | 3 +
fs/xfs/libxfs/xfs_rmap.h | 3 +
fs/xfs/libxfs/xfs_rmap_btree.c | 4 --
fs/xfs/xfs_scrub.c | 84 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 91 insertions(+), 6 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 6b8ff75..31e091c 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -543,7 +543,8 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_CNTBT 5 /* freesp by length btree */
#define XFS_SCRUB_TYPE_INOBT 6 /* inode btree */
#define XFS_SCRUB_TYPE_FINOBT 7 /* free inode btree */
-#define XFS_SCRUB_TYPE_MAX 7
+#define XFS_SCRUB_TYPE_RMAPBT 8 /* reverse mapping btree */
+#define XFS_SCRUB_TYPE_MAX 8
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c
index 3a8cc71..b0308fc 100644
--- a/fs/xfs/libxfs/xfs_rmap.c
+++ b/fs/xfs/libxfs/xfs_rmap.c
@@ -179,7 +179,8 @@ done:
return error;
}
-static int
+/* Convert an internal btree record to an rmap record. */
+int
xfs_rmap_btrec_to_irec(
union xfs_btree_rec *rec,
struct xfs_rmap_irec *irec)
diff --git a/fs/xfs/libxfs/xfs_rmap.h b/fs/xfs/libxfs/xfs_rmap.h
index 7899305..188db38 100644
--- a/fs/xfs/libxfs/xfs_rmap.h
+++ b/fs/xfs/libxfs/xfs_rmap.h
@@ -212,5 +212,8 @@ int xfs_rmap_find_left_neighbor(struct xfs_btree_cur *cur, xfs_agblock_t bno,
int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
uint64_t owner, uint64_t offset, unsigned int flags,
struct xfs_rmap_irec *irec, int *stat);
+union xfs_btree_rec;
+int xfs_rmap_btrec_to_irec(union xfs_btree_rec *rec,
+ struct xfs_rmap_irec *irec);
#endif /* __XFS_RMAP_H__ */
diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c
index 385c2ac..f4c776d 100644
--- a/fs/xfs/libxfs/xfs_rmap_btree.c
+++ b/fs/xfs/libxfs/xfs_rmap_btree.c
@@ -377,7 +377,6 @@ const struct xfs_buf_ops xfs_rmapbt_buf_ops = {
.verify_write = xfs_rmapbt_write_verify,
};
-#if defined(DEBUG) || defined(XFS_WARN)
STATIC int
xfs_rmapbt_keys_inorder(
struct xfs_btree_cur *cur,
@@ -437,7 +436,6 @@ xfs_rmapbt_recs_inorder(
return 1;
return 0;
}
-#endif /* DEBUG */
static const struct xfs_btree_ops xfs_rmapbt_ops = {
.rec_len = sizeof(struct xfs_rmap_rec),
@@ -456,10 +454,8 @@ static const struct xfs_btree_ops xfs_rmapbt_ops = {
.key_diff = xfs_rmapbt_key_diff,
.buf_ops = &xfs_rmapbt_buf_ops,
.diff_two_keys = xfs_rmapbt_diff_two_keys,
-#if defined(DEBUG) || defined(XFS_WARN)
.keys_inorder = xfs_rmapbt_keys_inorder,
.recs_inorder = xfs_rmapbt_recs_inorder,
-#endif
};
/*
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 81d24f5..d155c52 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -1164,6 +1164,89 @@ xfs_scrub_finobt(
return xfs_scrub_iallocbt(ip->i_mount, sm, XFS_BTNUM_FINO);
}
+/* Reverse-mapping scrubber. */
+
+/* Scrub an rmapbt record. */
+STATIC int
+xfs_scrub_rmapbt_helper(
+ struct xfs_scrub_btree *bs,
+ union xfs_btree_rec *rec)
+{
+ struct xfs_mount *mp = bs->cur->bc_mp;
+ struct xfs_agf *agf;
+ struct xfs_rmap_irec irec;
+ xfs_agblock_t eoag;
+ bool non_inode;
+ bool is_unwritten;
+ bool is_bmbt;
+ bool is_attr;
+ int error;
+
+ error = xfs_rmap_btrec_to_irec(rec, &irec);
+ if (error)
+ return error;
+
+ agf = XFS_BUF_TO_AGF(bs->agf_bp);
+ eoag = be32_to_cpu(agf->agf_length);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rm_startblock < mp->m_sb.sb_agblocks)
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rm_startblock < eoag);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rm_startblock < irec.rm_startblock +
+ irec.rm_blockcount);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rm_startblock + irec.rm_blockcount <=
+ mp->m_sb.sb_agblocks)
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rm_startblock + irec.rm_blockcount <=
+ eoag);
+
+ non_inode = XFS_RMAP_NON_INODE_OWNER(irec.rm_owner);
+ is_bmbt = irec.rm_flags & XFS_RMAP_BMBT_BLOCK;
+ is_attr = irec.rm_flags & XFS_RMAP_ATTR_FORK;
+ is_unwritten = irec.rm_flags & XFS_RMAP_UNWRITTEN;
+
+ XFS_BTREC_SCRUB_CHECK(bs, !is_bmbt || irec.rm_offset == 0);
+ XFS_BTREC_SCRUB_CHECK(bs, !non_inode || irec.rm_offset == 0);
+ XFS_BTREC_SCRUB_CHECK(bs, !is_unwritten || !(is_bmbt || non_inode ||
+ is_attr));
+ XFS_BTREC_SCRUB_CHECK(bs, !non_inode || !(is_bmbt || is_unwritten ||
+ is_attr));
+
+ /* XXX: check with the owner */
+
+ return error;
+}
+
+/* Scrub the rmap btree for some AG. */
+STATIC int
+xfs_scrub_rmapbt(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_scrub_btree bs;
+ xfs_agnumber_t agno;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ error = xfs_scrub_btree_get_ag_headers(mp, &bs, agno);
+ if (error)
+ return error;
+
+ bs.cur = xfs_rmapbt_init_cursor(mp, NULL, bs.agf_bp, agno);
+ bs.scrub_rec = xfs_scrub_rmapbt_helper;
+ xfs_rmap_ag_owner(&bs.oinfo, XFS_RMAP_OWN_AG);
+ error = xfs_scrub_btree(&bs);
+ xfs_btree_del_cursor(bs.cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ xfs_scrub_btree_put_ag_headers(&bs);
+
+ if (!error && bs.error)
+ error = bs.error;
+
+ return error;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -1180,6 +1263,7 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_cntbt, NULL},
{xfs_scrub_inobt, NULL},
{xfs_scrub_finobt, xfs_sb_version_hasfinobt},
+ {xfs_scrub_rmapbt, xfs_sb_version_hasrmapbt},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 14/25] xfs: support scrubbing refcount btree
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (12 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 13/25] xfs: support scrubbing rmap btree Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 15/25] xfs: scrub inodes Darrick J. Wong
` (10 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Plumb in the pieces necessary to check the refcount btree. If rmap is
available, check the reference count by performing an interval query
against the rmapbt.
v2: Handle the case where the rmap records are not all at least the
length of the refcount extent.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 3 +-
fs/xfs/libxfs/xfs_refcount_btree.c | 33 +++++++++++++-----
fs/xfs/xfs_scrub.c | 67 ++++++++++++++++++++++++++++++++++++
3 files changed, 94 insertions(+), 9 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 31e091c..60dfe03 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -544,7 +544,8 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_INOBT 6 /* inode btree */
#define XFS_SCRUB_TYPE_FINOBT 7 /* free inode btree */
#define XFS_SCRUB_TYPE_RMAPBT 8 /* reverse mapping btree */
-#define XFS_SCRUB_TYPE_MAX 8
+#define XFS_SCRUB_TYPE_REFCNTBT 9 /* reference count btree */
+#define XFS_SCRUB_TYPE_MAX 9
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c
index fa8dfd6..a58540c 100644
--- a/fs/xfs/libxfs/xfs_refcount_btree.c
+++ b/fs/xfs/libxfs/xfs_refcount_btree.c
@@ -164,18 +164,26 @@ xfs_refcountbt_init_key_from_rec(
union xfs_btree_key *key,
union xfs_btree_rec *rec)
{
- ASSERT(rec->refc.rc_startblock != 0);
-
key->refc.rc_startblock = rec->refc.rc_startblock;
}
STATIC void
+xfs_refcountbt_init_high_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ __u32 x;
+
+ x = be32_to_cpu(rec->refc.rc_startblock);
+ x += be32_to_cpu(rec->refc.rc_blockcount) - 1;
+ key->refc.rc_startblock = cpu_to_be32(x);
+}
+
+STATIC void
xfs_refcountbt_init_rec_from_cur(
struct xfs_btree_cur *cur,
union xfs_btree_rec *rec)
{
- ASSERT(cur->bc_rec.rc.rc_startblock != 0);
-
rec->refc.rc_startblock = cpu_to_be32(cur->bc_rec.rc.rc_startblock);
rec->refc.rc_blockcount = cpu_to_be32(cur->bc_rec.rc.rc_blockcount);
rec->refc.rc_refcount = cpu_to_be32(cur->bc_rec.rc.rc_refcount);
@@ -205,6 +213,16 @@ xfs_refcountbt_key_diff(
return (__int64_t)be32_to_cpu(kp->rc_startblock) - rec->rc_startblock;
}
+STATIC __int64_t
+xfs_refcountbt_diff_two_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return (__int64_t)be32_to_cpu(k1->refc.rc_startblock) -
+ be32_to_cpu(k2->refc.rc_startblock);
+}
+
STATIC bool
xfs_refcountbt_verify(
struct xfs_buf *bp)
@@ -267,7 +285,6 @@ const struct xfs_buf_ops xfs_refcountbt_buf_ops = {
.verify_write = xfs_refcountbt_write_verify,
};
-#if defined(DEBUG) || defined(XFS_WARN)
STATIC int
xfs_refcountbt_keys_inorder(
struct xfs_btree_cur *cur,
@@ -296,13 +313,13 @@ xfs_refcountbt_recs_inorder(
b.rc_startblock = be32_to_cpu(r2->refc.rc_startblock);
b.rc_blockcount = be32_to_cpu(r2->refc.rc_blockcount);
b.rc_refcount = be32_to_cpu(r2->refc.rc_refcount);
+ a = a; b = b;
trace_xfs_refcount_rec_order_error(cur->bc_mp,
cur->bc_private.a.agno, &a, &b);
}
return ret;
}
-#endif /* DEBUG */
static const struct xfs_btree_ops xfs_refcountbt_ops = {
.rec_len = sizeof(struct xfs_refcount_rec),
@@ -315,14 +332,14 @@ static const struct xfs_btree_ops xfs_refcountbt_ops = {
.get_minrecs = xfs_refcountbt_get_minrecs,
.get_maxrecs = xfs_refcountbt_get_maxrecs,
.init_key_from_rec = xfs_refcountbt_init_key_from_rec,
+ .init_high_key_from_rec = xfs_refcountbt_init_high_key_from_rec,
.init_rec_from_cur = xfs_refcountbt_init_rec_from_cur,
.init_ptr_from_cur = xfs_refcountbt_init_ptr_from_cur,
.key_diff = xfs_refcountbt_key_diff,
.buf_ops = &xfs_refcountbt_buf_ops,
-#if defined(DEBUG) || defined(XFS_WARN)
+ .diff_two_keys = xfs_refcountbt_diff_two_keys,
.keys_inorder = xfs_refcountbt_keys_inorder,
.recs_inorder = xfs_refcountbt_recs_inorder,
-#endif
};
/*
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index d155c52..4894221 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -1247,6 +1247,72 @@ xfs_scrub_rmapbt(
return error;
}
+/* Reference count btree scrubber. */
+
+/* Scrub a refcountbt record. */
+STATIC int
+xfs_scrub_refcountbt_helper(
+ struct xfs_scrub_btree *bs,
+ union xfs_btree_rec *rec)
+{
+ struct xfs_mount *mp = bs->cur->bc_mp;
+ struct xfs_agf *agf;
+ struct xfs_refcount_irec irec;
+ xfs_agblock_t eoag;
+ int error = 0;
+
+ irec.rc_startblock = be32_to_cpu(rec->refc.rc_startblock);
+ irec.rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
+ irec.rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
+ agf = XFS_BUF_TO_AGF(bs->agf_bp);
+ eoag = be32_to_cpu(agf->agf_length);
+
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rc_startblock < mp->m_sb.sb_agblocks);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rc_startblock < eoag);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rc_startblock < irec.rc_startblock +
+ irec.rc_blockcount);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)irec.rc_startblock +
+ irec.rc_blockcount <= mp->m_sb.sb_agblocks);
+ XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)irec.rc_startblock +
+ irec.rc_blockcount <= eoag);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount >= 1);
+
+ return error;
+}
+
+/* Scrub the refcount btree for some AG. */
+STATIC int
+xfs_scrub_refcountbt(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_scrub_btree bs;
+ xfs_agnumber_t agno;
+ int error;
+
+ if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
+ return -EINVAL;
+ agno = sm->control;
+
+ error = xfs_scrub_btree_get_ag_headers(mp, &bs, agno);
+ if (error)
+ return error;
+
+ bs.cur = xfs_refcountbt_init_cursor(mp, NULL, bs.agf_bp, agno, NULL);
+ bs.scrub_rec = xfs_scrub_refcountbt_helper;
+ xfs_rmap_ag_owner(&bs.oinfo, XFS_RMAP_OWN_REFC);
+ error = xfs_scrub_btree(&bs);
+ xfs_btree_del_cursor(bs.cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ xfs_scrub_btree_put_ag_headers(&bs);
+
+ if (!error && bs.error)
+ error = bs.error;
+
+ return error;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -1264,6 +1330,7 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_inobt, NULL},
{xfs_scrub_finobt, xfs_sb_version_hasfinobt},
{xfs_scrub_rmapbt, xfs_sb_version_hasrmapbt},
+ {xfs_scrub_refcountbt, xfs_sb_version_hasreflink},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 15/25] xfs: scrub inodes
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (13 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 14/25] xfs: support scrubbing refcount btree Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:41 ` [PATCH 16/25] xfs: scrub inode block mappings Darrick J. Wong
` (9 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Scrub the fields within an inode.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 3 +-
fs/xfs/xfs_itable.c | 2 +
fs/xfs/xfs_itable.h | 5 +++
fs/xfs/xfs_scrub.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 60dfe03..30903d1 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -545,7 +545,8 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_FINOBT 7 /* free inode btree */
#define XFS_SCRUB_TYPE_RMAPBT 8 /* reverse mapping btree */
#define XFS_SCRUB_TYPE_REFCNTBT 9 /* reference count btree */
-#define XFS_SCRUB_TYPE_MAX 9
+#define XFS_SCRUB_TYPE_INODE 10 /* inode record */
+#define XFS_SCRUB_TYPE_MAX 10
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index 66e8817..4fd5fe1 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -31,7 +31,7 @@
#include "xfs_trace.h"
#include "xfs_icache.h"
-STATIC int
+int
xfs_internal_inum(
xfs_mount_t *mp,
xfs_ino_t ino)
diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h
index 6ea8b39..dd2427b 100644
--- a/fs/xfs/xfs_itable.h
+++ b/fs/xfs/xfs_itable.h
@@ -96,4 +96,9 @@ xfs_inumbers(
void __user *buffer, /* buffer with inode info */
inumbers_fmt_pf formatter);
+int
+xfs_internal_inum(
+ xfs_mount_t *mp,
+ xfs_ino_t ino);
+
#endif /* __XFS_ITABLE_H__ */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 4894221..383a00e 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -43,6 +43,8 @@
#include "xfs_rmap.h"
#include "xfs_rmap_btree.h"
#include "xfs_rtalloc.h"
+#include "xfs_icache.h"
+#include "xfs_itable.h"
/* Report a scrub corruption in dmesg. */
STATIC void
@@ -1313,6 +1315,72 @@ xfs_scrub_refcountbt(
return error;
}
+/* Scrub an inode. */
+STATIC int
+xfs_scrub_inode(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_inode *ips;
+ int error;
+
+ if (sm->flags)
+ return -EINVAL;
+
+ if (sm->control && sm->control != ip->i_ino) {
+ if (xfs_internal_inum(mp, sm->control)) {
+ error = -ENOENT;
+ goto out;
+ }
+ error = xfs_iget(ip->i_mount, NULL, sm->control,
+ XFS_IGET_UNTRUSTED | XFS_IGET_DONTCACHE,
+ XFS_ILOCK_EXCL, &ips);
+ if (error)
+ goto out;
+ } else {
+ ips = ip;
+ xfs_ilock(ips, XFS_ILOCK_EXCL);
+ }
+
+ /* The verifiers should refuse any inode with bad fields. */
+ XFS_INO_SCRUB_GOTO(ips, NULL, "inode", ips != NULL, out_unlock);
+
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ ips->i_d.di_projid_hi == 0 ||
+ xfs_sb_version_hasprojid32bit(&mp->m_sb));
+
+ if (ips->i_d.di_flags & XFS_DIFLAG_EXTSIZE) {
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ ips->i_d.di_extsize > 0);
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ ips->i_d.di_extsize <= MAXEXTLEN);
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ XFS_IS_REALTIME_INODE(ip) ||
+ ips->i_d.di_extsize <= mp->m_sb.sb_agblocks / 2);
+ }
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ !(ip->i_d.di_flags & XFS_DIFLAG_IMMUTABLE) ||
+ !(ip->i_d.di_flags & XFS_DIFLAG_APPEND));
+
+ if (ips->i_d.di_version == 3 &&
+ ips->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) {
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ ips->i_d.di_cowextsize > 0);
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ ips->i_d.di_cowextsize <= MAXEXTLEN);
+ XFS_INO_SCRUB_CHECK(ips, NULL, "inode",
+ ips->i_d.di_cowextsize <= mp->m_sb.sb_agblocks / 2);
+ }
+
+out_unlock:
+ xfs_iunlock(ips, XFS_ILOCK_EXCL);
+ if (ips != ip)
+ IRELE(ips);
+out:
+ return error;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -1331,6 +1399,7 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_finobt, xfs_sb_version_hasfinobt},
{xfs_scrub_rmapbt, xfs_sb_version_hasrmapbt},
{xfs_scrub_refcountbt, xfs_sb_version_hasreflink},
+ {xfs_scrub_inode, NULL},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 16/25] xfs: scrub inode block mappings
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (14 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 15/25] xfs: scrub inodes Darrick J. Wong
@ 2016-08-25 23:41 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 17/25] xfs: scrub realtime bitmap/summary Darrick J. Wong
` (8 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:41 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Scrub an individual inode's block mappings to make sure they make sense.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_bmap.c | 2
fs/xfs/libxfs/xfs_bmap.h | 6 +
fs/xfs/libxfs/xfs_bmap_btree.c | 26 +++-
fs/xfs/libxfs/xfs_fs.h | 5 +
fs/xfs/xfs_scrub.c | 296 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 329 insertions(+), 6 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 9ae4a3a..7a91618 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1425,7 +1425,7 @@ xfs_bmap_search_multi_extents(
* Else, *lastxp will be set to the index of the found
* entry; *gotp will contain the entry.
*/
-STATIC xfs_bmbt_rec_host_t * /* pointer to found extent entry */
+xfs_bmbt_rec_host_t * /* pointer to found extent entry */
xfs_bmap_search_extents(
xfs_inode_t *ip, /* incore inode pointer */
xfs_fileoff_t bno, /* block number searched for */
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index 134ea00..4afa21c 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -259,4 +259,10 @@ int xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
struct xfs_inode *ip, int whichfork,
struct xfs_bmbt_irec *imap);
+struct xfs_bmbt_rec_host *
+ xfs_bmap_search_extents(struct xfs_inode *ip, xfs_fileoff_t bno,
+ int fork, int *eofp, xfs_extnum_t *lastxp,
+ struct xfs_bmbt_irec *gotp,
+ struct xfs_bmbt_irec *prevp);
+
#endif /* __XFS_BMAP_H__ */
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 8007d2b..1fc3eed 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -623,6 +623,16 @@ xfs_bmbt_init_key_from_rec(
}
STATIC void
+xfs_bmbt_init_high_key_from_rec(
+ union xfs_btree_key *key,
+ union xfs_btree_rec *rec)
+{
+ key->bmbt.br_startoff = cpu_to_be64(
+ xfs_bmbt_disk_get_startoff(&rec->bmbt) +
+ xfs_bmbt_disk_get_blockcount(&rec->bmbt) - 1);
+}
+
+STATIC void
xfs_bmbt_init_rec_from_cur(
struct xfs_btree_cur *cur,
union xfs_btree_rec *rec)
@@ -647,6 +657,16 @@ xfs_bmbt_key_diff(
cur->bc_rec.b.br_startoff;
}
+STATIC __int64_t
+xfs_bmbt_diff_two_keys(
+ struct xfs_btree_cur *cur,
+ union xfs_btree_key *k1,
+ union xfs_btree_key *k2)
+{
+ return (__int64_t)be64_to_cpu(k1->bmbt.br_startoff) -
+ be64_to_cpu(k2->bmbt.br_startoff);
+}
+
static bool
xfs_bmbt_verify(
struct xfs_buf *bp)
@@ -737,7 +757,6 @@ const struct xfs_buf_ops xfs_bmbt_buf_ops = {
};
-#if defined(DEBUG) || defined(XFS_WARN)
STATIC int
xfs_bmbt_keys_inorder(
struct xfs_btree_cur *cur,
@@ -758,7 +777,6 @@ xfs_bmbt_recs_inorder(
xfs_bmbt_disk_get_blockcount(&r1->bmbt) <=
xfs_bmbt_disk_get_startoff(&r2->bmbt);
}
-#endif /* DEBUG */
static const struct xfs_btree_ops xfs_bmbt_ops = {
.rec_len = sizeof(xfs_bmbt_rec_t),
@@ -772,14 +790,14 @@ static const struct xfs_btree_ops xfs_bmbt_ops = {
.get_minrecs = xfs_bmbt_get_minrecs,
.get_dmaxrecs = xfs_bmbt_get_dmaxrecs,
.init_key_from_rec = xfs_bmbt_init_key_from_rec,
+ .init_high_key_from_rec = xfs_bmbt_init_high_key_from_rec,
.init_rec_from_cur = xfs_bmbt_init_rec_from_cur,
.init_ptr_from_cur = xfs_bmbt_init_ptr_from_cur,
.key_diff = xfs_bmbt_key_diff,
+ .diff_two_keys = xfs_bmbt_diff_two_keys,
.buf_ops = &xfs_bmbt_buf_ops,
-#if defined(DEBUG) || defined(XFS_WARN)
.keys_inorder = xfs_bmbt_keys_inorder,
.recs_inorder = xfs_bmbt_recs_inorder,
-#endif
};
/*
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 30903d1..c688deb 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -546,7 +546,10 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_RMAPBT 8 /* reverse mapping btree */
#define XFS_SCRUB_TYPE_REFCNTBT 9 /* reference count btree */
#define XFS_SCRUB_TYPE_INODE 10 /* inode record */
-#define XFS_SCRUB_TYPE_MAX 10
+#define XFS_SCRUB_TYPE_BMBTD 11 /* data fork block mapping */
+#define XFS_SCRUB_TYPE_BMBTA 12 /* attr fork block mapping */
+#define XFS_SCRUB_TYPE_BMBTC 13 /* CoW fork block mapping */
+#define XFS_SCRUB_TYPE_MAX 13
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 383a00e..573acd4 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -1381,6 +1381,299 @@ out:
return error;
}
+/*
+ * Inode fork block mapping (BMBT) scrubber.
+ * More complex than the others because we have to scrub
+ * all the extents regardless of whether or not the fork
+ * is in btree format.
+ */
+
+struct xfs_scrub_bmap_info {
+ struct xfs_scrub_btree bs;
+ struct xfs_inode *ip;
+ const char *type;
+ xfs_daddr_t eofs;
+ xfs_fileoff_t lastoff;
+ bool is_rt;
+ bool is_shared;
+ bool scrub_btrec;
+ int whichfork;
+};
+
+/* Scrub a single extent record. */
+STATIC int
+xfs_scrub_bmap_extent(
+ struct xfs_inode *ip,
+ struct xfs_scrub_bmap_info *info,
+ struct xfs_bmbt_irec *irec)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_buf *bp = NULL;
+ struct xfs_buf *agi_bp = NULL;
+ struct xfs_buf *agf_bp = NULL;
+ xfs_daddr_t daddr;
+ xfs_daddr_t dlen;
+ xfs_agnumber_t agno;
+ xfs_fsblock_t bno;
+ int error = 0;
+ int err2 = 0;
+
+ if (info->bs.cur)
+ xfs_btree_get_block(info->bs.cur, 0, &bp);
+
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+ irec->br_startoff >= info->lastoff);
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+ irec->br_startblock != HOLESTARTBLOCK);
+
+ if (irec->br_startblock == DELAYSTARTBLOCK) {
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ irec->br_state == XFS_EXT_NORM);
+ goto out;
+ }
+
+ /* Actual mapping, so check the block ranges. */
+ if (info->is_rt) {
+ daddr = XFS_FSB_TO_BB(mp, irec->br_startblock);
+ agno = NULLAGNUMBER;
+ bno = irec->br_startblock;
+ } else {
+ daddr = XFS_FSB_TO_DADDR(mp, irec->br_startblock);
+ agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock);
+ bno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock);
+ }
+ dlen = XFS_FSB_TO_BB(mp, irec->br_blockcount);
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type, daddr < info->eofs);
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+ daddr + dlen < info->eofs);
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+ irec->br_state != XFS_EXT_UNWRITTEN ||
+ xfs_sb_version_hasextflgbit(&mp->m_sb));
+
+ /* Set ourselves up for cross-referencing later. */
+ if (!info->is_rt) {
+ err2 = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (err2)
+ goto out;
+ }
+
+out:
+ xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+ info->lastoff = irec->br_startoff + irec->br_blockcount;
+ if (!error && err2)
+ error = err2;
+ return error;
+}
+
+/* Scrub a bmbt record. */
+STATIC int
+xfs_scrub_bmapbt_helper(
+ struct xfs_scrub_btree *bs,
+ union xfs_btree_rec *rec)
+{
+ struct xfs_bmbt_rec_host ihost;
+ struct xfs_bmbt_irec irec;
+ struct xfs_scrub_bmap_info *info;
+ int error;
+
+ info = container_of(bs, struct xfs_scrub_bmap_info, bs);
+ if (!info->scrub_btrec)
+ return 0;
+
+ /* Set up the in-core record and scrub it. */
+ ihost.l0 = be64_to_cpu(rec->bmbt.l0);
+ ihost.l1 = be64_to_cpu(rec->bmbt.l1);
+ xfs_bmbt_get_all(&ihost, &irec);
+ error = xfs_scrub_bmap_extent(info->ip, info, &irec);
+
+ /* Record the error, but keep going. */
+ if (bs->error == 0 && error != 0)
+ bs->error = error;
+ return 0;
+}
+
+/* Scrub an inode fork's block mappings. */
+STATIC int
+xfs_scrub_bmap(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm,
+ int whichfork)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_ifork *ifp;
+ struct xfs_bmbt_irec irec;
+ struct xfs_bmbt_irec imap;
+ struct xfs_scrub_bmap_info info;
+ xfs_fileoff_t off;
+ xfs_fileoff_t endoff;
+ xfs_extnum_t extnum;
+ int eof;
+ int nmaps;
+ int flags = 0;
+ int error = 0;
+ int err2 = 0;
+
+ if (sm->control || sm->flags)
+ return -EINVAL;
+
+ memset(&info, 0, sizeof(info));
+ switch (whichfork) {
+ case XFS_DATA_FORK:
+ info.type = "data fork";
+ break;
+ case XFS_ATTR_FORK:
+ info.type = "attr fork";
+ break;
+ case XFS_COW_FORK:
+ info.type = "CoW fork";
+ break;
+ default:
+ info.type = NULL;
+ ASSERT(0);
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+
+ info.is_rt = whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip);
+ info.eofs = XFS_FSB_TO_BB(mp, info.is_rt ? mp->m_sb.sb_rblocks :
+ mp->m_sb.sb_dblocks);
+ info.ip = ip;
+ info.whichfork = whichfork;
+ info.is_shared = whichfork == XFS_DATA_FORK && xfs_is_reflink_inode(ip);
+
+ switch (whichfork) {
+ case XFS_COW_FORK:
+ /* Non-existent CoW forks are ignorable. */
+ if (!ifp)
+ goto out_unlock;
+ /* No CoW forks on non-reflink inodes/filesystems. */
+ XFS_INO_SCRUB_GOTO(ip, NULL, info.type,
+ xfs_is_reflink_inode(ip), out_unlock);
+ break;
+ case XFS_ATTR_FORK:
+ if (!ifp)
+ goto out_unlock;
+ XFS_INO_SCRUB_CHECK(ip, NULL, info.type,
+ xfs_sb_version_hasattr(&mp->m_sb));
+ break;
+ }
+
+ /* Check the fork values */
+ switch (XFS_IFORK_FORMAT(ip, whichfork)) {
+ case XFS_DINODE_FMT_UUID:
+ case XFS_DINODE_FMT_DEV:
+ case XFS_DINODE_FMT_LOCAL:
+ /* No mappings to check. */
+ goto out_unlock;
+ case XFS_DINODE_FMT_EXTENTS:
+ XFS_INO_SCRUB_GOTO(ip, NULL, info.type,
+ ifp->if_flags & XFS_IFEXTENTS, out_unlock);
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ XFS_INO_SCRUB_CHECK(ip, NULL, info.type,
+ whichfork != XFS_COW_FORK);
+ /*
+ * Scan the btree. If extents aren't loaded, have the btree
+ * scrub routine examine the extent records.
+ */
+ info.scrub_btrec = !(ifp->if_flags & XFS_IFEXTENTS);
+
+ info.bs.cur = xfs_bmbt_init_cursor(mp, NULL, ip, whichfork);
+ info.bs.scrub_rec = xfs_scrub_bmapbt_helper;
+ xfs_rmap_ino_bmbt_owner(&info.bs.oinfo, ip->i_ino, whichfork);
+ err2 = xfs_scrub_btree(&info.bs);
+ xfs_btree_del_cursor(info.bs.cur,
+ err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ info.bs.cur = NULL;
+ if (err2)
+ goto out_unlock;
+ if (error == 0 && info.bs.error != 0)
+ error = info.bs.error;
+ if (info.scrub_btrec)
+ goto out_unlock;
+ break;
+ default:
+ XFS_INO_SCRUB_GOTO(ip, NULL, info.type, false, out_unlock);
+ break;
+ }
+
+ /* Extent data is in memory, so scrub that. */
+ switch (whichfork) {
+ case XFS_ATTR_FORK:
+ flags |= XFS_BMAPI_ATTRFORK;
+ break;
+ case XFS_COW_FORK:
+ flags |= XFS_BMAPI_COWFORK;
+ break;
+ default:
+ break;
+ }
+
+ /* Find the offset of the last extent in the mapping. */
+ xfs_bmap_search_extents(ip, -1ULL, whichfork, &eof, &extnum,
+ &irec, &imap);
+
+ /* Scrub extent records. */
+ off = 0;
+ endoff = irec.br_startoff + irec.br_blockcount;
+ while (true) {
+ nmaps = 1;
+ err2 = xfs_bmapi_read(ip, off, endoff - off, &irec,
+ &nmaps, flags);
+ if (err2 || nmaps == 0 || irec.br_startoff > endoff)
+ break;
+
+ /* Scrub non-hole extent. */
+ if (irec.br_startblock != HOLESTARTBLOCK) {
+ err2 = xfs_scrub_bmap_extent(ip, &info, &irec);
+ if (!error && err2)
+ error = err2;
+ if (xfs_scrub_should_terminate(&error))
+ break;
+ }
+
+ off += irec.br_blockcount;
+ }
+
+out_unlock:
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+ if (error == 0 && err2 != 0)
+ error = err2;
+ return error;
+}
+
+/* Scrub an inode's data fork. */
+STATIC int
+xfs_scrub_bmap_data(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ return xfs_scrub_bmap(ip, sm, XFS_DATA_FORK);
+}
+
+/* Scrub an inode's attr fork. */
+STATIC int
+xfs_scrub_bmap_attr(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ return xfs_scrub_bmap(ip, sm, XFS_ATTR_FORK);
+}
+
+/* Scrub an inode's CoW fork. */
+STATIC int
+xfs_scrub_bmap_cow(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ if (!xfs_is_reflink_inode(ip))
+ return -ENOENT;
+
+ return xfs_scrub_bmap(ip, sm, XFS_COW_FORK);
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -1400,6 +1693,9 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_rmapbt, xfs_sb_version_hasrmapbt},
{xfs_scrub_refcountbt, xfs_sb_version_hasreflink},
{xfs_scrub_inode, NULL},
+ {xfs_scrub_bmap_data, NULL},
+ {xfs_scrub_bmap_attr, NULL},
+ {xfs_scrub_bmap_cow, NULL},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 17/25] xfs: scrub realtime bitmap/summary
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (15 preceding siblings ...)
2016-08-25 23:41 ` [PATCH 16/25] xfs: scrub inode block mappings Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 18/25] xfs: scrub should cross-reference with the bnobt Darrick J. Wong
` (7 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Perform simple tests of the realtime bitmap and summary.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_format.h | 5 +++
fs/xfs/libxfs/xfs_fs.h | 4 ++
fs/xfs/libxfs/xfs_rtbitmap.c | 2 +
fs/xfs/xfs_rtalloc.h | 3 ++
fs/xfs/xfs_scrub.c | 78 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 90 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index a3aa5e9..5703b57 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -315,6 +315,11 @@ static inline bool xfs_sb_good_version(struct xfs_sb *sbp)
return false;
}
+static inline bool xfs_sb_version_hasrealtime(struct xfs_sb *sbp)
+{
+ return sbp->sb_rblocks > 0;
+}
+
/*
* Detect a mismatched features2 field. Older kernels read/wrote
* this into the wrong slot, so to be safe we keep them in sync.
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index c688deb..211c874 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -549,7 +549,9 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_BMBTD 11 /* data fork block mapping */
#define XFS_SCRUB_TYPE_BMBTA 12 /* attr fork block mapping */
#define XFS_SCRUB_TYPE_BMBTC 13 /* CoW fork block mapping */
-#define XFS_SCRUB_TYPE_MAX 13
+#define XFS_SCRUB_TYPE_RTBITMAP 14 /* realtime bitmap */
+#define XFS_SCRUB_TYPE_RTSUM 15 /* realtime summary */
+#define XFS_SCRUB_TYPE_MAX 15
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c
index ea45584..f4b68c0 100644
--- a/fs/xfs/libxfs/xfs_rtbitmap.c
+++ b/fs/xfs/libxfs/xfs_rtbitmap.c
@@ -70,7 +70,7 @@ const struct xfs_buf_ops xfs_rtbuf_ops = {
* Get a buffer for the bitmap or summary file block specified.
* The buffer is returned read and locked.
*/
-static int
+int
xfs_rtbuf_get(
xfs_mount_t *mp, /* file system mount structure */
xfs_trans_t *tp, /* transaction pointer */
diff --git a/fs/xfs/xfs_rtalloc.h b/fs/xfs/xfs_rtalloc.h
index 355dd9e..91e48f9 100644
--- a/fs/xfs/xfs_rtalloc.h
+++ b/fs/xfs/xfs_rtalloc.h
@@ -98,6 +98,8 @@ xfs_growfs_rt(
/*
* From xfs_rtbitmap.c
*/
+int xfs_rtbuf_get(struct xfs_mount *mp, struct xfs_trans *tp,
+ xfs_rtblock_t block, int issum, struct xfs_buf **bpp);
int xfs_rtcheck_range(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_rtblock_t start, xfs_extlen_t len, int val,
xfs_rtblock_t *new, int *stat);
@@ -126,6 +128,7 @@ int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
# define xfs_rtfree_extent(t,b,l) (ENOSYS)
# define xfs_rtpick_extent(m,t,l,rb) (ENOSYS)
# define xfs_growfs_rt(mp,in) (ENOSYS)
+# define xfs_rtbuf_get(m,t,b,i,p) (ENOSYS)
static inline int /* error */
xfs_rtmount_init(
xfs_mount_t *mp) /* file system mount structure */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 573acd4..22ba07d 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -773,6 +773,7 @@ xfs_scrub_sb(
XFS_SCRUB_SB_FEAT(metauuid);
XFS_SCRUB_SB_FEAT(rmapbt);
XFS_SCRUB_SB_FEAT(reflink);
+ XFS_SCRUB_SB_FEAT(realtime);
#undef XFS_SCRUB_SB_FEAT
out:
@@ -1674,6 +1675,81 @@ xfs_scrub_bmap_cow(
return xfs_scrub_bmap(ip, sm, XFS_COW_FORK);
}
+/* Scrub the realtime bitmap. */
+STATIC int
+xfs_scrub_rtbitmap(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_buf *bp = NULL;
+ xfs_rtblock_t rtstart;
+ xfs_rtblock_t rtend;
+ xfs_rtblock_t block;
+ xfs_rtblock_t rem;
+ int is_free;
+ int error = 0;
+ int err2 = 0;
+
+ if (sm->control || sm->flags)
+ return -EINVAL;
+
+ xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+
+ /* Iterate the bitmap, looking for discrepancies. */
+ rtstart = 0;
+ rem = mp->m_sb.sb_rblocks;
+ while (rem) {
+ if (xfs_scrub_should_terminate(&error))
+ break;
+
+ /* Is the first block free? */
+ err2 = xfs_rtcheck_range(mp, NULL, rtstart, 1, 1, &rtend,
+ &is_free);
+ if (err2)
+ goto out_unlock;
+
+ /* How long does the extent go for? */
+ err2 = xfs_rtfind_forw(mp, NULL, rtstart,
+ mp->m_sb.sb_rblocks - 1, &rtend);
+ if (err2)
+ goto out_unlock;
+
+ /* Find the buffer for error reporting. */
+ block = XFS_BITTOBLOCK(mp, rtstart);
+ err2 = xfs_rtbuf_get(mp, NULL, block, 0, &bp);
+ if (err2)
+ break;
+ XFS_SCRUB_CHECK(mp, bp, "rtbitmap", rtend >= rtstart);
+
+ xfs_buf_relse(bp);
+ bp = NULL;
+ rem -= rtend - rtstart + 1;
+ rtstart = rtend + 1;
+ }
+
+out_unlock:
+ if (bp)
+ xfs_buf_relse(bp);
+ xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+ if (!error && err2)
+ error = err2;
+ return error;
+}
+
+/* Scrub the realtime summary. */
+STATIC int
+xfs_scrub_rtsummary(
+ struct xfs_inode *ip,
+ struct xfs_scrub_metadata *sm)
+{
+ if (sm->control || sm->flags)
+ return -EINVAL;
+
+ /* XXX: implement this some day */
+ return -ENOENT;
+}
+
/* Scrubbing dispatch. */
struct xfs_scrub_meta_fns {
@@ -1696,6 +1772,8 @@ static const struct xfs_scrub_meta_fns meta_scrub_fns[] = {
{xfs_scrub_bmap_data, NULL},
{xfs_scrub_bmap_attr, NULL},
{xfs_scrub_bmap_cow, NULL},
+ {xfs_scrub_rtbitmap, xfs_sb_version_hasrealtime},
+ {xfs_scrub_rtsummary, xfs_sb_version_hasrealtime},
};
/* Dispatch metadata scrubbing. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 18/25] xfs: scrub should cross-reference with the bnobt
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (16 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 17/25] xfs: scrub realtime bitmap/summary Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 19/25] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
` (6 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
When we're scrubbing various btrees, cross-reference the records with
the bnobt to ensure that we don't also think the space is free.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_alloc.c | 19 +++++
fs/xfs/libxfs/xfs_alloc.h | 3 +
fs/xfs/xfs_scrub.c | 184 +++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 197 insertions(+), 9 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 37782a1..0de83f5 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -2974,3 +2974,22 @@ xfs_alloc_query_range(
return xfs_btree_query_range(cur, &low_brec, &high_brec,
xfs_alloc_query_range_helper, &query);
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_alloc_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.a.ar_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.a.ar_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index c3ada6b..b740456 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -222,4 +222,7 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur,
struct xfs_alloc_rec_incore *high_rec,
xfs_alloc_query_range_fn fn, void *priv);
+int xfs_alloc_has_record(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+ xfs_extlen_t len, bool *exist);
+
#endif /* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 22ba07d..612d3c3 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -515,6 +515,67 @@ xfs_scrub_should_terminate(
}
/*
+ * Make sure this btree block isn't in the free list and that there's
+ * an rmap record for it.
+ */
+STATIC int
+xfs_scrub_btree_check_block_owner(
+ struct xfs_scrub_btree *bs,
+ xfs_fsblock_t fsb)
+{
+ xfs_agnumber_t agno;
+ xfs_agblock_t bno;
+ bool is_freesp;
+ struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *bcur = NULL;
+ int error = 0;
+ int err2;
+
+ agno = XFS_FSB_TO_AGNO(bs->cur->bc_mp, fsb);
+ bno = XFS_FSB_TO_AGBNO(bs->cur->bc_mp, fsb);
+
+ if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ err2 = xfs_alloc_read_agf(bs->cur->bc_mp, NULL, agno,
+ 0, &agf_bp);
+ if (err2)
+ return error;
+ bcur = xfs_allocbt_init_cursor(bs->cur->bc_mp, NULL,
+ agf_bp, agno, XFS_BTNUM_BNO);
+ } else {
+ bcur = bs->bno_cur;
+ }
+
+ /* Check that this block isn't free. */
+ err2 = xfs_alloc_has_record(bcur, bno, 1, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
+ if (agf_bp) {
+ xfs_btree_del_cursor(bcur, XFS_BTREE_ERROR);
+ xfs_buf_relse(agf_bp);
+ }
+
+ return error;
+}
+
+/* Check the owner of a btree block. */
+STATIC int
+xfs_scrub_btree_check_owner(
+ struct xfs_scrub_btree *bs,
+ struct xfs_buf *bp)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ xfs_fsblock_t fsbno;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && bp == NULL)
+ return 0;
+
+ fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn);
+
+ return xfs_scrub_btree_check_block_owner(bs, fsbno);
+}
+
+/*
* 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
@@ -604,6 +665,9 @@ xfs_scrub_btree(
error = xfs_btree_check_block(cur, block, level, bp);
if (error)
goto out;
+ error = xfs_scrub_btree_check_owner(bs, bp);
+ if (error)
+ goto out;
cur->bc_ptrs[level] = 1;
@@ -664,6 +728,10 @@ xfs_scrub_btree(
if (error)
goto out;
+ error = xfs_scrub_btree_check_owner(bs, bp);
+ if (error)
+ goto out;
+
cur->bc_ptrs[level] = 1;
}
@@ -716,9 +784,14 @@ xfs_scrub_sb(
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_buf *bp;
+ struct xfs_buf *agi_bp = NULL;
+ struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
struct xfs_sb sb;
xfs_agnumber_t agno;
+ bool is_freesp;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -732,7 +805,7 @@ xfs_scrub_sb(
return error;
if (agno == 0)
- goto out;
+ goto btree_xref;
xfs_sb_from_disk(&sb, XFS_BUF_TO_SBP(bp));
@@ -776,6 +849,22 @@ xfs_scrub_sb(
XFS_SCRUB_SB_FEAT(realtime);
#undef XFS_SCRUB_SB_FEAT
+ if (error)
+ goto out;
+
+btree_xref:
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
+ goto out;
+
+ /* Cross-reference with bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_SB_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, bp, "superblock", !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+ xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
xfs_buf_relse(bp);
return error;
@@ -791,12 +880,15 @@ xfs_scrub_agf(
struct xfs_agf *agf;
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t daddr;
xfs_daddr_t eofs;
+ bool is_freesp;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -853,6 +945,13 @@ xfs_scrub_agf(
XFS_SCRUB_CHECK(mp, agf_bp, "AGF", daddr < eofs);
}
+ /* Cross-reference with the bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_AGF_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -869,12 +968,15 @@ xfs_scrub_agfl(
struct xfs_buf *agf_bp = NULL;
struct xfs_buf *agfl_bp;
__be32 *agfl_bno;
+ struct xfs_btree_cur *xcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t eofs;
+ bool is_freesp;
int i;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -893,6 +995,12 @@ xfs_scrub_agfl(
eofs = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
eoag = be32_to_cpu(agf->agf_length);
+ /* Cross-reference with the bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_AGFL_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !is_freesp);
+
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
for (i = be32_to_cpu(agf->agf_flfirst);
i <= be32_to_cpu(agf->agf_fllast);
@@ -904,8 +1012,14 @@ xfs_scrub_agfl(
agbno < mp->m_sb.sb_agblocks);
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
agbno < eoag);
+
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(xcur, agbno, 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !is_freesp);
}
+ xfs_btree_del_cursor(xcur, XFS_BTREE_ERROR);
xfs_buf_relse(agfl_bp);
err_no_agfl:
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
@@ -923,12 +1037,15 @@ xfs_scrub_agi(
struct xfs_agf *agf;
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t daddr;
xfs_daddr_t eofs;
+ bool is_freesp;
int error;
+ int err2;
if (sm->control >= mp->m_sb.sb_agcount || sm->flags)
return -EINVAL;
@@ -958,6 +1075,13 @@ xfs_scrub_agi(
XFS_SCRUB_CHECK(mp, agi_bp, "AGI", daddr < eofs);
}
+ /* Cross-reference with bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno, XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, XFS_AGI_BLOCK(mp), 1, &is_freesp);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1056,9 +1180,11 @@ xfs_scrub_iallocbt_helper(
xfs_agblock_t bno;
xfs_agblock_t eoag;
xfs_extlen_t len;
+ bool is_freesp;
int holecount;
int i;
int error = 0;
+ int err2;
uint64_t holes;
xfs_inobt_btrec_to_irec(mp, rec, &irec);
@@ -1082,7 +1208,14 @@ xfs_scrub_iallocbt_helper(
mp->m_sb.sb_agblocks);
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
eoag);
- return error;
+
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(bs->bno_cur, bno, len,
+ &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
+ goto out;
}
/* Check each chunk of a sparse inode cluster. */
@@ -1109,12 +1242,19 @@ xfs_scrub_iallocbt_helper(
mp->m_sb.sb_agblocks);
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
eoag);
+
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(bs->bno_cur, bno, len,
+ &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
}
XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
XFS_BTREC_SCRUB_CHECK(bs, holecount + irec.ir_count ==
XFS_INODES_PER_CHUNK);
+out:
return error;
}
@@ -1179,11 +1319,13 @@ xfs_scrub_rmapbt_helper(
struct xfs_agf *agf;
struct xfs_rmap_irec irec;
xfs_agblock_t eoag;
+ bool is_freesp;
bool non_inode;
bool is_unwritten;
bool is_bmbt;
bool is_attr;
- int error;
+ int error = 0;
+ int err2;
error = xfs_rmap_btrec_to_irec(rec, &irec);
if (error)
@@ -1212,7 +1354,11 @@ xfs_scrub_rmapbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, !non_inode || !(is_bmbt || is_unwritten ||
is_attr));
- /* XXX: check with the owner */
+ /* check there's no record in freesp btrees */
+ err2 = xfs_alloc_has_record(bs->bno_cur, irec.rm_startblock,
+ irec.rm_blockcount, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
return error;
}
@@ -1262,7 +1408,9 @@ xfs_scrub_refcountbt_helper(
struct xfs_agf *agf;
struct xfs_refcount_irec irec;
xfs_agblock_t eoag;
+ bool is_freesp;
int error = 0;
+ int err2;
irec.rc_startblock = be32_to_cpu(rec->refc.rc_startblock);
irec.rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
@@ -1280,6 +1428,12 @@ xfs_scrub_refcountbt_helper(
irec.rc_blockcount <= eoag);
XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount >= 1);
+ /* Cross-reference with the bnobt. */
+ err2 = xfs_alloc_has_record(bs->bno_cur, irec.rc_startblock,
+ irec.rc_blockcount, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
return error;
}
@@ -1412,10 +1566,12 @@ xfs_scrub_bmap_extent(
struct xfs_buf *bp = NULL;
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
+ struct xfs_btree_cur *xcur = NULL;
xfs_daddr_t daddr;
xfs_daddr_t dlen;
xfs_agnumber_t agno;
xfs_fsblock_t bno;
+ bool is_freesp;
int error = 0;
int err2 = 0;
@@ -1450,19 +1606,29 @@ xfs_scrub_bmap_extent(
XFS_INO_SCRUB_CHECK(ip, bp, info->type,
irec->br_state != XFS_EXT_UNWRITTEN ||
xfs_sb_version_hasextflgbit(&mp->m_sb));
+ if (error)
+ goto out;
/* Set ourselves up for cross-referencing later. */
if (!info->is_rt) {
- err2 = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
- if (err2)
+ error = xfs_scrub_get_ag_headers(mp, agno, &agi_bp, &agf_bp);
+ if (error)
goto out;
+
+ /* Cross-reference with the bnobt. */
+ xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno,
+ XFS_BTNUM_BNO);
+ err2 = xfs_alloc_has_record(xcur, bno,
+ irec->br_blockcount, &is_freesp);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(&info->bs, !is_freesp);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
}
-out:
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
+out:
info->lastoff = irec->br_startoff + irec->br_blockcount;
- if (!error && err2)
- error = err2;
return error;
}
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 19/25] xfs: cross-reference bnobt records with cntbt
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (17 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 18/25] xfs: scrub should cross-reference with the bnobt Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 20/25] xfs: cross-reference extents with AG header Darrick J. Wong
` (5 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Scrub should make sure that each bnobt record has a corresponding
cntbt record.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_alloc.c | 2 +-
fs/xfs/libxfs/xfs_alloc.h | 7 +++++++
fs/xfs/xfs_scrub.c | 24 ++++++++++++++++++++++++
3 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 0de83f5..a65cd8d 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -170,7 +170,7 @@ xfs_alloc_lookup_ge(
* Lookup the first record less than or equal to [bno, len]
* in the btree given by cur.
*/
-static int /* error */
+int /* error */
xfs_alloc_lookup_le(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_agblock_t bno, /* starting block of extent */
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index b740456..2afc024 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -189,6 +189,13 @@ xfs_free_extent(
enum xfs_ag_resv_type type); /* block reservation type */
int /* error */
+xfs_alloc_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat); /* success/failure */
+
+int /* error */
xfs_alloc_lookup_ge(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_agblock_t bno, /* starting block of extent */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 612d3c3..66365e2 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -1096,9 +1096,14 @@ xfs_scrub_allocbt_helper(
{
struct xfs_mount *mp = bs->cur->bc_mp;
struct xfs_agf *agf;
+ struct xfs_btree_cur *other_cur;
+ xfs_agblock_t fbno;
xfs_agblock_t bno;
+ xfs_extlen_t flen;
xfs_extlen_t len;
+ int has_otherrec;
int error = 0;
+ int err2;
bno = be32_to_cpu(rec->alloc.ar_startblock);
len = be32_to_cpu(rec->alloc.ar_blockcount);
@@ -1112,6 +1117,25 @@ xfs_scrub_allocbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
be32_to_cpu(agf->agf_length));
+ /*
+ * Ensure there's a corresponding cntbt/bnobt record matching
+ * this bnobt/cntbt record, respectively.
+ */
+ other_cur = (bs->cnt_cur ? bs->cnt_cur : bs->bno_cur);
+ err2 = xfs_alloc_lookup_le(other_cur, bno, len, &has_otherrec);
+ if (err2)
+ goto skip_freesp_xref;
+ XFS_BTREC_SCRUB_CHECK(bs, has_otherrec);
+ if (!has_otherrec)
+ goto skip_freesp_xref;
+ err2 = xfs_alloc_get_rec(other_cur, &fbno, &flen, &has_otherrec);
+ if (!err2) {
+ XFS_BTREC_SCRUB_CHECK(bs, has_otherrec);
+ XFS_BTREC_SCRUB_CHECK(bs, fbno == bno);
+ XFS_BTREC_SCRUB_CHECK(bs, flen == len);
+ }
+skip_freesp_xref:
+
return error;
}
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 20/25] xfs: cross-reference extents with AG header
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (18 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 19/25] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 21/25] xfs: cross-reference inode btrees during scrub Darrick J. Wong
` (4 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Ensure that none of the AG btree records overlap the AG sb/agf/agfl/agi
headers except for the XFS_RMAP_OWN_FS rmap.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/xfs_scrub.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 66365e2..63a7434 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -434,6 +434,30 @@ xfs_scrub_put_ag_headers(
*agi_bpp = *agf_bpp = NULL;
}
+/* Does this AG extent cover the AG headers? */
+STATIC bool
+xfs_scrub_extent_covers_ag_head(
+ struct xfs_mount *mp,
+ xfs_agblock_t agbno,
+ xfs_extlen_t len)
+{
+ xfs_agblock_t bno;
+
+ bno = XFS_SB_BLOCK(mp);
+ if (bno >= agbno && bno < agbno + len)
+ return true;
+ bno = XFS_AGF_BLOCK(mp);
+ if (bno >= agbno && bno < agbno + len)
+ return true;
+ bno = XFS_AGFL_BLOCK(mp);
+ if (bno >= agbno && bno < agbno + len)
+ return true;
+ bno = XFS_AGI_BLOCK(mp);
+ if (bno >= agbno && bno < agbno + len)
+ return true;
+ return false;
+}
+
/*
* For scrub, grab the AGI and the AGF headers, in that order.
* Locking order requires us to get the AGI before the AGF.
@@ -1013,6 +1037,10 @@ xfs_scrub_agfl(
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
agbno < eoag);
+ /* Cross-reference with the AG headers. */
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+ !xfs_scrub_extent_covers_ag_head(mp, agbno, 1));
+
/* Cross-reference with the bnobt. */
err2 = xfs_alloc_has_record(xcur, agbno, 1, &is_freesp);
if (!err2)
@@ -1117,6 +1145,10 @@ xfs_scrub_allocbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
be32_to_cpu(agf->agf_length));
+ /* Make sure we don't cover the AG headers. */
+ XFS_BTREC_SCRUB_CHECK(bs,
+ !xfs_scrub_extent_covers_ag_head(mp, bno, len));
+
/*
* Ensure there's a corresponding cntbt/bnobt record matching
* this bnobt/cntbt record, respectively.
@@ -1233,6 +1265,10 @@ xfs_scrub_iallocbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
eoag);
+ /* Make sure we don't cover the AG headers. */
+ XFS_BTREC_SCRUB_CHECK(bs,
+ !xfs_scrub_extent_covers_ag_head(mp, bno, len));
+
/* Cross-reference with the bnobt. */
err2 = xfs_alloc_has_record(bs->bno_cur, bno, len,
&is_freesp);
@@ -1267,6 +1303,10 @@ xfs_scrub_iallocbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, (unsigned long long)bno + len <=
eoag);
+ /* Make sure we don't cover the AG headers. */
+ XFS_BTREC_SCRUB_CHECK(bs,
+ !xfs_scrub_extent_covers_ag_head(mp, bno, len));
+
/* Cross-reference with the bnobt. */
err2 = xfs_alloc_has_record(bs->bno_cur, bno, len,
&is_freesp);
@@ -1378,6 +1418,11 @@ xfs_scrub_rmapbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, !non_inode || !(is_bmbt || is_unwritten ||
is_attr));
+ /* Make sure only the AG header owner maps to the AG header. */
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rm_owner == XFS_RMAP_OWN_FS ||
+ !xfs_scrub_extent_covers_ag_head(mp, irec.rm_startblock,
+ irec.rm_blockcount));
+
/* check there's no record in freesp btrees */
err2 = xfs_alloc_has_record(bs->bno_cur, irec.rm_startblock,
irec.rm_blockcount, &is_freesp);
@@ -1452,6 +1497,10 @@ xfs_scrub_refcountbt_helper(
irec.rc_blockcount <= eoag);
XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount >= 1);
+ /* Make sure we don't cover the AG headers. */
+ XFS_BTREC_SCRUB_CHECK(bs, !xfs_scrub_extent_covers_ag_head(mp,
+ irec.rc_startblock, irec.rc_blockcount));
+
/* Cross-reference with the bnobt. */
err2 = xfs_alloc_has_record(bs->bno_cur, irec.rc_startblock,
irec.rc_blockcount, &is_freesp);
@@ -1639,6 +1688,11 @@ xfs_scrub_bmap_extent(
if (error)
goto out;
+ /* Make sure we don't cover the AG headers. */
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type, info->is_rt ||
+ !xfs_scrub_extent_covers_ag_head(mp, bno,
+ irec->br_blockcount));
+
/* Cross-reference with the bnobt. */
xcur = xfs_allocbt_init_cursor(mp, NULL, agf_bp, agno,
XFS_BTNUM_BNO);
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 21/25] xfs: cross-reference inode btrees during scrub
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (19 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 20/25] xfs: cross-reference extents with AG header Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 22/25] xfs: cross-reference reverse-mapping btree Darrick J. Wong
` (3 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Cross-reference the inode btrees with the other metadata when we
scrub the filesystem.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_ialloc.c | 58 ++++++++++++
fs/xfs/libxfs/xfs_ialloc.h | 4 +
fs/xfs/xfs_scrub.c | 205 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 267 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index ab05f63..d6521fd 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2665,3 +2665,61 @@ xfs_ialloc_pagi_init(
xfs_trans_brelse(tp, bp);
return 0;
}
+
+/* Is there an inode record covering a given range of inode numbers? */
+int
+xfs_ialloc_has_inode_record(
+ struct xfs_btree_cur *cur,
+ xfs_agino_t low,
+ xfs_agino_t high,
+ bool *exists)
+{
+ struct xfs_inobt_rec_incore irec;
+ xfs_agino_t agino;
+ __uint16_t holemask;
+ int has;
+ int i;
+ int error;
+
+ *exists = false;
+ error = xfs_inobt_lookup(cur, low, XFS_LOOKUP_LE, &has);
+ while (error == 0 && has) {
+ error = xfs_inobt_get_rec(cur, &irec, &has);
+ if (error || irec.ir_startino > high)
+ break;
+
+ agino = irec.ir_startino;
+ holemask = irec.ir_holemask;
+ for (i = 0; i < XFS_INOBT_HOLEMASK_BITS; holemask >>= 1,
+ i++, agino += XFS_INODES_PER_HOLEMASK_BIT) {
+ if (holemask & 1)
+ continue;
+ if (agino + XFS_INODES_PER_HOLEMASK_BIT > low &&
+ agino <= high) {
+ *exists = true;
+ goto out;
+ }
+ }
+
+ error = xfs_btree_increment(cur, 0, &has);
+ }
+out:
+ return error;
+}
+
+/* Is there an inode record covering a given extent? */
+int
+xfs_ialloc_has_inodes_at_extent(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ xfs_agino_t low;
+ xfs_agino_t high;
+
+ low = XFS_OFFBNO_TO_AGINO(cur->bc_mp, bno, 0);
+ high = XFS_OFFBNO_TO_AGINO(cur->bc_mp, bno + len, 0) - 1;
+
+ return xfs_ialloc_has_inode_record(cur, low, high, exists);
+}
diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h
index 8e5861d..f20d958 100644
--- a/fs/xfs/libxfs/xfs_ialloc.h
+++ b/fs/xfs/libxfs/xfs_ialloc.h
@@ -171,5 +171,9 @@ int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp,
union xfs_btree_rec;
void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, union xfs_btree_rec *rec,
struct xfs_inobt_rec_incore *irec);
+int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
+ xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
+int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
+ xfs_agino_t high, bool *exists);
#endif /* __XFS_IALLOC_H__ */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 63a7434..cc85584 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -814,6 +814,7 @@ xfs_scrub_sb(
struct xfs_sb sb;
xfs_agnumber_t agno;
bool is_freesp;
+ bool has_inodes;
int error;
int err2;
@@ -888,6 +889,26 @@ btree_xref:
XFS_SCRUB_CHECK(mp, bp, "superblock", !is_freesp);
xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ /* Cross-reference with inobt. */
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno, XFS_BTNUM_INO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur, XFS_SB_BLOCK(mp), 1,
+ &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, bp, "superblock", !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+ /* Cross-reference with finobt. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno,
+ XFS_BTNUM_FINO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur, XFS_SB_BLOCK(mp),
+ 1, &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, bp, "superblock", !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
xfs_buf_relse(bp);
@@ -911,6 +932,7 @@ xfs_scrub_agf(
xfs_daddr_t daddr;
xfs_daddr_t eofs;
bool is_freesp;
+ bool has_inodes;
int error;
int err2;
@@ -976,6 +998,26 @@ xfs_scrub_agf(
XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !is_freesp);
xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ /* Cross-reference with inobt. */
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno, XFS_BTNUM_INO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur, XFS_AGF_BLOCK(mp), 1,
+ &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+ /* Cross-reference with finobt. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno,
+ XFS_BTNUM_FINO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur, XFS_AGF_BLOCK(mp),
+ 1, &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -993,11 +1035,14 @@ xfs_scrub_agfl(
struct xfs_buf *agfl_bp;
__be32 *agfl_bno;
struct xfs_btree_cur *xcur = NULL;
+ struct xfs_btree_cur *icur = NULL;
+ struct xfs_btree_cur *fcur = NULL;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t eofs;
bool is_freesp;
+ bool has_inodes;
int i;
int error;
int err2;
@@ -1025,6 +1070,23 @@ xfs_scrub_agfl(
if (!err2)
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !is_freesp);
+ /* Cross-reference with inobt. */
+ icur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno, XFS_BTNUM_INO);
+ err2 = xfs_ialloc_has_inodes_at_extent(icur, XFS_AGFL_BLOCK(mp), 1,
+ &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_inodes);
+
+ /* Cross-reference with finobt. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ fcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno,
+ XFS_BTNUM_FINO);
+ err2 = xfs_ialloc_has_inodes_at_extent(fcur, XFS_AGFL_BLOCK(mp),
+ 1, &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_inodes);
+ }
+
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
for (i = be32_to_cpu(agf->agf_flfirst);
i <= be32_to_cpu(agf->agf_fllast);
@@ -1045,8 +1107,26 @@ xfs_scrub_agfl(
err2 = xfs_alloc_has_record(xcur, agbno, 1, &is_freesp);
if (!err2)
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !is_freesp);
+
+ /* Cross-reference with inobt. */
+ err2 = xfs_ialloc_has_inodes_at_extent(icur, agbno, 1,
+ &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_inodes);
+
+ /* Cross-reference with finobt. */
+ if (fcur) {
+ err2 = xfs_ialloc_has_inodes_at_extent(fcur, agbno, 1,
+ &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+ !has_inodes);
+ }
}
+ if (fcur)
+ xfs_btree_del_cursor(fcur, XFS_BTREE_ERROR);
+ xfs_btree_del_cursor(icur, XFS_BTREE_ERROR);
xfs_btree_del_cursor(xcur, XFS_BTREE_ERROR);
xfs_buf_relse(agfl_bp);
err_no_agfl:
@@ -1072,6 +1152,7 @@ xfs_scrub_agi(
xfs_daddr_t daddr;
xfs_daddr_t eofs;
bool is_freesp;
+ bool has_inodes;
int error;
int err2;
@@ -1110,6 +1191,26 @@ xfs_scrub_agi(
XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !is_freesp);
xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ /* Cross-reference with inobt. */
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno, XFS_BTNUM_INO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur, XFS_AGI_BLOCK(mp), 1,
+ &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+ /* Cross-reference with finobt. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno,
+ XFS_BTNUM_FINO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur, XFS_AGI_BLOCK(mp),
+ 1, &has_inodes);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1129,6 +1230,7 @@ xfs_scrub_allocbt_helper(
xfs_agblock_t bno;
xfs_extlen_t flen;
xfs_extlen_t len;
+ bool has_inodes;
int has_otherrec;
int error = 0;
int err2;
@@ -1168,6 +1270,20 @@ xfs_scrub_allocbt_helper(
}
skip_freesp_xref:
+ /* Cross-reference with inobt. */
+ err2 = xfs_ialloc_has_inodes_at_extent(bs->ino_cur, bno, len,
+ &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_inodes);
+
+ /* Cross-reference with finobt. */
+ if (bs->fino_cur) {
+ err2 = xfs_ialloc_has_inodes_at_extent(bs->fino_cur, bno, len,
+ &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_inodes);
+ }
+
return error;
}
@@ -1230,6 +1346,7 @@ xfs_scrub_iallocbt_helper(
{
struct xfs_mount *mp = bs->cur->bc_mp;
struct xfs_agf *agf;
+ struct xfs_btree_cur *other_cur;
struct xfs_inobt_rec_incore irec;
__uint16_t holemask;
xfs_agino_t agino;
@@ -1237,6 +1354,7 @@ xfs_scrub_iallocbt_helper(
xfs_agblock_t eoag;
xfs_extlen_t len;
bool is_freesp;
+ bool has_inodes;
int holecount;
int i;
int error = 0;
@@ -1275,6 +1393,18 @@ xfs_scrub_iallocbt_helper(
if (!err2)
XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+ /* If we have a finobt, cross-reference with it. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ other_cur = bs->fino_cur ? bs->fino_cur : bs->ino_cur;
+ if (bs->cur->bc_btnum == XFS_BTNUM_FINO ||
+ irec.ir_freecount) {
+ err2 = xfs_ialloc_has_inode_record(other_cur,
+ agino, agino, &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, has_inodes);
+ }
+ }
+
goto out;
}
@@ -1312,6 +1442,18 @@ xfs_scrub_iallocbt_helper(
&is_freesp);
if (!err2)
XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+
+ /* If we have a finobt, cross-reference with it. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ other_cur = bs->fino_cur ? bs->fino_cur : bs->ino_cur;
+ if (bs->cur->bc_btnum == XFS_BTNUM_FINO ||
+ irec.ir_freecount) {
+ err2 = xfs_ialloc_has_inode_record(other_cur,
+ agino, agino, &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, has_inodes);
+ }
+ }
}
XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
@@ -1388,6 +1530,7 @@ xfs_scrub_rmapbt_helper(
bool is_unwritten;
bool is_bmbt;
bool is_attr;
+ bool has_inodes;
int error = 0;
int err2;
@@ -1429,6 +1572,25 @@ xfs_scrub_rmapbt_helper(
if (!err2)
XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+ /* Cross-reference with inobt. */
+ err2 = xfs_ialloc_has_inodes_at_extent(bs->ino_cur, irec.rm_startblock,
+ irec.rm_blockcount, &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs,
+ irec.rm_owner == XFS_RMAP_OWN_INODES ||
+ !has_inodes);
+
+ /* Cross-reference with finobt. */
+ if (bs->fino_cur) {
+ err2 = xfs_ialloc_has_inodes_at_extent(bs->fino_cur,
+ irec.rm_startblock, irec.rm_blockcount,
+ &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs,
+ irec.rm_owner == XFS_RMAP_OWN_INODES ||
+ !has_inodes);
+ }
+
return error;
}
@@ -1478,6 +1640,7 @@ xfs_scrub_refcountbt_helper(
struct xfs_refcount_irec irec;
xfs_agblock_t eoag;
bool is_freesp;
+ bool has_inodes;
int error = 0;
int err2;
@@ -1507,6 +1670,21 @@ xfs_scrub_refcountbt_helper(
if (!err2)
XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+ /* Cross-reference with inobt. */
+ err2 = xfs_ialloc_has_inodes_at_extent(bs->ino_cur, irec.rc_startblock,
+ irec.rc_blockcount, &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_inodes);
+
+ /* Cross-reference with finobt. */
+ if (bs->fino_cur) {
+ err2 = xfs_ialloc_has_inodes_at_extent(bs->fino_cur,
+ irec.rc_startblock, irec.rc_blockcount,
+ &has_inodes);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_inodes);
+ }
+
return error;
}
@@ -1645,6 +1823,7 @@ xfs_scrub_bmap_extent(
xfs_agnumber_t agno;
xfs_fsblock_t bno;
bool is_freesp;
+ bool has_inodes;
int error = 0;
int err2 = 0;
@@ -1702,6 +1881,32 @@ xfs_scrub_bmap_extent(
XFS_BTREC_SCRUB_CHECK(&info->bs, !is_freesp);
xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
XFS_BTREE_NOERROR);
+
+ /* Cross-reference with inobt. */
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno,
+ XFS_BTNUM_INO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur,
+ irec->br_startblock, irec->br_blockcount,
+ &has_inodes);
+ if (!err2)
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type, !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+
+ /* Cross-reference with finobt. */
+ if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+ xcur = xfs_inobt_init_cursor(mp, NULL, agi_bp, agno,
+ XFS_BTNUM_FINO);
+ err2 = xfs_ialloc_has_inodes_at_extent(xcur,
+ irec->br_startblock,
+ irec->br_blockcount,
+ &has_inodes);
+ if (!err2)
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+ !has_inodes);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
}
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 22/25] xfs: cross-reference reverse-mapping btree
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (20 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 21/25] xfs: cross-reference inode btrees during scrub Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 23/25] xfs: cross-reference refcount btree during scrub Darrick J. Wong
` (2 subsequent siblings)
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
When scrubbing various btrees, we should cross-reference the records
with the reverse mapping btree.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_rmap.c | 58 ++++++
fs/xfs/libxfs/xfs_rmap.h | 5 +
fs/xfs/xfs_scrub.c | 441 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 503 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c
index b0308fc..b22f93d 100644
--- a/fs/xfs/libxfs/xfs_rmap.c
+++ b/fs/xfs/libxfs/xfs_rmap.c
@@ -2292,3 +2292,61 @@ xfs_rmap_free_extent(
return __xfs_rmap_add(mp, dfops, XFS_RMAP_FREE, owner,
XFS_DATA_FORK, &bmap);
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_rmap_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.r.rm_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.r.rm_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
+
+/* Is there a record covering a given extent? */
+int
+xfs_rmap_record_exists(
+ struct xfs_btree_cur *cur,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ struct xfs_owner_info *oinfo,
+ bool *has_rmap)
+{
+ uint64_t owner;
+ uint64_t offset;
+ unsigned int flags;
+ int stat;
+ struct xfs_rmap_irec irec;
+ int error;
+
+ xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+
+ error = xfs_rmap_lookup_le(cur, bno, len, owner, offset, flags, &stat);
+ if (error)
+ return error;
+ if (!stat) {
+ *has_rmap = false;
+ return 0;
+ }
+
+ error = xfs_rmap_get_rec(cur, &irec, &stat);
+ if (error)
+ return error;
+ if (!stat) {
+ *has_rmap = false;
+ return 0;
+ }
+
+ *has_rmap = (irec.rm_startblock <= bno &&
+ irec.rm_startblock + irec.rm_blockcount >= bno + len);
+ return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_rmap.h b/fs/xfs/libxfs/xfs_rmap.h
index 188db38..c5c5817 100644
--- a/fs/xfs/libxfs/xfs_rmap.h
+++ b/fs/xfs/libxfs/xfs_rmap.h
@@ -215,5 +215,10 @@ int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
union xfs_btree_rec;
int xfs_rmap_btrec_to_irec(union xfs_btree_rec *rec,
struct xfs_rmap_irec *irec);
+int xfs_rmap_has_record(struct xfs_btree_cur *cur, xfs_fsblock_t bno,
+ xfs_filblks_t len, bool *exists);
+int xfs_rmap_record_exists(struct xfs_btree_cur *cur, xfs_fsblock_t bno,
+ xfs_filblks_t len, struct xfs_owner_info *oinfo,
+ bool *has_rmap);
#endif /* __XFS_RMAP_H__ */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index cc85584..34c23f7 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -393,6 +393,11 @@ xfs_scrub_btree_key(
return 0;
}
+struct check_owner {
+ struct list_head list;
+ xfs_fsblock_t bno;
+};
+
/*
* For scrub, grab the AGI and the AGF headers, in that order.
* Locking order requires us to get the AGI before the AGF.
@@ -550,8 +555,10 @@ xfs_scrub_btree_check_block_owner(
xfs_agnumber_t agno;
xfs_agblock_t bno;
bool is_freesp;
+ bool has_rmap;
struct xfs_buf *agf_bp = NULL;
struct xfs_btree_cur *bcur = NULL;
+ struct xfs_btree_cur *rcur = NULL;
int error = 0;
int err2;
@@ -565,8 +572,12 @@ xfs_scrub_btree_check_block_owner(
return error;
bcur = xfs_allocbt_init_cursor(bs->cur->bc_mp, NULL,
agf_bp, agno, XFS_BTNUM_BNO);
+ if (xfs_sb_version_hasrmapbt(&bs->cur->bc_mp->m_sb))
+ rcur = xfs_rmapbt_init_cursor(bs->cur->bc_mp, NULL,
+ agf_bp, agno);
} else {
bcur = bs->bno_cur;
+ rcur = bs->rmap_cur;
}
/* Check that this block isn't free. */
@@ -574,7 +585,17 @@ xfs_scrub_btree_check_block_owner(
if (!err2)
XFS_BTREC_SCRUB_CHECK(bs, !is_freesp);
+ /* Check that this block is in the rmap. */
+ if (rcur) {
+ err2 = xfs_rmap_record_exists(rcur, bno, 1, &bs->oinfo,
+ &has_rmap);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
+ }
+
if (agf_bp) {
+ if (rcur)
+ xfs_btree_del_cursor(rcur, XFS_BTREE_ERROR);
xfs_btree_del_cursor(bcur, XFS_BTREE_ERROR);
xfs_buf_relse(agf_bp);
}
@@ -589,6 +610,7 @@ xfs_scrub_btree_check_owner(
struct xfs_buf *bp)
{
struct xfs_btree_cur *cur = bs->cur;
+ struct check_owner *co;
xfs_fsblock_t fsbno;
if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && bp == NULL)
@@ -596,6 +618,15 @@ xfs_scrub_btree_check_owner(
fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn);
+ /* Do we need to defer this one? */
+ if ((!bs->rmap_cur && xfs_sb_version_hasrmapbt(&cur->bc_mp->m_sb)) ||
+ !bs->bno_cur) {
+ co = kmem_alloc(sizeof(struct check_owner), KM_SLEEP | KM_NOFS);
+ co->bno = fsbno;
+ list_add_tail(&co->list, &bs->to_check);
+ return 0;
+ }
+
return xfs_scrub_btree_check_block_owner(bs, fsbno);
}
@@ -617,6 +648,8 @@ xfs_scrub_btree(
struct xfs_btree_block *block;
int level;
struct xfs_buf *bp;
+ struct check_owner *co;
+ struct check_owner *n;
int i;
int error = 0;
@@ -778,6 +811,23 @@ out:
}
}
+ /* Process deferred rmap owner checks on btree blocks. */
+ if (!error) {
+ if (bs->cur->bc_btnum == XFS_BTNUM_BNO)
+ bs->bno_cur = bs->cur;
+ else if (bs->cur->bc_btnum == XFS_BTNUM_RMAP)
+ bs->rmap_cur = bs->cur;
+ list_for_each_entry(co, &bs->to_check, list) {
+ error = xfs_scrub_btree_check_block_owner(bs, co->bno);
+ if (error)
+ break;
+ }
+ }
+ list_for_each_entry_safe(co, n, &bs->to_check, list) {
+ list_del(&co->list);
+ kmem_free(co);
+ }
+
if (bs->refc_cur)
xfs_btree_del_cursor(bs->refc_cur, XFS_BTREE_ERROR);
if (bs->rmap_cur && bs->rmap_cur != bs->cur)
@@ -812,9 +862,11 @@ xfs_scrub_sb(
struct xfs_buf *agf_bp = NULL;
struct xfs_btree_cur *xcur = NULL;
struct xfs_sb sb;
+ struct xfs_owner_info oinfo;
xfs_agnumber_t agno;
bool is_freesp;
bool has_inodes;
+ bool has_rmap;
int error;
int err2;
@@ -909,6 +961,18 @@ btree_xref:
XFS_BTREE_NOERROR);
}
+ /* Cross-reference with the rmapbt. */
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xcur = xfs_rmapbt_init_cursor(mp, NULL, agf_bp, agno);
+ err2 = xfs_rmap_record_exists(xcur, XFS_SB_BLOCK(mp), 1,
+ &oinfo, &has_rmap);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, bp, "superblock", has_rmap);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
xfs_buf_relse(bp);
@@ -926,13 +990,16 @@ xfs_scrub_agf(
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
struct xfs_btree_cur *xcur = NULL;
+ struct xfs_owner_info oinfo;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t daddr;
xfs_daddr_t eofs;
+ xfs_extlen_t blocks;
bool is_freesp;
bool has_inodes;
+ bool has_rmap;
int error;
int err2;
@@ -1018,6 +1085,24 @@ xfs_scrub_agf(
XFS_BTREE_NOERROR);
}
+ /* Cross-reference with the rmapbt. */
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xcur = xfs_rmapbt_init_cursor(mp, NULL, agf_bp, agno);
+ err2 = xfs_rmap_record_exists(xcur, XFS_AGF_BLOCK(mp), 1,
+ &oinfo, &has_rmap);
+ if (err2)
+ goto skip_rmap_xref;
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", has_rmap);
+ err2 = xfs_btree_count_blocks(xcur, &blocks);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", blocks ==
+ be32_to_cpu(agf->agf_rmap_blocks));
+skip_rmap_xref:
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1037,12 +1122,15 @@ xfs_scrub_agfl(
struct xfs_btree_cur *xcur = NULL;
struct xfs_btree_cur *icur = NULL;
struct xfs_btree_cur *fcur = NULL;
+ struct xfs_btree_cur *rcur = NULL;
+ struct xfs_owner_info oinfo;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
xfs_daddr_t eofs;
bool is_freesp;
bool has_inodes;
+ bool has_rmap;
int i;
int error;
int err2;
@@ -1087,6 +1175,17 @@ xfs_scrub_agfl(
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_inodes);
}
+ /* Set up cross-reference with rmapbt. */
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+ rcur = xfs_rmapbt_init_cursor(mp, NULL, agf_bp, agno);
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ err2 = xfs_rmap_record_exists(rcur, XFS_AGFL_BLOCK(mp), 1,
+ &oinfo, &has_rmap);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap);
+ }
+
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG);
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
for (i = be32_to_cpu(agf->agf_flfirst);
i <= be32_to_cpu(agf->agf_fllast);
@@ -1122,8 +1221,18 @@ xfs_scrub_agfl(
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
!has_inodes);
}
+
+ /* Cross-reference with the rmapbt. */
+ if (rcur) {
+ err2 = xfs_rmap_record_exists(rcur, agbno, 1, &oinfo,
+ &has_rmap);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap);
+ }
}
+ if (rcur)
+ xfs_btree_del_cursor(rcur, XFS_BTREE_ERROR);
if (fcur)
xfs_btree_del_cursor(fcur, XFS_BTREE_ERROR);
xfs_btree_del_cursor(icur, XFS_BTREE_ERROR);
@@ -1146,6 +1255,7 @@ xfs_scrub_agi(
struct xfs_buf *agi_bp = NULL;
struct xfs_buf *agf_bp = NULL;
struct xfs_btree_cur *xcur = NULL;
+ struct xfs_owner_info oinfo;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
xfs_agblock_t eoag;
@@ -1153,6 +1263,7 @@ xfs_scrub_agi(
xfs_daddr_t eofs;
bool is_freesp;
bool has_inodes;
+ bool has_rmap;
int error;
int err2;
@@ -1211,6 +1322,18 @@ xfs_scrub_agi(
XFS_BTREE_NOERROR);
}
+ /* Cross-reference with the rmapbt. */
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
+ xcur = xfs_rmapbt_init_cursor(mp, NULL, agf_bp, agno);
+ err2 = xfs_rmap_record_exists(xcur, XFS_AGI_BLOCK(mp), 1,
+ &oinfo, &has_rmap);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", has_rmap);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1230,6 +1353,7 @@ xfs_scrub_allocbt_helper(
xfs_agblock_t bno;
xfs_extlen_t flen;
xfs_extlen_t len;
+ bool has_rmap;
bool has_inodes;
int has_otherrec;
int error = 0;
@@ -1284,6 +1408,13 @@ skip_freesp_xref:
XFS_BTREC_SCRUB_CHECK(bs, !has_inodes);
}
+ /* Cross-reference with the rmapbt. */
+ if (bs->rmap_cur) {
+ err2 = xfs_rmap_has_record(bs->rmap_cur, bno, len, &has_rmap);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_rmap);
+ }
+
return error;
}
@@ -1348,6 +1479,7 @@ xfs_scrub_iallocbt_helper(
struct xfs_agf *agf;
struct xfs_btree_cur *other_cur;
struct xfs_inobt_rec_incore irec;
+ struct xfs_owner_info oinfo;
__uint16_t holemask;
xfs_agino_t agino;
xfs_agblock_t bno;
@@ -1355,10 +1487,11 @@ xfs_scrub_iallocbt_helper(
xfs_extlen_t len;
bool is_freesp;
bool has_inodes;
+ bool has_rmap;
int holecount;
int i;
int error = 0;
- int err2;
+ int err2 = 0;
uint64_t holes;
xfs_inobt_btrec_to_irec(mp, rec, &irec);
@@ -1368,6 +1501,7 @@ xfs_scrub_iallocbt_helper(
agino = irec.ir_startino;
agf = XFS_BUF_TO_AGF(bs->agf_bp);
eoag = be32_to_cpu(agf->agf_length);
+ xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_INODES);
/* Handle non-sparse inodes */
if (!xfs_inobt_issparse(irec.ir_holemask)) {
@@ -1405,6 +1539,14 @@ xfs_scrub_iallocbt_helper(
}
}
+ /* Cross-reference with rmapbt. */
+ if (bs->rmap_cur) {
+ err2 = xfs_rmap_record_exists(bs->rmap_cur, bno, len,
+ &oinfo, &has_rmap);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
+ }
+
goto out;
}
@@ -1454,6 +1596,14 @@ xfs_scrub_iallocbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, has_inodes);
}
}
+
+ /* Cross-reference with the rmapbt. */
+ if (bs->rmap_cur) {
+ err2 = xfs_rmap_record_exists(bs->rmap_cur, bno, len,
+ &oinfo, &has_rmap);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
+ }
}
XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
@@ -1629,6 +1779,165 @@ xfs_scrub_rmapbt(
/* Reference count btree scrubber. */
+struct xfs_refcountbt_scrub_fragment {
+ struct xfs_rmap_irec rm;
+ struct list_head list;
+};
+
+struct xfs_refcountbt_scrub_rmap_check_info {
+ struct xfs_scrub_btree *bs;
+ xfs_nlink_t nr;
+ struct xfs_refcount_irec rc;
+ struct list_head fragments;
+};
+
+/*
+ * Decide if the given rmap is large enough that we can redeem it
+ * towards refcount verification now, or if it's a fragment, in
+ * which case we'll hang onto it in the hopes that we'll later
+ * discover that we've collected exactly the correct number of
+ * fragments as the refcountbt says we should have.
+ */
+STATIC int
+xfs_refcountbt_scrub_rmap_check(
+ struct xfs_btree_cur *cur,
+ struct xfs_rmap_irec *rec,
+ void *priv)
+{
+ struct xfs_refcountbt_scrub_rmap_check_info *rsrci = priv;
+ struct xfs_refcountbt_scrub_fragment *frag;
+ xfs_agblock_t rm_last;
+ xfs_agblock_t rc_last;
+
+ rm_last = rec->rm_startblock + rec->rm_blockcount;
+ rc_last = rsrci->rc.rc_startblock + rsrci->rc.rc_blockcount;
+ XFS_BTREC_SCRUB_CHECK(rsrci->bs, rsrci->rc.rc_refcount != 1 ||
+ rec->rm_owner == XFS_RMAP_OWN_COW);
+ if (rec->rm_startblock <= rsrci->rc.rc_startblock && rm_last >= rc_last)
+ rsrci->nr++;
+ else {
+ frag = kmem_zalloc(sizeof(struct xfs_refcountbt_scrub_fragment),
+ KM_SLEEP);
+ frag->rm = *rec;
+ list_add_tail(&frag->list, &rsrci->fragments);
+ }
+
+ return 0;
+}
+
+/*
+ * Given a bunch of rmap fragments, iterate through them, keeping
+ * a running tally of the refcount. If this ever deviates from
+ * what we expect (which is the refcountbt's refcount minus the
+ * number of extents that totally covered the refcountbt extent),
+ * we have a refcountbt error.
+ */
+STATIC void
+xfs_refcountbt_process_rmap_fragments(
+ struct xfs_mount *mp,
+ struct xfs_refcountbt_scrub_rmap_check_info *rsrci)
+{
+ struct list_head worklist;
+ struct xfs_refcountbt_scrub_fragment *cur;
+ struct xfs_refcountbt_scrub_fragment *n;
+ xfs_agblock_t bno;
+ xfs_agblock_t rbno;
+ xfs_agblock_t next_rbno;
+ xfs_nlink_t nr;
+ xfs_nlink_t target_nr;
+
+ target_nr = rsrci->rc.rc_refcount - rsrci->nr;
+ if (target_nr == 0)
+ return;
+
+ /*
+ * There are (rsrci->rc.rc_refcount - rsrci->nr refcount)
+ * references we haven't found yet. Pull that many off the
+ * fragment list and figure out where the smallest rmap ends
+ * (and therefore the next rmap should start). All the rmaps
+ * we pull off should start at or before the beginning of the
+ * refcount record's range.
+ */
+ INIT_LIST_HEAD(&worklist);
+ rbno = NULLAGBLOCK;
+ nr = 1;
+ list_for_each_entry_safe(cur, n, &rsrci->fragments, list) {
+ if (cur->rm.rm_startblock > rsrci->rc.rc_startblock)
+ goto fail;
+ bno = cur->rm.rm_startblock + cur->rm.rm_blockcount;
+ if (rbno > bno)
+ rbno = bno;
+ list_del(&cur->list);
+ list_add_tail(&cur->list, &worklist);
+ if (nr == target_nr)
+ break;
+ nr++;
+ }
+
+ if (nr != target_nr)
+ goto fail;
+
+ while (!list_empty(&rsrci->fragments)) {
+ /* Discard any fragments ending at rbno. */
+ nr = 0;
+ next_rbno = NULLAGBLOCK;
+ list_for_each_entry_safe(cur, n, &worklist, list) {
+ bno = cur->rm.rm_startblock + cur->rm.rm_blockcount;
+ if (bno != rbno) {
+ if (next_rbno > bno)
+ next_rbno = bno;
+ continue;
+ }
+ list_del(&cur->list);
+ kmem_free(cur);
+ nr++;
+ }
+
+ /* Empty list? We're done. */
+ if (list_empty(&rsrci->fragments))
+ break;
+
+ /* Try to add nr rmaps starting at rbno to the worklist. */
+ list_for_each_entry_safe(cur, n, &rsrci->fragments, list) {
+ bno = cur->rm.rm_startblock + cur->rm.rm_blockcount;
+ if (cur->rm.rm_startblock != rbno)
+ goto fail;
+ list_del(&cur->list);
+ list_add_tail(&cur->list, &worklist);
+ if (next_rbno > bno)
+ next_rbno = bno;
+ nr--;
+ if (nr == 0)
+ break;
+ }
+
+ rbno = next_rbno;
+ }
+
+ /*
+ * Make sure the last extent we processed ends at or beyond
+ * the end of the refcount extent.
+ */
+ if (rbno < rsrci->rc.rc_startblock + rsrci->rc.rc_blockcount)
+ goto fail;
+
+ rsrci->nr = rsrci->rc.rc_refcount;
+fail:
+ /* Delete fragments and work list. */
+ while (!list_empty(&worklist)) {
+ cur = list_first_entry(&worklist,
+ struct xfs_refcountbt_scrub_fragment, list);
+ list_del(&cur->list);
+ kmem_free(cur);
+ }
+ while (!list_empty(&rsrci->fragments)) {
+ cur = list_first_entry(&rsrci->fragments,
+ struct xfs_refcountbt_scrub_fragment, list);
+ list_del(&cur->list);
+ kmem_free(cur);
+ }
+}
+
/* Scrub a refcountbt record. */
STATIC int
xfs_scrub_refcountbt_helper(
@@ -1638,6 +1947,10 @@ xfs_scrub_refcountbt_helper(
struct xfs_mount *mp = bs->cur->bc_mp;
struct xfs_agf *agf;
struct xfs_refcount_irec irec;
+ struct xfs_rmap_irec low;
+ struct xfs_rmap_irec high;
+ struct xfs_refcountbt_scrub_rmap_check_info rsrci;
+ struct xfs_refcountbt_scrub_fragment *cur;
xfs_agblock_t eoag;
bool is_freesp;
bool has_inodes;
@@ -1685,6 +1998,34 @@ xfs_scrub_refcountbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, !has_inodes);
}
+ /* Cross-reference with the rmapbt to confirm the refcount. */
+ if (bs->rmap_cur) {
+ memset(&low, 0, sizeof(low));
+ low.rm_startblock = irec.rc_startblock;
+ memset(&high, 0xFF, sizeof(high));
+ high.rm_startblock = irec.rc_startblock +
+ irec.rc_blockcount - 1;
+
+ rsrci.bs = bs;
+ rsrci.nr = 0;
+ rsrci.rc = irec;
+ INIT_LIST_HEAD(&rsrci.fragments);
+ err2 = xfs_rmap_query_range(bs->rmap_cur, &low, &high,
+ &xfs_refcountbt_scrub_rmap_check, &rsrci);
+ if (err2 == 0) {
+ xfs_refcountbt_process_rmap_fragments(mp, &rsrci);
+ XFS_BTREC_SCRUB_CHECK(bs, irec.rc_refcount == rsrci.nr);
+ }
+
+ while (!list_empty(&rsrci.fragments)) {
+ cur = list_first_entry(&rsrci.fragments,
+ struct xfs_refcountbt_scrub_fragment,
+ list);
+ list_del(&cur->list);
+ kmem_free(cur);
+ }
+ }
+
return error;
}
@@ -1822,8 +2163,13 @@ xfs_scrub_bmap_extent(
xfs_daddr_t dlen;
xfs_agnumber_t agno;
xfs_fsblock_t bno;
+ struct xfs_rmap_irec rmap;
+ uint64_t owner;
+ xfs_fileoff_t offset;
bool is_freesp;
bool has_inodes;
+ unsigned int rflags;
+ int has_rmap;
int error = 0;
int err2 = 0;
@@ -1909,6 +2255,99 @@ xfs_scrub_bmap_extent(
}
}
+ /* Cross-reference with rmapbt. */
+ if (xfs_sb_version_hasrmapbt(&mp->m_sb) && !info->is_rt) {
+ xcur = xfs_rmapbt_init_cursor(mp, NULL, agf_bp, agno);
+
+ if (info->whichfork == XFS_COW_FORK) {
+ owner = XFS_RMAP_OWN_COW;
+ offset = 0;
+ } else {
+ owner = ip->i_ino;
+ offset = irec->br_startoff;
+ }
+
+ /* Look for a corresponding rmap. */
+ rflags = 0;
+ if (info->whichfork == XFS_ATTR_FORK)
+ rflags |= XFS_RMAP_ATTR_FORK;
+
+ if (info->is_shared) {
+ err2 = xfs_rmap_lookup_le_range(xcur, bno, owner,
+ offset, rflags, &rmap,
+ &has_rmap);
+ if (err2)
+ goto skip_rmap_xref;
+ XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_rmap,
+ skip_rmap_xref);
+ } else {
+ err2 = xfs_rmap_lookup_le(xcur, bno, 0, owner,
+ offset, rflags, &has_rmap);
+ if (err2)
+ goto skip_rmap_xref;
+ XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_rmap,
+ skip_rmap_xref);
+
+ err2 = xfs_rmap_get_rec(xcur, &rmap, &has_rmap);
+ if (err2)
+ goto skip_rmap_xref;
+ XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_rmap,
+ skip_rmap_xref);
+ }
+
+ /* Check the rmap. */
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_startblock <= bno);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_startblock + rmap.rm_blockcount >
+ rmap.rm_startblock);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_startblock + rmap.rm_blockcount >=
+ bno + irec->br_blockcount);
+ if (owner != XFS_RMAP_OWN_COW) {
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_offset <= offset);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_offset + rmap.rm_blockcount >
+ rmap.rm_offset);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_offset + rmap.rm_blockcount >=
+ offset + irec->br_blockcount);
+ }
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_owner == owner);
+ switch (irec->br_state) {
+ case XFS_EXT_UNWRITTEN:
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_flags & XFS_RMAP_UNWRITTEN);
+ break;
+ case XFS_EXT_NORM:
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ !(rmap.rm_flags & XFS_RMAP_UNWRITTEN));
+ break;
+ default:
+ break;
+ }
+ switch (info->whichfork) {
+ case XFS_ATTR_FORK:
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ rmap.rm_flags & XFS_RMAP_ATTR_FORK);
+ break;
+ case XFS_DATA_FORK:
+ case XFS_COW_FORK:
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ !(rmap.rm_flags & XFS_RMAP_ATTR_FORK));
+ break;
+ }
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ !(rmap.rm_flags & XFS_RMAP_BMBT_BLOCK));
+
+skip_rmap_xref:
+ /* Free cursor. */
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
info->lastoff = irec->br_startoff + irec->br_blockcount;
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 23/25] xfs: cross-reference refcount btree during scrub
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (21 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 22/25] xfs: cross-reference reverse-mapping btree Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 24/25] xfs: scrub should cross-reference the realtime bitmap Darrick J. Wong
2016-08-25 23:42 ` [PATCH 25/25] xfs: query the per-AG reservation counters Darrick J. Wong
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
During metadata btree scrub, we should cross-reference with the
reference counts.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_refcount.c | 19 ++++
fs/xfs/libxfs/xfs_refcount.h | 3 +
fs/xfs/xfs_scrub.c | 184 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 206 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 9136745..af82ea3 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -1535,3 +1535,22 @@ xfs_refcount_free_cow_extent(
return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,
fsb, len);
}
+
+/* Is there a record covering a given extent? */
+int
+xfs_refcount_has_record(
+ struct xfs_btree_cur *cur,
+ xfs_agblock_t bno,
+ xfs_extlen_t len,
+ bool *exists)
+{
+ union xfs_btree_irec low;
+ union xfs_btree_irec high;
+
+ memset(&low, 0, sizeof(low));
+ low.rc.rc_startblock = bno;
+ memset(&high, 0xFF, sizeof(high));
+ high.rc.rc_startblock = bno + len - 1;
+
+ return xfs_btree_has_record(cur, &low, &high, exists);
+}
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
index 105c246..a00400f 100644
--- a/fs/xfs/libxfs/xfs_refcount.h
+++ b/fs/xfs/libxfs/xfs_refcount.h
@@ -64,4 +64,7 @@ extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp,
struct xfs_defer_ops *dfops, xfs_fsblock_t fsb,
xfs_extlen_t len);
+extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
+ xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
+
#endif /* __XFS_REFCOUNT_H__ */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index 34c23f7..ff55d8c 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -867,6 +867,7 @@ xfs_scrub_sb(
bool is_freesp;
bool has_inodes;
bool has_rmap;
+ bool has_refcount;
int error;
int err2;
@@ -973,6 +974,17 @@ btree_xref:
XFS_BTREE_NOERROR);
}
+ /* Cross-reference with the refcountbt. */
+ if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+ xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+ err2 = xfs_refcount_has_record(xcur, XFS_SB_BLOCK(mp), 1,
+ &has_refcount);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, bp, "superblock", !has_refcount);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
xfs_buf_relse(bp);
@@ -1000,6 +1012,7 @@ xfs_scrub_agf(
bool is_freesp;
bool has_inodes;
bool has_rmap;
+ bool has_refcount;
int error;
int err2;
@@ -1103,6 +1116,23 @@ skip_rmap_xref:
XFS_BTREE_NOERROR);
}
+ /* Cross-reference with the refcountbt. */
+ if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+ xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+ err2 = xfs_refcount_has_record(xcur, XFS_AGF_BLOCK(mp), 1,
+ &has_refcount);
+ if (err2)
+ goto skip_refc_xref;
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", !has_refcount);
+ err2 = xfs_btree_count_blocks(xcur, &blocks);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agf_bp, "AGF", blocks ==
+ be32_to_cpu(agf->agf_refcount_blocks));
+skip_refc_xref:
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1123,6 +1153,7 @@ xfs_scrub_agfl(
struct xfs_btree_cur *icur = NULL;
struct xfs_btree_cur *fcur = NULL;
struct xfs_btree_cur *rcur = NULL;
+ struct xfs_btree_cur *ccur = NULL;
struct xfs_owner_info oinfo;
xfs_agnumber_t agno;
xfs_agblock_t agbno;
@@ -1131,6 +1162,7 @@ xfs_scrub_agfl(
bool is_freesp;
bool has_inodes;
bool has_rmap;
+ bool has_refcount;
int i;
int error;
int err2;
@@ -1185,6 +1217,15 @@ xfs_scrub_agfl(
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap);
}
+ /* Set up cross-reference with refcountbt. */
+ if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+ ccur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+ err2 = xfs_refcount_has_record(ccur, XFS_AGFL_BLOCK(mp), 1,
+ &has_refcount);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", !has_refcount);
+ }
+
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG);
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, agfl_bp);
for (i = be32_to_cpu(agf->agf_flfirst);
@@ -1229,8 +1270,19 @@ xfs_scrub_agfl(
if (!err2)
XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL", has_rmap);
}
+
+ /* Cross-reference with the refcountbt. */
+ if (ccur) {
+ err2 = xfs_refcount_has_record(ccur, agbno, 1,
+ &has_refcount);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agfl_bp, "AGFL",
+ !has_refcount);
+ }
}
+ if (ccur)
+ xfs_btree_del_cursor(ccur, XFS_BTREE_ERROR);
if (rcur)
xfs_btree_del_cursor(rcur, XFS_BTREE_ERROR);
if (fcur)
@@ -1264,6 +1316,7 @@ xfs_scrub_agi(
bool is_freesp;
bool has_inodes;
bool has_rmap;
+ bool has_refcount;
int error;
int err2;
@@ -1334,6 +1387,17 @@ xfs_scrub_agi(
XFS_BTREE_NOERROR);
}
+ /* Cross-reference with the refcountbt. */
+ if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+ xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+ err2 = xfs_refcount_has_record(xcur, XFS_AGI_BLOCK(mp), 1,
+ &has_refcount);
+ if (!err2)
+ XFS_SCRUB_CHECK(mp, agi_bp, "AGI", !has_refcount);
+ xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
return error;
}
@@ -1355,6 +1419,7 @@ xfs_scrub_allocbt_helper(
xfs_extlen_t len;
bool has_rmap;
bool has_inodes;
+ bool has_refcount;
int has_otherrec;
int error = 0;
int err2;
@@ -1415,6 +1480,14 @@ skip_freesp_xref:
XFS_BTREC_SCRUB_CHECK(bs, !has_rmap);
}
+ /* Cross-reference with the refcountbt. */
+ if (bs->refc_cur) {
+ err2 = xfs_refcount_has_record(bs->refc_cur, bno, len,
+ &has_refcount);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_refcount);
+ }
+
return error;
}
@@ -1488,6 +1561,7 @@ xfs_scrub_iallocbt_helper(
bool is_freesp;
bool has_inodes;
bool has_rmap;
+ bool has_refcount;
int holecount;
int i;
int error = 0;
@@ -1547,6 +1621,14 @@ xfs_scrub_iallocbt_helper(
XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
}
+ /* Cross-reference with the refcountbt. */
+ if (bs->refc_cur) {
+ err2 = xfs_refcount_has_record(bs->refc_cur, bno,
+ len, &has_refcount);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_refcount);
+ }
+
goto out;
}
@@ -1604,6 +1686,14 @@ xfs_scrub_iallocbt_helper(
if (!err2)
XFS_BTREC_SCRUB_CHECK(bs, has_rmap);
}
+
+ /* Cross-reference with the refcountbt. */
+ if (bs->refc_cur) {
+ err2 = xfs_refcount_has_record(bs->refc_cur, bno,
+ len, &has_refcount);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, !has_refcount);
+ }
}
XFS_BTREC_SCRUB_CHECK(bs, holecount <= XFS_INODES_PER_CHUNK);
@@ -1674,13 +1764,17 @@ xfs_scrub_rmapbt_helper(
struct xfs_mount *mp = bs->cur->bc_mp;
struct xfs_agf *agf;
struct xfs_rmap_irec irec;
+ struct xfs_refcount_irec crec;
xfs_agblock_t eoag;
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
bool is_freesp;
bool non_inode;
bool is_unwritten;
bool is_bmbt;
bool is_attr;
bool has_inodes;
+ int has_refcount;
int error = 0;
int err2;
@@ -1741,6 +1835,45 @@ xfs_scrub_rmapbt_helper(
!has_inodes);
}
+ /* Cross-reference with the refcount btree. */
+ if (bs->refc_cur) {
+ if (irec.rm_owner == XFS_RMAP_OWN_COW) {
+ /* Check this CoW staging extent. */
+ err2 = xfs_refcount_lookup_le(bs->refc_cur,
+ irec.rm_startblock, &has_refcount);
+ if (err2)
+ goto skip_refc_xref;
+ XFS_BTREC_SCRUB_GOTO(bs, has_refcount, skip_refc_xref);
+
+ err2 = xfs_refcount_get_rec(bs->refc_cur, &crec,
+ &has_refcount);
+ if (err2)
+ goto skip_refc_xref;
+ XFS_BTREC_SCRUB_GOTO(bs, has_refcount, skip_refc_xref);
+ XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock <=
+ irec.rm_startblock);
+ XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock +
+ crec.rc_blockcount >
+ crec.rc_startblock);
+ XFS_BTREC_SCRUB_CHECK(bs, crec.rc_startblock +
+ crec.rc_blockcount >=
+ irec.rm_startblock +
+ irec.rm_blockcount);
+ XFS_BTREC_SCRUB_CHECK(bs,
+ crec.rc_refcount == 1);
+ } else {
+ /* If this is shared, the inode flag must be set. */
+ err2 = xfs_refcount_find_shared(bs->refc_cur,
+ irec.rm_startblock, irec.rm_blockcount,
+ &fbno, &flen, false);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(bs, flen == 0 ||
+ (!non_inode && !is_attr &&
+ !is_bmbt && !is_unwritten));
+ }
+skip_refc_xref:;
+ }
+
return error;
}
@@ -2164,12 +2297,16 @@ xfs_scrub_bmap_extent(
xfs_agnumber_t agno;
xfs_fsblock_t bno;
struct xfs_rmap_irec rmap;
+ struct xfs_refcount_irec crec;
uint64_t owner;
xfs_fileoff_t offset;
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
bool is_freesp;
bool has_inodes;
unsigned int rflags;
int has_rmap;
+ int has_refcount;
int error = 0;
int err2 = 0;
@@ -2348,6 +2485,53 @@ skip_rmap_xref:
XFS_BTREE_NOERROR);
}
+ /*
+ * If this is a non-shared file on a reflink filesystem,
+ * check the refcountbt to see if the flag is wrong.
+ */
+ if (xfs_sb_version_hasreflink(&mp->m_sb) && !info->is_rt) {
+ xcur = xfs_refcountbt_init_cursor(mp, NULL, agf_bp, agno, NULL);
+
+ if (info->whichfork == XFS_COW_FORK) {
+ /* Check this CoW staging extent. */
+ err2 = xfs_refcount_lookup_le(xcur, bno, &has_refcount);
+ if (err2)
+ goto skip_refc_xref;
+ XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_refcount,
+ skip_refc_xref);
+
+ err2 = xfs_refcount_get_rec(xcur, &crec, &has_refcount);
+ if (err2)
+ goto skip_refc_xref;
+ XFS_INO_SCRUB_GOTO(ip, NULL, info->type, has_refcount,
+ skip_refc_xref);
+
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ crec.rc_startblock <= bno);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ crec.rc_startblock +
+ crec.rc_blockcount >
+ crec.rc_startblock);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ crec.rc_startblock +
+ crec.rc_blockcount >=
+ bno + irec->br_blockcount);
+ XFS_INO_SCRUB_CHECK(ip, NULL, info->type,
+ crec.rc_refcount == 1);
+ } else {
+ /* If this is shared, the inode flag must be set. */
+ err2 = xfs_refcount_find_shared(xcur, bno,
+ irec->br_blockcount, &fbno, &flen,
+ false);
+ if (!err2)
+ XFS_INO_SCRUB_CHECK(ip, bp, info->type,
+ flen == 0 ||
+ xfs_is_reflink_inode(ip));
+ }
+skip_refc_xref:
+ xfs_btree_del_cursor(xcur, XFS_BTREE_NOERROR);
+ }
+
xfs_scrub_put_ag_headers(&agi_bp, &agf_bp);
out:
info->lastoff = irec->br_startoff + irec->br_blockcount;
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 24/25] xfs: scrub should cross-reference the realtime bitmap
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (22 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 23/25] xfs: cross-reference refcount btree during scrub Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
2016-08-25 23:42 ` [PATCH 25/25] xfs: query the per-AG reservation counters Darrick J. Wong
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
While we're scrubbing various btrees, cross-reference the records
with the other metadata.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_rtbitmap.c | 29 +++++++++++++++++++++++++++++
fs/xfs/xfs_rtalloc.h | 3 +++
fs/xfs/xfs_scrub.c | 9 +++++++++
3 files changed, 41 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_rtbitmap.c b/fs/xfs/libxfs/xfs_rtbitmap.c
index f4b68c0..0f95c19 100644
--- a/fs/xfs/libxfs/xfs_rtbitmap.c
+++ b/fs/xfs/libxfs/xfs_rtbitmap.c
@@ -1016,3 +1016,32 @@ xfs_rtfree_extent(
}
return 0;
}
+
+/* Is the given extent all free? */
+int
+xfs_rtbitmap_extent_is_free(
+ struct xfs_mount *mp,
+ xfs_rtblock_t start,
+ xfs_rtblock_t len,
+ bool *is_free)
+{
+ xfs_rtblock_t end;
+ xfs_extlen_t clen;
+ int matches;
+ int error;
+
+ *is_free = false;
+ while (len) {
+ clen = len > ~0U ? ~0U : len;
+ error = xfs_rtcheck_range(mp, NULL, start, clen, 1, &end,
+ &matches);
+ if (error || !matches || end < start + clen)
+ return error;
+
+ len -= end - start;
+ start = end + 1;
+ }
+
+ *is_free = true;
+ return error;
+}
diff --git a/fs/xfs/xfs_rtalloc.h b/fs/xfs/xfs_rtalloc.h
index 91e48f9..14fd2c3 100644
--- a/fs/xfs/xfs_rtalloc.h
+++ b/fs/xfs/xfs_rtalloc.h
@@ -121,6 +121,8 @@ int xfs_rtmodify_summary(struct xfs_mount *mp, struct xfs_trans *tp, int log,
int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_rtblock_t start, xfs_extlen_t len,
struct xfs_buf **rbpp, xfs_fsblock_t *rsb);
+int xfs_rtbitmap_extent_is_free(struct xfs_mount *mp,
+ xfs_rtblock_t start, xfs_rtblock_t len, bool *is_free);
#else
@@ -129,6 +131,7 @@ int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
# define xfs_rtpick_extent(m,t,l,rb) (ENOSYS)
# define xfs_growfs_rt(mp,in) (ENOSYS)
# define xfs_rtbuf_get(m,t,b,i,p) (ENOSYS)
+# define xfs_rtbitmap_extent_is_free(m,s,l,i) (ENOSYS)
static inline int /* error */
xfs_rtmount_init(
xfs_mount_t *mp) /* file system mount structure */
diff --git a/fs/xfs/xfs_scrub.c b/fs/xfs/xfs_scrub.c
index ff55d8c..e4e3210 100644
--- a/fs/xfs/xfs_scrub.c
+++ b/fs/xfs/xfs_scrub.c
@@ -2304,6 +2304,7 @@ xfs_scrub_bmap_extent(
xfs_extlen_t flen;
bool is_freesp;
bool has_inodes;
+ bool is_free;
unsigned int rflags;
int has_rmap;
int has_refcount;
@@ -2390,6 +2391,14 @@ xfs_scrub_bmap_extent(
xfs_btree_del_cursor(xcur, err2 ? XFS_BTREE_ERROR :
XFS_BTREE_NOERROR);
}
+ } else {
+ /* Cross-reference with rtbitmap. */
+ xfs_ilock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
+ err2 = xfs_rtbitmap_extent_is_free(mp, irec->br_startblock,
+ irec->br_blockcount, &is_free);
+ if (!err2)
+ XFS_BTREC_SCRUB_CHECK(&info->bs, !is_free);
+ xfs_iunlock(mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
}
/* Cross-reference with rmapbt. */
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH 25/25] xfs: query the per-AG reservation counters
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
` (23 preceding siblings ...)
2016-08-25 23:42 ` [PATCH 24/25] xfs: scrub should cross-reference the realtime bitmap Darrick J. Wong
@ 2016-08-25 23:42 ` Darrick J. Wong
24 siblings, 0 replies; 26+ messages in thread
From: Darrick J. Wong @ 2016-08-25 23:42 UTC (permalink / raw)
To: david, darrick.wong; +Cc: linux-xfs, xfs
Establish an ioctl for userspace to query the original and current
per-AG reservation counts. This will be used by xfs_scrub to
check that the vfs counters are at least somewhat sane.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_fs.h | 10 ++++++++++
fs/xfs/xfs_fsops.c | 29 +++++++++++++++++++++++++++++
fs/xfs/xfs_fsops.h | 2 ++
fs/xfs/xfs_ioctl.c | 16 ++++++++++++++++
4 files changed, 57 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 211c874..f273e76 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -556,6 +556,15 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_FLAGS_ALL 0x0 /* no flags yet */
/*
+ * AG reserved block counters
+ */
+struct xfs_fsop_ag_resblks {
+ __u64 resblks; /* blocks reserved now */
+ __u64 resblks_orig; /* blocks reserved at mount time */
+ __u64 reserved[2];
+};
+
+/*
* ioctl limits
*/
#ifdef XATTR_LIST_MAX
@@ -631,6 +640,7 @@ struct xfs_scrub_metadata {
#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom)
#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t)
+#define XFS_IOC_GET_AG_RESBLKS _IOR ('X', 126, struct xfs_fsop_ag_resblks)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
/* reflink ioctls; these MUST match the btrfs ioctl definitions */
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 9880eeb..9d7984d 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -44,6 +44,7 @@
#include "xfs_filestream.h"
#include "xfs_rmap.h"
#include "xfs_ag_resv.h"
+#include "xfs_fs.h"
/*
* File system operations
@@ -1044,3 +1045,31 @@ xfs_fs_unreserve_ag_blocks(
if (error)
xfs_warn(mp, "Error %d unreserving metadata blocks.", error);
}
+
+/* Query the per-AG reservations to see how many blocks we have reserved. */
+int
+xfs_fs_get_ag_reserve_blocks(
+ struct xfs_mount *mp,
+ struct xfs_fsop_ag_resblks *out)
+{
+ struct xfs_ag_resv *r;
+ struct xfs_perag *pag;
+ xfs_agnumber_t agno;
+
+ out->resblks = 0;
+ out->resblks_orig = 0;
+ out->reserved[0] = out->reserved[1] = 0;
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ pag = xfs_perag_get(mp, agno);
+ r = xfs_perag_resv(pag, XFS_AG_RESV_METADATA);
+ out->resblks += r->ar_reserved;
+ out->resblks_orig += r->ar_asked;
+ r = xfs_perag_resv(pag, XFS_AG_RESV_AGFL);
+ out->resblks += r->ar_reserved;
+ out->resblks_orig += r->ar_asked;
+ xfs_perag_put(pag);
+ }
+
+ return 0;
+}
diff --git a/fs/xfs/xfs_fsops.h b/fs/xfs/xfs_fsops.h
index 71e32480..58b584b 100644
--- a/fs/xfs/xfs_fsops.h
+++ b/fs/xfs/xfs_fsops.h
@@ -25,6 +25,8 @@ extern int xfs_fs_counts(xfs_mount_t *mp, xfs_fsop_counts_t *cnt);
extern int xfs_reserve_blocks(xfs_mount_t *mp, __uint64_t *inval,
xfs_fsop_resblks_t *outval);
extern int xfs_fs_goingdown(xfs_mount_t *mp, __uint32_t inflags);
+extern int xfs_fs_get_ag_reserve_blocks(struct xfs_mount *mp,
+ struct xfs_fsop_ag_resblks *out);
extern void xfs_fs_reserve_ag_blocks(struct xfs_mount *mp);
extern void xfs_fs_unreserve_ag_blocks(struct xfs_mount *mp);
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 65f0c03..2a61268 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1993,6 +1993,22 @@ xfs_file_ioctl(
return 0;
}
+ case XFS_IOC_GET_AG_RESBLKS: {
+ struct xfs_fsop_ag_resblks out;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ error = xfs_fs_get_ag_reserve_blocks(mp, &out);
+ if (error)
+ return error;
+
+ if (copy_to_user(arg, &out, sizeof(out)))
+ return -EFAULT;
+
+ return 0;
+ }
+
case XFS_IOC_FSGROWFSDATA: {
xfs_growfs_data_t in;
_______________________________________________
xfs mailing list
xfs@oss.sgi.com
http://oss.sgi.com/mailman/listinfo/xfs
^ permalink raw reply related [flat|nested] 26+ messages in thread
end of thread, other threads:[~2016-08-25 23:43 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-25 23:40 [PATCH v8 00/25] xfs: online scrub support Darrick J. Wong
2016-08-25 23:40 ` [PATCH 01/25] xfs: introduce the XFS_IOC_GETFSMAP ioctl Darrick J. Wong
2016-08-25 23:40 ` [PATCH 02/25] xfs: report shared extents in getfsmapx Darrick J. Wong
2016-08-25 23:40 ` [PATCH 03/25] xfs: have getfsmap fall back to the freesp btrees when rmap is not present Darrick J. Wong
2016-08-25 23:40 ` [PATCH 04/25] xfs: getfsmap should fall back to rtbitmap when rtrmapbt " Darrick J. Wong
2016-08-25 23:40 ` [PATCH 05/25] xfs: add scrub tracepoints Darrick J. Wong
2016-08-25 23:40 ` [PATCH 06/25] xfs: generic functions to scrub metadata and btrees Darrick J. Wong
2016-08-25 23:40 ` [PATCH 07/25] xfs: create an ioctl to scrub AG metadata Darrick J. Wong
2016-08-25 23:41 ` [PATCH 08/25] xfs: scrub the backup superblocks Darrick J. Wong
2016-08-25 23:41 ` [PATCH 09/25] xfs: scrub AGF and AGFL Darrick J. Wong
2016-08-25 23:41 ` [PATCH 10/25] xfs: scrub the AGI Darrick J. Wong
2016-08-25 23:41 ` [PATCH 11/25] xfs: support scrubbing free space btrees Darrick J. Wong
2016-08-25 23:41 ` [PATCH 12/25] xfs: support scrubbing inode btrees Darrick J. Wong
2016-08-25 23:41 ` [PATCH 13/25] xfs: support scrubbing rmap btree Darrick J. Wong
2016-08-25 23:41 ` [PATCH 14/25] xfs: support scrubbing refcount btree Darrick J. Wong
2016-08-25 23:41 ` [PATCH 15/25] xfs: scrub inodes Darrick J. Wong
2016-08-25 23:41 ` [PATCH 16/25] xfs: scrub inode block mappings Darrick J. Wong
2016-08-25 23:42 ` [PATCH 17/25] xfs: scrub realtime bitmap/summary Darrick J. Wong
2016-08-25 23:42 ` [PATCH 18/25] xfs: scrub should cross-reference with the bnobt Darrick J. Wong
2016-08-25 23:42 ` [PATCH 19/25] xfs: cross-reference bnobt records with cntbt Darrick J. Wong
2016-08-25 23:42 ` [PATCH 20/25] xfs: cross-reference extents with AG header Darrick J. Wong
2016-08-25 23:42 ` [PATCH 21/25] xfs: cross-reference inode btrees during scrub Darrick J. Wong
2016-08-25 23:42 ` [PATCH 22/25] xfs: cross-reference reverse-mapping btree Darrick J. Wong
2016-08-25 23:42 ` [PATCH 23/25] xfs: cross-reference refcount btree during scrub Darrick J. Wong
2016-08-25 23:42 ` [PATCH 24/25] xfs: scrub should cross-reference the realtime bitmap Darrick J. Wong
2016-08-25 23:42 ` [PATCH 25/25] xfs: query the per-AG reservation counters Darrick J. Wong
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.