fix
authorMiklos Szeredi <miklos@szeredi.hu>
Wed, 12 Apr 2006 10:41:50 +0000 (10:41 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Wed, 12 Apr 2006 10:41:50 +0000 (10:41 +0000)
ChangeLog
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/inode.c

index 97fdce3620bdc7e269b390a2970c8bad23ac5613..0c598b123b3621102897d9c0ee6d3f09c0348b24 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2006-04-11  Csaba Henk <csaba.henk@creo.hu>
+
+       * kernel: remove request pool, instead allocate requests on
+       demand.  Account the number of background requests, and if they go
+       over a limit, block the allocation of new requests.
+
+       * kernel: fix deadlock if backgrounded request holds the last
+       reference to the super block
+
+       * kernel: don't use fuse_reset_request() during direct I/O
+
 2006-04-06  Csaba Henk <csaba.henk@creo.hu>
 
        * lib: Let FreeBSD mount option parsing routine recognize "no"
index c9689418576a887a34ac8aa2b25da85026fe978d..932e9c35f0582413b3b65097e288a33f3835847a 100644 (file)
@@ -112,10 +112,8 @@ static void restore_sigs(sigset_t *oldset)
  */
 void fuse_reset_request(struct fuse_req *req)
 {
-       int preallocated = req->preallocated;
        BUG_ON(atomic_read(&req->count) != 1);
        fuse_request_init(req);
-       req->preallocated = preallocated;
 }
 
 static void __fuse_get_request(struct fuse_req *req)
@@ -130,80 +128,54 @@ static void __fuse_put_request(struct fuse_req *req)
        atomic_dec(&req->count);
 }
 
-static struct fuse_req *do_get_request(struct fuse_conn *fc)
+struct fuse_req *fuse_get_req(struct fuse_conn *fc)
 {
        struct fuse_req *req;
-
-       spin_lock(&fc->lock);
-       BUG_ON(list_empty(&fc->unused_list));
-       req = list_entry(fc->unused_list.next, struct fuse_req, list);
-       list_del_init(&req->list);
-       spin_unlock(&fc->lock);
-       fuse_request_init(req);
-       req->preallocated = 1;
-       req->in.h.uid = current->fsuid;
-       req->in.h.gid = current->fsgid;
-       req->in.h.pid = current->pid;
-       return req;
-}
-
-/* This can return NULL, but only in case it's interrupted by a SIGKILL */
-struct fuse_req *fuse_get_request(struct fuse_conn *fc)
-{
-       int intr;
        sigset_t oldset;
+       int intr;
+       int err;
 
        atomic_inc(&fc->num_waiting);
        block_sigs(&oldset);
-       intr = down_interruptible(&fc->outstanding_sem);
+       intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
        restore_sigs(&oldset);
-       if (intr) {
-               atomic_dec(&fc->num_waiting);
-               return NULL;
-       }
-       return do_get_request(fc);
-}
+       err = -EINTR;
+       if (intr)
+               goto out;
 
-/* Must be called with fc->lock held */
-static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
-{
-       if (req->preallocated) {
-               atomic_dec(&fc->num_waiting);
-               list_add(&req->list, &fc->unused_list);
-       } else
-               fuse_request_free(req);
+       req = fuse_request_alloc();
+       err = -ENOMEM;
+       if (!req)
+               goto out;
 
-       /* If we are in debt decrease that first */
-       if (fc->outstanding_debt)
-               fc->outstanding_debt--;
-       else
-               up(&fc->outstanding_sem);
+       req->in.h.uid = current->fsuid;
+       req->in.h.gid = current->fsgid;
+       req->in.h.pid = current->pid;
+       req->waiting = 1;
+       return req;
+
+ out:
+       atomic_dec(&fc->num_waiting);
+       return ERR_PTR(err);
 }
 
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        if (atomic_dec_and_test(&req->count)) {
-               spin_lock(&fc->lock);
-               fuse_putback_request(fc, req);
-               spin_unlock(&fc->lock);
+               if (req->waiting)
+                       atomic_dec(&fc->num_waiting);
+               fuse_request_free(req);
        }
 }
 
