Skip to content

Commit 24be7e3

Browse files
committed
workqueue: Add workqueue_unbound_exclude_cpumask() to exclude CPUs from wq_unbound_cpumask
JIRA: https://issues.redhat.com/browse/RHEL-21798 Conflicts: 1) A merge conflict in the workqueue_unbound_exclude_cpumask() hunk of kernel/workqueue.c due to missing upstream commit 63c5484 ("workqueue: Add multiple affinity scopes and interface to select them"). 2) A merge conflict in the workqueue_init_early() hunk of kernel/workqueue.c due to upstream merge conflict resolved according to upstream merge commit 2025956 ("Merge branch 'for-6.7-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq into for-6.8"). commit fe28f63 Author: Waiman Long <[email protected]> Date: Wed, 25 Oct 2023 14:25:52 -0400 workqueue: Add workqueue_unbound_exclude_cpumask() to exclude CPUs from wq_unbound_cpumask When the "isolcpus" boot command line option is used to add a set of isolated CPUs, those CPUs will be excluded automatically from wq_unbound_cpumask to avoid running work functions from unbound workqueues. Recently cpuset has been extended to allow the creation of partitions of isolated CPUs dynamically. To make it closer to the "isolcpus" in functionality, the CPUs in those isolated cpuset partitions should be excluded from wq_unbound_cpumask as well. This can be done currently by explicitly writing to the workqueue's cpumask sysfs file after creating the isolated partitions. However, this process can be error prone. Ideally, the cpuset code should be allowed to request the workqueue code to exclude those isolated CPUs from wq_unbound_cpumask so that this operation can be done automatically and the isolated CPUs will be returned back to wq_unbound_cpumask after the destructions of the isolated cpuset partitions. This patch adds a new workqueue_unbound_exclude_cpumask() function to enable that. This new function will exclude the specified isolated CPUs from wq_unbound_cpumask. To be able to restore those isolated CPUs back after the destruction of isolated cpuset partitions, a new wq_requested_unbound_cpumask is added to store the user provided unbound cpumask either from the boot command line options or from writing to the cpumask sysfs file. This new cpumask provides the basis for CPU exclusion. To enable users to understand how the wq_unbound_cpumask is being modified internally, this patch also exposes the newly introduced wq_requested_unbound_cpumask as well as a wq_isolated_cpumask to store the cpumask to be excluded from wq_unbound_cpumask as read-only sysfs files. Signed-off-by: Waiman Long <[email protected]> Signed-off-by: Tejun Heo <[email protected]> Signed-off-by: Waiman Long <[email protected]>
1 parent 1d28ea8 commit 24be7e3

File tree

2 files changed

+84
-9
lines changed

2 files changed

+84
-9
lines changed

include/linux/workqueue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ struct workqueue_attrs *alloc_workqueue_attrs(void);
434434
void free_workqueue_attrs(struct workqueue_attrs *attrs);
435435
int apply_workqueue_attrs(struct workqueue_struct *wq,
436436
const struct workqueue_attrs *attrs);
437-
int workqueue_set_unbound_cpumask(cpumask_var_t cpumask);
437+
extern int workqueue_unbound_exclude_cpumask(cpumask_var_t cpumask);
438438

439439
extern bool queue_work_on(int cpu, struct workqueue_struct *wq,
440440
struct work_struct *work);

kernel/workqueue.c

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ static bool workqueue_freezing; /* PL: have wqs started freezing? */
327327
/* PL&A: allowable cpus for unbound wqs and work items */
328328
static cpumask_var_t wq_unbound_cpumask;
329329

330+
/* PL: user requested unbound cpumask via sysfs */
331+
static cpumask_var_t wq_requested_unbound_cpumask;
332+
333+
/* PL: isolated cpumask to be excluded from unbound cpumask */
334+
static cpumask_var_t wq_isolated_cpumask;
335+
330336
/* for further constrain wq_unbound_cpumask by cmdline parameter*/
331337
static struct cpumask wq_cmdline_cpumask __initdata;
332338

