diff options
Diffstat (limited to 'drivers/base/attribute_container.c')
| -rw-r--r-- | drivers/base/attribute_container.c | 171 |
1 files changed, 116 insertions, 55 deletions
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index ecc1929d7f6a..b6f941a6ab69 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * attribute_container.c - implementation of a simple container for classes * * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com> * - * This file is licensed under GPLv2 - * * The basic idea here is to enable a device to be attached to an * aritrary numer of classes without having to allocate storage for them. * Instead, the contained classes select the devices they need to attach @@ -12,7 +11,6 @@ */ #include <linux/attribute_container.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -75,9 +73,9 @@ int attribute_container_register(struct attribute_container *cont) { INIT_LIST_HEAD(&cont->node); - klist_init(&cont->containers,internal_container_klist_get, + klist_init(&cont->containers, internal_container_klist_get, internal_container_klist_put); - + mutex_lock(&attribute_container_mutex); list_add_tail(&cont->node, &attribute_container_list); mutex_unlock(&attribute_container_mutex); @@ -95,6 +93,7 @@ int attribute_container_unregister(struct attribute_container *cont) { int retval = -EBUSY; + mutex_lock(&attribute_container_mutex); spin_lock(&cont->containers.k_lock); if (!list_empty(&cont->containers.k_list)) @@ -105,14 +104,14 @@ attribute_container_unregister(struct attribute_container *cont) spin_unlock(&cont->containers.k_lock); mutex_unlock(&attribute_container_mutex); return retval; - + } EXPORT_SYMBOL_GPL(attribute_container_unregister); /* private function used as class release */ static void attribute_container_release(struct device *classdev) { - struct internal_container *ic + struct internal_container *ic = container_of(classdev, struct internal_container, classdev); struct device *dev = classdev->parent; @@ -185,8 +184,8 @@ attribute_container_add_device(struct device *dev, struct klist_node *n = klist_next(iter); \ n ? container_of(n, typeof(*pos), member) : \ ({ klist_iter_exit(iter) ; NULL; }); \ - }) ) != NULL; ) - + })) != NULL;) + /** * attribute_container_remove_device - make device eligible for removal. @@ -237,68 +236,143 @@ 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 - execute a trigger for each matching classdev + * 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. + * @fn: the function to execute for each classdev. + * @undo: A function to undo the work previously done in case of error * - * This funcion is for executing a trigger when you need to know both - * the container and the classdev. If you only care about the - * container, then use attribute_container_trigger() instead. + * 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. */ -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 *)) { - struct attribute_container *cont; + struct attribute_container *cont, *failed = NULL; + int ret = 0; mutex_lock(&attribute_container_mutex); + list_for_each_entry(cont, &attribute_container_list, node) { - struct internal_container *ic; - struct klist_iter iter; if (!cont->match(cont, dev)) continue; - if (attribute_container_no_classdevs(cont)) { - fn(cont, dev, NULL); - continue; + ret = do_attribute_container_device_trigger_safe(dev, cont, + fn, undo); + if (ret) { + failed = cont; + break; } + } - klist_for_each_entry(ic, &cont->containers, node, &iter) { - if (dev == ic->classdev.parent) - fn(cont, dev, &ic->classdev); + 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_trigger - trigger a function for each matching container + * attribute_container_device_trigger - execute a trigger for each matching classdev * - * @dev: The generic device to activate the trigger for - * @fn: the function to trigger + * @dev: The generic device to run the trigger for + * @fn: the function to execute for each classdev. * - * This routine triggers a function that only needs to know the - * matching containers (not the classdev) associated with a device. - * It is more lightweight than attribute_container_device_trigger, so - * should be used in preference unless the triggering function - * actually needs to know the classdev. + * This function is for executing a trigger when you need to know both + * the container and the classdev. */ void -attribute_container_trigger(struct device *dev, - int (*fn)(struct attribute_container *, - struct device *)) +attribute_container_device_trigger(struct device *dev, + int (*fn)(struct attribute_container *, + struct device *, + struct device *)) { struct attribute_container *cont; mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { - if (cont->match(cont, dev)) - fn(cont, dev); + struct internal_container *ic; + struct klist_iter iter; + + if (!cont->match(cont, dev)) + continue; + + if (attribute_container_no_classdevs(cont)) { + fn(cont, dev, NULL); + continue; + } + + klist_for_each_entry(ic, &cont->containers, node, &iter) { + if (dev == ic->classdev.parent) + fn(cont, dev, &ic->classdev); + } } mutex_unlock(&attribute_container_mutex); } @@ -350,26 +424,13 @@ int attribute_container_add_class_device(struct device *classdev) { int error = device_add(classdev); + if (error) return error; return attribute_container_add_attrs(classdev); } /** - * attribute_container_add_class_device_adapter - simple adapter for triggers - * - * This function is identical to attribute_container_add_class_device except - * that it is designed to be called from the triggers - */ -int -attribute_container_add_class_device_adapter(struct attribute_container *cont, - struct device *dev, - struct device *classdev) -{ - return attribute_container_add_class_device(classdev); -} - -/** * attribute_container_remove_attrs - remove any attribute files * * @classdev: The class device to remove the files from |
