Hirofumi pointed out that the first version of my patch will fail if a 
file has a dentry open at the time of unlink.  The dcache handles this 
brutally: any dentry with an elevated use count at the time of unlink 
is forcibly removed from the dentry hash and otherwise remains attached 
to the still-open inode and its parent directory.  Eventually the open 
file will close, then the corresponding dput will reduce the dentry 
count to zero and detach the dentry from the inode and parent 
directory.

The problem for deferred delete is, we cannot remove the dentry from the 
cache hash like that because we need it to stay around as a negative 
dentry: without the negative dentry a real lookup on the filesystem at 
that point would erroneously report that the name still exists.  The 
user has just done an unlink, so that would be a surprise.

To fix this I introduced a new dentry flag, DCACHE_HIDDEN, to make the 
open dentry appear to be a negative dentry, even while it is still 
attached to an inode.  I added a d_negative(dentry) wrapper that tests 
for both the flag and the absence of an inode for the dentry, the 
latter being the traditional way of marking a dentry negative.  
(Actually, it might make sense to use a state bit for this instead of 
the inode test, but that is another story.)

The new d_negative wrapper had to be laboriously applied to each place
dentry->inode is used as a logical value to determine whether a dentry
is negative.  There are a lot of places where the field is used for
other purposes, and a few where it is hard to tell for sure on a quick 
reading, which I have commented in the patch.

Only fs/namei.c appears to need to be updated with this wrapper.  
Dcache.c itself does not do lookups, and usage in other filesystems is 
confined to those filesystems.  The traditional way of doing things is 
unaffected.  I am not sure about the network filesystems nfs and cifs, 
these may be doing operations on the dentries of the underlying 
filesystem.  I haven't looked into that question yet.

Now, when an open file is unlinked, the ->hide method in ext2 sets the 
HIDDEN bit, takes a reference count to keep the dentry around until the 
deferred unlink takes place, and returns a flag indicating that the 
dentry had to be hidden, which is only necessary in the case that the 
name still exists in the underlying filesystem (DCACHE_BACKED from the 
first version of the patch).  If the deferred delete and the dcache 
itself hold the only references on the dentry, then it will be detached 
from the inode immediately and the inode is scheduled for deletion at 
that time (also deferred).  Otherwise, the dentry is brutally unhashed 
in the traditional way, and eventually will be d_deleted again by the 
flush routine, after the name has been removed from the underlying 
filesystem.  If the file is still open at that time, d_delete falls 
back to the traditional unhash, which now does no harm
because the dcache state matches the filesystem state, so it is no 
longer necessary to hold the dentry in cache.

An additional improvement in this patch is to have ext2's getdents 
method flush the directory first.  This method retrieves its results 
directly from the filesystem, so any pending changes in the dcache have 
to be flushed to the filesystem to get consistent results.

At this point, only two changes have been made to the dentry cache 
itself:

  1) A new d_ops.hide method is added.

  2) A new DCACHE_HIDDEN flag marks a dentry as negative while
     still attached to an inode.

Actually, these changes are very small in comparision to the new feature 
supported.  The dcache is now able to function as a writeback cache, 
holding a consistent future state of the filesystem, whereas up till 
now it is functioned as a read-only cache, whose contents exactly match 
the filesystem outside its spinlocks.

So far, this patch only addresses deferred delete.  To be useful for 
Tux3, deferred create and rename have to be implemented as well, 
otherwise the contention on inode table blocks betweeen front end 
operations and back end flushing cannot be eliminated.  I think the 
delete side was probably the hardest part and the only place where the 
dcache needed an extension, but we shall see.

As I mentioned earlier, we will be able to compile with or without this 
dcache extension.  Without the deferred namespace operations we will 
use a somewhat less efficient update method that wraps the delta 
staging operation in a rw semaphore.  When we build as a module, it 
will typically be without the dcache patch.

The following two traces show the sequence of events for a deferred 
unlink.  In the second case, I simulated an open file via a small 
change to d_delete.  The effect is to initiate the deferred inode 
delete a little later.

