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