attribute_container_device_trigger invokes callbacks that may fail for
one or more classdev's, 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.

Signed-off-by: Gabriel Krisman Bertazi <[email protected]>
---
 drivers/base/attribute_container.c  | 103 ++++++++++++++++++++++++++++
 include/linux/attribute_container.h |   7 ++
 2 files changed, 110 insertions(+)

diff --git a/drivers/base/attribute_container.c 
b/drivers/base/attribute_container.c
index 20736aaa0e69..f7bd0f4db13d 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -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
  *
diff --git a/include/linux/attribute_container.h 
b/include/linux/attribute_container.h
index d12bb2153cd6..e4004d1e6725 100644
--- a/include/linux/attribute_container.h
+++ b/include/linux/attribute_container.h
@@ -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 *));
-- 
2.24.1

-- 
You received this message because you are subscribed to the Google Groups 
"open-iscsi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/open-iscsi/20200106185817.640331-2-krisman%40collabora.com.

Reply via email to