]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
i2c: core: fix hang on adapter registration failure
authorJohan Hovold <johan@kernel.org>
Mon, 11 May 2026 14:37:07 +0000 (16:37 +0200)
committerWolfram Sang <wsa+renesas@sang-engineering.com>
Sat, 30 May 2026 21:57:18 +0000 (23:57 +0200)
Clients may be registered from bus notifier callbacks when the adapter
is registered. On a subsequent error during registration, the adapter
references taken by such clients prevent the wait for the references to
be released from ever completing.

Fix this by refactoring client deregistration and deregistering also on
late adapter registration failures.

Fixes: f8756c67b3de ("i2c: core: call of_i2c_setup_smbus_alert in i2c_register_adapter")
Cc: stable@vger.kernel.org # 4.15
Cc: Phil Reid <preid@electromag.com.au>
Signed-off-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
drivers/i2c/i2c-core-base.c

index abe8341c1d6e6dc6fabfe9f282ec6a8618275e17..e42851a10098101532fd3e287a2ece0d4ff2b09d 100644 (file)
@@ -63,6 +63,7 @@
 static DEFINE_MUTEX(core_lock);
 static DEFINE_IDR(i2c_adapter_idr);
 
+static void i2c_deregister_clients(struct i2c_adapter *adap);
 static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
 
 static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key);
@@ -1605,6 +1606,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
        return 0;
 
 out_reg:
+       i2c_deregister_clients(adap);
        debugfs_remove_recursive(adap->debugfs);
        init_completion(&adap->dev_released);
        device_unregister(&adap->dev);
@@ -1746,29 +1748,10 @@ static int __process_removed_adapter(struct device_driver *d, void *data)
        return 0;
 }
 
-/**
- * i2c_del_adapter - unregister I2C adapter
- * @adap: the adapter being unregistered
- * Context: can sleep
- *
- * This unregisters an I2C adapter which was previously registered
- * by @i2c_add_adapter or @i2c_add_numbered_adapter.
- */
-void i2c_del_adapter(struct i2c_adapter *adap)
+static void i2c_deregister_clients(struct i2c_adapter *adap)
 {
-       struct i2c_adapter *found;
        struct i2c_client *client, *next;
 
-       /* First make sure that this adapter was ever added */
-       mutex_lock(&core_lock);
-       found = idr_find(&i2c_adapter_idr, adap->nr);
-       mutex_unlock(&core_lock);
-       if (found != adap) {
-               pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
-               return;
-       }
-
-       i2c_acpi_remove_space_handler(adap);
        /* Tell drivers about this removal */
        mutex_lock(&core_lock);
        bus_for_each_drv(&i2c_bus_type, NULL, adap,
@@ -1794,6 +1777,32 @@ void i2c_del_adapter(struct i2c_adapter *adap)
         * them up properly, so we give them a chance to do that first. */
        device_for_each_child(&adap->dev, NULL, __unregister_client);
        device_for_each_child(&adap->dev, NULL, __unregister_dummy);
+}
+
+/**
+ * i2c_del_adapter - unregister I2C adapter
+ * @adap: the adapter being unregistered
+ * Context: can sleep
+ *
+ * This unregisters an I2C adapter which was previously registered
+ * by @i2c_add_adapter or @i2c_add_numbered_adapter.
+ */
+void i2c_del_adapter(struct i2c_adapter *adap)
+{
+       struct i2c_adapter *found;
+
+       /* First make sure that this adapter was ever added */
+       mutex_lock(&core_lock);
+       found = idr_find(&i2c_adapter_idr, adap->nr);
+       mutex_unlock(&core_lock);
+       if (found != adap) {
+               pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
+               return;
+       }
+
+       i2c_acpi_remove_space_handler(adap);
+
+       i2c_deregister_clients(adap);
 
        /* device name is gone after device_unregister */
        dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);