mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 12:21:37 +00:00
scsi: drivers: base: Support atomic version of attribute_container_device_trigger
attribute_container_device_trigger invokes callbacks that may fail for one or more classdevs, for instance, the transport_add_class_device callback, called during transport creation, does memory allocation. This information, though, is not propagated to upper layers, and any driver using the attribute_container_device_trigger API will not know whether any, some, or all callbacks succeeded. This patch implements a safe version of this dispatcher, to either succeed all the callbacks or revert to the original state. Link: https://lore.kernel.org/r/20200106185817.640331-2-krisman@collabora.com Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
54155ed419
commit
7c1ef33870
@ -236,6 +236,109 @@ attribute_container_remove_device(struct device *dev,
|
||||
mutex_unlock(&attribute_container_mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
do_attribute_container_device_trigger_safe(struct device *dev,
|
||||
struct attribute_container *cont,
|
||||
int (*fn)(struct attribute_container *,
|
||||
struct device *, struct device *),
|
||||
int (*undo)(struct attribute_container *,
|
||||
struct device *, struct device *))
|
||||
{
|
||||
int ret;
|
||||
struct internal_container *ic, *failed;
|
||||
struct klist_iter iter;
|
||||
|
||||
if (attribute_container_no_classdevs(cont))
|
||||
return fn(cont, dev, NULL);
|
||||
|
||||
klist_for_each_entry(ic, &cont->containers, node, &iter) {
|
||||
if (dev == ic->classdev.parent) {
|
||||
ret = fn(cont, dev, &ic->classdev);
|
||||
if (ret) {
|
||||
failed = ic;
|
||||
klist_iter_exit(&iter);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (!undo)
|
||||
return ret;
|
||||
|
||||
/* Attempt to undo the work partially done. */
|
||||
klist_for_each_entry(ic, &cont->containers, node, &iter) {
|
||||
if (ic == failed) {
|
||||
klist_iter_exit(&iter);
|
||||
break;
|
||||
}
|
||||
if (dev == ic->classdev.parent)
|
||||
undo(cont, dev, &ic->classdev);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* attribute_container_device_trigger_safe - execute a trigger for each
|
||||
* matching classdev or fail all of them.
|
||||
*
|
||||
* @dev: The generic device to run the trigger for
|
||||
* @fn the function to execute for each classdev.
|
||||
* @undo A function to undo the work previously done in case of error
|
||||
*
|
||||
* This function is a safe version of
|
||||
* attribute_container_device_trigger. It stops on the first error and
|
||||
* undo the partial work that has been done, on previous classdev. It
|
||||
* is guaranteed that either they all succeeded, or none of them
|
||||
* succeeded.
|
||||
*/
|
||||
int
|
||||
attribute_container_device_trigger_safe(struct device *dev,
|
||||
int (*fn)(struct attribute_container *,
|
||||
struct device *,
|
||||
struct device *),
|
||||
int (*undo)(struct attribute_container *,
|
||||
struct device *,
|
||||
struct device *))
|
||||
{
|
||||
struct attribute_container *cont, *failed = NULL;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&attribute_container_mutex);
|
||||
|
||||
list_for_each_entry(cont, &attribute_container_list, node) {
|
||||
|
||||
if (!cont->match(cont, dev))
|
||||
continue;
|
||||
|
||||
ret = do_attribute_container_device_trigger_safe(dev, cont,
|
||||
fn, undo);
|
||||
if (ret) {
|
||||
failed = cont;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret && !WARN_ON(!undo)) {
|
||||
list_for_each_entry(cont, &attribute_container_list, node) {
|
||||
|
||||
if (failed == cont)
|
||||
break;
|
||||
|
||||
if (!cont->match(cont, dev))
|
||||
continue;
|
||||
|
||||
do_attribute_container_device_trigger_safe(dev, cont,
|
||||
undo, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&attribute_container_mutex);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* attribute_container_device_trigger - execute a trigger for each matching classdev
|
||||
*
|
||||
|
@ -54,6 +54,13 @@ void attribute_container_device_trigger(struct device *dev,
|
||||
int (*fn)(struct attribute_container *,
|
||||
struct device *,
|
||||
struct device *));
|
||||
int attribute_container_device_trigger_safe(struct device *dev,
|
||||
int (*fn)(struct attribute_container *,
|
||||
struct device *,
|
||||
struct device *),
|
||||
int (*undo)(struct attribute_container *,
|
||||
struct device *,
|
||||
struct device *));
|
||||
void attribute_container_trigger(struct device *dev,
|
||||
int (*fn)(struct attribute_container *,
|
||||
struct device *));
|
||||
|
Loading…
Reference in New Issue
Block a user