From: Miklos Szeredi Date: Sun, 8 May 2005 19:47:22 +0000 (+0000) Subject: fix X-Git-Tag: fuse_2_3_pre7~1 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=3800902d3a379366d5bbe51c73b24fdd5a0e5b78;p=qemu-gpiodev%2Flibfuse.git fix --- diff --git a/ChangeLog b/ChangeLog index 0e9e54d..e1051c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2005-05-08 Miklos Szeredi + + * 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 * Fix abort for out of order FORGET messages. Again. Spotted by diff --git a/kernel/dir.c b/kernel/dir.c index 0912d50..078d517 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -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); diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 25724b4..6ba851e 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -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 diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index 3c06967..104d31a 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -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 @@ -113,7 +113,7 @@ struct fuse_entry_out { }; struct fuse_forget_in { - __u64 version; + __u64 nlookup; }; struct fuse_attr_out { diff --git a/kernel/inode.c b/kernel/inode.c index 6ef9491..4c36ad0 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -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 diff --git a/lib/fuse.c b/lib/fuse.c index 9bc933f..04fb2f0 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -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();