From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D6994028E2 for ; Thu, 9 Jul 2026 10:25:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.47 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783592723; cv=none; b=aGWJhUR1UNmJHdFE2tcDFGbBhuvspGLXxIAP4JeittR5FDbkS3R4Xn3+sgPrrn2fmiPc6TDN8+CoFXYINLugHYGEEQLjzrTJAStidTo1+BeaRcuC7bDo7v+dbhTR1UO5BFv7wxJ4b5jCI6hlWKtvxYPuditQJu2/ziFUgMsj46k= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783592723; c=relaxed/simple; bh=txwrv1FGixcaA37/9ZndNEo0S9acIWiwKfUZGT+sKto=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nPyPeyWCNqKZul4BEF+AXv+D/sDVZapEIgGsNQ1G2tNWfFJFjU9BSTonJ9nxtm325C/TTmSGYKPNAha088NktbIdd/xuSxIX9IzVz7P0aNB7qgYhaW99/WDMICKQ/rtLOU9g46+A/h3+O6t8VPDIzH/4k7XLB3DuodvpTxd1Wck= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (2048-bit key) header.d=suse.com header.i=@suse.com header.b=F/hIt8f+; arc=none smtp.client-ip=209.85.128.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=suse.com header.i=@suse.com header.b="F/hIt8f+" Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-493ce08a75bso6913175e9.1 for ; Thu, 09 Jul 2026 03:25:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=google; t=1783592719; x=1784197519; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xYVe/7eFlg/cl34q5aMH3AG6JKrJh1i07UsPmsAXscU=; b=F/hIt8f+/TsbHeSlPC//S/qDeYo7ahpT4Lnn1qwyvstQNg2yz6S3mjSxldTbACToVg GFodSz9xksMDkSpQQZ8wsIk2w1oRJdtIjVoWb3hICaIN6rIW3tcc7rvoRPeogVI968W5 5vkITWw9Qv9eCc/2tDFLTNfV/InqoIGHhDUX7ijDEOIi+QaT+ihvqXrh9me2x0u6e19Y Skzh9TK8eYJmOalW2mMHWFsEg7J2ksaGMfamTYROu7a9xD4vOtcepMplWkmoMY+FQcbu 3nOaEJiWEkGQUHPDiiPTro8E9GjoHecmisMn0SHSLO0kcOo62jNCcjA4PH5O/w2Uwa7u auGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783592719; x=1784197519; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=xYVe/7eFlg/cl34q5aMH3AG6JKrJh1i07UsPmsAXscU=; b=h54CoiM9Mo3Qib6RyFG5kh0yOSjc+o0PVnyjWu1WAHfaTWL4lGiN/UzDLmPt9ftAOu OKTeP+FX3SBCbzc8UEXWIqUDWkltzAx0fPwGAXb8oZA2uTpqqPWOAch/z9kK6eowTs4i 7qG5q2REOUElSXfTz+076qqviL0e9/ZuVowuVcpDaxJzsBqn4WFOlPGKBcMk+BbZNKHy AtLCzNb2Y5Pxwcc8jCk+wVU1P9UKVgBFnBTAL+sx/c8V76RleqOete+ZnEKSXwNFNjw3 2tgt2ZpCl8VkR2lSlKI6GOpzCUGszbkJsuLnlYbK0p7aJRIo5mFuNpuuLBUJRR3McaLM 6ckA== X-Gm-Message-State: AOJu0YwTs+hEUi2fw1mqC2DL0NwhR+iDQ1y9SRFoAejpNxJAkHNmEB3B y8j0a+fLbCvp/Ki2CTkC5Q7VeDUAm7uWjzCK9uDMYpjNf/F435Km+DC/fXuF7laDdCA7nbfpTt6 8qlZoYXc= X-Gm-Gg: AfdE7ckm6tBUbIH4gehpbdQz60CMPzpwnPuKCKy5NDGODOIC3I66dwVUCHX1ZjmQMcq MzScWq7c5kCsNiLCGLGeXurvtZA5HZvcUne9+ckGOjzn7O1e8RzKsq/BMZdm4L9k5+j3+aRQGur 5r4Wv7UXooNlkIDOCB77cJKqJc32n0gKv1cSNikIptHIJ9U1bbgWJB4YyHxWcDjXgRkkSc9cPzK ozKPXWTDtRP1qDdFqH4eWbGg5maOjyavQ1wLX7xXb3VrJz80FgF1ChAj7mitVEsP1vSjOS5NZXw pdfBMYE7LN51on3Y/V0rQGd+O9tbLEVyF2yftFdU500/wrErQ08MFl/G4G/PfiVbhTd41Sv9gF1 GWj1OPu6IYRMN/tj9tQd7+WC3j+JSuO1vhmQCLo5xzSLXkOVJTqGjW1kNYr9kUk3PnXo5ltceDb qxBYgKzTr0U68N9q2aFtjaD1QPGGEPejBCP7Ol X-Received: by 2002:a05:600c:3516:b0:492:3e66:6c84 with SMTP id 5b1f17b1804b1-493e68782cdmr65862145e9.30.1783592719449; Thu, 09 Jul 2026 03:25:19 -0700 (PDT) Received: from localhost.localdomain ([2a00:6d43:105:c401:e307:1a37:2e76:ce91]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-47a9de1d91bsm55097033f8f.4.2026.07.09.03.25.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 09 Jul 2026 03:25:19 -0700 (PDT) From: Marco Crivellari To: linux-kernel@vger.kernel.org Cc: Tejun Heo , Lai Jiangshan , Frederic Weisbecker , Sebastian Andrzej Siewior , Marco Crivellari , Michal Hocko , Breno Leitao Subject: [PATCH RFC 1/2] workqueue: Make unbound the default alloc_workqueue() behavior Date: Thu, 9 Jul 2026 12:25:09 +0200 Message-ID: <20260709102510.180513-2-marco.crivellari@suse.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709102510.180513-1-marco.crivellari@suse.com> References: <20260709102510.180513-1-marco.crivellari@suse.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The original behavior of alloc_workqueue() was to be per-CPU when WQ_UNBOUND was not specified. Currently when alloc_workqueue() is called one among WQ_PERCPU or WQ_UNBOUND must be specified, this is enforced by a WARN_ON() and properly flag settings. Move forwared the Workqueue refactoring changing the default Workqueue behavior from per-CPU to unbound when WQ_PERCPU is not present. WQ_UNBOUND will be later removed from every alloc_workqueue() caller. Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/ Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari --- include/linux/workqueue.h | 8 ++--- kernel/workqueue.c | 68 ++++++++++++++------------------------- 2 files changed, 29 insertions(+), 47 deletions(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index a283766a192a..c5b84fd0e415 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -577,7 +577,7 @@ alloc_workqueue_lockdep_map(const char *fmt, unsigned int flags, int max_active, * Pointer to the allocated workqueue on success, %NULL on failure. */ #define alloc_ordered_workqueue_lockdep_map(fmt, flags, lockdep_map, args...) \ - alloc_hooks(alloc_workqueue_lockdep_map(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags),\ + alloc_hooks(alloc_workqueue_lockdep_map(fmt, __WQ_ORDERED | (flags),\ 1, lockdep_map, ##args)) #endif @@ -595,14 +595,14 @@ alloc_workqueue_lockdep_map(const char *fmt, unsigned int flags, int max_active, * Pointer to the allocated workqueue on success, %NULL on failure. */ #define alloc_ordered_workqueue(fmt, flags, args...) \ - alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args) + alloc_workqueue(fmt, __WQ_ORDERED | (flags), 1, ##args) #define devm_alloc_ordered_workqueue(dev, fmt, flags, args...) \ - devm_alloc_workqueue(dev, fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args) + devm_alloc_workqueue(dev, fmt, __WQ_ORDERED | (flags), 1, ##args) #define create_workqueue(name) \ alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_PERCPU, 1, (name)) #define create_freezable_workqueue(name) \ - alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \ + alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | \ WQ_MEM_RECLAIM, 1, (name)) #define create_singlethread_workqueue(name) \ alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 78068ae8f28a..0267c6a94994 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1370,7 +1370,7 @@ static void wq_cpu_intensive_report(work_func_t func) if (wq_cpu_intensive_warning_thresh && cnt >= wq_cpu_intensive_warning_thresh && is_power_of_2(cnt + 1 - wq_cpu_intensive_warning_thresh)) - printk_deferred(KERN_WARNING "workqueue: %ps hogged CPU for >%luus %llu times, consider switching to WQ_UNBOUND\n", + printk_deferred(KERN_WARNING "workqueue: %ps hogged CPU for >%luus %llu times, consider removing WQ_PERCPU\n", ent->func, wq_cpu_intensive_thresh_us, atomic64_read(&ent->cnt)); return; @@ -1586,7 +1586,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)) + if (wq->flags & WQ_PERCPU) return NULL; if (node == NUMA_NO_NODE) @@ -2322,7 +2322,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, retry: /* pwq which will be used unless @work is executing elsewhere */ if (req_cpu == WORK_CPU_UNBOUND) { - if (wq->flags & WQ_UNBOUND) + if (!(wq->flags & WQ_PERCPU)) cpu = wq_select_unbound_cpu(raw_smp_processor_id()); else cpu = raw_smp_processor_id(); @@ -2369,7 +2369,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, * on it, so the retrying is guaranteed to make forward-progress. */ if (unlikely(!pwq->refcnt)) { - if (wq->flags & WQ_UNBOUND) { + if (!(wq->flags & WQ_PERCPU)) { raw_spin_unlock(&pool->lock); cpu_relax(); goto retry; @@ -2522,7 +2522,7 @@ bool queue_work_node(int node, struct workqueue_struct *wq, * workqueue_select_cpu_near would need to be updated to allow for * some round robin type logic. */ - WARN_ON_ONCE(!(wq->flags & WQ_UNBOUND)); + WARN_ON_ONCE(wq->flags & WQ_PERCPU); local_irq_save(irq_flags); @@ -5028,7 +5028,7 @@ static void rcu_free_wq(struct rcu_head *rcu) struct workqueue_struct *wq = container_of(rcu, struct workqueue_struct, rcu); - if (wq->flags & WQ_UNBOUND) + if (!(wq->flags & WQ_PERCPU)) free_node_nr_active(wq->node_nr_active); wq_free_lockdep(wq); @@ -5220,7 +5220,7 @@ static void pwq_release_workfn(struct kthread_work *work) mutex_unlock(&wq->mutex); } - if (wq->flags & WQ_UNBOUND) { + if (!(wq->flags & WQ_PERCPU)) { mutex_lock(&wq_pool_mutex); put_unbound_pool(pool); mutex_unlock(&wq_pool_mutex); @@ -5489,7 +5489,7 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq, struct apply_wqattrs_ctx *ctx; /* only unbound workqueues can change attributes */ - if (WARN_ON(!(wq->flags & WQ_UNBOUND))) + if (WARN_ON(wq->flags & WQ_PERCPU)) return -EINVAL; ctx = apply_wqattrs_prepare(wq, attrs, wq_unbound_cpumask); @@ -5556,7 +5556,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_PERCPU || wq->unbound_attrs->ordered) return; /* @@ -5610,7 +5610,7 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) if (!wq->cpu_pwq) goto enomem; - if (!(wq->flags & WQ_UNBOUND)) { + if (wq->flags & WQ_PERCPU) { struct worker_pool __percpu *pools; if (wq->flags & WQ_BH) @@ -5770,7 +5770,7 @@ static void wq_adjust_max_active(struct workqueue_struct *wq) WRITE_ONCE(wq->max_active, new_max); WRITE_ONCE(wq->min_active, new_min); - if (wq->flags & WQ_UNBOUND) + if (!(wq->flags & WQ_PERCPU)) wq_update_node_max_active(wq, -1); if (new_max == 0) @@ -5816,10 +5816,10 @@ 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 = (flags & ~WQ_PERCPU) | WQ_UNBOUND; + flags = (flags & ~WQ_PERCPU); /* allocate wq and format name */ - if (flags & WQ_UNBOUND) + if (!(flags & WQ_PERCPU)) wq_size = struct_size(wq, node_nr_active, nr_node_ids + 1); else wq_size = sizeof(*wq); @@ -5828,7 +5828,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, if (!wq) return NULL; - if (flags & WQ_UNBOUND) { + if (!(flags & WQ_PERCPU)) { wq->unbound_attrs = alloc_workqueue_attrs_noprof(); if (!wq->unbound_attrs) goto err_free_wq; @@ -5840,23 +5840,6 @@ 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 @@ -5883,7 +5866,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, INIT_LIST_HEAD(&wq->list); - if (flags & WQ_UNBOUND) { + if (!(flags & WQ_PERCPU)) { if (alloc_node_nr_active(wq->node_nr_active) < 0) goto err_free_wq; } @@ -5920,7 +5903,7 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, * flushing the pwq_release_worker ensures that the pwq_release_workfn() * completes before calling kfree(wq). */ - if (wq->flags & WQ_UNBOUND) { + if (!(wq->flags & WQ_PERCPU)) { kthread_flush_worker(pwq_release_worker); free_node_nr_active(wq->node_nr_active); } @@ -6148,7 +6131,7 @@ void workqueue_set_max_active(struct workqueue_struct *wq, int max_active) mutex_lock(&wq->mutex); wq->saved_max_active = max_active; - if (wq->flags & WQ_UNBOUND) + if (!(wq->flags & WQ_PERCPU)) wq->saved_min_active = min(wq->saved_min_active, max_active); wq_adjust_max_active(wq); @@ -6174,8 +6157,7 @@ EXPORT_SYMBOL_GPL(workqueue_set_max_active); void workqueue_set_min_active(struct workqueue_struct *wq, int min_active) { /* min_active is only meaningful for non-ordered unbound workqueues */ - if (WARN_ON((wq->flags & (WQ_BH | WQ_UNBOUND | __WQ_ORDERED)) != - WQ_UNBOUND)) + if (WARN_ON(wq->flags & (WQ_BH | WQ_PERCPU | __WQ_ORDERED))) return; mutex_lock(&wq->mutex); @@ -7081,7 +7063,7 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask) lockdep_assert_held(&wq_pool_mutex); list_for_each_entry(wq, &workqueues, list) { - if (!(wq->flags & WQ_UNBOUND) || (wq->flags & __WQ_DESTROYING)) + if ((wq->flags & WQ_PERCPU) || (wq->flags & __WQ_DESTROYING)) continue; ctx = apply_wqattrs_prepare(wq, wq->unbound_attrs, unbound_cpumask); @@ -7242,7 +7224,7 @@ static ssize_t per_cpu_show(struct device *dev, struct device_attribute *attr, { struct workqueue_struct *wq = dev_to_wq(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)!(wq->flags & WQ_UNBOUND)); + return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)(wq->flags & WQ_PERCPU)); } static DEVICE_ATTR_RO(per_cpu); @@ -7618,7 +7600,7 @@ int workqueue_sysfs_register(struct workqueue_struct *wq) return ret; } - if (wq->flags & WQ_UNBOUND) { + if (!(wq->flags & WQ_PERCPU)) { struct device_attribute *attr; for (attr = wq_sysfs_unbound_attrs; attr->attr.name; attr++) { @@ -8057,8 +8039,8 @@ void __init workqueue_init_early(void) 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_DEPRECATED, WQ_MAX_ACTIVE); - system_dfl_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, WQ_MAX_ACTIVE); + system_unbound_wq = alloc_workqueue("events_unbound", __WQ_DEPRECATED, WQ_MAX_ACTIVE); + system_dfl_wq = alloc_workqueue("events_unbound", 0, WQ_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE | WQ_PERCPU, 0); system_power_efficient_wq = alloc_workqueue("events_power_efficient", @@ -8068,7 +8050,7 @@ void __init workqueue_init_early(void) system_bh_wq = alloc_workqueue("events_bh", WQ_BH | WQ_PERCPU, 0); system_bh_highpri_wq = alloc_workqueue("events_bh_highpri", WQ_BH | WQ_HIGHPRI | WQ_PERCPU, 0); - system_dfl_long_wq = alloc_workqueue("events_dfl_long", WQ_UNBOUND, WQ_MAX_ACTIVE); + system_dfl_long_wq = alloc_workqueue("events_dfl_long", 0, WQ_MAX_ACTIVE); BUG_ON(!system_wq || !system_percpu_wq|| !system_highpri_wq || !system_long_wq || !system_unbound_wq || !system_freezable_wq || !system_dfl_wq || !system_power_efficient_wq || @@ -8446,7 +8428,7 @@ void __init workqueue_init_topology(void) list_for_each_entry(wq, &workqueues, list) { for_each_online_cpu(cpu) unbound_wq_update_pwq(wq, cpu); - if (wq->flags & WQ_UNBOUND) { + if (!(wq->flags & WQ_PERCPU)) { mutex_lock(&wq->mutex); wq_update_node_max_active(wq, -1); mutex_unlock(&wq->mutex); -- 2.54.0