devres: allow adding custom actions to the stack
Sometimes drivers need to execute one-off actions in their error handling or device teardown paths. An example would be toggling a GPIO line to reset the controlled device into predefined state. To allow performing such actions when using managed resources let's allow adding them to stack/group of devres resources. Acked-by: Tejun Heo <tj@kernel.org> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
7241443f67
commit
d6b0c58048
@ -670,6 +670,80 @@ int devres_release_group(struct device *dev, void *id)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(devres_release_group);
|
EXPORT_SYMBOL_GPL(devres_release_group);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Custom devres actions allow inserting a simple function call
|
||||||
|
* into the teadown sequence.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct action_devres {
|
||||||
|
void *data;
|
||||||
|
void (*action)(void *);
|
||||||
|
};
|
||||||
|
|
||||||
|
static int devm_action_match(struct device *dev, void *res, void *p)
|
||||||
|
{
|
||||||
|
struct action_devres *devres = res;
|
||||||
|
struct action_devres *target = p;
|
||||||
|
|
||||||
|
return devres->action == target->action &&
|
||||||
|
devres->data == target->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devm_action_release(struct device *dev, void *res)
|
||||||
|
{
|
||||||
|
struct action_devres *devres = res;
|
||||||
|
|
||||||
|
devres->action(devres->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_add_action() - add a custom action to list of managed resources
|
||||||
|
* @dev: Device that owns the action
|
||||||
|
* @action: Function that should be called
|
||||||
|
* @data: Pointer to data passed to @action implementation
|
||||||
|
*
|
||||||
|
* This adds a custom action to the list of managed resources so that
|
||||||
|
* it gets executed as part of standard resource unwinding.
|
||||||
|
*/
|
||||||
|
int devm_add_action(struct device *dev, void (*action)(void *), void *data)
|
||||||
|
{
|
||||||
|
struct action_devres *devres;
|
||||||
|
|
||||||
|
devres = devres_alloc(devm_action_release,
|
||||||
|
sizeof(struct action_devres), GFP_KERNEL);
|
||||||
|
if (!devres)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
devres->data = data;
|
||||||
|
devres->action = action;
|
||||||
|
|
||||||
|
devres_add(dev, devres);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_add_action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_remove_action() - removes previously added custom action
|
||||||
|
* @dev: Device that owns the action
|
||||||
|
* @action: Function implementing the action
|
||||||
|
* @data: Pointer to data passed to @action implementation
|
||||||
|
*
|
||||||
|
* Removes instance of @action previously added by devm_add_action().
|
||||||
|
* Both action and data should match one of the existing entries.
|
||||||
|
*/
|
||||||
|
void devm_remove_action(struct device *dev, void (*action)(void *), void *data)
|
||||||
|
{
|
||||||
|
struct action_devres devres = {
|
||||||
|
.data = data,
|
||||||
|
.action = action,
|
||||||
|
};
|
||||||
|
|
||||||
|
WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match,
|
||||||
|
&devres));
|
||||||
|
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_remove_action);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Managed kzalloc/kfree
|
* Managed kzalloc/kfree
|
||||||
*/
|
*/
|
||||||
|
@ -567,6 +567,10 @@ extern void devm_kfree(struct device *dev, void *p);
|
|||||||
void __iomem *devm_request_and_ioremap(struct device *dev,
|
void __iomem *devm_request_and_ioremap(struct device *dev,
|
||||||
struct resource *res);
|
struct resource *res);
|
||||||
|
|
||||||
|
/* allows to add/remove a custom action to devres stack */
|
||||||
|
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
|
||||||
|
void devm_remove_action(struct device *dev, void (*action)(void *), void *data);
|
||||||
|
|
||||||
struct device_dma_parameters {
|
struct device_dma_parameters {
|
||||||
/*
|
/*
|
||||||
* a low level driver may set these to teach IOMMU code about
|
* a low level driver may set these to teach IOMMU code about
|
||||||
|
Loading…
Reference in New Issue
Block a user