From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> --- fs/xfs/scrub/dir_repair.c | 27 ++++++++++++++++++++++++++ fs/xfs/scrub/parent.h | 1 + fs/xfs/scrub/parent_repair.c | 44 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/trace.h | 1 + 4 files changed, 73 insertions(+) diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index e2de2fc24ba0..871b14c09e86 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -1033,6 +1033,29 @@ xrep_dir_lookup_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_dir_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_dir_find_parent( @@ -1044,6 +1067,10 @@ xrep_dir_find_parent( if (rd->parent_ino != NULLFSINO) return 0; + rd->parent_ino = xrep_dir_dcache_parent(rd); + if (rd->parent_ino != NULLFSINO) + return 0; + rd->parent_ino = xrep_dir_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 e1979f5bb001..c20673d8f093 100644 --- a/fs/xfs/scrub/parent.h +++ b/fs/xfs/scrub/parent.h @@ -10,6 +10,7 @@ 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); xfs_ino_t xrep_parent_self_reference(struct xfs_scrub *sc); diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c index d275c2129176..d83948d1fd05 100644 --- a/fs/xfs/scrub/parent_repair.c +++ b/fs/xfs/scrub/parent_repair.c @@ -263,6 +263,44 @@ xrep_parent_confirm( 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; + } + + xchk_irele(sc, XFS_I(pip)); + +out_dput: + dput(dentry); +out: + return ret; +} + /* * Scan the entire filesystem looking for a parent inode for the inode being * scrubbed. @sc->ip must not be the root of a directory tree. @@ -392,6 +430,12 @@ xrep_parent( if (parent_ino != NULLFSINO) goto reset_parent; + /* 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) diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index b27abaa84d11..d8223ec24369 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -2535,6 +2535,7 @@ DEFINE_EVENT(xrep_parent_salvage_class, name, \ TP_ARGS(dp, ino)) 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) */