aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/kthread.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-06-29 04:07:09 -0400
committerTejun Heo <tj@kernel.org>2010-06-29 04:07:09 -0400
commitb56c0d8937e665a27d90517ee7a746d0aa05af46 (patch)
treefefff33fe3bbebfc8d60ba581e5343dc6cb56a70 /kernel/kthread.c
parent53c5f5ba42c194cb13dd3083ed425f2c5b1ec439 (diff)
kthread: implement kthread_worker
Implement simple work processor for kthread. This is to ease using kthread. Single thread workqueue used to be used for things like this but workqueue won't guarantee fixed kthread association anymore to enable worker sharing. This can be used in cases where specific kthread association is necessary, for example, when it should have RT priority or be assigned to certain cgroup. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'kernel/kthread.c')
-rw-r--r--kernel/kthread.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/kernel/kthread.c b/kernel/kthread.c
index 83911c780175..8b63c7fee73b 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -14,6 +14,8 @@
14#include <linux/file.h> 14#include <linux/file.h>
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/mutex.h> 16#include <linux/mutex.h>
17#include <linux/slab.h>
18#include <linux/freezer.h>
17#include <trace/events/sched.h> 19#include <trace/events/sched.h>
18 20
19static DEFINE_SPINLOCK(kthread_create_lock); 21static DEFINE_SPINLOCK(kthread_create_lock);
@@ -247,3 +249,150 @@ int kthreadd(void *unused)
247 249
248 return 0; 250 return 0;
249} 251}
252
253/**
254 * kthread_worker_fn - kthread function to process kthread_worker
255 * @worker_ptr: pointer to initialized kthread_worker
256 *
257 * This function can be used as @threadfn to kthread_create() or
258 * kthread_run() with @worker_ptr argument pointing to an initialized
259 * kthread_worker. The started kthread will process work_list until
260 * the it is stopped with kthread_stop(). A kthread can also call
261 * this function directly after extra initialization.
262 *
263 * Different kthreads can be used for the same kthread_worker as long
264 * as there's only one kthread attached to it at any given time. A
265 * kthread_worker without an attached kthread simply collects queued
266 * kthread_works.
267 */
268int kthread_worker_fn(void *worker_ptr)
269{
270 struct kthread_worker *worker = worker_ptr;
271 struct kthread_work *work;
272
273 WARN_ON(worker->task);
274 worker->task = current;
275repeat:
276 set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */
277
278 if (kthread_should_stop()) {
279 __set_current_state(TASK_RUNNING);
280 spin_lock_irq(&worker->lock);
281 worker->task = NULL;
282 spin_unlock_irq(&worker->lock);
283 return 0;
284 }
285
286 work = NULL;
287 spin_lock_irq(&worker->lock);
288 if (!list_empty(&worker->work_list)) {
289 work = list_first_entry(&worker->work_list,
290 struct kthread_work, node);
291 list_del_init(&work->node);
292 }
293 spin_unlock_irq(&worker->lock);
294
295 if (work) {
296 __set_current_state(TASK_RUNNING);
297 work->func(work);
298 smp_wmb(); /* wmb worker-b0 paired with flush-b1 */
299 work->done_seq = work->queue_seq;
300 smp_mb(); /* mb worker-b1 paired with flush-b0 */
301 if (atomic_read(&work->flushing))
302 wake_up_all(&work->done);
303 } else if (!freezing(current))
304 schedule();
305
306 try_to_freeze();
307 goto repeat;
308}
309EXPORT_SYMBOL_GPL(kthread_worker_fn);
310
311/**
312 * queue_kthread_work - queue a kthread_work
313 * @worker: target kthread_worker
314 * @work: kthread_work to queue
315 *
316 * Queue @work to work processor @task for async execution. @task
317 * must have been created with kthread_worker_create(). Returns %true
318 * if @work was successfully queued, %false if it was already pending.
319 */
320bool queue_kthread_work(struct kthread_worker *worker,
321 struct kthread_work *work)
322{
323 bool ret = false;
324 unsigned long flags;
325
326 spin_lock_irqsave(&worker->lock, flags);
327 if (list_empty(&work->node)) {
328 list_add_tail(&work->node, &worker->work_list);
329 work->queue_seq++;
330 if (likely(worker->task))
331 wake_up_process(worker->task);
332 ret = true;
333 }
334 spin_unlock_irqrestore(&worker->lock, flags);
335 return ret;
336}
337EXPORT_SYMBOL_GPL(queue_kthread_work);
338
339/**
340 * flush_kthread_work - flush a kthread_work
341 * @work: work to flush
342 *
343 * If @work is queued or executing, wait for it to finish execution.
344 */
345void flush_kthread_work(struct kthread_work *work)
346{
347 int seq = work->queue_seq;
348
349 atomic_inc(&work->flushing);
350
351 /*
352 * mb flush-b0 paired with worker-b1, to make sure either
353 * worker sees the above increment or we see done_seq update.
354 */
355 smp_mb__after_atomic_inc();
356
357 /* A - B <= 0 tests whether B is in front of A regardless of overflow */
358 wait_event(work->done, seq - work->done_seq <= 0);
359 atomic_dec(&work->flushing);
360
361 /*
362 * rmb flush-b1 paired with worker-b0, to make sure our caller
363 * sees every change made by work->func().
364 */
365 smp_mb__after_atomic_dec();
366}
367EXPORT_SYMBOL_GPL(flush_kthread_work);
368
369struct kthread_flush_work {
370 struct kthread_work work;
371 struct completion done;
372};
373
374static void kthread_flush_work_fn(struct kthread_work *work)
375{
376 struct kthread_flush_work *fwork =
377 container_of(work, struct kthread_flush_work, work);
378 complete(&fwork->done);
379}
380
381/**
382 * flush_kthread_worker - flush all current works on a kthread_worker
383 * @worker: worker to flush
384 *
385 * Wait until all currently executing or pending works on @worker are
386 * finished.
387 */
388void flush_kthread_worker(struct kthread_worker *worker)
389{
390 struct kthread_flush_work fwork = {
391 KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
392 COMPLETION_INITIALIZER_ONSTACK(fwork.done),
393 };
394
395 queue_kthread_work(worker, &fwork.work);
396 wait_for_completion(&fwork.done);
397}
398EXPORT_SYMBOL_GPL(flush_kthread_worker);