diff options
Diffstat (limited to 'fs/btrfs/async-thread.c')
| -rw-r--r-- | fs/btrfs/async-thread.c | 329 |
1 files changed, 272 insertions, 57 deletions
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index 019e8af449ab..c0861e781cdb 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c | |||
| @@ -48,6 +48,9 @@ struct btrfs_worker_thread { | |||
| 48 | /* number of things on the pending list */ | 48 | /* number of things on the pending list */ |
| 49 | atomic_t num_pending; | 49 | atomic_t num_pending; |
| 50 | 50 | ||
| 51 | /* reference counter for this struct */ | ||
| 52 | atomic_t refs; | ||
| 53 | |||
| 51 | unsigned long sequence; | 54 | unsigned long sequence; |
| 52 | 55 | ||
| 53 | /* protects the pending list. */ | 56 | /* protects the pending list. */ |
| @@ -61,6 +64,51 @@ struct btrfs_worker_thread { | |||
| 61 | }; | 64 | }; |
| 62 | 65 | ||
| 63 | /* | 66 | /* |
| 67 | * btrfs_start_workers uses kthread_run, which can block waiting for memory | ||
| 68 | * for a very long time. It will actually throttle on page writeback, | ||
| 69 | * and so it may not make progress until after our btrfs worker threads | ||
| 70 | * process all of the pending work structs in their queue | ||
| 71 | * | ||
| 72 | * This means we can't use btrfs_start_workers from inside a btrfs worker | ||
| 73 | * thread that is used as part of cleaning dirty memory, which pretty much | ||
| 74 | * involves all of the worker threads. | ||
| 75 | * | ||
| 76 | * Instead we have a helper queue who never has more than one thread | ||
| 77 | * where we scheduler thread start operations. This worker_start struct | ||
| 78 | * is used to contain the work and hold a pointer to the queue that needs | ||
| 79 | * another worker. | ||
| 80 | */ | ||
| 81 | struct worker_start { | ||
| 82 | struct btrfs_work work; | ||
| 83 | struct btrfs_workers *queue; | ||
| 84 | }; | ||
| 85 | |||
| 86 | static void start_new_worker_func(struct btrfs_work *work) | ||
| 87 | { | ||
| 88 | struct worker_start *start; | ||
| 89 | start = container_of(work, struct worker_start, work); | ||
| 90 | btrfs_start_workers(start->queue, 1); | ||
| 91 | kfree(start); | ||
| 92 | } | ||
| 93 | |||
| 94 | static int start_new_worker(struct btrfs_workers *queue) | ||
| 95 | { | ||
| 96 | struct worker_start *start; | ||
| 97 | int ret; | ||
| 98 | |||
| 99 | start = kzalloc(sizeof(*start), GFP_NOFS); | ||
| 100 | if (!start) | ||
| 101 | return -ENOMEM; | ||
| 102 | |||
| 103 | start->work.func = start_new_worker_func; | ||
| 104 | start->queue = queue; | ||
| 105 | ret = btrfs_queue_worker(queue->atomic_worker_start, &start->work); | ||
| 106 | if (ret) | ||
| 107 | kfree(start); | ||
| 108 | return ret; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* | ||
| 64 | * helper function to move a thread onto the idle list after it | 112 | * helper function to move a thread onto the idle list after it |
| 65 | * has finished some requests. | 113 | * has finished some requests. |
| 66 | */ | 114 | */ |
| @@ -71,7 +119,12 @@ static void check_idle_worker(struct btrfs_worker_thread *worker) | |||
| 71 | unsigned long flags; | 119 | unsigned long flags; |
| 72 | spin_lock_irqsave(&worker->workers->lock, flags); | 120 | spin_lock_irqsave(&worker->workers->lock, flags); |
| 73 | worker->idle = 1; | 121 | worker->idle = 1; |
| 74 | list_move(&worker->worker_list, &worker->workers->idle_list); | 122 | |
| 123 | /* the list may be empty if the worker is just starting */ | ||
| 124 | if (!list_empty(&worker->worker_list)) { | ||
| 125 | list_move(&worker->worker_list, | ||
| 126 | &worker->workers->idle_list); | ||
| 127 | } | ||
| 75 | spin_unlock_irqrestore(&worker->workers->lock, flags); | 128 | spin_unlock_irqrestore(&worker->workers->lock, flags); |
| 76 | } | 129 | } |
| 77 | } | 130 | } |
| @@ -87,23 +140,51 @@ static void check_busy_worker(struct btrfs_worker_thread *worker) | |||
| 87 | unsigned long flags; | 140 | unsigned long flags; |
| 88 | spin_lock_irqsave(&worker->workers->lock, flags); | 141 | spin_lock_irqsave(&worker->workers->lock, flags); |
| 89 | worker->idle = 0; | 142 | worker->idle = 0; |
| 90 | list_move_tail(&worker->worker_list, | 143 | |
| 91 | &worker->workers->worker_list); | 144 | if (!list_empty(&worker->worker_list)) { |
| 145 | list_move_tail(&worker->worker_list, | ||
| 146 | &worker->workers->worker_list); | ||
| 147 | } | ||
| 92 | spin_unlock_irqrestore(&worker->workers->lock, flags); | 148 | spin_unlock_irqrestore(&worker->workers->lock, flags); |
| 93 | } | 149 | } |
| 94 | } | 150 | } |
| 95 | 151 | ||
| 96 | static noinline int run_ordered_completions(struct btrfs_workers *workers, | 152 | static void check_pending_worker_creates(struct btrfs_worker_thread *worker) |
| 97 | struct btrfs_work *work) | ||
| 98 | { | 153 | { |
| 154 | struct btrfs_workers *workers = worker->workers; | ||
| 99 | unsigned long flags; | 155 | unsigned long flags; |
| 100 | 156 | ||
| 157 | rmb(); | ||
| 158 | if (!workers->atomic_start_pending) | ||
| 159 | return; | ||
| 160 | |||
| 161 | spin_lock_irqsave(&workers->lock, flags); | ||
| 162 | if (!workers->atomic_start_pending) | ||
| 163 | goto out; | ||
| 164 | |||
| 165 | workers->atomic_start_pending = 0; | ||
| 166 | if (workers->num_workers + workers->num_workers_starting >= | ||
| 167 | workers->max_workers) | ||
| 168 | goto out; | ||
| 169 | |||
| 170 | workers->num_workers_starting += 1; | ||
| 171 | spin_unlock_irqrestore(&workers->lock, flags); | ||
| 172 | start_new_worker(workers); | ||
| 173 | return; | ||
| 174 | |||
| 175 | out: | ||
| 176 | spin_unlock_irqrestore(&workers->lock, flags); | ||
| 177 | } | ||
| 178 | |||
| 179 | static noinline int run_ordered_completions(struct btrfs_workers *workers, | ||
| 180 | struct btrfs_work *work) | ||
| 181 | { | ||
| 101 | if (!workers->ordered) | 182 | if (!workers->ordered) |
| 102 | return 0; | 183 | return 0; |
| 103 | 184 | ||
| 104 | set_bit(WORK_DONE_BIT, &work->flags); | 185 | set_bit(WORK_DONE_BIT, &work->flags); |
| 105 | 186 | ||
| 106 | spin_lock_irqsave(&workers->lock, flags); | 187 | spin_lock(&workers->order_lock); |
| 107 | 188 | ||
| 108 | while (1) { | 189 | while (1) { |
| 109 | if (!list_empty(&workers->prio_order_list)) { | 190 | if (!list_empty(&workers->prio_order_list)) { |
| @@ -126,45 +207,118 @@ static noinline int run_ordered_completions(struct btrfs_workers *workers, | |||
| 126 | if (test_and_set_bit(WORK_ORDER_DONE_BIT, &work->flags)) | 207 | if (test_and_set_bit(WORK_ORDER_DONE_BIT, &work->flags)) |
| 127 | break; | 208 | break; |
| 128 | 209 | ||
| 129 | spin_unlock_irqrestore(&workers->lock, flags); | 210 | spin_unlock(&workers->order_lock); |
| 130 | 211 | ||
| 131 | work->ordered_func(work); | 212 | work->ordered_func(work); |
| 132 | 213 | ||
| 133 | /* now take the lock again and call the freeing code */ | 214 | /* now take the lock again and call the freeing code */ |
| 134 | spin_lock_irqsave(&workers->lock, flags); | 215 | spin_lock(&workers->order_lock); |
| 135 | list_del(&work->order_list); | 216 | list_del(&work->order_list); |
| 136 | work->ordered_free(work); | 217 | work->ordered_free(work); |
| 137 | } | 218 | } |
| 138 | 219 | ||
| 139 | spin_unlock_irqrestore(&workers->lock, flags); | 220 | spin_unlock(&workers->order_lock); |
| 140 | return 0; | 221 | return 0; |
| 141 | } | 222 | } |
| 142 | 223 | ||
| 224 | static void put_worker(struct btrfs_worker_thread *worker) | ||
| 225 | { | ||
| 226 | if (atomic_dec_and_test(&worker->refs)) | ||
| 227 | kfree(worker); | ||
| 228 | } | ||
| 229 | |||
| 230 | static int try_worker_shutdown(struct btrfs_worker_thread *worker) | ||
| 231 | { | ||
| 232 | int freeit = 0; | ||
| 233 | |||
| 234 | spin_lock_irq(&worker->lock); | ||
| 235 | spin_lock(&worker->workers->lock); | ||
| 236 | if (worker->workers->num_workers > 1 && | ||
| 237 | worker->idle && | ||
| 238 | !worker->working && | ||
| 239 | !list_empty(&worker->worker_list) && | ||
| 240 | list_empty(&worker->prio_pending) && | ||
| 241 | list_empty(&worker->pending) && | ||
| 242 | atomic_read(&worker->num_pending) == 0) { | ||
| 243 | freeit = 1; | ||
| 244 | list_del_init(&worker->worker_list); | ||
| 245 | worker->workers->num_workers--; | ||
| 246 | } | ||
| 247 | spin_unlock(&worker->workers->lock); | ||
| 248 | spin_unlock_irq(&worker->lock); | ||
| 249 | |||
| 250 | if (freeit) | ||
| 251 | put_worker(worker); | ||
| 252 | return freeit; | ||
| 253 | } | ||
| 254 | |||
| 255 | static struct btrfs_work *get_next_work(struct btrfs_worker_thread *worker, | ||
| 256 | struct list_head *prio_head, | ||
| 257 | struct list_head *head) | ||
| 258 | { | ||
| 259 | struct btrfs_work *work = NULL; | ||
| 260 | struct list_head *cur = NULL; | ||
| 261 | |||
| 262 | if(!list_empty(prio_head)) | ||
| 263 | cur = prio_head->next; | ||
| 264 | |||
| 265 | smp_mb(); | ||
| 266 | if (!list_empty(&worker->prio_pending)) | ||
| 267 | goto refill; | ||
| 268 | |||
| 269 | if (!list_empty(head)) | ||
| 270 | cur = head->next; | ||
| 271 | |||
| 272 | if (cur) | ||
| 273 | goto out; | ||
| 274 | |||
| 275 | refill: | ||
| 276 | spin_lock_irq(&worker->lock); | ||
| 277 | list_splice_tail_init(&worker->prio_pending, prio_head); | ||
| 278 | list_splice_tail_init(&worker->pending, head); | ||
| 279 | |||
| 280 | if (!list_empty(prio_head)) | ||
| 281 | cur = prio_head->next; | ||
| 282 | else if (!list_empty(head)) | ||
| 283 | cur = head->next; | ||
| 284 | spin_unlock_irq(&worker->lock); | ||
| 285 | |||
| 286 | if (!cur) | ||
| 287 | goto out_fail; | ||
| 288 | |||
| 289 | out: | ||
| 290 | work = list_entry(cur, struct btrfs_work, list); | ||
| 291 | |||
| 292 | out_fail: | ||
| 293 | return work; | ||
| 294 | } | ||
| 295 | |||
| 143 | /* | 296 | /* |
| 144 | * main loop for servicing work items | 297 | * main loop for servicing work items |
| 145 | */ | 298 | */ |
| 146 | static int worker_loop(void *arg) | 299 | static int worker_loop(void *arg) |
| 147 | { | 300 | { |
| 148 | struct btrfs_worker_thread *worker = arg; | 301 | struct btrfs_worker_thread *worker = arg; |
| 149 | struct list_head *cur; | 302 | struct list_head head; |
| 303 | struct list_head prio_head; | ||
| 150 | struct btrfs_work *work; | 304 | struct btrfs_work *work; |
| 305 | |||
| 306 | INIT_LIST_HEAD(&head); | ||
| 307 | INIT_LIST_HEAD(&prio_head); | ||
| 308 | |||
| 151 | do { | 309 | do { |
| 152 | spin_lock_irq(&worker->lock); | 310 | again: |
| 153 | again_locked: | ||
| 154 | while (1) { | 311 | while (1) { |
| 155 | if (!list_empty(&worker->prio_pending)) | 312 | |
| 156 | cur = worker->prio_pending.next; | 313 | |
| 157 | else if (!list_empty(&worker->pending)) | 314 | work = get_next_work(worker, &prio_head, &head); |
| 158 | cur = worker->pending.next; | 315 | if (!work) |
| 159 | else | ||
| 160 | break; | 316 | break; |
| 161 | 317 | ||
| 162 | work = list_entry(cur, struct btrfs_work, list); | ||
| 163 | list_del(&work->list); | 318 | list_del(&work->list); |
| 164 | clear_bit(WORK_QUEUED_BIT, &work->flags); | 319 | clear_bit(WORK_QUEUED_BIT, &work->flags); |
| 165 | 320 | ||
| 166 | work->worker = worker; | 321 | work->worker = worker; |
| 167 | spin_unlock_irq(&worker->lock); | ||
| 168 | 322 | ||
| 169 | work->func(work); | 323 | work->func(work); |
| 170 | 324 | ||
| @@ -175,9 +329,13 @@ again_locked: | |||
| 175 | */ | 329 | */ |
| 176 | run_ordered_completions(worker->workers, work); | 330 | run_ordered_completions(worker->workers, work); |
| 177 | 331 | ||
| 178 | spin_lock_irq(&worker->lock); | 332 | check_pending_worker_creates(worker); |
| 179 | check_idle_worker(worker); | 333 | |
| 180 | } | 334 | } |
| 335 | |||
| 336 | spin_lock_irq(&worker->lock); | ||
| 337 | check_idle_worker(worker); | ||
| 338 | |||
| 181 | if (freezing(current)) { | 339 | if (freezing(current)) { |
| 182 | worker->working = 0; | 340 | worker->working = 0; |
| 183 | spin_unlock_irq(&worker->lock); | 341 | spin_unlock_irq(&worker->lock); |
| @@ -216,8 +374,10 @@ again_locked: | |||
| 216 | spin_lock_irq(&worker->lock); | 374 | spin_lock_irq(&worker->lock); |
| 217 | set_current_state(TASK_INTERRUPTIBLE); | 375 | set_current_state(TASK_INTERRUPTIBLE); |
| 218 | if (!list_empty(&worker->pending) || | 376 | if (!list_empty(&worker->pending) || |
| 219 | !list_empty(&worker->prio_pending)) | 377 | !list_empty(&worker->prio_pending)) { |
| 220 | goto again_locked; | 378 | spin_unlock_irq(&worker->lock); |
| 379 | goto again; | ||
| 380 | } | ||
| 221 | 381 | ||
| 222 | /* | 382 | /* |
| 223 | * this makes sure we get a wakeup when someone | 383 | * this makes sure we get a wakeup when someone |
| @@ -226,8 +386,13 @@ again_locked: | |||
| 226 | worker->working = 0; | 386 | worker->working = 0; |
| 227 | spin_unlock_irq(&worker->lock); | 387 | spin_unlock_irq(&worker->lock); |
| 228 | 388 | ||
| 229 | if (!kthread_should_stop()) | 389 | if (!kthread_should_stop()) { |
| 230 | schedule(); | 390 | schedule_timeout(HZ * 120); |
| 391 | if (!worker->working && | ||
| 392 | try_worker_shutdown(worker)) { | ||
| 393 | return 0; | ||
| 394 | } | ||
| 395 | } | ||
| 231 | } | 396 | } |
| 232 | __set_current_state(TASK_RUNNING); | 397 | __set_current_state(TASK_RUNNING); |
| 233 | } | 398 | } |
| @@ -242,41 +407,61 @@ int btrfs_stop_workers(struct btrfs_workers *workers) | |||
| 242 | { | 407 | { |
| 243 | struct list_head *cur; | 408 | struct list_head *cur; |
| 244 | struct btrfs_worker_thread *worker; | 409 | struct btrfs_worker_thread *worker; |
| 410 | int can_stop; | ||
| 245 | 411 | ||
| 412 | spin_lock_irq(&workers->lock); | ||
| 246 | list_splice_init(&workers->idle_list, &workers->worker_list); | 413 | list_splice_init(&workers->idle_list, &workers->worker_list); |
| 247 | while (!list_empty(&workers->worker_list)) { | 414 | while (!list_empty(&workers->worker_list)) { |
| 248 | cur = workers->worker_list.next; | 415 | cur = workers->worker_list.next; |
| 249 | worker = list_entry(cur, struct btrfs_worker_thread, | 416 | worker = list_entry(cur, struct btrfs_worker_thread, |
| 250 | worker_list); | 417 | worker_list); |
| 251 | kthread_stop(worker->task); | 418 | |
| 252 | list_del(&worker->worker_list); | 419 | atomic_inc(&worker->refs); |
| 253 | kfree(worker); | 420 | workers->num_workers -= 1; |
| 421 | if (!list_empty(&worker->worker_list)) { | ||
| 422 | list_del_init(&worker->worker_list); | ||
| 423 | put_worker(worker); | ||
| 424 | can_stop = 1; | ||
| 425 | } else | ||
| 426 | can_stop = 0; | ||
| 427 | spin_unlock_irq(&workers->lock); | ||
| 428 | if (can_stop) | ||
| 429 | kthread_stop(worker->task); | ||
| 430 | spin_lock_irq(&workers->lock); | ||
| 431 | put_worker(worker); | ||
| 254 | } | 432 | } |
| 433 | spin_unlock_irq(&workers->lock); | ||
| 255 | return 0; | 434 | return 0; |
| 256 | } | 435 | } |
| 257 | 436 | ||
| 258 | /* | 437 | /* |
| 259 | * simple init on struct btrfs_workers | 438 | * simple init on struct btrfs_workers |
| 260 | */ | 439 | */ |
| 261 | void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max) | 440 | void btrfs_init_workers(struct btrfs_workers *workers, char *name, int max, |
| 441 | struct btrfs_workers *async_helper) | ||
| 262 | { | 442 | { |
| 263 | workers->num_workers = 0; | 443 | workers->num_workers = 0; |
| 444 | workers->num_workers_starting = 0; | ||
| 264 | INIT_LIST_HEAD(&workers->worker_list); | 445 | INIT_LIST_HEAD(&workers->worker_list); |
| 265 | INIT_LIST_HEAD(&workers->idle_list); | 446 | INIT_LIST_HEAD(&workers->idle_list); |
| 266 | INIT_LIST_HEAD(&workers->order_list); | 447 | INIT_LIST_HEAD(&workers->order_list); |
| 267 | INIT_LIST_HEAD(&workers->prio_order_list); | 448 | INIT_LIST_HEAD(&workers->prio_order_list); |
| 268 | spin_lock_init(&workers->lock); | 449 | spin_lock_init(&workers->lock); |
| 450 | spin_lock_init(&workers->order_lock); | ||
| 269 | workers->max_workers = max; | 451 | workers->max_workers = max; |
| 270 | workers->idle_thresh = 32; | 452 | workers->idle_thresh = 32; |
| 271 | workers->name = name; | 453 | workers->name = name; |
| 272 | workers->ordered = 0; | 454 | workers->ordered = 0; |
| 455 | workers->atomic_start_pending = 0; | ||
| 456 | workers->atomic_worker_start = async_helper; | ||
| 273 | } | 457 | } |
| 274 | 458 | ||
| 275 | /* | 459 | /* |
| 276 | * starts new worker threads. This does not enforce the max worker | 460 | * starts new worker threads. This does not enforce the max worker |
| 277 | * count in case you need to temporarily go past it. | 461 | * count in case you need to temporarily go past it. |
| 278 | */ | 462 | */ |
| 279 | int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) | 463 | static int __btrfs_start_workers(struct btrfs_workers *workers, |
| 464 | int num_workers) | ||
| 280 | { | 465 | { |
| 281 | struct btrfs_worker_thread *worker; | 466 | struct btrfs_worker_thread *worker; |
| 282 | int ret = 0; | 467 | int ret = 0; |
| @@ -293,7 +478,9 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) | |||
| 293 | INIT_LIST_HEAD(&worker->prio_pending); | 478 | INIT_LIST_HEAD(&worker->prio_pending); |
| 294 | INIT_LIST_HEAD(&worker->worker_list); | 479 | INIT_LIST_HEAD(&worker->worker_list); |
| 295 | spin_lock_init(&worker->lock); | 480 | spin_lock_init(&worker->lock); |
| 481 | |||
| 296 | atomic_set(&worker->num_pending, 0); | 482 | atomic_set(&worker->num_pending, 0); |
| 483 | atomic_set(&worker->refs, 1); | ||
| 297 | worker->workers = workers; | 484 | worker->workers = workers; |
| 298 | worker->task = kthread_run(worker_loop, worker, | 485 | worker->task = kthread_run(worker_loop, worker, |
| 299 | "btrfs-%s-%d", workers->name, | 486 | "btrfs-%s-%d", workers->name, |
| @@ -303,11 +490,12 @@ int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) | |||
| 303 | kfree(worker); | 490 | kfree(worker); |
| 304 | goto fail; | 491 | goto fail; |
| 305 | } | 492 | } |
| 306 | |||
| 307 | spin_lock_irq(&workers->lock); | 493 | spin_lock_irq(&workers->lock); |
| 308 | list_add_tail(&worker->worker_list, &workers->idle_list); | 494 | list_add_tail(&worker->worker_list, &workers->idle_list); |
| 309 | worker->idle = 1; | 495 | worker->idle = 1; |
| 310 | workers->num_workers++; | 496 | workers->num_workers++; |
| 497 | workers->num_workers_starting--; | ||
| 498 | WARN_ON(workers->num_workers_starting < 0); | ||
| 311 | spin_unlock_irq(&workers->lock); | 499 | spin_unlock_irq(&workers->lock); |
| 312 | } | 500 | } |
| 313 | return 0; | 501 | return 0; |
| @@ -316,6 +504,14 @@ fail: | |||
| 316 | return ret; | 504 | return ret; |
| 317 | } | 505 | } |
| 318 | 506 | ||
| 507 | int btrfs_start_workers(struct btrfs_workers *workers, int num_workers) | ||
| 508 | { | ||
| 509 | spin_lock_irq(&workers->lock); | ||
| 510 | workers->num_workers_starting += num_workers; | ||
| 511 | spin_unlock_irq(&workers->lock); | ||
| 512 | return __btrfs_start_workers(workers, num_workers); | ||
| 513 | } | ||
| 514 | |||
| 319 | /* | 515 | /* |
| 320 | * run through the list and find a worker thread that doesn't have a lot | 516 | * run through the list and find a worker thread that doesn't have a lot |
| 321 | * to do right now. This can return null if we aren't yet at the thread | 517 | * to do right now. This can return null if we aren't yet at the thread |
| @@ -325,7 +521,10 @@ static struct btrfs_worker_thread *next_worker(struct btrfs_workers *workers) | |||
| 325 | { | 521 | { |
| 326 | struct btrfs_worker_thread *worker; | 522 | struct btrfs_worker_thread *worker; |
| 327 | struct list_head *next; | 523 | struct list_head *next; |
| 328 | int enforce_min = workers->num_workers < workers->max_workers; | 524 | int enforce_min; |
| 525 | |||
| 526 | enforce_min = (workers->num_workers + workers->num_workers_starting) < | ||
| 527 | workers->max_workers; | ||
| 329 | 528 | ||
| 330 | /* | 529 | /* |
| 331 | * if we find an idle thread, don't move it to the end of the | 530 | * if we find an idle thread, don't move it to the end of the |
| @@ -350,7 +549,6 @@ static struct btrfs_worker_thread *next_worker(struct btrfs_workers *workers) | |||
| 350 | */ | 549 | */ |
| 351 | next = workers->worker_list.next; | 550 | next = workers->worker_list.next; |
| 352 | worker = list_entry(next, struct btrfs_worker_thread, worker_list); | 551 | worker = list_entry(next, struct btrfs_worker_thread, worker_list); |
| 353 | atomic_inc(&worker->num_pending); | ||
| 354 | worker->sequence++; | 552 | worker->sequence++; |
| 355 | 553 | ||
| 356 | if (worker->sequence % workers->idle_thresh == 0) | 554 | if (worker->sequence % workers->idle_thresh == 0) |
| @@ -367,35 +565,49 @@ static struct btrfs_worker_thread *find_worker(struct btrfs_workers *workers) | |||
| 367 | { | 565 | { |
| 368 | struct btrfs_worker_thread *worker; | 566 | struct btrfs_worker_thread *worker; |
| 369 | unsigned long flags; | 567 | unsigned long flags; |
| 568 | struct list_head *fallback; | ||
| 370 | 569 | ||
| 371 | again: | 570 | again: |
| 372 | spin_lock_irqsave(&workers->lock, flags); | 571 | spin_lock_irqsave(&workers->lock, flags); |
| 373 | worker = next_worker(workers); | 572 | worker = next_worker(workers); |
| 374 | spin_unlock_irqrestore(&workers->lock, flags); | ||
| 375 | 573 | ||
| 376 | if (!worker) { | 574 | if (!worker) { |
| 377 | spin_lock_irqsave(&workers->lock, flags); | 575 | if (workers->num_workers + workers->num_workers_starting >= |
| 378 | if (workers->num_workers >= workers->max_workers) { | 576 | workers->max_workers) { |
| 379 | struct list_head *fallback = NULL; | 577 | goto fallback; |
| 380 | /* | 578 | } else if (workers->atomic_worker_start) { |
| 381 | * we have failed to find any workers, just | 579 | workers->atomic_start_pending = 1; |
| 382 | * return the force one | 580 | goto fallback; |
| 383 | */ | ||
| 384 | if (!list_empty(&workers->worker_list)) | ||
| 385 | fallback = workers->worker_list.next; | ||
| 386 | if (!list_empty(&workers->idle_list)) | ||
| 387 | fallback = workers->idle_list.next; | ||
| 388 | BUG_ON(!fallback); | ||
| 389 | worker = list_entry(fallback, | ||
| 390 | struct btrfs_worker_thread, worker_list); | ||
| 391 | spin_unlock_irqrestore(&workers->lock, flags); | ||
| 392 | } else { | 581 | } else { |
| 582 | workers->num_workers_starting++; | ||
| 393 | spin_unlock_irqrestore(&workers->lock, flags); | 583 | spin_unlock_irqrestore(&workers->lock, flags); |
| 394 | /* we're below the limit, start another worker */ | 584 | /* we're below the limit, start another worker */ |
| 395 | btrfs_start_workers(workers, 1); | 585 | __btrfs_start_workers(workers, 1); |
| 396 | goto again; | 586 | goto again; |
| 397 | } | 587 | } |
| 398 | } | 588 | } |
| 589 | goto found; | ||
| 590 | |||
| 591 | fallback: | ||
| 592 | fallback = NULL; | ||
| 593 | /* | ||
| 594 | * we have failed to find any workers, just | ||
| 595 | * return the first one we can find. | ||
| 596 | */ | ||
| 597 | if (!list_empty(&workers->worker_list)) | ||
| 598 | fallback = workers->worker_list.next; | ||
| 599 | if (!list_empty(&workers->idle_list)) | ||
| 600 | fallback = workers->idle_list.next; | ||
| 601 | BUG_ON(!fallback); | ||
| 602 | worker = list_entry(fallback, | ||
| 603 | struct btrfs_worker_thread, worker_list); | ||
| 604 | found: | ||
| 605 | /* | ||
| 606 | * this makes sure the worker doesn't exit before it is placed | ||
| 607 | * onto a busy/idle list | ||
| 608 | */ | ||
| 609 | atomic_inc(&worker->num_pending); | ||
| 610 | spin_unlock_irqrestore(&workers->lock, flags); | ||
| 399 | return worker; | 611 | return worker; |
| 400 | } | 612 | } |
| 401 | 613 | ||
| @@ -427,7 +639,7 @@ int btrfs_requeue_work(struct btrfs_work *work) | |||
| 427 | spin_lock(&worker->workers->lock); | 639 | spin_lock(&worker->workers->lock); |
| 428 | worker->idle = 0; | 640 | worker->idle = 0; |
| 429 | list_move_tail(&worker->worker_list, | 641 | list_move_tail(&worker->worker_list, |
| 430 | &worker->workers->worker_list); | 642 | &worker->workers->worker_list); |
| 431 | spin_unlock(&worker->workers->lock); | 643 | spin_unlock(&worker->workers->lock); |
| 432 | } | 644 | } |
| 433 | if (!worker->working) { | 645 | if (!worker->working) { |
| @@ -435,9 +647,9 @@ int btrfs_requeue_work(struct btrfs_work *work) | |||
| 435 | worker->working = 1; | 647 | worker->working = 1; |
| 436 | } | 648 | } |
| 437 | 649 | ||
| 438 | spin_unlock_irqrestore(&worker->lock, flags); | ||
| 439 | if (wake) | 650 | if (wake) |
| 440 | wake_up_process(worker->task); | 651 | wake_up_process(worker->task); |
| 652 | spin_unlock_irqrestore(&worker->lock, flags); | ||
| 441 | out: | 653 | out: |
| 442 | 654 | ||
| 443 | return 0; | 655 | return 0; |
| @@ -463,14 +675,18 @@ int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work) | |||
| 463 | 675 | ||
| 464 | worker = find_worker(workers); | 676 | worker = find_worker(workers); |
| 465 | if (workers->ordered) { | 677 | if (workers->ordered) { |
| 466 | spin_lock_irqsave(&workers->lock, flags); | 678 | /* |
| 679 | * you're not allowed to do ordered queues from an | ||
| 680 | * interrupt handler | ||
| 681 | */ | ||
| 682 | spin_lock(&workers->order_lock); | ||
| 467 | if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags)) { | 683 | if (test_bit(WORK_HIGH_PRIO_BIT, &work->flags)) { |
| 468 | list_add_tail(&work->order_list, | 684 | list_add_tail(&work->order_list, |
| 469 | &workers->prio_order_list); | 685 | &workers->prio_order_list); |
| 470 | } else { | 686 | } else { |
| 471 | list_add_tail(&work->order_list, &workers->order_list); | 687 | list_add_tail(&work->order_list, &workers->order_list); |
| 472 | } | 688 | } |
| 473 | spin_unlock_irqrestore(&workers->lock, flags); | 689 | spin_unlock(&workers->order_lock); |
| 474 | } else { | 690 | } else { |
| 475 | INIT_LIST_HEAD(&work->order_list); | 691 | INIT_LIST_HEAD(&work->order_list); |
| 476 | } | 692 | } |
| @@ -481,7 +697,6 @@ int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work) | |||
| 481 | list_add_tail(&work->list, &worker->prio_pending); | 697 | list_add_tail(&work->list, &worker->prio_pending); |
| 482 | else | 698 | else |
| 483 | list_add_tail(&work->list, &worker->pending); | 699 | list_add_tail(&work->list, &worker->pending); |
| 484 | atomic_inc(&worker->num_pending); | ||
| 485 | check_busy_worker(worker); | 700 | check_busy_worker(worker); |
| 486 | 701 | ||
| 487 | /* | 702 | /* |
| @@ -492,10 +707,10 @@ int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work) | |||
| 492 | wake = 1; | 707 | wake = 1; |
| 493 | worker->working = 1; | 708 | worker->working = 1; |
| 494 | 709 | ||
| 495 | spin_unlock_irqrestore(&worker->lock, flags); | ||
| 496 | |||
| 497 | if (wake) | 710 | if (wake) |
| 498 | wake_up_process(worker->task); | 711 | wake_up_process(worker->task); |
| 712 | spin_unlock_irqrestore(&worker->lock, flags); | ||
| 713 | |||
| 499 | out: | 714 | out: |
| 500 | return 0; | 715 | return 0; |
| 501 | } | 716 | } |
