Unverified Commit 6494f4c7 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!9876 v3 Fix xfs file creation issue

Merge Pull Request from: @ci-robot 
 
PR sync from: Zizhi Wo <wozizhi@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/OJOPCKDXS4X63WCCKLFSR5OSKJJCBHLN/ 
V3: Add a fix tag that was missing from the second patch.

V2: Add a fix tag that was missing from the first patch.

V1: Fix main problem.

Zizhi Wo (2):
  Revert "xfs: Fix file creation failure"
  xfs: Avoid races with cnt_btree lastrec updates


-- 
2.39.2
 
https://gitee.com/openeuler/kernel/issues/I9TDTA 
 
Link:https://gitee.com/openeuler/kernel/pulls/9876

 

Reviewed-by: default avatarZhang Peng <zhangpeng362@huawei.com>
Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 57aa1dda 69fd5583
Loading
Loading
Loading
Loading
+113 −12
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "xfs_ag.h"
#include "xfs_ag_resv.h"
#include "xfs_bmap.h"
#include "xfs_health.h"

struct kmem_cache	*xfs_extfree_item_cache;

@@ -484,6 +485,97 @@ xfs_alloc_fix_len(
	args->len = rlen;
}

/*
 * Determine if the cursor points to the block that contains the right-most
 * block of records in the by-count btree. This block contains the largest
 * contiguous free extent in the AG, so if we modify a record in this block we
 * need to call xfs_alloc_fixup_longest() once the modifications are done to
 * ensure the agf->agf_longest field is kept up to date with the longest free
 * extent tracked by the by-count btree.
 */
static bool
xfs_alloc_cursor_at_lastrec(
	struct xfs_btree_cur	*cnt_cur)
{
	struct xfs_btree_block	*block;
	union xfs_btree_ptr	ptr;
	struct xfs_buf		*bp;

	block = xfs_btree_get_block(cnt_cur, 0, &bp);

	xfs_btree_get_sibling(cnt_cur, block, &ptr, XFS_BB_RIGHTSIB);
	return xfs_btree_ptr_is_null(cnt_cur, &ptr);
}

/*
 * Find the rightmost record of the cntbt, and return the longest free space
 * recorded in it. Simply set both the block number and the length to their
 * maximum values before searching.
 */
static int
xfs_cntbt_longest(
	struct xfs_btree_cur	*cnt_cur,
	xfs_extlen_t		*longest)
{
	struct xfs_alloc_rec_incore irec;
	union xfs_btree_rec	    *rec;
	int			    stat = 0;
	int			    error;

	memset(&cnt_cur->bc_rec, 0xFF, sizeof(cnt_cur->bc_rec));
	error = xfs_btree_lookup(cnt_cur, XFS_LOOKUP_LE, &stat);
	if (error)
		return error;
	if (!stat) {
		/* totally empty tree */
		*longest = 0;
		return 0;
	}

	error = xfs_btree_get_rec(cnt_cur, &rec, &stat);
	if (error)
		return error;
	if (XFS_IS_CORRUPT(cnt_cur->bc_mp, !stat)) {
		xfs_ag_mark_sick(cnt_cur->bc_ag.pag, XFS_BTNUM_CNT);
		return -EFSCORRUPTED;
	}

	xfs_alloc_btrec_to_irec(rec, &irec);
	*longest = irec.ar_blockcount;
	return 0;
}

/*
 * Update the longest contiguous free extent in the AG from the by-count cursor
 * that is passed to us. This should be done at the end of any allocation or
 * freeing operation that touches the longest extent in the btree.
 *
 * Needing to update the longest extent can be determined by calling
 * xfs_alloc_cursor_at_lastrec() after the cursor is positioned for record
 * modification but before the modification begins.
 */
static int
xfs_alloc_fixup_longest(
	struct xfs_btree_cur	*cnt_cur)
{
	struct xfs_perag	*pag = cnt_cur->bc_ag.pag;
	struct xfs_buf		*bp = cnt_cur->bc_ag.agbp;
	struct xfs_agf		*agf = bp->b_addr;
	xfs_extlen_t		longest = 0;
	int			error;

	/* Lookup last rec in order to update AGF. */
	error = xfs_cntbt_longest(cnt_cur, &longest);
	if (error)
		return error;

	pag->pagf_longest = longest;
	agf->agf_longest = cpu_to_be32(pag->pagf_longest);
	xfs_alloc_log_agf(cnt_cur->bc_tp, bp, XFS_AGF_LONGEST);

