Skip to content

Commit 22597dc

Browse files
pmladektorvalds
authored andcommitted
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>
1 parent 8197b3d commit 22597dc

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

include/linux/kthread.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,12 @@ extern int tsk_fork_get_node(struct task_struct *tsk);
6363
*/
6464
struct kthread_work;
6565
typedef void (*kthread_work_func_t)(struct kthread_work *work);
66+
void kthread_delayed_work_timer_fn(unsigned long __data);
6667

6768
struct kthread_worker {
6869
spinlock_t lock;
6970
struct list_head work_list;
71+
struct list_head delayed_work_list;
7072
struct task_struct *task;
7173
struct kthread_work *current_work;
7274
};
@@ -77,22 +79,39 @@ struct kthread_work {
7779
struct kthread_worker *worker;
7880
};
7981

82+
struct kthread_delayed_work {
83+
struct kthread_work work;
84+
struct timer_list timer;
85+
};
86+
8087
#define KTHREAD_WORKER_INIT(worker) { \
8188
.lock = __SPIN_LOCK_UNLOCKED((worker).lock), \
8289
.work_list = LIST_HEAD_INIT((worker).work_list), \
90+
.delayed_work_list = LIST_HEAD_INIT((worker).delayed_work_list),\
8391
}
8492

8593
#define KTHREAD_WORK_INIT(work, fn) { \
8694
.node = LIST_HEAD_INIT((work).node), \
8795
.func = (fn), \
8896
}
8997

98+
#define KTHREAD_DELAYED_WORK_INIT(dwork, fn) { \
99+
.work = KTHREAD_WORK_INIT((dwork).work, (fn)), \
100+
.timer = __TIMER_INITIALIZER(kthread_delayed_work_timer_fn, \
101+
0, (unsigned long)&(dwork), \
102+
TIMER_IRQSAFE), \
103+
}
104+
90105
#define DEFINE_KTHREAD_WORKER(worker) \
91106
struct kthread_worker worker = KTHREAD_WORKER_INIT(worker)
92107

93108
#define DEFINE_KTHREAD_WORK(work, fn) \
94109
struct kthread_work work = KTHREAD_WORK_INIT(work, fn)
95110

