summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 10:59:07 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-12-15 17:29:03 -0800
commita11f10691dd5b792bf9b086d9ca61833418465c5 (patch)
tree87bd5ffd10a7d427aeb8087452e377fd98b508ec
parente30d5e3406845a5f94c0af728a34af015b7d3d92 (diff)
xfs: ask the dentry cache if it knows the parent of a directory
It's possible that the dentry cache can tell us the parent of a directory. Therefore, when repairing directory dot dot entries, query the dcache as a last resort before scanning the entire filesystem. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/scrub/dir_repair.c27
-rw-r--r--fs/xfs/scrub/parent.h1
-rw-r--r--fs/xfs/scrub/parent_repair.c45
-rw-r--r--fs/xfs/scrub/trace.h1
4 files changed, 74 insertions, 0 deletions
diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c
index cf3b03d902f5..e209c04f5c39 100644
--- a/fs/xfs/scrub/dir_repair.c
+++ b/fs/xfs/scrub/dir_repair.c
@@ -1110,6 +1110,29 @@ xrep_directory_scan_parent(
return parent_ino;
}
+/*
+ * Look up '..' in the dentry cache and confirm that it's really the parent.
+ * Returns NULLFSINO if the dcache misses or if the hit is implausible.
+ */
+static inline xfs_ino_t
+xrep_directory_dcache_parent(
+ struct xrep_dir *rd)
+{
+ struct xfs_scrub *sc = rd->sc;
+ xfs_ino_t parent_ino;
+ int error;
+
+ parent_ino = xrep_parent_from_dcache(sc);
+ if (parent_ino == NULLFSINO)
+ return parent_ino;
+
+ error = xrep_parent_confirm(sc, &parent_ino);
+ if (error)
+ return NULLFSINO;
+
+ return parent_ino;
+}
+
/* Try to find the parent of the directory being repaired. */
STATIC int
xrep_directory_find_parent(
@@ -1119,6 +1142,10 @@ xrep_directory_find_parent(
if (rd->parent_ino != NULLFSINO)
return 0;
+ rd->parent_ino = xrep_directory_dcache_parent(rd);
+ if (rd->parent_ino != NULLFSINO)
+ return 0;
+
rd->parent_ino = xrep_directory_lookup_parent(rd);
if (rd->parent_ino != NULLFSINO)
return 0;
diff --git a/fs/xfs/scrub/parent.h b/fs/xfs/scrub/parent.h
index 10612f204d41..03681ab24596 100644
--- a/fs/xfs/scrub/parent.h
+++ b/fs/xfs/scrub/parent.h
@@ -10,5 +10,6 @@ int xchk_parent_lock_two_dirs(struct xfs_scrub *sc, struct xfs_inode *dp);
int xrep_parent_confirm(struct xfs_scrub *sc, xfs_ino_t *parent_ino);
int xrep_parent_scan(struct xfs_scrub *sc, xfs_ino_t *parent_ino);
+xfs_ino_t xrep_parent_from_dcache(struct xfs_scrub *sc);
#endif /* __XFS_SCRUB_PARENT_H__ */
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c
index e8c1d759259e..648345b1c1a9 100644
--- a/fs/xfs/scrub/parent_repair.c
+++ b/fs/xfs/scrub/parent_repair.c
@@ -282,6 +282,44 @@ out_rele:
return error;
}
+/* Check the dentry cache to see if knows of a parent for the scrub target. */
+xfs_ino_t
+xrep_parent_from_dcache(
+ struct xfs_scrub *sc)
+{
+ struct inode *pip = NULL;
+ struct dentry *dentry, *parent;
+ xfs_ino_t ret = NULLFSINO;
+
+ dentry = d_find_alias(VFS_I(sc->ip));
+ if (!dentry)
+ goto out;
+
+ parent = dget_parent(dentry);
+ if (!parent)
+ goto out_dput;
+
+ if (parent->d_sb != sc->ip->i_mount->m_super) {
+ dput(parent);
+ goto out_dput;
+ }
+
+ pip = igrab(d_inode(parent));
+ dput(parent);
+
+ if (S_ISDIR(pip->i_mode)) {
+ trace_xrep_findparent_from_dcache(sc->ip, XFS_I(pip)->i_ino);
+ ret = XFS_I(pip)->i_ino;
+ }
+
+ xfs_irele(XFS_I(pip));
+
+out_dput:
+ dput(dentry);
+out:
+ return ret;
+}
+
/*
* Scan the entire filesystem looking for a parent inode.
*
@@ -384,6 +422,12 @@ xrep_parent(
if (sick & XFS_SICK_INO_DIR)
return -EFSCORRUPTED;
+ /* Does the VFS dcache have an answer for us? */
+ parent_ino = xrep_parent_from_dcache(sc);
+ error = xrep_parent_confirm(sc, &parent_ino);
+ if (!error && parent_ino != NULLFSINO)
+ goto reset_parent;
+
/* Scan the entire filesystem for a parent. */
error = xrep_parent_scan(sc, &parent_ino);
if (error)
@@ -391,6 +435,7 @@ xrep_parent(
if (parent_ino == NULLFSINO)
return -EFSCORRUPTED;
+reset_parent:
/* If the '..' entry is already set to the parent inode, we're done. */
curr_parent = xrep_dotdot_lookup(sc);
if (curr_parent != NULLFSINO && curr_parent == parent_ino)
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 3151985d9fbe..476fb62a6e54 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -1871,6 +1871,7 @@ DEFINE_EVENT(xrep_parent_salvage_class, name, \
DEFINE_XREP_PARENT_SALVAGE_CLASS(xrep_directory_salvaged_parent);
DEFINE_XREP_PARENT_SALVAGE_CLASS(xrep_dir_salvaged_parent);
DEFINE_XREP_PARENT_SALVAGE_CLASS(xrep_findparent_dirent);
+DEFINE_XREP_PARENT_SALVAGE_CLASS(xrep_findparent_from_dcache);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */