diff options
Diffstat (limited to 'fs/fuse')
| -rw-r--r-- | fs/fuse/dev.c | 40 | ||||
| -rw-r--r-- | fs/fuse/file.c | 9 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 3 | ||||
| -rw-r--r-- | fs/fuse/inode.c | 14 |
4 files changed, 51 insertions, 15 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 4526da8907c6..f556a0d5c0d3 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
| @@ -120,9 +120,9 @@ struct fuse_req *fuse_get_request(struct fuse_conn *fc) | |||
| 120 | return do_get_request(fc); | 120 | return do_get_request(fc); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | /* Must be called with fuse_lock held */ | ||
| 123 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | 124 | static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) |
| 124 | { | 125 | { |
| 125 | spin_lock(&fuse_lock); | ||
| 126 | if (req->preallocated) { | 126 | if (req->preallocated) { |
| 127 | atomic_dec(&fc->num_waiting); | 127 | atomic_dec(&fc->num_waiting); |
| 128 | list_add(&req->list, &fc->unused_list); | 128 | list_add(&req->list, &fc->unused_list); |
| @@ -134,11 +134,19 @@ static void fuse_putback_request(struct fuse_conn *fc, struct fuse_req *req) | |||
| 134 | fc->outstanding_debt--; | 134 | fc->outstanding_debt--; |
| 135 | else | 135 | else |
| 136 | up(&fc->outstanding_sem); | 136 | up(&fc->outstanding_sem); |
| 137 | spin_unlock(&fuse_lock); | ||
| 138 | } | 137 | } |
| 139 | 138 | ||
| 140 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | 139 | void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) |
| 141 | { | 140 | { |
| 141 | if (atomic_dec_and_test(&req->count)) { | ||
| 142 | spin_lock(&fuse_lock); | ||
| 143 | fuse_putback_request(fc, req); | ||
| 144 | spin_unlock(&fuse_lock); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | static void fuse_put_request_locked(struct fuse_conn *fc, struct fuse_req *req) | ||
| 149 | { | ||
| 142 | if (atomic_dec_and_test(&req->count)) | 150 | if (atomic_dec_and_test(&req->count)) |
| 143 | fuse_putback_request(fc, req); | 151 | fuse_putback_request(fc, req); |
| 144 | } | 152 | } |
| @@ -163,26 +171,36 @@ void fuse_release_background(struct fuse_req *req) | |||
| 163 | * still waiting), the 'end' callback is called if given, else the | 171 | * still waiting), the 'end' callback is called if given, else the |
| 164 | * reference to the request is released | 172 | * reference to the request is released |
| 165 | * | 173 | * |
| 174 | * Releasing extra reference for foreground requests must be done | ||
| 175 | * within the same locked region as setting state to finished. This | ||
| 176 | * is because fuse_reset_request() may be called after request is | ||
| 177 | * finished and it must be the sole possessor. If request is | ||
| 178 | * interrupted and put in the background, it will return with an error | ||
| 179 | * and hence never be reset and reused. | ||
| 180 | * | ||
| 166 | * Called with fuse_lock, unlocks it | 181 | * Called with fuse_lock, unlocks it |
| 167 | */ | 182 | */ |
| 168 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) | 183 | static void request_end(struct fuse_conn *fc, struct fuse_req *req) |
| 169 | { | 184 | { |
| 170 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | ||
| 171 | req->end = NULL; | ||
| 172 | list_del(&req->list); | 185 | list_del(&req->list); |
| 173 | req->state = FUSE_REQ_FINISHED; | 186 | req->state = FUSE_REQ_FINISHED; |
| 174 | spin_unlock(&fuse_lock); | 187 | if (!req->background) { |
| 175 | if (req->background) { | 188 | wake_up(&req->waitq); |
| 189 | fuse_put_request_locked(fc, req); | ||
| 190 | spin_unlock(&fuse_lock); | ||
| 191 | } else { | ||
| 192 | void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; | ||
| 193 | req->end = NULL; | ||
| 194 | spin_unlock(&fuse_lock); | ||
| 176 | down_read(&fc->sbput_sem); | 195 | down_read(&fc->sbput_sem); |
| 177 | if (fc->mounted) | 196 | if (fc->mounted) |
| 178 | fuse_release_background(req); | 197 | fuse_release_background(req); |
| 179 | up_read(&fc->sbput_sem); | 198 | up_read(&fc->sbput_sem); |
| 199 | if (end) | ||
| 200 | end(fc, req); | ||
| 201 | else | ||
| 202 | fuse_put_request(fc, req); | ||
| 180 | } | 203 | } |
| 181 | wake_up(&req->waitq); | ||
| 182 | if (end) | ||
| 183 | end(fc, req); | ||
| 184 | else | ||
| 185 | fuse_put_request(fc, req); | ||
| 186 | } | 204 | } |
| 187 | 205 | ||
| 188 | /* | 206 | /* |
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index a7ef5e716f3c..296351615b00 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
| @@ -335,9 +335,14 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file, | |||
| 335 | loff_t pos = page_offset(req->pages[0]); | 335 | loff_t pos = page_offset(req->pages[0]); |
| 336 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; | 336 | size_t count = req->num_pages << PAGE_CACHE_SHIFT; |
| 337 | req->out.page_zeroing = 1; | 337 | req->out.page_zeroing = 1; |
| 338 | req->end = fuse_readpages_end; | ||
| 339 | fuse_read_fill(req, file, inode, pos, count, FUSE_READ); | 338 | fuse_read_fill(req, file, inode, pos, count, FUSE_READ); |
| 340 | request_send_background(fc, req); | 339 | if (fc->async_read) { |
| 340 | req->end = fuse_readpages_end; | ||
| 341 | request_send_background(fc, req); | ||
| 342 | } else { | ||
| 343 | request_send(fc, req); | ||
| 344 | fuse_readpages_end(fc, req); | ||
| 345 | } | ||
| 341 | } | 346 | } |
| 342 | 347 | ||
| 343 | struct fuse_readpages_data { | 348 | 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; |