	return 0;
}

/*
 * Update the two btrees, logically removing from freespace the extent
 * starting at rbno, rlen blocks.  The extent is contained within the
@@ -508,6 +600,7 @@ xfs_alloc_fixup_trees(
	xfs_extlen_t	nflen1=0;	/* first new free length */
	xfs_extlen_t	nflen2=0;	/* second new free length */
	struct xfs_mount *mp;
	bool		fixup_longest = false;

	mp = cnt_cur->bc_mp;

@@ -587,11 +680,8 @@ xfs_alloc_fixup_trees(
		nflen2 = (fbno + flen) - nfbno2;
	}

	/*
	 * Record the potential maximum free length in advance.
	 */
	if (nfbno1 != NULLAGBLOCK || nfbno2 != NULLAGBLOCK)
		cnt_cur->bc_ag.bc_free_longest = XFS_EXTLEN_MAX(nflen1, nflen2);
	if (xfs_alloc_cursor_at_lastrec(cnt_cur))
		fixup_longest = true;

	/*
	 * Delete the entry from the by-size btree.
@@ -654,6 +744,10 @@ xfs_alloc_fixup_trees(
		if (XFS_IS_CORRUPT(mp, i != 1))
			return -EFSCORRUPTED;
	}

	if (fixup_longest)
		return xfs_alloc_fixup_longest(cnt_cur);

	return 0;
}

@@ -1942,6 +2036,7 @@ xfs_free_ag_extent(
	int				i;
	int				error;
	struct xfs_perag		*pag = agbp->b_pag;
	bool				fixup_longest = false;

	bno_cur = cnt_cur = NULL;
	mp = tp->t_mountp;
@@ -2026,13 +2121,6 @@ xfs_free_ag_extent(
	 * Now allocate and initialize a cursor for the by-size tree.
	 */
	cnt_cur = xfs_allocbt_init_cursor(mp, tp, agbp, pag, XFS_BTNUM_CNT);
	/*
	 * Record the potential maximum free length in advance.
	 */
	if (haveleft)
		cnt_cur->bc_ag.bc_free_longest = ltlen;
	if (haveright)
		cnt_cur->bc_ag.bc_free_longest = gtlen;
	/*
	 * Have both left and right contiguous neighbors.
	 * Merge all three into a single free block.
@@ -2195,8 +2283,13 @@ xfs_free_ag_extent(
	}
	xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
	bno_cur = NULL;

	/*
	 * In all cases we need to insert the new freespace in the by-size tree.
	 *
	 * If this new freespace is being inserted in the block that contains
	 * the largest free space in the btree, make sure we also fix up the
	 * agf->agf-longest tracker field.
	 */
	if ((error = xfs_alloc_lookup_eq(cnt_cur, nbno, nlen, &i)))
		goto error0;
@@ -2204,12 +2297,20 @@ xfs_free_ag_extent(
		error = -EFSCORRUPTED;
		goto error0;
	}
	if (xfs_alloc_cursor_at_lastrec(cnt_cur))
		fixup_longest = true;
	if ((error = xfs_btree_insert(cnt_cur, &i)))
		goto error0;
	if (XFS_IS_CORRUPT(mp, i != 1)) {
		error = -EFSCORRUPTED;
		goto error0;
	}
	if (fixup_longest) {
		error = xfs_alloc_fixup_longest(cnt_cur);
		if (error)
			goto error0;
	}

	xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
	cnt_cur = NULL;

+0 −72
Original line number Diff line number Diff line
@@ -100,74 +100,6 @@ xfs_allocbt_free_block(
	return 0;
}

/*
 * Update the longest extent in the AGF
 */
