summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub/parent.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/xfs/scrub/parent.c')
-rw-r--r--fs/xfs/scrub/parent.c49
1 files changed, 34 insertions, 15 deletions
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index 9dbfa4585167..5e11d5141d19 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -16,6 +16,7 @@
#include "xfs_dir2_priv.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
+#include "scrub/parent.h"
/* Set us up to scrub parents. */
int
@@ -121,6 +122,36 @@ out:
}
/*
+ * Try to iolock the parent dir @dp in shared mode and the child dir @sc->ip
+ * exclusively.
+ */
+int
+xchk_parent_lock_two_dirs(
+ struct xfs_scrub *sc,
+ struct xfs_inode *dp)
+{
+ int error = 0;
+
+ /* Callers shouldn't do this, but protect ourselves anyway. */
+ if (dp == sc->ip) {
+ ASSERT(dp != sc->ip);
+ return -EDEADLOCK;
+ }
+
+ xchk_iunlock(sc, sc->ilock_flags);
+ while (true) {
+ if (xchk_should_terminate(sc, &error))
+ return error;
+ xfs_ilock(dp, XFS_IOLOCK_SHARED);
+ if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
+ break;
+ xfs_iunlock(dp, XFS_IOLOCK_SHARED);
+ }
+
+ return 0;
+}
+
+/*
* Given the inode number of the alleged parent of the inode being
* scrubbed, try to validate that the parent has exactly one directory
* entry pointing back to the inode being scrubbed.
@@ -185,21 +216,9 @@ xchk_parent_validate(
* try to lock the alleged parent and trylock the child.
*/
if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
- xchk_iunlock(sc, sc->ilock_flags);
- while (true) {
- if (xchk_should_terminate(sc, &error))
- goto out_rele;
- xfs_ilock(dp, XFS_IOLOCK_SHARED);
- if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
- break;
- xfs_iunlock(dp, XFS_IOLOCK_SHARED);
- }
-
- /*
- * Now that we've locked out updates to the child directory,
- * re-sample the expected nlink and the '..' dirent.
- */
- expected_nlink = VFS_I(sc->ip)->i_nlink == 0 ? 0 : 1;
+ error = xchk_parent_lock_two_dirs(sc, dp);
+ if (error)
+ goto out_rele;
error = xfs_dir_lookup(sc->tp, sc->ip, &xfs_name_dotdot,
&parent_ino, NULL);