diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2018-05-14 15:34:34 +0200 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-05-16 03:12:50 +0200 |
commit | ddd10c2fe20e7ca6d11ddf84f905edba080b26a7 (patch) | |
tree | 4959baa41dc5019a27a59d55699a573cfe9afd21 /fs/xfs/scrub/parent.c | |
parent | xfs: scrub the data fork of the realtime inodes (diff) | |
download | linux-ddd10c2fe20e7ca6d11ddf84f905edba080b26a7.tar.xz linux-ddd10c2fe20e7ca6d11ddf84f905edba080b26a7.zip |
xfs: avoid ABBA deadlock when scrubbing parent pointers
In normal operation, the XFS convention is to take an inode's iolock
and then allocate a transaction. However, when scrubbing parent inodes
this is inverted -- we allocated the transaction to do the scrub, and
now we're trying to grab the parent's iolock. This can lead to ABBA
deadlocks: some thread grabbed the parent's iolock and is waiting for
space for a transaction while our parent scrubber is sitting on a
transaction trying to get the parent's iolock.
Therefore, convert all iolock attempts to use trylock; if that fails,
they can use the existing mechanisms to back off and try again.
The ABBA deadlock didn't happen with a non-repair scrub because the
transactions don't reserve any space, but repair scrubs require
reservation in order to update metadata. However, any other concurrent
metadata update (e.g. directory create in the parent) could also induce
this deadlock with the parent scrubber.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Diffstat (limited to 'fs/xfs/scrub/parent.c')
-rw-r--r-- | fs/xfs/scrub/parent.c | 16 |
1 files changed, 14 insertions, 2 deletions
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c index fc336807e156..77c6b22c6bfd 100644 --- a/fs/xfs/scrub/parent.c +++ b/fs/xfs/scrub/parent.c @@ -214,7 +214,9 @@ xfs_scrub_parent_validate( */ xfs_iunlock(sc->ip, sc->ilock_flags); sc->ilock_flags = 0; - xfs_ilock(dp, XFS_IOLOCK_SHARED); + error = xfs_scrub_ilock_inverted(dp, XFS_IOLOCK_SHARED); + if (error) + goto out_rele; /* Go looking for our dentry. */ error = xfs_scrub_parent_count_parent_dentries(sc, dp, &nlink); @@ -223,8 +225,10 @@ xfs_scrub_parent_validate( /* Drop the parent lock, relock this inode. */ xfs_iunlock(dp, XFS_IOLOCK_SHARED); + error = xfs_scrub_ilock_inverted(sc->ip, XFS_IOLOCK_EXCL); + if (error) + goto out_rele; sc->ilock_flags = XFS_IOLOCK_EXCL; - xfs_ilock(sc->ip, sc->ilock_flags); /* * If we're an unlinked directory, the parent /won't/ have a link @@ -326,5 +330,13 @@ xfs_scrub_parent( if (try_again && tries == 20) xfs_scrub_set_incomplete(sc); out: + /* + * If we failed to lock the parent inode even after a retry, just mark + * this scrub incomplete and return. + */ + if (sc->try_harder && error == -EDEADLOCK) { + error = 0; + xfs_scrub_set_incomplete(sc); + } return error; } |