111+
#define DEFINE_KTHREAD_DELAYED_WORK(dwork, fn) \
112+
struct kthread_delayed_work dwork = \
113+
KTHREAD_DELAYED_WORK_INIT(dwork, fn)
114+
96115
/*
97116
* kthread_worker.lock needs its own lockdep class key when defined on
98117
* stack with lockdep enabled. Use the following macros in such cases.
@@ -122,6 +141,15 @@ extern void __kthread_init_worker(struct kthread_worker *worker,
122141
(work)->func = (fn); \
123142
} while (0)
124143

144+
#define kthread_init_delayed_work(dwork, fn) \
145+
do { \
146+
kthread_init_work(&(dwork)->work, (fn)); \
147+
__setup_timer(&(dwork)->timer, \
148+
kthread_delayed_work_timer_fn, \
149+
(unsigned long)(dwork), \
150+
TIMER_IRQSAFE); \
151+
} while (0)
152+
125153
int kthread_worker_fn(void *worker_ptr);
126154

127155
__printf(1, 2)
@@ -133,6 +161,11 @@ kthread_create_worker_on_cpu(int cpu, const char namefmt[], ...);
133161

134162
bool kthread_queue_work(struct kthread_worker *worker,
135163
struct kthread_work *work);
164+
165+
bool kthread_queue_delayed_work(struct kthread_worker *worker,
166+
struct kthread_delayed_work *dwork,
167+
unsigned long delay);
168+
136169
void kthread_flush_work(struct kthread_work *work);
137170
void kthread_flush_worker(struct kthread_worker *worker);
138171

kernel/kthread.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ void __kthread_init_worker(struct kthread_worker *worker,
563563
spin_lock_init(&worker->lock);
564564
lockdep_set_class_and_name(&worker->lock, key, name);
565565
INIT_LIST_HEAD(&worker->work_list);
566+
INIT_LIST_HEAD(&worker->delayed_work_list);
566567
worker->task = NULL;
567568
}
568569
EXPORT_SYMBOL_GPL(__kthread_init_worker);
@@ -767,6 +768,107 @@ bool kthread_queue_work(struct kthread_worker *worker,
767768
}
768769
EXPORT_SYMBOL_GPL(kthread_queue_work);
769770

771+
/**
772+
* kthread_delayed_work_timer_fn - callback that queues the associated kthread
773+
* delayed work when the timer expires.
774+
* @__data: pointer to the data associated with the timer
775+
*
776+
* The format of the function is defined by struct timer_list.
777+
* It should have been called from irqsafe timer with irq already off.
778+
*/
779+
void kthread_delayed_work_timer_fn(unsigned long __data)
780+
{
781+
struct kthread_delayed_work *dwork =
782+
(struct kthread_delayed_work *)__data;
783+
struct kthread_work *work = &dwork->work;
784+
struct kthread_worker *worker = work->worker;
785+
786+
/*
787+
* This might happen when a pending work is reinitialized.
788+
* It means that it is used a wrong way.
789+
*/
790+
if (WARN_ON_ONCE(!worker))
791+
return;
792+
793+
spin_lock(&worker->lock);
794+
/* Work must not be used with >1 worker, see kthread_queue_work(). */
795+
WARN_ON_ONCE(work->worker != worker);
796+
797+
/* Move the work from worker->delayed_work_list. */
798+
WARN_ON_ONCE(list_empty(&work->node));
799+
list_del_init(&work->node);
800+
kthread_insert_work(worker, work, &worker->work_list);
801+
802+
spin_unlock(&worker->lock);
803+
}
804+
EXPORT_SYMBOL(kthread_delayed_work_timer_fn);
805+
806+
void __kthread_queue_delayed_work(struct kthread_worker *worker,
807+
struct kthread_delayed_work *dwork,
808+
unsigned long delay)
809+
{
810+
struct timer_list *timer = &dwork->timer;
811+
struct kthread_work *work = &dwork->work;
812+
813+
WARN_ON_ONCE(timer->function != kthread_delayed_work_timer_fn ||
814+
timer->data != (unsigned long)dwork);
815+
816+
/*
817+
* If @delay is 0, queue @dwork->work immediately. This is for
818+
* both optimization and correctness. The earliest @timer can
819+
* expire is on the closest next tick and delayed_work users depend
820+
* on that there's no such delay when @delay is 0.
821+
*/
822+
if (!delay) {
823+
kthread_insert_work(worker, work, &worker->work_list);
824+
return;
825+
}
826+
827+
/* Be paranoid and try to detect possible races already now. */
828+
kthread_insert_work_sanity_check(worker, work);
829+
830+
list_add(&work->node, &worker->delayed_work_list);
831+
work->worker = worker;
832+
timer_stats_timer_set_start_info(&dwork->timer);
833+
timer->expires = jiffies + delay;
834+
add_timer(timer);
835+
}
836+
837+
/**
838+
* kthread_queue_delayed_work - queue the associated kthread work
839+
* after a delay.
840+
* @worker: target kthread_worker
841+
* @dwork: kthread_delayed_work to queue
842+
* @delay: number of jiffies to wait before queuing
843+
*
844+
* If the work has not been pending it starts a timer that will queue
845+
* the work after the given @delay. If @delay is zero, it queues the
846+
* work immediately.
847+
*
848+
* Return: %false if the @work has already been pending. It means that
849+
* either the timer was running or the work was queued. It returns %true
850+
* otherwise.
851+
*/
852+
bool kthread_queue_delayed_work(struct kthread_worker *worker,
853+
struct kthread_delayed_work *dwork,
854+
unsigned long delay)
855+
{
856+
struct kthread_work *work = &dwork->work;
857+
unsigned long flags;
858+
bool ret = false;
859+
860+
spin_lock_irqsave(&worker->lock, flags);
861+
862+
if (list_empty(&work->node)) {
863+
__kthread_queue_delayed_work(worker, dwork, delay);
864+
ret = true;
865+
}
866+
867+
spin_unlock_irqrestore(&worker->lock, flags);
868+
return ret;
869+
}
870+
EXPORT_SYMBOL_GPL(kthread_queue_delayed_work);
871+
770872
struct kthread_flush_work {
771873
struct kthread_work work;
772874
struct completion done;

0 commit comments

Comments
 (0)