[EMAIL PROTECTED]:~# rm /mnt/foo
>>> defer unlink: 0986e9f8/1 48 "foo"
>>> hide dentry: 0986e9f8/1 48 "foo"
>>> defer inode delete: 0988c0b0/0 0
[EMAIL PROTECTED]:~# ls /mnt
>>> ext2_sync_dir 0984dd30 "/"
>>> dentry: 0986e9f8/1 68 "foo"
>>> drop hidden dentry: 0986e9f8/1 48 "foo"
>>> deferred unlink: 0986e9f8/1 8 "foo"
>>> ext2_sync_dir 0984dd30 "/"
>>> dentry: 0986e9f8/0 8 "foo"
d  dir  lost+found
[EMAIL PROTECTED]:~# umount /mnt
>>> delete deferred inode: 0988c0b0/0 0
>>> ext2_delete_inode

[EMAIL PROTECTED]:~# rm /mnt/foo
>>> defer unlink: 0985365c/1 48 "foo"
>>> hide dentry: 0985365c/1 48 "foo"
[EMAIL PROTECTED]:~# ls /mnt/foo
/mnt/foo
[EMAIL PROTECTED]:~# ls /mnt
>>> ext2_sync_dir 09851cac "/"
>>> dentry: 0985365c/1 68 "foo"
>>> drop hidden dentry: 0985365c/1 48 "foo"
>>> deferred unlink: 0985365c/1 8 "foo"
>>> defer inode delete: 0988ae0c/0 0
>>> ext2_sync_dir 09851cac "/"
>>> dentry: 0985365c/0 8 "foo"
d  dir  lost+found
[EMAIL PROTECTED]:~# umount /mnt
>>> delete deferred inode: 0988ae0c/0 0
>>> ext2_delete_inode
diff --git a/Makefile b/Makefile
index 56fb747..985a7ce 100644
--- a/Makefile
+++ b/Makefile
@@ -538,7 +538,7 @@ NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
 CHECKFLAGS     += $(NOSTDINC_FLAGS)
 
 # warn about C99 declaration after statement
-KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
+#KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
 
 # disable pointer signed / unsigned warnings in gcc 4.0
 KBUILD_CFLAGS += $(call cc-option,-Wno-pointer-sign,)
