diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2008-02-06 04:38:39 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:13 -0500 |
commit | d12def1bcb809b6172ee207a24e00a0a4398df1d (patch) | |
tree | 96e151de1e80cacd9202a00b77654533a9754207 | |
parent | b57d426445c98789265de6a9338cdb06462d15fb (diff) |
fuse: limit queued background requests
Libfuse basically creates a new thread for each new request. This is fine for
synchronous requests, which are naturally limited. However background
requests (especially writepage) can cause a thread creation storm.
To avoid this, limit the number of background requests available to userspace.
This is done by introducing another queue for background requests, and a
counter for the number of "active" requests, which are currently available for
userspace.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/fuse/dev.c | 113 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 6 | ||||
-rw-r--r-- | fs/fuse/inode.c | 1 |
3 files changed, 74 insertions, 46 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index db534bcde45f..af639807524e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -201,6 +201,55 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) | |||
201 | } | 201 | } |
202 | } | 202 | } |
203 | 203 | ||
204 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) | ||
205 | { | ||
206 | unsigned nbytes = 0; | ||
207 | unsigned i; | ||
208 | |||
209 | for (i = 0; i < numargs; i++) | ||
210 | nbytes += args[i].size; | ||
211 | |||
212 | return nbytes; | ||
213 | } | ||
214 | |||
215 | static u64 fuse_get_unique(struct fuse_conn *fc) | ||
216 | { | ||
217 | fc->reqctr++; | ||
218 | /* zero is special */ | ||
219 | if (fc->reqctr == 0) | ||
220 | fc->reqctr = 1; | ||
221 | |||
222 | return fc->reqctr; | ||
223 | } | ||
224 | |||
225 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | ||
226 | { | ||
227 | req->in.h.unique = fuse_get_unique(fc); | ||
228 | req->in.h.len = sizeof(struct fuse_in_header) + | ||
229 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); | ||
230 | list_add_tail(&req->list, &fc->pending); | ||
231 | req->state = FUSE_REQ_PENDING; | ||
232 | if (!req->waiting) { | ||
233 | req->waiting = 1; | ||
234 | atomic_inc(&fc->num_waiting); | ||
235 | } | ||
236 | wake_up(&fc->waitq); | ||
237 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); | ||
238 | } | ||
239 | |||
240 | static void flush_bg_queue(struct fuse_conn *fc) | ||
241 | { | ||
242 | while (fc->active_background < FUSE_MAX_BACKGROUND && | ||
243 | !list_empty(&fc->bg_queue)) { | ||
244 | struct fuse_req *req; | ||
245 | |||
246 | req = list_entry(fc->bg_queue.next, struct fuse_req, list); | ||
247 | list_del(&req->list); | ||
248 | fc->active_background++; | ||
249 | queue_request(fc, req); | ||
250 | } | ||
251 | } | ||
252 | |||
204 | /* | 253 | /* |
205 | * This function is called when a request is finished. Either a reply | 254 | * This function is called when a request is finished. Either a reply |
206 | * has arrived or it was aborted (and not yet sent) or some error | 255 | * has arrived or it was aborted (and not yet sent) or some error |
@@ -229,6 +278,8 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) | |||
229 | clear_bdi_congested(&fc->bdi, WRITE); | 278 | clear_bdi_congested(&fc->bdi, WRITE); |
230 | } | 279 | } |
231 | fc->num_background--; | 280 | fc->num_background--; |
281 | fc->active_background--; | ||
282 | flush_bg_queue(fc); | ||
232 | } | 283 | } |
233 | spin_unlock(&fc->lock); | 284 | spin_unlock(&fc->lock); |
234 | wake_up(&req->waitq); | 285 | wake_up(&req->waitq); |
@@ -320,42 +371,6 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) | |||
320 | } | 371 | } |
321 | } | 372 | } |
322 | 373 | ||
323 | static unsigned len_args(unsigned numargs, struct fuse_arg *args) | ||
324 | { | ||
325 | unsigned nbytes = 0; | ||
326 | unsigned i; | ||
327 | |||
328 | for (i = 0; i < numargs; i++) | ||
329 | nbytes += args[i].size; | ||
330 | |||
331 | return nbytes; | ||
332 | } | ||
333 | |||
334 | static u64 fuse_get_unique(struct fuse_conn *fc) | ||
335 | { | ||
336 | fc->reqctr++; | ||
337 | /* zero is special */ | ||
338 | if (fc->reqctr == 0) | ||
339 | fc->reqctr = 1; | ||
340 | |||
341 | return fc->reqctr; | ||
342 | } | ||
343 | |||
344 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | ||
345 | { | ||
346 | req->in.h.unique = fuse_get_unique(fc); | ||
347 | req->in.h.len = sizeof(struct fuse_in_header) + | ||
348 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); | ||
349 | list_add_tail(&req->list, &fc->pending); | ||
350 | req->state = FUSE_REQ_PENDING; | ||
351 | if (!req->waiting) { | ||
352 | req->waiting = 1; | ||
353 | atomic_inc(&fc->num_waiting); | ||
354 | } | ||
355 | wake_up(&fc->waitq); | ||
356 | kill_fasync(&fc->fasync, SIGIO, POLL_IN); | ||
357 | } | ||
358 | |||
359 | void request_send(struct fuse_conn *fc, struct fuse_req *req) | 374 | void request_send(struct fuse_conn *fc, struct fuse_req *req) |
360 | { | 375 | { |
361 | req->isreply = 1; | 376 | req->isreply = 1; |
@@ -375,20 +390,26 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req) | |||
375 | spin_unlock(&fc->lock); | 390 | spin_unlock(&fc->lock); |
376 | } | 391 | } |
377 | 392 | ||
393 | static void request_send_nowait_locked(struct fuse_conn *fc, | ||
394 | struct fuse_req *req) | ||
395 | { | ||
396 | req->background = 1; | ||
397 | fc->num_background++; | ||
398 | if (fc->num_background == FUSE_MAX_BACKGROUND) | ||
399 | fc->blocked = 1; | ||
400 | if (fc->num_background == FUSE_CONGESTION_THRESHOLD) { | ||
401 | set_bdi_congested(&fc->bdi, READ); | ||
402 | set_bdi_congested(&fc->bdi, WRITE); | ||
403 | } | ||
404 | list_add_tail(&req->list, &fc->bg_queue); | ||
405 | flush_bg_queue(fc); | ||
406 | } | ||
407 | |||
378 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) | 408 | static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) |
379 | { | 409 | { |
380 | spin_lock(&fc->lock); | 410 | spin_lock(&fc->lock); |
381 | if (fc->connected) { | 411 | if (fc->connected) { |
382 | req->background = 1; | 412 | request_send_nowait_locked(fc, req); |
383 | fc->num_background++; | ||
384 | if (fc->num_background == FUSE_MAX_BACKGROUND) | ||
385 | fc->blocked = 1; | ||
386 | if (fc->num_background == FUSE_CONGESTION_THRESHOLD) { | ||
387 | set_bdi_congested(&fc->bdi, READ); | ||
388 | set_bdi_congested(&fc->bdi, WRITE); | ||
389 | } | ||
390 | |||
391 | queue_request(fc, req); | ||
392 | spin_unlock(&fc->lock); | 413 | spin_unlock(&fc->lock); |
393 | } else { | 414 | } else { |
394 | req->out.h.error = -ENOTCONN; | 415 | req->out.h.error = -ENOTCONN; |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index c5c1ebff1e2d..67aaf6ee38ea 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -296,6 +296,12 @@ struct fuse_conn { | |||
296 | /** Number of requests currently in the background */ | 296 | /** Number of requests currently in the background */ |
297 | unsigned num_background; | 297 | unsigned num_background; |
298 | 298 | ||
299 | /** Number of background requests currently queued for userspace */ | ||
300 | unsigned active_background; | ||
301 | |||
302 | /** The list of background requests set aside for later queuing */ | ||
303 | struct list_head bg_queue; | ||
304 | |||
299 | /** Pending interrupts */ | 305 | /** Pending interrupts */ |
300 | struct list_head interrupts; | 306 | struct list_head interrupts; |
301 | 307 | ||
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index e5e80d1a4687..c90f633d0b57 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -465,6 +465,7 @@ static struct fuse_conn *new_conn(void) | |||
465 | INIT_LIST_HEAD(&fc->processing); | 465 | INIT_LIST_HEAD(&fc->processing); |
466 | INIT_LIST_HEAD(&fc->io); | 466 | INIT_LIST_HEAD(&fc->io); |
467 | INIT_LIST_HEAD(&fc->interrupts); | 467 | INIT_LIST_HEAD(&fc->interrupts); |
468 | INIT_LIST_HEAD(&fc->bg_queue); | ||
468 | atomic_set(&fc->num_waiting, 0); | 469 | atomic_set(&fc->num_waiting, 0); |
469 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; | 470 | fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; |
470 | fc->bdi.unplug_io_fn = default_unplug_io_fn; | 471 | fc->bdi.unplug_io_fn = default_unplug_io_fn; |