Commit 34c9382c authored by Darrick J. Wong's avatar Darrick J. Wong

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.

A reviewer asks:

"How high is the chance that we actually have a valid dcache entry for a
file in a corrupted directory?"

There's a decent chance of this actually working.  Say you have a
1000-block directory foo, and block 980 gets corrupted.  Let's further
suppose that block 0 has a correct entry for ".." and "bar".  If someone
accesses /mnt/foo/bar, that will cause the dcache to create a dentry
from /mnt to /mnt/foo whose d_parent points back to /mnt.  If you then
want to rebuild the directory, XFS can obtain the parent from the dcache
without needing to wander into parent pointers or scan the filesystem to
find /mnt's connection to foo.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent cc22edab
......@@ -208,6 +208,29 @@ xrep_dir_lookup_parent(
return 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_findparent_from_dcache(sc);
if (parent_ino == NULLFSINO)
return parent_ino;
error = xrep_findparent_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(
......@@ -221,6 +244,12 @@ xrep_dir_find_parent(
return 0;
}
ino = xrep_dir_dcache_parent(rd);
if (ino != NULLFSINO) {
xrep_findparent_scan_finish_early(&rd->pscan, ino);
return 0;
}
ino = xrep_dir_lookup_parent(rd);
if (ino != NULLFSINO) {
xrep_findparent_scan_finish_early(&rd->pscan, ino);
......
......@@ -53,7 +53,8 @@
* must not read the scan results without re-taking @sc->ip's ILOCK.
*
* There are a few shortcuts that we can take to avoid scanning the entire
* filesystem, such as noticing directory tree roots.
* filesystem, such as noticing directory tree roots and querying the dentry
* cache for parent information.
*/
struct xrep_findparent_info {
......@@ -410,3 +411,38 @@ xrep_findparent_self_reference(
return NULLFSINO;
}
/* Check the dentry cache to see if knows of a parent for the scrub target. */
xfs_ino_t
xrep_findparent_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;
ASSERT(parent->d_sb == sc->ip->i_mount->m_super);
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;
}
......@@ -45,5 +45,6 @@ void xrep_findparent_scan_finish_early(struct xrep_parent_scan_info *pscan,
int xrep_findparent_confirm(struct xfs_scrub *sc, xfs_ino_t *parent_ino);
xfs_ino_t xrep_findparent_self_reference(struct xfs_scrub *sc);
xfs_ino_t xrep_findparent_from_dcache(struct xfs_scrub *sc);
#endif /* __XFS_SCRUB_FINDPARENT_H__ */
......@@ -118,7 +118,20 @@ xrep_parent_find_dotdot(
* then retake the ILOCK so that we can salvage directory entries.
*/
xchk_iunlock(sc, XFS_ILOCK_EXCL);
/* Does the VFS dcache have an answer for us? */
ino = xrep_findparent_from_dcache(sc);
if (ino != NULLFSINO) {
error = xrep_findparent_confirm(sc, &ino);
if (!error && ino != NULLFSINO) {
xrep_findparent_scan_finish_early(&rp->pscan, ino);
goto out_relock;
}
}
/* Scan the entire filesystem for a parent. */
error = xrep_findparent_scan(&rp->pscan);
out_relock:
xchk_ilock(sc, XFS_ILOCK_EXCL);
return error;
......
......@@ -2613,6 +2613,7 @@ DEFINE_EVENT(xrep_parent_salvage_class, name, \
TP_ARGS(dp, ino))
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_dir_salvaged_parent);
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_findparent_dirent);
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_findparent_from_dcache);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment