fix
authorMiklos Szeredi <miklos@szeredi.hu>
Sun, 8 May 2005 19:47:22 +0000 (19:47 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Sun, 8 May 2005 19:47:22 +0000 (19:47 +0000)
ChangeLog
kernel/dir.c
kernel/fuse_i.h
kernel/fuse_kernel.h
kernel/inode.c
lib/fuse.c

index 0e9e54d10b38aecf9a737a4725807414878252df..e1051c11eb6fc6f1974d11380ffe79b4673d6933 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-05-08  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Better fix for out of order FORGET messages.  Now the
+       LOOKUP/FORGET messages are balanced exactly (one FORGET can
+       balance many lookups), so the order no longer matters.  This
+       changes the ABI slightly, but the library remains backward
+       compatible.
+
 2005-05-06  Miklos Szeredi <miklos@szeredi.hu>
 
        * Fix abort for out of order FORGET messages.  Again.  Spotted by
index 0912d50c08a6b8a1a10af827bbd5486488b7a1a7..078d517c0f6b8d946044ce5f6600670c46c325c1 100644 (file)
@@ -48,7 +48,6 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
                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);
@@ -59,15 +58,19 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
 
                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,
@@ -94,7 +97,6 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
                            struct inode **inodep)
 {
        int err;
-       int version;
        struct fuse_entry_out outarg;
        struct inode *inode = NULL;
        struct fuse_conn *fc = get_fuse_conn(dir);
@@ -109,13 +111,12 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry,
 
        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;
                }
        }
@@ -154,7 +155,6 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
        struct fuse_entry_out outarg;
        struct inode *inode;
        struct fuse_inode *fi;
-       int version;
        int err;
 
        req->in.h.nodeid = get_node_id(dir);
@@ -163,16 +163,15 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req,
        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);
index 25724b43da1cc7058c45cb743c5d61d15cd572f0..6ba851e3f3405cb25d0820bca66dd6f982797302 100644 (file)
@@ -110,6 +110,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;
 
