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

xfs: hook live rmap operations during a repair operation



Hook the regular rmap code when an rmapbt repair operation is running so
that we can unlock the AGF buffer to scan the filesystem and keep the
in-memory btree up to date during the scan.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 4787fc80
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -417,6 +417,7 @@ xfs_initialize_perag(
		init_waitqueue_head(&pag->pag_active_wq);
		pag->pagb_count = 0;
		pag->pagb_tree = RB_ROOT;
		xfs_hooks_init(&pag->pag_rmap_update_hooks);
#endif /* __KERNEL__ */

		error = xfs_buf_cache_init(&pag->pag_bcache);
+3 −0
Original line number Diff line number Diff line
@@ -120,6 +120,9 @@ struct xfs_perag {
	 * inconsistencies.
	 */
	struct xfs_defer_drain	pag_intents_drain;

	/* Hook to feed rmapbt updates to an active online repair. */
	struct xfs_hooks	pag_rmap_update_hooks;
#endif /* __KERNEL__ */
};

+121 −33
Original line number Diff line number Diff line
@@ -821,6 +821,86 @@ xfs_rmap_unmap(
	return error;
}

#ifdef CONFIG_XFS_LIVE_HOOKS
/*
 * Use a static key here to reduce the overhead of rmapbt live updates.  If
 * the compiler supports jump labels, the static branch will be replaced by a
 * nop sled when there are no hook users.  Online fsck is currently the only
 * caller, so this is a reasonable tradeoff.
 *
 * Note: Patching the kernel code requires taking the cpu hotplug lock.  Other
 * parts of the kernel allocate memory with that lock held, which means that
 * XFS callers cannot hold any locks that might be used by memory reclaim or
 * writeback when calling the static_branch_{inc,dec} functions.
 */
DEFINE_STATIC_XFS_HOOK_SWITCH(xfs_rmap_hooks_switch);

void
xfs_rmap_hook_disable(void)
{
	xfs_hooks_switch_off(&xfs_rmap_hooks_switch);
}

void
xfs_rmap_hook_enable(void)
{
	xfs_hooks_switch_on(&xfs_rmap_hooks_switch);
}

/* Call downstream hooks for a reverse mapping update. */
static inline void
xfs_rmap_update_hook(
	struct xfs_trans		*tp,
	struct xfs_perag		*pag,
	enum xfs_rmap_intent_type	op,
	xfs_agblock_t			startblock,
	xfs_extlen_t			blockcount,
	bool				unwritten,
	const struct xfs_owner_info	*oinfo)
{
	if (xfs_hooks_switched_on(&xfs_rmap_hooks_switch)) {
		struct xfs_rmap_update_params	p = {
			.startblock	= startblock,
			.blockcount	= blockcount,
			.unwritten	= unwritten,
			.oinfo		= *oinfo, /* struct copy */
		};

		if (pag)
			xfs_hooks_call(&pag->pag_rmap_update_hooks, op, &p);
	}
}

/* Call the specified function during a reverse mapping update. */
int
xfs_rmap_hook_add(
	struct xfs_perag	*pag,
	struct xfs_rmap_hook	*hook)
{
	return xfs_hooks_add(&pag->pag_rmap_update_hooks, &hook->rmap_hook);
}

/* Stop calling the specified function during a reverse mapping update. */
void
xfs_rmap_hook_del(
	struct xfs_perag	*pag,
	struct xfs_rmap_hook	*hook)
{
	xfs_hooks_del(&pag->pag_rmap_update_hooks, &hook->rmap_hook);
}

/* Configure rmap update hook functions. */
void
xfs_rmap_hook_setup(
	struct xfs_rmap_hook	*hook,
	notifier_fn_t		mod_fn)
{
	xfs_hook_setup(&hook->rmap_hook, mod_fn);
}
#else
# define xfs_rmap_update_hook(t, p, o, s, b, u, oi)	do { } while (0)
#endif /* CONFIG_XFS_LIVE_HOOKS */

/*
 * Remove a reference to an extent in the rmap btree.
 */
@@ -841,7 +921,7 @@ xfs_rmap_free(
		return 0;

	cur = xfs_rmapbt_init_cursor(mp, tp, agbp, pag);

	xfs_rmap_update_hook(tp, pag, XFS_RMAP_UNMAP, bno, len, false, oinfo);
	error = xfs_rmap_unmap(cur, bno, len, false, oinfo);

	xfs_btree_del_cursor(cur, error);
@@ -1093,6 +1173,7 @@ xfs_rmap_alloc(
		return 0;

	cur = xfs_rmapbt_init_cursor(mp, tp, agbp, pag);
	xfs_rmap_update_hook(tp, pag, XFS_RMAP_MAP, bno, len, false, oinfo);
	error = xfs_rmap_map(cur, bno, len, false, oinfo);

	xfs_btree_del_cursor(cur, error);
@@ -2508,6 +2589,38 @@ xfs_rmap_finish_one_cleanup(
		xfs_trans_brelse(tp, agbp);
}

/* Commit an rmap operation into the ondisk tree. */
int
__xfs_rmap_finish_intent(
	struct xfs_btree_cur		*rcur,
	enum xfs_rmap_intent_type	op,
	xfs_agblock_t			bno,
	xfs_extlen_t			len,
	const struct xfs_owner_info	*oinfo,
	bool				unwritten)
{
	switch (op) {
	case XFS_RMAP_ALLOC:
	case XFS_RMAP_MAP:
		return xfs_rmap_map(rcur, bno, len, unwritten, oinfo);
	case XFS_RMAP_MAP_SHARED:
		return xfs_rmap_map_shared(rcur, bno, len, unwritten, oinfo);
	case XFS_RMAP_FREE:
	case XFS_RMAP_UNMAP:
		return xfs_rmap_unmap(rcur, bno, len, unwritten, oinfo);
	case XFS_RMAP_UNMAP_SHARED:
		return xfs_rmap_unmap_shared(rcur, bno, len, unwritten, oinfo);
	case XFS_RMAP_CONVERT:
		return xfs_rmap_convert(rcur, bno, len, !unwritten, oinfo);
	case XFS_RMAP_CONVERT_SHARED:
		return xfs_rmap_convert_shared(rcur, bno, len, !unwritten,
				oinfo);
	default:
		ASSERT(0);
		return -EFSCORRUPTED;
	}
}

/*
 * Process one of the deferred rmap operations.  We pass back the
 * btree cursor to maintain our lock on the rmapbt between calls.
@@ -2574,39 +2687,14 @@ xfs_rmap_finish_one(
	unwritten = ri->ri_bmap.br_state == XFS_EXT_UNWRITTEN;
	bno = XFS_FSB_TO_AGBNO(rcur->bc_mp, ri->ri_bmap.br_startblock);

	switch (ri->ri_type) {
	case XFS_RMAP_ALLOC:
	case XFS_RMAP_MAP:
		error = xfs_rmap_map(rcur, bno, ri->ri_bmap.br_blockcount,
				unwritten, &oinfo);
		break;
	case XFS_RMAP_MAP_SHARED:
		error = xfs_rmap_map_shared(rcur, bno,
				ri->ri_bmap.br_blockcount, unwritten, &oinfo);
		break;
	case XFS_RMAP_FREE:
	case XFS_RMAP_UNMAP:
		error = xfs_rmap_unmap(rcur, bno, ri->ri_bmap.br_blockcount,
				unwritten, &oinfo);
		break;
	case XFS_RMAP_UNMAP_SHARED:
		error = xfs_rmap_unmap_shared(rcur, bno,
				ri->ri_bmap.br_blockcount, unwritten, &oinfo);
		break;
	case XFS_RMAP_CONVERT:
		error = xfs_rmap_convert(rcur, bno, ri->ri_bmap.br_blockcount,
				!unwritten, &oinfo);
		break;
	case XFS_RMAP_CONVERT_SHARED:
		error = xfs_rmap_convert_shared(rcur, bno,
				ri->ri_bmap.br_blockcount, !unwritten, &oinfo);
		break;
	default:
		ASSERT(0);
		error = -EFSCORRUPTED;
	}

	error = __xfs_rmap_finish_intent(rcur, ri->ri_type, bno,
			ri->ri_bmap.br_blockcount, &oinfo, unwritten);
	if (error)
		return error;

	xfs_rmap_update_hook(tp, ri->ri_pag, ri->ri_type, bno,
			ri->ri_bmap.br_blockcount, unwritten, &oinfo);
	return 0;
}

/*
+29 −0
Original line number Diff line number Diff line
@@ -186,6 +186,10 @@ void xfs_rmap_finish_one_cleanup(struct xfs_trans *tp,
		struct xfs_btree_cur *rcur, int error);
int xfs_rmap_finish_one(struct xfs_trans *tp, struct xfs_rmap_intent *ri,
		struct xfs_btree_cur **pcur);
int __xfs_rmap_finish_intent(struct xfs_btree_cur *rcur,
		enum xfs_rmap_intent_type op, xfs_agblock_t bno,
		xfs_extlen_t len, const struct xfs_owner_info *oinfo,
		bool unwritten);

int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
		uint64_t owner, uint64_t offset, unsigned int flags,
@@ -235,4 +239,29 @@ extern struct kmem_cache *xfs_rmap_intent_cache;
int __init xfs_rmap_intent_init_cache(void);
void xfs_rmap_intent_destroy_cache(void);

/*
 * Parameters for tracking reverse mapping changes.  The hook function arg
 * parameter is enum xfs_rmap_intent_type, and the rest is below.
 */
struct xfs_rmap_update_params {
	xfs_agblock_t			startblock;
	xfs_extlen_t			blockcount;
	struct xfs_owner_info		oinfo;
	bool				unwritten;
};

#ifdef CONFIG_XFS_LIVE_HOOKS

struct xfs_rmap_hook {
	struct xfs_hook			rmap_hook;
};

void xfs_rmap_hook_disable(void);
void xfs_rmap_hook_enable(void);

int xfs_rmap_hook_add(struct xfs_perag *pag, struct xfs_rmap_hook *hook);
void xfs_rmap_hook_del(struct xfs_perag *pag, struct xfs_rmap_hook *hook);
void xfs_rmap_hook_setup(struct xfs_rmap_hook *hook, notifier_fn_t mod_fn);
#endif

#endif	/* __XFS_RMAP_H__ */
+3 −0
Original line number Diff line number Diff line
@@ -1309,6 +1309,9 @@ xchk_fsgates_enable(
	if (scrub_fsgates & XCHK_FSGATES_DIRENTS)
		xfs_dir_hook_enable();

	if (scrub_fsgates & XCHK_FSGATES_RMAP)
		xfs_rmap_hook_enable();

	sc->flags |= scrub_fsgates;
}

Loading