Add an optional callback that gets called when d_walk is *leaving* a dentry. This will be used in a later patch to provide a function to safely perform d_genocide on live trees. Signed-off-by: Ondrej Mosnacek <omosnace@xxxxxxxxxx> --- fs/dcache.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 9ed4c0f99e57..70afcb6e6892 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1260,13 +1260,15 @@ enum d_walk_ret { * d_walk - walk the dentry tree * @parent: start of walk * @lock_inode whether to lock also parent inode - * @data: data passed to @enter() and @finish() + * @data: data passed to @enter() and @leave() * @enter: callback when first entering the dentry + * @leave: callback when leaving the dentry * * The @enter() callbacks are called with d_lock held. */ static void d_walk(struct dentry *parent, bool lock_inode, void *data, - enum d_walk_ret (*enter)(void *, struct dentry *)) + enum d_walk_ret (*enter)(void *, struct dentry *), + void (*leave)(void *, struct dentry *)) { struct dentry *this_parent; struct list_head *next; @@ -1339,6 +1341,8 @@ resume: } goto repeat; } + if (leave) + leave(data, dentry); spin_unlock(&dentry->d_lock); } /* @@ -1350,6 +1354,8 @@ ascend: struct dentry *child = this_parent; this_parent = child->d_parent; + if (leave) + leave(data, child); spin_unlock(&child->d_lock); if (lock_inode) { inode_unlock(child->d_inode); @@ -1370,6 +1376,8 @@ ascend: rcu_read_unlock(); goto resume; } + if (leave) + leave(data, parent); if (need_seqretry(&rename_lock, seq)) goto rename_retry; rcu_read_unlock(); @@ -1425,7 +1433,7 @@ int path_has_submounts(const struct path *parent) struct check_mount data = { .mnt = parent->mnt, .mounted = 0 }; read_seqlock_excl(&mount_lock); - d_walk(parent->dentry, false, &data, path_check_mount); + d_walk(parent->dentry, false, &data, path_check_mount, NULL); read_sequnlock_excl(&mount_lock); return data.mounted; @@ -1564,7 +1572,7 @@ void shrink_dcache_parent(struct dentry *parent) struct select_data data = {.start = parent}; INIT_LIST_HEAD(&data.dispose); - d_walk(parent, false, &data, select_collect); + d_walk(parent, false, &data, select_collect, NULL); if (!list_empty(&data.dispose)) { shrink_dentry_list(&data.dispose); @@ -1575,7 +1583,7 @@ void shrink_dcache_parent(struct dentry *parent) if (!data.found) break; data.victim = NULL; - d_walk(parent, false, &data, select_collect2); + d_walk(parent, false, &data, select_collect2, NULL); if (data.victim) { struct dentry *parent; spin_lock(&data.victim->d_lock); @@ -1622,7 +1630,7 @@ static enum d_walk_ret umount_check(void *_data, struct dentry *dentry) static void do_one_tree(struct dentry *dentry) { shrink_dcache_parent(dentry); - d_walk(dentry, false, dentry, umount_check); + d_walk(dentry, false, dentry, umount_check, NULL); d_drop(dentry); dput(dentry); } @@ -1679,7 +1687,7 @@ void d_invalidate(struct dentry *dentry) shrink_dcache_parent(dentry); for (;;) { struct dentry *victim = NULL; - d_walk(dentry, false, &victim, find_submount); + d_walk(dentry, false, &victim, find_submount, NULL); if (!victim) { if (had_submounts) shrink_dcache_parent(dentry); @@ -3129,7 +3137,7 @@ static enum d_walk_ret d_genocide_kill(void *data, struct dentry *dentry) void d_genocide(struct dentry *parent) { - d_walk(parent, false, parent, d_genocide_kill); + d_walk(parent, false, parent, d_genocide_kill, NULL); } EXPORT_SYMBOL(d_genocide); -- 2.21.0