+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
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);
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,
struct inode **inodep)
{
int err;
- int version;
struct fuse_entry_out outarg;
struct inode *inode = NULL;
struct fuse_conn *fc = get_fuse_conn(dir);
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;
}
}
struct fuse_entry_out outarg;
struct inode *inode;
struct fuse_inode *fi;
- int version;
int err;
req->in.h.nodeid = get_node_id(dir);
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);
* 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;
* 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
#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
};
struct fuse_forget_in {
- __u64 version;
+ __u64 nlookup;
};
struct fuse_attr_out {
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);
}
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;
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;
}
}
}
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;
goto retry;
}
+ fi = get_fuse_inode(inode);
+ fi->nlookup ++;
fuse_change_attributes(inode, attr);
- inode->i_version = version;
return inode;
}
#else
}
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:
goto retry;
}
+ fi = get_fuse_inode(inode);
+ fi->nlookup ++;
fuse_change_attributes(inode, attr);
- inode->i_version = version;
return inode;
}
#endif
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
nodeid_t parent;
char *name;
uint64_t version;
+ uint64_t nlookup;
int open_count;
int is_hidden;
};
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);
hash_id(f, node);
}
node->version = version;
+ node->nlookup ++;
out_err:
pthread_mutex_unlock(&f->lock);
return node;
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;
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)
}
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,
{
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)
}
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,
}
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)
}
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);
}
}
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,
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;
root->nodeid = FUSE_ROOT_ID;
root->generation = 0;
root->refctr = 1;
+ root->nlookup = 1;
hash_id(f, root);
f->owner = getuid();