aboutsummaryrefslogtreecommitdiffstats
diff options
authorMark Brown <broonie@kernel.org>2026-05-29 23:00:08 +0100
committerMark Brown <broonie@kernel.org>2026-05-29 23:00:08 +0100
commit8d353be232212e7e9a53c582d6cbc9570ad24ab8 (patch)
tree78b5cc6f96374539496cca6ccf990a383e4240c1
parentf624c17e475e17a1dbeef56ef624378f4e5a80b7 (diff)
parentbe7e2b52b04031b7f311a9e27e522a8702fe0b2f (diff)
downloadlinux-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.c2
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c8
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-gpio.c2
-rw-r--r--drivers/rapidio/rio.c2
-rw-r--r--drivers/virt/acrn/irqfd.c2
-rw-r--r--include/linux/workqueue.h1
-rw-r--r--kernel/umh.c2
-rw-r--r--kernel/workqueue.c71
-rw-r--r--lib/Kconfig.debug5
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