diff --git a/Documentation/core-api/workqueue.rst b/Documentation/core-api/workqueue.rst index 411e1b28b8de..bb770f556568 100644 --- a/Documentation/core-api/workqueue.rst +++ b/Documentation/core-api/workqueue.rst @@ -356,7 +356,7 @@ Guidelines well under the default limit. * A wq serves as a domain for forward progress guarantee - (``WQ_MEM_RECLAIM``, flush and work item attributes. Work items + (``WQ_MEM_RECLAIM``), flush and work item attributes. Work items which are not involved in memory reclaim and don't need to be flushed as a part of a group of work items, and don't require any special attribute, can use one of the system wq. There is no diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 78068ae8f28a..bfeef512f6dd 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -226,6 +226,8 @@ struct worker_pool { /* L: hash of busy workers */ struct worker *manager; /* L: purely informational */ + /* L: last worker woken by kick_pool() */ + struct worker *last_woken_worker; struct list_head workers; /* A: attached workers */ struct ida worker_ida; /* worker IDs for task name */ @@ -369,7 +371,7 @@ struct workqueue_struct { int saved_max_active; /* WQ: saved max_active */ int saved_min_active; /* WQ: saved min_active */ - struct workqueue_attrs *unbound_attrs; /* PW: only for unbound wqs */ + struct workqueue_attrs *attrs; /* PW: workqueue attributes */ struct pool_workqueue __rcu *dfl_pwq; /* PW: only for unbound wqs */ #ifdef CONFIG_SYSFS @@ -757,7 +759,7 @@ static struct pool_workqueue *unbound_pwq(struct workqueue_struct *wq, int cpu) * unbound_effective_cpumask - effective cpumask of an unbound workqueue * @wq: workqueue of interest * - * @wq->unbound_attrs->cpumask contains the cpumask requested by the user which + * @wq->attrs->cpumask contains the cpumask requested by the user which * is masked with wq_unbound_cpumask to determine the effective cpumask. The * default pwq is always mapped to the pool with the current effective cpumask. */ @@ -1258,19 +1260,27 @@ static void kick_bh_pool(struct worker_pool *pool) } /** - * kick_pool - wake up an idle worker if necessary + * kick_pool_pick - select an idle worker to kick, deferring the wakeup * @pool: pool to kick + * @wakep: out-param, set to the task to wake after pool->lock is dropped * - * @pool may have pending work items. Wake up worker if necessary. Returns - * whether a worker was woken up. + * Like kick_pool() but, for a regular (non-BH) pool, returns the picked + * worker's task via @wakep instead of waking it, so the caller can issue the + * wakeup after dropping pool->lock (the wakeup takes rq->lock). Worker + * selection, wake_cpu setup and the BH kick still happen under the lock. + * Returns whether a worker was selected or kicked. + * + * Must be called with @pool->lock held. */ -static bool kick_pool(struct worker_pool *pool) +static bool kick_pool_pick(struct worker_pool *pool, struct task_struct **wakep) { struct worker *worker = first_idle_worker(pool); struct task_struct *p; lockdep_assert_held(&pool->lock); + *wakep = NULL; + if (!need_more_worker(pool) || !worker) return false; @@ -1299,21 +1309,42 @@ static bool kick_pool(struct worker_pool *pool) * its affinity scope. Repatriate. */ if (!pool->attrs->affn_strict && - !cpumask_test_cpu(p->wake_cpu, pool->attrs->__pod_cpumask)) { + !cpumask_test_cpu(READ_ONCE(p->wake_cpu), + pool->attrs->__pod_cpumask)) { struct work_struct *work = list_first_entry(&pool->worklist, struct work_struct, entry); int wake_cpu = cpumask_any_and_distribute(pool->attrs->__pod_cpumask, cpu_online_mask); if (wake_cpu < nr_cpu_ids) { - p->wake_cpu = wake_cpu; + WRITE_ONCE(p->wake_cpu, wake_cpu); get_work_pwq(work)->stats[PWQ_STAT_REPATRIATED]++; } } #endif - wake_up_process(p); + /* Track the last idle worker woken, used for stall diagnostics. */ + pool->last_woken_worker = worker; + + *wakep = p; return true; } +/** + * kick_pool - wake up an idle worker if necessary + * @pool: pool to kick + * + * @pool may have pending work items. Wake up worker if necessary. Returns + * whether a worker was woken up. + */ +static bool kick_pool(struct worker_pool *pool) +{ + struct task_struct *p; + bool kicked = kick_pool_pick(pool, &p); + + if (p) + wake_up_process(p); + return kicked; +} + #ifdef CONFIG_WQ_CPU_INTENSIVE_REPORT /* @@ -1438,7 +1469,7 @@ void wq_worker_running(struct task_struct *task) * CPU intensive auto-detection cares about how long a work item hogged * CPU without sleeping. Reset the starting timestamp on wakeup. */ - worker->current_at = worker->task->se.sum_exec_runtime; + worker->current_at = READ_ONCE(worker->task->se.sum_exec_runtime); WRITE_ONCE(worker->sleeping, 0); } @@ -1505,7 +1536,11 @@ void wq_worker_tick(struct task_struct *task) if (!pwq) return; - pwq->stats[PWQ_STAT_CPU_TIME] += TICK_USEC; + /* + * @pwq is shared across CPUs for unbound wqs and this advisory stat is + * bumped outside pool->lock, so the update is intentionally racy. + */ + data_race(pwq->stats[PWQ_STAT_CPU_TIME] += TICK_USEC); if (!wq_cpu_intensive_thresh_us) return; @@ -1523,7 +1558,7 @@ void wq_worker_tick(struct task_struct *task) * We probably want to make this prettier in the future. */ if ((worker->flags & WORKER_NOT_RUNNING) || READ_ONCE(worker->sleeping) || - worker->task->se.sum_exec_runtime - worker->current_at < + READ_ONCE(worker->task->se.sum_exec_runtime) - worker->current_at < wq_cpu_intensive_thresh_us * NSEC_PER_USEC) return; @@ -1570,14 +1605,19 @@ work_func_t wq_worker_last_func(struct task_struct *task) return worker->last_func; } +/* True if @pool is a static per-cpu pool rather than an unbound one. */ +static bool is_percpu_pool(struct worker_pool *pool) +{ + return pool->cpu >= 0; +} + /** * wq_node_nr_active - Determine wq_node_nr_active to use * @wq: workqueue of interest * @node: NUMA node, can be %NUMA_NO_NODE * - * Determine wq_node_nr_active to use for @wq on @node. Returns: - * - * - %NULL for per-cpu workqueues as they don't need to use shared nr_active. + * Determine wq_node_nr_active to use for @wq on @node. @wq must be unbound. + * Returns: * * - node_nr_active[nr_node_ids] if @node is %NUMA_NO_NODE. * @@ -1586,8 +1626,7 @@ work_func_t wq_worker_last_func(struct task_struct *task) static struct wq_node_nr_active *wq_node_nr_active(struct workqueue_struct *wq, int node) { - if (!(wq->flags & WQ_UNBOUND)) - return NULL; + BUG_ON(!(wq->flags & WQ_UNBOUND)); if (node == NUMA_NO_NODE) node = nr_node_ids; @@ -1742,13 +1781,16 @@ static bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill) { struct workqueue_struct *wq = pwq->wq; struct worker_pool *pool = pwq->pool; - struct wq_node_nr_active *nna = wq_node_nr_active(wq, pool->node); + struct wq_node_nr_active *nna; bool obtained = false; lockdep_assert_held(&pool->lock); - if (!nna) { - /* BH or per-cpu workqueue, pwq->nr_active is sufficient */ + /* + * A concurrency-managed per-cpu pool accounts nr_active per pwq, so + * pwq->nr_active against wq->max_active is sufficient. + */ + if (is_percpu_pool(pool)) { obtained = pwq->nr_active < READ_ONCE(wq->max_active); goto out; } @@ -1756,6 +1798,8 @@ static bool pwq_tryinc_nr_active(struct pool_workqueue *pwq, bool fill) if (unlikely(pwq->plugged)) return false; + nna = wq_node_nr_active(wq, pool->node); + /* * Unbound workqueue uses per-node shared nr_active $nna. If @pwq is * already waiting on $nna, pwq_dec_nr_active() will maintain the @@ -1973,7 +2017,7 @@ static void node_activate_pending_pwq(struct wq_node_nr_active *nna, static void pwq_dec_nr_active(struct pool_workqueue *pwq) { struct worker_pool *pool = pwq->pool; - struct wq_node_nr_active *nna = wq_node_nr_active(pwq->wq, pool->node); + struct wq_node_nr_active *nna; lockdep_assert_held(&pool->lock); @@ -1984,14 +2028,16 @@ static void pwq_dec_nr_active(struct pool_workqueue *pwq) pwq->nr_active--; /* - * For a percpu workqueue, it's simple. Just need to kick the first + * A concurrency-managed per-cpu pool only needs to kick the first * inactive work item on @pwq itself. */ - if (!nna) { + if (is_percpu_pool(pool)) { pwq_activate_first_inactive(pwq, false); return; } + nna = wq_node_nr_active(pwq->wq, pool->node); + /* * If @pwq is for an unbound workqueue, it's more complicated because * multiple pwqs and pools may be sharing the nr_active count. When a @@ -2277,6 +2323,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, { struct pool_workqueue *pwq; struct worker_pool *last_pool, *pool; + struct task_struct *wake_task = NULL; unsigned int work_flags; unsigned int req_cpu = cpu; @@ -2399,7 +2446,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, trace_workqueue_activate_work(work); insert_work(pwq, work, &pool->worklist, work_flags); - kick_pool(pool); + kick_pool_pick(pool, &wake_task); } else { work_flags |= WORK_STRUCT_INACTIVE; insert_work(pwq, work, &pwq->inactive_works, work_flags); @@ -2407,6 +2454,8 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, out: raw_spin_unlock(&pool->lock); + if (wake_task) + wake_up_process(wake_task); rcu_read_unlock(); } @@ -2716,7 +2765,7 @@ static struct worker *alloc_worker(int node) static cpumask_t *pool_allowed_cpus(struct worker_pool *pool) { - if (pool->cpu < 0 && pool->attrs->affn_strict) + if (!is_percpu_pool(pool) && pool->attrs->affn_strict) return pool->attrs->__pod_cpumask; else return pool->attrs->cpumask; @@ -2948,6 +2997,13 @@ static void set_worker_dying(struct worker *worker, struct list_head *list) pool->nr_workers--; pool->nr_idle--; + /* + * Clear last_woken_worker if it points to this worker, so that + * show_cpu_pool_busy_workers() cannot dereference a freed worker. + */ + if (pool->last_woken_worker == worker) + pool->last_woken_worker = NULL; + worker->flags |= WORKER_DIE; list_move(&worker->entry, list); @@ -3223,6 +3279,7 @@ __acquires(&pool->lock) { struct pool_workqueue *pwq = get_work_pwq(work); struct worker_pool *pool = worker->pool; + struct task_struct *wake_task = NULL; unsigned long work_data; int lockdep_start_depth, rcu_start_depth; bool bh_draining = pool->flags & POOL_BH_DRAINING; @@ -3249,7 +3306,7 @@ __acquires(&pool->lock) worker->current_func = work->func; worker->current_pwq = pwq; if (worker->task) - worker->current_at = worker->task->se.sum_exec_runtime; + worker->current_at = READ_ONCE(worker->task->se.sum_exec_runtime); worker->current_start = jiffies; work_data = *work_data_bits(work); worker->current_color = get_work_color(work_data); @@ -3276,8 +3333,11 @@ __acquires(&pool->lock) * since nr_running would always be >= 1 at this point. This is used to * chain execution of the pending work items for WORKER_NOT_RUNNING * workers such as the UNBOUND and CPU_INTENSIVE ones. + * + * Select the worker under pool->lock; the wakeup is deferred until + * after the lock is dropped, guarded by the rcu_read_lock() below. */ - kick_pool(pool); + kick_pool_pick(pool, &wake_task); /* * Record the last pool and clear PENDING which should be the last @@ -3288,7 +3348,12 @@ __acquires(&pool->lock) set_work_pool_and_clear_pending(work, pool->id, pool_offq_flags(pool)); pwq->stats[PWQ_STAT_STARTED]++; + + rcu_read_lock(); raw_spin_unlock_irq(&pool->lock); + if (wake_task) + wake_up_process(wake_task); + rcu_read_unlock(); rcu_start_depth = rcu_preempt_depth(); lockdep_start_depth = lockdep_depth(current); @@ -5033,7 +5098,7 @@ static void rcu_free_wq(struct rcu_head *rcu) wq_free_lockdep(wq); free_percpu(wq->cpu_pwq); - free_workqueue_attrs(wq->unbound_attrs); + free_workqueue_attrs(wq->attrs); kfree(wq); } @@ -5068,7 +5133,7 @@ static void put_unbound_pool(struct worker_pool *pool) return; /* sanity checks */ - if (WARN_ON(!(pool->cpu < 0)) || + if (WARN_ON(is_percpu_pool(pool)) || WARN_ON(!list_empty(&pool->worklist))) return; @@ -5220,7 +5285,7 @@ static void pwq_release_workfn(struct kthread_work *work) mutex_unlock(&wq->mutex); } - if (wq->flags & WQ_UNBOUND) { + if (!is_percpu_pool(pool)) { mutex_lock(&wq_pool_mutex); put_unbound_pool(pool); mutex_unlock(&wq_pool_mutex); @@ -5297,8 +5362,22 @@ static void link_pwq(struct pool_workqueue *pwq) list_add_tail_rcu(&pwq->pwqs_node, &wq->pwqs); } +/* Return the static per-cpu worker_pool that backs @wq on @cpu. */ +static struct worker_pool *get_percpu_pool(struct workqueue_struct *wq, int cpu) +{ + struct worker_pool __percpu *pools; + bool highpri = wq->flags & WQ_HIGHPRI; + + if (wq->flags & WQ_BH) + pools = bh_worker_pools; + else + pools = cpu_worker_pools; + + return &per_cpu_ptr(pools, cpu)[highpri]; +} + /* obtain a pool matching @attr and create a pwq associating the pool and @wq */ -static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq, +static struct pool_workqueue *alloc_pwq(struct workqueue_struct *wq, const struct workqueue_attrs *attrs) { struct worker_pool *pool; @@ -5421,7 +5500,7 @@ apply_wqattrs_prepare(struct workqueue_struct *wq, copy_workqueue_attrs(new_attrs, attrs); wqattrs_actualize_cpumask(new_attrs, unbound_cpumask); cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask); - ctx->dfl_pwq = alloc_unbound_pwq(wq, new_attrs); + ctx->dfl_pwq = alloc_pwq(wq, new_attrs); if (!ctx->dfl_pwq) goto out_free; @@ -5431,7 +5510,7 @@ apply_wqattrs_prepare(struct workqueue_struct *wq, ctx->pwq_tbl[cpu] = ctx->dfl_pwq; } else { wq_calc_pod_cpumask(new_attrs, cpu); - ctx->pwq_tbl[cpu] = alloc_unbound_pwq(wq, new_attrs); + ctx->pwq_tbl[cpu] = alloc_pwq(wq, new_attrs); if (!ctx->pwq_tbl[cpu]) goto out_free; } @@ -5469,7 +5548,7 @@ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx) /* all pwqs have been created successfully, let's install'em */ mutex_lock(&ctx->wq->mutex); - copy_workqueue_attrs(ctx->wq->unbound_attrs, ctx->attrs); + copy_workqueue_attrs(ctx->wq->attrs, ctx->attrs); /* save the previous pwqs and install the new ones */ for_each_possible_cpu(cpu) @@ -5477,8 +5556,9 @@ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx) ctx->pwq_tbl[cpu]); ctx->dfl_pwq = install_unbound_pwq(ctx->wq, -1, ctx->dfl_pwq); - /* update node_nr_active->max */ - wq_update_node_max_active(ctx->wq, -1); + /* update node_nr_active->max, which only unbound workqueues have */ + if (ctx->wq->flags & WQ_UNBOUND) + wq_update_node_max_active(ctx->wq, -1); mutex_unlock(&ctx->wq->mutex); } @@ -5556,7 +5636,7 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu) lockdep_assert_held(&wq_pool_mutex); - if (!(wq->flags & WQ_UNBOUND) || wq->unbound_attrs->ordered) + if (!(wq->flags & WQ_UNBOUND) || wq->attrs->ordered) return; /* @@ -5566,7 +5646,7 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu) */ target_attrs = unbound_wq_update_pwq_attrs_buf; - copy_workqueue_attrs(target_attrs, wq->unbound_attrs); + copy_workqueue_attrs(target_attrs, wq->attrs); wqattrs_actualize_cpumask(target_attrs, wq_unbound_cpumask); /* nothing to do if the target cpumask matches the current pwq */ @@ -5575,7 +5655,7 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu) return; /* create a new pwq */ - pwq = alloc_unbound_pwq(wq, target_attrs); + pwq = alloc_pwq(wq, target_attrs); if (!pwq) { pr_warn("workqueue: allocation failed while updating CPU pod affinity of \"%s\"\n", wq->name); @@ -5599,6 +5679,30 @@ static void unbound_wq_update_pwq(struct workqueue_struct *wq, int cpu) put_pwq_unlocked(old_pwq); } +static int alloc_and_link_percpu_pwqs(struct workqueue_struct *wq) +{ + struct pool_workqueue *pwq; + int cpu; + + for_each_possible_cpu(cpu) { + struct worker_pool *pool = get_percpu_pool(wq, cpu); + + pwq = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, pool->node); + if (!pwq) + return -ENOMEM; + + init_pwq(pwq, wq, pool); + + mutex_lock(&wq->mutex); + link_pwq(pwq); + mutex_unlock(&wq->mutex); + + rcu_assign_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu), pwq); + } + + return 0; +} + static int alloc_and_link_pwqs(struct workqueue_struct *wq) { bool highpri = wq->flags & WQ_HIGHPRI; @@ -5606,40 +5710,13 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) lockdep_assert_held(&wq_pool_mutex); - wq->cpu_pwq = alloc_percpu(struct pool_workqueue *); + wq->cpu_pwq = alloc_percpu(struct pool_workqueue __rcu *); if (!wq->cpu_pwq) goto enomem; if (!(wq->flags & WQ_UNBOUND)) { - struct worker_pool __percpu *pools; - - if (wq->flags & WQ_BH) - pools = bh_worker_pools; - else - pools = cpu_worker_pools; - - for_each_possible_cpu(cpu) { - struct pool_workqueue **pwq_p; - struct worker_pool *pool; - - pool = &(per_cpu_ptr(pools, cpu)[highpri]); - pwq_p = per_cpu_ptr(wq->cpu_pwq, cpu); - - *pwq_p = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, - pool->node); - if (!*pwq_p) - goto enomem; - - init_pwq(*pwq_p, wq, pool); - - mutex_lock(&wq->mutex); - link_pwq(*pwq_p); - mutex_unlock(&wq->mutex); - } - return 0; - } - - if (wq->flags & __WQ_ORDERED) { + ret = alloc_and_link_percpu_pwqs(wq); + } else if (wq->flags & __WQ_ORDERED) { struct pool_workqueue *dfl_pwq; ret = apply_workqueue_attrs_locked(wq, ordered_wq_attrs[highpri]); @@ -5659,8 +5736,11 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) enomem: if (wq->cpu_pwq) { for_each_possible_cpu(cpu) { - struct pool_workqueue *pwq = *per_cpu_ptr(wq->cpu_pwq, cpu); + struct pool_workqueue __rcu **slot; + struct pool_workqueue *pwq; + slot = per_cpu_ptr(wq->cpu_pwq, cpu); + pwq = rcu_access_pointer(*slot); if (pwq) { /* * Unlink pwq from wq->pwqs since link_pwq() @@ -5828,11 +5908,9 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, if (!wq) return NULL; - if (flags & WQ_UNBOUND) { - wq->unbound_attrs = alloc_workqueue_attrs_noprof(); - if (!wq->unbound_attrs) - goto err_free_wq; - } + wq->attrs = alloc_workqueue_attrs_noprof(); + if (!wq->attrs) + goto err_free_wq; name_len = vsnprintf(wq->name, sizeof(wq->name), fmt, args); @@ -5925,7 +6003,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, free_node_nr_active(wq->node_nr_active); } err_free_wq: - free_workqueue_attrs(wq->unbound_attrs); + free_workqueue_attrs(wq->attrs); kfree(wq); return NULL; err_unlock_destroy: @@ -6244,7 +6322,7 @@ bool workqueue_congested(int cpu, struct workqueue_struct *wq) if (cpu == WORK_CPU_UNBOUND) cpu = smp_processor_id(); - pwq = *per_cpu_ptr(wq->cpu_pwq, cpu); + pwq = rcu_dereference_sched(*per_cpu_ptr(wq->cpu_pwq, cpu)); ret = !list_empty(&pwq->inactive_works); preempt_enable(); @@ -6869,9 +6947,9 @@ int workqueue_online_cpu(unsigned int cpu) /* update pod affinity of unbound workqueues */ list_for_each_entry(wq, &workqueues, list) { - struct workqueue_attrs *attrs = wq->unbound_attrs; + struct workqueue_attrs *attrs = wq->attrs; - if (attrs) { + if (wq->flags & WQ_UNBOUND) { const struct wq_pod_type *pt = wqattrs_pod_type(attrs); int tcpu; @@ -6904,9 +6982,9 @@ int workqueue_offline_cpu(unsigned int cpu) cpumask_clear_cpu(cpu, wq_online_cpumask); list_for_each_entry(wq, &workqueues, list) { - struct workqueue_attrs *attrs = wq->unbound_attrs; + struct workqueue_attrs *attrs = wq->attrs; - if (attrs) { + if (wq->flags & WQ_UNBOUND) { const struct wq_pod_type *pt = wqattrs_pod_type(attrs); int tcpu; @@ -7084,7 +7162,7 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask) if (!(wq->flags & WQ_UNBOUND) || (wq->flags & __WQ_DESTROYING)) continue; - ctx = apply_wqattrs_prepare(wq, wq->unbound_attrs, unbound_cpumask); + ctx = apply_wqattrs_prepare(wq, wq->attrs, unbound_cpumask); if (IS_ERR(ctx)) { ret = PTR_ERR(ctx); break; @@ -7302,7 +7380,7 @@ static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr, int written; mutex_lock(&wq->mutex); - written = scnprintf(buf, PAGE_SIZE, "%d\n", wq->unbound_attrs->nice); + written = scnprintf(buf, PAGE_SIZE, "%d\n", wq->attrs->nice); mutex_unlock(&wq->mutex); return written; @@ -7319,7 +7397,7 @@ static struct workqueue_attrs *wq_sysfs_prep_attrs(struct workqueue_struct *wq) if (!attrs) return NULL; - copy_workqueue_attrs(attrs, wq->unbound_attrs); + copy_workqueue_attrs(attrs, wq->attrs); return attrs; } @@ -7356,7 +7434,7 @@ static ssize_t wq_cpumask_show(struct device *dev, mutex_lock(&wq->mutex); written = scnprintf(buf, PAGE_SIZE, "%*pb\n", - cpumask_pr_args(wq->unbound_attrs->cpumask)); + cpumask_pr_args(wq->attrs->cpumask)); mutex_unlock(&wq->mutex); return written; } @@ -7392,13 +7470,13 @@ static ssize_t wq_affn_scope_show(struct device *dev, int written; mutex_lock(&wq->mutex); - if (wq->unbound_attrs->affn_scope == WQ_AFFN_DFL) + if (wq->attrs->affn_scope == WQ_AFFN_DFL) written = scnprintf(buf, PAGE_SIZE, "%s (%s)\n", wq_affn_names[WQ_AFFN_DFL], wq_affn_names[wq_affn_dfl]); else written = scnprintf(buf, PAGE_SIZE, "%s\n", - wq_affn_names[wq->unbound_attrs->affn_scope]); + wq_affn_names[wq->attrs->affn_scope]); mutex_unlock(&wq->mutex); return written; @@ -7433,7 +7511,7 @@ static ssize_t wq_affinity_strict_show(struct device *dev, struct workqueue_struct *wq = dev_to_wq(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", - wq->unbound_attrs->affn_strict); + wq->attrs->affn_strict); } static ssize_t wq_affinity_strict_store(struct device *dev, @@ -7606,7 +7684,7 @@ int workqueue_sysfs_register(struct workqueue_struct *wq) dev_set_name(&wq_dev->dev, "%s", wq->name); /* - * unbound_attrs are created separately. Suppress uevent until + * attrs are created separately. Suppress uevent until * everything is ready. */ dev_set_uevent_suppress(&wq_dev->dev, true); @@ -7689,20 +7767,58 @@ module_param_named(panic_on_stall_time, wq_panic_on_stall_time, uint, 0644); MODULE_PARM_DESC(panic_on_stall_time, "Panic if stall exceeds this many seconds (0=disabled)"); /* - * Show workers that might prevent the processing of pending work items. - * A busy worker that is not running on the CPU (e.g. sleeping in - * wait_event_idle() with PF_WQ_WORKER cleared) can stall the pool just as - * effectively as a CPU-bound one, so dump every in-flight worker. + * Report that a pool has no worker in running state, which is a sign that the + * pool may be stuck. Print pool info. Must be called with pool->lock held and + * inside a printk_deferred_enter/exit region. + */ +static void show_pool_no_running_worker(struct worker_pool *pool) +{ + lockdep_assert_held(&pool->lock); + + printk_deferred_enter(); + pr_info("pool %d: no worker in running state, cpu=%d is %s (nr_workers=%d nr_idle=%d)\n", + pool->id, pool->cpu, + idle_cpu(pool->cpu) ? "idle" : "busy", + pool->nr_workers, pool->nr_idle); + pr_info("The pool might have trouble waking an idle worker.\n"); + /* + * last_woken_worker and its task are valid here: set_worker_dying() + * clears it under pool->lock before setting WORKER_DIE, so if + * last_woken_worker is non-NULL the kthread has not yet exited and + * worker->task is still alive. + */ + if (pool->last_woken_worker) { + pr_info("Backtrace of last woken worker:\n"); + sched_show_task(pool->last_woken_worker->task); + } else { + pr_info("Last woken worker empty\n"); + } + printk_deferred_exit(); +} + +/* + * Show running workers that might prevent the processing of pending work items. + * If no running worker is found, the pool may be stuck waiting for an idle + * worker to be woken, so report the pool state and the last woken worker. */ static void show_cpu_pool_busy_workers(struct worker_pool *pool) { + bool found_running = false; struct worker *worker; unsigned long irq_flags; - int bkt; + int cpu, bkt; raw_spin_lock_irqsave(&pool->lock, irq_flags); + /* Snapshot cpu inside the lock to safely use it after unlock. */ + cpu = pool->cpu; + hash_for_each(pool->busy_hash, bkt, worker, hentry) { + /* Skip workers that are not actively running on the CPU. */ + if (!task_is_running(worker->task)) + continue; + + found_running = true; /* * Defer printing to avoid deadlocks in console * drivers that queue work while holding locks @@ -7716,7 +7832,24 @@ static void show_cpu_pool_busy_workers(struct worker_pool *pool) printk_deferred_exit(); } + /* + * If no running worker was found, the pool is likely stuck. Print pool + * state and the backtrace of the last woken worker, which is the prime + * suspect for the stall. + */ + if (!found_running) + show_pool_no_running_worker(pool); + raw_spin_unlock_irqrestore(&pool->lock, irq_flags); + + /* + * Trigger a backtrace on the stalled CPU to capture what it is + * currently executing. Skip an offline CPU, whose NMI is never acked + * and would make the backtrace busy-wait until it times out. Done + * after releasing the lock to avoid issues with NMI delivery. + */ + if (!found_running && cpu_online(cpu)) + trigger_single_cpu_backtrace(cpu); } static void show_cpu_pools_busy_workers(void) @@ -7832,7 +7965,7 @@ static void wq_watchdog_timer_fn(struct timer_list *unused) lockup_detected = true; stall_time = jiffies_to_msecs(now - pool_ts) / 1000; max_stall_time = max(max_stall_time, stall_time); - if (pool->cpu >= 0 && !(pool->flags & POOL_BH)) { + if (is_percpu_pool(pool) && !(pool->flags & POOL_BH)) { pool->cpu_stall = true; cpu_pool_stall = true; } diff --git a/tools/workqueue/wq_dump.py b/tools/workqueue/wq_dump.py index ce4161f52f2f..31afc24ef17b 100644 --- a/tools/workqueue/wq_dump.py +++ b/tools/workqueue/wq_dump.py @@ -46,6 +46,11 @@ each workqueue: import sys +import argparse +parser = argparse.ArgumentParser(description=desc, + formatter_class=argparse.RawTextHelpFormatter) +args = parser.parse_args() + import drgn from drgn.helpers.linux.list import list_for_each_entry,list_empty from drgn.helpers.linux.percpu import per_cpu_ptr @@ -53,11 +58,6 @@ from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu from drgn.helpers.linux.nodemask import for_each_node from drgn.helpers.linux.idr import idr_for_each -import argparse -parser = argparse.ArgumentParser(description=desc, - formatter_class=argparse.RawTextHelpFormatter) -args = parser.parse_args() - def err(s): print(s, file=sys.stderr, flush=True) sys.exit(1) @@ -85,7 +85,7 @@ def wq_type_str(wq): if wq.flags & WQ_ORDERED: return f'{"ordered":{wq_type_len}}' else: - if wq.unbound_attrs.affn_strict: + if wq.attrs.affn_strict: return f'{"unbound,S":{wq_type_len}}' else: return f'{"unbound":{wq_type_len}}' @@ -205,8 +205,8 @@ for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_( continue print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='') - if wq.unbound_attrs.value_() != 0: - print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='') + if wq.flags & WQ_UNBOUND: + print(f' {cpumask_str(wq.attrs.cpumask):{ucpus_len}}', end='') else: print(f' {"":{ucpus_len}}', end='') diff --git a/tools/workqueue/wq_monitor.py b/tools/workqueue/wq_monitor.py index 9e964c5be40c..7f47fa398e3c 100644 --- a/tools/workqueue/wq_monitor.py +++ b/tools/workqueue/wq_monitor.py @@ -37,9 +37,6 @@ import re import time import json -import drgn -from drgn.helpers.linux.list import list_for_each_entry - import argparse parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawTextHelpFormatter) @@ -51,6 +48,9 @@ parser.add_argument('-j', '--json', action='store_true', help='Output in json') args = parser.parse_args() +import drgn +from drgn.helpers.linux.list import list_for_each_entry + workqueues = prog['workqueues'] WQ_UNBOUND = prog['WQ_UNBOUND']