Extract the lock code from __queue_work() and name it 
lock_pool_executing_work().
It makes the code better readability and make __queue_work() shorter,

And this new function can be reused by others(later patches).

Add/Use proper locking API.

This patch has a little bad side effect for __queue_work():
even worker_pool_by_id(pool_id) == get_cwq(cpu, wq)->pool,
It will still call find_worker_executing_work() unconditionally, but it is
it is a very small overhead compared to the lockings and worker_pool_by_id(),
and this overhead will be reduced later.

Signed-off-by: Lai Jiangshan <[email protected]>
---
 kernel/workqueue.c |   72 +++++++++++++++++++++++++++++++++++----------------
 1 files changed, 49 insertions(+), 23 deletions(-)

diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index a138844..6e92f18 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -934,6 +934,43 @@ static struct worker *find_worker_executing_work(struct 
worker_pool *pool,
        return NULL;
 }
 
+/** lock_pool_executing_work - lock the pool a given offq work is running on
+ * @work: work of interest
+ * @worker: return the worker which is executing @work if found
+ *
+ * CONTEXT:
+ * local_irq_disable()
+ *
+ * RETURNS:
+ * Pointer to work pool(and locked) on which @work is running if found,
+ * NULL otherwise.
+ */
+static struct worker_pool *lock_pool_executing_work(struct work_struct *work,
+                                                   struct worker **worker)
+{
+       unsigned long pool_id = offq_work_pool_id(work);
+       struct worker_pool *pool;
+       struct worker *exec;
+
+       if (pool_id == WORK_OFFQ_POOL_NONE)
+               return NULL;
+
+       pool = worker_pool_by_id(pool_id);
+       if (!pool)
+               return NULL;
+
+       spin_lock(&pool->lock);
+       exec = find_worker_executing_work(pool, work);
+       if (exec) {
+               BUG_ON(pool != exec->pool);
+               *worker = exec;
+               return pool;
+       }
+       spin_unlock(&pool->lock);
+
+       return NULL;
+}
+
 /**
  * move_linked_works - move linked works to a list
  * @work: start of series of works to be scheduled
@@ -1235,35 +1272,24 @@ static void __queue_work(unsigned int cpu, struct 
workqueue_struct *wq,
 
        /* determine pool to use */
        if (!(wq->flags & WQ_UNBOUND)) {
-               struct worker_pool *last_pool;
+               struct worker *worker;
 
                if (cpu == WORK_CPU_UNBOUND)
                        cpu = raw_smp_processor_id();
 
                /*
-                * It's multi cpu.  If @work was previously on a different
-                * cpu, it might still be running there, in which case the
-                * work needs to be queued on that cpu to guarantee
-                * non-reentrancy.
+                * It's multi cpu and pool. If @work is still running on a pool,
+                * in which case the work needs to be queued on that pool
+                * to guarantee non-reentrancy.
                 */
-               pool = get_cwq(cpu, wq)->pool;
-               last_pool = get_work_pool(work);
-
-               if (last_pool && last_pool != pool) {
-                       struct worker *worker;
-
-                       spin_lock(&last_pool->lock);
-
-                       worker = find_worker_executing_work(last_pool, work);
-
-                       if (worker && worker->current_cwq->wq == wq)
-                               pool = last_pool;
-                       else {
-                               /* meh... not running there, queue here */
-                               spin_unlock(&last_pool->lock);
-                               spin_lock(&pool->lock);
-                       }
-               } else {
+               BUG_ON(get_work_cwq(work));
+               pool = lock_pool_executing_work(work, &worker);
+               if (pool && worker->current_cwq->wq != wq) {
+                       spin_unlock(&pool->lock);
+                       pool = NULL;
+               }
+               if (!pool) {
+                       pool = get_cwq(cpu, wq)->pool;
                        spin_lock(&pool->lock);
                }
        } else {
-- 
1.7.7.6

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to