]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob
b055db3c8335be9c7ab4cbc760038fd679968f5a
[thirdparty/kernel/stable-queue.git] /
1 From 46f3d976213452350f9d10b0c2780c2681f7075b Mon Sep 17 00:00:00 2001
2 From: Tejun Heo <tj@kernel.org>
3 Date: Thu, 19 Jul 2012 13:52:53 -0700
4 Subject: kthread_worker: reimplement flush_kthread_work() to allow freeing the work item being executed
5
6 From: Tejun Heo <tj@kernel.org>
7
8 commit 46f3d976213452350f9d10b0c2780c2681f7075b upstream.
9
10 kthread_worker provides minimalistic workqueue-like interface for
11 users which need a dedicated worker thread (e.g. for realtime
12 priority). It has basic queue, flush_work, flush_worker operations
13 which mostly match the workqueue counterparts; however, due to the way
14 flush_work() is implemented, it has a noticeable difference of not
15 allowing work items to be freed while being executed.
16
17 While the current users of kthread_worker are okay with the current
18 behavior, the restriction does impede some valid use cases. Also,
19 removing this difference isn't difficult and actually makes the code
20 easier to understand.
21
22 This patch reimplements flush_kthread_work() such that it uses a
23 flush_work item instead of queue/done sequence numbers.
24
25 Signed-off-by: Tejun Heo <tj@kernel.org>
26 Cc: Colin Cross <ccross@google.com>
27 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
28
29 ---
30 include/linux/kthread.h | 8 +------
31 kernel/kthread.c | 52 ++++++++++++++++++++++++++----------------------
32 2 files changed, 31 insertions(+), 29 deletions(-)
33
34 --- a/include/linux/kthread.h
35 +++ b/include/linux/kthread.h
36 @@ -49,8 +49,6 @@ extern int tsk_fork_get_node(struct task
37 * can be queued and flushed using queue/flush_kthread_work()
38 * respectively. Queued kthread_works are processed by a kthread
39 * running kthread_worker_fn().
40 - *
41 - * A kthread_work can't be freed while it is executing.
42 */
43 struct kthread_work;
44 typedef void (*kthread_work_func_t)(struct kthread_work *work);
45 @@ -59,15 +57,14 @@ struct kthread_worker {
46 spinlock_t lock;
47 struct list_head work_list;
48 struct task_struct *task;
49 + struct kthread_work *current_work;
50 };
51
52 struct kthread_work {
53 struct list_head node;
54 kthread_work_func_t func;
55 wait_queue_head_t done;
56 - atomic_t flushing;
57 - int queue_seq;
58 - int done_seq;
59 + struct kthread_worker *worker;
60 };
61
62 #define KTHREAD_WORKER_INIT(worker) { \
63 @@ -79,7 +76,6 @@ struct kthread_work {
64 .node = LIST_HEAD_INIT((work).node), \
65 .func = (fn), \
66 .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done), \
67 - .flushing = ATOMIC_INIT(0), \
68 }
69
70 #define DEFINE_KTHREAD_WORKER(worker) \
71 --- a/kernel/kthread.c
72 +++ b/kernel/kthread.c
73 @@ -360,16 +360,12 @@ repeat:
74 struct kthread_work, node);
75 list_del_init(&work->node);
76 }
77 + worker->current_work = work;
78 spin_unlock_irq(&worker->lock);
79
80 if (work) {
81 __set_current_state(TASK_RUNNING);
82 work->func(work);
83 - smp_wmb(); /* wmb worker-b0 paired with flush-b1 */
84 - work->done_seq = work->queue_seq;
85 - smp_mb(); /* mb worker-b1 paired with flush-b0 */
86 - if (atomic_read(&work->flushing))
87 - wake_up_all(&work->done);
88 } else if (!freezing(current))
89 schedule();
90
91 @@ -386,7 +382,7 @@ static void insert_kthread_work(struct k
92 lockdep_assert_held(&worker->lock);
93
94 list_add_tail(&work->node, pos);
95 - work->queue_seq++;
96 + work->worker = worker;
97 if (likely(worker->task))
98 wake_up_process(worker->task);
99 }
100 @@ -436,25 +432,35 @@ static void kthread_flush_work_fn(struct
101 */
102 void flush_kthread_work(struct kthread_work *work)
103 {
104 - int seq = work->queue_seq;
105 + struct kthread_flush_work fwork = {
106 + KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
107 + COMPLETION_INITIALIZER_ONSTACK(fwork.done),
108 + };
109 + struct kthread_worker *worker;
110 + bool noop = false;
111 +
112 +retry:
113 + worker = work->worker;
114 + if (!worker)
115 + return;
116 +
117 + spin_lock_irq(&worker->lock);
118 + if (work->worker != worker) {
119 + spin_unlock_irq(&worker->lock);
120 + goto retry;
121 + }
122 +
123 + if (!list_empty(&work->node))
124 + insert_kthread_work(worker, &fwork.work, work->node.next);
125 + else if (worker->current_work == work)
126 + insert_kthread_work(worker, &fwork.work, worker->work_list.next);
127 + else
128 + noop = true;
129
130 - atomic_inc(&work->flushing);
131 + spin_unlock_irq(&worker->lock);
132
133 - /*
134 - * mb flush-b0 paired with worker-b1, to make sure either
135 - * worker sees the above increment or we see done_seq update.
136 - */
137 - smp_mb__after_atomic_inc();
138 -
139 - /* A - B <= 0 tests whether B is in front of A regardless of overflow */
140 - wait_event(work->done, seq - work->done_seq <= 0);
141 - atomic_dec(&work->flushing);
142 -
143 - /*
144 - * rmb flush-b1 paired with worker-b0, to make sure our caller
145 - * sees every change made by work->func().
146 - */
147 - smp_mb__after_atomic_dec();
148 + if (!noop)
149 + wait_for_completion(&fwork.done);
150 }
151 EXPORT_SYMBOL_GPL(flush_kthread_work);
152