-static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req)
+void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req)
 {
-       if (atomic_dec_and_test(&req->count))
-               fuse_putback_request(fc, req);
-}
-
-void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
-{
-       iput(req->inode);
-       iput(req->inode2);
-       if (req->file)
-               fput(req->file);
-       spin_lock(&fc->lock);
-       list_del(&req->bg_entry);
-       spin_unlock(&fc->lock);
+       list_del_init(&req->bg_entry);
+       if (fc->num_background == FUSE_MAX_BACKGROUND) {
+               fc->blocked = 0;
+               wake_up_all(&fc->blocked_waitq);
+       }
+       fc->num_background--;
 }
 
 /*
@@ -229,21 +201,31 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
        list_del(&req->list);
        req->state = FUSE_REQ_FINISHED;
        if (!req->background) {
-               wake_up(&req->waitq);
-               fuse_put_request_locked(fc, req);
                spin_unlock(&fc->lock);
+               wake_up(&req->waitq);
+               fuse_put_request(fc, req);
        } else {
+               struct inode *inode = req->inode;
+               struct inode *inode2 = req->inode2;
+               struct file *file = req->file;
                void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
                req->end = NULL;
+               req->inode = NULL;
+               req->inode2 = NULL;
+               req->file = NULL;
+               if (!list_empty(&req->bg_entry))
+                       fuse_remove_background(fc, req);
                spin_unlock(&fc->lock);
-               down_read(&fc->sbput_sem);
-               if (fc->mounted)
-                       fuse_release_background(fc, req);
-               up_read(&fc->sbput_sem);
+
                if (end)
                        end(fc, req);
                else
                        fuse_put_request(fc, req);
+
+               if (file)
+                       fput(file);
+               iput(inode);
+               iput(inode2);
        }
 }
 
@@ -280,6 +262,9 @@ static void background_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        req->background = 1;
        list_add(&req->bg_entry, &fc->background);
+       fc->num_background++;
+       if (fc->num_background == FUSE_MAX_BACKGROUND)
+               fc->blocked = 1;
        if (req->inode)
                req->inode = igrab(req->inode);
        if (req->inode2)
@@ -342,18 +327,12 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
        req->in.h.unique = fc->reqctr;
        req->in.h.len = sizeof(struct fuse_in_header) +
                len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
-       if (!req->preallocated) {
-               /* If request is not preallocated (either FORGET or
-                  RELEASE), then still decrease outstanding_sem, so
-                  user can't open infinite number of files while not
-                  processing the RELEASE requests.  However for
-                  efficiency do it without blocking, so if down()
-                  would block, just increase the debt instead */
-               if (down_trylock(&fc->outstanding_sem))
-                       fc->outstanding_debt++;
-       }
        list_add_tail(&req->list, &fc->pending);
        req->state = FUSE_REQ_PENDING;
+       if (!req->waiting) {
+               req->waiting = 1;
+               atomic_inc(&fc->num_waiting);
+       }
        wake_up(&fc->waitq);
        kill_fasync(&fc->fasync, SIGIO, POLL_IN);
 }