STATIC void
xfs_allocbt_update_lastrec(
	struct xfs_btree_cur		*cur,
	const struct xfs_btree_block	*block,
	const union xfs_btree_rec	*rec,
	int				ptr,
	int				reason)
{
	struct xfs_agf		*agf = cur->bc_ag.agbp->b_addr;
	struct xfs_perag	*pag;
	__be32			len;
	int			numrecs;

	ASSERT(cur->bc_btnum == XFS_BTNUM_CNT);

	switch (reason) {
	case LASTREC_UPDATE:
		/*
		 * If this is the last leaf block and it's the last record,
		 * then update the size of the longest extent in the AG.
		 */
		if (ptr != xfs_btree_get_numrecs(block))
			return;
		len = rec->alloc.ar_blockcount;
		break;
	case LASTREC_INSREC:
		if (be32_to_cpu(rec->alloc.ar_blockcount) <=
		    be32_to_cpu(agf->agf_longest))
			return;
		len = rec->alloc.ar_blockcount;
		break;
	case LASTREC_DELREC:
		numrecs = xfs_btree_get_numrecs(block);
		if (ptr <= numrecs)
			return;
		ASSERT(ptr == numrecs + 1);

		if (numrecs) {
			xfs_alloc_rec_t *rrp;

			rrp = XFS_ALLOC_REC_ADDR(cur->bc_mp, block, numrecs);
			len = rrp->ar_blockcount;
		} else {
			/*
			 * Update in advance to prevent file creation failure
			 * for concurrent processes even though there is no
			 * numrec currently.
			 * And there's no need to worry as the value that no
			 * less than bc_free_longest will be inserted later.
			 */
			len = cpu_to_be32(cur->bc_ag.bc_free_longest);
		}

		break;
	default:
		ASSERT(0);
		return;
	}

	agf->agf_longest = len;
	pag = cur->bc_ag.agbp->b_pag;
	pag->pagf_longest = be32_to_cpu(len);
	xfs_alloc_log_agf(cur->bc_tp, cur->bc_ag.agbp, XFS_AGF_LONGEST);
}

