summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 11:18:40 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-09-17 18:55:22 -0700
commita7e3d023ce9ab7ba2438cb0f622b0a729b8a53f5 (patch)
tree8d9442b16f16f0299f64e482e0b4c6a3df7bfc49 /fs
parent811cfe3da99827c096623fa271299c409be158dc (diff)
xfs: wire up a new inode fork type for the realtime refcount
Plumb in the pieces we need to embed the root of the realtime refcount btree in an inode's data fork, complete with new fork type and on-disk interpretation functions. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/libxfs/xfs_format.h8
-rw-r--r--fs/xfs/libxfs/xfs_inode_fork.c8
-rw-r--r--fs/xfs/libxfs/xfs_rtrefcount_btree.c237
-rw-r--r--fs/xfs/libxfs/xfs_rtrefcount_btree.h112
-rw-r--r--fs/xfs/xfs_inode_item_recover.c4
-rw-r--r--fs/xfs/xfs_ondisk.h1
6 files changed, 367 insertions, 3 deletions
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index dd6c7efdff81..3e6577ebb718 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -1669,6 +1669,14 @@ typedef __be32 xfs_refcount_ptr_t;
#define XFS_RTREFC_CRC_MAGIC 0x52434e54 /* 'RCNT' */
/*
+ * rt refcount root header, on-disk form only.
+ */
+struct xfs_rtrefcount_root {
+ __be16 bb_level; /* 0 is a leaf */
+ __be16 bb_numrecs; /* current # of data records */
+};
+
+/*
* Extents that are being used to stage a copy on write are stored
* in the refcount btree with a refcount of 1 and the upper bit set
* on the startblock. This speeds up mount time deletion of stale
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 5f7cd5b74c95..9d7c0302c424 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -27,6 +27,7 @@
#include "xfs_errortag.h"
#include "xfs_health.h"
#include "xfs_rtrmap_btree.h"
+#include "xfs_rtrefcount_btree.h"
kmem_zone_t *xfs_ifork_zone;
@@ -268,8 +269,7 @@ xfs_iformat_data_fork(
case XFS_DINODE_FMT_REFCOUNT:
if (!xfs_has_rtreflink(ip->i_mount))
return -EFSCORRUPTED;
- ASSERT(0); /* to be implemented later */
- return -EFSCORRUPTED;
+ return xfs_iformat_rtrefcount(ip, dip);
default:
xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__,
dip, sizeof(*dip), __this_address);
@@ -658,7 +658,9 @@ xfs_iflush_fork(
break;
case XFS_DINODE_FMT_REFCOUNT:
- ASSERT(0); /* to be implemented later */
+ ASSERT(whichfork == XFS_DATA_FORK);
+ if (iip->ili_fields & brootflag[whichfork])
+ xfs_iflush_rtrefcount(ip, dip);
break;
default:
diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.c b/fs/xfs/libxfs/xfs_rtrefcount_btree.c
index e416b738939e..636e43f1d76c 100644
--- a/fs/xfs/libxfs/xfs_rtrefcount_btree.c
+++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.c
@@ -79,6 +79,41 @@ xfs_rtrefcountbt_get_maxrecs(
return cur->bc_mp->m_rtrefc_mxr[level != 0];
}
+/*
+ * Calculate number of records in a realtime refcount btree inode root.
+ */
+unsigned int
+xfs_rtrefcountbt_droot_maxrecs(
+ unsigned int blocklen,
+ bool leaf)
+{
+ blocklen -= sizeof(struct xfs_rtrefcount_root);
+
+ if (leaf)
+ return blocklen / sizeof(struct xfs_rtrefcount_rec);
+ return blocklen / (2 * sizeof(struct xfs_rtrefcount_key) +
+ sizeof(xfs_rtrefcount_ptr_t));
+}
+
+/*
+ * Get the maximum records we could store in the on-disk format.
+ *
+ * For non-root nodes this is equivalent to xfs_rtrefcountbt_get_maxrecs, but
+ * for the root node this checks the available space in the dinode fork so that
+ * we can resize the in-memory buffer to match it. After a resize to the
+ * maximum size this function returns the same value as
+ * xfs_rtrefcountbt_get_maxrecs for the root node, too.
+ */
+STATIC int
+xfs_rtrefcountbt_get_dmaxrecs(
+ struct xfs_btree_cur *cur,
+ int level)
+{
+ if (level != cur->bc_nlevels - 1)
+ return cur->bc_mp->m_rtrefc_mxr[level != 0];
+ return xfs_rtrefcountbt_droot_maxrecs(cur->bc_ino.forksize, level == 0);
+}
+
STATIC void
xfs_rtrefcountbt_init_key_from_rec(
union xfs_btree_key *key,
@@ -240,6 +275,68 @@ xfs_rtrefcountbt_recs_inorder(
be64_to_cpu(r2->rtrefc.rc_startblock);
}
+/* Move the rt refcount btree root from one incore buffer to another. */
+static void
+xfs_rtrefcountbt_broot_move(
+ struct xfs_inode *ip,
+ int whichfork,
+ struct xfs_btree_block *dst_broot,
+ size_t dst_bytes,
+ struct xfs_btree_block *src_broot,
+ size_t src_bytes,
+ unsigned int level,
+ unsigned int numrecs)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ void *dptr;
+ void *sptr;
+
+ ASSERT(xfs_rtrefcount_droot_space(src_broot) <=
+ XFS_IFORK_SIZE(ip, whichfork));
+
+ /*
+ * We always have to move the pointers because they are not butted
+ * against the btree block header.
+ */
+ if (numrecs && level > 0) {
+ sptr = xfs_rtrefcount_broot_ptr_addr(mp, src_broot, 1,
+ src_bytes);
+ dptr = xfs_rtrefcount_broot_ptr_addr(mp, dst_broot, 1,
+ dst_bytes);
+ memmove(dptr, sptr, numrecs * sizeof(xfs_fsblock_t));
+ }
+
+ if (src_broot == dst_broot)
+ return;
+
+ /*
+ * If the root is being totally relocated, we have to migrate the block
+ * header and the keys/records that come after it.
+ */
+ memcpy(dst_broot, src_broot, XFS_RTREFCOUNT_BLOCK_LEN);
+
+ if (!numrecs)
+ return;
+
+ if (level == 0) {
+ sptr = xfs_rtrefcount_rec_addr(src_broot, 1);
+ dptr = xfs_rtrefcount_rec_addr(dst_broot, 1);
+ memcpy(dptr, sptr,
+ numrecs * sizeof(struct xfs_rtrefcount_rec));
+ } else {
+ sptr = xfs_rtrefcount_key_addr(src_broot, 1);
+ dptr = xfs_rtrefcount_key_addr(dst_broot, 1);
+ memcpy(dptr, sptr,
+ numrecs * sizeof(struct xfs_rtrefcount_key));
+ }
+}
+
+static const struct xfs_ifork_broot_ops xfs_rtrefcountbt_iroot_ops = {
+ .maxrecs = xfs_rtrefcountbt_maxrecs,
+ .size = xfs_rtrefcount_broot_space_calc,
+ .move = xfs_rtrefcountbt_broot_move,
+};
+
static const struct xfs_btree_ops xfs_rtrefcountbt_ops = {
.rec_len = sizeof(struct xfs_rtrefcount_rec),
.key_len = sizeof(struct xfs_rtrefcount_key),
@@ -249,6 +346,7 @@ static const struct xfs_btree_ops xfs_rtrefcountbt_ops = {
.free_block = xfs_btree_free_imeta_block,
.get_minrecs = xfs_rtrefcountbt_get_minrecs,
.get_maxrecs = xfs_rtrefcountbt_get_maxrecs,
+ .get_dmaxrecs = xfs_rtrefcountbt_get_dmaxrecs,
.init_key_from_rec = xfs_rtrefcountbt_init_key_from_rec,
.init_high_key_from_rec = xfs_rtrefcountbt_init_high_key_from_rec,
.init_rec_from_cur = xfs_rtrefcountbt_init_rec_from_cur,
@@ -258,6 +356,7 @@ static const struct xfs_btree_ops xfs_rtrefcountbt_ops = {
.diff_two_keys = xfs_rtrefcountbt_diff_two_keys,
.keys_inorder = xfs_rtrefcountbt_keys_inorder,
.recs_inorder = xfs_rtrefcountbt_recs_inorder,
+ .iroot_ops = &xfs_rtrefcountbt_iroot_ops,
};
/* Initialize a new rt refcount btree cursor. */
@@ -425,3 +524,141 @@ xfs_rtrefcountbt_calc_reserves(
return xfs_rtrefcountbt_max_size(mp, mp->m_sb.sb_rblocks);
}
+
+/*
+ * Convert on-disk form of btree root to in-memory form.
+ */
+STATIC void
+xfs_rtrefcountbt_from_disk(
+ struct xfs_inode *ip,
+ struct xfs_rtrefcount_root *dblock,
+ int dblocklen,
+ struct xfs_btree_block *rblock)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_rtrefcount_key *fkp;
+ __be64 *fpp;
+ struct xfs_rtrefcount_key *tkp;
+ __be64 *tpp;
+ struct xfs_rtrefcount_rec *frp;
+ struct xfs_rtrefcount_rec *trp;
+ unsigned int numrecs;
+ unsigned int maxrecs;
+ unsigned int rblocklen;
+
+ rblocklen = xfs_rtrefcount_broot_space(mp, dblock);
+
+ xfs_btree_init_block_int(mp, rblock, XFS_BUF_DADDR_NULL,
+ XFS_BTNUM_RTREFC, 0, 0, ip->i_ino,
+ XFS_BTREE_LONG_PTRS | XFS_BTREE_CRC_BLOCKS);
+
+ rblock->bb_level = dblock->bb_level;
+ rblock->bb_numrecs = dblock->bb_numrecs;
+
+ if (be16_to_cpu(rblock->bb_level) > 0) {
+ maxrecs = xfs_rtrefcountbt_droot_maxrecs(dblocklen, false);
+ fkp = xfs_rtrefcount_droot_key_addr(dblock, 1);
+ tkp = xfs_rtrefcount_key_addr(rblock, 1);
+ fpp = xfs_rtrefcount_droot_ptr_addr(dblock, 1, maxrecs);
+ tpp = xfs_rtrefcount_broot_ptr_addr(mp, rblock, 1, rblocklen);
+ numrecs = be16_to_cpu(dblock->bb_numrecs);
+ memcpy(tkp, fkp, 2 * sizeof(*fkp) * numrecs);
+ memcpy(tpp, fpp, sizeof(*fpp) * numrecs);
+ } else {
+ frp = xfs_rtrefcount_droot_rec_addr(dblock, 1);
+ trp = xfs_rtrefcount_rec_addr(rblock, 1);
+ numrecs = be16_to_cpu(dblock->bb_numrecs);
+ memcpy(trp, frp, sizeof(*frp) * numrecs);
+ }
+}
+
+/* Load a realtime reference count btree root in from disk. */
+int
+xfs_iformat_rtrefcount(
+ struct xfs_inode *ip,
+ struct xfs_dinode *dip)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ struct xfs_rtrefcount_root *dfp = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
+ unsigned int numrecs;
+ unsigned int level;
+ int dsize;
+
+ dsize = XFS_DFORK_SIZE(dip, mp, XFS_DATA_FORK);
+ numrecs = be16_to_cpu(dfp->bb_numrecs);
+ level = be16_to_cpu(dfp->bb_level);
+
+ if (level > mp->m_rtrefc_maxlevels ||
+ xfs_rtrefcount_droot_space_calc(level, numrecs) > dsize)
+ return -EFSCORRUPTED;
+
+ xfs_iroot_alloc(ip, XFS_DATA_FORK,
+ xfs_rtrefcount_broot_space_calc(mp, level, numrecs));
+ xfs_rtrefcountbt_from_disk(ip, dfp, dsize, ifp->if_broot);
+ return 0;
+}
+
+/*
+ * Convert in-memory form of btree root to on-disk form.
+ */
+void
+xfs_rtrefcountbt_to_disk(
+ struct xfs_mount *mp,
+ struct xfs_btree_block *rblock,
+ int rblocklen,
+ struct xfs_rtrefcount_root *dblock,
+ int dblocklen)
+{
+ struct xfs_rtrefcount_key *fkp;
+ __be64 *fpp;
+ struct xfs_rtrefcount_key *tkp;
+ __be64 *tpp;
+ struct xfs_rtrefcount_rec *frp;
+ struct xfs_rtrefcount_rec *trp;
+ unsigned int maxrecs;
+ unsigned int numrecs;
+
+ ASSERT(rblock->bb_magic == cpu_to_be32(XFS_RTREFC_CRC_MAGIC));
+ ASSERT(uuid_equal(&rblock->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid));
+ ASSERT(rblock->bb_u.l.bb_blkno == cpu_to_be64(XFS_BUF_DADDR_NULL));
+ ASSERT(rblock->bb_u.l.bb_leftsib == cpu_to_be64(NULLFSBLOCK));
+ ASSERT(rblock->bb_u.l.bb_rightsib == cpu_to_be64(NULLFSBLOCK));
+
+ dblock->bb_level = rblock->bb_level;
+ dblock->bb_numrecs = rblock->bb_numrecs;
+
+ if (be16_to_cpu(rblock->bb_level) > 0) {
+ maxrecs = xfs_rtrefcountbt_droot_maxrecs(dblocklen, false);
+ fkp = xfs_rtrefcount_key_addr(rblock, 1);
+ tkp = xfs_rtrefcount_droot_key_addr(dblock, 1);
+ fpp = xfs_rtrefcount_broot_ptr_addr(mp, rblock, 1, rblocklen);
+ tpp = xfs_rtrefcount_droot_ptr_addr(dblock, 1, maxrecs);
+ numrecs = be16_to_cpu(rblock->bb_numrecs);
+ memcpy(tkp, fkp, 2 * sizeof(*fkp) * numrecs);
+ memcpy(tpp, fpp, sizeof(*fpp) * numrecs);
+ } else {
+ frp = xfs_rtrefcount_rec_addr(rblock, 1);
+ trp = xfs_rtrefcount_droot_rec_addr(dblock, 1);
+ numrecs = be16_to_cpu(rblock->bb_numrecs);
+ memcpy(trp, frp, sizeof(*frp) * numrecs);
+ }
+}
+
+/* Flush a realtime reference count btree root out to disk. */
+void
+xfs_iflush_rtrefcount(
+ struct xfs_inode *ip,
+ struct xfs_dinode *dip)
+{
+ struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ struct xfs_rtrefcount_root *dfp = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
+
+ ASSERT(ifp->if_broot != NULL);
+ ASSERT(ifp->if_broot_bytes > 0);
+ ASSERT(xfs_rtrefcount_droot_space(ifp->if_broot) <=
+ XFS_IFORK_SIZE(ip, XFS_DATA_FORK));
+ xfs_rtrefcountbt_to_disk(ip->i_mount, ifp->if_broot,
+ ifp->if_broot_bytes, dfp,
+ XFS_DFORK_SIZE(dip, ip->i_mount, XFS_DATA_FORK));
+}
diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.h b/fs/xfs/libxfs/xfs_rtrefcount_btree.h
index c31849b97529..62b2a1dc6ea5 100644
--- a/fs/xfs/libxfs/xfs_rtrefcount_btree.h
+++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.h
@@ -24,6 +24,7 @@ unsigned int xfs_rtrefcountbt_maxrecs(struct xfs_mount *mp,
unsigned int blocklen, bool leaf);
unsigned int xfs_rtrefcountbt_compute_maxlevels(struct xfs_mount *mp,
xfs_rfsblock_t dblocks, xfs_rfsblock_t rblocks);
+unsigned int xfs_rtrefcountbt_droot_maxrecs(unsigned int blocklen, bool leaf);
/*
* Addresses of records, keys, and pointers within an incore rtrefcountbt block.
@@ -64,4 +65,115 @@ xfs_rtrefcount_ptr_addr(
xfs_filblks_t xfs_rtrefcountbt_calc_reserves(struct xfs_mount *mp);
+/* Addresses of key, pointers, and records within an ondisk rtrefcount block. */
+
+static inline struct xfs_rtrefcount_rec *
+xfs_rtrefcount_droot_rec_addr(
+ struct xfs_rtrefcount_root *block,
+ unsigned int index)
+{
+ return (struct xfs_rtrefcount_rec *)
+ ((char *)(block + 1) +
+ (index - 1) * sizeof(struct xfs_rtrefcount_rec));
+}
+
+static inline struct xfs_rtrefcount_key *
+xfs_rtrefcount_droot_key_addr(
+ struct xfs_rtrefcount_root *block,
+ unsigned int index)
+{
+ return (struct xfs_rtrefcount_key *)
+ ((char *)(block + 1) +
+ (index - 1) * sizeof(struct xfs_rtrefcount_key));
+}
+
+static inline xfs_rtrefcount_ptr_t *
+xfs_rtrefcount_droot_ptr_addr(
+ struct xfs_rtrefcount_root *block,
+ unsigned int index,
+ unsigned int maxrecs)
+{
+ return (xfs_rtrefcount_ptr_t *)
+ ((char *)(block + 1) +
+ maxrecs * sizeof(struct xfs_rtrefcount_key) +
+ (index - 1) * sizeof(xfs_rtrefcount_ptr_t));
+}
+
+/*
+ * Address of pointers within the incore btree root.
+ *
+ * These are to be used when we know the size of the block and
+ * we don't have a cursor.
+ */
+static inline xfs_rtrefcount_ptr_t *
+xfs_rtrefcount_broot_ptr_addr(
+ struct xfs_mount *mp,
+ struct xfs_btree_block *bb,
+ unsigned int index,
+ unsigned int block_size)
+{
+ return xfs_rtrefcount_ptr_addr(bb, index,
+ xfs_rtrefcountbt_maxrecs(mp, block_size, false));
+}
+
+/*
+ * Compute the space required for the incore btree root containing the given
+ * number of records.
+ */
+static inline size_t
+xfs_rtrefcount_broot_space_calc(
+ struct xfs_mount *mp,
+ unsigned int level,
+ unsigned int nrecs)
+{
+ size_t sz = XFS_RTREFCOUNT_BLOCK_LEN;
+
+ if (level > 0)
+ return sz + nrecs * (sizeof(struct xfs_rtrefcount_key) +
+ sizeof(xfs_rtrefcount_ptr_t));
+ return sz + nrecs * sizeof(struct xfs_rtrefcount_rec);
+}
+
+/*
+ * Compute the space required for the incore btree root given the ondisk
+ * btree root block.
+ */
+static inline size_t
+xfs_rtrefcount_broot_space(struct xfs_mount *mp, struct xfs_rtrefcount_root *bb)
+{
+ return xfs_rtrefcount_broot_space_calc(mp, be16_to_cpu(bb->bb_level),
+ be16_to_cpu(bb->bb_numrecs));
+}
+
+/* Compute the space required for the ondisk root block. */
+static inline size_t
+xfs_rtrefcount_droot_space_calc(
+ unsigned int level,
+ unsigned int nrecs)
+{
+ size_t sz = sizeof(struct xfs_rtrefcount_root);
+
+ if (level > 0)
+ return sz + nrecs * (sizeof(struct xfs_rtrefcount_key) +
+ sizeof(xfs_rtrefcount_ptr_t));
+ return sz + nrecs * sizeof(struct xfs_rtrefcount_rec);
+}
+
+/*
+ * Compute the space required for the ondisk root block given an incore root
+ * block.
+ */
+static inline size_t
+xfs_rtrefcount_droot_space(struct xfs_btree_block *bb)
+{
+ return xfs_rtrefcount_droot_space_calc(be16_to_cpu(bb->bb_level),
+ be16_to_cpu(bb->bb_numrecs));
+}
+
+int xfs_iformat_rtrefcount(struct xfs_inode *ip, struct xfs_dinode *dip);
+void xfs_rtrefcountbt_to_disk(struct xfs_mount *mp,
+ struct xfs_btree_block *rblock, int rblocklen,
+ struct xfs_rtrefcount_root *dblock, int dblocklen);
+void xfs_iflush_rtrefcount(struct xfs_inode *ip, struct xfs_dinode *dip);
+
#endif /* __XFS_RTREFCOUNT_BTREE_H__ */
diff --git a/fs/xfs/xfs_inode_item_recover.c b/fs/xfs/xfs_inode_item_recover.c
index d807125475a4..2afc867d2df9 100644
--- a/fs/xfs/xfs_inode_item_recover.c
+++ b/fs/xfs/xfs_inode_item_recover.c
@@ -23,6 +23,7 @@
#include "xfs_icache.h"
#include "xfs_bmap_btree.h"
#include "xfs_rtrmap_btree.h"
+#include "xfs_rtrefcount_btree.h"
STATIC void
xlog_recover_inode_ra_pass2(
@@ -210,6 +211,9 @@ xlog_recover_inode_dbroot(
case XFS_DINODE_FMT_RMAP:
xfs_rtrmapbt_to_disk(mp, src, len, dfork, dsize);
break;
+ case XFS_DINODE_FMT_REFCOUNT:
+ xfs_rtrefcountbt_to_disk(mp, src, len, dfork, dsize);
+ break;
default:
ASSERT(0);
return -EFSCORRUPTED;
diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
index 88f812062f31..082af167b515 100644
--- a/fs/xfs/xfs_ondisk.h
+++ b/fs/xfs/xfs_ondisk.h
@@ -45,6 +45,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_STRUCT_SIZE(struct xfs_refcount_rec, 12);
XFS_CHECK_STRUCT_SIZE(struct xfs_rtrefcount_key, 8);
XFS_CHECK_STRUCT_SIZE(struct xfs_rtrefcount_rec, 20);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_rtrefcount_root, 4);
XFS_CHECK_STRUCT_SIZE(struct xfs_rmap_key, 20);
XFS_CHECK_STRUCT_SIZE(struct xfs_rmap_rec, 24);
XFS_CHECK_STRUCT_SIZE(xfs_timestamp_t, 8);