/**
* worker_set_flags - set worker flags and adjust nr_running accordingly
- * @worker: worker to set flags for
+ * @worker: self
* @flags: flags to set
* @wakeup: wakeup an idle worker if necessary
*
* nr_running becomes zero and @wakeup is %true, an idle worker is
* woken up.
*
- * LOCKING:
- * spin_lock_irq(gcwq->lock).
+ * CONTEXT:
+ * spin_lock_irq(gcwq->lock)
*/
static inline void worker_set_flags(struct worker *worker, unsigned int flags,
bool wakeup)
{
struct global_cwq *gcwq = worker->gcwq;
+ WARN_ON_ONCE(worker->task != current);
+
/*
* If transitioning into NOT_RUNNING, adjust nr_running and
* wake up an idle worker as necessary if requested by
/**
* worker_clr_flags - clear worker flags and adjust nr_running accordingly
- * @worker: worker to set flags for
+ * @worker: self
* @flags: flags to clear
*
* Clear @flags in @worker->flags and adjust nr_running accordingly.
*
- * LOCKING:
- * spin_lock_irq(gcwq->lock).
+ * CONTEXT:
+ * spin_lock_irq(gcwq->lock)
*/
static inline void worker_clr_flags(struct worker *worker, unsigned int flags)
{
struct global_cwq *gcwq = worker->gcwq;
unsigned int oflags = worker->flags;
+ WARN_ON_ONCE(worker->task != current);
+
worker->flags &= ~flags;
/* if transitioning out of NOT_RUNNING, increment nr_running */
BUG_ON(!list_empty(&worker->entry) &&
(worker->hentry.next || worker->hentry.pprev));
- worker_set_flags(worker, WORKER_IDLE, false);
+ /* can't use worker_set_flags(), also called from start_worker() */
+ worker->flags |= WORKER_IDLE;
gcwq->nr_idle++;
worker->last_active = jiffies;
jiffies + IDLE_WORKER_TIMEOUT);
} else
wake_up_all(&gcwq->trustee_wait);
+
+ /* sanity check nr_running */
+ WARN_ON_ONCE(gcwq->nr_workers == gcwq->nr_idle &&
+ atomic_read(get_gcwq_nr_running(gcwq->cpu)));
}
/**
*/
static void start_worker(struct worker *worker)
{
- worker_set_flags(worker, WORKER_STARTED, false);
+ worker->flags |= WORKER_STARTED;
worker->gcwq->nr_workers++;
worker_enter_idle(worker);
wake_up_process(worker->task);
gcwq->nr_idle--;
list_del_init(&worker->entry);
- worker_set_flags(worker, WORKER_DIE, false);
+ worker->flags |= WORKER_DIE;
spin_unlock_irq(&gcwq->lock);
gcwq->flags |= GCWQ_MANAGING_WORKERS;
list_for_each_entry(worker, &gcwq->idle_list, entry)
- worker_set_flags(worker, WORKER_ROGUE, false);
+ worker->flags |= WORKER_ROGUE;
for_each_busy_worker(worker, i, pos, gcwq)
- worker_set_flags(worker, WORKER_ROGUE, false);
+ worker->flags |= WORKER_ROGUE;
/*
* Call schedule() so that we cross rq->lock and thus can
spin_lock_irq(&gcwq->lock);
/*
- * Sched callbacks are disabled now. gcwq->nr_running should
- * be zero and will stay that way, making need_more_worker()
- * and keep_working() always return true as long as the
- * worklist is not empty.
+ * Sched callbacks are disabled now. Zap nr_running. After
+ * this, nr_running stays zero and need_more_worker() and
+ * keep_working() are always true as long as the worklist is
+ * not empty.
*/
- WARN_ON_ONCE(atomic_read(get_gcwq_nr_running(gcwq->cpu)) != 0);
+ atomic_set(get_gcwq_nr_running(gcwq->cpu), 0);
spin_unlock_irq(&gcwq->lock);
del_timer_sync(&gcwq->idle_timer);
worker = create_worker(gcwq, false);
spin_lock_irq(&gcwq->lock);
if (worker) {
- worker_set_flags(worker, WORKER_ROGUE, false);
+ worker->flags |= WORKER_ROGUE;
start_worker(worker);
}
}
* operations. Use a separate flag to mark that
* rebinding is scheduled.
*/
- worker_set_flags(worker, WORKER_REBIND, false);
- worker_clr_flags(worker, WORKER_ROGUE);
+ worker->flags |= WORKER_REBIND;
+ worker->flags &= ~WORKER_ROGUE;
/* queue rebind_work, wq doesn't matter, use the default one */
if (test_and_set_bit(WORK_STRUCT_PENDING_BIT,