From: Miklos S. <mi...@sz...> - 2009-01-28 11:18:23
|
On Tue, 27 Jan 2009, David Shaw wrote: > Hello, > > We've hit another little problem when NFS exporting a fuse filesystem. > The kernel is 2.6.27.9-159.fc10.i686.PAE (that is a Fedora kernel - we > haven't tried this with a vanilla kernel yet), and the userspace side > is fuse-2.8.0pre2. > > I've attached a sample program that reproduces the problem fairly > quickly. We use it like this: > > 1) Run fusexmp and NFS export the resulting filesystem > 2) Mount the filesystem on a client as /mnt/testing > 3) Run the attached program on the client > > After a while, there starts to be ESTALE errors returned. We did some > digging, and the requests that fail don't seem to be making it to the > userspace process (that is, they fail at a lower level, and the > userspace process never knows about them). > > The amount of memory seems to be relevant here, as it is a lot harder > to duplicate the problem when there is more memory available. This is known. Please try the attached patch for libfuse and use "noforget" and the "use_ino" or "readdir_ino" options. Thanks, Miklos Index: include/fuse_common.h =================================================================== --- include/fuse_common.h.orig 2008-12-08 20:27:39.000000000 +0100 +++ include/fuse_common.h 2009-01-28 12:07:22.000000000 +0100 @@ -86,11 +86,13 @@ struct fuse_file_info { * FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests * FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking * FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag + * FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." * FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB */ #define FUSE_CAP_ASYNC_READ (1 << 0) #define FUSE_CAP_POSIX_LOCKS (1 << 1) #define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) #define FUSE_CAP_BIG_WRITES (1 << 5) /** Index: lib/fuse.c =================================================================== --- lib/fuse.c.orig 2009-01-28 10:46:45.000000000 +0100 +++ lib/fuse.c 2009-01-28 12:07:22.000000000 +0100 @@ -48,6 +48,7 @@ struct fuse_config { double attr_timeout; double ac_attr_timeout; int ac_attr_timeout_set; + int noforget; int debug; int hard_remove; int use_ino; @@ -409,12 +410,17 @@ static struct node *find_node(struct fus struct node *node; pthread_mutex_lock(&f->lock); - node = lookup_node(f, parent, name); + if (!name) + node = get_node(f, parent); + else + node = lookup_node(f, parent, name); if (node == NULL) { node = (struct node *) calloc(1, sizeof(struct node)); if (node == NULL) goto out_err; + if (f->conf.noforget) + node->nlookup = 1; node->refctr = 1; node->nodeid = next_id(f); node->generation = f->generation; @@ -807,6 +813,15 @@ static void forget_node(struct fuse *f, pthread_mutex_unlock(&f->lock); } +static void unlink_node(struct fuse *f, struct node *node) +{ + if (f->conf.noforget) { + assert(node->nlookup > 1); + node->nlookup--; + } + unhash_name(f, node); +} + static void remove_node(struct fuse *f, fuse_ino_t dir, const char *name) { struct node *node; @@ -814,7 +829,7 @@ static void remove_node(struct fuse *f, pthread_mutex_lock(&f->lock); node = lookup_node(f, dir, name); if (node != NULL) - unhash_name(f, node); + unlink_node(f, node); pthread_mutex_unlock(&f->lock); } @@ -837,7 +852,7 @@ static int rename_node(struct fuse *f, f err = -EBUSY; goto out; } - unhash_name(f, newnode); + unlink_node(f, newnode); } unhash_name(f, node); @@ -1928,6 +1943,7 @@ static void fuse_lib_init(void *data, st memset(c, 0, sizeof(*c)); c->ctx.fuse = f; + conn->want |= FUSE_CAP_EXPORT_SUPPORT; fuse_fs_init(f->fs, conn); } @@ -1959,6 +1975,32 @@ static void fuse_lib_lookup(fuse_req_t r struct fuse_entry_param e; char *path; int err; + struct node *dot = NULL; + + if (name[0] == '.') { + int len = strlen(name); + + if (len == 1 || (name[1] == '.' && len == 2)) { + pthread_mutex_lock(&f->lock); + if (len == 1) { + if (f->conf.debug) + fprintf(stderr, "LOOKUP-DOT\n"); + dot = get_node_nocheck(f, parent); + if (dot == NULL) { + pthread_mutex_unlock(&f->lock); + reply_entry(req, &e, -ESTALE); + return; + } + dot->refctr++; + } else { + if (f->conf.debug) + fprintf(stderr, "LOOKUP-DOTDOT\n"); + parent = get_node(f, parent)->parent->nodeid; + } + pthread_mutex_unlock(&f->lock); + name = NULL; + } + } err = get_path_name(f, parent, name, &path); if (!err) { @@ -1975,6 +2017,11 @@ static void fuse_lib_lookup(fuse_req_t r fuse_finish_interrupt(f, req, &d); free_path(f, parent, path); } + if (dot) { + pthread_mutex_lock(&f->lock); + unref_node(f, dot); + pthread_mutex_unlock(&f->lock); + } reply_entry(req, &e, err); } @@ -3448,6 +3495,7 @@ static const struct fuse_opt fuse_lib_op FUSE_LIB_OPT("ac_attr_timeout=%lf", ac_attr_timeout, 0), FUSE_LIB_OPT("ac_attr_timeout=", ac_attr_timeout_set, 1), FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0), + FUSE_LIB_OPT("noforget", noforget, 1), FUSE_LIB_OPT("intr", intr, 1), FUSE_LIB_OPT("intr_signal=%d", intr_signal, 0), FUSE_LIB_OPT("modules=%s", modules, 0), Index: lib/fuse_lowlevel.c =================================================================== --- lib/fuse_lowlevel.c.orig 2009-01-28 10:46:45.000000000 +0100 +++ lib/fuse_lowlevel.c 2009-01-28 12:07:22.000000000 +0100 @@ -52,6 +52,7 @@ struct fuse_ll { int debug; int allow_root; int atomic_o_trunc; + int no_remote_lock; int big_writes; struct fuse_lowlevel_ops op; int got_init; @@ -1174,6 +1175,8 @@ static void do_init(fuse_req_t req, fuse f->conn.capable |= FUSE_CAP_POSIX_LOCKS; if (arg->flags & FUSE_ATOMIC_O_TRUNC) f->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; + if (arg->flags & FUSE_EXPORT_SUPPORT) + f->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; if (arg->flags & FUSE_BIG_WRITES) f->conn.capable |= FUSE_CAP_BIG_WRITES; } else { @@ -1183,7 +1186,7 @@ static void do_init(fuse_req_t req, fuse if (f->atomic_o_trunc) f->conn.want |= FUSE_CAP_ATOMIC_O_TRUNC; - if (f->op.getlk && f->op.setlk) + if (f->op.getlk && f->op.setlk && !f->no_remote_lock) f->conn.want |= FUSE_CAP_POSIX_LOCKS; if (f->big_writes) f->conn.want |= FUSE_CAP_BIG_WRITES; @@ -1211,6 +1214,8 @@ static void do_init(fuse_req_t req, fuse outarg.flags |= FUSE_POSIX_LOCKS; if (f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) outarg.flags |= FUSE_ATOMIC_O_TRUNC; + if (f->conn.want & FUSE_CAP_EXPORT_SUPPORT) + outarg.flags |= FUSE_EXPORT_SUPPORT; if (f->conn.want & FUSE_CAP_BIG_WRITES) outarg.flags |= FUSE_BIG_WRITES; outarg.max_readahead = f->conn.max_readahead; @@ -1433,6 +1438,7 @@ static struct fuse_opt fuse_ll_opts[] = { "async_read", offsetof(struct fuse_ll, conn.async_read), 1 }, { "sync_read", offsetof(struct fuse_ll, conn.async_read), 0 }, { "atomic_o_trunc", offsetof(struct fuse_ll, atomic_o_trunc), 1}, + { "no_remote_lock", offsetof(struct fuse_ll, no_remote_lock), 1}, { "big_writes", offsetof(struct fuse_ll, big_writes), 1}, FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD), FUSE_OPT_KEY("-h", KEY_HELP), @@ -1456,7 +1462,8 @@ static void fuse_ll_help(void) " -o async_read perform reads asynchronously (default)\n" " -o sync_read perform reads synchronously\n" " -o atomic_o_trunc enable atomic open+truncate support\n" -" -o big_writes enable larger than 4kB writes\n"); +" -o big_writes enable larger than 4kB writes\n" +" -o no_remote_lock disable remote file locking\n"); } static int fuse_ll_opt_proc(void *data, const char *arg, int key, |