[PATCH -next] workqueue: devres: provide device-managed allocate workqueue APIs

From: Yang Yingliang
Date: Sat Sep 17 2022 - 07:43:08 EST


Provide a series of device-managed allocate workqueue APIs. The
workqueue allocated by these APIs are managed by device resource,
it will be destroyed whenever the device is unbound. Drivers could
use these APIs to simpilfy the error path.

Signed-off-by: Yang Yingliang <yangyingliang@xxxxxxxxxx>
---
.../driver-api/driver-model/devres.rst | 7 ++++
include/linux/workqueue.h | 19 ++++++++++
kernel/workqueue.c | 35 +++++++++++++++++++
3 files changed, 61 insertions(+)

diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 2895f9ea00c4..76cc256c9e4f 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -440,3 +440,10 @@ SPI

WATCHDOG
devm_watchdog_register_device()
+
+WORKQUEUE
+ devm_alloc_workqueue()
+ devm_alloc_ordered_workqueue()
+ devm_create_workqueue()
+ devm_create_freezable_workqueue()
+ devm_create_singlethread_workqueue()
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index a0143dd24430..4f6b9940acfa 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -402,6 +402,11 @@ extern struct workqueue_struct *system_freezable_power_efficient_wq;
__printf(1, 4) struct workqueue_struct *
alloc_workqueue(const char *fmt, unsigned int flags, int max_active, ...);

+__printf(2, 5) struct workqueue_struct *
+devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags,
+ int max_active, ...);
+
+
/**
* alloc_ordered_workqueue - allocate an ordered workqueue
* @fmt: printf format for the name of the workqueue
@@ -419,15 +424,29 @@ alloc_workqueue(const char *fmt, unsigned int flags, int max_active, ...);
alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | \
__WQ_ORDERED_EXPLICIT | (flags), 1, ##args)

+#define devm_alloc_ordered_workqueue(dev, fmt, flags, args...) \
+ devm_alloc_workqueue(dev, fmt, WQ_UNBOUND | __WQ_ORDERED | \
+ __WQ_ORDERED_EXPLICIT | (flags), 1, ##args)\
+
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
+#define devm_create_workqueue(dev, name) \
+ devm_alloc_workqueue(dev, "%s", __WQ_LEGACY | WQ_MEM_RECLAIM, \
+ 1, (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, 1, (name))
+#define devm_create_freezable_workqueue(dev, name) \
+ devm_alloc_workqueue(dev, "%s", __WQ_LEGACY | WQ_FREEZABLE | \
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
+#define devm_create_singlethread_workqueue(dev, name) \
+ devm_alloc_ordered_workqueue(dev, "%s",__WQ_LEGACY | \
+ WQ_MEM_RECLAIM, name)

extern void destroy_workqueue(struct workqueue_struct *wq);
+extern void devm_destroy_workqueue(struct device *dev, void *res);

struct workqueue_attrs *alloc_workqueue_attrs(void);
void free_workqueue_attrs(struct workqueue_attrs *attrs);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 39060a5d0905..418b21b4d025 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4490,6 +4490,41 @@ void destroy_workqueue(struct workqueue_struct *wq)
}
EXPORT_SYMBOL_GPL(destroy_workqueue);

+void devm_destroy_workqueue(struct device *dev, void *res)
+{
+ destroy_workqueue(*(struct workqueue_struct **)res);
+}
+EXPORT_SYMBOL_GPL(devm_destroy_workqueue);
+
+__printf(2, 5) struct workqueue_struct *
+devm_alloc_workqueue(struct device *dev, const char *fmt, unsigned int flags,
+ int max_active, ...)
+{
+ struct workqueue_struct **ptr;
+ struct workqueue_struct *wq;
+ char name[WQ_NAME_LEN];
+ va_list args;
+
+ ptr = devres_alloc(devm_destroy_workqueue, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return NULL;
+
+ va_start(args, max_active);
+ vsnprintf(name, sizeof(name), fmt, args);
+ va_end(args);
+
+ wq = alloc_workqueue(name, flags, max_active);
+ if (!wq) {
+ devres_free(ptr);
+ return NULL;
+ }
+
+ *ptr = wq;
+ devres_add(dev, ptr);
+
+ return wq;
+}
+EXPORT_SYMBOL_GPL(devm_alloc_workqueue);
/**
* workqueue_set_max_active - adjust max_active of a workqueue
* @wq: target workqueue
--
2.25.1