[PATCH 3/4] workqueue: introduce helper workqueue_unbound_affinity_scope()

Hannes Reinecke hare at kernel.org
Wed Jul 3 06:50:20 PDT 2024


For drivers creating their own workqueue it might be useful to
switch to the 'cpu' unbound affinity scope to keep the locality
and reduce contention. As it's cumbersome to instruct the user
how to switch to affinity scope from userland introduce a helper
workqueue_unbound_affinity_scope() to let the driver set the
affinity scope directly.

Cc: Tejun Heo <tj at kernel.org>
Signed-off-by: Hannes Reinecke <hare at kernel.org>
---
 include/linux/workqueue.h |  1 +
 kernel/workqueue.c        | 47 +++++++++++++++++++++++++++++++--------
 2 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index fb3993894536..6c5f3dc614d9 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -541,6 +541,7 @@ void free_workqueue_attrs(struct workqueue_attrs *attrs);
 int apply_workqueue_attrs(struct workqueue_struct *wq,
 			  const struct workqueue_attrs *attrs);
 extern int workqueue_unbound_exclude_cpumask(cpumask_var_t cpumask);
+extern int workqueue_unbound_affinity_scope(struct workqueue_struct *wq, int scope);
 
 extern bool queue_work_on(int cpu, struct workqueue_struct *wq,
 			struct work_struct *work);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 003474c9a77d..53b1ca11fc86 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -7143,26 +7143,55 @@ static ssize_t wq_affn_scope_show(struct device *dev,
 	return written;
 }
 
-static ssize_t wq_affn_scope_store(struct device *dev,
-				   struct device_attribute *attr,
-				   const char *buf, size_t count)
+int workqueue_unbound_affinity_scope(struct workqueue_struct *wq, int scope)
 {
-	struct workqueue_struct *wq = dev_to_wq(dev);
 	struct workqueue_attrs *attrs;
-	int affn, ret = -ENOMEM;
+	int ret = -ENOMEM;;
 
-	affn = parse_affn_scope(buf);
-	if (affn < 0)
-		return affn;
+	if (!(wq->flags & WQ_UNBOUND))
+		return -EINVAL;
+
+	switch(scope) {
+	case WQ_AFFN_DFL:
+		/* fallthrough */
+	case WQ_AFFN_CPU:
+		/* fallthrough */
+	case WQ_AFFN_SMT:
+		/* fallthrough */
+	case WQ_AFFN_CACHE:
+		/* fallthrough */
+	case WQ_AFFN_NUMA:
+		/* fallthrough */
+	case WQ_AFFN_SYSTEM:
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	apply_wqattrs_lock();
 	attrs = wq_sysfs_prep_attrs(wq);
 	if (attrs) {
-		attrs->affn_scope = affn;
+		attrs->affn_scope = scope;
 		ret = apply_workqueue_attrs_locked(wq, attrs);
 	}
 	apply_wqattrs_unlock();
 	free_workqueue_attrs(attrs);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(workqueue_unbound_affinity_scope);
+
+static ssize_t wq_affn_scope_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	struct workqueue_struct *wq = dev_to_wq(dev);
+	int affn, ret = -ENOMEM;
+
+	affn = parse_affn_scope(buf);
+	if (affn < 0)
+		return affn;
+
+	ret = workqueue_unbound_affinity_scope(wq, affn);
 	return ret ?: count;
 }
 
-- 
2.35.3




More information about the Linux-nvme mailing list