workqueue / drivers: Add device-managed allocate workqueue
Subsystems (10)
v2 cover workqueue / drivers: Add device-managed allocate workqueue
Merging / Dependency
====================
All further patches depend on the first one, thus this probably should go via one tree, e.g. power supply. The first patch might be needed for other trees as well, e.g. if more drivers are discovered, so the best if it is on dedicated branch in case it has to be shared.
Changes in v2:
==============
- See individual patches
- Link to v1: https://patch.msgid.link/[email protected]
Description
===========
Add a Resource-managed version of alloc_workqueue() to fix common problem of drivers mixing devm() calls with destroy_workqueue. Such naive and discouraged driver approach leads to difficult to debug bugs when the driver:
1. Allocates workqueue in standard way and destroys it in driver remove() callback,
2. Sets work struct with devm_work_autocancel(),
3. Registers interrupt handler with devm_request_threaded_irq().
Which leads to following unbind/removal path:
1. destroy_workqueue() via driver remove(),
Any interrupt coming now would still execute the interrupt handler, which queues work on destroyed workqueue.
2. devm_irq_release(),
3. devm_work_drop() -> cancel_work_sync() on destroyed workqueue.
devm_alloc_workqueue() has two benefits:
1. Solves above problem of mix-and-match devres and non-devres code in driver,
2. Simplify any sane drivers which were correctly using alloc_workqueue() + devm_add_action_or_reset().
Best regards,
Krzysztof
---
Krzysztof Kozlowski (10):
workqueue: devres: Add device-managed allocate workqueue
power: supply: cw2015: Free allocated workqueue
power: supply: max77705: Drop duplicated IRQ error message
power: supply: max77705: Free allocated workqueue and fix removal order
power: supply: mt6370: Simplify with devm_alloc_ordered_workqueue()
power: supply: ipaq_micro: Simplify with devm
mfd: ezx-pcap: Drop memory allocation error message
mfd: ezx-pcap: Return directly instead of empty gotos
mfd: ezx-pcap: Avoid rescheduling after destroying workqueue platform/chrome: cros_usbpd_logger: Simplify with devm
Documentation/driver-api/driver-model/devres.rst | 4 ++ drivers/mfd/ezx-pcap.c | 27 +++++-------- drivers/platform/chrome/cros_usbpd_logger.c | 18 ++++----- drivers/power/supply/cw2015_battery.c | 3 +- drivers/power/supply/ipaq_micro_battery.c | 50 ++++++++---------------- drivers/power/supply/max77705_charger.c | 36 ++++++----------- drivers/power/supply/mt6370-charger.c | 13 +----- include/linux/workqueue.h | 22 +++++++++++ kernel/workqueue.c | 28 +++++++++++++ 9 files changed, 100 insertions(+), 101 deletions(-)
---
base-commit: 9739e59909e70058fab7a131d7bee60d447ab732
change-id: 20260220-workqueue-devm-d9591e5e70eb
Best regards,
Signature · 1 line
Krzysztof Kozlowski <[email protected]>
PATCH v2 1/10 workqueue: devres: Add device-managed allocate workqueue
Add a Resource-managed version of alloc_workqueue() to fix common problem of drivers mixing devm() calls with destroy_workqueue. Such naive and discouraged driver approach leads to difficult to debug bugs when the driver:
1. Allocates workqueue in standard way and destroys it in driver remove() callback,
2. Sets work struct with devm_work_autocancel(),
3. Registers interrupt handler with devm_request_threaded_irq().
Which leads to following unbind/removal path:
1. destroy_workqueue() via driver remove(),
Any interrupt coming now would still execute the interrupt handler, which queues work on destroyed workqueue.
2. devm_irq_release(),
3. devm_work_drop() -> cancel_work_sync() on destroyed workqueue.
devm_alloc_workqueue() has two benefits:
1. Solves above problem of mix-and-match devres and non-devres code in driver,
2. Simplify any sane drivers which were correctly using alloc_workqueue() + devm_add_action_or_reset().
---
All further patches depend on this one.
Changes in v2:
1. Drop devm_create_workqueue(), devm_create_freezable_workqueue() and devm_create_singlethread_workqueue()
2. Simplify with devm_add_action_or_reset()
3. Do not export devm_destroy_workqueue()
4. I did not move the declarations to devm-helpers.h because consensus was not reached and I think it would not be accurate place. The main alloc_workqueue() is here, so should be the devm- interface.
--- Documentation/driver-api/driver-model/devres.rst | 4 ++++ include/linux/workqueue.h | 22 +++++++++++++++++++ kernel/workqueue.c | 28 ++++++++++++++++++++++++ 3 files changed, 54 insertions(+)
Documentation/driver-api/driver-model/devres.rst +4 -0
@@ -464,3 +464,7 @@ SPI WATCHDOG devm_watchdog_register_device() + +WORKQUEUE + devm_alloc_workqueue() + devm_alloc_ordered_workqueue()
include/linux/workqueue.h +22 -0
@@ -512,6 +512,26 @@ __printf(1, 4) struct workqueue_struct * alloc_workqueue_noprof(const char *fmt, unsigned int flags, int max_active, ...);#define alloc_workqueue (...) alloc_hooks(alloc_workqueue_noprof(__VA_ARGS__)) +/** +* devm_alloc_workqueue - Resource-managed allocate a workqueue +* @dev: Device to allocate workqueue for +* @fmt: printf format for the name of the workqueue +* @flags: WQ_* flags +* @max_active: max in-flight work items, 0 for default +* @...: args for @fmt +* +* Resource managed workqueue, see alloc_workqueue() for details. +* +* The workqueue will be automatically destroyed on driver detach. Typically +* this should be used in drivers already relying on devm interafaces. +* +* RETURNS: +* Pointer to the allocated workqueue on success, %NULL on failure. +*/ +__printf(2, 5) struct workqueue_struct * +devm_alloc_workqueue (struct device *dev,const char *fmt,unsigned int flags, +int max_active, ...); +#ifdef CONFIG_LOCKDEP /** * alloc_workqueue_lockdep_map - allocate a workqueue with user-defined lockdep_map
@@ -568,6 +588,8 @@ alloc_workqueue_lockdep_map(const char *fmt, unsigned int flags, int max_active,*/ #define alloc_ordered_workqueue(fmt, flags, args...) \ alloc_workqueue(fmt, WQ_UNBOUND | __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)#define create_workqueue (name) \ alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_PERCPU, 1, (name))
kernel/workqueue.c +28 -0
@@ -41,6 +41,7 @@#include <linux/mempolicy.h> #include <linux/freezer.h> #include <linux/debug_locks.h> +#include <linux/device/devres.h> #include <linux/lockdep.h> #include <linux/idr.h> #include <linux/jhash.h>
@@ -5909,6 +5910,33 @@ struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, }EXPORT_SYMBOL_GPL (alloc_workqueue_noprof); +static void devm_workqueue_release (void *res) +{ +destroy_workqueue (res); +} + +__printf(2, 5) struct workqueue_struct * +devm_alloc_workqueue (struct device *dev,const char *fmt,unsigned int flags, +int max_active, ...) +{ +struct workqueue_struct *wq; +va_list args; +int ret; + +va_start (args, max_active); + wq =alloc_workqueue (fmt, flags, max_active, args); +va_end (args); +if (!wq) +return NULL ; + + ret =devm_add_action_or_reset (dev, devm_workqueue_release, wq); +if (ret) +return NULL ; + +return wq; +} +EXPORT_SYMBOL_GPL (devm_alloc_workqueue); +#ifdef CONFIG_LOCKDEP __printf(1, 5) struct workqueue_struct *
Signature · 1 line
2.51.0
Re: [PATCH v2 01/10] workqueue: devres: Add device-managed allocate workqueue
Quoted 18 lines · Krzysztof Kozlowski
Add a Resource-managed version of
alloc_workqueue()to fix common problem of drivers mixingdevm()calls withdestroy_workqueue. Such naive and discouraged driver approach leads to difficult to debug bugs when the driver:1. Allocates workqueue in standard way and destroys it in driver
remove()callback,
2. Sets work struct withdevm_work_autocancel(),
3. Registers interrupt handler withdevm_request_threaded_irq().Which leads to following unbind/removal path:
1.
destroy_workqueue()via driverremove(),
Any interrupt coming now would still execute the interrupt handler, which queues work on destroyed workqueue.
2.devm_irq_release(),
3.devm_work_drop()->cancel_work_sync()on destroyed workqueue.
Quoted 7 lines
devm_alloc_workqueue()has two benefits:
1. Solves above problem of mix-and-match devres and non-devres code in driver,
2. Simplify any sane drivers which were correctly usingalloc_workqueue()+devm_add_action_or_reset().Signed-off-by Krzysztof Kozlowski <[email protected]>
Please let me know how you wanna route the patch.
Thanks.
Signature · 1 line
tejun
Re: [PATCH v2 01/10] workqueue: devres: Add device-managed allocate workqueue
Quoted 25 lines · Tejun Heo
Quoted 24 lines · Krzysztof Kozlowski
Add a Resource-managed version of
alloc_workqueue()to fix common problem of drivers mixingdevm()calls withdestroy_workqueue. Such naive and discouraged driver approach leads to difficult to debug bugs when the driver:1. Allocates workqueue in standard way and destroys it in driver
remove()callback,
2. Sets work struct withdevm_work_autocancel(),
3. Registers interrupt handler withdevm_request_threaded_irq().Which leads to following unbind/removal path:
1.
destroy_workqueue()via driverremove(),
Any interrupt coming now would still execute the interrupt handler, which queues work on destroyed workqueue.
2.devm_irq_release(),
3.devm_work_drop()->cancel_work_sync()on destroyed workqueue.
devm_alloc_workqueue()has two benefits:
1. Solves above problem of mix-and-match devres and non-devres code in driver,
2. Simplify any sane drivers which were correctly usingalloc_workqueue()+devm_add_action_or_reset().
Quoted 5 lines
Quoted 1 line
Signed-off-by Krzysztof Kozlowski <[email protected]>Acked-by Tejun Heo <[email protected]>Please let me know how you wanna route the patch.
Like I described in cover letter, so I propose this going via one tree, e.g. power supply. The first patch might be needed for other trees as well, e.g. if more drivers are discovered, so the best if it is on dedicated branch in case it has to be shared just in case.
Does this look reasonable @Tejun, @Sebastian?
Best regards,
Krzysztof
Tejun Heo
maintainerRe: [PATCH v2 01/10] workqueue: devres: Add device-managed allocate workqueue
Hello,
Applied the first patch to a dedicated branch. Please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-7.1-devm-alloc-wq
Thanks.
--
tejun
PATCH v2 2/10 power: supply: cw2015: Free allocated workqueue
In reply to Krzysztof KozlowskiUse devm interface so allocated workqueue will be freed during device removal and error paths, thus fixing a memory leak.
Change is not equivalent in the workqueue itself: use non-legacy API which does not set (__WQ_LEGACY | WQ_MEM_RECLAIM). The workqueue is used to read updated data from the battery, thus there is no point to run it for memory reclaim.
---
Depends on devm_xxx() from earlier patches.
Changes in v2:
1. Use devm_alloc_ordered_workqueue(), mention this in commit msg
--- drivers/power/supply/cw2015_battery.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/power/supply/cw2015_battery.c +2 -1
@@ -694,7 +694,8 @@ static int cw_bat_probe(struct i2c_client *client) "No monitored battery, some properties will be missing\n"); } - cw_bat->battery_workqueue =create_singlethread_workqueue ("rk_battery" ); + cw_bat->battery_workqueue =devm_alloc_ordered_workqueue (&client->dev, +"rk_battery" ,0 );if (!cw_bat->battery_workqueue)return -ENOMEM ;
Signature · 1 line
2.51.0
PATCH v2 3/10 power: supply: max77705: Drop duplicated IRQ error message
In reply to Krzysztof KozlowskiCore already prints error message on devm_request_threaded_irq() failure, so no need to do that second time.
---
Changes in v2:
1. New patch
--- drivers/power/supply/max77705_charger.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
drivers/power/supply/max77705_charger.c +2 -6
@@ -666,19 +666,15 @@ static int max77705_charger_probe(struct i2c_client *i2c)NULL , max77705_chgin_irq,IRQF_TRIGGER_NONE , "chgin-irq", chg); -if (ret) { -dev_err_probe (dev, ret,"Failed to Request chgin IRQ\n" ); +if (ret) goto destroy_wq; - } ret =devm_request_threaded_irq (dev,regmap_irq_get_virq (irq_data,MAX77705_AICL_I ),NULL , max77705_aicl_irq,IRQF_TRIGGER_NONE ,"aicl-irq" , chg); -if (ret) { -dev_err_probe (dev, ret,"Failed to Request aicl IRQ\n" ); +if (ret) goto destroy_wq; - } ret =max77705_charger_enable (chg);if (ret) {
Signature · 1 line
2.51.0
PATCH v2 4/10 power: supply: max77705: Free allocated workqueue and fix removal order
In reply to Krzysztof KozlowskiUse devm interface for allocating workqueue to fix two bugs at the same time:
1. Driver leaks the memory on remove(), because the workqueue is not destroyed.
2. Driver allocates workqueue and then registers interrupt handlers with devm interface. This means that probe error paths will not use a reversed order, but first destroy the workqueue and then, via devm release handlers, free the interrupt.
The interrupt handler schedules work on this exact workqueue, thus if interrupt is hit in this short time window - after destroying workqueue, but before devm() frees the interrupt - the schedulled work will lead to use of freed memory.
Change is not equivalent in the workqueue itself: use non-legacy API which does not set (__WQ_LEGACY | WQ_MEM_RECLAIM). The workqueue is used to update power supply (power_supply_changed()) status, thus there is no point to run it for memory reclaim. Note that dev_name() is not directly used in second argument to prevent possible unlikely parsing any "%" character in device name as format.
---
Changes in v2:
1. Use devm_alloc_ordered_workqueue(), mention this in commit msg
--- drivers/power/supply/max77705_charger.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-)
drivers/power/supply/max77705_charger.c +9 -19
@@ -646,47 +646,37 @@ static int max77705_charger_probe(struct i2c_client *i2c)if (ret)return dev_err_probe (dev, ret,"failed to add irq chip\n" ); - chg->wqueue =create_singlethread_workqueue (dev_name (dev)); + chg->wqueue =devm_alloc_ordered_workqueue (dev,"%s" ,0 ,dev_name (dev));if (!chg->wqueue)return -ENOMEM ; ret =devm_work_autocancel (dev, &chg->chgin_work, max77705_chgin_isr_work); -if (ret) { -dev_err_probe (dev, ret,"failed to initialize interrupt work\n" ); - goto destroy_wq; - } +if (ret) +return dev_err_probe (dev, ret,"failed to initialize interrupt work\n" ); ret =max77705_charger_initialize (chg); -if (ret) { -dev_err_probe (dev, ret,"failed to initialize charger IC\n" ); - goto destroy_wq; - } +if (ret) +return dev_err_probe (dev, ret,"failed to initialize charger IC\n" ); ret =devm_request_threaded_irq (dev,regmap_irq_get_virq (irq_data,MAX77705_CHGIN_I ),NULL , max77705_chgin_irq,IRQF_TRIGGER_NONE ,"chgin-irq" , chg);if (ret) - goto destroy_wq; +return ret; ret =devm_request_threaded_irq (dev,regmap_irq_get_virq (irq_data,MAX77705_AICL_I ),NULL , max77705_aicl_irq,IRQF_TRIGGER_NONE ,"aicl-irq" , chg);if (ret) - goto destroy_wq; +return ret; ret =max77705_charger_enable (chg); -if (ret) { -dev_err_probe (dev, ret,"failed to enable charge\n" ); - goto destroy_wq; - } +if (ret) +return dev_err_probe (dev, ret,"failed to enable charge\n" );return devm_add_action_or_reset (dev, max77705_charger_disable, chg); - -destroy_wq: -destroy_workqueue (chg->wqueue); -return ret; }static const struct of_device_id max77705_charger_of_match[] = {
Signature · 1 line
2.51.0
PATCH v2 5/10 power: supply: mt6370: Simplify with devm_alloc_ordered_workqueue()
In reply to Krzysztof KozlowskiSimplify the driver probe function by using devm_alloc_ordered_workqueue() which handles the cleanup already.
Change is not equivalent in the workqueue itself: use non-legacy API which does not set (__WQ_LEGACY | WQ_MEM_RECLAIM). The workqueue is used to update power supply data (power_supply_changed()) status, thus there is no point to run it for memory reclaim. Note that dev_name() is not directly used in second argument to prevent possible unlikely parsing any "%" character in device name as format.
---
Changes in v2:
1. Use devm_alloc_ordered_workqueue(), mention this in commit msg
--- drivers/power/supply/mt6370-charger.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-)
drivers/power/supply/mt6370-charger.c +1 -12
@@ -761,13 +761,6 @@ static int mt6370_chg_init_psy(struct mt6370_priv *priv)return PTR_ERR_OR_ZERO (priv->psy); } -static void mt6370_chg_destroy_wq (void *data) -{ -struct workqueue_struct *wq = data; - -destroy_workqueue (wq); -} -static irqreturn_t mt6370_attach_i_handler (int irq,void *data) {struct mt6370_priv *priv = data;
@@ -893,14 +886,10 @@ static int mt6370_chg_probe(struct platform_device *pdev) priv->attach =MT6370_ATTACH_STAT_DETACH ; - priv->wq =create_singlethread_workqueue (dev_name (priv->dev)); + priv->wq =devm_alloc_ordered_workqueue (dev,"%s" ,0 ,dev_name (priv->dev));if (!priv->wq)return -ENOMEM ; - ret =devm_add_action_or_reset (dev, mt6370_chg_destroy_wq, priv->wq); -if (ret) -return ret; - ret =devm_work_autocancel (dev, &priv->bc12_work, mt6370_chg_bc12_work_func);if (ret)return dev_err_probe (dev, ret,"Failed to init bc12 work\n" );
Signature · 1 line
2.51.0
PATCH v2 6/10 power: supply: ipaq_micro: Simplify with devm
In reply to Krzysztof KozlowskiSimplify the driver by using devm interfaces, which allow to drop probe() error paths and the remove() callback.
--- drivers/power/supply/ipaq_micro_battery.c | 50 ++++++++++--------------------- 1 file changed, 16 insertions(+), 34 deletions(-)
drivers/power/supply/ipaq_micro_battery.c +16 -34
@@ -7,6 +7,7 @@* Author : Linus Walleij <[email protected]> */ +#include <linux/devm-helpers.h> #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h>
@@ -232,49 +233,31 @@ static int micro_batt_probe(struct platform_device *pdev)return -ENOMEM ; mb->micro =dev_get_drvdata (pdev->dev.parent); - mb->wq =alloc_workqueue ("ipaq-battery-wq" , -WQ_MEM_RECLAIM |WQ_PERCPU ,0 ); + mb->wq =devm_alloc_workqueue (&pdev->dev,"ipaq-battery-wq" , +WQ_MEM_RECLAIM |WQ_PERCPU ,0 );if (!mb->wq)return -ENOMEM ; -INIT_DELAYED_WORK (&mb->update, micro_battery_work); + ret =devm_delayed_work_autocancel (&pdev->dev, &mb->update, micro_battery_work); +if (ret) +return ret; +platform_set_drvdata (pdev, mb);queue_delayed_work (mb->wq, &mb->update,1 ); - micro_batt_power =power_supply_register (&pdev->dev, - µ_batt_power_desc,NULL ); -if (IS_ERR (micro_batt_power)) { - ret =PTR_ERR (micro_batt_power); - goto batt_err; - } + micro_batt_power =devm_power_supply_register (&pdev->dev, + µ_batt_power_desc, +NULL ); +if (IS_ERR (micro_batt_power)) +return PTR_ERR (micro_batt_power); - micro_ac_power =power_supply_register (&pdev->dev, - µ_ac_power_desc,NULL ); -if (IS_ERR (micro_ac_power)) { - ret =PTR_ERR (micro_ac_power); - goto ac_err; - } + micro_ac_power =devm_power_supply_register (&pdev->dev, + µ_ac_power_desc,NULL ); +if (IS_ERR (micro_ac_power)) +return PTR_ERR (micro_ac_power);dev_info (&pdev->dev,"iPAQ micro battery driver\n" );return 0 ; - -ac_err: -power_supply_unregister (micro_batt_power); -batt_err: -cancel_delayed_work_sync (&mb->update); -destroy_workqueue (mb->wq); -return ret; -} - -static void micro_batt_remove (struct platform_device *pdev) - -{ -struct micro_battery *mb =platform_get_drvdata (pdev); - -power_supply_unregister (micro_ac_power); -power_supply_unregister (micro_batt_power); -cancel_delayed_work_sync (&mb->update); -destroy_workqueue (mb->wq); }static int __maybe_unused micro_batt_suspend (struct device *dev)
@@ -303,7 +286,6 @@ static struct platform_driver micro_batt_device_driver = { .pm = µ_batt_dev_pm_ops, }, .probe = micro_batt_probe, - .remove = micro_batt_remove, };module_platform_driver (micro_batt_device_driver);
Signature · 1 line
2.51.0
PATCH v2 7/10 mfd: ezx-pcap: Drop memory allocation error message
In reply to Krzysztof KozlowskiDrivers should not print error messages on memory allocation failures, because core already does it.
---
Further patch depends on this one, thus it should not be picked separately.
--- drivers/mfd/ezx-pcap.c | 1 - 1 file changed, 1 deletion(-)
drivers/mfd/ezx-pcap.c +0 -1
@@ -416,7 +416,6 @@ static int ezx_pcap_probe(struct spi_device *spi) pcap->workqueue =create_singlethread_workqueue ("pcapd" );if (!pcap->workqueue) { ret = -ENOMEM ; -dev_err (&spi->dev,"can't create pcap thread\n" ); goto ret; }
Signature · 1 line
2.51.0
PATCH v2 8/10 mfd: ezx-pcap: Return directly instead of empty gotos
In reply to Krzysztof KozlowskiCode is easier to read if empty error paths simply return, instead of jumping to empty label doing only "return ret".
--- drivers/mfd/ezx-pcap.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
drivers/mfd/ezx-pcap.c +7 -11
@@ -384,17 +384,15 @@ static int ezx_pcap_probe(struct spi_device *spi)struct pcap_platform_data *pdata =dev_get_platdata (&spi->dev);struct pcap_chip *pcap;int i, adc_irq; -int ret = -ENODEV ; +int ret;/* platform data is required */ if (!pdata) - goto ret; +return -ENODEV ; pcap =devm_kzalloc (&spi->dev,sizeof (*pcap),GFP_KERNEL ); -if (!pcap) { - ret = -ENOMEM ; - goto ret; - } +if (!pcap) +return -ENOMEM ;spin_lock_init (&pcap->io_lock);spin_lock_init (&pcap->adc_lock);
@@ -407,17 +405,15 @@ static int ezx_pcap_probe(struct spi_device *spi) spi->mode =SPI_MODE_0 | (pdata->config &PCAP_CS_AH ?SPI_CS_HIGH :0 ); ret =spi_setup (spi);if (ret) - goto ret; +return ret; pcap->spi = spi;/* setup irq */ pcap->irq_base = pdata->irq_base; pcap->workqueue =create_singlethread_workqueue ("pcapd" ); -if (!pcap->workqueue) { - ret = -ENOMEM ; - goto ret; - } +if (!pcap->workqueue) +return -ENOMEM ;/* redirect interrupts to AP, except adcdone2 */ if (!(pdata->config &PCAP_SECOND_PORT ))
Signature · 1 line
2.51.0
PATCH v2 9/10 mfd: ezx-pcap: Avoid rescheduling after destroying workqueue
In reply to Krzysztof KozlowskiDriver allocates workqueue and then registers additional interrupt handler with devm interface. This means that device removal will not use a reversed order, but first destroy workqueue and then, via devm release handlers, free the interrupt.
The interrupt handler registered with devm does not directly use/schedule work items on the workqueue and the remove() function correctly removes other IRQs handlers, however the code mixing devm and non-devm interfaces is difficult to analyze and read.
Make the code flow much more obvious by using devm interface for allocating the workqueue, so it will be freed with the rest of devm resources.
Change is not equivalent in the workqueue itself: use non-legacy API which does not set (__WQ_LEGACY | WQ_MEM_RECLAIM). The workqueue is used to update device registers, thus there is no point to run it for memory reclaim.
---
Depends on devm_xxx() from earlier patches.
Changes in v2:
1. Use devm_alloc_ordered_workqueue(), mention this in commit msg
--- drivers/mfd/ezx-pcap.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
drivers/mfd/ezx-pcap.c +2 -6
@@ -375,8 +375,6 @@ static void ezx_pcap_remove(struct spi_device *spi)/* cleanup irqchip */ for (i = pcap->irq_base; i < (pcap->irq_base +PCAP_NIRQS ); i++)irq_set_chip_and_handler (i,NULL ,NULL ); - -destroy_workqueue (pcap->workqueue); }static int ezx_pcap_probe (struct spi_device *spi)
@@ -411,7 +409,7 @@ static int ezx_pcap_probe(struct spi_device *spi)/* setup irq */ pcap->irq_base = pdata->irq_base; - pcap->workqueue =create_singlethread_workqueue ("pcapd" ); + pcap->workqueue =devm_alloc_ordered_workqueue (&spi->dev,"pcapd" ,0 );if (!pcap->workqueue)return -ENOMEM ;
@@ -463,9 +461,7 @@ static int ezx_pcap_probe(struct spi_device *spi) free_irqchip:for (i = pcap->irq_base; i < (pcap->irq_base +PCAP_NIRQS ); i++)irq_set_chip_and_handler (i,NULL ,NULL ); -/* destroy_workqueue: */ -destroy_workqueue (pcap->workqueue); -ret: +return ret; }
Signature · 1 line
2.51.0
PATCH v2 10/10 platform/chrome: cros_usbpd_logger: Simplify with devm
In reply to Krzysztof KozlowskiSimplify the driver by using devm interfaces, which allow to drop probe() error paths and the remove() callback.
Change is not equivalent in the workqueue itself: use non-legacy API which does not set (__WQ_LEGACY | WQ_MEM_RECLAIM). The workqueue is used to update logs, thus there is no point to run it for memory reclaim.
---
Changes in v2:
1. Use devm_alloc_ordered_workqueue(), mention this in commit msg
--- drivers/platform/chrome/cros_usbpd_logger.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-)
drivers/platform/chrome/cros_usbpd_logger.c +7 -11
@@ -5,6 +5,7 @@* Copyright 2018 Google LLC. */ +#include <linux/devm-helpers.h> #include <linux/ktime.h> #include <linux/math64.h> #include <linux/mod_devicetable.h>
@@ -199,6 +200,7 @@ static int cros_usbpd_logger_probe(struct platform_device *pd)struct cros_ec_dev *ec_dev =dev_get_drvdata (pd->dev.parent);struct device *dev = &pd->dev;struct logger_data *logger; +int ret; logger =devm_kzalloc (dev,sizeof (*logger),GFP_KERNEL );if (!logger)
@@ -210,25 +212,20 @@ static int cros_usbpd_logger_probe(struct platform_device *pd)platform_set_drvdata (pd, logger);/* Retrieve PD event logs periodically */ -INIT_DELAYED_WORK (&logger->log_work, cros_usbpd_log_check); - logger->log_workqueue =create_singlethread_workqueue ("cros_usbpd_log" ); + logger->log_workqueue =devm_alloc_ordered_workqueue (dev,"cros_usbpd_log" ,0 );if (!logger->log_workqueue)return -ENOMEM ; + ret =devm_delayed_work_autocancel (dev, &logger->log_work, cros_usbpd_log_check); +if (ret) +return ret; +queue_delayed_work (logger->log_workqueue, &logger->log_work,CROS_USBPD_LOG_UPDATE_DELAY );return 0 ; } -static void cros_usbpd_logger_remove (struct platform_device *pd) -{ -struct logger_data *logger =platform_get_drvdata (pd); - -cancel_delayed_work_sync (&logger->log_work); -destroy_workqueue (logger->log_workqueue); -} -static int __maybe_unused cros_usbpd_logger_resume (struct device *dev) {struct logger_data *logger =dev_get_drvdata (dev);
@@ -263,7 +260,6 @@ static struct platform_driver cros_usbpd_logger_driver = { .pm = &cros_usbpd_logger_pm_ops, }, .probe = cros_usbpd_logger_probe, - .remove = cros_usbpd_logger_remove, .id_table = cros_usbpd_logger_id, };
Signature · 1 line
2.51.0
Re: [PATCH v2 10/10] platform/chrome: cros_usbpd_logger: Simplify with devm
Quoted 9 lines · Krzysztof Kozlowski
Simplify the driver by using devm interfaces, which allow to drop
probe()error paths and theremove()callback.Change is not equivalent in the workqueue itself: use non-legacy API which does not set (
__WQ_LEGACY|WQ_MEM_RECLAIM). The workqueue is used to update logs, thus there is no point to run it for memory reclaim.Signed-off-by Krzysztof Kozlowski <[email protected]>
Quoted 32 lines · Krzysztof Kozlowski
Merging / Dependency
====================
All further patches depend on the first one, thus this probably should go via one tree, e.g. power supply. The first patch might be needed for other trees as well, e.g. if more drivers are discovered, so the best if it is on dedicated branch in case it has to be shared.Changes in v2:
==============
- See individual patches
- Link to v1: https://patch.msgid.link/[email protected]Description
===========
Add a Resource-managed version ofalloc_workqueue()to fix common problem of drivers mixingdevm()calls withdestroy_workqueue. Such naive and discouraged driver approach leads to difficult to debug bugs when the driver:1. Allocates workqueue in standard way and destroys it in driver
remove()callback,
2. Sets work struct withdevm_work_autocancel(),
3. Registers interrupt handler withdevm_request_threaded_irq().Which leads to following unbind/removal path:
1.
destroy_workqueue()via driverremove(),
Any interrupt coming now would still execute the interrupt handler, which queues work on destroyed workqueue.
2.devm_irq_release(),
3.devm_work_drop()->cancel_work_sync()on destroyed workqueue.
Quoted 5 lines
devm_alloc_workqueue()has two benefits:
1. Solves above problem of mix-and-match devres and non-devres code in driver,
2. Simplify any sane drivers which were correctly usingalloc_workqueue()+devm_add_action_or_reset().
Thanks, this version LGTM, FWIW,
Signature · 2 lines
With Best Regards, Andy Shevchenko
Sebastian Reichel
AppliedRe: (subset) [PATCH v2 00/10] workqueue / drivers: Add device-managed allocate workqueue
In reply to Krzysztof KozlowskiQuoted 8 lines · Krzysztof Kozlowski
Merging / Dependency
====================
All further patches depend on the first one, thus this probably should go via one tree, e.g. power supply. The first patch might be needed for other trees as well, e.g. if more drivers are discovered, so the best if it is on dedicated branch in case it has to be shared.[...]
Applied, thanks!
[02/10] power: supply: cw2015: Free allocated workqueue
commit: db254b0b232358ab1aeadebe8d147c99a3569559
[03/10] power: supply: max77705: Drop duplicated IRQ error message
commit: 2064c64ceb1996ee02a6bbb1de05fd6e8028e3e4
[04/10] power: supply: max77705: Free allocated workqueue and fix removal order
commit: 1e668baadefb16e81269dbfebf3ffc2672e3a3bb
[05/10] power: supply: mt6370: Simplify with devm_alloc_ordered_workqueue()
commit: f23afa01040a41882a048e4957a7acac1426da6f
[06/10] power: supply: ipaq_micro: Simplify with devm
commit: 2cfc7cac68e19c4acb236b8db6065bbaff5deee8Best regards,
Signature · 1 line
Sebastian Reichel <[email protected]>
Re: (subset) [PATCH v2 00/10] workqueue / drivers: Add device-managed allocate workqueue
In reply to Krzysztof KozlowskiQuoted 8 lines · Krzysztof Kozlowski
Merging / Dependency
====================
All further patches depend on the first one, thus this probably should go via one tree, e.g. power supply. The first patch might be needed for other trees as well, e.g. if more drivers are discovered, so the best if it is on dedicated branch in case it has to be shared.[...]
Applied, thanks!
[07/10] mfd: ezx-pcap: Drop memory allocation error message
commit: 33e0316783a205625b7c55a78041ddc0d5dce7c7
[08/10] mfd: ezx-pcap: Return directly instead of empty gotos
commit: 444e11d9d9e56c994da8a253cdf7f33ac2eeb15b
[09/10] mfd: ezx-pcap: Avoid rescheduling after destroying workqueue
commit: 356ee03f6ae7d04f90d8e2104660193c4f3a071c--
Lee Jones [李琼斯]
Tzung-Bi Shih
maintainerRe: (subset) [PATCH v2 00/10] workqueue / drivers: Add device-managed allocate workqueue
In reply to Krzysztof KozlowskiQuoted 8 lines · Krzysztof Kozlowski
Merging / Dependency
====================
All further patches depend on the first one, thus this probably should go via one tree, e.g. power supply. The first patch might be needed for other trees as well, e.g. if more drivers are discovered, so the best if it is on dedicated branch in case it has to be shared.[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git for-next
[10/10] platform/chrome: cros_usbpd_logger: Simplify with devm
commit: 168e4b208ca8c2e04de20cc6cb7e2fb035dc1ec8
Thanks!