diff options
| author | Mark Brown <broonie@kernel.org> | 2026-05-29 23:00:08 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-05-29 23:00:08 +0100 |
| commit | 8d353be232212e7e9a53c582d6cbc9570ad24ab8 (patch) | |
| tree | 78b5cc6f96374539496cca6ccf990a383e4240c1 | |
| parent | f624c17e475e17a1dbeef56ef624378f4e5a80b7 (diff) | |
| parent | be7e2b52b04031b7f311a9e27e522a8702fe0b2f (diff) | |
| download | linux-next-history-8d353be232212e7e9a53c582d6cbc9570ad24ab8.tar.gz | |
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git
| -rw-r--r-- | drivers/media/pci/ddbridge/ddbridge-core.c | 2 | ||||
| -rw-r--r-- | drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c | 8 | ||||
| -rw-r--r-- | drivers/platform/cznic/turris-omnia-mcu-gpio.c | 2 | ||||
| -rw-r--r-- | drivers/rapidio/rio.c | 2 | ||||
| -rw-r--r-- | drivers/virt/acrn/irqfd.c | 2 | ||||
| -rw-r--r-- | include/linux/workqueue.h | 1 | ||||
| -rw-r--r-- | kernel/umh.c | 2 | ||||
| -rw-r--r-- | kernel/workqueue.c | 71 | ||||
| -rw-r--r-- | lib/Kconfig.debug | 5 |
9 files changed, 58 insertions, 37 deletions
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 40e6c873c36d2..d240e291ba4fe 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -3430,7 +3430,7 @@ int ddb_init_ddbridge(void) if (ddb_class_create() < 0) return -1; - ddb_wq = alloc_workqueue("ddbridge", 0, 0); + ddb_wq = alloc_workqueue("ddbridge", WQ_PERCPU, 0); if (!ddb_wq) return ddb_exit_ddbridge(1, -1); diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c index 4c8957505a50d..25f8ca0d6d946 100644 --- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c +++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c @@ -1776,7 +1776,7 @@ static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev) FIFO_UNDERFLOW_INT_EN | HDMIRX_AXI_ERROR_INT_EN, 0); hdmirx_reset_dma(hdmirx_dev); - queue_delayed_work(system_unbound_wq, + queue_delayed_work(system_dfl_wq, &hdmirx_dev->delayed_work_res_change, msecs_to_jiffies(50)); } @@ -2238,7 +2238,7 @@ static void hdmirx_delayed_work_res_change(struct work_struct *work) if (hdmirx_wait_signal_lock(hdmirx_dev)) { hdmirx_plugout(hdmirx_dev); - queue_delayed_work(system_unbound_wq, + queue_delayed_work(system_dfl_wq, &hdmirx_dev->delayed_work_hotplug, msecs_to_jiffies(200)); } else { @@ -2253,7 +2253,7 @@ static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id) { struct snps_hdmirx_dev *hdmirx_dev = dev_id; - queue_delayed_work(system_unbound_wq, + queue_delayed_work(system_dfl_wq, &hdmirx_dev->delayed_work_hotplug, msecs_to_jiffies(10)); @@ -2518,7 +2518,7 @@ static void hdmirx_enable_irq(struct device *dev) enable_irq(hdmirx_dev->dma_irq); enable_irq(hdmirx_dev->det_irq); - queue_delayed_work(system_unbound_wq, + queue_delayed_work(system_dfl_wq, &hdmirx_dev->delayed_work_hotplug, msecs_to_jiffies(110)); } diff --git a/drivers/platform/cznic/turris-omnia-mcu-gpio.c b/drivers/platform/cznic/turris-omnia-mcu-gpio.c index 7f0ada4fa6062..4e430d6c3fc4b 100644 --- a/drivers/platform/cznic/turris-omnia-mcu-gpio.c +++ b/drivers/platform/cznic/turris-omnia-mcu-gpio.c @@ -893,7 +893,7 @@ static bool omnia_irq_read_pending_old(struct omnia_mcu *mcu, if (status & OMNIA_STS_BUTTON_PRESSED) { mcu->button_pressed_emul = true; - mod_delayed_work(system_wq, &mcu->button_release_emul_work, + mod_delayed_work(system_percpu_wq, &mcu->button_release_emul_work, msecs_to_jiffies(FRONT_BUTTON_RELEASE_DELAY_MS)); } else if (mcu->button_pressed_emul) { status |= OMNIA_STS_BUTTON_PRESSED; diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 0c175e6d424fd..f45e58b0971f5 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -1994,7 +1994,7 @@ int rio_init_mports(void) * TODO: Implement restart of discovery process for all or * individual discovering mports. */ - rio_wq = alloc_workqueue("riodisc", 0, 0); + rio_wq = alloc_workqueue("riodisc", WQ_PERCPU, 0); if (!rio_wq) { pr_err("RIO: unable allocate rio_wq\n"); goto no_disc; diff --git a/drivers/virt/acrn/irqfd.c b/drivers/virt/acrn/irqfd.c index acf8cd5f8f8c2..aab15f94166ae 100644 --- a/drivers/virt/acrn/irqfd.c +++ b/drivers/virt/acrn/irqfd.c @@ -206,7 +206,7 @@ int acrn_irqfd_init(struct acrn_vm *vm) { INIT_LIST_HEAD(&vm->irqfds); mutex_init(&vm->irqfds_lock); - vm->irqfd_wq = alloc_workqueue("acrn_irqfd-%u", 0, 0, vm->vmid); + vm->irqfd_wq = alloc_workqueue("acrn_irqfd-%u", WQ_PERCPU, 0, vm->vmid); if (!vm->irqfd_wq) return -ENOMEM; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 6177624539b3b..a283766a192aa 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -409,6 +409,7 @@ enum wq_flags { __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ __WQ_LEGACY = 1 << 18, /* internal: create*_workqueue() */ + __WQ_DEPRECATED = 1 << 19, /* internal: workqueue is deprecated */ /* BH wq only allows the following flags */ __WQ_BH_ALLOWS = WQ_BH | WQ_HIGHPRI | WQ_PERCPU, diff --git a/kernel/umh.c b/kernel/umh.c index cffda97d961cb..48117c569e1aa 100644 --- a/kernel/umh.c +++ b/kernel/umh.c @@ -430,7 +430,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done; sub_info->wait = wait; - queue_work(system_unbound_wq, &sub_info->work); + queue_work(system_dfl_wq, &sub_info->work); if (wait == UMH_NO_WAIT) /* task has freed sub_info */ goto unlock; diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 7382caebf2ee1..78f25afb4a9d6 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2281,6 +2281,14 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, unsigned int req_cpu = cpu; /* + * NOTE: Check whether the used workqueue is deprecated and warn + */ + if (unlikely(wq->flags & __WQ_DEPRECATED)) + pr_warn_once("workqueue: work func %ps enqueued on deprecated workqueue. " + "Use system_{percpu|dfl}_wq instead.\n", + work->func); + + /* * While a work item is PENDING && off queue, a task trying to * steal the PENDING will busy-loop waiting for it to either get * queued or lose PENDING. Grabbing PENDING and queueing should @@ -5316,16 +5324,6 @@ static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq, return pwq; } -static void apply_wqattrs_lock(void) -{ - mutex_lock(&wq_pool_mutex); -} - -static void apply_wqattrs_unlock(void) -{ - mutex_unlock(&wq_pool_mutex); -} - /** * wq_calc_pod_cpumask - calculate a wq_attrs' cpumask for a pod * @attrs: the wq_attrs of the default pwq of the target workqueue @@ -5822,7 +5820,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, /* see the comment above the definition of WQ_POWER_EFFICIENT */ if ((flags & WQ_POWER_EFFICIENT) && wq_power_efficient) - flags |= WQ_UNBOUND; + flags = (flags & ~WQ_PERCPU) | WQ_UNBOUND; /* allocate wq and format name */ if (flags & WQ_UNBOUND) @@ -5846,6 +5844,23 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, pr_warn_once("workqueue: name exceeds WQ_NAME_LEN. Truncating to: %s\n", wq->name); + /* + * One among WQ_PERCPU and WQ_UNBOUND must be set, but not both. + * - If neither is set, default to WQ_PERCPU + * - If both are set, default to WQ_UNBOUND + * + * This code can be removed after workqueue are unbound by default + */ + if (unlikely(!(flags & (WQ_UNBOUND | WQ_PERCPU)))) { + WARN_ONCE(1, "workqueue: %s is using neither WQ_PERCPU or WQ_UNBOUND. " + "Setting WQ_PERCPU.\n", wq->name); + flags |= WQ_PERCPU; + } else if (unlikely((flags & WQ_PERCPU) && (flags & WQ_UNBOUND))) { + WARN_ONCE(1, "workqueue: %s uses both WQ_PERCPU and WQ_UNBOUND. " + "Dropped WQ_PERCPU, keeping WQ_UNBOUND.\n", wq->name); + flags &= ~WQ_PERCPU; + } + if (flags & WQ_BH) { /* * BH workqueues always share a single execution context per CPU @@ -5881,7 +5896,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, * wq_pool_mutex protects the workqueues list, allocations of PWQs, * and the global freeze state. */ - apply_wqattrs_lock(); + mutex_lock(&wq_pool_mutex); if (alloc_and_link_pwqs(wq) < 0) goto err_unlock_free_node_nr_active; @@ -5895,7 +5910,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, if (wq_online && init_rescuer(wq) < 0) goto err_unlock_destroy; - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); if ((wq->flags & WQ_SYSFS) && workqueue_sysfs_register(wq)) goto err_destroy; @@ -5903,7 +5918,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, return wq; err_unlock_free_node_nr_active: - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); /* * Failed alloc_and_link_pwqs() may leave pending pwq->release_work, * flushing the pwq_release_worker ensures that the pwq_release_workfn() @@ -5918,7 +5933,7 @@ err_free_wq: kfree(wq); return NULL; err_unlock_destroy: - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); err_destroy: destroy_workqueue(wq); return NULL; @@ -6314,7 +6329,7 @@ EXPORT_SYMBOL_GPL(set_worker_desc); */ void print_worker_info(const char *log_lvl, struct task_struct *task) { - work_func_t *fn = NULL; + work_func_t fn = NULL; char name[WQ_NAME_LEN] = { }; char desc[WORKER_DESC_LEN] = { }; struct pool_workqueue *pwq = NULL; @@ -7319,7 +7334,7 @@ static ssize_t wq_nice_store(struct device *dev, struct device_attribute *attr, struct workqueue_attrs *attrs; int ret = -ENOMEM; - apply_wqattrs_lock(); + mutex_lock(&wq_pool_mutex); attrs = wq_sysfs_prep_attrs(wq); if (!attrs) @@ -7332,7 +7347,7 @@ static ssize_t wq_nice_store(struct device *dev, struct device_attribute *attr, ret = -EINVAL; out_unlock: - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); free_workqueue_attrs(attrs); return ret ?: count; } @@ -7358,7 +7373,7 @@ static ssize_t wq_cpumask_store(struct device *dev, struct workqueue_attrs *attrs; int ret = -ENOMEM; - apply_wqattrs_lock(); + mutex_lock(&wq_pool_mutex); attrs = wq_sysfs_prep_attrs(wq); if (!attrs) @@ -7369,7 +7384,7 @@ static ssize_t wq_cpumask_store(struct device *dev, ret = apply_workqueue_attrs_locked(wq, attrs); out_unlock: - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); free_workqueue_attrs(attrs); return ret ?: count; } @@ -7405,13 +7420,13 @@ static ssize_t wq_affn_scope_store(struct device *dev, if (affn < 0) return affn; - apply_wqattrs_lock(); + mutex_lock(&wq_pool_mutex); attrs = wq_sysfs_prep_attrs(wq); if (attrs) { attrs->affn_scope = affn; ret = apply_workqueue_attrs_locked(wq, attrs); } - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); free_workqueue_attrs(attrs); return ret ?: count; } @@ -7436,13 +7451,13 @@ static ssize_t wq_affinity_strict_store(struct device *dev, if (sscanf(buf, "%d", &v) != 1) return -EINVAL; - apply_wqattrs_lock(); + mutex_lock(&wq_pool_mutex); attrs = wq_sysfs_prep_attrs(wq); if (attrs) { attrs->affn_strict = (bool)v; ret = apply_workqueue_attrs_locked(wq, attrs); } - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); free_workqueue_attrs(attrs); return ret ?: count; } @@ -7483,12 +7498,12 @@ static int workqueue_set_unbound_cpumask(cpumask_var_t cpumask) cpumask_and(cpumask, cpumask, cpu_possible_mask); if (!cpumask_empty(cpumask)) { ret = 0; - apply_wqattrs_lock(); + mutex_lock(&wq_pool_mutex); if (!cpumask_equal(cpumask, wq_unbound_cpumask)) ret = workqueue_apply_unbound_cpumask(cpumask); if (!ret) cpumask_copy(wq_requested_unbound_cpumask, cpumask); - apply_wqattrs_unlock(); + mutex_unlock(&wq_pool_mutex); } return ret; @@ -8041,12 +8056,12 @@ void __init workqueue_init_early(void) ordered_wq_attrs[i] = attrs; } - system_wq = alloc_workqueue("events", WQ_PERCPU, 0); + system_wq = alloc_workqueue("events", WQ_PERCPU | __WQ_DEPRECATED, 0); system_percpu_wq = alloc_workqueue("events", WQ_PERCPU, 0); system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI | WQ_PERCPU, 0); system_long_wq = alloc_workqueue("events_long", WQ_PERCPU, 0); - system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_MAX_ACTIVE); + system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND | __WQ_DEPRECATED, WQ_MAX_ACTIVE); system_dfl_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE | WQ_PERCPU, 0); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index fec7c24784021..e2f976c3301b6 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2656,12 +2656,17 @@ config TEST_VMALLOC config TEST_WORKQUEUE tristate "Test module for stress/performance analysis of workqueue" + depends on m default n help This builds the "test_workqueue" module for benchmarking workqueue throughput under contention. Useful for evaluating affinity scope changes (e.g., cache_shard vs cache). + The test drives sysfs to switch affinity scopes, so it must be + loaded after userspace has mounted sysfs; building it in (=y) + would run module_init before /sys is available. + If unsure, say N. config TEST_BPF |
