summaryrefslogtreecommitdiff
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-10-22 16:41:08 -0700
commitcdb98f1269098e15d22eac7d8d3fc678ff488689 (patch)
tree44be898f6b6d18ec64cfa1ced9c2822f2fa11985
parente22660dc7050040ef73552e0bb24ec6accffd78b (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>
-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 fdcabfe6ef4d..7bc0fe704738 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"
struct kmem_cache *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 94ea2d53cbcd..6a5d9d31584e 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,
@@ -237,6 +272,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),
@@ -246,6 +343,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,
@@ -255,6 +353,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. */
@@ -445,3 +544,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 6c63c8336c54..d558b6fa658c 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.
@@ -65,4 +66,115 @@ xfs_rtrefcount_ptr_addr(
int __init xfs_rtrefcountbt_create_cursor_cache(void);
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 19c71a47583b..e900e3435eeb 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);