From: Ahmad Fatoum <[email protected]> Add devm_add_action() and devm_add_action_or_reset() on top of talloc_add_destructor() for drivers that need to undo things on remove.
As barebox calls the remove callbacks on shutdown, let's run the actions there as well, but keep the managed memory, which may still be referenced afterwards. Assisted-by: Claude:fable-5.1 Signed-off-by: Ahmad Fatoum <[email protected]> --- drivers/base/devres.c | 23 +++++++++++++++++++++++ drivers/base/driver.c | 1 + include/driver.h | 1 + include/linux/device.h | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 26abc47cd997..bf118ae0820d 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -36,6 +36,18 @@ void devres_release_all(struct device *dev) dev->devres = NULL; } +/** + * devres_run_actions() - run the managed actions of a device + * @dev: device that is shut down + * + * Like devres_release_all(), but keeps the managed memory, which may + * still be referenced after shutdown. + */ +void devres_run_actions(struct device *dev) +{ + talloc_run_destructors(dev->devres); +} + /* * Memory must be DMA-safe like kmalloc()'s, so align it within the chunk * and store the chunk pointer in front of it for devm_kfree(). @@ -145,3 +157,14 @@ char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...) return p; } EXPORT_SYMBOL(devm_kasprintf); + +int devm_add_action(struct device *dev, void (*action)(void *), void *data) +{ + void *ctx = devres_ctx(dev); + + if (!ctx) + return -ENOMEM; + + return talloc_add_destructor(ctx, action, data); +} +EXPORT_SYMBOL(devm_add_action); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index e214408e12a3..f87ecb200feb 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -720,6 +720,7 @@ static void devices_shutdown(void) pr_report_probe("%*sremove-> %s\n", 1 * 4, "", dev_name(dev)); remove(dev); } + devres_run_actions(dev); dev->driver = NULL; } } diff --git a/include/driver.h b/include/driver.h index 372b1297c6c4..c394c455b971 100644 --- a/include/driver.h +++ b/include/driver.h @@ -112,6 +112,7 @@ void free_device_res(struct device *dev); void free_device(struct device *dev); void devres_release_all(struct device *dev); +void devres_run_actions(struct device *dev); static inline void device_rescan(struct device *dev) { diff --git a/include/linux/device.h b/include/linux/device.h index 595abf412180..9b14bc74fef6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -101,6 +101,44 @@ static inline unsigned long *devm_bitmap_zalloc(struct device *dev, gfp); } +/** + * devm_add_action() - add a custom action to the managed resources + * @dev: device that owns the action + * @action: function to call when the device is unbound, unregistered + * or shut down + * @data: argument to pass to @action + * + * Actions run in reverse order of registration, interleaved with the + * freeing of managed memory. + * + * Return: 0 on success, -ENOMEM if the action could not be registered. + */ +int devm_add_action(struct device *dev, void (*action)(void *), void *data); + +/** + * devm_add_action_or_reset() - add a custom action, running it on failure + * @dev: device that owns the action + * @action: function to call when the device is unbound, unregistered + * or shut down + * @data: argument to pass to @action + * + * Like devm_add_action(), but @action is called right away if it could + * not be registered. + * + * Return: 0 on success, -ENOMEM if the action could not be registered. + */ +static inline int devm_add_action_or_reset(struct device *dev, + void (*action)(void *), void *data) +{ + int ret; + + ret = devm_add_action(dev, action, data); + if (ret) + action(data); + + return ret; +} + #define device_register register_device #define device_unregister unregister_device -- 2.47.3
