Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / fs / fuse / dev.c
index 9bfd1a3214e663fb579ad3523f0f22eb082e5926..1d55f94654000dbc8e8c0de37e0cb32471e3791a 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/pipe_fs_i.h>
 #include <linux/swap.h>
 #include <linux/splice.h>
+#include <linux/aio.h>
 
 MODULE_ALIAS_MISCDEV(FUSE_MINOR);
 MODULE_ALIAS("devname:fuse");
@@ -111,7 +112,7 @@ static void restore_sigs(sigset_t *oldset)
        sigprocmask(SIG_SETMASK, oldset, NULL);
 }
 
-static void __fuse_get_request(struct fuse_req *req)
+void __fuse_get_request(struct fuse_req *req)
 {
        atomic_inc(&req->count);
 }
@@ -130,20 +131,30 @@ static void fuse_req_init_context(struct fuse_req *req)
        req->in.h.pid = current->pid;
 }
 
-struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
+static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background)
+{
+       return !fc->initialized || (for_background && fc->blocked);
+}
+
+static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages,
+                                      bool for_background)
 {
        struct fuse_req *req;
-       sigset_t oldset;
-       int intr;
        int err;
-
        atomic_inc(&fc->num_waiting);
-       block_sigs(&oldset);
-       intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
-       restore_sigs(&oldset);
-       err = -EINTR;
-       if (intr)
-               goto out;
+
+       if (fuse_block_alloc(fc, for_background)) {
+               sigset_t oldset;
+               int intr;
+
+               block_sigs(&oldset);
+               intr = wait_event_interruptible_exclusive(fc->blocked_waitq,
+                               !fuse_block_alloc(fc, for_background));
+               restore_sigs(&oldset);
+               err = -EINTR;
+               if (intr)
+                       goto out;
+       }
 
        err = -ENOTCONN;
        if (!fc->connected)
@@ -151,19 +162,35 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
 
        req = fuse_request_alloc(npages);
        err = -ENOMEM;
-       if (!req)
+       if (!req) {
+               if (for_background)
+                       wake_up(&fc->blocked_waitq);
                goto out;
+       }
 
        fuse_req_init_context(req);
        req->waiting = 1;
+       req->background = for_background;
        return req;
 
  out:
        atomic_dec(&fc->num_waiting);
        return ERR_PTR(err);
 }
+
+struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
+{
+       return __fuse_get_req(fc, npages, false);
+}
 EXPORT_SYMBOL_GPL(fuse_get_req);
 
+struct fuse_req *fuse_get_req_for_background(struct fuse_conn *fc,
+                                            unsigned npages)
+{
+       return __fuse_get_req(fc, npages, true);
+}
+EXPORT_SYMBOL_GPL(fuse_get_req_for_background);
+
 /*
  * Return request in fuse_file->reserved_req.  However that may
  * currently be in use.  If that is the case, wait for it to become
@@ -225,19 +252,31 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,
        struct fuse_req *req;
 
        atomic_inc(&fc->num_waiting);
-       wait_event(fc->blocked_waitq, !fc->blocked);
+       wait_event(fc->blocked_waitq, fc->initialized);
        req = fuse_request_alloc(0);
        if (!req)
                req = get_reserved_req(fc, file);
 
        fuse_req_init_context(req);
        req->waiting = 1;
+       req->background = 0;
        return req;
 }
 
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        if (atomic_dec_and_test(&req->count)) {
+               if (unlikely(req->background)) {
+                       /*
+                        * We get here in the unlikely case that a background
+                        * request was allocated but not sent
+                        */
+                       spin_lock(&fc->lock);
+                       if (!fc->blocked)
+                               wake_up(&fc->blocked_waitq);
+                       spin_unlock(&fc->lock);
+               }
+
                if (req->waiting)
                        atomic_dec(&fc->num_waiting);
 
@@ -335,10 +374,15 @@ __releases(fc->lock)
        list_del(&req->intr_entry);
        req->state = FUSE_REQ_FINISHED;
        if (req->background) {
-               if (fc->num_background == fc->max_background) {
+               req->background = 0;
+
+               if (fc->num_background == fc->max_background)
                        fc->blocked = 0;
-                       wake_up_all(&fc->blocked_waitq);
-               }
+
+               /* Wake up next waiter, if any */
+               if (!fc->blocked && waitqueue_active(&fc->blocked_waitq))
+                       wake_up(&fc->blocked_waitq);
+
                if (fc->num_background == fc->congestion_threshold &&
                    fc->connected && fc->bdi_initialized) {
                        clear_bdi_congested(&fc->bdi, BLK_RW_SYNC);
@@ -442,6 +486,7 @@ __acquires(fc->lock)
 
 static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
 {
+       BUG_ON(req->background);
        spin_lock(&fc->lock);
        if (!fc->connected)
                req->out.h.error = -ENOTCONN;
@@ -469,7 +514,7 @@ EXPORT_SYMBOL_GPL(fuse_request_send);
 static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
                                            struct fuse_req *req)
 {
-       req->background = 1;
+       BUG_ON(!req->background);
        fc->num_background++;
        if (fc->num_background == fc->max_background)
                fc->blocked = 1;
@@ -2071,6 +2116,7 @@ void fuse_abort_conn(struct fuse_conn *fc)
        if (fc->connected) {
                fc->connected = 0;
                fc->blocked = 0;
+               fc->initialized = 1;
                end_io_requests(fc);
                end_queued_requests(fc);
                end_polls(fc);
@@ -2089,6 +2135,7 @@ int fuse_dev_release(struct inode *inode, struct file *file)
                spin_lock(&fc->lock);
                fc->connected = 0;
                fc->blocked = 0;
+               fc->initialized = 1;
                end_queued_requests(fc);
                end_polls(fc);
                wake_up_all(&fc->blocked_waitq);