mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 04:42:12 +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);
|
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
|
* 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 *,
|
int (*fn)(struct attribute_container *,
|
||||||
struct device *,
|
struct device *,
|
||||||
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,
|
void attribute_container_trigger(struct device *dev,
|
||||||
int (*fn)(struct attribute_container *,
|
int (*fn)(struct attribute_container *,
|
||||||
struct device *));
|
struct device *));
|
||||||
|
Loading…
Reference in New Issue
Block a user