diff --git a/fs/dcache.c b/fs/dcache.c
index 6068c25..94b3656 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -272,7 +272,7 @@ int d_invalidate(struct dentry * dentry)
 	 */
 	spin_lock(&dentry->d_lock);
 	if (atomic_read(&dentry->d_count) > 1) {
-		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { // what to do here?
 			spin_unlock(&dentry->d_lock);
 			spin_unlock(&dcache_lock);
 			return -EBUSY;
@@ -1387,20 +1387,22 @@ out:
  
 void d_delete(struct dentry * dentry)
 {
-	int isdir = 0;
+	int isdir = 0, hidden;
 	/*
 	 * Are we the only user?
 	 */
 	spin_lock(&dcache_lock);
 	spin_lock(&dentry->d_lock);
 	isdir = S_ISDIR(dentry->d_inode->i_mode);
-	if (atomic_read(&dentry->d_count) == 1) {
+	hidden = dentry->d_op && dentry->d_op->d_hide && dentry->d_op->d_hide(dentry);
+
+	if (atomic_read(&dentry->d_count) == 1 + hidden) {
 		dentry_iput(dentry);
 		fsnotify_nameremove(dentry, isdir);
 		return;
 	}
 
-	if (!d_unhashed(dentry))
+	if (!d_unhashed(dentry) && !hidden)
 		__d_drop(dentry);
 
 	spin_unlock(&dentry->d_lock);
@@ -1514,7 +1516,7 @@ static void d_move_locked(struct dentry * dentry, struct dentry * target)
 {
 	struct hlist_head *list;
 
-	if (!dentry->d_inode)
+	if (d_negative(dentry))
 		printk(KERN_WARNING "VFS: moving negative dcache entry\n");
 
 	write_seqlock(&rename_lock);
@@ -2103,7 +2105,7 @@ ino_t find_inode_number(struct dentry *dir, struct qstr *name)
 
 	dentry = d_hash_and_lookup(dir, name);
 	if (dentry) {
-		if (dentry->d_inode)
+		if (!d_negative(dentry))
 			ino = dentry->d_inode->i_ino;
 		dput(dentry);
 	}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index a78c6b4..06b5d05 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -270,6 +270,45 @@ static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)
 		de->file_type = 0;
 }
 
+int ext2_unlink_deferred(struct inode *dir, struct dentry *dentry);
+void dentry_iput(struct dentry *dentry);
+
+int ext2_flush_dir(struct dentry *dir)
+{
+	printk(">>> ext2_sync_dir %p \"%.*s\"\n", dir, dir->d_name.len, dir->d_name.name);
+	struct list_head *next;
+	spin_lock(&dcache_lock);
+	for (next = dir->d_subdirs.next; next != &dir->d_subdirs;) {
+		struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child);
+		if (d_unhashed(dentry))
+			continue;
+		show_dentry("dentry", dentry);
+		next = next->next;
+		spin_lock(&dentry->d_lock);
+		if (d_negative(dentry)) {
+			if ((dentry->d_flags & DCACHE_HIDDEN)) {
+				dentry->d_flags &= ~DCACHE_HIDDEN;
+				show_dentry("drop hidden dentry", dentry);
+				if ((dentry->d_flags & DCACHE_BACKED)) {
+					dentry->d_flags &= ~DCACHE_BACKED;
+					show_dentry("deferred unlink", dentry);
+					spin_unlock(&dentry->d_lock);
+					spin_unlock(&dcache_lock);
+					ext2_unlink_deferred(dir->d_inode, dentry);
+				} else {
+					spin_unlock(&dentry->d_lock);
+					spin_unlock(&dcache_lock);
+				}
+				d_delete(dentry);
+				dput(dentry);
+				spin_lock(&dcache_lock);
+			}
+		}
+	}
+	spin_unlock(&dcache_lock);
+	return 0; // really??
+}
+
 static int
 ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
 {
@@ -283,6 +322,8 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
 	unsigned char *types = NULL;
 	int need_revalidate = filp->f_version != inode->i_version;
 
+	ext2_flush_dir(filp->f_path.dentry);
+
 	if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
 		return 0;
 
@@ -699,6 +740,12 @@ not_empty:
 	return 0;
 }
 
+int ext2_sync_dir(struct file *file, struct dentry *dir, int datasync)
+{
+	ext2_flush_dir(dir);
+	return ext2_sync_file(file, dir, datasync);
+}
+
 const struct file_operations ext2_dir_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= generic_read_dir,
@@ -707,5 +754,5 @@ const struct file_operations ext2_dir_operations = {
 #ifdef CONFIG_COMPAT
 	.compat_ioctl	= ext2_compat_ioctl,
 #endif
-	.fsync		= ext2_sync_file,
+	.fsync		= ext2_sync_dir,
 };
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 384fc0d..a9ce89b 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -58,6 +58,7 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode)
  */
 void ext2_delete_inode (struct inode * inode)
 {
+	printk(">>> ext2_delete_inode\n");
 	truncate_inode_pages(&inode->i_data, 0);
 
 	if (is_bad_inode(inode))
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 80c97fd..bdd640c 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -36,10 +36,27 @@
 #include "acl.h"
 #include "xip.h"
 
+static int ext2_hide_dentry(struct dentry *dentry)
+{
+	if (dentry->d_flags & DCACHE_BACKED) {
+		BUG_ON(!dentry->d_inode);
+		show_dentry("hide dentry", dentry);
+		dentry->d_flags |= DCACHE_HIDDEN;
+		dget(dentry);
+		return 1;
+	}
+	return 0;
+}
+
+static struct dentry_operations ext2_dentry_operations = {
+	.d_hide = ext2_hide_dentry,
+};
+
 static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
 {
 	int err = ext2_add_link(dentry, inode);
 	if (!err) {
+		dentry->d_op = &ext2_dentry_operations;
 		d_instantiate(dentry, inode);
 		return 0;
 	}
@@ -67,6 +84,8 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str
 		if (IS_ERR(inode))
 			return ERR_CAST(inode);
 	}
+	dentry->d_flags |= DCACHE_BACKED;
+	dentry->d_op = &ext2_dentry_operations;
 	return d_splice_alias(inode, dentry);
 }
 
@@ -237,6 +256,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
 	if (err)
 		goto out_fail;
 
+	dentry->d_op = &ext2_dentry_operations;
 	d_instantiate(dentry, inode);
 out:
 	return err;
@@ -250,6 +270,21 @@ out_dir:
 	goto out;
 }
 
