]> 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 6c740f860665f4de7bf2b91e7c2e70ae146263cb..104a62dadb94c9b8fa489963b51a97bc69b3dba2 100644 (file)
@@ -92,40 +92,52 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
 {
        struct fuse_req *req;
        sigset_t oldset;
+       int intr;
        int err;
 
+       atomic_inc(&fc->num_waiting);
        block_sigs(&oldset);
-       err = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
+       intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
        restore_sigs(&oldset);
-       if (err)
-               return ERR_PTR(-EINTR);
+       err = -EINTR;
+       if (intr)
+               goto out;
 
        req = fuse_request_alloc();
+       err = -ENOMEM;
        if (!req)
-               return ERR_PTR(-ENOMEM);
+               goto out;
 
-       atomic_inc(&fc->num_waiting);
-       fuse_request_init(req);
        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)) {
-               atomic_dec(&fc->num_waiting);
+               if (req->waiting)
+                       atomic_dec(&fc->num_waiting);
                fuse_request_free(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) {
@@ -170,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
@@ -277,6 +294,10 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
                len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
        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);
 }