From: Oleg Nesterov <oleg@redhat.com> Date: Sun, 30 May 2010 16:20:17 -0400 Subject: [misc] workqueue: add set_wq_data and get_wq_data helpers Message-id: <20100530162017.GD9577@redhat.com> Patchwork-id: 25906 O-Subject: [RHEL5 PATCH 3/7] bz#596626: workqueues: introduce set_wq_data() and get_wq_data() helpers Bugzilla: 596626 RH-Acked-by: Stanislaw Gruszka <sgruszka@redhat.com> RH-Acked-by: Prarit Bhargava <prarit@redhat.com> (changes the existing code) cancel/flush helpers need to look at work->wq_data and figure out where this work might be queued. Unfortunately, INIT_WORK() does not initialize ->wq_data at all. And we can't change this macro, everything should work with the already compiled modules. Introduce set_wq_data/get_wq_data helper which set/check the unused bit 1 in work->pending, and change __queue_work() and set_delayed_wq_data() to use set_wq_data(). Now it is always safe to use get_wq_data(), it returns either NULL or the valid cwq, like in upstream. Signed-off-by: Oleg Nesterov <oleg@redhat.com> diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 3a61c86..080fc3e 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -80,6 +80,28 @@ static inline int is_single_threaded(struct workqueue_struct *wq) return list_empty(&wq->list); } +/* + * INIT_WORK() doesn't initialize work->wq_data, and we can't change + * this helper due to kabi problems. That is why we use bit 1 to mark + * this work as having the valid ->wq_data == cwq. + */ +static void set_wq_data(struct work_struct *work, + struct cpu_workqueue_struct *cwq) +{ + work->wq_data = cwq; + if (!test_bit(1, &work->pending)) { + smp_wmb(); /* get_wq_data()->rmb() */ + set_bit(1, &work->pending); + } +} +static struct cpu_workqueue_struct *get_wq_data(struct work_struct *work) +{ + if (!test_bit(1, &work->pending)) + return NULL; + smp_rmb(); /* set_wq_data()->wmb() */ + return work->wq_data; +} + /* Preempt must be disabled. */ static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) @@ -87,7 +109,7 @@ static void __queue_work(struct cpu_workqueue_struct *cwq, unsigned long flags; spin_lock_irqsave(&cwq->lock, flags); - work->wq_data = cwq; + set_wq_data(work, cwq); /* * Ensure that we get the right work->wq_data if we see the * result of list_add() below, see try_to_grab_pending(). @@ -145,7 +167,7 @@ static void set_delayed_wq_data(struct work_struct *work, { int cpu = is_single_threaded(wq) ? singlethread_cpu : raw_smp_processor_id(); - work->wq_data = per_cpu_ptr(wq->cpu_wq, cpu); + set_wq_data(work, per_cpu_ptr(wq->cpu_wq, cpu)); } /**