Skip to content

Commit

Permalink
workqueue: Make default affinity_scope dynamically updatable
Browse files Browse the repository at this point in the history
While workqueue.default_affinity_scope is writable, it only affects
workqueues which are created afterwards and isn't very useful. Instead,
let's introduce explicit "default" scope and update the effective scope
dynamically when workqueue.default_affinity_scope is changed.

Signed-off-by: Tejun Heo <tj@kernel.org>
  • Loading branch information
htejun committed Aug 8, 2023
1 parent 7dbf15c commit 523a301
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 13 deletions.
8 changes: 4 additions & 4 deletions Documentation/admin-guide/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7014,10 +7014,10 @@
information, see the Affinity Scopes section in
Documentation/core-api/workqueue.rst.

This can be updated after boot through the matching
file under /sys/module/workqueue/parameters.
However, the changed default will only apply to
unbound workqueues created afterwards.
This can be changed after boot by writing to the
matching /sys/module/workqueue/parameters file. All
workqueues with the "default" affinity scope will be
updated accordignly.

workqueue.debug_force_rr_cpu
Workqueue used to implicitly guarantee that work
Expand Down
9 changes: 8 additions & 1 deletion Documentation/core-api/workqueue.rst
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,11 @@ on one of the CPUs which share the last level cache with the issuing CPU.
Once started, the worker may or may not be allowed to move outside the scope
depending on the ``affinity_strict`` setting of the scope.

Workqueue currently supports the following five affinity scopes.
Workqueue currently supports the following affinity scopes.

``default``
Use the scope in module parameter ``workqueue.default_affinity_scope``
which is always set to one of the scopes below.

``cpu``
CPUs are not grouped. A work item issued on one CPU is processed by a
Expand Down Expand Up @@ -392,6 +396,9 @@ directory.
``affinity_scope``
Read to see the current affinity scope. Write to change.

When default is the current scope, reading this file will also show the
current effective scope in parentheses, for example, ``default (cache)``.

``affinity_strict``
0 by default indicating that affinity scopes are not strict. When a work
item starts execution, workqueue makes a best-effort attempt to ensure
Expand Down
3 changes: 1 addition & 2 deletions include/linux/workqueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,14 @@ struct rcu_work {
};

enum wq_affn_scope {
WQ_AFFN_DFL, /* use system default */
WQ_AFFN_CPU, /* one pod per CPU */
WQ_AFFN_SMT, /* one pod poer SMT */
WQ_AFFN_CACHE, /* one pod per LLC */
WQ_AFFN_NUMA, /* one pod per NUMA node */
WQ_AFFN_SYSTEM, /* one pod across the whole system */

WQ_AFFN_NR_TYPES,

WQ_AFFN_DFL = WQ_AFFN_CACHE,
};

/**
Expand Down
45 changes: 39 additions & 6 deletions kernel/workqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,10 @@ struct wq_pod_type {
};

static struct wq_pod_type wq_pod_types[WQ_AFFN_NR_TYPES];
static enum wq_affn_scope wq_affn_dfl = WQ_AFFN_DFL;
static enum wq_affn_scope wq_affn_dfl = WQ_AFFN_CACHE;

static const char *wq_affn_names[WQ_AFFN_NR_TYPES] = {
[WQ_AFFN_DFL] = "default",
[WQ_AFFN_CPU] = "cpu",
[WQ_AFFN_SMT] = "smt",
[WQ_AFFN_CACHE] = "cache",
Expand Down Expand Up @@ -3734,7 +3735,7 @@ struct workqueue_attrs *alloc_workqueue_attrs(void)
goto fail;

cpumask_copy(attrs->cpumask, cpu_possible_mask);
attrs->affn_scope = wq_affn_dfl;
attrs->affn_scope = WQ_AFFN_DFL;
return attrs;
fail:
free_workqueue_attrs(attrs);
Expand Down Expand Up @@ -3815,7 +3816,18 @@ static void wqattrs_actualize_cpumask(struct workqueue_attrs *attrs,
static const struct wq_pod_type *
wqattrs_pod_type(const struct workqueue_attrs *attrs)
{
struct wq_pod_type *pt = &wq_pod_types[attrs->affn_scope];
enum wq_affn_scope scope;
struct wq_pod_type *pt;

/* to synchronize access to wq_affn_dfl */
lockdep_assert_held(&wq_pool_mutex);

if (attrs->affn_scope == WQ_AFFN_DFL)
scope = wq_affn_dfl;
else
scope = attrs->affn_scope;

pt = &wq_pod_types[scope];

if (!WARN_ON_ONCE(attrs->affn_scope == WQ_AFFN_NR_TYPES) &&
likely(pt->nr_pods))
Expand Down Expand Up @@ -5847,13 +5859,29 @@ static int parse_affn_scope(const char *val)

static int wq_affn_dfl_set(const char *val, const struct kernel_param *kp)
{
int affn;
struct workqueue_struct *wq;
int affn, cpu;

affn = parse_affn_scope(val);
if (affn < 0)
return affn;
if (affn == WQ_AFFN_DFL)
return -EINVAL;

cpus_read_lock();
mutex_lock(&wq_pool_mutex);

wq_affn_dfl = affn;

list_for_each_entry(wq, &workqueues, list) {
for_each_online_cpu(cpu) {
wq_update_pod(wq, cpu, cpu, true);
}
}

mutex_unlock(&wq_pool_mutex);
cpus_read_unlock();

return 0;
}

Expand Down Expand Up @@ -6033,8 +6061,13 @@ static ssize_t wq_affn_scope_show(struct device *dev,
int written;

mutex_lock(&wq->mutex);
written = scnprintf(buf, PAGE_SIZE, "%s\n",
wq_affn_names[wq->unbound_attrs->affn_scope]);
if (wq->unbound_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]);
mutex_unlock(&wq->mutex);

return written;
Expand Down

0 comments on commit 523a301

Please sign in to comment.