@@ -390,13 +393,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);
 
 /**
  * Send READ or READDIR request
index 3c0696796ca6fef0999c0f765b0c60a4400fe1b2..104d31a24b4b8b010a01dddac9d27e9054314226 100644 (file)
@@ -11,7 +11,7 @@
 #include <asm/types.h>
 
 /** 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
@@ -113,7 +113,7 @@ struct fuse_entry_out {
 };
 
 struct fuse_forget_in {
-       __u64   version;
+       __u64   nlookup;
 };
 
 struct fuse_attr_out {
index 6ef9491afa461679cfa5377978b4ec5c9c40fd12..4c36ad04f1ae83a45addd0c74a82fac3ebf406b5 100644 (file)
@@ -63,6 +63,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
        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);
@@ -86,10 +87,10 @@ static void fuse_read_inode(struct inode *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;
@@ -103,7 +104,7 @@ static void fuse_clear_inode(struct inode *inode)
        if (inode->i_sb->s_flags & MS_ACTIVE) {
                struct fuse_conn *fc = get_fuse_conn(inode);
                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;
        }
 }
@@ -181,9 +182,10 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp)
 }
 
 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;
 
@@ -206,8 +208,9 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
                goto retry;
        }
 
+       fi = get_fuse_inode(inode);
+       fi->nlookup ++;
        fuse_change_attributes(inode, attr);
-       inode->i_version = version;
        return inode;
 }
 #else
@@ -220,9 +223,10 @@ static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp)
 }
 
 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;
        int retried = 0;
 
  retry:
@@ -245,8 +249,9 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
                goto retry;
        }
 
+       fi = get_fuse_inode(inode);
+       fi->nlookup ++;
        fuse_change_attributes(inode, attr);
-       inode->i_version = version;
        return inode;
 }
 #endif
@@ -523,7 +528,7 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
 
        attr.mode = mode;
        attr.ino = FUSE_ROOT_ID;
-       return fuse_iget(sb, 1, 0, &attr, 0);
+       return fuse_iget(sb, 1, 0, &attr);
 }
 
 #ifdef KERNEL_2_6
index 9bc933fd59dda6303af877e5f28d72d5dfc3c43e..04fb2f0fac50c65ee9a61be4f90d80e40a3a6fa4 100644 (file)
@@ -68,6 +68,7 @@ struct node {
     nodeid_t parent;
     char *name;
     uint64_t version;
+    uint64_t nlookup;
     int open_count;
     int is_hidden;
 };
@@ -254,6 +255,10 @@ static int hash_name(struct fuse *f, struct node *node, nodeid_t parent,
 
 static void delete_node(struct fuse *f, struct node *node)
 {
+    if (f->flags & FUSE_DEBUG) {
+        printf("delete: %lu\n", node->nodeid);
+        fflush(stdout);
+    }
     assert(!node->name);
     unhash_id(f, node);
     free_node(node);
@@ -325,6 +330,7 @@ static struct node *find_node(struct fuse *f, nodeid_t parent, char *name,
         hash_id(f, node);
     }
     node->version = version;
+    node->nlookup ++;
  out_err:
     pthread_mutex_unlock(&f->lock);
     return node;
@@ -386,7 +392,23 @@ static char *get_path(struct fuse *f, nodeid_t nodeid)
     return get_path_name(f, nodeid, NULL);
 }
 
-static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t version)
+static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t nlookup)
+{
+    struct node *node;
+    if (nodeid == FUSE_ROOT_ID)
+        return;
+    pthread_mutex_lock(&f->lock);
+    node = get_node(f, nodeid);
+    assert(node->nlookup >= nlookup);
+    node->nlookup -= nlookup;
+    if (!node->nlookup) {
+        unhash_name(f, node);
+        unref_node(f, node);
+    }
+    pthread_mutex_unlock(&f->lock);
+}
+
+static void forget_node_old(struct fuse *f, nodeid_t nodeid, uint64_t version)
 {
     struct node *node;
 
@@ -398,7 +420,14 @@ static void forget_node(struct fuse *f, nodeid_t nodeid, uint64_t version)
         unref_node(f, node);
     }
     pthread_mutex_unlock(&f->lock);
+}
 
+static void cancel_lookup(struct fuse *f, nodeid_t nodeid, uint64_t version)
+{
+    if (f->major <= 6)
+        forget_node_old(f, nodeid, version);
+    else
+        forget_node(f, nodeid, 1);
 }
 
 static void remove_node(struct fuse *f, nodeid_t dir, const char *name)
@@ -658,7 +687,7 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
     }
     res2 = send_reply(f, in, res, &arg, sizeof(arg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, arg.nodeid, in->unique);
+        cancel_lookup(f, arg.nodeid, in->unique);
 }
 
 static void do_forget(struct fuse *f, struct fuse_in_header *in,
@@ -666,10 +695,13 @@ static void do_forget(struct fuse *f, struct fuse_in_header *in,
 {
     if (f->flags & FUSE_DEBUG) {
         printf("FORGET %lu/%llu\n", (unsigned long) in->nodeid,
-               arg->version);
+               arg->nlookup);
         fflush(stdout);
     }
-    forget_node(f, in->nodeid, arg->version);
+    if (f->major <= 6)
+        forget_node_old(f, in->nodeid, arg->nlookup);
+    else
+        forget_node(f, in->nodeid, arg->nlookup);
 }
 
 static void do_getattr(struct fuse *f, struct fuse_in_header *in)
@@ -836,7 +868,7 @@ static void do_mknod(struct fuse *f, struct fuse_in_header *in,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 }
 
 static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
@@ -865,7 +897,7 @@ static void do_mkdir(struct fuse *f, struct fuse_in_header *in,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 }
 
 static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
@@ -944,7 +976,7 @@ static void do_symlink(struct fuse *f, struct fuse_in_header *in, char *name,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 
 }
 
@@ -1019,7 +1051,7 @@ static void do_link(struct fuse *f, struct fuse_in_header *in,
     }
     res2 = send_reply(f, in, res, &outarg, sizeof(outarg));
     if (res == 0 && res2 == -ENOENT)
-        forget_node(f, outarg.nodeid, in->unique);
+        cancel_lookup(f, outarg.nodeid, in->unique);
 }
 
 static void do_open(struct fuse *f, struct fuse_in_header *in,
@@ -1483,6 +1515,9 @@ static void do_init(struct fuse *f, struct fuse_in_header *in,
     if (arg->major == 5) {
         f->major = 5;
         f->minor = 1;
+    } else if (arg->major == 6) {
+        f->major = 6;
+        f->minor = 1;
     } else {
         f->major = FUSE_KERNEL_VERSION;
         f->minor = FUSE_KERNEL_MINOR_VERSION;
@@ -2113,6 +2148,7 @@ struct fuse *fuse_new_common(int fd, const char *opts,
     root->nodeid = FUSE_ROOT_ID;
     root->generation = 0;
     root->refctr = 1;
+    root->nlookup = 1;
     hash_id(f, root);
 
     f->owner = getuid();