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; |
