]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/fuse/dev.c
Merge branch 'origin'
[karo-tx-linux.git] / fs / fuse / dev.c
index 60c222517ccd116cd8843e2d624cd01636b9d0c6..f556a0d5c0d31010b86552ff958dcb55b20947f5 100644 (file)
@@ -120,9 +120,9 @@ struct fuse_req *fuse_get_request(struct fuse_conn *fc)
        return do_get_request(fc);
 }
 
+/* Must be called with fuse_lock held */
 static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
 {
-       spin_lock(&fuse_lock);
        if (req->preallocated) {
                atomic_dec(&fc->num_waiting);
                list_add(&req->list, &fc->unused_list);
@@ -134,10 +134,18 @@ static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req)
                fc->outstanding_debt--;
        else
                up(&fc->outstanding_sem);
-       spin_unlock(&fuse_lock);
 }
 
 void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
+{
+       if (atomic_dec_and_test(&req->count)) {
+               spin_lock(&fuse_lock);
+               fuse_putback_request(fc, req);
+               spin_unlock(&fuse_lock);
+       }
+}
+
+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);
@@ -154,34 +162,21 @@ void fuse_release_background(struct fuse_req *req)
        spin_unlock(&fuse_lock);
 }
 
-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)
-               fc->conn_error = 1;
-       else {
-               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);
-}
-
 /*
  * This function is called when a request is finished.  Either a reply
  * has arrived or it was interrupted (and not yet sent) or some error
  * occurred during communication with userspace, or the device file
  * was closed.  In case of a background request the reference to the
  * stored objects are released.  The requester thread is woken up (if
- * still waiting), and finally the reference to the request is
- * released
+ * still waiting), the 'end' callback is called if given, else the
+ * reference to the request is released
+ *
+ * Releasing extra reference for foreground requests must be done
+ * within the same locked region as setting state to finished.  This
+ * is because fuse_reset_request() may be called after request is
+ * finished and it must be the sole possessor.  If request is
+ * interrupted and put in the background, it will return with an error
+ * and hence never be reset and reused.
  *
  * Called with fuse_lock, unlocks it
  */
@@ -189,24 +184,23 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
 {
        list_del(&req->list);
        req->state = FUSE_REQ_FINISHED;
-       spin_unlock(&fuse_lock);
-       if (req->background) {
+       if (!req->background) {
+               wake_up(&req->waitq);
+               fuse_put_request_locked(fc, req);
+               spin_unlock(&fuse_lock);
+       } else {
+               void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
+               req->end = NULL;
+               spin_unlock(&fuse_lock);
                down_read(&fc->sbput_sem);
                if (fc->mounted)
                        fuse_release_background(req);
                up_read(&fc->sbput_sem);
+               if (end)
+                       end(fc, req);
+               else
+                       fuse_put_request(fc, req);
        }
-       wake_up(&req->waitq);
-       if (req->in.h.opcode == FUSE_INIT)
-               process_init_reply(fc, req);
-       else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) {
-               /* Special case for failed iget in CREATE */
-               u64 nodeid = req->in.h.nodeid;
-               fuse_reset_request(req);
-               fuse_send_forget(fc, req, nodeid, 1);
-               return;
-       }
-       fuse_put_request(fc, req);
 }
 
 /*
@@ -368,28 +362,6 @@ void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
        request_send_nowait(fc, req);
 }
 
-void fuse_send_init(struct fuse_conn *fc)
-{
-       /* 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;
-       arg->minor = FUSE_KERNEL_MINOR_VERSION;
-       req->in.h.opcode = FUSE_INIT;
-       req->in.numargs = 1;
-       req->in.args[0].size = sizeof(*arg);
-       req->in.args[0].value = arg;
-       req->out.numargs = 1;
-       /* Variable length arguement used for backward compatibility
-          with interface version < 7.5.  Rest of init_out is zeroed
-          by do_get_request(), so a short reply is not a problem */
-       req->out.argvar = 1;
-       req->out.args[0].size = sizeof(struct fuse_init_out);
-       req->out.args[0].value = &req->misc.init_out;
-       request_send_background(fc, req);
-}
-
 /*
  * Lock the request.  Up to the next unlock_request() there mustn't be
  * anything that could cause a page-fault.  If the request was already
@@ -864,17 +836,32 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
  * The requests are set to interrupted and finished, and the request
  * waiter is woken up.  This will make request_wait_answer() wait
  * until the request is unlocked and then return.
+ *
+ * If the request is asynchronous, then the end function needs to be
+ * called after waiting for the request to be unlocked (if it was
+ * locked).
  */
 static void end_io_requests(struct fuse_conn *fc)
 {
        while (!list_empty(&fc->io)) {
-               struct fuse_req *req;
-               req = list_entry(fc->io.next, struct fuse_req, list);
+               struct fuse_req *req =
+                       list_entry(fc->io.next, struct fuse_req, list);
+               void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
+
                req->interrupted = 1;
                req->out.h.error = -ECONNABORTED;
                req->state = FUSE_REQ_FINISHED;
                list_del_init(&req->list);
                wake_up(&req->waitq);
+               if (end) {
+                       req->end = NULL;
+                       /* The end function will consume this reference */
+                       __fuse_get_request(req);
+                       spin_unlock(&fuse_lock);
+                       wait_event(req->waitq, !req->locked);
+                       end(fc, req);
+                       spin_lock(&fuse_lock);
+               }
        }
 }