From: Darrick J. Wong <djwong@xxxxxxxxxx> While we're scanning the filesystem, we still need to keep the tempdir up to date with whatever changes get made to the you know what. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_dir2.c | 2 + fs/xfs/libxfs/xfs_dir2.h | 2 + fs/xfs/scrub/dir_repair.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c index a73573d47b13..825a8cd9ee57 100644 --- a/fs/xfs/libxfs/xfs_dir2.c +++ b/fs/xfs/libxfs/xfs_dir2.c @@ -435,7 +435,7 @@ int xfs_dir_removename( struct xfs_trans *tp, struct xfs_inode *dp, - struct xfs_name *name, + const struct xfs_name *name, xfs_ino_t ino, xfs_extlen_t total) /* bmap's total block count */ { diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h index 15a36cf7ae87..d394bcd5e82c 100644 --- a/fs/xfs/libxfs/xfs_dir2.h +++ b/fs/xfs/libxfs/xfs_dir2.h @@ -46,7 +46,7 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp, const struct xfs_name *name, xfs_ino_t *inum, struct xfs_name *ci_name); extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp, - struct xfs_name *name, xfs_ino_t ino, + const struct xfs_name *name, xfs_ino_t ino, xfs_extlen_t tot); extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp, const struct xfs_name *name, xfs_ino_t inum, diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c index a1f2bca53655..c484e6f36ca0 100644 --- a/fs/xfs/scrub/dir_repair.c +++ b/fs/xfs/scrub/dir_repair.c @@ -121,6 +121,9 @@ struct xrep_dir { /* Mutex protecting dir_entries, dir_names, and parent_ino. */ struct mutex lock; + /* Hook to capture directory entry updates. */ + struct xfs_dirent_hook hooks; + /* * This is the dotdot inumber that we're going to set on the * reconstructed directory. @@ -138,6 +141,7 @@ xrep_dir_teardown( { struct xrep_dir *rd = sc->buf; + xfs_dirent_hook_del(sc->mp, &rd->hooks); xchk_iscan_teardown(&rd->iscan); mutex_destroy(&rd->lock); xfblob_destroy(rd->dir_names); @@ -152,6 +156,8 @@ xrep_setup_directory( struct xrep_dir *rd; int error; + xchk_fshooks_enable(sc, XCHK_FSHOOKS_DIRENTS); + error = xrep_tempfile_create(sc, S_IFDIR); if (error) return error; @@ -899,6 +905,74 @@ xrep_dir_rebuild_tree( return xrep_dir_replay_updates(rd); } +/* + * Capture dirent updates being made by other threads which are relevant to the + * directory being repaired. + */ +STATIC int +xrep_dir_live_update( + struct notifier_block *nb, + unsigned long action, + void *data) +{ + struct xfs_dirent_update_params *p = data; + struct xrep_dir *rd; + struct xfs_scrub *sc; + int error = 0; + + rd = container_of(nb, struct xrep_dir, hooks.delta_hook.nb); + sc = rd->sc; + + /* + * This thread updated a child dirent in the directory that we're + * rebuilding. Stash the update for replay against the temporary + * directory. + */ + if (action == XFS_DIRENT_CHILD_DELTA && + p->dp->i_ino == sc->ip->i_ino && + xchk_iscan_want_live_update(&rd->iscan, p->ip->i_ino)) { + mutex_lock(&rd->lock); + if (p->delta > 0) + error = xrep_dir_add_dirent(rd, p->name, p->ip->i_ino); + else + error = xrep_dir_remove_dirent(rd, p->name, + p->ip->i_ino); + mutex_unlock(&rd->lock); + if (error) + goto out_abort; + } + + /* + * This thread updated another directory's child dirent that points to + * the directory that we're rebuilding, so remember the new dotdot + * target. + */ + if (action == XFS_DIRENT_BACKREF_DELTA && + p->ip->i_ino == sc->ip->i_ino && + xchk_iscan_want_live_update(&rd->iscan, p->dp->i_ino)) { + if (p->delta > 0) { + trace_xrep_dir_add_dirent(sc->tempip, &xfs_name_dotdot, + p->dp->i_ino); + + mutex_lock(&rd->lock); + rd->parent_ino = p->dp->i_ino; + mutex_unlock(&rd->lock); + } else { + trace_xrep_dir_remove_dirent(sc->tempip, + &xfs_name_dotdot, NULLFSINO); + + mutex_lock(&rd->lock); + rd->parent_ino = NULLFSINO; + mutex_unlock(&rd->lock); + } + } + + return NOTIFY_DONE; +out_abort: + xchk_iscan_abort(&rd->iscan); + return NOTIFY_DONE; +} + /* Set up the filesystem scan so we can regenerate directory entries. */ STATIC int xrep_dir_setup_scan( @@ -921,8 +995,24 @@ xrep_dir_setup_scan( /* Retry iget every tenth of a second for up to 30 seconds. */ xchk_iscan_start(sc, 30000, 100, &rd->iscan); + /* + * Hook into the dirent update code. The hook only operates on inodes + * that were already scanned, and the scanner thread takes each inode's + * ILOCK, which means that any in-progress inode updates will finish + * before we can scan the inode. + */ + ASSERT(sc->flags & XCHK_FSHOOKS_DIRENTS); + xfs_hook_setup(&rd->hooks.delta_hook, xrep_dir_live_update); + error = xfs_dirent_hook_add(sc->mp, &rd->hooks); + if (error) + goto out_scan; + return 0; +out_scan: + xchk_iscan_teardown(&rd->iscan); + mutex_destroy(&rd->lock); + xfblob_destroy(rd->dir_names); out_entries: xfarray_destroy(rd->dir_entries); return error;