From 515db266a9dace92b0cbaed9a6044dd5304b8ca9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 16 Jul 2019 17:21:06 +0200 Subject: driver core: Remove device link creation limitation If device_link_add() is called for a consumer/supplier pair with an existing device link between them and the existing link's type is not in agreement with the flags passed to that function by its caller, NULL will be returned. That is seriously inconvenient, because it forces the callers of device_link_add() to worry about what others may or may not do even if that is not relevant to them for any other reasons. It turns out, however, that this limitation can be made go away relatively easily. The underlying observation is that if DL_FLAG_STATELESS has been passed to device_link_add() in flags for the given consumer/supplier pair at least once, calling either device_link_del() or device_link_remove() to release the link returned by it should work, but there are no other requirements associated with that flag. In turn, if at least one of the callers of device_link_add() for the given consumer/supplier pair has not passed DL_FLAG_STATELESS to it in flags, the driver core should track the status of the link and act on it as appropriate (ie. the link should be treated as "managed"). This means that DL_FLAG_STATELESS needs to be set for managed device links and it should be valid to call device_link_del() or device_link_remove() to drop references to them in certain sutiations. To allow that to happen, introduce a new (internal) device link flag called DL_FLAG_MANAGED and make device_link_add() set it automatically whenever DL_FLAG_STATELESS is not passed to it. Also make it take additional references to existing device links that were previously stateless (that is, with DL_FLAG_STATELESS set and DL_FLAG_MANAGED unset) and will need to be managed going forward and initialize their status (which has been DL_STATE_NONE so far). Accordingly, when a managed device link is dropped automatically by the driver core, make it clear DL_FLAG_MANAGED, reset the link's status back to DL_STATE_NONE and drop the reference to it associated with DL_FLAG_MANAGED instead of just deleting it right away (to allow it to stay around in case it still needs to be released explicitly by someone). With that, since setting DL_FLAG_STATELESS doesn't mean that the device link in question is not managed any more, replace all of the status-tracking checks against DL_FLAG_STATELESS with analogous checks against DL_FLAG_MANAGED and update the documentation to reflect these changes. While at it, make device_link_add() reject flags that it does not recognize, including DL_FLAG_MANAGED. Signed-off-by: Rafael J. Wysocki Reviewed-by: Saravana Kannan Tested-by: Marek Szyprowski Review-by: Saravana Kannan Link: https://lore.kernel.org/r/2305283.AStDPdUUnE@kreacher Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 176 ++++++++++++++++++++++++------------------- drivers/base/power/runtime.c | 4 +- 2 files changed, 101 insertions(+), 79 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index da84a73f2ba6..21cd08162219 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -124,6 +124,50 @@ static int device_is_dependent(struct device *dev, void *target) return ret; } +static void device_link_init_status(struct device_link *link, + struct device *consumer, + struct device *supplier) +{ + switch (supplier->links.status) { + case DL_DEV_PROBING: + switch (consumer->links.status) { + case DL_DEV_PROBING: + /* + * A consumer driver can create a link to a supplier + * that has not completed its probing yet as long as it + * knows that the supplier is already functional (for + * example, it has just acquired some resources from the + * supplier). + */ + link->status = DL_STATE_CONSUMER_PROBE; + break; + default: + link->status = DL_STATE_DORMANT; + break; + } + break; + case DL_DEV_DRIVER_BOUND: + switch (consumer->links.status) { + case DL_DEV_PROBING: + link->status = DL_STATE_CONSUMER_PROBE; + break; + case DL_DEV_DRIVER_BOUND: + link->status = DL_STATE_ACTIVE; + break; + default: + link->status = DL_STATE_AVAILABLE; + break; + } + break; + case DL_DEV_UNBINDING: + link->status = DL_STATE_SUPPLIER_UNBIND; + break; + default: + link->status = DL_STATE_DORMANT; + break; + } +} + static int device_reorder_to_tail(struct device *dev, void *not_used) { struct device_link *link; @@ -165,6 +209,10 @@ void device_pm_move_to_tail(struct device *dev) device_links_read_unlock(idx); } +#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \ + DL_FLAG_AUTOREMOVE_SUPPLIER | \ + DL_FLAG_AUTOPROBE_CONSUMER) + /** * device_link_add - Create a link between two devices. * @consumer: Consumer end of the link. @@ -179,9 +227,9 @@ void device_pm_move_to_tail(struct device *dev) * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be * ignored. * - * If DL_FLAG_STATELESS is set in @flags, the link is not going to be managed by - * the driver core and, in particular, the caller of this function is expected - * to drop the reference to the link acquired by it directly. + * If DL_FLAG_STATELESS is set in @flags, the caller of this function is + * expected to release the link returned by it directly with the help of either + * device_link_del() or device_link_remove(). * * If that flag is not set, however, the caller of this function is handing the * management of the link over to the driver core entirely and its return value @@ -201,9 +249,16 @@ void device_pm_move_to_tail(struct device *dev) * be used to request the driver core to automaticall probe for a consmer * driver after successfully binding a driver to the supplier device. * - * The combination of DL_FLAG_STATELESS and either DL_FLAG_AUTOREMOVE_CONSUMER - * or DL_FLAG_AUTOREMOVE_SUPPLIER set in @flags at the same time is invalid and - * will cause NULL to be returned upfront. + * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER, + * DL_FLAG_AUTOREMOVE_SUPPLIER, or DL_FLAG_AUTOPROBE_CONSUMER set in @flags at + * the same time is invalid and will cause NULL to be returned upfront. + * However, if a device link between the given @consumer and @supplier pair + * exists already when this function is called for them, the existing link will + * be returned regardless of its current type and status (the link's flags may + * be modified then). The caller of this function is then expected to treat + * the link as though it has just been created, so (in particular) if + * DL_FLAG_STATELESS was passed in @flags, the link needs to be released + * explicitly when not needed any more (as stated above). * * A side effect of the link creation is re-ordering of dpm_list and the * devices_kset list by moving the consumer device and all devices depending @@ -220,10 +275,8 @@ struct device_link *device_link_add(struct device *consumer, struct device_link *link; if (!consumer || !supplier || - (flags & DL_FLAG_STATELESS && - flags & (DL_FLAG_AUTOREMOVE_CONSUMER | - DL_FLAG_AUTOREMOVE_SUPPLIER | - DL_FLAG_AUTOPROBE_CONSUMER)) || + (flags & ~(DL_FLAG_STATELESS | DL_MANAGED_LINK_FLAGS)) || + (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) || (flags & DL_FLAG_AUTOPROBE_CONSUMER && flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER))) @@ -236,6 +289,9 @@ struct device_link *device_link_add(struct device *consumer, } } + if (!(flags & DL_FLAG_STATELESS)) + flags |= DL_FLAG_MANAGED; + device_links_write_lock(); device_pm_lock(); @@ -262,15 +318,6 @@ struct device_link *device_link_add(struct device *consumer, if (link->consumer != consumer) continue; - /* - * Don't return a stateless link if the caller wants a stateful - * one and vice versa. - */ - if (WARN_ON((flags & DL_FLAG_STATELESS) != (link->flags & DL_FLAG_STATELESS))) { - link = NULL; - goto out; - } - if (flags & DL_FLAG_PM_RUNTIME) { if (!(link->flags & DL_FLAG_PM_RUNTIME)) { pm_runtime_new_link(consumer); @@ -281,6 +328,7 @@ struct device_link *device_link_add(struct device *consumer, } if (flags & DL_FLAG_STATELESS) { + link->flags |= DL_FLAG_STATELESS; kref_get(&link->kref); goto out; } @@ -299,6 +347,11 @@ struct device_link *device_link_add(struct device *consumer, link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER); } + if (!(link->flags & DL_FLAG_MANAGED)) { + kref_get(&link->kref); + link->flags |= DL_FLAG_MANAGED; + device_link_init_status(link, consumer, supplier); + } goto out; } @@ -325,48 +378,10 @@ struct device_link *device_link_add(struct device *consumer, kref_init(&link->kref); /* Determine the initial link state. */ - if (flags & DL_FLAG_STATELESS) { + if (flags & DL_FLAG_STATELESS) link->status = DL_STATE_NONE; - } else { - switch (supplier->links.status) { - case DL_DEV_PROBING: - switch (consumer->links.status) { - case DL_DEV_PROBING: - /* - * A consumer driver can create a link to a - * supplier that has not completed its probing - * yet as long as it knows that the supplier is - * already functional (for example, it has just - * acquired some resources from the supplier). - */ - link->status = DL_STATE_CONSUMER_PROBE; - break; - default: - link->status = DL_STATE_DORMANT; - break; - } - break; - case DL_DEV_DRIVER_BOUND: - switch (consumer->links.status) { - case DL_DEV_PROBING: - link->status = DL_STATE_CONSUMER_PROBE; - break; - case DL_DEV_DRIVER_BOUND: - link->status = DL_STATE_ACTIVE; - break; - default: - link->status = DL_STATE_AVAILABLE; - break; - } - break; - case DL_DEV_UNBINDING: - link->status = DL_STATE_SUPPLIER_UNBIND; - break; - default: - link->status = DL_STATE_DORMANT; - break; - } - } + else + device_link_init_status(link, consumer, supplier); /* * Some callers expect the link creation during consumer driver probe to @@ -528,7 +543,7 @@ static void device_links_missing_supplier(struct device *dev) * mark the link as "consumer probe in progress" to make the supplier removal * wait for us to complete (or bad things may happen). * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ int device_links_check_suppliers(struct device *dev) { @@ -538,7 +553,7 @@ int device_links_check_suppliers(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; if (link->status != DL_STATE_AVAILABLE) { @@ -563,7 +578,7 @@ int device_links_check_suppliers(struct device *dev) * * Also change the status of @dev's links to suppliers to "active". * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ void device_links_driver_bound(struct device *dev) { @@ -572,7 +587,7 @@ void device_links_driver_bound(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; /* @@ -593,7 +608,7 @@ void device_links_driver_bound(struct device *dev) } list_for_each_entry(link, &dev->links.suppliers, c_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); @@ -605,6 +620,13 @@ void device_links_driver_bound(struct device *dev) device_links_write_unlock(); } +static void device_link_drop_managed(struct device_link *link) +{ + link->flags &= ~DL_FLAG_MANAGED; + WRITE_ONCE(link->status, DL_STATE_NONE); + kref_put(&link->kref, __device_link_del); +} + /** * __device_links_no_driver - Update links of a device without a driver. * @dev: Device without a drvier. @@ -615,18 +637,18 @@ void device_links_driver_bound(struct device *dev) * unless they already are in the "supplier unbind in progress" state in which * case they need not be updated. * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ static void __device_links_no_driver(struct device *dev) { struct device_link *link, *ln; list_for_each_entry_safe_reverse(link, ln, &dev->links.suppliers, c_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER) - __device_link_del(&link->kref); + device_link_drop_managed(link); else if (link->status == DL_STATE_CONSUMER_PROBE || link->status == DL_STATE_ACTIVE) WRITE_ONCE(link->status, DL_STATE_AVAILABLE); @@ -643,7 +665,7 @@ static void __device_links_no_driver(struct device *dev) * %__device_links_no_driver() to update links to suppliers for it as * appropriate. * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ void device_links_no_driver(struct device *dev) { @@ -652,7 +674,7 @@ void device_links_no_driver(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; /* @@ -680,7 +702,7 @@ void device_links_no_driver(struct device *dev) * invoke %__device_links_no_driver() to update links to suppliers for it as * appropriate. * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ void device_links_driver_cleanup(struct device *dev) { @@ -689,7 +711,7 @@ void device_links_driver_cleanup(struct device *dev) device_links_write_lock(); list_for_each_entry_safe(link, ln, &dev->links.consumers, s_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; WARN_ON(link->flags & DL_FLAG_AUTOREMOVE_CONSUMER); @@ -702,7 +724,7 @@ void device_links_driver_cleanup(struct device *dev) */ if (link->status == DL_STATE_SUPPLIER_UNBIND && link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER) - __device_link_del(&link->kref); + device_link_drop_managed(link); WRITE_ONCE(link->status, DL_STATE_DORMANT); } @@ -724,7 +746,7 @@ void device_links_driver_cleanup(struct device *dev) * * Return 'false' if there are no probing or active consumers. * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ bool device_links_busy(struct device *dev) { @@ -734,7 +756,7 @@ bool device_links_busy(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; if (link->status == DL_STATE_CONSUMER_PROBE @@ -764,7 +786,7 @@ bool device_links_busy(struct device *dev) * driver to unbind and start over (the consumer will not re-probe as we have * changed the state of the link already). * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links without the DL_FLAG_MANAGED flag set are ignored. */ void device_links_unbind_consumers(struct device *dev) { @@ -776,7 +798,7 @@ void device_links_unbind_consumers(struct device *dev) list_for_each_entry(link, &dev->links.consumers, s_node) { enum device_link_state status; - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; status = link->status; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index b75335508d2c..45a8fbe6987a 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1624,7 +1624,7 @@ void pm_runtime_remove(struct device *dev) * runtime PM references to the device, drop the usage counter of the device * (as many times as needed). * - * Links with the DL_FLAG_STATELESS flag set are ignored. + * Links with the DL_FLAG_MANAGED flag unset are ignored. * * Since the device is guaranteed to be runtime-active at the point this is * called, nothing else needs to be done here. @@ -1641,7 +1641,7 @@ void pm_runtime_clean_up_links(struct device *dev) idx = device_links_read_lock(); list_for_each_entry_rcu(link, &dev->links.consumers, s_node) { - if (link->flags & DL_FLAG_STATELESS) + if (!(link->flags & DL_FLAG_MANAGED)) continue; while (refcount_dec_not_one(&link->rpm_active)) -- cgit v1.2.3-59-g8ed1b From fb583c8eeeb1fd57e24ef41ed94c9112067aeac9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Jul 2019 11:28:57 +0200 Subject: driver core: Fix creation of device links with PM-runtime flags After commit 515db266a9da ("driver core: Remove device link creation limitation"), if PM-runtime flags are passed to device_link_add(), it will fail (returning NULL) due to an overly restrictive flags check introduced by that commit. Fix this issue by extending the check in question to cover the PM-runtime flags too. Fixes: 515db266a9da ("driver core: Remove device link creation limitation") Reported-by: Dmitry Osipenko Tested-by: Jon Hunter Signed-off-by: Rafael J. Wysocki Tested-by: Dmitry Osipenko Tested-by: Marek Szyprowski Link: https://lore.kernel.org/r/7674989.cD04D8YV3U@kreacher Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 21cd08162219..08eecab2497b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -213,6 +213,9 @@ void device_pm_move_to_tail(struct device *dev) DL_FLAG_AUTOREMOVE_SUPPLIER | \ DL_FLAG_AUTOPROBE_CONSUMER) +#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \ + DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE) + /** * device_link_add - Create a link between two devices. * @consumer: Consumer end of the link. @@ -274,8 +277,7 @@ struct device_link *device_link_add(struct device *consumer, { struct device_link *link; - if (!consumer || !supplier || - (flags & ~(DL_FLAG_STATELESS | DL_MANAGED_LINK_FLAGS)) || + if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS || (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) || (flags & DL_FLAG_AUTOPROBE_CONSUMER && flags & (DL_FLAG_AUTOREMOVE_CONSUMER | -- cgit v1.2.3-59-g8ed1b From 7723f4c5ecdb8d832f049f8483beb0d1081cedf6 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 29 Jul 2019 22:38:43 -0700 Subject: driver core: platform: Add an error message to platform_get_irq*() A grep of the kernel shows that many drivers print an error message if they fail to get the irq they're looking for. Furthermore, those drivers all decide to print the device name, or not, and the irq they were requesting, or not, etc. Let's consolidate all these error messages into the API itself, allowing us to get rid of the error messages in each driver. Cc: Rob Herring Cc: Bartlomiej Zolnierkiewicz Cc: Javier Martinez Canillas Cc: Andrzej Hajda Cc: Mark Brown Cc: Russell King - ARM Linux Cc: Marek Szyprowski Cc: Markus Elfring Reviewed-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20190730053845.126834-2-swboyd@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a174ce5ea17c..9426736551b5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -99,12 +99,7 @@ void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); #endif /* CONFIG_HAS_IOMEM */ -/** - * platform_get_irq - get an IRQ for a device - * @dev: platform device - * @num: IRQ number index - */ -int platform_get_irq(struct platform_device *dev, unsigned int num) +static int __platform_get_irq(struct platform_device *dev, unsigned int num) { #ifdef CONFIG_SPARC /* sparc does not have irqs represented as IORESOURCE_IRQ resources */ @@ -163,6 +158,33 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) return -ENXIO; #endif } + +/** + * platform_get_irq - get an IRQ for a device + * @dev: platform device + * @num: IRQ number index + * + * Gets an IRQ for a platform device and prints an error message if finding the + * IRQ fails. Device drivers should check the return value for errors so as to + * not pass a negative integer value to the request_irq() APIs. + * + * Example: + * int irq = platform_get_irq(pdev, 0); + * if (irq < 0) + * return irq; + * + * Return: IRQ number on success, negative error number on failure. + */ +int platform_get_irq(struct platform_device *dev, unsigned int num) +{ + int ret; + + ret = __platform_get_irq(dev, num); + if (ret < 0 && ret != -EPROBE_DEFER) + dev_err(&dev->dev, "IRQ index %u not found\n", num); + + return ret; +} EXPORT_SYMBOL_GPL(platform_get_irq); /** @@ -175,7 +197,7 @@ int platform_irq_count(struct platform_device *dev) { int ret, nr = 0; - while ((ret = platform_get_irq(dev, nr)) >= 0) + while ((ret = __platform_get_irq(dev, nr)) >= 0) nr++; if (ret == -EPROBE_DEFER) @@ -228,7 +250,11 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name) } r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name); - return r ? r->start : -ENXIO; + if (r) + return r->start; + + dev_err(&dev->dev, "IRQ %s not found\n", name); + return -ENXIO; } EXPORT_SYMBOL_GPL(platform_get_irq_byname); -- cgit v1.2.3-59-g8ed1b From 5302dd7dd0b6d04c63cdce51d1e9fda9ef0be886 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 31 Jul 2019 15:17:14 -0700 Subject: driver core: Add support for linking devices during device addition When devices are added, the bus might want to create device links to track functional dependencies between supplier and consumer devices. This tracking of supplier-consumer relationship allows optimizing device probe order and tracking whether all consumers of a supplier are active. The add_links bus callback is added to support this. However, when consumer devices are added, they might not have a supplier device to link to despite needing mandatory resources/functionality from one or more suppliers. A waiting_for_suppliers list is created to track such consumers and retry linking them when new devices get added. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190731221721.187713-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 14 +++++++++ 2 files changed, 97 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 90f2dd4661f5..18e04ca1de13 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); #endif /* Device links support. */ +static LIST_HEAD(wait_for_suppliers); +static DEFINE_MUTEX(wfs_lock); #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -418,6 +420,51 @@ struct device_link *device_link_add(struct device *consumer, } EXPORT_SYMBOL_GPL(device_link_add); +/** + * device_link_wait_for_supplier - Mark device as waiting for supplier + * @consumer: Consumer device + * + * Marks the consumer device as waiting for suppliers to become available. The + * consumer device will never be probed until it's unmarked as waiting for + * suppliers. The caller is responsible for adding the link to the supplier + * once the supplier device is present. + * + * This function is NOT meant to be called from the probe function of the + * consumer but rather from code that creates/adds the consumer device. + */ +static void device_link_wait_for_supplier(struct device *consumer) +{ + mutex_lock(&wfs_lock); + list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); + mutex_unlock(&wfs_lock); +} + +/** + * device_link_check_waiting_consumers - Try to unmark waiting consumers + * + * Loops through all consumers waiting on suppliers and tries to add all their + * supplier links. If that succeeds, the consumer device is unmarked as waiting + * for suppliers. Otherwise, they are left marked as waiting on suppliers, + * + * The add_links bus callback is expected to return 0 if it has found and added + * all the supplier links for the consumer device. It should return an error if + * it isn't able to do so. + * + * The caller of device_link_wait_for_supplier() is expected to call this once + * it's aware of potential suppliers becoming available. + */ +static void device_link_check_waiting_consumers(void) +{ + struct device *dev, *tmp; + + mutex_lock(&wfs_lock); + list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, + links.needs_suppliers) + if (!dev->bus->add_links(dev)) + list_del_init(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); +} + static void device_link_free(struct device_link *link) { while (refcount_dec_not_one(&link->rpm_active)) @@ -552,6 +599,19 @@ int device_links_check_suppliers(struct device *dev) struct device_link *link; int ret = 0; + /* + * If a device is waiting for one or more suppliers (in + * wait_for_suppliers list), it is not ready to probe yet. So just + * return -EPROBE_DEFER without having to check the links with existing + * suppliers. + */ + mutex_lock(&wfs_lock); + if (!list_empty(&dev->links.needs_suppliers)) { + mutex_unlock(&wfs_lock); + return -EPROBE_DEFER; + } + mutex_unlock(&wfs_lock); + device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { @@ -836,6 +896,10 @@ static void device_links_purge(struct device *dev) { struct device_link *link, *ln; + mutex_lock(&wfs_lock); + list_del(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); + /* * Delete all of the remaining links from this device to any other * devices (either consumers or suppliers). @@ -1697,6 +1761,7 @@ void device_initialize(struct device *dev) #endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); + INIT_LIST_HEAD(&dev->links.needs_suppliers); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); @@ -2132,6 +2197,24 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); + + /* + * Check if any of the other devices (consumers) have been waiting for + * this device (supplier) to be added so that they can create a device + * link to it. + * + * This needs to happen after device_pm_add() because device_link_add() + * requires the supplier be registered before it's called. + * + * But this also needs to happe before bus_probe_device() to make sure + * waiting consumers can link to it before the driver is bound to the + * device and the driver sync_state callback is called for this device. + */ + device_link_check_waiting_consumers(); + + if (dev->bus && dev->bus->add_links && dev->bus->add_links(dev)) + device_link_wait_for_supplier(dev); + bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, diff --git a/include/linux/device.h b/include/linux/device.h index bff46ce3bc3b..1e05911325f0 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -78,6 +78,17 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * -EPROBE_DEFER it will queue the device for deferred probing. * @uevent: Called when a device is added, removed, or a few other things * that generate uevents to add the environment variables. + * @add_links: Called, perhaps multiple times per device, after a device is + * added to this bus. The function is expected to create device + * links to all the suppliers of the input device that are + * available at the time this function is called. As in, the + * function should NOT stop at the first failed device link if + * other unlinked supplier devices are present in the system. + * + * Return 0 if device links have been successfully created to all + * the suppliers of this device. Return an error if some of the + * suppliers are not yet available and this function needs to be + * reattempted in the future. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. * @remove: Called when a device removed from this bus. @@ -122,6 +133,7 @@ struct bus_type { int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); + int (*add_links)(struct device *dev); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -1128,11 +1140,13 @@ enum dl_dev_state { * struct dev_links_info - Device data related to device links. * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. + * @needs_suppliers: Hook to global list of devices waiting for suppliers. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; + struct list_head needs_suppliers; enum dl_dev_state status; }; -- cgit v1.2.3-59-g8ed1b From 134b23eec9e3a3c795a6ceb0efe2fa63e87983b2 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 31 Jul 2019 15:17:15 -0700 Subject: driver core: Add edit_links() callback for drivers The driver core/bus adding supplier-consumer dependencies by default enables functional dependencies to be tracked correctly even when the consumer devices haven't had their drivers registered or loaded (if they are modules). However, when the bus incorrectly adds dependencies that it shouldn't have added, the devices might never probe. For example, if device-C is a consumer of device-S and they have phandles to each other in DT, the following could happen: 1. Device-S get added first. 2. The bus add_links() callback will (incorrectly) try to link it as a consumer of device-C. 3. Since device-C isn't present, device-S will be put in "waiting-for-supplier" list. 4. Device-C gets added next. 5. All devices in "waiting-for-supplier" list are retried for linking. 6. Device-S gets linked as consumer to Device-C. 7. The bus add_links() callback will (correctly) try to link it as a consumer of device-S. 8. This isn't allowed because it would create a cyclic device links. Neither devices will get probed since the supplier is marked as dependent on the consumer. And the consumer will never probe because the consumer can't get resources from the supplier. Without this patch, things stay in this broken state. However, with this patch, the execution will continue like this: 9. Device-C's driver is loaded. 10. Device-C's driver removes Device-S as a consumer of Device-C. 11. Device-C's driver adds Device-C as a consumer of Device-S. 12. Device-S probes. 14. Device-C probes. kbuild test robot reported missing documentation for device.has_edit_links Reported-by: kbuild test robot Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190731221721.187713-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 24 ++++++++++++++++++++++-- drivers/base/dd.c | 29 +++++++++++++++++++++++++++++ include/linux/device.h | 20 ++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 18e04ca1de13..feec8dee1e91 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -439,6 +439,19 @@ static void device_link_wait_for_supplier(struct device *consumer) mutex_unlock(&wfs_lock); } +/** + * device_link_remove_from_wfs - Unmark device as waiting for supplier + * @consumer: Consumer device + * + * Unmark the consumer device as waiting for suppliers to become available. + */ +void device_link_remove_from_wfs(struct device *consumer) +{ + mutex_lock(&wfs_lock); + list_del_init(&consumer->links.needs_suppliers); + mutex_unlock(&wfs_lock); +} + /** * device_link_check_waiting_consumers - Try to unmark waiting consumers * @@ -456,12 +469,19 @@ static void device_link_wait_for_supplier(struct device *consumer) static void device_link_check_waiting_consumers(void) { struct device *dev, *tmp; + int ret; mutex_lock(&wfs_lock); list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) - if (!dev->bus->add_links(dev)) + links.needs_suppliers) { + ret = 0; + if (dev->has_edit_links) + ret = driver_edit_links(dev); + else if (dev->bus->add_links) + ret = dev->bus->add_links(dev); + if (!ret) list_del_init(&dev->links.needs_suppliers); + } mutex_unlock(&wfs_lock); } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 994a90747420..5e7041ede0d7 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -698,6 +698,12 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); + if (drv->edit_links) { + if (drv->edit_links(dev)) + dev->has_edit_links = true; + else + device_link_remove_from_wfs(dev); + } pm_runtime_get_suppliers(dev); if (dev->parent) pm_runtime_get_sync(dev->parent); @@ -786,6 +792,29 @@ struct device_attach_data { bool have_async; }; +static int __driver_edit_links(struct device_driver *drv, void *data) +{ + struct device *dev = data; + + if (!drv->edit_links) + return 0; + + if (driver_match_device(drv, dev) <= 0) + return 0; + + return drv->edit_links(dev); +} + +int driver_edit_links(struct device *dev) +{ + int ret; + + device_lock(dev); + ret = bus_for_each_drv(dev->bus, NULL, dev, __driver_edit_links); + device_unlock(dev); + return ret; +} + static int __device_attach_driver(struct device_driver *drv, void *_data) { struct device_attach_data *data = _data; diff --git a/include/linux/device.h b/include/linux/device.h index 1e05911325f0..d3991810f39d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -349,6 +349,20 @@ enum probe_type { * @probe_type: Type of the probe (synchronous or asynchronous) to use. * @of_match_table: The open firmware table. * @acpi_match_table: The ACPI match table. + * @edit_links: Called to allow a matched driver to edit the device links the + * bus might have added incorrectly. This will be useful to handle + * cases where the bus incorrectly adds functional dependencies + * that aren't true or tries to create cyclic dependencies. But + * doesn't correctly handle functional dependencies that are + * missed by the bus as the supplier's sync_state might get to + * execute before the driver for a missing consumer is loaded and + * gets to edit the device links for the consumer. + * + * This function might be called multiple times after a new device + * is added. The function is expected to create all the device + * links for the new device and return 0 if it was completed + * successfully or return an error if it needs to be reattempted + * in the future. * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. @@ -388,6 +402,7 @@ struct device_driver { const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; + int (*edit_links)(struct device *dev); int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); @@ -1220,6 +1235,8 @@ struct dev_links_info { * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. + * @has_edit_links: This device has a driver than is capable of + * editing the device links created by driver core. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @@ -1313,6 +1330,7 @@ struct device { bool offline_disabled:1; bool offline:1; bool of_node_reused:1; + bool has_edit_links:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -1564,6 +1582,7 @@ extern int __must_check device_attach(struct device *dev); extern int __must_check driver_attach(struct device_driver *drv); extern void device_initial_probe(struct device *dev); extern int __must_check device_reprobe(struct device *dev); +extern int driver_edit_links(struct device *dev); extern bool device_is_bound(struct device *dev); @@ -1654,6 +1673,7 @@ struct device_link *device_link_add(struct device *consumer, struct device *supplier, u32 flags); void device_link_del(struct device_link *link); void device_link_remove(void *consumer, struct device *supplier); +void device_link_remove_from_wfs(struct device *consumer); #ifndef dev_fmt #define dev_fmt(fmt) fmt -- cgit v1.2.3-59-g8ed1b From 8f8184d6bf676a8680d6f441e40317d166b46f73 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 31 Jul 2019 15:17:17 -0700 Subject: driver core: Add sync_state driver/bus callback This sync_state driver/bus callback is called once all the consumers of a supplier have probed successfully. This allows the supplier device's driver/bus to sync the supplier device's state to the software state with the guarantee that all the consumers are actively managing the resources provided by the supplier device. To maintain backwards compatibility and ease transition from existing frameworks and resource cleanup schemes, late_initcall_sync is the earliest when the sync_state callback might be called. There is no upper bound on the time by which the sync_state callback has to be called. This is because if a consumer device never probes, the supplier has to maintain its resources in the state left by the bootloader. For example, if the bootloader leaves the display backlight at a fixed voltage and the backlight driver is never probed, you don't want the backlight to ever be turned off after boot up. Also, when multiple devices are added after kernel init, some suppliers could be added before their consumer devices get added. In these instances, the supplier devices could get their sync_state callback called right after they probe because the consumers devices haven't had a chance to create device links to the suppliers. To handle this correctly, this change also provides APIs to pause/resume sync state callbacks so that when multiple devices are added, their sync_state callback evaluation can be postponed to happen after all of them are added. kbuild test robot reported missing documentation for device.state_synced Reported-by: kbuild test robot Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190731221721.187713-5-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 26 ++++++++++++++++++++ 2 files changed, 91 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index feec8dee1e91..5b1795cd116a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -46,6 +46,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); /* Device links support. */ static LIST_HEAD(wait_for_suppliers); static DEFINE_MUTEX(wfs_lock); +static LIST_HEAD(deferred_sync); +static unsigned int supplier_sync_state_disabled; #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -651,6 +653,62 @@ int device_links_check_suppliers(struct device *dev) return ret; } +static void __device_links_supplier_sync_state(struct device *dev) +{ + struct device_link *link; + + if (dev->state_synced) + return; + + list_for_each_entry(link, &dev->links.consumers, s_node) { + if (!(link->flags & DL_FLAG_MANAGED)) + continue; + if (link->status != DL_STATE_ACTIVE) + return; + } + + if (dev->bus->sync_state) + dev->bus->sync_state(dev); + else if (dev->driver && dev->driver->sync_state) + dev->driver->sync_state(dev); + + dev->state_synced = true; +} + +void device_links_supplier_sync_state_pause(void) +{ + device_links_write_lock(); + supplier_sync_state_disabled++; + device_links_write_unlock(); +} + +void device_links_supplier_sync_state_resume(void) +{ + struct device *dev, *tmp; + + device_links_write_lock(); + if (!supplier_sync_state_disabled) { + WARN(true, "Unmatched sync_state pause/resume!"); + goto out; + } + supplier_sync_state_disabled--; + if (supplier_sync_state_disabled) + goto out; + + list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { + __device_links_supplier_sync_state(dev); + list_del_init(&dev->links.defer_sync); + } +out: + device_links_write_unlock(); +} + +static void __device_links_supplier_defer_sync(struct device *sup) +{ + if (list_empty(&sup->links.defer_sync)) + list_add_tail(&sup->links.defer_sync, &deferred_sync); +} + /** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. @@ -695,6 +753,11 @@ void device_links_driver_bound(struct device *dev) WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); WRITE_ONCE(link->status, DL_STATE_ACTIVE); + + if (supplier_sync_state_disabled) + __device_links_supplier_defer_sync(link->supplier); + else + __device_links_supplier_sync_state(link->supplier); } dev->links.status = DL_DEV_DRIVER_BOUND; @@ -811,6 +874,7 @@ void device_links_driver_cleanup(struct device *dev) WRITE_ONCE(link->status, DL_STATE_DORMANT); } + list_del_init(&dev->links.defer_sync); __device_links_no_driver(dev); device_links_write_unlock(); @@ -1782,6 +1846,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); + INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/include/linux/device.h b/include/linux/device.h index d3991810f39d..63a3aafabcd6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -84,6 +84,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * available at the time this function is called. As in, the * function should NOT stop at the first failed device link if * other unlinked supplier devices are present in the system. + * This is necessary for the sync_state() callback to work + * correctly. * * Return 0 if device links have been successfully created to all * the suppliers of this device. Return an error if some of the @@ -91,6 +93,13 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * reattempted in the future. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. + * @sync_state: Called to sync device state to software state after all the + * state tracking consumers linked to this device (present at + * the time of late_initcall) have successfully bound to a + * driver. If the device has no consumers, this function will + * be called at late_initcall_sync level. If the device has + * consumers that are never bound to a driver, this function + * will never get called until they do. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * @@ -135,6 +144,7 @@ struct bus_type { int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*add_links)(struct device *dev); int (*probe)(struct device *dev); + void (*sync_state)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -366,6 +376,13 @@ enum probe_type { * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. + * @sync_state: Called to sync device state to software state after all the + * state tracking consumers linked to this device (present at + * the time of late_initcall) have successfully bound to a + * driver. If the device has no consumers, this function will + * be called at late_initcall_sync level. If the device has + * consumers that are never bound to a driver, this function + * will never get called until they do. * @remove: Called when the device is removed from the system to * unbind a device from this driver. * @shutdown: Called at shut-down time to quiesce the device. @@ -404,6 +421,7 @@ struct device_driver { int (*edit_links)(struct device *dev); int (*probe) (struct device *dev); + void (*sync_state)(struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); @@ -1156,12 +1174,14 @@ enum dl_dev_state { * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. * @needs_suppliers: Hook to global list of devices waiting for suppliers. + * @defer_sync: Hook to global list of devices that have deferred sync_state. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; struct list_head needs_suppliers; + struct list_head defer_sync; enum dl_dev_state status; }; @@ -1237,6 +1257,9 @@ struct dev_links_info { * device. * @has_edit_links: This device has a driver than is capable of * editing the device links created by driver core. + * @state_synced: The hardware state of this device has been synced to match + * the software state of this device by calling the driver/bus + * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @@ -1331,6 +1354,7 @@ struct device { bool offline:1; bool of_node_reused:1; bool has_edit_links:1; + bool state_synced:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -1674,6 +1698,8 @@ struct device_link *device_link_add(struct device *consumer, void device_link_del(struct device_link *link); void device_link_remove(void *consumer, struct device *supplier); void device_link_remove_from_wfs(struct device *consumer); +void device_links_supplier_sync_state_pause(void); +void device_links_supplier_sync_state_resume(void); #ifndef dev_fmt #define dev_fmt(fmt) fmt -- cgit v1.2.3-59-g8ed1b From 23b6904442d08b7dbed7622ed33b236d41a3aa8b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 31 Jul 2019 14:43:40 +0200 Subject: driver core: add dev_groups to all drivers Add the ability for the driver core to create and remove a list of attribute groups automatically when the device is bound/unbound from a specific driver. Signed-off-by: Dmitry Torokhov Tested-by: Richard Gong Link: https://lore.kernel.org/r/20190731124349.4474-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 14 ++++++++++++++ include/linux/device.h | 3 +++ 2 files changed, 17 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 994a90747420..d811e60610d3 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -554,9 +554,16 @@ re_probe: goto probe_failed; } + if (device_add_groups(dev, drv->dev_groups)) { + dev_err(dev, "device_add_groups() failed\n"); + goto dev_groups_failed; + } + if (test_remove) { test_remove = false; + device_remove_groups(dev, drv->dev_groups); + if (dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) @@ -584,6 +591,11 @@ re_probe: drv->bus->name, __func__, dev_name(dev), drv->name); goto done; +dev_groups_failed: + if (dev->bus->remove) + dev->bus->remove(dev); + else if (drv->remove) + drv->remove(dev); probe_failed: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, @@ -1114,6 +1126,8 @@ static void __device_release_driver(struct device *dev, struct device *parent) pm_runtime_put_sync(dev); + device_remove_groups(dev, drv->dev_groups); + if (dev->bus && dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) diff --git a/include/linux/device.h b/include/linux/device.h index c330b75c6c57..98c00b71b598 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -262,6 +262,8 @@ enum probe_type { * @resume: Called to bring a device from sleep mode. * @groups: Default attributes that get created by the driver core * automatically. + * @dev_groups: Additional attributes attached to device instance once the + * it is bound to the driver. * @pm: Power management operations of the device which matched * this driver. * @coredump: Called when sysfs entry is written to. The device driver @@ -296,6 +298,7 @@ struct device_driver { int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; + const struct attribute_group **dev_groups; const struct dev_pm_ops *pm; void (*coredump) (struct device *dev); -- cgit v1.2.3-59-g8ed1b From ce684d957c5672f3bb55e6b0872932b5b4c39c56 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 31 Jul 2019 12:00:06 +0200 Subject: devcoredump: use memory_read_from_buffer Use memory_read_from_buffer() to simplify devcd_readv(). Reviewed-by: Chaitanya Kulkarni Reviewed-by: Johannes Berg Signed-off-by: Akinobu Mita Link: https://lore.kernel.org/r/1564243146-5681-2-git-send-email-akinobu.mita@gmail.com Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20190731100007.32684-1-johannes@sipsolutions.net Signed-off-by: Greg Kroah-Hartman --- drivers/base/devcoredump.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index f1a3353f3494..3c960a63062f 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -164,16 +164,7 @@ static struct class devcd_class = { static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, void *data, size_t datalen) { - if (offset > datalen) - return -EINVAL; - - if (offset + count > datalen) - count = datalen - offset; - - if (count) - memcpy(buffer, ((u8 *)data) + offset, count); - - return count; + return memory_read_from_buffer(buffer, count, &offset, data, datalen); } static void devcd_freev(void *data) -- cgit v1.2.3-59-g8ed1b From 2a77eec0d3ca20481a340d4543930a704b3230f6 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 31 Jul 2019 12:00:07 +0200 Subject: devcoredump: fix typo in comment s/dev_coredumpmsg/dev_coredumpsg/ in the kernel-doc Reviewed-by: Chaitanya Kulkarni Signed-off-by: Akinobu Mita Link: https://lore.kernel.org/r/1564243146-5681-3-git-send-email-akinobu.mita@gmail.com Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20190731100007.32684-2-johannes@sipsolutions.net Signed-off-by: Greg Kroah-Hartman --- drivers/base/devcoredump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 3c960a63062f..e42d0b514384 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -314,7 +314,7 @@ void dev_coredumpm(struct device *dev, struct module *owner, EXPORT_SYMBOL_GPL(dev_coredumpm); /** - * dev_coredumpmsg - create device coredump that uses scatterlist as data + * dev_coredumpsg - create device coredump that uses scatterlist as data * parameter * @dev: the struct device for the crashed device * @table: the dump data -- cgit v1.2.3-59-g8ed1b From bcca686c11cdad3b4566ed1818cd2688e7901def Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Aug 2019 21:40:50 +0200 Subject: Revert "driver core: Add sync_state driver/bus callback" This reverts commit 8f8184d6bf676a8680d6f441e40317d166b46f73. Based on a lot of email and in-person discussions, this patch series is being reworked to address a number of issues that were pointed out that needed to be taken care of before it should be merged. It will be resubmitted with those changes hopefully soon. Cc: Frank Rowand Cc: Saravana Kannan Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 65 -------------------------------------------------- include/linux/device.h | 26 -------------------- 2 files changed, 91 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 75fd59360940..de775c7a4d7c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -46,8 +46,6 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); /* Device links support. */ static LIST_HEAD(wait_for_suppliers); static DEFINE_MUTEX(wfs_lock); -static LIST_HEAD(deferred_sync); -static unsigned int supplier_sync_state_disabled; #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -653,62 +651,6 @@ int device_links_check_suppliers(struct device *dev) return ret; } -static void __device_links_supplier_sync_state(struct device *dev) -{ - struct device_link *link; - - if (dev->state_synced) - return; - - list_for_each_entry(link, &dev->links.consumers, s_node) { - if (!(link->flags & DL_FLAG_MANAGED)) - continue; - if (link->status != DL_STATE_ACTIVE) - return; - } - - if (dev->bus->sync_state) - dev->bus->sync_state(dev); - else if (dev->driver && dev->driver->sync_state) - dev->driver->sync_state(dev); - - dev->state_synced = true; -} - -void device_links_supplier_sync_state_pause(void) -{ - device_links_write_lock(); - supplier_sync_state_disabled++; - device_links_write_unlock(); -} - -void device_links_supplier_sync_state_resume(void) -{ - struct device *dev, *tmp; - - device_links_write_lock(); - if (!supplier_sync_state_disabled) { - WARN(true, "Unmatched sync_state pause/resume!"); - goto out; - } - supplier_sync_state_disabled--; - if (supplier_sync_state_disabled) - goto out; - - list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { - __device_links_supplier_sync_state(dev); - list_del_init(&dev->links.defer_sync); - } -out: - device_links_write_unlock(); -} - -static void __device_links_supplier_defer_sync(struct device *sup) -{ - if (list_empty(&sup->links.defer_sync)) - list_add_tail(&sup->links.defer_sync, &deferred_sync); -} - /** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. @@ -753,11 +695,6 @@ void device_links_driver_bound(struct device *dev) WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); WRITE_ONCE(link->status, DL_STATE_ACTIVE); - - if (supplier_sync_state_disabled) - __device_links_supplier_defer_sync(link->supplier); - else - __device_links_supplier_sync_state(link->supplier); } dev->links.status = DL_DEV_DRIVER_BOUND; @@ -874,7 +811,6 @@ void device_links_driver_cleanup(struct device *dev) WRITE_ONCE(link->status, DL_STATE_DORMANT); } - list_del_init(&dev->links.defer_sync); __device_links_no_driver(dev); device_links_write_unlock(); @@ -1849,7 +1785,6 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); - INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/include/linux/device.h b/include/linux/device.h index 76496497e753..90142ec9ce84 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -84,8 +84,6 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * available at the time this function is called. As in, the * function should NOT stop at the first failed device link if * other unlinked supplier devices are present in the system. - * This is necessary for the sync_state() callback to work - * correctly. * * Return 0 if device links have been successfully created to all * the suppliers of this device. Return an error if some of the @@ -93,13 +91,6 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * reattempted in the future. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. - * @sync_state: Called to sync device state to software state after all the - * state tracking consumers linked to this device (present at - * the time of late_initcall) have successfully bound to a - * driver. If the device has no consumers, this function will - * be called at late_initcall_sync level. If the device has - * consumers that are never bound to a driver, this function - * will never get called until they do. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * @@ -144,7 +135,6 @@ struct bus_type { int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*add_links)(struct device *dev); int (*probe)(struct device *dev); - void (*sync_state)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -376,13 +366,6 @@ enum probe_type { * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. - * @sync_state: Called to sync device state to software state after all the - * state tracking consumers linked to this device (present at - * the time of late_initcall) have successfully bound to a - * driver. If the device has no consumers, this function will - * be called at late_initcall_sync level. If the device has - * consumers that are never bound to a driver, this function - * will never get called until they do. * @remove: Called when the device is removed from the system to * unbind a device from this driver. * @shutdown: Called at shut-down time to quiesce the device. @@ -423,7 +406,6 @@ struct device_driver { int (*edit_links)(struct device *dev); int (*probe) (struct device *dev); - void (*sync_state)(struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); @@ -1177,14 +1159,12 @@ enum dl_dev_state { * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. * @needs_suppliers: Hook to global list of devices waiting for suppliers. - * @defer_sync: Hook to global list of devices that have deferred sync_state. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; struct list_head needs_suppliers; - struct list_head defer_sync; enum dl_dev_state status; }; @@ -1262,9 +1242,6 @@ struct dev_links_info { * device. * @has_edit_links: This device has a driver than is capable of * editing the device links created by driver core. - * @state_synced: The hardware state of this device has been synced to match - * the software state of this device by calling the driver/bus - * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @@ -1362,7 +1339,6 @@ struct device { bool offline:1; bool of_node_reused:1; bool has_edit_links:1; - bool state_synced:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -1707,8 +1683,6 @@ struct device_link *device_link_add(struct device *consumer, void device_link_del(struct device_link *link); void device_link_remove(void *consumer, struct device *supplier); void device_link_remove_from_wfs(struct device *consumer); -void device_links_supplier_sync_state_pause(void); -void device_links_supplier_sync_state_resume(void); #ifndef dev_fmt #define dev_fmt(fmt) fmt -- cgit v1.2.3-59-g8ed1b From 33cbfe54499338af08ab906a99afac247ea533f6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Aug 2019 21:41:06 +0200 Subject: Revert "driver core: Add edit_links() callback for drivers" This reverts commit 134b23eec9e3a3c795a6ceb0efe2fa63e87983b2. Based on a lot of email and in-person discussions, this patch series is being reworked to address a number of issues that were pointed out that needed to be taken care of before it should be merged. It will be resubmitted with those changes hopefully soon. Cc: Frank Rowand Cc: Saravana Kannan Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 24 ++---------------------- drivers/base/dd.c | 29 ----------------------------- include/linux/device.h | 20 -------------------- 3 files changed, 2 insertions(+), 71 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index de775c7a4d7c..605905de0cca 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -439,19 +439,6 @@ static void device_link_wait_for_supplier(struct device *consumer) mutex_unlock(&wfs_lock); } -/** - * device_link_remove_from_wfs - Unmark device as waiting for supplier - * @consumer: Consumer device - * - * Unmark the consumer device as waiting for suppliers to become available. - */ -void device_link_remove_from_wfs(struct device *consumer) -{ - mutex_lock(&wfs_lock); - list_del_init(&consumer->links.needs_suppliers); - mutex_unlock(&wfs_lock); -} - /** * device_link_check_waiting_consumers - Try to unmark waiting consumers * @@ -469,19 +456,12 @@ void device_link_remove_from_wfs(struct device *consumer) static void device_link_check_waiting_consumers(void) { struct device *dev, *tmp; - int ret; mutex_lock(&wfs_lock); list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) { - ret = 0; - if (dev->has_edit_links) - ret = driver_edit_links(dev); - else if (dev->bus->add_links) - ret = dev->bus->add_links(dev); - if (!ret) + links.needs_suppliers) + if (!dev->bus->add_links(dev)) list_del_init(&dev->links.needs_suppliers); - } mutex_unlock(&wfs_lock); } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 55fbc2467b37..d811e60610d3 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -710,12 +710,6 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); - if (drv->edit_links) { - if (drv->edit_links(dev)) - dev->has_edit_links = true; - else - device_link_remove_from_wfs(dev); - } pm_runtime_get_suppliers(dev); if (dev->parent) pm_runtime_get_sync(dev->parent); @@ -804,29 +798,6 @@ struct device_attach_data { bool have_async; }; -static int __driver_edit_links(struct device_driver *drv, void *data) -{ - struct device *dev = data; - - if (!drv->edit_links) - return 0; - - if (driver_match_device(drv, dev) <= 0) - return 0; - - return drv->edit_links(dev); -} - -int driver_edit_links(struct device *dev) -{ - int ret; - - device_lock(dev); - ret = bus_for_each_drv(dev->bus, NULL, dev, __driver_edit_links); - device_unlock(dev); - return ret; -} - static int __device_attach_driver(struct device_driver *drv, void *_data) { struct device_attach_data *data = _data; diff --git a/include/linux/device.h b/include/linux/device.h index 90142ec9ce84..73210745cc6b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -349,20 +349,6 @@ enum probe_type { * @probe_type: Type of the probe (synchronous or asynchronous) to use. * @of_match_table: The open firmware table. * @acpi_match_table: The ACPI match table. - * @edit_links: Called to allow a matched driver to edit the device links the - * bus might have added incorrectly. This will be useful to handle - * cases where the bus incorrectly adds functional dependencies - * that aren't true or tries to create cyclic dependencies. But - * doesn't correctly handle functional dependencies that are - * missed by the bus as the supplier's sync_state might get to - * execute before the driver for a missing consumer is loaded and - * gets to edit the device links for the consumer. - * - * This function might be called multiple times after a new device - * is added. The function is expected to create all the device - * links for the new device and return 0 if it was completed - * successfully or return an error if it needs to be reattempted - * in the future. * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. @@ -404,7 +390,6 @@ struct device_driver { const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; - int (*edit_links)(struct device *dev); int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); @@ -1240,8 +1225,6 @@ struct dev_links_info { * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. - * @has_edit_links: This device has a driver than is capable of - * editing the device links created by driver core. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @@ -1338,7 +1321,6 @@ struct device { bool offline_disabled:1; bool offline:1; bool of_node_reused:1; - bool has_edit_links:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -1590,7 +1572,6 @@ extern int __must_check device_attach(struct device *dev); extern int __must_check driver_attach(struct device_driver *drv); extern void device_initial_probe(struct device *dev); extern int __must_check device_reprobe(struct device *dev); -extern int driver_edit_links(struct device *dev); extern bool device_is_bound(struct device *dev); @@ -1682,7 +1663,6 @@ struct device_link *device_link_add(struct device *consumer, struct device *supplier, u32 flags); void device_link_del(struct device_link *link); void device_link_remove(void *consumer, struct device *supplier); -void device_link_remove_from_wfs(struct device *consumer); #ifndef dev_fmt #define dev_fmt(fmt) fmt -- cgit v1.2.3-59-g8ed1b From bfb3943bed670a0655aa2f9dcf82222ff09d6dd1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Aug 2019 21:41:16 +0200 Subject: Revert "driver core: Add support for linking devices during device addition" This reverts commit 5302dd7dd0b6d04c63cdce51d1e9fda9ef0be886. Based on a lot of email and in-person discussions, this patch series is being reworked to address a number of issues that were pointed out that needed to be taken care of before it should be merged. It will be resubmitted with those changes hopefully soon. Cc: Frank Rowand Cc: Saravana Kannan Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 83 -------------------------------------------------- include/linux/device.h | 14 --------- 2 files changed, 97 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index 605905de0cca..066badad988c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -44,8 +44,6 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); #endif /* Device links support. */ -static LIST_HEAD(wait_for_suppliers); -static DEFINE_MUTEX(wfs_lock); #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -420,51 +418,6 @@ struct device_link *device_link_add(struct device *consumer, } EXPORT_SYMBOL_GPL(device_link_add); -/** - * device_link_wait_for_supplier - Mark device as waiting for supplier - * @consumer: Consumer device - * - * Marks the consumer device as waiting for suppliers to become available. The - * consumer device will never be probed until it's unmarked as waiting for - * suppliers. The caller is responsible for adding the link to the supplier - * once the supplier device is present. - * - * This function is NOT meant to be called from the probe function of the - * consumer but rather from code that creates/adds the consumer device. - */ -static void device_link_wait_for_supplier(struct device *consumer) -{ - mutex_lock(&wfs_lock); - list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); - mutex_unlock(&wfs_lock); -} - -/** - * device_link_check_waiting_consumers - Try to unmark waiting consumers - * - * Loops through all consumers waiting on suppliers and tries to add all their - * supplier links. If that succeeds, the consumer device is unmarked as waiting - * for suppliers. Otherwise, they are left marked as waiting on suppliers, - * - * The add_links bus callback is expected to return 0 if it has found and added - * all the supplier links for the consumer device. It should return an error if - * it isn't able to do so. - * - * The caller of device_link_wait_for_supplier() is expected to call this once - * it's aware of potential suppliers becoming available. - */ -static void device_link_check_waiting_consumers(void) -{ - struct device *dev, *tmp; - - mutex_lock(&wfs_lock); - list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, - links.needs_suppliers) - if (!dev->bus->add_links(dev)) - list_del_init(&dev->links.needs_suppliers); - mutex_unlock(&wfs_lock); -} - static void device_link_free(struct device_link *link) { while (refcount_dec_not_one(&link->rpm_active)) @@ -599,19 +552,6 @@ int device_links_check_suppliers(struct device *dev) struct device_link *link; int ret = 0; - /* - * If a device is waiting for one or more suppliers (in - * wait_for_suppliers list), it is not ready to probe yet. So just - * return -EPROBE_DEFER without having to check the links with existing - * suppliers. - */ - mutex_lock(&wfs_lock); - if (!list_empty(&dev->links.needs_suppliers)) { - mutex_unlock(&wfs_lock); - return -EPROBE_DEFER; - } - mutex_unlock(&wfs_lock); - device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { @@ -896,10 +836,6 @@ static void device_links_purge(struct device *dev) { struct device_link *link, *ln; - mutex_lock(&wfs_lock); - list_del(&dev->links.needs_suppliers); - mutex_unlock(&wfs_lock); - /* * Delete all of the remaining links from this device to any other * devices (either consumers or suppliers). @@ -1764,7 +1700,6 @@ void device_initialize(struct device *dev) #endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); - INIT_LIST_HEAD(&dev->links.needs_suppliers); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); @@ -2251,24 +2186,6 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); - - /* - * Check if any of the other devices (consumers) have been waiting for - * this device (supplier) to be added so that they can create a device - * link to it. - * - * This needs to happen after device_pm_add() because device_link_add() - * requires the supplier be registered before it's called. - * - * But this also needs to happe before bus_probe_device() to make sure - * waiting consumers can link to it before the driver is bound to the - * device and the driver sync_state callback is called for this device. - */ - device_link_check_waiting_consumers(); - - if (dev->bus && dev->bus->add_links && dev->bus->add_links(dev)) - device_link_wait_for_supplier(dev); - bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, diff --git a/include/linux/device.h b/include/linux/device.h index 73210745cc6b..ec598ede9455 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -78,17 +78,6 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * -EPROBE_DEFER it will queue the device for deferred probing. * @uevent: Called when a device is added, removed, or a few other things * that generate uevents to add the environment variables. - * @add_links: Called, perhaps multiple times per device, after a device is - * added to this bus. The function is expected to create device - * links to all the suppliers of the input device that are - * available at the time this function is called. As in, the - * function should NOT stop at the first failed device link if - * other unlinked supplier devices are present in the system. - * - * Return 0 if device links have been successfully created to all - * the suppliers of this device. Return an error if some of the - * suppliers are not yet available and this function needs to be - * reattempted in the future. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. * @remove: Called when a device removed from this bus. @@ -133,7 +122,6 @@ struct bus_type { int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); - int (*add_links)(struct device *dev); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -1143,13 +1131,11 @@ enum dl_dev_state { * struct dev_links_info - Device data related to device links. * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. - * @needs_suppliers: Hook to global list of devices waiting for suppliers. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; - struct list_head needs_suppliers; enum dl_dev_state status; }; -- cgit v1.2.3-59-g8ed1b From 8973ea47901c81a1912bd05f1577bed9b5b52506 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 28 Aug 2019 10:34:10 +0200 Subject: driver core: platform: Introduce platform_get_irq_optional() In some cases the interrupt line of a device is optional. Introduce a new platform_get_irq_optional() that works much like platform_get_irq() but does not output an error on failure to find the interrupt. Signed-off-by: Thierry Reding Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20190828083411.2496-1-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 22 ++++++++++++++++++++++ include/linux/platform_device.h | 1 + 2 files changed, 23 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 8ad701068c11..0dda6ade50fd 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -192,6 +192,28 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) } EXPORT_SYMBOL_GPL(platform_get_irq); +/** + * platform_get_irq_optional - get an optional IRQ for a device + * @dev: platform device + * @num: IRQ number index + * + * Gets an IRQ for a platform device. Device drivers should check the return + * value for errors so as to not pass a negative integer value to the + * request_irq() APIs. This is the same as platform_get_irq(), except that it + * does not print an error message if an IRQ can not be obtained. + * + * Example: + * int irq = platform_get_irq_optional(pdev, 0); + * if (irq < 0) + * return irq; + * + * Return: IRQ number on success, negative error number on failure. + */ +int platform_get_irq_optional(struct platform_device *dev, unsigned int num) +{ + return __platform_get_irq(dev, num); +} + /** * platform_irq_count - Count the number of IRQs a platform device uses * @dev: platform device diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 37e15a935a42..35bc4355a9df 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -58,6 +58,7 @@ extern void __iomem * devm_platform_ioremap_resource(struct platform_device *pdev, unsigned int index); extern int platform_get_irq(struct platform_device *, unsigned int); +extern int platform_get_irq_optional(struct platform_device *, unsigned int); extern int platform_irq_count(struct platform_device *); extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, -- cgit v1.2.3-59-g8ed1b From d9430f96c051ede4e4a3d5a20e68518181e9cd3c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 29 Aug 2019 09:29:32 +0200 Subject: driver core: platform: Export platform_get_irq_optional() This function can be used by modules, so it needs to be exported. Reported-by: kbuild test robot Reported-by: Stephen Rothwell Signed-off-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 0dda6ade50fd..11c6e56ccc22 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -213,6 +213,7 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num) { return __platform_get_irq(dev, num); } +EXPORT_SYMBOL_GPL(platform_get_irq_optional); /** * platform_irq_count - Count the number of IRQs a platform device uses -- cgit v1.2.3-59-g8ed1b