mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 13:11:40 +00:00
xfs: hold xfs_buf locked between shortform->leaf conversion and the addition of an attribute
The new attribute leaf buffer is not held locked across the transaction roll between the shortform->leaf modification and the addition of the new entry. As a result, the attribute buffer modification being made is not atomic from an operational perspective. Hence the AIL push can grab it in the transient state of "just created" after the initial transaction is rolled, because the buffer has been released. This leads to xfs_attr3_leaf_verify() asserting that hdr.count is zero, treating this as in-memory corruption, and shutting down the filesystem. Darrick ported the original patch to 4.15 and reworked it use the xfs_defer_bjoin helper and hold/join the buffer correctly across the second transaction roll. Signed-off-by: Alex Lyakas <alex@zadarastorage.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
b7b2846fe2
commit
6e643cd094
@ -212,6 +212,7 @@ xfs_attr_set(
|
||||
int flags)
|
||||
{
|
||||
struct xfs_mount *mp = dp->i_mount;
|
||||
struct xfs_buf *leaf_bp = NULL;
|
||||
struct xfs_da_args args;
|
||||
struct xfs_defer_ops dfops;
|
||||
struct xfs_trans_res tres;
|
||||
@ -327,9 +328,16 @@ xfs_attr_set(
|
||||
* GROT: another possible req'mt for a double-split btree op.
|
||||
*/
|
||||
xfs_defer_init(args.dfops, args.firstblock);
|
||||
error = xfs_attr_shortform_to_leaf(&args);
|
||||
error = xfs_attr_shortform_to_leaf(&args, &leaf_bp);
|
||||
if (error)
|
||||
goto out_defer_cancel;
|
||||
/*
|
||||
* Prevent the leaf buffer from being unlocked so that a
|
||||
* concurrent AIL push cannot grab the half-baked leaf
|
||||
* buffer and run into problems with the write verifier.
|
||||
*/
|
||||
xfs_trans_bhold(args.trans, leaf_bp);
|
||||
xfs_defer_bjoin(args.dfops, leaf_bp);
|
||||
xfs_defer_ijoin(args.dfops, dp);
|
||||
error = xfs_defer_finish(&args.trans, args.dfops);
|
||||
if (error)
|
||||
@ -337,13 +345,14 @@ xfs_attr_set(
|
||||
|
||||
/*
|
||||
* Commit the leaf transformation. We'll need another (linked)
|
||||
* transaction to add the new attribute to the leaf.
|
||||
* transaction to add the new attribute to the leaf, which
|
||||
* means that we have to hold & join the leaf buffer here too.
|
||||
*/
|
||||
|
||||
error = xfs_trans_roll_inode(&args.trans, dp);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
xfs_trans_bjoin(args.trans, leaf_bp);
|
||||
leaf_bp = NULL;
|
||||
}
|
||||
|
||||
if (xfs_bmap_one_block(dp, XFS_ATTR_FORK))
|
||||
@ -374,8 +383,9 @@ xfs_attr_set(
|
||||
|
||||
out_defer_cancel:
|
||||
xfs_defer_cancel(&dfops);
|
||||
args.trans = NULL;
|
||||
out:
|
||||
if (leaf_bp)
|
||||
xfs_trans_brelse(args.trans, leaf_bp);
|
||||
if (args.trans)
|
||||
xfs_trans_cancel(args.trans);
|
||||
xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
||||
|
@ -735,10 +735,13 @@ xfs_attr_shortform_getvalue(xfs_da_args_t *args)
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert from using the shortform to the leaf.
|
||||
* Convert from using the shortform to the leaf. On success, return the
|
||||
* buffer so that we can keep it locked until we're totally done with it.
|
||||
*/
|
||||
int
|
||||
xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
|
||||
xfs_attr_shortform_to_leaf(
|
||||
struct xfs_da_args *args,
|
||||
struct xfs_buf **leaf_bp)
|
||||
{
|
||||
xfs_inode_t *dp;
|
||||
xfs_attr_shortform_t *sf;
|
||||
@ -818,7 +821,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
|
||||
sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
|
||||
}
|
||||
error = 0;
|
||||
|
||||
*leaf_bp = bp;
|
||||
out:
|
||||
kmem_free(tmpbuffer);
|
||||
return error;
|
||||
|
@ -48,7 +48,8 @@ void xfs_attr_shortform_create(struct xfs_da_args *args);
|
||||
void xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
|
||||
int xfs_attr_shortform_lookup(struct xfs_da_args *args);
|
||||
int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
|
||||
int xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
|
||||
int xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
|
||||
struct xfs_buf **leaf_bp);
|
||||
int xfs_attr_shortform_remove(struct xfs_da_args *args);
|
||||
int xfs_attr_shortform_allfit(struct xfs_buf *bp, struct xfs_inode *dp);
|
||||
int xfs_attr_shortform_bytesfit(struct xfs_inode *dp, int bytes);
|
||||
|
Loading…
Reference in New Issue
Block a user