summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/fuse/dev.c42
-rw-r--r--fs/fuse/file.c10
-rw-r--r--fs/fuse/fuse_i.h5
3 files changed, 43 insertions, 14 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 60c222517ccd..99325547604f 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -172,6 +172,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fuse_putback_request() */
for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
up(&fc->outstanding_sem);
+
+ fuse_put_request(fc, req);
}
/*
@@ -180,13 +182,15 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
* 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
*
* Called with fuse_lock, unlocks it
*/
static void request_end(struct fuse_conn *fc, struct fuse_req *req)
{
+ void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
+ req->end = NULL;
list_del(&req->list);
req->state = FUSE_REQ_FINISHED;
spin_unlock(&fuse_lock);
@@ -197,16 +201,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
up_read(&fc->sbput_sem);
}
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);
+ if (end)
+ end(fc, req);
+ else
+ fuse_put_request(fc, req);
}
/*
@@ -387,6 +385,7 @@ void fuse_send_init(struct fuse_conn *fc)
req->out.argvar = 1;
req->out.args[0].size = sizeof(struct fuse_init_out);
req->out.args[0].value = &req->misc.init_out;
+ req->end = process_init_reply;
request_send_background(fc, req);
}
@@ -864,17 +863,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);
+ }
}
}
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 52557664a89e..043d5b36846d 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -113,6 +113,14 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
return err;
}
+/* Special case for failed iget in CREATE */
+static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
+{
+ u64 nodeid = req->in.h.nodeid;
+ fuse_reset_request(req);
+ fuse_send_forget(fc, req, nodeid, 1);
+}
+
void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff,
u64 nodeid, struct inode *inode, int flags, int isdir)
{
@@ -128,6 +136,8 @@ void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff,
req->in.args[0].size = sizeof(struct fuse_release_in);
req->in.args[0].value = inarg;
request_send_background(fc, req);
+ if (!inode)
+ req->end = fuse_release_end;
kfree(ff);
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index e6381db41df9..145098056ca6 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -120,6 +120,8 @@ enum fuse_req_state {
FUSE_REQ_FINISHED
};
+struct fuse_conn;
+
/**
* A request to the client
*/
@@ -186,6 +188,9 @@ struct fuse_req {
/** File used in the request (or NULL) */
struct file *file;
+
+ /** Request completion callback */
+ void (*end)(struct fuse_conn *, struct fuse_req *);
};
/**