workqueue: change cancel_work_sync() to clear work->data
authorOleg Nesterov <oleg@redhat.com>
Fri, 23 Apr 2010 15:40:40 +0000 (17:40 +0200)
committerTejun Heo <tj@kernel.org>
Fri, 30 Apr 2010 06:57:25 +0000 (08:57 +0200)
commit4d707b9f48e2c4aa94b96f1133813b73df71fb55
tree2e8e6c44e55bcea9ae2de200ebb3edaac81c2a88
parenteef6a7d5c2f38adadab8240fabf43730fe796482
workqueue: change cancel_work_sync() to clear work->data

In short: change cancel_work_sync(work) to mark this work as "never
queued" upon return.

When cancel_work_sync(work) succeeds, we know that this work can't be
queued or running, and since we own WORK_STRUCT_PENDING nobody can change
the bits in work->data under us. This means we can also clear the "cwq"
part along with _PENDING bit lockless before return, unless the work is
queued nobody can assume get_wq_data() is stable even under cwq->lock.

This change can speedup the subsequent cancel/flush requests, and as
Dmitry pointed out this simplifies the usage of work_struct's which
can be queued on different workqueues. Consider this pseudo code from
the input subsystem:

struct workqueue_struct *WQ;
struct work_struct *WORK;

for (;;) {
WQ = create_workqueue();
...
if (condition())
queue_work(WQ, WORK);
...
cancel_work_sync(WORK);
destroy_workqueue(WQ);
}

If condition() returns T and then F, cancel_work_sync() will crash the
kernel because WORK->data still points to the already destroyed workqueue.
With this patch the code like above becomes correct.

Suggested-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/workqueue.c