Commit f33659e8 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: wire up a new metafile type for the realtime rmap



Plumb in the pieces we need to embed the root of the realtime rmap btree
in an inode's data fork, complete with new metafile type and on-disk
interpretation functions.

Signed-off-by: default avatar"Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 8491a55c
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -1736,6 +1736,14 @@ typedef __be32 xfs_rmap_ptr_t;
 */
#define	XFS_RTRMAP_CRC_MAGIC	0x4d415052	/* 'MAPR' */

/*
 * rtrmap root header, on-disk form only.
 */
struct xfs_rtrmap_root {
	__be16		bb_level;	/* 0 is a leaf */
	__be16		bb_numrecs;	/* current # of data records */
};

/* inode-based btree pointer type */
typedef __be64 xfs_rtrmap_ptr_t;

+3 −3
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include "xfs_errortag.h"
#include "xfs_health.h"
#include "xfs_symlink_remote.h"
#include "xfs_rtrmap_btree.h"

struct kmem_cache *xfs_ifork_cache;

@@ -270,8 +271,7 @@ xfs_iformat_data_fork(
		case XFS_DINODE_FMT_META_BTREE:
			switch (ip->i_metatype) {
			case XFS_METAFILE_RTRMAP:
				ASSERT(0); /* to be implemented later */
				return -EFSCORRUPTED;
				return xfs_iformat_rtrmap(ip, dip);
			default:
				break;
			}
@@ -618,7 +618,7 @@ xfs_iflush_fork(

		switch (ip->i_metatype) {
		case XFS_METAFILE_RTRMAP:
			ASSERT(0); /* to be implemented later */
			xfs_iflush_rtrmap(ip, dip);
			break;
		default:
			ASSERT(0);
+1 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ xfs_check_ondisk_structs(void)
	XFS_CHECK_STRUCT_SIZE(union xfs_suminfo_raw,		4);
	XFS_CHECK_STRUCT_SIZE(struct xfs_rtbuf_blkinfo,		48);
	XFS_CHECK_STRUCT_SIZE(xfs_rtrmap_ptr_t,			8);
	XFS_CHECK_STRUCT_SIZE(struct xfs_rtrmap_root,		4);

	/*
	 * m68k has problems with struct xfs_attr_leaf_name_remote, but we pad
+251 −0
Original line number Diff line number Diff line
@@ -77,6 +77,39 @@ xfs_rtrmapbt_get_maxrecs(
	return cur->bc_mp->m_rtrmap_mxr[level != 0];
}

/* Calculate number of records in the ondisk realtime rmap btree inode root. */
unsigned int
xfs_rtrmapbt_droot_maxrecs(
	unsigned int		blocklen,
	bool			leaf)
{
	blocklen -= sizeof(struct xfs_rtrmap_root);

	if (leaf)
		return blocklen / sizeof(struct xfs_rmap_rec);
	return blocklen / (2 * sizeof(struct xfs_rmap_key) +
			sizeof(xfs_rtrmap_ptr_t));
}

/*
 * Get the maximum records we could store in the on-disk format.
 *
 * For non-root nodes this is equivalent to xfs_rtrmapbt_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_rtrmapbt_get_maxrecs for the root node, too.
 */
STATIC int
xfs_rtrmapbt_get_dmaxrecs(
	struct xfs_btree_cur	*cur,
	int			level)
{
	if (level != cur->bc_nlevels - 1)
		return cur->bc_mp->m_rtrmap_mxr[level != 0];
	return xfs_rtrmapbt_droot_maxrecs(cur->bc_ino.forksize, level == 0);
}

/*
 * Convert the ondisk record's offset field into the ondisk key's offset field.
 * Fork and bmbt are significant parts of the rmap record key, but written
@@ -369,6 +402,87 @@ xfs_rtrmapbt_keys_contiguous(
				 be32_to_cpu(key2->rmap.rm_startblock));
}

static inline void
xfs_rtrmapbt_move_ptrs(
	struct xfs_mount	*mp,
	struct xfs_btree_block	*broot,
	short			old_size,
	size_t			new_size,
	unsigned int		numrecs)
{
	void			*dptr;
	void			*sptr;

	sptr = xfs_rtrmap_broot_ptr_addr(mp, broot, 1, old_size);
	dptr = xfs_rtrmap_broot_ptr_addr(mp, broot, 1, new_size);
	memmove(dptr, sptr, numrecs * sizeof(xfs_rtrmap_ptr_t));
}

static struct xfs_btree_block *
xfs_rtrmapbt_broot_realloc(
	struct xfs_btree_cur	*cur,
	unsigned int		new_numrecs)
{
	struct xfs_mount	*mp = cur->bc_mp;
	struct xfs_ifork	*ifp = xfs_btree_ifork_ptr(cur);
	struct xfs_btree_block	*broot;
	unsigned int		new_size;
	unsigned int		old_size = ifp->if_broot_bytes;
	const unsigned int	level = cur->bc_nlevels - 1;

	new_size = xfs_rtrmap_broot_space_calc(mp, level, new_numrecs);

	/* Handle the nop case quietly. */
	if (new_size == old_size)
		return ifp->if_broot;

	if (new_size > old_size) {
		unsigned int	old_numrecs;

		/*
		 * If there wasn't any memory allocated before, just allocate
		 * it now and get out.
		 */
		if (old_size == 0)
			return xfs_broot_realloc(ifp, new_size);

		/*
		 * If there is already an existing if_broot, then we need to
		 * realloc it and possibly move the node block pointers because
		 * those are not butted up against the btree block header.
		 */
		old_numrecs = xfs_rtrmapbt_maxrecs(mp, old_size, level == 0);
		broot = xfs_broot_realloc(ifp, new_size);
		if (level > 0)
			xfs_rtrmapbt_move_ptrs(mp, broot, old_size, new_size,
					old_numrecs);
		goto out_broot;
	}

	/*
	 * We're reducing numrecs.  If we're going all the way to zero, just
	 * free the block.
	 */
	ASSERT(ifp->if_broot != NULL && old_size > 0);
	if (new_size == 0)
		return xfs_broot_realloc(ifp, 0);

	/*
	 * Shrink the btree root by possibly moving the rtrmapbt pointers,
	 * since they are not butted up against the btree block header.  Then
	 * reallocate broot.
	 */
	if (level > 0)
		xfs_rtrmapbt_move_ptrs(mp, ifp->if_broot, old_size, new_size,
				new_numrecs);
	broot = xfs_broot_realloc(ifp, new_size);

out_broot:
	ASSERT(xfs_rtrmap_droot_space(broot) <=
	       xfs_inode_fork_size(cur->bc_ino.ip, cur->bc_ino.whichfork));
	return broot;
}

const struct xfs_btree_ops xfs_rtrmapbt_ops = {
	.name			= "rtrmap",
	.type			= XFS_BTREE_TYPE_INODE,
@@ -388,6 +502,7 @@ const struct xfs_btree_ops xfs_rtrmapbt_ops = {
	.free_block		= xfs_btree_free_metafile_block,
	.get_minrecs		= xfs_rtrmapbt_get_minrecs,
	.get_maxrecs		= xfs_rtrmapbt_get_maxrecs,
	.get_dmaxrecs		= xfs_rtrmapbt_get_dmaxrecs,
	.init_key_from_rec	= xfs_rtrmapbt_init_key_from_rec,
	.init_high_key_from_rec	= xfs_rtrmapbt_init_high_key_from_rec,
	.init_rec_from_cur	= xfs_rtrmapbt_init_rec_from_cur,
@@ -398,6 +513,7 @@ const struct xfs_btree_ops xfs_rtrmapbt_ops = {
	.keys_inorder		= xfs_rtrmapbt_keys_inorder,
	.recs_inorder		= xfs_rtrmapbt_recs_inorder,
	.keys_contiguous	= xfs_rtrmapbt_keys_contiguous,
	.broot_realloc		= xfs_rtrmapbt_broot_realloc,
};

/* Allocate a new rt rmap btree cursor. */
@@ -581,3 +697,138 @@ xfs_rtrmapbt_calc_reserves(
	return max_t(xfs_filblks_t, blocks / 100,
			xfs_rtrmapbt_max_size(mp, blocks));
}

/* Convert on-disk form of btree root to in-memory form. */
STATIC void
xfs_rtrmapbt_from_disk(
	struct xfs_inode	*ip,
	struct xfs_rtrmap_root	*dblock,
	unsigned int		dblocklen,
	struct xfs_btree_block	*rblock)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_rmap_key	*fkp;
	__be64			*fpp;
	struct xfs_rmap_key	*tkp;
	__be64			*tpp;
	struct xfs_rmap_rec	*frp;
	struct xfs_rmap_rec	*trp;
	unsigned int		rblocklen = xfs_rtrmap_broot_space(mp, dblock);
	unsigned int		numrecs;
	unsigned int		maxrecs;

	xfs_btree_init_block(mp, rblock, &xfs_rtrmapbt_ops, 0, 0, ip->i_ino);

	rblock->bb_level = dblock->bb_level;
	rblock->bb_numrecs = dblock->bb_numrecs;
	numrecs = be16_to_cpu(dblock->bb_numrecs);

	if (be16_to_cpu(rblock->bb_level) > 0) {
		maxrecs = xfs_rtrmapbt_droot_maxrecs(dblocklen, false);
		fkp = xfs_rtrmap_droot_key_addr(dblock, 1);
		tkp = xfs_rtrmap_key_addr(rblock, 1);
		fpp = xfs_rtrmap_droot_ptr_addr(dblock, 1, maxrecs);
		tpp = xfs_rtrmap_broot_ptr_addr(mp, rblock, 1, rblocklen);
		memcpy(tkp, fkp, 2 * sizeof(*fkp) * numrecs);
		memcpy(tpp, fpp, sizeof(*fpp) * numrecs);
	} else {
		frp = xfs_rtrmap_droot_rec_addr(dblock, 1);
		trp = xfs_rtrmap_rec_addr(rblock, 1);
		memcpy(trp, frp, sizeof(*frp) * numrecs);
	}
}

/* Load a realtime reverse mapping btree root in from disk. */
int
xfs_iformat_rtrmap(
	struct xfs_inode	*ip,
	struct xfs_dinode	*dip)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_rtrmap_root	*dfp = XFS_DFORK_PTR(dip, XFS_DATA_FORK);
	struct xfs_btree_block	*broot;
	unsigned int		numrecs;
	unsigned int		level;
	int			dsize;

	/*
	 * growfs must create the rtrmap inodes before adding a realtime volume
	 * to the filesystem, so we cannot use the rtrmapbt predicate here.
	 */
	if (!xfs_has_rmapbt(ip->i_mount))
		return -EFSCORRUPTED;

	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_rtrmap_maxlevels ||
	    xfs_rtrmap_droot_space_calc(level, numrecs) > dsize)
		return -EFSCORRUPTED;

	broot = xfs_broot_alloc(xfs_ifork_ptr(ip, XFS_DATA_FORK),
			xfs_rtrmap_broot_space_calc(mp, level, numrecs));
	if (broot)
		xfs_rtrmapbt_from_disk(ip, dfp, dsize, broot);
	return 0;
}

/* Convert in-memory form of btree root to on-disk form. */
void
xfs_rtrmapbt_to_disk(
	struct xfs_mount	*mp,
	struct xfs_btree_block	*rblock,
	unsigned int		rblocklen,
	struct xfs_rtrmap_root	*dblock,
	unsigned int		dblocklen)
{
	struct xfs_rmap_key	*fkp;
	__be64			*fpp;
	struct xfs_rmap_key	*tkp;
	__be64			*tpp;
	struct xfs_rmap_rec	*frp;
	struct xfs_rmap_rec	*trp;
	unsigned int		numrecs;
	unsigned int		maxrecs;

	ASSERT(rblock->bb_magic == cpu_to_be32(XFS_RTRMAP_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;
	numrecs = be16_to_cpu(rblock->bb_numrecs);

	if (be16_to_cpu(rblock->bb_level) > 0) {
		maxrecs = xfs_rtrmapbt_droot_maxrecs(dblocklen, false);
		fkp = xfs_rtrmap_key_addr(rblock, 1);
		tkp = xfs_rtrmap_droot_key_addr(dblock, 1);
		fpp = xfs_rtrmap_broot_ptr_addr(mp, rblock, 1, rblocklen);
		tpp = xfs_rtrmap_droot_ptr_addr(dblock, 1, maxrecs);
		memcpy(tkp, fkp, 2 * sizeof(*fkp) * numrecs);
		memcpy(tpp, fpp, sizeof(*fpp) * numrecs);
	} else {
		frp = xfs_rtrmap_rec_addr(rblock, 1);
		trp = xfs_rtrmap_droot_rec_addr(dblock, 1);
		memcpy(trp, frp, sizeof(*frp) * numrecs);
	}
}

/* Flush a realtime reverse mapping btree root out to disk. */
void
xfs_iflush_rtrmap(
	struct xfs_inode	*ip,
	struct xfs_dinode	*dip)
{
	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
	struct xfs_rtrmap_root	*dfp = XFS_DFORK_PTR(dip, XFS_DATA_FORK);

	ASSERT(ifp->if_broot != NULL);
	ASSERT(ifp->if_broot_bytes > 0);
	ASSERT(xfs_rtrmap_droot_space(ifp->if_broot) <=
			xfs_inode_fork_size(ip, XFS_DATA_FORK));
	xfs_rtrmapbt_to_disk(ip->i_mount, ifp->if_broot, ifp->if_broot_bytes,
			dfp, XFS_DFORK_SIZE(dip, ip->i_mount, XFS_DATA_FORK));
}
+112 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ void xfs_rtrmapbt_commit_staged_btree(struct xfs_btree_cur *cur,
unsigned int xfs_rtrmapbt_maxrecs(struct xfs_mount *mp, unsigned int blocklen,
		bool leaf);
void xfs_rtrmapbt_compute_maxlevels(struct xfs_mount *mp);
unsigned int xfs_rtrmapbt_droot_maxrecs(unsigned int blocklen, bool leaf);

/*
 * Addresses of records, keys, and pointers within an incore rtrmapbt block.
@@ -81,4 +82,115 @@ void xfs_rtrmapbt_destroy_cur_cache(void);

xfs_filblks_t xfs_rtrmapbt_calc_reserves(struct xfs_mount *mp);

/* Addresses of key, pointers, and records within an ondisk rtrmapbt block. */

static inline struct xfs_rmap_rec *
xfs_rtrmap_droot_rec_addr(
	struct xfs_rtrmap_root	*block,
	unsigned int		index)
{
	return (struct xfs_rmap_rec *)
		((char *)(block + 1) +
		 (index - 1) * sizeof(struct xfs_rmap_rec));
}

static inline struct xfs_rmap_key *
xfs_rtrmap_droot_key_addr(
	struct xfs_rtrmap_root	*block,
	unsigned int		index)
{
	return (struct xfs_rmap_key *)
		((char *)(block + 1) +
		 (index - 1) * 2 * sizeof(struct xfs_rmap_key));
}

static inline xfs_rtrmap_ptr_t *
xfs_rtrmap_droot_ptr_addr(
	struct xfs_rtrmap_root	*block,
	unsigned int		index,
	unsigned int		maxrecs)
{
	return (xfs_rtrmap_ptr_t *)
		((char *)(block + 1) +
		 maxrecs * 2 * sizeof(struct xfs_rmap_key) +
		 (index - 1) * sizeof(xfs_rtrmap_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_rtrmap_ptr_t *
xfs_rtrmap_broot_ptr_addr(
	struct xfs_mount	*mp,
	struct xfs_btree_block	*bb,
	unsigned int		index,
	unsigned int		block_size)
{
	return xfs_rtrmap_ptr_addr(bb, index,
			xfs_rtrmapbt_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_rtrmap_broot_space_calc(
	struct xfs_mount	*mp,
	unsigned int		level,
	unsigned int		nrecs)
{
	size_t			sz = XFS_RTRMAP_BLOCK_LEN;

	if (level > 0)
		return sz + nrecs * (2 * sizeof(struct xfs_rmap_key) +
					 sizeof(xfs_rtrmap_ptr_t));
	return sz + nrecs * sizeof(struct xfs_rmap_rec);
}

/*
 * Compute the space required for the incore btree root given the ondisk
 * btree root block.
 */
static inline size_t
xfs_rtrmap_broot_space(struct xfs_mount *mp, struct xfs_rtrmap_root *bb)
{
	return xfs_rtrmap_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_rtrmap_droot_space_calc(
	unsigned int		level,
	unsigned int		nrecs)
{
	size_t			sz = sizeof(struct xfs_rtrmap_root);

	if (level > 0)
		return sz + nrecs * (2 * sizeof(struct xfs_rmap_key) +
					 sizeof(xfs_rtrmap_ptr_t));
	return sz + nrecs * sizeof(struct xfs_rmap_rec);
}

/*
 * Compute the space required for the ondisk root block given an incore root
 * block.
 */
static inline size_t
xfs_rtrmap_droot_space(struct xfs_btree_block *bb)
{
	return xfs_rtrmap_droot_space_calc(be16_to_cpu(bb->bb_level),
			be16_to_cpu(bb->bb_numrecs));
}

int xfs_iformat_rtrmap(struct xfs_inode *ip, struct xfs_dinode *dip);
void xfs_rtrmapbt_to_disk(struct xfs_mount *mp, struct xfs_btree_block *rblock,
		unsigned int rblocklen, struct xfs_rtrmap_root *dblock,
		unsigned int dblocklen);
void xfs_iflush_rtrmap(struct xfs_inode *ip, struct xfs_dinode *dip);

#endif /* __XFS_RTRMAP_BTREE_H__ */
Loading