+static int defer_unlink(struct inode *dir, struct dentry *dentry)
+{
+	show_dentry("defer unlink", dentry);
+	dentry->d_inode->i_ctime = dir->i_ctime;
+	inode_dec_link_count(dentry->d_inode);
+	return 0;
+}
+
+int ext2_unlink_deferred(struct inode *dir, struct dentry *dentry)
+{
+	struct page *page;
+	struct ext2_dir_entry_2 *de = ext2_find_entry(dir, dentry, &page);
+	return de ? ext2_delete_entry(de, page) : -ENOENT;
+}
+
 static int ext2_unlink(struct inode * dir, struct dentry *dentry)
 {
 	struct inode * inode = dentry->d_inode;
@@ -377,7 +412,7 @@ const struct inode_operations ext2_dir_inode_operations = {
 	.create		= ext2_create,
 	.lookup		= ext2_lookup,
 	.link		= ext2_link,
-	.unlink		= ext2_unlink,
+	.unlink		= defer_unlink,
 	.symlink	= ext2_symlink,
 	.mkdir		= ext2_mkdir,
 	.rmdir		= ext2_rmdir,
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index ef50cbc..e8066b3 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -295,17 +295,52 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, siz
 static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off);
 #endif
 
+extern spinlock_t inode_lock;
+
+static inline void show_inode(char *tag, struct inode *inode)
+{
+	printk(">>> %s: %p/%i %x\n", tag, inode,
+		atomic_read(&inode->i_count), inode->i_flags);
+}
+
+static void defer_drop_inode(struct inode *inode)
+{
+	if (inode->i_nlink) {
+		generic_drop_inode(inode);
+		return;
+	}
+	show_inode("defer inode delete", inode);
+	inode->i_state |= I_DIRTY;
+	list_move(&inode->i_list, &EXT2_SB(inode->i_sb)->delete);
+	spin_unlock(&inode_lock);
+}
+
+extern struct list_head inode_unused;
+
+static int ext2_sync_fs(struct super_block *sb, int wait)
+{
+	while (!list_empty(&EXT2_SB(sb)->delete)) {
+		struct inode *inode = list_entry(EXT2_SB(sb)->delete.next, struct inode, i_list);
+		show_inode("delete deferred inode", inode);
+		spin_lock(&inode_lock);
+		generic_delete_inode(inode); /* removes from list and drops lock */
+	}
+	return 0;
+}
+
 static const struct super_operations ext2_sops = {
 	.alloc_inode	= ext2_alloc_inode,
 	.destroy_inode	= ext2_destroy_inode,
 	.write_inode	= ext2_write_inode,
 	.delete_inode	= ext2_delete_inode,
+	.drop_inode	= defer_drop_inode,
 	.put_super	= ext2_put_super,
 	.write_super	= ext2_write_super,
 	.statfs		= ext2_statfs,
 	.remount_fs	= ext2_remount,
 	.clear_inode	= ext2_clear_inode,
 	.show_options	= ext2_show_options,
+	.sync_fs	= ext2_sync_fs,
 #ifdef CONFIG_QUOTA
 	.quota_read	= ext2_quota_read,
 	.quota_write	= ext2_quota_write,
@@ -614,6 +649,7 @@ static int ext2_setup_super (struct super_block * sb,
 			EXT2_BLOCKS_PER_GROUP(sb),
 			EXT2_INODES_PER_GROUP(sb),
 			sbi->s_mount_opt);
+	INIT_LIST_HEAD(&sbi->delete);
 	return res;
 }
 
diff --git a/fs/namei.c b/fs/namei.c
index 3b26a24..f4eae06 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -955,9 +955,9 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 			break;
 
 		err = -ENOENT;
-		inode = next.dentry->d_inode;
-		if (!inode)
+		if (d_negative(next.dentry))
 			goto out_dput;
+		inode = next.dentry->d_inode;
 		err = -ENOTDIR; 
 		if (!inode->i_op)
 			goto out_dput;
@@ -967,9 +967,9 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
 			if (err)
 				goto return_err;
 			err = -ENOENT;
-			inode = nd->path.dentry->d_inode;
-			if (!inode)
+			if (d_negative(nd->path.dentry))
 				break;
+			inode = nd->path.dentry->d_inode;
 			err = -ENOTDIR; 
 			if (!inode->i_op)
 				break;
@@ -1077,7 +1077,7 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
 	if (path_walk(name, nd))
 		return 0;		/* something went wrong... */
 
-	if (!nd->path.dentry->d_inode ||
+	if (d_negative(nd->path.dentry) ||
 	    S_ISDIR(nd->path.dentry->d_inode->i_mode)) {
 		struct path old_path = nd->path;
 		struct qstr last = nd->last;
@@ -1094,7 +1094,7 @@ static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
 		path_get(&fs->root);
 		read_unlock(&fs->lock);
 		if (path_walk(name, nd) == 0) {
-			if (nd->path.dentry->d_inode) {
+			if (d_negative(nd->path.dentry)) {
 				path_put(&old_path);
 				return 1;
 			}
@@ -1487,7 +1487,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
 {
 	int error;
 
-	if (!victim->d_inode)
+	if (d_negative(victim))
 		return -ENOENT;
 
 	BUG_ON(victim->d_parent->d_inode != dir);
@@ -1526,7 +1526,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
 static inline int may_create(struct inode *dir, struct dentry *child,
 			     struct nameidata *nd)
 {
-	if (child->d_inode)
+	if (!d_negative(child))
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
 		return -ENOENT;
@@ -1618,12 +1618,14 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
 int may_open(struct nameidata *nd, int acc_mode, int flag)
 {
 	struct dentry *dentry = nd->path.dentry;
-	struct inode *inode = dentry->d_inode;
+	struct inode *inode;
 	int error;
 
-	if (!inode)
+	if (d_negative(dentry))
 		return -ENOENT;
 
+	inode = dentry->d_inode;
+
 	if (S_ISLNK(inode->i_mode))
 		return -ELOOP;
 	
@@ -1828,7 +1830,7 @@ do_last:
 	}
 
 	/* Negative dentry, just create the file */
-	if (!path.dentry->d_inode) {
+	if (d_negative(path.dentry)) {
 		/*
 		 * This write is needed to ensure that a
 		 * ro->rw transition does not occur between
@@ -1866,14 +1868,14 @@ do_last:
 	}
 
 	error = -ENOENT;
-	if (!path.dentry->d_inode)
+	if (d_negative(path.dentry))
 		goto exit_dput;
 	if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
 		goto do_link;
 
 	path_to_nameidata(&path, &nd);
 	error = -EISDIR;
-	if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
+	if (!d_negative(path.dentry) && S_ISDIR(path.dentry->d_inode->i_mode))
 		goto exit;
 ok:
 	/*
@@ -2017,7 +2019,7 @@ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
 	if (IS_ERR(dentry))
 		goto fail;
 
-	if (dentry->d_inode)
+	if (!d_negative(dentry))
 		goto eexist;
 	/*
 	 * Special case - lookup gave negative, but... we had foo/bar/
@@ -2381,7 +2383,7 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 		/* Why not before? Because we want correct error value */
 		if (nd.last.name[nd.last.len])
 			goto slashes;
-		inode = dentry->d_inode;
+		inode = dentry->d_inode; // what to do here?
 		if (inode)
 			atomic_inc(&inode->i_count);
 		error = mnt_want_write(nd.path.mnt);
@@ -2402,7 +2404,7 @@ exit:
 	return error;
 
 slashes:
-	error = !dentry->d_inode ? -ENOENT :
+	error = d_negative(dentry) ? -ENOENT :
 		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
 	goto exit2;
 }
@@ -2680,7 +2682,7 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
 
 	dget(new_dentry);
 	target = new_dentry->d_inode;
-	if (target)
+	if (target) // what to do here?
 		mutex_lock(&target->i_mutex);
 	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
 		error = -EBUSY;
@@ -2710,7 +2712,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (error)
 		return error;
 
-	if (!new_dentry->d_inode)
+	if (d_negative(new_dentry))
 		error = may_create(new_dir, new_dentry, NULL);
 	else
 		error = may_delete(new_dir, new_dentry, is_dir);
@@ -2777,7 +2779,7 @@ static int do_rename(int olddfd, const char *oldname,
 		goto exit3;
 	/* source must exist */
 	error = -ENOENT;
-	if (!old_dentry->d_inode)
+	if (d_negative(old_dentry))
 		goto exit4;
 	/* unless the source is a directory trailing slashes give -ENOTDIR */
 	if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d982eb8..c434d8d 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -133,6 +133,7 @@ struct dentry_operations {
 	void (*d_release)(struct dentry *);
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)(struct dentry *, char *, int);
+	int (*d_hide)(struct dentry *);
 };
 
 /* the dentry parameter passed to d_hash and d_compare is the parent
@@ -175,6 +176,9 @@ d_iput:		no		no		no       yes
 #define DCACHE_UNHASHED		0x0010	
 
 #define DCACHE_INOTIFY_PARENT_WATCHED	0x0020 /* Parent inode is watched */
+#define DCACHE_HIDDEN		0x0020 /* Unlinked but referenced by an open file */
+#define DCACHE_BACKED		0x0040 /* FS has dirent */
+#define DCACHE_WRONG		0x0080 /* FS has wrong dirent */
 
 extern spinlock_t dcache_lock;
 extern seqlock_t rename_lock;
@@ -341,6 +345,11 @@ static inline int d_unhashed(struct dentry *dentry)
 	return (dentry->d_flags & DCACHE_UNHASHED);
 }
 
+static inline int d_negative(struct dentry *dentry)
+{
+	return !dentry->d_inode || (dentry->d_flags & DCACHE_HIDDEN);
+}
+
 static inline struct dentry *dget_parent(struct dentry *dentry)
 {
 	struct dentry *ret;
@@ -363,4 +372,11 @@ extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
 
 extern int sysctl_vfs_cache_pressure;
 
+static inline void show_dentry(char *tag, struct dentry *dentry)
+{
+	printk(">>> %s: %p/%i %x \"%.*s\"\n", tag, dentry,
+		atomic_read(&dentry->d_count), dentry->d_flags,
+		dentry->d_name.len, dentry->d_name.name);
+}
+
 #endif	/* __LINUX_DCACHE_H */
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
index f273415..54ef185 100644
--- a/include/linux/ext2_fs_sb.h
+++ b/include/linux/ext2_fs_sb.h
@@ -106,6 +106,7 @@ struct ext2_sb_info {
 	spinlock_t s_rsv_window_lock;
 	struct rb_root s_rsv_window_root;
 	struct ext2_reserve_window_node s_rsv_window_head;
+	struct list_head delete;
 };
 
 #endif	/* _LINUX_EXT2_FS_SB */
_______________________________________________
Tux3 mailing list
[email protected]
http://mailman.tux3.org/cgi-bin/mailman/listinfo/tux3

Reply via email to