diff options
-rw-r--r-- | Documentation/slow-work.txt | 44 | ||||
-rw-r--r-- | include/linux/slow-work.h | 3 | ||||
-rw-r--r-- | kernel/slow-work.c | 94 |
3 files changed, 132 insertions, 9 deletions
diff --git a/Documentation/slow-work.txt b/Documentation/slow-work.txt index 0169c9d9dd16..52bc31433723 100644 --- a/Documentation/slow-work.txt +++ b/Documentation/slow-work.txt | |||
@@ -158,6 +158,50 @@ with a requeue pending). This can be used to work out whether an item on which | |||
158 | another depends is on the queue, thus allowing a dependent item to be queued | 158 | another depends is on the queue, thus allowing a dependent item to be queued |
159 | after it. | 159 | after it. |
160 | 160 | ||
161 | If the above shows an item on which another depends not to be queued, then the | ||
162 | owner of the dependent item might need to wait. However, to avoid locking up | ||
163 | the threads unnecessarily be sleeping in them, it can make sense under some | ||
164 | circumstances to return the work item to the queue, thus deferring it until | ||
165 | some other items have had a chance to make use of the yielded thread. | ||
166 | |||
167 | To yield a thread and defer an item, the work function should simply enqueue | ||
168 | the work item again and return. However, this doesn't work if there's nothing | ||
169 | actually on the queue, as the thread just vacated will jump straight back into | ||
170 | the item's work function, thus busy waiting on a CPU. | ||
171 | |||
172 | Instead, the item should use the thread to wait for the dependency to go away, | ||
173 | but rather than using schedule() or schedule_timeout() to sleep, it should use | ||
174 | the following function: | ||
175 | |||
176 | bool requeue = slow_work_sleep_till_thread_needed( | ||
177 | struct slow_work *work, | ||
178 | signed long *_timeout); | ||
179 | |||
180 | This will add a second wait and then sleep, such that it will be woken up if | ||
181 | either something appears on the queue that could usefully make use of the | ||
182 | thread - and behind which this item can be queued, or if the event the caller | ||
183 | set up to wait for happens. True will be returned if something else appeared | ||
184 | on the queue and this work function should perhaps return, of false if | ||
185 | something else woke it up. The timeout is as for schedule_timeout(). | ||
186 | |||
187 | For example: | ||
188 | |||
189 | wq = bit_waitqueue(&my_flags, MY_BIT); | ||
190 | init_wait(&wait); | ||
191 | requeue = false; | ||
192 | do { | ||
193 | prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); | ||
194 | if (!test_bit(MY_BIT, &my_flags)) | ||
195 | break; | ||
196 | requeue = slow_work_sleep_till_thread_needed(&my_work, | ||
197 | &timeout); | ||
198 | } while (timeout > 0 && !requeue); | ||
199 | finish_wait(wq, &wait); | ||
200 | if (!test_bit(MY_BIT, &my_flags) | ||
201 | goto do_my_thing; | ||
202 | if (requeue) | ||
203 | return; // to slow_work | ||
204 | |||
161 | 205 | ||
162 | =============== | 206 | =============== |
163 | ITEM OPERATIONS | 207 | ITEM OPERATIONS |
diff --git a/include/linux/slow-work.h b/include/linux/slow-work.h index bfd3ab4c8898..5035a2691739 100644 --- a/include/linux/slow-work.h +++ b/include/linux/slow-work.h | |||
@@ -152,6 +152,9 @@ static inline void delayed_slow_work_cancel(struct delayed_slow_work *dwork) | |||
152 | slow_work_cancel(&dwork->work); | 152 | slow_work_cancel(&dwork->work); |
153 | } | 153 | } |
154 | 154 | ||
155 | extern bool slow_work_sleep_till_thread_needed(struct slow_work *work, | ||
156 | signed long *_timeout); | ||
157 | |||
155 | #ifdef CONFIG_SYSCTL | 158 | #ifdef CONFIG_SYSCTL |
156 | extern ctl_table slow_work_sysctls[]; | 159 | extern ctl_table slow_work_sysctls[]; |
157 | #endif | 160 | #endif |
diff --git a/kernel/slow-work.c b/kernel/slow-work.c index b763bc2d2670..da94f3c101af 100644 --- a/kernel/slow-work.c +++ b/kernel/slow-work.c | |||
@@ -133,6 +133,15 @@ LIST_HEAD(vslow_work_queue); | |||
133 | DEFINE_SPINLOCK(slow_work_queue_lock); | 133 | DEFINE_SPINLOCK(slow_work_queue_lock); |
134 | 134 | ||
135 | /* | 135 | /* |
136 | * The following are two wait queues that get pinged when a work item is placed | ||
137 | * on an empty queue. These allow work items that are hogging a thread by | ||
138 | * sleeping in a way that could be deferred to yield their thread and enqueue | ||
139 | * themselves. | ||
140 | */ | ||
141 | static DECLARE_WAIT_QUEUE_HEAD(slow_work_queue_waits_for_occupation); | ||
142 | static DECLARE_WAIT_QUEUE_HEAD(vslow_work_queue_waits_for_occupation); | ||
143 | |||
144 | /* | ||
136 | * The thread controls. A variable used to signal to the threads that they | 145 | * The thread controls. A variable used to signal to the threads that they |
137 | * should exit when the queue is empty, a waitqueue used by the threads to wait | 146 | * should exit when the queue is empty, a waitqueue used by the threads to wait |
138 | * for signals, and a completion set by the last thread to exit. | 147 | * for signals, and a completion set by the last thread to exit. |
@@ -306,6 +315,50 @@ auto_requeue: | |||
306 | } | 315 | } |
307 | 316 | ||
308 | /** | 317 | /** |
318 | * slow_work_sleep_till_thread_needed - Sleep till thread needed by other work | ||
319 | * work: The work item under execution that wants to sleep | ||
320 | * _timeout: Scheduler sleep timeout | ||
321 | * | ||
322 | * Allow a requeueable work item to sleep on a slow-work processor thread until | ||
323 | * that thread is needed to do some other work or the sleep is interrupted by | ||
324 | * some other event. | ||
325 | * | ||
326 | * The caller must set up a wake up event before calling this and must have set | ||
327 | * the appropriate sleep mode (such as TASK_UNINTERRUPTIBLE) and tested its own | ||
328 | * condition before calling this function as no test is made here. | ||
329 | * | ||
330 | * False is returned if there is nothing on the queue; true is returned if the | ||
331 | * work item should be requeued | ||
332 | */ | ||
333 | bool slow_work_sleep_till_thread_needed(struct slow_work *work, | ||
334 | signed long *_timeout) | ||
335 | { | ||
336 | wait_queue_head_t *wfo_wq; | ||
337 | struct list_head *queue; | ||
338 | |||
339 | DEFINE_WAIT(wait); | ||
340 | |||
341 | if (test_bit(SLOW_WORK_VERY_SLOW, &work->flags)) { | ||
342 | wfo_wq = &vslow_work_queue_waits_for_occupation; | ||
343 | queue = &vslow_work_queue; | ||
344 | } else { | ||
345 | wfo_wq = &slow_work_queue_waits_for_occupation; | ||
346 | queue = &slow_work_queue; | ||
347 | } | ||
348 | |||
349 | if (!list_empty(queue)) | ||
350 | return true; | ||
351 | |||
352 | add_wait_queue_exclusive(wfo_wq, &wait); | ||
353 | if (list_empty(queue)) | ||
354 | *_timeout = schedule_timeout(*_timeout); | ||
355 | finish_wait(wfo_wq, &wait); | ||
356 | |||
357 | return !list_empty(queue); | ||
358 | } | ||
359 | EXPORT_SYMBOL(slow_work_sleep_till_thread_needed); | ||
360 | |||
361 | /** | ||
309 | * slow_work_enqueue - Schedule a slow work item for processing | 362 | * slow_work_enqueue - Schedule a slow work item for processing |
310 | * @work: The work item to queue | 363 | * @work: The work item to queue |
311 | * | 364 | * |
@@ -335,6 +388,8 @@ auto_requeue: | |||
335 | */ | 388 | */ |
336 | int slow_work_enqueue(struct slow_work *work) | 389 | int slow_work_enqueue(struct slow_work *work) |
337 | { | 390 | { |
391 | wait_queue_head_t *wfo_wq; | ||
392 | struct list_head *queue; | ||
338 | unsigned long flags; | 393 | unsigned long flags; |
339 | int ret; | 394 | int ret; |
340 | 395 | ||
@@ -354,6 +409,14 @@ int slow_work_enqueue(struct slow_work *work) | |||
354 | * maintaining our promise | 409 | * maintaining our promise |
355 | */ | 410 | */ |
356 | if (!test_and_set_bit_lock(SLOW_WORK_PENDING, &work->flags)) { | 411 | if (!test_and_set_bit_lock(SLOW_WORK_PENDING, &work->flags)) { |
412 | if (test_bit(SLOW_WORK_VERY_SLOW, &work->flags)) { | ||
413 | wfo_wq = &vslow_work_queue_waits_for_occupation; | ||
414 | queue = &vslow_work_queue; | ||
415 | } else { | ||
416 | wfo_wq = &slow_work_queue_waits_for_occupation; | ||
417 | queue = &slow_work_queue; | ||
418 | } | ||
419 | |||
357 | spin_lock_irqsave(&slow_work_queue_lock, flags); | 420 | spin_lock_irqsave(&slow_work_queue_lock, flags); |
358 | 421 | ||
359 | if (unlikely(test_bit(SLOW_WORK_CANCELLING, &work->flags))) | 422 | if (unlikely(test_bit(SLOW_WORK_CANCELLING, &work->flags))) |
@@ -380,11 +443,13 @@ int slow_work_enqueue(struct slow_work *work) | |||
380 | if (ret < 0) | 443 | if (ret < 0) |
381 | goto failed; | 444 | goto failed; |
382 | slow_work_mark_time(work); | 445 | slow_work_mark_time(work); |
383 | if (test_bit(SLOW_WORK_VERY_SLOW, &work->flags)) | 446 | list_add_tail(&work->link, queue); |
384 | list_add_tail(&work->link, &vslow_work_queue); | ||
385 | else | ||
386 | list_add_tail(&work->link, &slow_work_queue); | ||
387 | wake_up(&slow_work_thread_wq); | 447 | wake_up(&slow_work_thread_wq); |
448 | |||
449 | /* if someone who could be requeued is sleeping on a | ||
450 | * thread, then ask them to yield their thread */ | ||
451 | if (work->link.prev == queue) | ||
452 | wake_up(wfo_wq); | ||
388 | } | 453 | } |
389 | 454 | ||
390 | spin_unlock_irqrestore(&slow_work_queue_lock, flags); | 455 | spin_unlock_irqrestore(&slow_work_queue_lock, flags); |
@@ -487,9 +552,19 @@ EXPORT_SYMBOL(slow_work_cancel); | |||
487 | */ | 552 | */ |
488 | static void delayed_slow_work_timer(unsigned long data) | 553 | static void delayed_slow_work_timer(unsigned long data) |
489 | { | 554 | { |
555 | wait_queue_head_t *wfo_wq; | ||
556 | struct list_head *queue; | ||
490 | struct slow_work *work = (struct slow_work *) data; | 557 | struct slow_work *work = (struct slow_work *) data; |
491 | unsigned long flags; | 558 | unsigned long flags; |
492 | bool queued = false, put = false; | 559 | bool queued = false, put = false, first = false; |
560 | |||
561 | if (test_bit(SLOW_WORK_VERY_SLOW, &work->flags)) { | ||
562 | wfo_wq = &vslow_work_queue_waits_for_occupation; | ||
563 | queue = &vslow_work_queue; | ||
564 | } else { | ||
565 | wfo_wq = &slow_work_queue_waits_for_occupation; | ||
566 | queue = &slow_work_queue; | ||
567 | } | ||
493 | 568 | ||
494 | spin_lock_irqsave(&slow_work_queue_lock, flags); | 569 | spin_lock_irqsave(&slow_work_queue_lock, flags); |
495 | if (likely(!test_bit(SLOW_WORK_CANCELLING, &work->flags))) { | 570 | if (likely(!test_bit(SLOW_WORK_CANCELLING, &work->flags))) { |
@@ -502,17 +577,18 @@ static void delayed_slow_work_timer(unsigned long data) | |||
502 | put = true; | 577 | put = true; |
503 | } else { | 578 | } else { |
504 | slow_work_mark_time(work); | 579 | slow_work_mark_time(work); |
505 | if (test_bit(SLOW_WORK_VERY_SLOW, &work->flags)) | 580 | list_add_tail(&work->link, queue); |
506 | list_add_tail(&work->link, &vslow_work_queue); | ||
507 | else | ||
508 | list_add_tail(&work->link, &slow_work_queue); | ||
509 | queued = true; | 581 | queued = true; |
582 | if (work->link.prev == queue) | ||
583 | first = true; | ||
510 | } | 584 | } |
511 | } | 585 | } |
512 | 586 | ||
513 | spin_unlock_irqrestore(&slow_work_queue_lock, flags); | 587 | spin_unlock_irqrestore(&slow_work_queue_lock, flags); |
514 | if (put) | 588 | if (put) |
515 | slow_work_put_ref(work); | 589 | slow_work_put_ref(work); |
590 | if (first) | ||
591 | wake_up(wfo_wq); | ||
516 | if (queued) | 592 | if (queued) |
517 | wake_up(&slow_work_thread_wq); | 593 | wake_up(&slow_work_thread_wq); |
518 | } | 594 | } |