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