diff mbox series

[RFC,1/2] driver core: Add ability to delete device links of unregistered devices

Message ID 20210707172948.1025-2-adrian.hunter@intel.com
State New
Headers show
Series driver core: Add ability to delete device links of unregistered devices | expand

Commit Message

Adrian Hunter July 7, 2021, 5:29 p.m. UTC
Managed device links are deleted by device_del(). However it is possible to
add a device link to a consumer before device_add(), and then discover an
error prevents the device from being used. In that case normally references
to the device would be dropped and the device would be deleted. However the
device link holds a reference to the device, so the device link and device
remain indefinitely.

Add a function to delete device links of an unregistered device, so that
drivers can get rid of unregistered devices and their device links.

To do that, the devlink_remove_symlinks() function must be amended to cope
with the absence of the consumer's sysfs presence, otherwise
sysfs_remove_link() will generate warnings.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
 Documentation/driver-api/device_link.rst |  7 +++++--
 drivers/base/core.c                      | 22 +++++++++++++++++++---
 include/linux/device.h                   |  1 +
 3 files changed, 25 insertions(+), 5 deletions(-)
diff mbox series


diff --git a/Documentation/driver-api/device_link.rst b/Documentation/driver-api/device_link.rst
index ee913ae16371..12c0602822c8 100644
--- a/Documentation/driver-api/device_link.rst
+++ b/Documentation/driver-api/device_link.rst
@@ -75,7 +75,9 @@  driver is compiled as a module, the device link is added on module load and
 orderly deleted on unload.  The same restrictions that apply to device link
 addition (e.g. exclusion of a parallel suspend/resume transition) apply equally
 to deletion.  Device links managed by the driver core are deleted automatically
-by it.
+by it, except if the consumer device is never registered (e.g. because of an
+error), in which case the device links must be explicitly removed using
 Several flags may be specified on device link addition, two of which
 have already been mentioned above:  ``DL_FLAG_STATELESS`` to express that no
@@ -317,4 +319,5 @@  State machine
-See device_link_add(), device_link_del() and device_link_remove().
+See device_link_add(), device_link_del(), device_link_remove() and
diff --git a/drivers/base/core.c b/drivers/base/core.c
index ea5b85354526..2ed32ab50b84 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -562,7 +562,8 @@  static void devlink_remove_symlinks(struct device *dev,
 	struct device *con = link->consumer;
 	char *buf;
-	sysfs_remove_link(&link->link_dev.kobj, "consumer");
+	if (device_is_registered(con))
+		sysfs_remove_link(&link->link_dev.kobj, "consumer");
 	sysfs_remove_link(&link->link_dev.kobj, "supplier");
 	len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)),
@@ -575,8 +576,10 @@  static void devlink_remove_symlinks(struct device *dev,
-	snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
-	sysfs_remove_link(&con->kobj, buf);
+	if (device_is_registered(con)) {
+		snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
+		sysfs_remove_link(&con->kobj, buf);
+	}
 	snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
 	sysfs_remove_link(&sup->kobj, buf);
@@ -1538,6 +1541,19 @@  static void device_links_purge(struct device *dev)
+ * device_links_scrap - Device was never registered, delete any device links.
+ * @dev: Target device.
+ */
+void device_links_scrap(struct device *dev)
+	if (WARN_ON(device_is_registered(dev)))
+		return;
+	device_links_purge(dev);
diff --git a/include/linux/device.h b/include/linux/device.h
index 2a22875238a6..90574f9aa8f2 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -967,6 +967,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_links_scrap(struct device *dev);
 void device_links_supplier_sync_state_pause(void);
 void device_links_supplier_sync_state_resume(void);