summaryrefslogtreecommitdiffstats
path: root/fs/xfs/libxfs
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2023-12-15 10:03:39 -0800
committerDarrick J. Wong <djwong@kernel.org>2023-12-15 10:03:39 -0800
commit8f71bede8efd820627ac05c19eac2758214bc896 (patch)
treee4d9e6e6bc22da842aefeb090f1fca5e3ed43ebb /fs/xfs/libxfs
parent66da11280f7ecd77abd999c469efc0dd643f26f5 (diff)
downloadlinux-stable-8f71bede8efd820627ac05c19eac2758214bc896.tar.gz
linux-stable-8f71bede8efd820627ac05c19eac2758214bc896.tar.bz2
linux-stable-8f71bede8efd820627ac05c19eac2758214bc896.zip
xfs: repair inode fork block mapping data structures
Use the reverse-mapping btree information to rebuild an inode block map. Update the btree bulk loading code as necessary to support inode rooted btrees and fix some bitrot problems. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/libxfs')
-rw-r--r--fs/xfs/libxfs/xfs_bmap_btree.c121
-rw-r--r--fs/xfs/libxfs/xfs_bmap_btree.h5
-rw-r--r--fs/xfs/libxfs/xfs_btree_staging.c11
-rw-r--r--fs/xfs/libxfs/xfs_btree_staging.h2
-rw-r--r--fs/xfs/libxfs/xfs_iext_tree.c23
-rw-r--r--fs/xfs/libxfs/xfs_inode_fork.c1
-rw-r--r--fs/xfs/libxfs/xfs_inode_fork.h3
7 files changed, 136 insertions, 30 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 8360256cff16..71f2d50f7823 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -15,6 +15,7 @@
#include "xfs_trans.h"
#include "xfs_alloc.h"
#include "xfs_btree.h"
+#include "xfs_btree_staging.h"
#include "xfs_bmap_btree.h"
#include "xfs_bmap.h"
#include "xfs_error.h"
@@ -288,10 +289,7 @@ xfs_bmbt_get_minrecs(
int level)
{
if (level == cur->bc_nlevels - 1) {
- struct xfs_ifork *ifp;
-
- ifp = xfs_ifork_ptr(cur->bc_ino.ip,
- cur->bc_ino.whichfork);
+ struct xfs_ifork *ifp = xfs_btree_ifork_ptr(cur);
return xfs_bmbt_maxrecs(cur->bc_mp,
ifp->if_broot_bytes, level == 0) / 2;
@@ -306,10 +304,7 @@ xfs_bmbt_get_maxrecs(
int level)
{
if (level == cur->bc_nlevels - 1) {
- struct xfs_ifork *ifp;
-
- ifp = xfs_ifork_ptr(cur->bc_ino.ip,
- cur->bc_ino.whichfork);
+ struct xfs_ifork *ifp = xfs_btree_ifork_ptr(cur);
return xfs_bmbt_maxrecs(cur->bc_mp,
ifp->if_broot_bytes, level == 0);
@@ -543,23 +538,19 @@ static const struct xfs_btree_ops xfs_bmbt_ops = {
.keys_contiguous = xfs_bmbt_keys_contiguous,
};
-/*
- * Allocate a new bmap btree cursor.
- */
-struct xfs_btree_cur * /* new bmap btree cursor */
-xfs_bmbt_init_cursor(
- struct xfs_mount *mp, /* file system mount point */
- struct xfs_trans *tp, /* transaction pointer */
- struct xfs_inode *ip, /* inode owning the btree */
- int whichfork) /* data or attr fork */
+static struct xfs_btree_cur *
+xfs_bmbt_init_common(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ int whichfork)
{
- struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
struct xfs_btree_cur *cur;
+
ASSERT(whichfork != XFS_COW_FORK);
cur = xfs_btree_alloc_cursor(mp, tp, XFS_BTNUM_BMAP,
mp->m_bm_maxlevels[whichfork], xfs_bmbt_cur_cache);
- cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_bmbt_2);
cur->bc_ops = &xfs_bmbt_ops;
@@ -567,10 +558,30 @@ xfs_bmbt_init_cursor(
if (xfs_has_crc(mp))
cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
- cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork);
cur->bc_ino.ip = ip;
cur->bc_ino.allocated = 0;
cur->bc_ino.flags = 0;
+
+ return cur;
+}
+
+/*
+ * Allocate a new bmap btree cursor.
+ */
+struct xfs_btree_cur *
+xfs_bmbt_init_cursor(
+ struct xfs_mount *mp,
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ int whichfork)
+{
+ struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
+ struct xfs_btree_cur *cur;
+
+ cur = xfs_bmbt_init_common(mp, tp, ip, whichfork);
+
+ cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
+ cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork);
cur->bc_ino.whichfork = whichfork;
return cur;
@@ -588,6 +599,76 @@ xfs_bmbt_block_maxrecs(
}
/*
+ * Allocate a new bmap btree cursor for reloading an inode block mapping data
+ * structure. Note that callers can use the staged cursor to reload extents
+ * format inode forks if they rebuild the iext tree and commit the staged
+ * cursor immediately.
+ */
+struct xfs_btree_cur *
+xfs_bmbt_stage_cursor(
+ struct xfs_mount *mp,
+ struct xfs_inode *ip,
+ struct xbtree_ifakeroot *ifake)
+{
+ struct xfs_btree_cur *cur;
+ struct xfs_btree_ops *ops;
+
+ /* data fork always has larger maxheight */
+ cur = xfs_bmbt_init_common(mp, NULL, ip, XFS_DATA_FORK);
+ cur->bc_nlevels = ifake->if_levels;
+ cur->bc_ino.forksize = ifake->if_fork_size;
+
+ /* Don't let anyone think we're attached to the real fork yet. */
+ cur->bc_ino.whichfork = -1;
+ xfs_btree_stage_ifakeroot(cur, ifake, &ops);
+ ops->update_cursor = NULL;
+ return cur;
+}
+
+/*
+ * Swap in the new inode fork root. Once we pass this point the newly rebuilt
+ * mappings are in place and we have to kill off any old btree blocks.
+ */
+void
+xfs_bmbt_commit_staged_btree(
+ struct xfs_btree_cur *cur,
+ struct xfs_trans *tp,
+ int whichfork)
+{
+ struct xbtree_ifakeroot *ifake = cur->bc_ino.ifake;
+ struct xfs_ifork *ifp;
+ static const short brootflag[2] = {XFS_ILOG_DBROOT, XFS_ILOG_ABROOT};
+ static const short extflag[2] = {XFS_ILOG_DEXT, XFS_ILOG_AEXT};
+ int flags = XFS_ILOG_CORE;
+
+ ASSERT(cur->bc_flags & XFS_BTREE_STAGING);
+ ASSERT(whichfork != XFS_COW_FORK);
+
+ /*
+ * Free any resources hanging off the real fork, then shallow-copy the
+ * staging fork's contents into the real fork to transfer everything
+ * we just built.
+ */
+ ifp = xfs_ifork_ptr(cur->bc_ino.ip, whichfork);
+ xfs_idestroy_fork(ifp);
+ memcpy(ifp, ifake->if_fork, sizeof(struct xfs_ifork));
+
+ switch (ifp->if_format) {
+ case XFS_DINODE_FMT_EXTENTS:
+ flags |= extflag[whichfork];
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ flags |= brootflag[whichfork];
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ xfs_trans_log_inode(tp, cur->bc_ino.ip, flags);
+ xfs_btree_commit_ifakeroot(cur, tp, whichfork, &xfs_bmbt_ops);
+}
+
+/*
* Calculate number of records in a bmap btree block.
*/
int
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
index 3e7a40a83835..151b8491f60e 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.h
+++ b/fs/xfs/libxfs/xfs_bmap_btree.h
@@ -11,6 +11,7 @@ struct xfs_btree_block;
struct xfs_mount;
struct xfs_inode;
struct xfs_trans;
+struct xbtree_ifakeroot;
/*
* Btree block header size depends on a superblock flag.
@@ -106,6 +107,10 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
struct xfs_trans *, struct xfs_inode *, int);
+struct xfs_btree_cur *xfs_bmbt_stage_cursor(struct xfs_mount *mp,
+ struct xfs_inode *ip, struct xbtree_ifakeroot *ifake);
+void xfs_bmbt_commit_staged_btree(struct xfs_btree_cur *cur,
+ struct xfs_trans *tp, int whichfork);
extern unsigned long long xfs_bmbt_calc_size(struct xfs_mount *mp,
unsigned long long len);
diff --git a/fs/xfs/libxfs/xfs_btree_staging.c b/fs/xfs/libxfs/xfs_btree_staging.c
index 0c978a31e284..e276eba87cb1 100644
--- a/fs/xfs/libxfs/xfs_btree_staging.c
+++ b/fs/xfs/libxfs/xfs_btree_staging.c
@@ -405,7 +405,7 @@ xfs_btree_bload_prep_block(
ASSERT(*bpp == NULL);
/* Allocate a new incore btree root block. */
- new_size = bbl->iroot_size(cur, nr_this_block, priv);
+ new_size = bbl->iroot_size(cur, level, nr_this_block, priv);
ifp->if_broot = kmem_zalloc(new_size, 0);
ifp->if_broot_bytes = (int)new_size;
@@ -596,7 +596,14 @@ xfs_btree_bload_level_geometry(
unsigned int desired_npb;
unsigned int maxnr;
- maxnr = cur->bc_ops->get_maxrecs(cur, level);
+ /*
+ * Compute the absolute maximum number of records that we can store in
+ * the ondisk block or inode root.
+ */
+ if (cur->bc_ops->get_dmaxrecs)
+ maxnr = cur->bc_ops->get_dmaxrecs(cur, level);
+ else
+ maxnr = cur->bc_ops->get_maxrecs(cur, level);
/*
* Compute the number of blocks we need to fill each block with the
diff --git a/fs/xfs/libxfs/xfs_btree_staging.h b/fs/xfs/libxfs/xfs_btree_staging.h
index f0a5007284ef..055ea43b1e18 100644
--- a/fs/xfs/libxfs/xfs_btree_staging.h
+++ b/fs/xfs/libxfs/xfs_btree_staging.h
@@ -53,7 +53,7 @@ typedef int (*xfs_btree_bload_get_records_fn)(struct xfs_btree_cur *cur,
typedef int (*xfs_btree_bload_claim_block_fn)(struct xfs_btree_cur *cur,
union xfs_btree_ptr *ptr, void *priv);
typedef size_t (*xfs_btree_bload_iroot_size_fn)(struct xfs_btree_cur *cur,
- unsigned int nr_this_level, void *priv);
+ unsigned int level, unsigned int nr_this_level, void *priv);
struct xfs_btree_bload {
/*
diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
index 773cf4349428..d062794cc795 100644
--- a/fs/xfs/libxfs/xfs_iext_tree.c
+++ b/fs/xfs/libxfs/xfs_iext_tree.c
@@ -622,13 +622,11 @@ static inline void xfs_iext_inc_seq(struct xfs_ifork *ifp)
}
void
-xfs_iext_insert(
- struct xfs_inode *ip,
+xfs_iext_insert_raw(
+ struct xfs_ifork *ifp,
struct xfs_iext_cursor *cur,
- struct xfs_bmbt_irec *irec,
- int state)
+ struct xfs_bmbt_irec *irec)
{
- struct xfs_ifork *ifp = xfs_iext_state_to_fork(ip, state);
xfs_fileoff_t offset = irec->br_startoff;
struct xfs_iext_leaf *new = NULL;
int nr_entries, i;
@@ -662,12 +660,23 @@ xfs_iext_insert(
xfs_iext_set(cur_rec(cur), irec);
ifp->if_bytes += sizeof(struct xfs_iext_rec);
- trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
-
if (new)
xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
}
+void
+xfs_iext_insert(
+ struct xfs_inode *ip,
+ struct xfs_iext_cursor *cur,
+ struct xfs_bmbt_irec *irec,
+ int state)
+{
+ struct xfs_ifork *ifp = xfs_iext_state_to_fork(ip, state);
+
+ xfs_iext_insert_raw(ifp, cur, irec);
+ trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
+}
+
static struct xfs_iext_node *
xfs_iext_rebalance_node(
struct xfs_iext_node *parent,
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index dad8ea832c20..b86d57589f67 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -520,6 +520,7 @@ xfs_idata_realloc(
ifp->if_bytes = new_size;
}
+/* Free all memory and reset a fork back to its initial state. */
void
xfs_idestroy_fork(
struct xfs_ifork *ifp)
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index 96d307784c85..535be5c03689 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -180,6 +180,9 @@ void xfs_init_local_fork(struct xfs_inode *ip, int whichfork,
const void *data, int64_t size);
xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp);
+void xfs_iext_insert_raw(struct xfs_ifork *ifp,
+ struct xfs_iext_cursor *cur,
+ struct xfs_bmbt_irec *irec);
void xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
struct xfs_bmbt_irec *, int);
void xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,