]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/fuse/dev.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / fs / fuse / dev.c
index 63d2cf43b5e31c5df807523713a239f2527e9cf5..104a62dadb94c9b8fa489963b51a97bc69b3dba2 100644 (file)
@@ -72,10 +72,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)
@@ -90,79 +88,63 @@ 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)
-{
-       if (atomic_dec_and_test(&req->count))
-               fuse_putback_request(fc, req);
-}
-
+/*
+ * Called with sbput_sem held for read (request_end) or write
+ * (fuse_put_super).  By the time fuse_put_super() is finished, all
+ * inodes belonging to background requests must be released, so the
+ * iputs have to be done within the locked region.
+ */
 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);
+       if (fc->num_background == FUSE_MAX_BACKGROUND) {
+               fc->blocked = 0;
+               wake_up_all(&fc->blocked_waitq);
+       }
+       fc->num_background--;
        spin_unlock(&fc->lock);
 }
 
@@ -189,9 +171,9 @@ 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 {
                void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
                req->end = NULL;
@@ -200,6 +182,11 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
                if (fc->mounted)
                        fuse_release_background(fc, req);
                up_read(&fc->sbput_sem);
+
+               /* fput must go outside sbput_sem, otherwise it can deadlock */
+               if (req->file)
+                       fput(req->file);
+
                if (end)
                        end(fc, req);
                else
@@ -240,6 +227,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)
@@ -302,18 +292,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);
 }
@@ -343,6 +327,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);
@@ -361,9 +346,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);
 }
 
@@ -739,7 +721,7 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
        struct fuse_copy_state cs;
        struct fuse_conn *fc = fuse_get_conn(file);
        if (!fc)
-               return -ENODEV;
+               return -EPERM;
 
        fuse_copy_init(&cs, fc, 0, NULL, iov, nr_segs);
        if (nbytes < sizeof(struct fuse_out_header))
@@ -930,7 +912,7 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
 {
        struct fuse_conn *fc = fuse_get_conn(file);
        if (!fc)
-               return -ENODEV;
+               return -EPERM;
 
        /* No locking - fasync_helper does its own locking */
        return fasync_helper(fd, file, on, &fc->fasync);