forked from TencentOS/TencentOS-kernel
xfs: Fix deadlock between AGI and AGF when target_ip exists in xfs_rename()
Backport from the mainline kernel: commit 93597ae8dac0149b5c00b787cba6bf7ba213e666 Fix deadlock between AGI and AGF when target_ip exists in xfs_rename(). Signed-off-by: Kaixu Xia <kaixuxia@tencent.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> [darrick: reword the comment] Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
parent
f8c4b7aac0
commit
44a72efebd
|
|
@ -139,6 +139,8 @@ extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
|
|||
struct xfs_name *name, xfs_ino_t ino,
|
||||
xfs_fsblock_t *first,
|
||||
struct xfs_defer_ops *dfops, xfs_extlen_t tot);
|
||||
extern bool xfs_dir2_sf_replace_needblock(struct xfs_inode *dp,
|
||||
xfs_ino_t inum);
|
||||
extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
|
||||
struct xfs_name *name, xfs_ino_t inum,
|
||||
xfs_fsblock_t *first,
|
||||
|
|
|
|||
|
|
@ -961,6 +961,27 @@ xfs_dir2_sf_removename(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the sf dir replace operation need more blocks.
|
||||
*/
|
||||
bool
|
||||
xfs_dir2_sf_replace_needblock(
|
||||
struct xfs_inode *dp,
|
||||
xfs_ino_t inum)
|
||||
{
|
||||
int newsize;
|
||||
struct xfs_dir2_sf_hdr *sfp;
|
||||
|
||||
if (dp->i_d.di_format != XFS_DINODE_FMT_LOCAL)
|
||||
return false;
|
||||
|
||||
sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data;
|
||||
newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF;
|
||||
|
||||
return inum > XFS_DIR2_MAX_SHORT_INUM &&
|
||||
sfp->i8count == 0 && newsize > XFS_IFORK_DSIZE(dp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace the inode number of an entry in a shortform directory.
|
||||
*/
|
||||
|
|
@ -997,17 +1018,14 @@ xfs_dir2_sf_replace(
|
|||
*/
|
||||
if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) {
|
||||
int error; /* error return value */
|
||||
int newsize; /* new inode size */
|
||||
|
||||
newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF;
|
||||
/*
|
||||
* Won't fit as shortform, convert to block then do replace.
|
||||
*/
|
||||
if (newsize > XFS_IFORK_DSIZE(dp)) {
|
||||
if (xfs_dir2_sf_replace_needblock(dp, args->inumber)) {
|
||||
error = xfs_dir2_sf_to_block(args);
|
||||
if (error) {
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return xfs_dir2_block_replace(args);
|
||||
}
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2947,6 +2947,7 @@ xfs_rename(
|
|||
xfs_fsblock_t first_block;
|
||||
struct xfs_inode *wip = NULL; /* whiteout inode */
|
||||
struct xfs_inode *inodes[__XFS_SORT_INODES];
|
||||
struct xfs_buf *agibp;
|
||||
int num_inodes = __XFS_SORT_INODES;
|
||||
bool new_parent = (src_dp != target_dp);
|
||||
bool src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
|
||||
|
|
@ -3116,6 +3117,22 @@ xfs_rename(
|
|||
* In case there is already an entry with the same
|
||||
* name at the destination directory, remove it first.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check whether the replace operation will need to allocate
|
||||
* blocks. This happens when the shortform directory lacks
|
||||
* space and we have to convert it to a block format directory.
|
||||
* When more blocks are necessary, we must lock the AGI first
|
||||
* to preserve locking order (AGI -> AGF).
|
||||
*/
|
||||
if (xfs_dir2_sf_replace_needblock(target_dp, src_ip->i_ino)) {
|
||||
error = xfs_read_agi(mp, tp,
|
||||
XFS_INO_TO_AGNO(mp, target_ip->i_ino),
|
||||
&agibp);
|
||||
if (error)
|
||||
goto out_trans_cancel;
|
||||
}
|
||||
|
||||
error = xfs_dir_replace(tp, target_dp, target_name,
|
||||
src_ip->i_ino,
|
||||
&first_block, &dfops, spaceres);
|
||||
|
|
|
|||
Loading…
Reference in New Issue