scsi: drivers: base: Support atomic version of attribute_container_device_trigger
[linux-2.6-microblaze.git] / drivers / base / attribute_container.c
index 20736aa..f7bd0f4 100644 (file)
@@ -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
  *