diff options
author | Petr Mladek <pmladek@suse.com> | 2016-10-11 13:55:40 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-11 15:06:33 -0700 |
commit | 22597dc3d97b1ead2aca201397415a1a84bf2b26 (patch) | |
tree | cb2b8efb6f3181e963e9c3619c78f3d02c98226b /include/linux | |
parent | 8197b3d43b250b8214b9913878e9227eef8bd85f (diff) | |
download | linux-stable-22597dc3d97b1ead2aca201397415a1a84bf2b26.tar.gz linux-stable-22597dc3d97b1ead2aca201397415a1a84bf2b26.tar.bz2 linux-stable-22597dc3d97b1ead2aca201397415a1a84bf2b26.zip |
kthread: initial support for delayed kthread work
We are going to use kthread_worker more widely and delayed works
will be pretty useful.
The implementation is inspired by workqueues. It uses a timer to queue
the work after the requested delay. If the delay is zero, the work is
queued immediately.
In compare with workqueues, each work is associated with a single worker
(kthread). Therefore the implementation could be much easier. In
particular, we use the worker->lock to synchronize all the operations with
the work. We do not need any atomic operation with a flags variable.
In fact, we do not need any state variable at all. Instead, we add a list
of delayed works into the worker. Then the pending work is listed either
in the list of queued or delayed works. And the existing check of pending
works is the same even for the delayed ones.
A work must not be assigned to another worker unless reinitialized.
Therefore the timer handler might expect that dwork->work->worker is valid
and it could simply take the lock. We just add some sanity checks to help
with debugging a potential misuse.
Link: http://lkml.kernel.org/r/1470754545-17632-9-git-send-email-pmladek@suse.com
Signed-off-by: Petr Mladek <pmladek@suse.com>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jiri Kosina <jkosina@suse.cz>
Cc: Borislav Petkov <bp@suse.de>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include/linux')
-rw-r--r-- | include/linux/kthread.h | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/include/linux/kthread.h b/include/linux/kthread.h index afc8939da861..4acde1ae2228 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -63,10 +63,12 @@ extern int tsk_fork_get_node(struct task_struct *tsk); */ struct kthread_work; typedef void (*kthread_work_func_t)(struct kthread_work *work); +void kthread_delayed_work_timer_fn(unsigned long __data); struct kthread_worker { spinlock_t lock; struct list_head work_list; + struct list_head delayed_work_list; struct task_struct *task; struct kthread_work *current_work; }; @@ -77,9 +79,15 @@ struct kthread_work { struct kthread_worker *worker; }; +struct kthread_delayed_work { + struct kthread_work work; + struct timer_list timer; +}; + #define KTHREAD_WORKER_INIT(worker) { \ .lock = __SPIN_LOCK_UNLOCKED((worker).lock), \ .work_list = LIST_HEAD_INIT((worker).work_list), \ + .delayed_work_list = LIST_HEAD_INIT((worker).delayed_work_list),\ } #define KTHREAD_WORK_INIT(work, fn) { \ @@ -87,12 +95,23 @@ struct kthread_work { .func = (fn), \ } +#define KTHREAD_DELAYED_WORK_INIT(dwork, fn) { \ + .work = KTHREAD_WORK_INIT((dwork).work, (fn)), \ + .timer = __TIMER_INITIALIZER(kthread_delayed_work_timer_fn, \ + 0, (unsigned long)&(dwork), \ + TIMER_IRQSAFE), \ + } + #define DEFINE_KTHREAD_WORKER(worker) \ struct kthread_worker worker = KTHREAD_WORKER_INIT(worker) #define DEFINE_KTHREAD_WORK(work, fn) \ struct kthread_work work = KTHREAD_WORK_INIT(work, fn) +#define DEFINE_KTHREAD_DELAYED_WORK(dwork, fn) \ + struct kthread_delayed_work dwork = \ + KTHREAD_DELAYED_WORK_INIT(dwork, fn) + /* * kthread_worker.lock needs its own lockdep class key when defined on * stack with lockdep enabled. Use the following macros in such cases. @@ -122,6 +141,15 @@ extern void __kthread_init_worker(struct kthread_worker *worker, (work)->func = (fn); \ } while (0) +#define kthread_init_delayed_work(dwork, fn) \ + do { \ + kthread_init_work(&(dwork)->work, (fn)); \ + __setup_timer(&(dwork)->timer, \ + kthread_delayed_work_timer_fn, \ + (unsigned long)(dwork), \ + TIMER_IRQSAFE); \ + } while (0) + int kthread_worker_fn(void *worker_ptr); __printf(1, 2) @@ -133,6 +161,11 @@ kthread_create_worker_on_cpu(int cpu, const char namefmt[], ...); bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work); + +bool kthread_queue_delayed_work(struct kthread_worker *worker, + struct kthread_delayed_work *dwork, + unsigned long delay); + void kthread_flush_work(struct kthread_work *work); void kthread_flush_worker(struct kthread_worker *worker); |