[PATCH] fuse: add asynchronous request support

Add possibility for requests to run asynchronously and call an 'end' callback
when finished.

With this, the special handling of the INIT and RELEASE requests can be
cleaned up too.

Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Miklos Szeredi and committed by Linus Torvalds 64c6d8ed 69a53bf2

+43 -14
+28 -14
fs/fuse/dev.c
··· 172 172 fuse_putback_request() */ 173 173 for (i = 1; i < FUSE_MAX_OUTSTANDING; i++) 174 174 up(&fc->outstanding_sem); 175 + 176 + fuse_put_request(fc, req); 175 177 } 176 178 177 179 /* ··· 182 180 * occurred during communication with userspace, or the device file 183 181 * was closed. In case of a background request the reference to the 184 182 * stored objects are released. The requester thread is woken up (if 185 - * still waiting), and finally the reference to the request is 186 - * released 183 + * still waiting), the 'end' callback is called if given, else the 184 + * reference to the request is released 187 185 * 188 186 * Called with fuse_lock, unlocks it 189 187 */ 190 188 static void request_end(struct fuse_conn *fc, struct fuse_req *req) 191 189 { 190 + void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; 191 + req->end = NULL; 192 192 list_del(&req->list); 193 193 req->state = FUSE_REQ_FINISHED; 194 194 spin_unlock(&fuse_lock); ··· 201 197 up_read(&fc->sbput_sem); 202 198 } 203 199 wake_up(&req->waitq); 204 - if (req->in.h.opcode == FUSE_INIT) 205 - process_init_reply(fc, req); 206 - else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) { 207 - /* Special case for failed iget in CREATE */ 208 - u64 nodeid = req->in.h.nodeid; 209 - fuse_reset_request(req); 210 - fuse_send_forget(fc, req, nodeid, 1); 211 - return; 212 - } 213 - fuse_put_request(fc, req); 200 + if (end) 201 + end(fc, req); 202 + else 203 + fuse_put_request(fc, req); 214 204 } 215 205 216 206 /* ··· 385 387 req->out.argvar = 1; 386 388 req->out.args[0].size = sizeof(struct fuse_init_out); 387 389 req->out.args[0].value = &req->misc.init_out; 390 + req->end = process_init_reply; 388 391 request_send_background(fc, req); 389 392 } 390 393 ··· 863 864 * The requests are set to interrupted and finished, and the request 864 865 * waiter is woken up. This will make request_wait_answer() wait 865 866 * until the request is unlocked and then return. 867 + * 868 + * If the request is asynchronous, then the end function needs to be 869 + * called after waiting for the request to be unlocked (if it was 870 + * locked). 866 871 */ 867 872 static void end_io_requests(struct fuse_conn *fc) 868 873 { 869 874 while (!list_empty(&fc->io)) { 870 - struct fuse_req *req; 871 - req = list_entry(fc->io.next, struct fuse_req, list); 875 + struct fuse_req *req = 876 + list_entry(fc->io.next, struct fuse_req, list); 877 + void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; 878 + 872 879 req->interrupted = 1; 873 880 req->out.h.error = -ECONNABORTED; 874 881 req->state = FUSE_REQ_FINISHED; 875 882 list_del_init(&req->list); 876 883 wake_up(&req->waitq); 884 + if (end) { 885 + req->end = NULL; 886 + /* The end function will consume this reference */ 887 + __fuse_get_request(req); 888 + spin_unlock(&fuse_lock); 889 + wait_event(req->waitq, !req->locked); 890 + end(fc, req); 891 + spin_lock(&fuse_lock); 892 + } 877 893 } 878 894 } 879 895
+10
fs/fuse/file.c
··· 113 113 return err; 114 114 } 115 115 116 + /* Special case for failed iget in CREATE */ 117 + static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) 118 + { 119 + u64 nodeid = req->in.h.nodeid; 120 + fuse_reset_request(req); 121 + fuse_send_forget(fc, req, nodeid, 1); 122 + } 123 + 116 124 void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, 117 125 u64 nodeid, struct inode *inode, int flags, int isdir) 118 126 { ··· 136 128 req->in.args[0].size = sizeof(struct fuse_release_in); 137 129 req->in.args[0].value = inarg; 138 130 request_send_background(fc, req); 131 + if (!inode) 132 + req->end = fuse_release_end; 139 133 kfree(ff); 140 134 } 141 135
+5
fs/fuse/fuse_i.h
··· 120 120 FUSE_REQ_FINISHED 121 121 }; 122 122 123 + struct fuse_conn; 124 + 123 125 /** 124 126 * A request to the client 125 127 */ ··· 188 186 189 187 /** File used in the request (or NULL) */ 190 188 struct file *file; 189 + 190 + /** Request completion callback */ 191 + void (*end)(struct fuse_conn *, struct fuse_req *); 191 192 }; 192 193 193 194 /**