This patch introduces a new variant of dput(). This becomes necessary to prevent a recursive call to dput() from the union mount code. void __dput(struct dentry *dentry, struct list_head *list); __dput() works mostly like the original dput() did. The main difference is that it doesn't do a full d_kill() at the end but puts the dentry on a list as soon as it isn't reachable anymore. Therefore the union mount code can savely call __dput() when it wants to get rid of underlying dentry references during a dput(). After calling __dput() the caller must make sure that on all dentries __d_kill_final() is called. __d_kill_final() is actually doing the dentry_iput() and is also dereferencing the parent. Signed-off-by: Jan Blunck <jblunck@xxxxxxx> --- fs/dcache.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 5 deletions(-) --- a/fs/dcache.c +++ b/fs/dcache.c @@ -129,19 +129,56 @@ static void dentry_iput(struct dentry * * * If this is the root of the dentry tree, return NULL. */ -static struct dentry *d_kill(struct dentry *dentry) +static struct dentry *__d_kill(struct dentry *dentry, struct list_head *list) { struct dentry *parent; list_del(&dentry->d_u.d_child); dentry_stat.nr_dentry--; /* For d_free, below */ - /*drops the locks, at that point nobody can reach this dentry */ + + if (list) { + list_del_init(&dentry->d_alias); + /* at this point nobody can reach this dentry */ + list_add(&dentry->d_lru, list); + spin_unlock(&dentry->d_lock); + spin_unlock(&dcache_lock); + return NULL; + } + + /* drops the locks, at that point nobody can reach this dentry */ dentry_iput(dentry); parent = dentry->d_parent; d_free(dentry); return dentry == parent ? NULL : parent; } +void __dput(struct dentry *, struct list_head *); + +static void __d_kill_final(struct dentry *dentry, struct list_head *list) +{ + struct dentry *parent = dentry->d_parent; + struct inode *inode = dentry->d_inode; + + if (inode) { + dentry->d_inode = NULL; + if (!inode->i_nlink) + fsnotify_inoderemove(inode); + if (dentry->d_op && dentry->d_op->d_iput) + dentry->d_op->d_iput(dentry, inode); + else + iput(inode); + } + + d_free(dentry); + if (dentry != parent) + __dput(parent, list); +} + +static struct dentry *d_kill(struct dentry *dentry) +{ + return __d_kill(dentry, NULL); +} + /* * This is dput * @@ -171,7 +208,7 @@ static struct dentry *d_kill(struct dent * no dcache lock, please. */ -void dput(struct dentry *dentry) +void __dput(struct dentry *dentry, struct list_head *list) { if (!dentry) return; @@ -215,14 +252,27 @@ kill_it: * delete it from there */ if (!list_empty(&dentry->d_lru)) { - list_del(&dentry->d_lru); + list_del_init(&dentry->d_lru); dentry_stat.nr_unused--; } - dentry = d_kill(dentry); + + dentry = __d_kill(dentry, list); if (dentry) goto repeat; } +void dput(struct dentry *dentry) +{ + LIST_HEAD(mortuary); + + __dput(dentry, &mortuary); + while (!list_empty(&mortuary)) { + dentry = list_entry(mortuary.next, struct dentry, d_lru); + list_del(&dentry->d_lru); + __d_kill_final(dentry, &mortuary); + } +} + /** * d_invalidate - invalidate a dentry * @dentry: dentry to invalidate -- - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html