Skip to content

Commit d54c5ac

Browse files
author
Darrick J. Wong
committed
xfs: invalidate dirloop scrub path data when concurrent updates happen
Add a dirent update hook so that we can detect directory tree updates that affect any of the paths found by this scrubber and force it to rescan. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 928b721 commit d54c5ac

File tree

3 files changed

+244
-1
lines changed

3 files changed

+244
-1
lines changed

fs/xfs/scrub/dirtree.c

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ xchk_dirtree_buf_cleanup(
7070
struct xchk_dirtree *dl = buf;
7171
struct xchk_dirpath *path, *n;
7272

73+
if (dl->scan_ino != NULLFSINO)
74+
xfs_dir_hook_del(dl->sc->mp, &dl->dhook);
75+
7376
xchk_dirtree_for_each_path_safe(dl, path, n) {
7477
list_del_init(&path->list);
7578
xino_bitmap_destroy(&path->seen_inodes);
@@ -90,13 +93,17 @@ xchk_setup_dirtree(
9093
char *descr;
9194
int error;
9295

96+
xchk_fsgates_enable(sc, XCHK_FSGATES_DIRENTS);
97+
9398
dl = kvzalloc(sizeof(struct xchk_dirtree), XCHK_GFP_FLAGS);
9499
if (!dl)
95100
return -ENOMEM;
96101
dl->sc = sc;
97102
dl->xname.name = dl->namebuf;
103+
dl->hook_xname.name = dl->hook_namebuf;
98104
INIT_LIST_HEAD(&dl->path_list);
99105
dl->root_ino = NULLFSINO;
106+
dl->scan_ino = NULLFSINO;
100107

101108
mutex_init(&dl->lock);
102109

@@ -558,6 +565,133 @@ xchk_dirpath_walk_upwards(
558565
return error;
559566
}
560567

568+
/*
569+
* Decide if this path step has been touched by this live update. Returns
570+
* 1 for yes, 0 for no, or a negative errno.
571+
*/
572+
STATIC int
573+
xchk_dirpath_step_is_stale(
574+
struct xchk_dirtree *dl,
575+
struct xchk_dirpath *path,
576+
unsigned int step_nr,
577+
xfarray_idx_t step_idx,
578+
struct xfs_dir_update_params *p,
579+
xfs_ino_t *cursor)
580+
{
581+
struct xchk_dirpath_step step;
582+
xfs_ino_t child_ino = *cursor;
583+
int error;
584+
585+
error = xfarray_load(dl->path_steps, step_idx, &step);
586+
if (error)
587+
return error;
588+
*cursor = be64_to_cpu(step.pptr_rec.p_ino);
589+
590+
/*
591+
* If the parent and child being updated are not the ones mentioned in
592+
* this path step, the scan data is still ok.
593+
*/
594+
if (p->ip->i_ino != child_ino || p->dp->i_ino != *cursor)
595+
return 0;
596+
597+
/*
598+
* If the dirent name lengths or byte sequences are different, the scan
599+
* data is still ok.
600+
*/
601+
if (p->name->len != step.name_len)
602+
return 0;
603+
604+
error = xfblob_loadname(dl->path_names, step.name_cookie,
605+
&dl->hook_xname, step.name_len);
606+
if (error)
607+
return error;
608+
609+
if (memcmp(dl->hook_xname.name, p->name->name, p->name->len) != 0)
610+
return 0;
611+
612+
/* Exact match, scan data is out of date. */
613+
trace_xchk_dirpath_changed(dl->sc, path->path_nr, step_nr, p->dp,
614+
p->ip, p->name);
615+
return 1;
616+
}
617+
618+
/*
619+
* Decide if this path has been touched by this live update. Returns 1 for
620+
* yes, 0 for no, or a negative errno.
621+
*/
622+
STATIC int
623+
xchk_dirpath_is_stale(
624+
struct xchk_dirtree *dl,
625+
struct xchk_dirpath *path,
626+
struct xfs_dir_update_params *p)
627+
{
628+
xfs_ino_t cursor = dl->scan_ino;
629+
xfarray_idx_t idx = path->first_step;
630+
unsigned int i;
631+
int ret;
632+
633+
/*
634+
* The child being updated has not been seen by this path at all; this
635+
* path cannot be stale.
636+
*/
637+
if (!xino_bitmap_test(&path->seen_inodes, p->ip->i_ino))
638+
return 0;
639+
640+
ret = xchk_dirpath_step_is_stale(dl, path, 0, idx, p, &cursor);
641+
if (ret != 0)
642+
return ret;
643+
644+
for (i = 1, idx = path->second_step; i < path->nr_steps; i++, idx++) {
645+
ret = xchk_dirpath_step_is_stale(dl, path, i, idx, p, &cursor);
646+
if (ret != 0)
647+
return ret;
648+
}
649+
650+
return 0;
651+
}
652+
653+
/*
654+
* Decide if a directory update from the regular filesystem touches any of the
655+
* paths we've scanned, and invalidate the scan data if true.
656+
*/
657+
STATIC int
658+
xchk_dirtree_live_update(
659+
struct notifier_block *nb,
660+
unsigned long action,
661+
void *data)
662+
{
663+
struct xfs_dir_update_params *p = data;
664+
struct xchk_dirtree *dl;
665+
struct xchk_dirpath *path;
666+
int ret;
667+
668+
dl = container_of(nb, struct xchk_dirtree, dhook.dirent_hook.nb);
669+
670+
trace_xchk_dirtree_live_update(dl->sc, p->dp, action, p->ip, p->delta,
671+
p->name);
672+
673+
mutex_lock(&dl->lock);
674+
675+
if (dl->stale || dl->aborted)
676+
goto out_unlock;
677+
678+
xchk_dirtree_for_each_path(dl, path) {
679+
ret = xchk_dirpath_is_stale(dl, path, p);
680+
if (ret < 0) {
681+
dl->aborted = true;
682+
break;
683+
}
684+
if (ret == 1) {
685+
dl->stale = true;
686+
break;
687+
}
688+
}
689+
690+
out_unlock:
691+
mutex_unlock(&dl->lock);
692+
return NOTIFY_DONE;
693+
}
694+
561695
/* Delete all the collected path information. */
562696
STATIC void
563697
xchk_dirtree_reset(
@@ -673,6 +807,8 @@ xchk_dirtree_find_paths_to_root(
673807
}
674808
if (error)
675809
return error;
810+
if (dl->aborted)
811+
return 0;
676812
}
677813
} while (dl->stale);
678814

@@ -744,11 +880,28 @@ xchk_dirtree(
744880

745881
ASSERT(xfs_has_parent(sc->mp));
746882

747-
/* Find the root of the directory tree. */
883+
/*
884+
* Find the root of the directory tree. Remember which directory to
885+
* scan, because the hook doesn't detach until after sc->ip gets
886+
* released during teardown.
887+
*/
748888
dl->root_ino = sc->mp->m_rootip->i_ino;
889+
dl->scan_ino = sc->ip->i_ino;
749890

750891
trace_xchk_dirtree_start(sc->ip, sc->sm, 0);
751892

893+
/*
894+
* Hook into the directory entry code so that we can capture updates to
895+
* paths that we have already scanned. The scanner thread takes each
896+
* directory's ILOCK, which means that any in-progress directory update
897+
* will finish before we can scan the directory.
898+
*/
899+
ASSERT(sc->flags & XCHK_FSGATES_DIRENTS);
900+
xfs_dir_hook_setup(&dl->dhook, xchk_dirtree_live_update);
901+
error = xfs_dir_hook_add(sc->mp, &dl->dhook);
902+
if (error)
903+
goto out;
904+
752905
mutex_lock(&dl->lock);
753906

754907
/* Trace each parent pointer's path to the root. */
@@ -775,6 +928,10 @@ xchk_dirtree(
775928
}
776929
if (error)
777930
goto out_scanlock;
931+
if (dl->aborted) {
932+
xchk_set_incomplete(sc);
933+
goto out_scanlock;
934+
}
778935

779936
/* Assess what we found in our path evaluation. */
780937
xchk_dirtree_evaluate(dl, &oc);
@@ -790,6 +947,7 @@ xchk_dirtree(
790947

791948
out_scanlock:
792949
mutex_unlock(&dl->lock);
950+
out:
793951
trace_xchk_dirtree_done(sc->ip, sc->sm, error);
794952
return error;
795953
}

fs/xfs/scrub/dirtree.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ struct xchk_dirtree {
7272
/* Root inode that we're looking for. */
7373
xfs_ino_t root_ino;
7474

75+
/*
76+
* This is the inode that we're scanning. The live update hook can
77+
* continue to be called after xchk_teardown drops sc->ip but before
78+
* it calls buf_cleanup, so we keep a copy.
79+
*/
80+
xfs_ino_t scan_ino;
81+
7582
/* Scratch buffer for scanning pptr xattrs */
7683
struct xfs_parent_rec pptr_rec;
7784
struct xfs_da_args pptr_args;
@@ -80,9 +87,19 @@ struct xchk_dirtree {
8087
struct xfs_name xname;
8188
char namebuf[MAXNAMELEN];
8289

90+
/*
91+
* Hook into directory updates so that we can receive live updates
92+
* from other writer threads.
93+
*/
94+
struct xfs_dir_hook dhook;
95+
8396
/* lock for everything below here */
8497
struct mutex lock;
8598

99+
/* buffer for the live update functions to use for dirent names */
100+
struct xfs_name hook_xname;
101+
unsigned char hook_namebuf[MAXNAMELEN];
102+
86103
/*
87104
* All path steps observed during this scan. Each of the path
88105
* steps for a particular pathwalk are recorded in sequential
@@ -106,6 +123,9 @@ struct xchk_dirtree {
106123

107124
/* Have the path data been invalidated by a concurrent update? */
108125
bool stale:1;
126+
127+
/* Has the scan been aborted? */
128+
bool aborted:1;
109129
};
110130

111131
#define xchk_dirtree_for_each_path_safe(dl, path, n) \

fs/xfs/scrub/trace.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,71 @@ DEFINE_EVENT(xchk_dirtree_evaluate_class, name, \
17641764
TP_ARGS(dl, oc))
17651765
DEFINE_XCHK_DIRTREE_EVALUATE_EVENT(xchk_dirtree_evaluate);
17661766

1767+
TRACE_EVENT(xchk_dirpath_changed,
1768+
TP_PROTO(struct xfs_scrub *sc, unsigned int path_nr,
1769+
unsigned int step_nr, const struct xfs_inode *dp,
1770+
const struct xfs_inode *ip, const struct xfs_name *xname),
1771+
TP_ARGS(sc, path_nr, step_nr, dp, ip, xname),
1772+
TP_STRUCT__entry(
1773+
__field(dev_t, dev)
1774+
__field(unsigned int, path_nr)
1775+
__field(unsigned int, step_nr)
1776+
__field(xfs_ino_t, child_ino)
1777+
__field(xfs_ino_t, parent_ino)
1778+
__field(unsigned int, namelen)
1779+
__dynamic_array(char, name, xname->len)
1780+
),
1781+
TP_fast_assign(
1782+
__entry->dev = sc->mp->m_super->s_dev;
1783+
__entry->path_nr = path_nr;
1784+
__entry->step_nr = step_nr;
1785+
__entry->child_ino = ip->i_ino;
1786+
__entry->parent_ino = dp->i_ino;
1787+
__entry->namelen = xname->len;
1788+
memcpy(__get_str(name), xname->name, xname->len);
1789+
),
1790+
TP_printk("dev %d:%d path %u step %u child_ino 0x%llx parent_ino 0x%llx name '%.*s'",
1791+
MAJOR(__entry->dev), MINOR(__entry->dev),
1792+
__entry->path_nr,
1793+
__entry->step_nr,
1794+
__entry->child_ino,
1795+
__entry->parent_ino,
1796+
__entry->namelen,
1797+
__get_str(name))
1798+
);
1799+
1800+
TRACE_EVENT(xchk_dirtree_live_update,
1801+
TP_PROTO(struct xfs_scrub *sc, const struct xfs_inode *dp,
1802+
int action, const struct xfs_inode *ip, int delta,
1803+
const struct xfs_name *xname),
1804+
TP_ARGS(sc, dp, action, ip, delta, xname),
1805+
TP_STRUCT__entry(
1806+
__field(dev_t, dev)
1807+
__field(xfs_ino_t, parent_ino)
1808+
__field(int, action)
1809+
__field(xfs_ino_t, child_ino)
1810+
__field(int, delta)
1811+
__field(unsigned int, namelen)
1812+
__dynamic_array(char, name, xname->len)
1813+
),
1814+
TP_fast_assign(
1815+
__entry->dev = sc->mp->m_super->s_dev;
1816+
__entry->parent_ino = dp->i_ino;
1817+
__entry->action = action;
1818+
__entry->child_ino = ip->i_ino;
1819+
__entry->delta = delta;
1820+
__entry->namelen = xname->len;
1821+
memcpy(__get_str(name), xname->name, xname->len);
1822+
),
1823+
TP_printk("dev %d:%d parent_ino 0x%llx child_ino 0x%llx nlink_delta %d name '%.*s'",
1824+
MAJOR(__entry->dev), MINOR(__entry->dev),
1825+
__entry->parent_ino,
1826+
__entry->child_ino,
1827+
__entry->delta,
1828+
__entry->namelen,
1829+
__get_str(name))
1830+
);
1831+
17671832
/* repair tracepoints */
17681833
#if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR)
17691834

0 commit comments

Comments
 (0)