From 9ed9895370aedd6032af2a9181c62c394d08223b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> Date: Sun, 30 Oct 2016 17:32:16 +0100 Subject: driver core: Functional dependencies tracking support Currently, there is a problem with taking functional dependencies between devices into account. What I mean by a "functional dependency" is when the driver of device B needs device A to be functional and (generally) its driver to be present in order to work properly. This has certain consequences for power management (suspend/resume and runtime PM ordering) and shutdown ordering of these devices. In general, it also implies that the driver of A needs to be working for B to be probed successfully and it cannot be unbound from the device before the B's driver. Support for representing those functional dependencies between devices is added here to allow the driver core to track them and act on them in certain cases where applicable. The argument for doing that in the driver core is that there are quite a few distinct use cases involving device dependencies, they are relatively hard to get right in a driver (if one wants to address all of them properly) and it only gets worse if multiplied by the number of drivers potentially needing to do it. Morever, at least one case (asynchronous system suspend/resume) cannot be handled in a single driver at all, because it requires the driver of A to wait for B to suspend (during system suspend) and the driver of B to wait for A to resume (during system resume). For this reason, represent dependencies between devices as "links", with the help of struct device_link objects each containing pointers to the "linked" devices, a list node for each of them, status information, flags, and an RCU head for synchronization. Also add two new list heads, representing the lists of links to the devices that depend on the given one (consumers) and to the devices depended on by it (suppliers), and a "driver presence status" field (needed for figuring out initial states of device links) to struct device. The entire data structure consisting of all of the lists of link objects for all devices is protected by a mutex (for link object addition/removal and for list walks during device driver probing and removal) and by SRCU (for list walking in other case that will be introduced by subsequent change sets). If CONFIG_SRCU is not selected, however, an rwsem is used for protecting the entire data structure. In addition, each link object has an internal status field whose value reflects whether or not drivers are bound to the devices pointed to by the link or probing/removal of their drivers is in progress etc. That field is only modified under the device links mutex, but it may be read outside of it in some cases (introduced by subsequent change sets), so modifications of it are annotated with WRITE_ONCE(). New links are added by calling device_link_add() which takes three arguments: pointers to the devices in question and flags. In particular, if DL_FLAG_STATELESS is set in the flags, the link status is not to be taken into account for this link and the driver core will not manage it. In turn, if DL_FLAG_AUTOREMOVE is set in the flags, the driver core will remove the link automatically when the consumer device driver unbinds from it. One of the actions carried out by device_link_add() is to reorder the lists used for device shutdown and system suspend/resume to put the consumer device along with all of its children and all of its consumers (and so on, recursively) to the ends of those lists in order to ensure the right ordering between all of the supplier and consumer devices. For this reason, it is not possible to create a link between two devices if the would-be supplier device already depends on the would-be consumer device as either a direct descendant of it or a consumer of one of its direct descendants or one of its consumers and so on. There are two types of link objects, persistent and non-persistent. The persistent ones stay around until one of the target devices is deleted, while the non-persistent ones are removed automatically when the consumer driver unbinds from its device (ie. they are assumed to be valid only as long as the consumer device has a driver bound to it). Persistent links are created by default and non-persistent links are created when the DL_FLAG_AUTOREMOVE flag is passed to device_link_add(). Both persistent and non-persistent device links can be deleted with an explicit call to device_link_del(). Links created without the DL_FLAG_STATELESS flag set are managed by the driver core using a simple state machine. There are 5 states each link can be in: DORMANT (unused), AVAILABLE (the supplier driver is present and functional), CONSUMER_PROBE (the consumer driver is probing), ACTIVE (both supplier and consumer drivers are present and functional), and SUPPLIER_UNBIND (the supplier driver is unbinding). The driver core updates the link state automatically depending on what happens to the linked devices and for each link state specific actions are taken in addition to that. For example, if the supplier driver unbinds from its device, the driver core will also unbind the drivers of all of its consumers automatically under the assumption that they cannot function properly without the supplier. Analogously, the driver core will only allow the consumer driver to bind to its device if the supplier driver is present and functional (ie. the link is in the AVAILABLE state). If that's not the case, it will rely on the existing deferred probing mechanism to wait for the supplier driver to become available. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- include/linux/device.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'include/linux/device.h') diff --git a/include/linux/device.h b/include/linux/device.h index bc41e87a969b..9cae2feaf5cb 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -707,6 +707,81 @@ struct device_dma_parameters { unsigned long segment_boundary_mask; }; +/** + * enum device_link_state - Device link states. + * @DL_STATE_NONE: The presence of the drivers is not being tracked. + * @DL_STATE_DORMANT: None of the supplier/consumer drivers is present. + * @DL_STATE_AVAILABLE: The supplier driver is present, but the consumer is not. + * @DL_STATE_CONSUMER_PROBE: The consumer is probing (supplier driver present). + * @DL_STATE_ACTIVE: Both the supplier and consumer drivers are present. + * @DL_STATE_SUPPLIER_UNBIND: The supplier driver is unbinding. + */ +enum device_link_state { + DL_STATE_NONE = -1, + DL_STATE_DORMANT = 0, + DL_STATE_AVAILABLE, + DL_STATE_CONSUMER_PROBE, + DL_STATE_ACTIVE, + DL_STATE_SUPPLIER_UNBIND, +}; + +/* + * Device link flags. + * + * STATELESS: The core won't track the presence of supplier/consumer drivers. + * AUTOREMOVE: Remove this link automatically on consumer driver unbind. + */ +#define DL_FLAG_STATELESS BIT(0) +#define DL_FLAG_AUTOREMOVE BIT(1) + +/** + * struct device_link - Device link representation. + * @supplier: The device on the supplier end of the link. + * @s_node: Hook to the supplier device's list of links to consumers. + * @consumer: The device on the consumer end of the link. + * @c_node: Hook to the consumer device's list of links to suppliers. + * @status: The state of the link (with respect to the presence of drivers). + * @flags: Link flags. + * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks. + */ +struct device_link { + struct device *supplier; + struct list_head s_node; + struct device *consumer; + struct list_head c_node; + enum device_link_state status; + u32 flags; +#ifdef CONFIG_SRCU + struct rcu_head rcu_head; +#endif +}; + +/** + * enum dl_dev_state - Device driver presence tracking information. + * @DL_DEV_NO_DRIVER: There is no driver attached to the device. + * @DL_DEV_PROBING: A driver is probing. + * @DL_DEV_DRIVER_BOUND: The driver has been bound to the device. + * @DL_DEV_UNBINDING: The driver is unbinding from the device. + */ +enum dl_dev_state { + DL_DEV_NO_DRIVER = 0, + DL_DEV_PROBING, + DL_DEV_DRIVER_BOUND, + DL_DEV_UNBINDING, +}; + +/** + * struct dev_links_info - Device data related to device links. + * @suppliers: List of links to supplier devices. + * @consumers: List of links to consumer devices. + * @status: Driver status information. + */ +struct dev_links_info { + struct list_head suppliers; + struct list_head consumers; + enum dl_dev_state status; +}; + /** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. @@ -799,6 +874,7 @@ struct device { core doesn't touch it */ void *driver_data; /* Driver data, set and get with dev_set/get_drvdata */ + struct dev_links_info links; struct dev_pm_info power; struct dev_pm_domain *pm_domain; @@ -1116,6 +1192,10 @@ extern void device_shutdown(void); /* debugging and troubleshooting/diagnostic helpers. */ extern const char *dev_driver_string(const struct device *dev); +/* Device links interface. */ +struct device_link *device_link_add(struct device *consumer, + struct device *supplier, u32 flags); +void device_link_del(struct device_link *link); #ifdef CONFIG_PRINTK -- cgit From 21d5c57b3726166421251e94dabab047baaf8ce4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> Date: Sun, 30 Oct 2016 17:32:31 +0100 Subject: PM / runtime: Use device links Modify the runtime PM framework to use device links to ensure that supplier devices will not be suspended if any of their consumer devices are active. The idea is to reference count suppliers on the consumer's resume and drop references to them on its suspend. The information on whether or not the supplier has been reference counted by the consumer's (runtime) resume is stored in a new field (rpm_active) in the link object for each link. It may be necessary to clean up those references when the supplier is unbinding and that's why the links whose status is DEVICE_LINK_SUPPLIER_UNBIND are skipped by the runtime suspend and resume code. The above means that if the consumer device is probed in the runtime-active state, the supplier has to be resumed and reference counted by device_link_add() so the code works as expected on its (runtime) suspend. There is a new flag, DEVICE_LINK_RPM_ACTIVE, to tell device_link_add() about that (in which case the caller is responsible for making sure that the consumer really will be runtime-active when runtime PM is enabled for it). The other new link flag, DEVICE_LINK_PM_RUNTIME, tells the core whether or not the link should be used for runtime PM at all. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- include/linux/device.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux/device.h') diff --git a/include/linux/device.h b/include/linux/device.h index 9cae2feaf5cb..49f453892ca5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -730,9 +730,13 @@ enum device_link_state { * * STATELESS: The core won't track the presence of supplier/consumer drivers. * AUTOREMOVE: Remove this link automatically on consumer driver unbind. + * PM_RUNTIME: If set, the runtime PM framework will use this link. + * RPM_ACTIVE: Run pm_runtime_get_sync() on the supplier during link creation. */ #define DL_FLAG_STATELESS BIT(0) #define DL_FLAG_AUTOREMOVE BIT(1) +#define DL_FLAG_PM_RUNTIME BIT(2) +#define DL_FLAG_RPM_ACTIVE BIT(3) /** * struct device_link - Device link representation. @@ -742,6 +746,7 @@ enum device_link_state { * @c_node: Hook to the consumer device's list of links to suppliers. * @status: The state of the link (with respect to the presence of drivers). * @flags: Link flags. + * @rpm_active: Whether or not the consumer device is runtime-PM-active. * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks. */ struct device_link { @@ -751,6 +756,7 @@ struct device_link { struct list_head c_node; enum device_link_state status; u32 flags; + bool rpm_active; #ifdef CONFIG_SRCU struct rcu_head rcu_head; #endif -- cgit From 2027cbcf497579cb3bd71ed4173ae9b950aa09a3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Date: Tue, 22 Nov 2016 18:31:49 +0100 Subject: driver core: add CLASS_ATTR_WO() Some class subsystems are open-coding CLASS_ATTR_WO because the driver core never provided it. Add the macro to device.h so that we can go around and fix up the individual subsystems as needed. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/device.h') diff --git a/include/linux/device.h b/include/linux/device.h index 49f453892ca5..4264caacebb9 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -465,6 +465,8 @@ struct class_attribute { struct class_attribute class_attr_##_name = __ATTR_RW(_name) #define CLASS_ATTR_RO(_name) \ struct class_attribute class_attr_##_name = __ATTR_RO(_name) +#define CLASS_ATTR_WO(_name) \ + struct class_attribute class_attr_##_name = __ATTR_WO(_name) extern int __must_check class_create_file_ns(struct class *class, const struct class_attribute *attr, -- cgit From ced6473e7486702f530a49f886b73195e4977734 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Date: Mon, 28 Nov 2016 16:41:41 +0100 Subject: driver core: class: add class_groups support struct class needs to have a set of default groups that are added, as adding individual attributes does not work well in the long run. So add support for that. Future patches will convert the existing usages of class_attrs to use class_groups and then class_attrs will go away. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- include/linux/device.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/device.h') diff --git a/include/linux/device.h b/include/linux/device.h index 4264caacebb9..4cd8e52033b0 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -362,6 +362,7 @@ int subsys_virtual_register(struct bus_type *subsys, * @name: Name of the class. * @owner: The module owner. * @class_attrs: Default attributes of this class. + * @class_groups: Default attributes of this class. * @dev_groups: Default attributes of the devices that belong to the class. * @dev_kobj: The kobject that represents this class and links it into the hierarchy. * @dev_uevent: Called when a device is added, removed from this class, or a @@ -390,6 +391,7 @@ struct class { struct module *owner; struct class_attribute *class_attrs; + const struct attribute_group **class_groups; const struct attribute_group **dev_groups; struct kobject *dev_kobj; -- cgit From 64df1148876e35e81e91195e01c8197edc66fcc5 Mon Sep 17 00:00:00 2001 From: Lukas Wunner <lukas@wunner.de> Date: Sun, 4 Dec 2016 13:10:04 +0100 Subject: driver core: Silence device links sphinx warning Silence this warning emitted by sphinx: include/linux/device.h:938: warning: No description found for parameter 'links' While at it, fix typos in comments of device links code. Cc: Rafael J. Wysocki <rafael@kernel.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Silvio Fricke <silvio.fricke@gmail.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Reviewed-by: Mauro Carvalho Chehab <mchehab@s-opensource.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- include/linux/device.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/device.h') diff --git a/include/linux/device.h b/include/linux/device.h index 4cd8e52033b0..67bbbee8fe02 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -817,6 +817,7 @@ struct dev_links_info { * on. This shrinks the "Board Support Packages" (BSPs) and * minimizes board-specific #ifdefs in drivers. * @driver_data: Private pointer for driver specific info. + * @links: Links to suppliers and consumers of this device. * @power: For device power management. * See Documentation/power/devices.txt for details. * @pm_domain: Provide callbacks that are executed during system suspend, -- cgit