@@ -383,6 +362,7 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req)
 static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
 {
        spin_lock(&fc->lock);
+       background_request(fc, req);
        if (fc->connected) {
                queue_request(fc, req);
                spin_unlock(&fc->lock);
@@ -401,9 +381,6 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
 void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 {
        req->isreply = 1;
-       spin_lock(&fc->lock);
-       background_request(fc, req);
-       spin_unlock(&fc->lock);
        request_send_nowait(fc, req);
 }
 
@@ -669,8 +646,8 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
  restart:
        spin_lock(&fc->lock);
        err = -EAGAIN;
-       if((file->f_flags & O_NONBLOCK) && fc->connected &&
-          list_empty(&fc->pending))
+       if ((file->f_flags & O_NONBLOCK) && fc->connected &&
+           list_empty(&fc->pending))
                goto err_unlock;
 
        request_wait(fc);
index 1cee4a23670c1cacb1710f25dd392097b3cbd9d6..a2887b6b39a280c952a378397ee9a4ce24ea0b61 100644 (file)
@@ -123,8 +123,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd)
                        return 0;
 
                fc = get_fuse_conn(inode);
-               req = fuse_get_request(fc);
-               if (!req)
+               req = fuse_get_req(fc);
+               if (IS_ERR(req))
                        return 0;
 
                fuse_lookup_init(req, entry->d_parent->d_inode, entry, &outarg);
@@ -215,9 +215,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        if (entry->d_name.len > FUSE_NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return ERR_PTR(-EINTR);
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return ERR_PTR(PTR_ERR(req));
 
        fuse_lookup_init(req, dir, entry, &outarg);
        request_send(fc, req);
@@ -281,15 +281,14 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
        struct file *file;
        int flags = nd->intent.open.flags - 1;
 
-       err = -ENOSYS;
        if (fc->no_create)
-               goto out;
+               return -ENOSYS;
 
-       err = -EINTR;
-       req = fuse_get_request(fc);
-       if (!req)
-               goto out;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
+       err = -ENOMEM;
        ff = fuse_file_alloc();
        if (!ff)
                goto out_put_request;
@@ -351,7 +350,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
        fuse_file_free(ff);
  out_put_request:
        fuse_put_request(fc, req);
- out:
        return err;
 }
 #endif
@@ -413,9 +411,9 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
 {
        struct fuse_mknod_in inarg;
        struct fuse_conn *fc = get_fuse_conn(dir);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.mode = mode;
@@ -447,9 +445,9 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
 {
        struct fuse_mkdir_in inarg;
        struct fuse_conn *fc = get_fuse_conn(dir);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.mode = mode;
@@ -467,9 +465,9 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
 {
        struct fuse_conn *fc = get_fuse_conn(dir);
        unsigned len = strlen(link) + 1;
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        req->in.h.opcode = FUSE_SYMLINK;
        req->in.numargs = 2;
@@ -484,9 +482,9 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
 {
        int err;
        struct fuse_conn *fc = get_fuse_conn(dir);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        req->in.h.opcode = FUSE_UNLINK;
        req->in.h.nodeid = get_node_id(dir);
@@ -516,9 +514,9 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
 {
        int err;
        struct fuse_conn *fc = get_fuse_conn(dir);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        req->in.h.opcode = FUSE_RMDIR;
        req->in.h.nodeid = get_node_id(dir);
@@ -544,9 +542,9 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        int err;
        struct fuse_rename_in inarg;
        struct fuse_conn *fc = get_fuse_conn(olddir);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.newdir = get_node_id(newdir);
@@ -593,9 +591,9 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
        struct fuse_link_in inarg;
        struct inode *inode = entry->d_inode;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.oldnodeid = get_node_id(inode);
@@ -623,9 +621,9 @@ int fuse_do_getattr(struct inode *inode)
        int err;
        struct fuse_attr_out arg;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_req *req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       struct fuse_req *req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        req->in.h.opcode = FUSE_GETATTR;
        req->in.h.nodeid = get_node_id(inode);
@@ -719,9 +717,9 @@ static int fuse_access(struct inode *inode, int mask)
        if (fc->no_access)
                return 0;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.mask = mask;
@@ -842,9 +840,9 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
        if (is_bad_inode(inode))
                return -EIO;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        page = alloc_page(GFP_KERNEL);
        if (!page) {
@@ -871,11 +869,11 @@ static char *read_link(struct dentry *dentry)
 {
        struct inode *inode = dentry->d_inode;
        struct fuse_conn *fc = get_fuse_conn(inode);
-       struct fuse_req *req = fuse_get_request(fc);
+       struct fuse_req *req = fuse_get_req(fc);
        char *link;
 
-       if (!req)
-               return ERR_PTR(-EINTR);
+       if (IS_ERR(req))
+               return ERR_PTR(PTR_ERR(req));
 
        link = (char *) __get_free_page(GFP_KERNEL);
        if (!link) {
@@ -1042,9 +1040,9 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
                }
        }
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        iattr_to_fattr(attr, &inarg);
@@ -1138,9 +1136,9 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
        if (fc->no_setxattr)
                return -EOPNOTSUPP;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.size = size;
@@ -1178,9 +1176,9 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
        if (fc->no_getxattr)
                return -EOPNOTSUPP;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.size = size;
@@ -1228,9 +1226,9 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
        if (fc->no_listxattr)
                return -EOPNOTSUPP;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.size = size;
@@ -1274,9 +1272,9 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
        if (fc->no_removexattr)
                return -EOPNOTSUPP;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        req->in.h.opcode = FUSE_REMOVEXATTR;
        req->in.h.nodeid = get_node_id(inode);
index 77c540eef51b1d7b5b01fc14e3924726997caa8f..dd9758c3cfde9e8a59ac3d3ccad4305fa4957648 100644 (file)
@@ -32,9 +32,9 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir,
        struct fuse_req *req;
        int err;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
@@ -198,9 +198,9 @@ static int fuse_flush(struct file *file)
        if (fc->no_flush)
                return 0;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.fh = ff->fh;
@@ -237,9 +237,9 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
        if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
                return 0;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&inarg, 0, sizeof(inarg));
        inarg.fh = ff->fh;
@@ -311,9 +311,9 @@ static int fuse_readpage(struct file *file, struct page *page)
        if (is_bad_inode(inode))
                goto out;
 
-       err = -EINTR;
-       req = fuse_get_request(fc);
-       if (!req)
+       req = fuse_get_req(fc);
+       err = PTR_ERR(req);
+       if (IS_ERR(req))
                goto out;
 
        req->out.page_zeroing = 1;
@@ -383,10 +383,10 @@ static int fuse_readpages_fill(void *_data, struct page *page)
             (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
             req->pages[req->num_pages - 1]->index + 1 != page->index)) {
                fuse_send_readpages(req, data->file, inode);
-               data->req = req = fuse_get_request(fc);
-               if (!req) {
+               data->req = req = fuse_get_req(fc);
+               if (IS_ERR(req)) {
                        unlock_page(page);
-                       return -EINTR;
+                       return PTR_ERR(req);
                }
        }
        req->pages[req->num_pages] = page;
@@ -407,13 +407,17 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
 
        data.file = file;
        data.inode = inode;
-       data.req = fuse_get_request(fc);
-       if (!data.req)
-               return -EINTR;
+       data.req = fuse_get_req(fc);
+       if (IS_ERR(data.req))
+               return PTR_ERR(data.req);
 
        err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
-       if (!err)
-               fuse_send_readpages(data.req, file, inode);
+       if (!err) {
+               if (data.req->num_pages)
+                       fuse_send_readpages(data.req, file, inode);
+               else
+                       fuse_put_request(fc, data.req);
+       }
        return err;
 }
 #else /* KERNEL_2_6 */
@@ -497,9 +501,9 @@ static int fuse_file_bigread(struct file *file, struct inode *inode,
        starti = (pos & FUSE_BLOCK_MASK) >> PAGE_CACHE_SHIFT;
        endi = (end + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        for (; starti < endi; starti = nexti) {
                nexti = starti + (FUSE_BLOCK_SIZE >> PAGE_CACHE_SHIFT);
@@ -565,9 +569,9 @@ static int fuse_commit_write(struct file *file, struct page *page,
        if (is_bad_inode(inode))
                return -EIO;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        req->num_pages = 1;
        req->pages[0] = page;
@@ -643,9 +647,9 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
        if (is_bad_inode(inode))
                return -EIO;
 
-       req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        while (count) {
                size_t nres;
@@ -676,8 +680,12 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                buf += nres;
                if (nres != nbytes)
                        break;
-               if (count)
-                       fuse_reset_request(req);
+               if (count) {
+                       fuse_put_request(fc, req);
+                       req = fuse_get_req(fc);
+                       if (IS_ERR(req))
+                               break;
+               }
        }
        fuse_put_request(fc, req);
        if (res > 0) {
index d1f3e73cd083d3b428ae665e79f738b6e9aeea03..d8a1ac896ae686f8b01b091ac6e974b9575392e3 100644 (file)
@@ -110,8 +110,8 @@ static inline void kobject_put(struct kobject *kobj)
 /** Max number of pages that can be used in a single read request */
 #define FUSE_MAX_PAGES_PER_REQ 32
 
-/** If more requests are outstanding, then the operation will block */
-#define FUSE_MAX_OUTSTANDING 10
+/** Maximum number of outstanding background requests */
+#define FUSE_MAX_BACKGROUND 10
 
 /** It could be as large as PATH_MAX, but would that have any uses? */
 #define FUSE_NAME_MAX 1024
@@ -228,8 +228,8 @@ struct fuse_conn;
  * A request to the client
  */
 struct fuse_req {
-       /** This can be on either unused_list, pending processing or
-           io lists in fuse_conn */
+       /** This can be on either pending processing or io lists in
+           fuse_conn */
        struct list_head list;
 
        /** Entry on the background list */
@@ -247,9 +247,6 @@ struct fuse_req {
        /** True if the request has reply */
        unsigned isreply:1;
 
-       /** The request is preallocated */
-       unsigned preallocated:1;
-
        /** The request was interrupted */
        unsigned interrupted:1;
 
@@ -259,6 +256,9 @@ struct fuse_req {
        /** Data is being copied to/from the request */
        unsigned locked:1;
 
+       /** Request is counted as "waiting" */
+       unsigned waiting:1;
+
        /** State of the request */
        enum fuse_req_state state;
 
@@ -344,25 +344,20 @@ struct fuse_conn {
            interrupted request) */
        struct list_head background;
 
-       /** Controls the maximum number of outstanding requests */
-       struct semaphore outstanding_sem;
+       /** Number of requests currently in the background */
+       unsigned num_background;
 
-       /** This counts the number of outstanding requests if
-           outstanding_sem would go negative */
-       unsigned outstanding_debt;
+       /** Flag indicating if connection is blocked.  This will be
+           the case before the INIT reply is received, and if there
+           are too many outstading backgrounds requests */
+       int blocked;
 
-       /** RW semaphore for exclusion with fuse_put_super() */
-       struct rw_semaphore sbput_sem;
-
-       /** The list of unused requests */
-       struct list_head unused_list;
+       /** waitq for blocked connection */
+       wait_queue_head_t blocked_waitq;
 
        /** The next unique request id */
        u64 reqctr;
 
-       /** Mount is active */
-       unsigned mounted;
-
        /** Connection established, cleared on umount, connection
            abort and device release */
        unsigned connected;
@@ -551,11 +546,11 @@ void fuse_reset_request(struct fuse_req *req);
 /**
  * Reserve a preallocated request
  */
-struct fuse_req *fuse_get_request(struct fuse_conn *fc);
+struct fuse_req *fuse_get_req(struct fuse_conn *fc);
 
 /**
- * Decrement reference count of a request.  If count goes to zero put
- * on unused list (preallocated) or free request (not preallocated).
+ * Decrement reference count of a request.  If count goes to zero free
+ * the request.
  */
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req);
 
@@ -575,11 +570,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
 void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
 
 /**
- * Release inodes and file associated with background request
+ * Remove request from the the background list
  */
-void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req);
+void fuse_remove_background(struct fuse_conn *fc, struct fuse_req *req);
 
-/* Abort all requests */
+/** Abort all requests */
 void fuse_abort_conn(struct fuse_conn *fc);
 
 /**
index cc631899ccff8074e7180947de8848093deee144..c8c475342eb5c6d7f8646c54c477857939a59da2 100644 (file)
@@ -278,17 +278,26 @@ static void fuse_put_super(struct super_block *sb)
 {
        struct fuse_conn *fc = get_fuse_conn_super(sb);
 
-       down_write(&fc->sbput_sem);
-       while (!list_empty(&fc->background))
-               fuse_release_background(fc,
-                                       list_entry(fc->background.next,
-                                                  struct fuse_req, bg_entry));
-
        spin_lock(&fc->lock);
-       fc->mounted = 0;
        fc->connected = 0;
+       while (!list_empty(&fc->background)) {
+               struct fuse_req *req = list_entry(fc->background.next,
+                                                 struct fuse_req, bg_entry);
+               struct inode *inode = req->inode;
+               struct inode *inode2 = req->inode2;
+
+               /* File would hold a reference to vfsmount */
+               BUG_ON(req->file);
+               req->inode = NULL;
+               req->inode2 = NULL;
+               fuse_remove_background(fc, req);
+
+               spin_unlock(&fc->lock);
+               iput(inode);
+               iput(inode2);
+               spin_lock(&fc->lock);
+       }
        spin_unlock(&fc->lock);
-       up_write(&fc->sbput_sem);
        /* Flush all readers on this fs */
        kill_fasync(&fc->fasync, SIGIO, POLL_IN);
        wake_up_all(&fc->waitq);
@@ -321,9 +330,9 @@ static int fuse_statfs(struct super_block *sb, struct kstatfs *buf)
        struct fuse_statfs_out outarg;
        int err;
 
-        req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&outarg, 0, sizeof(outarg));
        req->in.numargs = 0;
@@ -463,15 +472,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
 
 static void fuse_conn_release(struct kobject *kobj)
 {
-       struct fuse_conn *fc = get_fuse_conn_kobj(kobj);
-
-       while (!list_empty(&fc->unused_list)) {
-               struct fuse_req *req;
-               req = list_entry(fc->unused_list.next, struct fuse_req, list);
-               list_del(&req->list);
-               fuse_request_free(req);
-       }
-       kfree(fc);
+       kfree(get_fuse_conn_kobj(kobj));
 }
 
 #ifndef HAVE_KZALLOC
@@ -489,16 +490,13 @@ static struct fuse_conn *new_conn(void)
 
        fc = kzalloc(sizeof(*fc), GFP_KERNEL);
        if (fc) {
-               int i;
                spin_lock_init(&fc->lock);
                init_waitqueue_head(&fc->waitq);
+               init_waitqueue_head(&fc->blocked_waitq);
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
                INIT_LIST_HEAD(&fc->io);
-               INIT_LIST_HEAD(&fc->unused_list);
                INIT_LIST_HEAD(&fc->background);
-               sema_init(&fc->outstanding_sem, 1); /* One for INIT */
-               init_rwsem(&fc->sbput_sem);
 #ifdef KERNEL_2_6
                kobj_set_kset_s(fc, connections_subsys);
                kobject_init(&fc->kobj);
@@ -507,19 +505,12 @@ static struct fuse_conn *new_conn(void)
                fc->kobj.release = fuse_conn_release;
 #endif
                atomic_set(&fc->num_waiting, 0);
-               for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
-                       struct fuse_req *req = fuse_request_alloc();
-                       if (!req) {
-                               kobject_put(&fc->kobj);
-                               return NULL;
-                       }
-                       list_add(&req->list, &fc->unused_list);
-               }
 #ifdef KERNEL_2_6_6_PLUS
                fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
                fc->bdi.unplug_io_fn = default_unplug_io_fn;
 #endif
                fc->reqctr = 0;
+               fc->blocked = 1;
        }
        return fc;
 }
@@ -611,7 +602,6 @@ static struct super_operations fuse_super_operations = {
 
 static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
 {
-       int i;
        struct fuse_init_out *arg = &req->misc.init_out;
 
        if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION)
@@ -632,22 +622,13 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
                fc->minor = arg->minor;
                fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
        }
-
-       /* After INIT reply is received other requests can go
-          out.  So do (FUSE_MAX_OUTSTANDING - 1) number of
-          up()s on outstanding_sem.  The last up() is done in
-          fuse_putback_request() */
-       for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
-               up(&fc->outstanding_sem);
-
        fuse_put_request(fc, req);
+       fc->blocked = 0;
+       wake_up_all(&fc->blocked_waitq);
 }
 
-static void fuse_send_init(struct fuse_conn *fc)
+static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
 {
-       /* This is called from fuse_read_super() so there's guaranteed
-          to be exactly one request available */
-       struct fuse_req *req = fuse_get_request(fc);
        struct fuse_init_in *arg = &req->misc.init_in;
 
        arg->major = FUSE_KERNEL_VERSION;
@@ -687,6 +668,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        struct fuse_mount_data d;
        struct file *file;
        struct dentry *root_dentry;
+       struct fuse_req *init_req;
        int err;
 
        if (!parse_fuse_opt((char *) data, &d))
@@ -738,18 +720,21 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
                goto err;
        }
 
+       init_req = fuse_request_alloc();
+       if (!init_req)
+               goto err_put_root;
+
 #ifdef KERNEL_2_6
        err = kobject_set_name(&fc->kobj, "%llu", conn_id());
        if (err)
-               goto err_put_root;
+               goto err_free_req;
 
        err = kobject_add(&fc->kobj);
        if (err)
-               goto err_put_root;
+               goto err_free_req;
 #endif
 
        sb->s_root = root_dentry;
-       fc->mounted = 1;
        fc->connected = 1;
        kobject_get(&fc->kobj);
        file->private_data = fc;
@@ -760,14 +745,16 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
         */
        fput(file);
 
-       fuse_send_init(fc);
+       fuse_send_init(fc, init_req);
 
        return 0;
 
 #ifdef KERNEL_2_6
+ err_free_req:
+       fuse_request_free(init_req);
+#endif
  err_put_root:
        dput(root_dentry);
-#endif
  err:
        fput(file);
        kobject_put(&fc->kobj);