@@ -5494,7 +5500,7 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask)
54945500
* -EINVAL - Invalid @cpumask
54955501
* -ENOMEM - Failed to allocate memory for attrs or pwqs.
54965502
*/
5497-
int workqueue_set_unbound_cpumask(cpumask_var_t cpumask)
5503+
static int workqueue_set_unbound_cpumask(cpumask_var_t cpumask)
54985504
{
54995505
int ret = -EINVAL;
55005506

@@ -5505,6 +5511,7 @@ int workqueue_set_unbound_cpumask(cpumask_var_t cpumask)
55055511
cpumask_and(cpumask, cpumask, cpu_possible_mask);
55065512
if (!cpumask_empty(cpumask)) {
55075513
apply_wqattrs_lock();
5514+
cpumask_copy(wq_requested_unbound_cpumask, cpumask);
55085515
if (cpumask_equal(cpumask, wq_unbound_cpumask)) {
55095516
ret = 0;
55105517
goto out_unlock;
@@ -5519,6 +5526,44 @@ int workqueue_set_unbound_cpumask(cpumask_var_t cpumask)
55195526
return ret;
55205527
}
55215528

5529+
/**
5530+
* workqueue_unbound_exclude_cpumask - Exclude given CPUs from unbound cpumask
5531+
* @exclude_cpumask: the cpumask to be excluded from wq_unbound_cpumask
5532+
*
5533+
* This function can be called from cpuset code to provide a set of isolated
5534+
* CPUs that should be excluded from wq_unbound_cpumask. The caller must hold
5535+
* either cpus_read_lock or cpus_write_lock.
5536+
*/
5537+
int workqueue_unbound_exclude_cpumask(cpumask_var_t exclude_cpumask)
5538+
{
5539+
cpumask_var_t cpumask;
5540+
int ret = 0;
5541+
5542+
if (!zalloc_cpumask_var(&cpumask, GFP_KERNEL))
5543+
return -ENOMEM;
5544+
5545+
lockdep_assert_cpus_held();
5546+
mutex_lock(&wq_pool_mutex);
5547+
5548+
/* Save the current isolated cpumask & export it via sysfs */
5549+
cpumask_copy(wq_isolated_cpumask, exclude_cpumask);
5550+
5551+
/*
5552+
* If the operation fails, it will fall back to
5553+
* wq_requested_unbound_cpumask which is initially set to
5554+
* (HK_TYPE_WQ ∩ HK_TYPE_DOMAIN) house keeping mask and rewritten
5555+
* by any subsequent write to workqueue/cpumask sysfs file.
5556+
*/
5557+
if (!cpumask_andnot(cpumask, wq_requested_unbound_cpumask, exclude_cpumask))
5558+
cpumask_copy(cpumask, wq_requested_unbound_cpumask);
5559+
if (!cpumask_equal(cpumask, wq_unbound_cpumask))
5560+
ret = workqueue_apply_unbound_cpumask(cpumask);
5561+
5562+
mutex_unlock(&wq_pool_mutex);
5563+
free_cpumask_var(cpumask);
5564+
return ret;
5565+
}
5566+
55225567
#ifdef CONFIG_SYSFS
55235568
/*
55245569
* Workqueues with WQ_SYSFS flag set is visible to userland via
@@ -5750,19 +5795,36 @@ static struct bus_type wq_subsys = {
57505795
.dev_groups = wq_sysfs_groups,
57515796
};
57525797

5753-
static ssize_t wq_unbound_cpumask_show(struct device *dev,
5754-
struct device_attribute *attr, char *buf)
5798+
static ssize_t __wq_cpumask_show(struct device *dev,
5799+
struct device_attribute *attr, char *buf, cpumask_var_t mask)
57555800
{
57565801
int written;
57575802

57585803
mutex_lock(&wq_pool_mutex);
5759-
written = scnprintf(buf, PAGE_SIZE, "%*pb\n",
5760-
cpumask_pr_args(wq_unbound_cpumask));
5804+
written = scnprintf(buf, PAGE_SIZE, "%*pb\n", cpumask_pr_args(mask));
57615805
mutex_unlock(&wq_pool_mutex);
57625806

57635807
return written;
57645808
}
57655809

5810+
static ssize_t wq_unbound_cpumask_show(struct device *dev,
5811+
struct device_attribute *attr, char *buf)
5812+
{
5813+
return __wq_cpumask_show(dev, attr, buf, wq_unbound_cpumask);
5814+
}
5815+
5816+
static ssize_t wq_requested_cpumask_show(struct device *dev,
5817+
struct device_attribute *attr, char *buf)
5818+
{
5819+
return __wq_cpumask_show(dev, attr, buf, wq_requested_unbound_cpumask);
5820+
}
5821+
5822+
static ssize_t wq_isolated_cpumask_show(struct device *dev,
5823+
struct device_attribute *attr, char *buf)
5824+
{
5825+
return __wq_cpumask_show(dev, attr, buf, wq_isolated_cpumask);
5826+
}
5827+
57665828
static ssize_t wq_unbound_cpumask_store(struct device *dev,
57675829
struct device_attribute *attr, const char *buf, size_t count)
57685830
{
@@ -5780,9 +5842,13 @@ static ssize_t wq_unbound_cpumask_store(struct device *dev,
57805842
return ret ? ret : count;
57815843
}
57825844

5783-
static struct device_attribute wq_sysfs_cpumask_attr =
5845+
static struct device_attribute wq_sysfs_cpumask_attrs[] = {
57845846
__ATTR(cpumask, 0644, wq_unbound_cpumask_show,
5785-
wq_unbound_cpumask_store);
5847+
wq_unbound_cpumask_store),
5848+
__ATTR(cpumask_requested, 0444, wq_requested_cpumask_show, NULL),
5849+
__ATTR(cpumask_isolated, 0444, wq_isolated_cpumask_show, NULL),
5850+
__ATTR_NULL,
5851+
};
57865852

57875853
static int __init wq_sysfs_init(void)
57885854
{
@@ -5795,7 +5861,13 @@ static int __init wq_sysfs_init(void)
57955861

57965862
dev_root = bus_get_dev_root(&wq_subsys);
57975863
if (dev_root) {
5798-
err = device_create_file(dev_root, &wq_sysfs_cpumask_attr);
5864+
struct device_attribute *attr;
5865+
5866+
for (attr = wq_sysfs_cpumask_attrs; attr->attr.name; attr++) {
5867+
err = device_create_file(dev_root, attr);
5868+
if (err)
5869+
break;
5870+
}
57995871
put_device(dev_root);
58005872
}
58015873
return err;
@@ -6118,11 +6190,14 @@ void __init workqueue_init_early(void)
61186190
BUILD_BUG_ON(__alignof__(struct pool_workqueue) < __alignof__(long long));
61196191

61206192
BUG_ON(!alloc_cpumask_var(&wq_unbound_cpumask, GFP_KERNEL));
6193+
BUG_ON(!alloc_cpumask_var(&wq_requested_unbound_cpumask, GFP_KERNEL));
6194+
BUG_ON(!zalloc_cpumask_var(&wq_isolated_cpumask, GFP_KERNEL));
61216195
cpumask_copy(wq_unbound_cpumask, cpu_possible_mask);
61226196
restrict_unbound_cpumask("HK_TYPE_WQ", housekeeping_cpumask(HK_TYPE_WQ));
61236197
restrict_unbound_cpumask("HK_TYPE_DOMAIN", housekeeping_cpumask(HK_TYPE_DOMAIN));
61246198
if (!cpumask_empty(&wq_cmdline_cpumask))
61256199
restrict_unbound_cpumask("workqueue.unbound_cpus", &wq_cmdline_cpumask);
6200+
cpumask_copy(wq_requested_unbound_cpumask, wq_unbound_cpumask);
61266201

61276202
pwq_cache = KMEM_CACHE(pool_workqueue, SLAB_PANIC);
61286203

0 commit comments

Comments
 (0)