Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2141

kernel-2.6.18-238.el5.src.rpm

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));
 }
 
 /**