[PATCH 2/9] driver core: prevent device_add() during system shutdown
From: David Jeffery
Date: Wed Sep 02 2026 - 14:24:11 EST
From: Tarun Sahu <tarunsahu@xxxxxxxxxx>
In Async device shutdown, device_kset->list lock is released to handle
asynchronisation and hold again to get entry from device_kset->list.
Which will leave window when device_add can try to add the device to
device_kset list and temper with ongoing shutdown process. New added
device can be async type or sync type and might also introduce new
dependency which can cause device_shutdown path to deadlock. S is
waiting C to finish but C is never scheduled as it was added recently
from device_add path. And C can only be scheduled when main loops
continue to reach to C which is waiting on S.
So, When a system enters shutdown (SYSTEM_HALT, SYSTEM_POWER_OFF, or
SYSTEM_RESTART), new devices should not be allowed to be added.
Adding system_state check (system_is_shutting_down()) to avoid
device_add incase of these states of the system. And use srcu so the
device shutdown operation can synchronize device_add and ensure any
device_add in progress is finished.
Signed-off-by: Tarun Sahu <tarunsahu@xxxxxxxxxx>
Signed-off-by: David Jeffery <djeffery@xxxxxxxxxx>
Tested-by: Laurence Oberman <loberman@xxxxxxxxxx>
---
drivers/base/core.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 83263e3fa5d4..bce555dd74f6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -47,6 +47,22 @@ static bool fw_devlink_drv_reg_done;
static bool fw_devlink_best_effort;
static struct workqueue_struct *device_link_wq;
+/**
+ * system_is_shutting_down - Check if system state is not active.
+ *
+ * When system state is not active and in shutdown state, new devices
+ * should not be allowed to be added.
+ *
+ * If system_state is SYSTEM_HALT || SYSTEM_POWER_OFF || SYSTEM_RESTART
+ * this function will return true.
+ */
+static inline bool system_is_shutting_down(void)
+{
+ return system_state == SYSTEM_HALT ||
+ system_state == SYSTEM_POWER_OFF ||
+ system_state == SYSTEM_RESTART;
+}
+
/**
* __fwnode_link_add - Create a link between two fwnode_handles.
* @con: Consumer end of the link.
@@ -3614,6 +3630,9 @@ static int device_private_init(struct device *dev)
return 0;
}
+
+DEFINE_STATIC_SRCU(device_add_srcu);
+
/**
* device_add - add device to device hierarchy.
* @dev: device.
@@ -3647,13 +3666,20 @@ int device_add(struct device *dev)
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
- int error = -EINVAL;
+ int idx, error = -EINVAL;
struct kobject *glue_dir = NULL;
+ idx = srcu_read_lock(&device_add_srcu);
+
dev = get_device(dev);
if (!dev)
goto done;
+ if (unlikely(system_is_shutting_down())) {
+ error = -ESHUTDOWN;
+ goto done;
+ }
+
if (!dev->p) {
error = device_private_init(dev);
if (error)
@@ -3803,6 +3829,7 @@ int device_add(struct device *dev)
}
done:
put_device(dev);
+ srcu_read_unlock(&device_add_srcu, idx);
return error;
SysEntryError:
if (MAJOR(dev->devt))
@@ -4877,6 +4904,7 @@ void device_shutdown(void)
wait_for_device_probe();
device_block_probing();
+ synchronize_srcu(&device_add_srcu);
cpufreq_suspend();
--
2.55.0