STATIC int
xfs_allocbt_get_minrecs(
	struct xfs_btree_cur	*cur,
@@ -458,7 +390,6 @@ static const struct xfs_btree_ops xfs_bnobt_ops = {
	.set_root		= xfs_allocbt_set_root,
	.alloc_block		= xfs_allocbt_alloc_block,
	.free_block		= xfs_allocbt_free_block,
	.update_lastrec		= xfs_allocbt_update_lastrec,
	.get_minrecs		= xfs_allocbt_get_minrecs,
	.get_maxrecs		= xfs_allocbt_get_maxrecs,
	.init_key_from_rec	= xfs_allocbt_init_key_from_rec,
@@ -481,7 +412,6 @@ static const struct xfs_btree_ops xfs_cntbt_ops = {
	.set_root		= xfs_allocbt_set_root,
	.alloc_block		= xfs_allocbt_alloc_block,
	.free_block		= xfs_allocbt_free_block,
	.update_lastrec		= xfs_allocbt_update_lastrec,
	.get_minrecs		= xfs_allocbt_get_minrecs,
	.get_maxrecs		= xfs_allocbt_get_maxrecs,
	.init_key_from_rec	= xfs_allocbt_init_key_from_rec,
@@ -515,7 +445,6 @@ xfs_allocbt_init_common(
	if (btnum == XFS_BTNUM_CNT) {
		cur->bc_ops = &xfs_cntbt_ops;
		cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_abtc_2);
		cur->bc_flags = XFS_BTREE_LASTREC_UPDATE;
	} else {
		cur->bc_ops = &xfs_bnobt_ops;
		cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_abtb_2);
@@ -591,7 +520,6 @@ xfs_allocbt_commit_staged_btree(
	if (cur->bc_btnum == XFS_BTNUM_BNO) {
		xfs_btree_commit_afakeroot(cur, tp, agbp, &xfs_bnobt_ops);
	} else {
		cur->bc_flags |= XFS_BTREE_LASTREC_UPDATE;
		xfs_btree_commit_afakeroot(cur, tp, agbp, &xfs_cntbt_ops);
	}
}
+0 −51
Original line number Diff line number Diff line
@@ -1235,30 +1235,6 @@ xfs_btree_init_block_cur(
				numrecs, owner, cur->bc_flags);
}

/*
 * Return true if ptr is the last record in the btree and
 * we need to track updates to this record.  The decision
 * will be further refined in the update_lastrec method.
 */
STATIC int
xfs_btree_is_lastrec(
	struct xfs_btree_cur	*cur,
	struct xfs_btree_block	*block,
	int			level)
{
	union xfs_btree_ptr	ptr;

	if (level > 0)
		return 0;
	if (!(cur->bc_flags & XFS_BTREE_LASTREC_UPDATE))
		return 0;

	xfs_btree_get_sibling(cur, block, &ptr, XFS_BB_RIGHTSIB);
	if (!xfs_btree_ptr_is_null(cur, &ptr))
		return 0;
	return 1;
}

STATIC void
xfs_btree_buf_to_ptr(
	struct xfs_btree_cur	*cur,
@@ -2286,15 +2262,6 @@ xfs_btree_update(
	xfs_btree_copy_recs(cur, rp, rec, 1);
	xfs_btree_log_recs(cur, bp, ptr, ptr);

	/*
	 * If we are tracking the last record in the tree and
	 * we are at the far right edge of the tree, update it.
	 */
	if (xfs_btree_is_lastrec(cur, block, 0)) {
		cur->bc_ops->update_lastrec(cur, block, rec,
					    ptr, LASTREC_UPDATE);
	}

	/* Pass new key value up to our parent. */
	if (xfs_btree_needs_key_update(cur, ptr)) {
		error = xfs_btree_update_keys(cur, 0);
@@ -3444,15 +3411,6 @@ xfs_btree_insrec(
			goto error0;
	}

	/*
	 * If we are tracking the last record in the tree and
	 * we are at the far right edge of the tree, update it.
	 */
	if (xfs_btree_is_lastrec(cur, block, level)) {
		cur->bc_ops->update_lastrec(cur, block, rec,
					    ptr, LASTREC_INSREC);
	}

	/*
	 * Return the new block number, if any.
	 * If there is one, give back a record value and a cursor too.
@@ -3808,15 +3766,6 @@ xfs_btree_delrec(
	xfs_btree_set_numrecs(block, --numrecs);
	xfs_btree_log_block(cur, bp, XFS_BB_NUMRECS);

	/*
	 * If we are tracking the last record in the tree and
	 * we are at the far right edge of the tree, update it.
	 */
	if (xfs_btree_is_lastrec(cur, block, level)) {
		cur->bc_ops->update_lastrec(cur, block, NULL,
					    ptr, LASTREC_DELREC);
	}

	/*
	 * We're at the root level.  First, shrink the root block in-memory.
	 * Try to get rid of the next level down.  If we can't then there's
+2 −17
Original line number Diff line number Diff line
@@ -132,12 +132,6 @@ struct xfs_btree_ops {
			       int *stat);
	int	(*free_block)(struct xfs_btree_cur *cur, struct xfs_buf *bp);

	/* update last record information */
	void	(*update_lastrec)(struct xfs_btree_cur *cur,
				  const struct xfs_btree_block *block,
				  const union xfs_btree_rec *rec,
				  int ptr, int reason);

	/* records in block/level */
	int	(*get_minrecs)(struct xfs_btree_cur *cur, int level);
	int	(*get_maxrecs)(struct xfs_btree_cur *cur, int level);
@@ -199,13 +193,6 @@ struct xfs_btree_ops {
			       const union xfs_btree_key *mask);
};

/*
 * Reasons for the update_lastrec method to be called.
 */
#define LASTREC_UPDATE	0
#define LASTREC_INSREC	1
#define LASTREC_DELREC	2


union xfs_btree_irec {
	struct xfs_alloc_rec_incore	a;
@@ -218,7 +205,6 @@ union xfs_btree_irec {
/* Per-AG btree information. */
struct xfs_btree_cur_ag {
	struct xfs_perag		*pag;
	xfs_extlen_t			bc_free_longest; /* the potential longest free extent */
	union {
		struct xfs_buf		*agbp;
		struct xbtree_afakeroot	*afake;	/* for staging cursor */
@@ -308,9 +294,8 @@ xfs_btree_cur_sizeof(unsigned int nlevels)
/* cursor flags */
#define XFS_BTREE_LONG_PTRS		(1<<0)	/* pointers are 64bits long */
#define XFS_BTREE_ROOT_IN_INODE		(1<<1)	/* root may be variable size */
#define XFS_BTREE_LASTREC_UPDATE	(1<<2)	/* track last rec externally */
#define XFS_BTREE_CRC_BLOCKS		(1<<3)	/* uses extended btree blocks */
#define XFS_BTREE_OVERLAPPING		(1<<4)	/* overlapping intervals */
#define XFS_BTREE_CRC_BLOCKS		(1<<2)	/* uses extended btree blocks */
#define XFS_BTREE_OVERLAPPING		(1<<3)	/* overlapping intervals */
/*
 * The root of this btree is a fakeroot structure so that we can stage a btree
 * rebuild without leaving it accessible via primary metadata.  The ops struct