extcon: Use unique number for the extcon device ID
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Tue, 11 Apr 2023 11:48:22 +0000 (14:48 +0300)
committerChanwoo Choi <cw00.choi@samsung.com>
Mon, 29 May 2023 14:41:29 +0000 (23:41 +0900)
The use of atomic variable is still racy when we do not control which
device has been unregistered and there is a (theoretical) possibility
of the overflow that may cause a duplicate extcon device ID number
to be allocated next time a device is registered.

Replace above mentioned approach by using IDA framework.

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
drivers/extcon/extcon.c
drivers/extcon/extcon.h

index 47819c5144d5739f00829ce896e24ddce8462678..5da1cc60582a5ea65de349ab8de5bf4b397e3886 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/idr.h>
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/fs.h>
@@ -238,6 +239,7 @@ struct extcon_cable {
 
 static struct class *extcon_class;
 
+static DEFINE_IDA(extcon_dev_ids);
 static LIST_HEAD(extcon_dev_list);
 static DEFINE_MUTEX(extcon_dev_list_lock);
 
@@ -1248,7 +1250,6 @@ static int extcon_alloc_groups(struct extcon_dev *edev)
 int extcon_dev_register(struct extcon_dev *edev)
 {
        int ret, index = 0;
-       static atomic_t edev_no = ATOMIC_INIT(-1);
 
        ret = create_extcon_class();
        if (ret < 0)
@@ -1275,8 +1276,14 @@ int extcon_dev_register(struct extcon_dev *edev)
                        "extcon device name is null\n");
                return -EINVAL;
        }
-       dev_set_name(&edev->dev, "extcon%lu",
-                       (unsigned long)atomic_inc_return(&edev_no));
+
+       ret = ida_alloc(&extcon_dev_ids, GFP_KERNEL);
+       if (ret < 0)
+               return ret;
+
+       edev->id = ret;
+
+       dev_set_name(&edev->dev, "extcon%d", edev->id);
 
        ret = extcon_alloc_cables(edev);
        if (ret < 0)
@@ -1339,6 +1346,7 @@ err_alloc_muex:
        if (edev->max_supported)
                kfree(edev->cables);
 err_alloc_cables:
+       ida_free(&extcon_dev_ids, edev->id);
 
        return ret;
 }
@@ -1367,6 +1375,8 @@ void extcon_dev_unregister(struct extcon_dev *edev)
                return;
        }
 
+       ida_free(&extcon_dev_ids, edev->id);
+
        device_unregister(&edev->dev);
 
        if (edev->mutually_exclusive && edev->max_supported) {
index 15616446140d503757101ad25fa1a085f56af179..94618268778603bf66a8330a26f536214cc35311 100644 (file)
@@ -20,6 +20,7 @@
  *                     {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
  *                     can be no simultaneous connections.
  * @dev:               Device of this extcon.
+ * @id:                        Unique device ID of this extcon.
  * @state:             Attach/detach state of this extcon. Do not provide at
  *                     register-time.
  * @nh_all:            Notifier for the state change events for all supported
@@ -46,6 +47,7 @@ struct extcon_dev {
 
        /* Internal data. Please do not set. */
        struct device dev;
+       unsigned int id;
        struct raw_notifier_head nh_all;
        struct raw_notifier_head *nh;
        struct list_head entry;