From: "Darrick J. Wong" <darrick.wong@oracle.com>
To: darrick.wong@oracle.com
Cc: linux-xfs@vger.kernel.org
Subject: [PATCH 17/22] xfs: scrub directory freespace
Date: Thu, 20 Jul 2017 21:40:19 -0700 [thread overview]
Message-ID: <150061201947.14732.7337884085202901044.stgit@magnolia> (raw)
In-Reply-To: <150061190859.14732.17040548800470377701.stgit@magnolia>
From: Darrick J. Wong <darrick.wong@oracle.com>
Check the free space information in a directory.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/scrub/dir.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 337 insertions(+)
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 1d6b5329..661aec3 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -232,6 +232,338 @@ xfs_scrub_dir_rec(
#undef XFS_SCRUB_DIRENT_GOTO
#undef XFS_SCRUB_DIRENT_CHECK
+#define XFS_SCRUB_DIR_BLOCK_CHECK(fs_ok) \
+ XFS_SCRUB_DATA_CHECK(sc, XFS_DATA_FORK, \
+ lblk << mp->m_sb.sb_blocklog, "dir", fs_ok)
+#define XFS_SCRUB_DIR_BLOCK_GOTO(fs_ok, label) \
+ XFS_SCRUB_DATA_GOTO(sc, XFS_DATA_FORK, \
+ lblk << mp->m_sb.sb_blocklog, "dir", fs_ok, label)
+#define XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(label) \
+ XFS_SCRUB_FILE_OP_ERROR_GOTO(sc, XFS_DATA_FORK, \
+ lblk << mp->m_sb.sb_blocklog, "dir", &error, label)
+/* Is this free entry either in the bestfree or smaller than all of them? */
+static inline bool
+xfs_scrub_directory_check_free_entry(
+ struct xfs_dir2_data_free *bf,
+ struct xfs_dir2_data_unused *dup)
+{
+ struct xfs_dir2_data_free *dfp;
+ unsigned int smallest;
+
+ smallest = -1U;
+ for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
+ if (dfp->offset &&
+ be16_to_cpu(dfp->length) == be16_to_cpu(dup->length))
+ return true;
+ if (smallest < be16_to_cpu(dfp->length))
+ smallest = be16_to_cpu(dfp->length);
+ }
+
+ return be16_to_cpu(dup->length) <= smallest;
+}
+
+/* Check free space info in a directory data block. */
+STATIC int
+xfs_scrub_directory_data_bestfree(
+ struct xfs_scrub_context *sc,
+ xfs_dablk_t lblk,
+ bool is_block)
+{
+ struct xfs_dir2_data_unused *dup;
+ struct xfs_dir2_data_free *dfp;
+ struct xfs_buf *bp;
+ struct xfs_dir2_data_free *bf;
+ struct xfs_mount *mp = sc->mp;
+ char *ptr;
+ char *endptr;
+ u16 tag;
+ int newlen;
+ int offset;
+ int error;
+
+ if (is_block) {
+ /* dir block format */
+ XFS_SCRUB_DIR_BLOCK_CHECK(lblk ==
+ XFS_B_TO_FSBT(mp, XFS_DIR2_DATA_OFFSET));
+ error = xfs_dir3_block_read(sc->tp, sc->ip, &bp);
+ } else {
+ /* dir data format */
+ error = xfs_dir3_data_read(sc->tp, sc->ip, lblk, -1, &bp);
+ }
+ XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(out);
+
+ /* Do the bestfrees correspond to actual free space? */
+ bf = sc->ip->d_ops->data_bestfree_p(bp->b_addr);
+ for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
+ offset = be16_to_cpu(dfp->offset);
+ XFS_SCRUB_DIR_BLOCK_GOTO(offset < BBTOB(bp->b_length), nextloop);
+ if (!offset)
+ continue;
+ dup = (struct xfs_dir2_data_unused *)(bp->b_addr + offset);
+ tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
+
+ XFS_SCRUB_DIR_BLOCK_CHECK(dup->freetag ==
+ cpu_to_be16(XFS_DIR2_DATA_FREE_TAG));
+ XFS_SCRUB_DIR_BLOCK_CHECK(be16_to_cpu(dup->length) ==
+ be16_to_cpu(dfp->length));
+ XFS_SCRUB_DIR_BLOCK_CHECK(tag ==
+ ((char *)dup - (char *)bp->b_addr));
+nextloop:;
+ }
+
+ /* Make sure the bestfrees are actually the best free spaces. */
+ ptr = (char *)sc->ip->d_ops->data_entry_p(bp->b_addr);
+ if (is_block) {
+ struct xfs_dir2_block_tail *btp;
+
+ btp = xfs_dir2_block_tail_p(sc->mp->m_dir_geo, bp->b_addr);
+ endptr = (char *)xfs_dir2_block_leaf_p(btp);
+ } else
+ endptr = (char *)bp->b_addr + BBTOB(bp->b_length);
+ while (ptr < endptr) {
+ dup = (struct xfs_dir2_data_unused *)ptr;
+ /* Skip real entries */
+ if (dup->freetag != cpu_to_be16(XFS_DIR2_DATA_FREE_TAG)) {
+ struct xfs_dir2_data_entry *dep;
+
+ dep = (struct xfs_dir2_data_entry *)ptr;
+ newlen = sc->ip->d_ops->data_entsize(dep->namelen);
+ XFS_SCRUB_DIR_BLOCK_GOTO(newlen > 0, out_buf);
+ ptr += newlen;
+ XFS_SCRUB_DIR_BLOCK_CHECK(ptr <= endptr);
+ continue;
+ }
+
+ /* Spot check this free entry */
+ tag = be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup));
+ XFS_SCRUB_DIR_BLOCK_CHECK(tag ==
+ ((char *)dup - (char *)bp->b_addr));
+
+ /*
+ * Either this entry is a bestfree or it's smaller than
+ * any of the bestfrees.
+ */
+ XFS_SCRUB_DIR_BLOCK_CHECK(
+ xfs_scrub_directory_check_free_entry(bf, dup));
+
+ /* Move on. */
+ newlen = be16_to_cpu(dup->length);
+ XFS_SCRUB_DIR_BLOCK_GOTO(newlen > 0, out_buf);
+ ptr += newlen;
+ XFS_SCRUB_DIR_BLOCK_CHECK(ptr <= endptr);
+ }
+out_buf:
+ xfs_trans_brelse(sc->tp, bp);
+out:
+ return error;
+}
+
+/* Is this the longest free entry in the block? */
+static inline bool
+xfs_scrub_directory_check_freesp(
+ struct xfs_inode *dp,
+ struct xfs_buf *dbp,
+ unsigned int len)
+{
+ struct xfs_dir2_data_free *bf;
+ struct xfs_dir2_data_free *dfp;
+ unsigned int longest = 0;
+ int offset;
+
+ bf = dp->d_ops->data_bestfree_p(dbp->b_addr);
+ for (dfp = &bf[0]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT]; dfp++) {
+ offset = be16_to_cpu(dfp->offset);
+ if (!offset)
+ continue;
+ if (longest < be16_to_cpu(dfp->length))
+ longest = be16_to_cpu(dfp->length);
+ }
+
+ return longest == len;
+}
+
+/* Check free space info in a directory leaf1 block. */
+STATIC int
+xfs_scrub_directory_leaf1_bestfree(
+ struct xfs_scrub_context *sc,
+ struct xfs_da_args *args,
+ xfs_dablk_t lblk)
+{
+ struct xfs_dir2_leaf_tail *ltp;
+ struct xfs_buf *dbp;
+ struct xfs_buf *bp;
+ struct xfs_mount *mp = sc->mp;
+ __be16 *bestp;
+ __u16 best;
+ int i;
+ int error;
+
+ /* Read the free space block */
+ error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, -1, &bp);
+ XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(out);
+
+ /* Check all the entries. */
+ ltp = xfs_dir2_leaf_tail_p(mp->m_dir_geo, bp->b_addr);
+ bestp = xfs_dir2_leaf_bests_p(ltp);
+ for (i = 0; i < be32_to_cpu(ltp->bestcount); i++, bestp++) {
+ best = be16_to_cpu(*bestp);
+ if (best == NULLDATAOFF)
+ continue;
+ error = xfs_dir3_data_read(sc->tp, sc->ip,
+ i * args->geo->fsbcount, -1, &dbp);
+ XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(skip_buf);
+ XFS_SCRUB_DIR_BLOCK_CHECK(
+ xfs_scrub_directory_check_freesp(sc->ip, dbp,
+ best));
+ xfs_trans_brelse(sc->tp, dbp);
+skip_buf:
+ ;
+ }
+out:
+ return error;
+}
+
+/* Check free space info in a directory freespace block. */
+STATIC int
+xfs_scrub_directory_free_bestfree(
+ struct xfs_scrub_context *sc,
+ struct xfs_da_args *args,
+ xfs_dablk_t lblk)
+{
+ struct xfs_dir3_icfree_hdr freehdr;
+ struct xfs_buf *dbp;
+ struct xfs_buf *bp;
+ struct xfs_mount *mp = sc->mp;
+ __be16 *bestp;
+ __be16 best;
+ int i;
+ int error;
+
+ /* Read the free space block */
+ error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp);
+ XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(out);
+
+ /* Check all the entries. */
+ sc->ip->d_ops->free_hdr_from_disk(&freehdr, bp->b_addr);
+ bestp = sc->ip->d_ops->free_bests_p(bp->b_addr);
+ for (i = 0; i < freehdr.nvalid; i++, bestp++) {
+ best = be16_to_cpu(*bestp);
+ if (best == NULLDATAOFF)
+ continue;
+ error = xfs_dir3_data_read(sc->tp, sc->ip,
+ (freehdr.firstdb + i) * args->geo->fsbcount,
+ -1, &dbp);
+ XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(skip_buf);
+ XFS_SCRUB_DIR_BLOCK_CHECK(
+ xfs_scrub_directory_check_freesp(sc->ip, dbp,
+ best));
+ xfs_trans_brelse(sc->tp, dbp);
+skip_buf:
+ ;
+ }
+out:
+ return error;
+}
+
+#define for_each_extent_dablk(lblk, args, got) \
+ for ((lblk) = roundup((xfs_dablk_t)(got)->br_startoff, (args)->geo->fsbcount); \
+ (lblk) < (got)->br_startoff + (got)->br_blockcount; \
+ (lblk) += (args)->geo->fsbcount)
+/* Check free space information in directories. */
+STATIC int
+xfs_scrub_directory_blocks(
+ struct xfs_scrub_context *sc)
+{
+ struct xfs_bmbt_irec got;
+ struct xfs_da_args args;
+ struct xfs_ifork *ifp;
+ struct xfs_mount *mp = sc->mp;
+ xfs_fileoff_t leaf_lblk;
+ xfs_fileoff_t free_lblk;
+ xfs_fileoff_t lblk;
+ xfs_extnum_t idx;
+ bool found;
+ int is_block = 0;
+ int error;
+
+ /* Ignore local format directories. */
+ if (sc->ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
+ sc->ip->i_d.di_format != XFS_DINODE_FMT_BTREE)
+ return 0;
+
+ ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK);
+ lblk = XFS_B_TO_FSB(mp, XFS_DIR2_DATA_OFFSET);
+ leaf_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_LEAF_OFFSET);
+ free_lblk = XFS_B_TO_FSB(mp, XFS_DIR2_FREE_OFFSET);
+
+ /* Is this a block dir? */
+ args.dp = sc->ip;
+ args.geo = mp->m_dir_geo;
+ args.trans = sc->tp;
+ error = xfs_dir2_isblock(&args, &is_block);
+ XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO(out);
+
+ /* Iterate all the data extents in the directory... */
+ found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+ while (found) {
+ /* No more data blocks... */
+ if (got.br_startoff >= leaf_lblk)
+ break;
+
+ /* Check each data block's bestfree data */
+ for_each_extent_dablk(lblk, &args, &got) {
+ error = xfs_scrub_directory_data_bestfree(sc, lblk,
+ is_block);
+ if (error)
+ goto out;
+ }
+
+ found = xfs_iext_get_extent(ifp, ++idx, &got);
+ }
+
+ /* Look for a leaf1 block, which has free info. */
+ if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &idx, &got) &&
+ got.br_startoff == leaf_lblk &&
+ got.br_blockcount == args.geo->fsbcount &&
+ !xfs_iext_get_extent(ifp, ++idx, &got)) {
+ XFS_SCRUB_DIR_BLOCK_GOTO(!is_block, not_leaf1);
+ error = xfs_scrub_directory_leaf1_bestfree(sc, &args,
+ leaf_lblk);
+ if (error)
+ goto out;
+ }
+not_leaf1:
+
+ /* Scan for free blocks */
+ lblk = free_lblk;
+ found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+ while (found) {
+ /*
+ * Dirs can't have blocks mapped above 2^32.
+ * Single-block dirs shouldn't even be here.
+ */
+ lblk = got.br_startoff;
+ XFS_SCRUB_DIR_BLOCK_GOTO(!(lblk & ~((1ULL << 32) - 1ULL)), out);
+ XFS_SCRUB_DIR_BLOCK_GOTO(!is_block, nextfree);
+
+ /* Check each dir free block's bestfree data */
+ for_each_extent_dablk(lblk, &args, &got) {
+ error = xfs_scrub_directory_free_bestfree(sc, &args,
+ lblk);
+ if (error)
+ goto out;
+ }
+
+nextfree:
+ found = xfs_iext_get_extent(ifp, ++idx, &got);
+ }
+out:
+ return error;
+}
+#undef for_each_extent_dablk
+#undef XFS_SCRUB_DIR_BLOCK_OP_ERROR_GOTO
+#undef XFS_SCRUB_DIR_BLOCK_CHECK
+
/* Scrub a whole directory. */
int
xfs_scrub_directory(
@@ -258,6 +590,11 @@ xfs_scrub_directory(
if (error)
return error;
+ /* Check the freespace. */
+ error = xfs_scrub_directory_blocks(sc);
+ if (error)
+ return error;
+
/* Check that every dirent we see can also be looked up by hash. */
bufsize = (size_t)min_t(loff_t, 32768, sc->ip->i_d.di_size);
sdc.sc = sc;
next prev parent reply other threads:[~2017-07-21 4:40 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-07-21 4:38 [PATCH v8 00/22] xfs: online scrub support Darrick J. Wong
2017-07-21 4:38 ` [PATCH 01/22] xfs: query the per-AG reservation counters Darrick J. Wong
2017-07-23 16:16 ` Allison Henderson
2017-07-23 22:25 ` Dave Chinner
2017-07-24 19:07 ` Darrick J. Wong
2017-07-21 4:38 ` [PATCH 02/22] xfs: add scrub tracepoints Darrick J. Wong
2017-07-23 16:23 ` Allison Henderson
2017-07-21 4:38 ` [PATCH 03/22] xfs: create an ioctl to scrub AG metadata Darrick J. Wong
2017-07-23 16:37 ` Allison Henderson
2017-07-23 23:45 ` Dave Chinner
2017-07-24 21:14 ` Darrick J. Wong
2017-07-21 4:38 ` [PATCH 04/22] xfs: generic functions to scrub metadata and btrees Darrick J. Wong
2017-07-23 16:40 ` Allison Henderson
2017-07-24 1:05 ` Dave Chinner
2017-07-24 21:58 ` Darrick J. Wong
2017-07-24 23:15 ` Dave Chinner
2017-07-25 0:39 ` Darrick J. Wong
2017-07-21 4:39 ` [PATCH 05/22] xfs: scrub in-memory metadata buffers Darrick J. Wong
2017-07-23 16:48 ` Allison Henderson
2017-07-24 1:43 ` Dave Chinner
2017-07-24 22:36 ` Darrick J. Wong
2017-07-24 23:38 ` Dave Chinner
2017-07-25 0:14 ` Darrick J. Wong
2017-07-25 3:32 ` Dave Chinner
2017-07-25 5:27 ` Darrick J. Wong
2017-07-21 4:39 ` [PATCH 06/22] xfs: scrub the backup superblocks Darrick J. Wong
2017-07-23 16:50 ` Allison Henderson
2017-07-25 4:05 ` Dave Chinner
2017-07-25 5:42 ` Darrick J. Wong
2017-07-21 4:39 ` [PATCH 07/22] xfs: scrub AGF and AGFL Darrick J. Wong
2017-07-23 16:59 ` Allison Henderson
2017-07-21 4:39 ` [PATCH 08/22] xfs: scrub the AGI Darrick J. Wong
2017-07-23 17:02 ` Allison Henderson
2017-07-21 4:39 ` [PATCH 09/22] xfs: scrub free space btrees Darrick J. Wong
2017-07-23 17:09 ` Allison Henderson
2017-07-21 4:39 ` [PATCH 10/22] xfs: scrub inode btrees Darrick J. Wong
2017-07-23 17:15 ` Allison Henderson
2017-07-21 4:39 ` [PATCH 11/22] xfs: scrub rmap btrees Darrick J. Wong
2017-07-23 17:21 ` Allison Henderson
2017-07-21 4:39 ` [PATCH 12/22] xfs: scrub refcount btrees Darrick J. Wong
2017-07-23 17:25 ` Allison Henderson
2017-07-21 4:39 ` [PATCH 13/22] xfs: scrub inodes Darrick J. Wong
2017-07-23 17:38 ` Allison Henderson
2017-07-24 20:02 ` Darrick J. Wong
2017-07-21 4:40 ` [PATCH 14/22] xfs: scrub inode block mappings Darrick J. Wong
2017-07-23 17:41 ` Allison Henderson
2017-07-24 20:05 ` Darrick J. Wong
2017-07-21 4:40 ` [PATCH 15/22] xfs: scrub directory/attribute btrees Darrick J. Wong
2017-07-23 17:45 ` Allison Henderson
2017-07-21 4:40 ` [PATCH 16/22] xfs: scrub directory metadata Darrick J. Wong
2017-07-23 17:51 ` Allison Henderson
2017-07-21 4:40 ` Darrick J. Wong [this message]
2017-07-23 17:55 ` [PATCH 17/22] xfs: scrub directory freespace Allison Henderson
2017-07-21 4:40 ` [PATCH 18/22] xfs: scrub extended attributes Darrick J. Wong
2017-07-23 17:57 ` Allison Henderson
2017-07-21 4:40 ` [PATCH 19/22] xfs: scrub symbolic links Darrick J. Wong
2017-07-23 17:59 ` Allison Henderson
2017-07-21 4:40 ` [PATCH 20/22] xfs: scrub parent pointers Darrick J. Wong
2017-07-23 18:03 ` Allison Henderson
2017-07-21 4:40 ` [PATCH 21/22] xfs: scrub realtime bitmap/summary Darrick J. Wong
2017-07-23 18:05 ` Allison Henderson
2017-07-21 4:40 ` [PATCH 22/22] xfs: scrub quota information Darrick J. Wong
2017-07-23 18:07 ` Allison Henderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=150061201947.14732.7337884085202901044.stgit@magnolia \
--to=darrick.wong@oracle.com \
--cc=linux-xfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).