diff options
Diffstat (limited to 'fs/fuse')
-rw-r--r-- | fs/fuse/dev.c | 46 | ||||
-rw-r--r-- | fs/fuse/file.c | 20 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 3 | ||||
-rw-r--r-- | fs/fuse/inode.c | 14 |
4 files changed, 65 insertions, 18 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 4526da8907c6..0c9a2ee54c91 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -66,6 +66,12 @@ static void restore_sigs(sigset_t *oldset) | |||
66 | sigprocmask(SIG_SETMASK, oldset, NULL); | 66 | sigprocmask(SIG_SETMASK, oldset, NULL); |
67 | } | 67 | } |
68 | 68 | ||
69 | /* | ||
70 | * Reset request, so that it can be reused | ||
71 | * | ||
72 | * The caller must be _very_ careful to make sure, that it is holding | ||
73 | * the only reference to req | ||
74 | */ | ||
69 | void fuse_reset_request(struct fuse_req *req) | 75 | void fuse_reset_request(struct fuse_req *req) |
70 | { | 76 | { |
71 | int preallocated = req->preallocated; | 77 | int preallocated = req->preallocated; |
@@ -120,9 +126,9 @@ struct fuse_req *fuse_get_request(struct fuse_conn *fc) | |||
120 | return do_get_request(fc); | 126 | return do_get_request(fc); |
121 | } | 127 | } |
122 | 128 | ||
129 | /* Must be called with fuse_lock held */ | ||
123 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | 130 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) |
124 | { | 131 | { |
125 | spin_lock(&fuse_lock); | ||
126 | if (req->preallocated) { | 132 | if (req->preallocated) { |
127 | atomic_dec(&fc->num_waiting); | 133 | atomic_dec(&fc->num_waiting); |
128 | list_add(&req->list, &fc->unused_list); | 134 | list_add(&req->list, &fc->unused_list); |
@@ -134,11 +140,19 @@ static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | |||
134 | fc->outstanding_debt--; | 140 | fc->outstanding_debt--; |
135 | else | 141 | else |
136 | up(&fc->outstanding_sem); | 142 | up(&fc->outstanding_sem); |
137 | spin_unlock(&fuse_lock); | ||
138 | } | 143 | } |
139 | 144 | ||
140 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | 145 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) |
141 | { | 146 | { |
147 | if (atomic_dec_and_test(&req->count)) { | ||
148 | spin_lock(&fuse_lock); | ||
149 | fuse_putback_request(fc, req); | ||
150 | spin_unlock(&fuse_lock); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req) | ||
155 | { | ||
142 | if (atomic_dec_and_test(&req->count)) | 156 | if (atomic_dec_and_test(&req->count)) |
143 | fuse_putback_request(fc, req); | 157 | fuse_putback_request(fc, req); |
144 | } | 158 | } |
@@ -163,26 +177,36 @@ void fuse_release_background(struct fuse_req *req) | |||
163 | * still waiting), the 'end' callback is called if given, else the | 177 | * still waiting), the 'end' callback is called if given, else the |
164 | * reference to the request is released | 178 | * reference to the request is released |
165 | * | 179 | * |
180 | * Releasing extra reference for foreground requests must be done | ||
181 | * within the same locked region as setting state to finished. This | ||
182 | * is because fuse_reset_request() may be called after request is | ||
183 | * finished and it must be the sole possessor. If request is | ||
184 | * interrupted and put in the background, it will return with an error | ||
185 | * and hence never be reset and reused. | ||
186 | * | ||
166 | * Called with fuse_lock, unlocks it | 187 | * Called with fuse_lock, unlocks it |
167 | */ | 188 | */ |
168 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) | 189 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) |
169 | { | 190 | { |
170 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | ||
171 | req->end = NULL; | ||
172 | list_del(&req->list); | 191 | list_del(&req->list); |
173 | req->state = FUSE_REQ_FINISHED; | 192 | req->state = FUSE_REQ_FINISHED; |
174 | spin_unlock(&fuse_lock); | 193 | if (!req->background) { |
175 | if (req->background) { | 194 | wake_up(&req->waitq); |
195 | fuse_put_request_locked(fc, req); | ||
196 | spin_unlock(&fuse_lock); | ||
197 | } else { | ||
198 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | ||
199 | req->end = NULL; | ||
200 | spin_unlock(&fuse_lock); | ||
176 | down_read(&fc->sbput_sem); | 201 | down_read(&fc->sbput_sem); |
177 | if (fc->mounted) | 202 | if (fc->mounted) |
178 | fuse_release_background(req); | 203 | fuse_release_background(req); |
179 | up_read(&fc->sbput_sem); | 204 | up_read(&fc->sbput_sem); |
205 | if (end) | ||
206 | end(fc, req); | ||
207 | else | ||
208 | fuse_put_request(fc, req); | ||
180 | } | 209 | } |
181 | wake_up(&req->waitq); | ||
182 | if (end) | ||
183 | end(fc, req); | ||
184 | else | ||
185 | fuse_put_request(fc, req); | ||
186 | } | 210 | } |
187 | 211 | ||
188 | /* | 212 | /* |
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a7ef5e716f3c..6f05379b0a0d 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -116,9 +116,14 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir) | |||
116 | /* Special case for failed iget in CREATE */ | 116 | /* Special case for failed iget in CREATE */ |
117 | static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) | 117 | static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) |
118 | { | 118 | { |
119 | u64 nodeid = req->in.h.nodeid; | 119 | /* If called from end_io_requests(), req has more than one |
120 | fuse_reset_request(req); | 120 | reference and fuse_reset_request() cannot work */ |
121 | fuse_send_forget(fc, req, nodeid, 1); | 121 | if (fc->connected) { |
122 | u64 nodeid = req->in.h.nodeid; | ||
123 | fuse_reset_request(req); | ||
124 | fuse_send_forget(fc, req, nodeid, 1); | ||
125 | } else | ||
126 | fuse_put_request(fc, req); | ||
122 | } | 127 | } |
123 | 128 | ||
124 | void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, | 129 | void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, |
@@ -335,9 +340,14 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file, | |||
335 | loff_t pos = page_offset(req->pages[0]); | 340 | loff_t pos = page_offset(req->pages[0]); |
336 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; | 341 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; |
337 | req->out.page_zeroing = 1; | 342 | req->out.page_zeroing = 1; |
338 | req->end = fuse_readpages_end; | ||
339 | fuse_read_fill(req, file, inode, pos, count, FUSE_READ); | 343 | fuse_read_fill(req, file, inode, pos, count, FUSE_READ); |
340 | request_send_background(fc, req); | 344 | if (fc->async_read) { |
345 | req->end = fuse_readpages_end; | ||
346 | request_send_background(fc, req); | ||
347 | } else { | ||
348 | request_send(fc, req); | ||
349 | fuse_readpages_end(fc, req); | ||
350 | } | ||
341 | } | 351 | } |
342 | 352 | ||
343 | struct fuse_readpages_data { | 353 | struct fuse_readpages_data { |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 46cf933aa3bf..4a83adfec968 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -272,6 +272,9 @@ struct fuse_conn { | |||
272 | reply, before any other request, and never cleared */ | 272 | reply, before any other request, and never cleared */ |
273 | unsigned conn_error : 1; | 273 | unsigned conn_error : 1; |
274 | 274 | ||
275 | /** Do readpages asynchronously? Only set in INIT */ | ||
276 | unsigned async_read : 1; | ||
277 | |||
275 | /* | 278 | /* |
276 | * The following bitfields are only for optimization purposes | 279 | * The following bitfields are only for optimization purposes |
277 | * and hence races in setting them will not cause malfunction | 280 | * and hence races in setting them will not cause malfunction |
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c755a0440a66..879e6fba9480 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -473,6 +473,16 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | |||
473 | if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION) | 473 | if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION) |
474 | fc->conn_error = 1; | 474 | fc->conn_error = 1; |
475 | else { | 475 | else { |
476 | unsigned long ra_pages; | ||
477 | |||
478 | if (arg->minor >= 6) { | ||
479 | ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; | ||
480 | if (arg->flags & FUSE_ASYNC_READ) | ||
481 | fc->async_read = 1; | ||
482 | } else | ||
483 | ra_pages = fc->max_read / PAGE_CACHE_SIZE; | ||
484 | |||
485 | fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages); | ||
476 | fc->minor = arg->minor; | 486 | fc->minor = arg->minor; |
477 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; | 487 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; |
478 | } | 488 | } |
@@ -496,6 +506,8 @@ static void fuse_send_init(struct fuse_conn *fc) | |||
496 | 506 | ||
497 | arg->major = FUSE_KERNEL_VERSION; | 507 | arg->major = FUSE_KERNEL_VERSION; |
498 | arg->minor = FUSE_KERNEL_MINOR_VERSION; | 508 | arg->minor = FUSE_KERNEL_MINOR_VERSION; |
509 | arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; | ||
510 | arg->flags |= FUSE_ASYNC_READ; | ||
499 | req->in.h.opcode = FUSE_INIT; | 511 | req->in.h.opcode = FUSE_INIT; |
500 | req->in.numargs = 1; | 512 | req->in.numargs = 1; |
501 | req->in.args[0].size = sizeof(*arg); | 513 | req->in.args[0].size = sizeof(*arg); |
@@ -552,8 +564,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) | |||
552 | fc->user_id = d.user_id; | 564 | fc->user_id = d.user_id; |
553 | fc->group_id = d.group_id; | 565 | fc->group_id = d.group_id; |
554 | fc->max_read = d.max_read; | 566 | fc->max_read = d.max_read; |
555 | if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) | ||
556 | fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE; | ||
557 | 567 | ||
558 | /* Used by get_root_inode() */ | 568 | /* Used by get_root_inode() */ |
559 | sb->s_fs_info = fc; | 569 | sb->s_fs_info = fc; |