Pan
Pan

Reputation: 155

Why linux workqueue __queue_work doesn't guard read on wq->flags?

I was learning the workqueue mechanism of linux. I meet this codes: When user want to queue a work to a workqueue, they call __queue_work, at the beginning of this function, it first check if the workqueue is at destroying or draining state by reading a flag variable. But it doesn't use mutex_lock to guard the read.

// linux-src-code/kernel/workqueue.c
static void __queue_work(int cpu, struct workqueue_struct *wq,
             struct work_struct *work)
{
    struct pool_workqueue *pwq;
    struct worker_pool *last_pool, *pool;
    unsigned int work_flags;
    unsigned int req_cpu = cpu;
    lockdep_assert_irqs_disabled();
    if (unlikely(wq->flags & (__WQ_DESTROYING | __WQ_DRAINING) &&
             WARN_ON_ONCE(!is_chained_work(wq))))
        return;
...
}

But in the drain_workqueue and destroy_workqueue, they guard the flags variable with mutex lock, this confuse me. I think there could be a race between reading and writing to the flags:

// linux-src-code/kernel/workqueue.c
void drain_workqueue(struct workqueue_struct *wq)
{
    unsigned int flush_cnt = 0;
    struct pool_workqueue *pwq;
    mutex_lock(&wq->mutex);
    if (!wq->nr_drainers++)
        wq->flags |= __WQ_DRAINING;
    mutex_unlock(&wq->mutex);
reflush:
    __flush_workqueue(wq);
...
}

My question is: why the read access of wq->flags in queue_work function is not guarded by mutex but the write access in destroy_workqueue does.

Add this function for more information: this means the mutex is not only protecting the combination of flags and nr_drainers, but also applies to single flags variable.

void destroy_workqueue(struct workqueue_struct *wq)
{
    struct pool_workqueue *pwq;
    int cpu;
    workqueue_sysfs_unregister(wq);
    /* mark the workqueue destruction is in progress */
    mutex_lock(&wq->mutex);
    wq->flags |= __WQ_DESTROYING;
    mutex_unlock(&wq->mutex);
}

Upvotes: 1

Views: 66

Answers (0)

Related Questions