[RFC 07/26] VFS: Introduce dput() variante that maintains a kill-list

From: Jan Blunck
Date: Mon Jul 30 2007 - 12:23:36 EST


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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/