workqueue: make work->data point to pool after try_to_grab_pending()
authorLai Jiangshan <laijs@cn.fujitsu.com>
Thu, 7 Feb 2013 02:04:53 +0000 (18:04 -0800)
committerTejun Heo <tj@kernel.org>
Thu, 7 Feb 2013 02:04:53 +0000 (18:04 -0800)
We plan to use work->data pointing to cwq as the synchronization
invariant when determining whether a given work item is on a locked
pool or not, which requires work->data pointing to cwq only while the
work item is queued on the associated pool.

With delayed_work updated not to overload work->data for target
workqueue recording, the only case where we still have off-queue
work->data pointing to cwq is try_to_grab_pending() which doesn't
update work->data after stealing a queued work item.  There's no
reason for try_to_grab_pending() to not update work->data to point to
the pool instead of cwq, like the normal execution does.

This patch adds set_work_pool_and_keep_pending() which makes
work->data point to pool instead of cwq but keeps the pending bit
unlike set_work_pool_and_clear_pending() (surprise!).

After this patch, it's guaranteed that only queued work items point to
cwqs.

This patch doesn't introduce any visible behavior change.

tj: Renamed the new helper function to match
    set_work_pool_and_clear_pending() and rewrote the description.

Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/workqueue.c

index 41a502c..1a442c3 100644 (file)
@@ -556,6 +556,13 @@ static void set_work_cwq(struct work_struct *work,
                      WORK_STRUCT_PENDING | WORK_STRUCT_CWQ | extra_flags);
 }
 
+static void set_work_pool_and_keep_pending(struct work_struct *work,
+                                          int pool_id)
+{
+       set_work_data(work, (unsigned long)pool_id << WORK_OFFQ_POOL_SHIFT,
+                     WORK_STRUCT_PENDING);
+}
+
 static void set_work_pool_and_clear_pending(struct work_struct *work,
                                            int pool_id)
 {
@@ -1115,6 +1122,9 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork,
                        cwq_dec_nr_in_flight(get_work_cwq(work),
                                get_work_color(work));
 
+                       /* work->data points to cwq iff queued, point to pool */
+                       set_work_pool_and_keep_pending(work, pool->id);
+
                        spin_unlock(&pool->lock);
                        return 1;
                }