From: Miklos Szeredi This patch changes the way in which the kernel notifies the userspace filesystem that it has no more reference to a particular inode (FORGET message). The old way was to set inode's i_version after each successful LOOKUP operation, and send this version in the FORGET message invoked from the clear_inode() method. If the userspace filesystem received a FORGET with an old version, it discarded it. The problem with this was that it was possible that two FORGETs would be sent to userspace, and the filesystem would process them out of order. In this case the when processing the FORGET sent first, the node is already deleted. The only solution would be for the userspace filesystem to somehow validate the node ID received in the FORGET. It would be much better if the node ID was always valid. So instead of sending a version number in FORGET. The number of lookups on an inode is accounted by both the kernel and the userspace part. A FORGET is sent with the stored lookup number. In userspace when a node's lookup counter goes to zero, it can be deleted. This mechanism ensures that FORGETs processed out order do not cause a problem. This change needs libfuse version 2.3-pre7 or later. Signed-off-by: Miklos Szeredi Signed-off-by: Andrew Morton --- fs/fuse/dir.c | 25 ++++++++++++------------- fs/fuse/fuse_i.h | 7 +++++-- fs/fuse/inode.c | 15 +++++++++------ include/linux/fuse.h | 4 ++-- 4 files changed, 28 insertions(+), 23 deletions(-) diff -puN fs/fuse/dir.c~fuse-read-write-operations-fix-lookup-forget-interface fs/fuse/dir.c --- 25/fs/fuse/dir.c~fuse-read-write-operations-fix-lookup-forget-interface 2005-05-10 02:21:32.000000000 -0700 +++ 25-akpm/fs/fuse/dir.c 2005-05-10 02:21:50.000000000 -0700 @@ -42,7 +42,6 @@ static int fuse_dentry_revalidate(struct return 0; else if (time_after(jiffies, entry->d_time)) { int err; - int version; struct fuse_entry_out outarg; struct inode *inode = entry->d_inode; struct fuse_inode *fi = get_fuse_inode(inode); @@ -53,15 +52,19 @@ static int fuse_dentry_revalidate(struct fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg); request_send_nonint(fc, req); - version = req->out.h.unique; err = req->out.h.error; + if (!err) { + if (outarg.nodeid != get_node_id(inode)) { + fuse_send_forget(fc, req, outarg.nodeid, 1); + return 0; + } + fi->nlookup ++; + } fuse_put_request(fc, req); - if (err || outarg.nodeid != get_node_id(inode) || - (outarg.attr.mode ^ inode->i_mode) & S_IFMT) + if (err || (outarg.attr.mode ^ inode->i_mode) & S_IFMT) return 0; fuse_change_attributes(inode, &outarg.attr); - inode->i_version = version; entry->d_time = time_to_jiffies(outarg.entry_valid, outarg.entry_valid_nsec); fi->i_time = time_to_jiffies(outarg.attr_valid, @@ -78,7 +81,6 @@ static int fuse_lookup_iget(struct inode struct inode **inodep) { int err; - int version; struct fuse_entry_out outarg; struct inode *inode = NULL; struct fuse_conn *fc = get_fuse_conn(dir); @@ -93,13 +95,12 @@ static int fuse_lookup_iget(struct inode fuse_lookup_init(req, dir, entry, &outarg); request_send(fc, req); - version = req->out.h.unique; err = req->out.h.error; if (!err) { inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, version); + &outarg.attr); if (!inode) { - fuse_send_forget(fc, req, outarg.nodeid, version); + fuse_send_forget(fc, req, outarg.nodeid, 1); return -ENOMEM; } } @@ -138,7 +139,6 @@ static int create_new_entry(struct fuse_ struct fuse_entry_out outarg; struct inode *inode; struct fuse_inode *fi; - int version; int err; req->in.h.nodeid = get_node_id(dir); @@ -147,16 +147,15 @@ static int create_new_entry(struct fuse_ req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; request_send(fc, req); - version = req->out.h.unique; err = req->out.h.error; if (err) { fuse_put_request(fc, req); return err; } inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, version); + &outarg.attr); if (!inode) { - fuse_send_forget(fc, req, outarg.nodeid, version); + fuse_send_forget(fc, req, outarg.nodeid, 1); return -ENOMEM; } fuse_put_request(fc, req); diff -puN fs/fuse/fuse_i.h~fuse-read-write-operations-fix-lookup-forget-interface fs/fuse/fuse_i.h --- 25/fs/fuse/fuse_i.h~fuse-read-write-operations-fix-lookup-forget-interface 2005-05-10 02:21:32.000000000 -0700 +++ 25-akpm/fs/fuse/fuse_i.h 2005-05-10 02:21:50.000000000 -0700 @@ -30,6 +30,9 @@ struct fuse_inode { * and kernel */ u64 nodeid; + /** Number of lookups on this inode */ + u64 nlookup; + /** The request used for sending the FORGET message */ struct fuse_req *forget_req; @@ -252,13 +255,13 @@ extern spinlock_t fuse_lock; * Get a filled in inode */ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, - int generation, struct fuse_attr *attr, int version); + int generation, struct fuse_attr *attr); /** * Send FORGET command */ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, - unsigned long nodeid, int version); + unsigned long nodeid, u64 nlookup); /** * Initialise inode operations on regular files and special files diff -puN fs/fuse/inode.c~fuse-read-write-operations-fix-lookup-forget-interface fs/fuse/inode.c --- 25/fs/fuse/inode.c~fuse-read-write-operations-fix-lookup-forget-interface 2005-05-10 02:21:32.000000000 -0700 +++ 25-akpm/fs/fuse/inode.c 2005-05-10 02:21:50.000000000 -0700 @@ -51,6 +51,7 @@ static struct inode *fuse_alloc_inode(st fi = get_fuse_inode(inode); fi->i_time = jiffies - 1; fi->nodeid = 0; + fi->nlookup = 0; fi->forget_req = fuse_request_alloc(); if (!fi->forget_req) { kmem_cache_free(fuse_inode_cachep, inode); @@ -74,10 +75,10 @@ static void fuse_read_inode(struct inode } void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, - unsigned long nodeid, int version) + unsigned long nodeid, u64 nlookup) { struct fuse_forget_in *inarg = &req->misc.forget_in; - inarg->version = version; + inarg->nlookup = nlookup; req->in.h.opcode = FUSE_FORGET; req->in.h.nodeid = nodeid; req->in.numargs = 1; @@ -91,7 +92,7 @@ static void fuse_clear_inode(struct inod struct fuse_conn *fc = get_fuse_conn(inode); if (fc) { struct fuse_inode *fi = get_fuse_inode(inode); - fuse_send_forget(fc, fi->forget_req, fi->nodeid, inode->i_version); + fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); fi->forget_req = NULL; } } @@ -156,9 +157,10 @@ static int fuse_inode_set(struct inode * } struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, - int generation, struct fuse_attr *attr, int version) + int generation, struct fuse_attr *attr) { struct inode *inode; + struct fuse_inode *fi; struct fuse_conn *fc = get_fuse_conn_super(sb); int retried = 0; @@ -181,8 +183,9 @@ struct inode *fuse_iget(struct super_blo goto retry; } + fi = get_fuse_inode(inode); + fi->nlookup ++; fuse_change_attributes(inode, attr); - inode->i_version = version; return inode; } @@ -389,7 +392,7 @@ static struct inode *get_root_inode(stru attr.mode = mode; attr.ino = FUSE_ROOT_ID; - return fuse_iget(sb, 1, 0, &attr, 0); + return fuse_iget(sb, 1, 0, &attr); } static struct super_operations fuse_super_operations = { diff -puN include/linux/fuse.h~fuse-read-write-operations-fix-lookup-forget-interface include/linux/fuse.h --- 25/include/linux/fuse.h~fuse-read-write-operations-fix-lookup-forget-interface 2005-05-10 02:21:32.000000000 -0700 +++ 25-akpm/include/linux/fuse.h 2005-05-10 02:21:50.000000000 -0700 @@ -11,7 +11,7 @@ #include /** Version number of this interface */ -#define FUSE_KERNEL_VERSION 6 +#define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ #define FUSE_KERNEL_MINOR_VERSION 1 @@ -96,7 +96,7 @@ struct fuse_entry_out { }; struct fuse_forget_in { - __u64 version; + __u64 nlookup; }; struct fuse_attr_out { _