Linux PCI subsystem development
 help / color / mirror / Atom feed
* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-03-19 14:11 [PATCH v12 0/5] shut down devices asynchronously David Jeffery
@ 2026-03-19 14:11 ` David Jeffery
  0 siblings, 0 replies; 19+ messages in thread
From: David Jeffery @ 2026-03-19 14:11 UTC (permalink / raw)
  To: linux-kernel, driver-core, linux-pci, linux-scsi,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: Tarun Sahu, Pasha Tatashin, Michał Cłapiński,
	Jordan Richards, Ewan Milne, John Meneghini, Lombardi, Maurizio,
	Stuart Hayes, Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	David Jeffery

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 791f9e444df8..10586298e18b 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4782,12 +4782,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4809,7 +4845,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4818,36 +4854,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-04-07 15:35 [PATCH v13 0/5] shut down devices asynchronously David Jeffery
@ 2026-04-07 15:35 ` David Jeffery
  0 siblings, 0 replies; 19+ messages in thread
From: David Jeffery @ 2026-04-07 15:35 UTC (permalink / raw)
  To: linux-kernel, driver-core, linux-pci, linux-scsi,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: Tarun Sahu, Pasha Tatashin, Michał Cłapiński,
	Jordan Richards, Ewan Milne, John Meneghini, Lombardi, Maurizio,
	Stuart Hayes, Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, David Jeffery

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 09b98f02f559..fabd17be1175 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4784,12 +4784,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4811,7 +4847,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4820,36 +4856,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-04-20 15:26 [PATCH v14 0/5] shut down devices asynchronously David Jeffery
@ 2026-04-20 15:26 ` David Jeffery
  2026-04-20 20:25   ` Pasha Tatashin
  0 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-04-20 15:26 UTC (permalink / raw)
  To: linux-kernel, driver-core, linux-pci, linux-scsi,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: Tarun Sahu, Pasha Tatashin, Michał Cłapiński,
	Jordan Richards, Ewan Milne, John Meneghini, Lombardi, Maurizio,
	Stuart Hayes, Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, David Jeffery

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0613de0fbe44..5353c6c22d49 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4783,12 +4783,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4810,7 +4846,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4819,36 +4855,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/5] driver core: separate function to shutdown one device
  2026-04-20 15:26 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
@ 2026-04-20 20:25   ` Pasha Tatashin
  0 siblings, 0 replies; 19+ messages in thread
From: Pasha Tatashin @ 2026-04-20 20:25 UTC (permalink / raw)
  To: David Jeffery
  Cc: linux-kernel, driver-core, linux-pci, linux-scsi,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
	Tarun Sahu, Pasha Tatashin, Michał Cłapiński,
	Jordan Richards, Ewan Milne, John Meneghini, Lombardi, Maurizio,
	Stuart Hayes, Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec

On 04-20 11:26, David Jeffery wrote:
> Make a separate function for the part of device_shutdown() that does the
> shutown for a single device.  This is in preparation for making device
> shutdown asynchronous.
> 
> Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
> Signed-off-by: David Jeffery <djeffery@redhat.com>
> Tested-by: Laurence Oberman <loberman@redhat.com>
> ---
>  drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
>  1 file changed, 39 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 0613de0fbe44..5353c6c22d49 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -4783,12 +4783,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
>  	return error;
>  }
>  
> +static void shutdown_one_device(struct device *dev)
> +{
> +	struct device *parent = dev->parent;
> +
> +	/* hold lock to avoid race with probe/release */
> +	if (parent)
> +		device_lock(parent);
> +	device_lock(dev);
> +
> +	/* Don't allow any more runtime suspends */
> +	pm_runtime_get_noresume(dev);
> +	pm_runtime_barrier(dev);
> +
> +	if (dev->class && dev->class->shutdown_pre) {
> +		if (initcall_debug)
> +			dev_info(dev, "shutdown_pre\n");
> +		dev->class->shutdown_pre(dev);
> +	}
> +	if (dev->bus && dev->bus->shutdown) {
> +		if (initcall_debug)
> +			dev_info(dev, "shutdown\n");
> +		dev->bus->shutdown(dev);
> +	} else if (dev->driver && dev->driver->shutdown) {
> +		if (initcall_debug)
> +			dev_info(dev, "shutdown\n");
> +		dev->driver->shutdown(dev);
> +	}
> +
> +	device_unlock(dev);
> +	if (parent)
> +		device_unlock(parent);
> +
> +	put_device(parent);
> +	put_device(dev);
> +}

Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com> 

Also, please CC: kexec@lists.infradead.org, this series helps the live 
update, and kexec performance.

Pasha

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-04-29 17:50 [PATCH v15 0/5] shut down devices asynchronously David Jeffery
@ 2026-04-29 17:50 ` David Jeffery
  0 siblings, 0 replies; 19+ messages in thread
From: David Jeffery @ 2026-04-29 17:50 UTC (permalink / raw)
  To: linux-kernel, driver-core, linux-pci, linux-scsi,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: Tarun Sahu, Pasha Tatashin, Michał Cłapiński,
	Jordan Richards, Ewan Milne, John Meneghini, Lombardi, Maurizio,
	Stuart Hayes, Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery,
	Pasha Tatashin

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index bd2ddf2aab50..90ef21c1e713 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4799,12 +4799,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4826,7 +4862,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4835,36 +4871,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-05-18 19:31 [PATCH v16 0/5] shut down devices asynchronously David Jeffery
@ 2026-05-18 19:32 ` David Jeffery
  0 siblings, 0 replies; 19+ messages in thread
From: David Jeffery @ 2026-05-18 19:32 UTC (permalink / raw)
  To: linux-kernel, driver-core, linux-pci, linux-scsi,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich
  Cc: Tarun Sahu, Pasha Tatashin, Michał Cłapiński,
	Jordan Richards, Ewan Milne, John Meneghini, Lombardi, Maurizio,
	Stuart Hayes, Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery,
	Pasha Tatashin

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Tested-by: Tarun Sahu <tarunsahu@google.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index d49420e066de..127432248165 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4795,12 +4795,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4822,7 +4858,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4831,36 +4867,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-06-16 15:22 [PATCH v17 0/5] shut down devices asynchronously David Jeffery
@ 2026-06-16 15:22 ` David Jeffery
  2026-06-16 15:34   ` sashiko-bot
  0 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-06-16 15:22 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery,
	Pasha Tatashin

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4d026682944f..74c693cd19cf 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4865,12 +4865,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4892,7 +4928,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4901,36 +4937,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/5] driver core: separate function to shutdown one device
  2026-06-16 15:22 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
@ 2026-06-16 15:34   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-06-16 15:34 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

> Make a separate function for the part of device_shutdown() that does the
> shutown for a single device.  This is in preparation for making device
> shutdown asynchronous.
> 
> Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
> Signed-off-by: David Jeffery <djeffery@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616152219.6268-1-djeffery@redhat.com?part=1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v18 0/5] shut down devices asynchronously
@ 2026-07-01 13:50 David Jeffery
  2026-07-01 13:50 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: David Jeffery @ 2026-07-01 13:50 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery

These patches are rebased against the driver-core tree's driver-core-next
branch and should also apply against recent linux-next. Changes for v18 are
contained in patch 3.

This patchset allows the kernel to shutdown devices asynchronously and
unrelated async devices to be shut down in parallel to each other.

Only devices which explicitly enable it are shut down asynchronously. The
default is for a device to be shut down from the synchronous shutdown loop.

This can dramatically reduce system shutdown/reboot time on systems that
have multiple devices that take many seconds to shut down (like certain
NVMe drives). On one system tested, the shutdown time went from 11 minutes
without this patch to 55 seconds with the patch. And on another system from
80 seconds to 11.

And thank you to everyone who has spent some of their valuable time
providing reviews, suggestions, criticisms, or tests on the various
iterations of this patchset.

Changes from V17:

Fix mangled text in kernel parameter description
Re-protect the list removal with the spinlock
  * Hold a device reference to ensure the device cannot be freed before
    attempting list removal

Changes from V16:

Drop spinlock before async subsystem call which uses GFP_KERNEL
Handle that async shutdown can widen races between device shutdown and deletion
  * __shutdown_one_device will immediately return if a device is dead
  * Set shutdown device completion to complete when marking a device dead to
      prevent waiting on a dead device
  * Only late-access a parent pointer if device is in a non-dead state to
      ensure the pointer is still valid

Changes from V15:

The async_shutdown bit field is converted to a device flags bit
Convert all patches to use the flag bit accessor macros to set or check if
  async shutdown should be used
Added documentation on the kernel parameter to control use of async shutdown

Changes from V14:

Remove unneeded use of '!!' with boolean type

Changes from V13:

Remove duplicate flagging of async shutdown on scsi hosts/targets/devices

Changes from V12:

Only acquire a parent reference if acquiring the parent's lock
device_enable_async_shutdown should return void
Minor comment and description cleanups

Changes from V11:

  * Swap the order of the first two patches
  * Rework conditional parent locking so that lock and unlock no longer use
    separate conditional checks
  * Remove an used variable
  * Comment and description text cleanups

Changes from V10:

Reworked to more closely match the design used for async suspend
  * No longer uses async subsystem cookies for synchronization
  * Minimized changes to struct device
  * Enable async shutdown for pci and scsi devices which support async suspend

Changes from V9:

Address resource and timing issues when spawning a unique async thread
for every device during shutdown:
  * Make the asynchronous threads able to shut down multiple devices,
    instead of spawning a unique thread for every device.
  * Modify core kernel async code with a custom wake function so it
    doesn't wake up a thread waiting to synchronize on a cookie until
    the cookie has reached the desired value, instead of waking up
    every waiting thread to check the cookie every time an async thread
    ends.

Changes from V8:

Deal with shutdown hangs resulting when a parent/supplier device is
  later in the devices_kset list than its children/consumers:
  * Ignore sync_state_only devlinks for shutdown dependencies
  * Ignore shutdown_after for devices that don't want async shutdown
  * Add a sanity check to revert to sync shutdown for any device that
    would otherwise wait for a child/consumer shutdown that hasn't
    already been scheduled

Changes from V7:

Do not expose driver async_shutdown_enable in sysfs.
Wrapped a long line.

Changes from V6:

Removed a sysfs attribute that allowed the async device shutdown to be
"on" (with driver opt-out), "safe" (driver opt-in), or "off"... what was
previously "safe" is now the only behavior, so drivers now only need to
have the option to enable or disable async shutdown.

Changes from V5:

Separated into multiple patches to make review easier.
Reworked some code to make it more readable
Made devices wait for consumers to shut down, not just children
  (suggested by David Jeffery)

Changes from V4:

Change code to use cookies for synchronization rather than async domains
Allow async shutdown to be disabled via sysfs, and allow driver opt-in or
  opt-out of async shutdown (when not disabled), with ability to control
  driver opt-in/opt-out via sysfs
  
Changes from V3:

Bug fix (used "parent" not "dev->parent" in device_shutdown)
 
Changes from V2:
 
Removed recursive functions to schedule children to be shutdown before
  parents, since existing device_shutdown loop will already do this
 
Changes from V1:

Rewritten using kernel async code (suggested by Lukas Wunner)


Stuart Hayes (2):
  driver core: separate function to shutdown one device
  driver core: do not always lock parent in shutdown

David Jeffery (3):
  driver core: async device shutdown infrastructure
  PCI: Enable async shutdown support
  scsi: Enable async shutdown support

 .../admin-guide/kernel-parameters.txt         |  10 +
 drivers/base/base.h                           |   2 +
 drivers/base/core.c                           | 221 +++++++++++++++---
 drivers/pci/probe.c                           |   2 +
 drivers/scsi/hosts.c                          |   2 +
 drivers/scsi/scsi_sysfs.c                     |   3 +
 include/linux/device.h                        |   2 +
 7 files changed, 206 insertions(+), 36 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/5] driver core: separate function to shutdown one device
  2026-07-01 13:50 [PATCH v18 0/5] shut down devices asynchronously David Jeffery
@ 2026-07-01 13:50 ` David Jeffery
  2026-07-01 13:58   ` sashiko-bot
  2026-07-01 13:50 ` [PATCH 2/5] driver core: do not always lock parent in shutdown David Jeffery
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-07-01 13:50 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery,
	Pasha Tatashin

Make a separate function for the part of device_shutdown() that does the
shutown for a single device.  This is in preparation for making device
shutdown asynchronous.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
---
 drivers/base/core.c | 71 +++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 32 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4d026682944f..74c693cd19cf 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4865,12 +4865,48 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent = dev->parent;
+
+	/* hold lock to avoid race with probe/release */
+	if (parent)
+		device_lock(parent);
+	device_lock(dev);
+
+	/* Don't allow any more runtime suspends */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_barrier(dev);
+
+	if (dev->class && dev->class->shutdown_pre) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown_pre\n");
+		dev->class->shutdown_pre(dev);
+	}
+	if (dev->bus && dev->bus->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->bus->shutdown(dev);
+	} else if (dev->driver && dev->driver->shutdown) {
+		if (initcall_debug)
+			dev_info(dev, "shutdown\n");
+		dev->driver->shutdown(dev);
+	}
+
+	device_unlock(dev);
+	if (parent)
+		device_unlock(parent);
+
+	put_device(parent);
+	put_device(dev);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4892,7 +4928,7 @@ void device_shutdown(void)
 		 * prevent it from being freed because parent's
 		 * lock is to be held
 		 */
-		parent = get_device(dev->parent);
+		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4901,36 +4937,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		/* hold lock to avoid race with probe/release */
-		if (parent)
-			device_lock(parent);
-		device_lock(dev);
-
-		/* Don't allow any more runtime suspends */
-		pm_runtime_get_noresume(dev);
-		pm_runtime_barrier(dev);
-
-		if (dev->class && dev->class->shutdown_pre) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown_pre\n");
-			dev->class->shutdown_pre(dev);
-		}
-		if (dev->bus && dev->bus->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->bus->shutdown(dev);
-		} else if (dev->driver && dev->driver->shutdown) {
-			if (initcall_debug)
-				dev_info(dev, "shutdown\n");
-			dev->driver->shutdown(dev);
-		}
-
-		device_unlock(dev);
-		if (parent)
-			device_unlock(parent);
-
-		put_device(dev);
-		put_device(parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/5] driver core: do not always lock parent in shutdown
  2026-07-01 13:50 [PATCH v18 0/5] shut down devices asynchronously David Jeffery
  2026-07-01 13:50 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
@ 2026-07-01 13:50 ` David Jeffery
  2026-07-01 14:02   ` sashiko-bot
  2026-07-01 13:50 ` [PATCH 3/5] driver core: async device shutdown infrastructure David Jeffery
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-07-01 13:50 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery

Don't lock a parent device unless it is needed in device_shutdown. This
is in preparation for making device shutdown asynchronous, when it will
be needed to allow children of a common parent to shut down
simultaneously.

And only acquire a reference to the parent device if the parent is to be
locked.

Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 42 ++++++++++++++++++++++++++----------------
 1 file changed, 26 insertions(+), 16 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 74c693cd19cf..3b3d983b1747 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4865,14 +4865,10 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
-static void shutdown_one_device(struct device *dev)
+static void __shutdown_one_device(struct device *dev)
 {
-	struct device *parent = dev->parent;
-
-	/* hold lock to avoid race with probe/release */
-	if (parent)
-		device_lock(parent);
-	device_lock(dev);
+	if (dev->p->dead)
+		return;
 
 	/* Don't allow any more runtime suspends */
 	pm_runtime_get_noresume(dev);
@@ -4892,12 +4888,32 @@ static void shutdown_one_device(struct device *dev)
 			dev_info(dev, "shutdown\n");
 		dev->driver->shutdown(dev);
 	}
+}
 
-	device_unlock(dev);
-	if (parent)
+static void shutdown_one_device(struct device *dev)
+{
+	struct device *parent;
+
+	device_lock(dev);
+
+	/* use parent lock if needed to avoid race with probe/release */
+	if (dev->bus && dev->bus->need_parent_lock && !dev->p->dead &&
+	    (parent = get_device(dev->parent))) {
+		/* the parent lock needs to be acquired first, so re-lock */
+		device_unlock(dev);
+
+		device_lock(parent);
+		device_lock(dev);
+
+		__shutdown_one_device(dev);
+		device_unlock(dev);
 		device_unlock(parent);
+		put_device(parent);
+	} else {
+		__shutdown_one_device(dev);
+		device_unlock(dev);
+	}
 
-	put_device(parent);
 	put_device(dev);
 }
 
@@ -4923,12 +4939,6 @@ void device_shutdown(void)
 		dev = list_entry(devices_kset->list.prev, struct device,
 				kobj.entry);
 
-		/*
-		 * hold reference count of device's parent to
-		 * prevent it from being freed because parent's
-		 * lock is to be held
-		 */
-		get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 3/5] driver core: async device shutdown infrastructure
  2026-07-01 13:50 [PATCH v18 0/5] shut down devices asynchronously David Jeffery
  2026-07-01 13:50 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
  2026-07-01 13:50 ` [PATCH 2/5] driver core: do not always lock parent in shutdown David Jeffery
@ 2026-07-01 13:50 ` David Jeffery
  2026-07-01 14:11   ` sashiko-bot
  2026-07-01 13:50 ` [PATCH 4/5] PCI: Enable async shutdown support David Jeffery
  2026-07-01 13:50 ` [PATCH 5/5] scsi: " David Jeffery
  4 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-07-01 13:50 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery

Patterned after async suspend, allow devices to mark themselves as wanting
to perform async shutdown. Devices using async shutdown wait only for their
dependencies to shutdown before executing their shutdown routine.

Sync shutdown devices are shut down one at a time and will only wait for an
async shutdown device if the async device is a dependency.

Enabled by default, async shutdown can be explicitly enabled or disabled
by using the kernel parameter "core.async_shutdown=<bool>"

Signed-off-by: David Jeffery <djeffery@redhat.com>
Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 .../admin-guide/kernel-parameters.txt         |  10 ++
 drivers/base/base.h                           |   2 +
 drivers/base/core.c                           | 134 +++++++++++++++++-
 include/linux/device.h                        |   2 +
 4 files changed, 147 insertions(+), 1 deletion(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5a51a36a048..7a428479089e 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1019,6 +1019,16 @@ Kernel parameters
 			seconds. A value of 0 disables the blank timer.
 			Defaults to 0.
 
+	core.async_shutdown=
+			[KNL]
+			Format: <bool>
+			Enable or disable asynchronous shutdown support. When
+			enabled, on system shutdown unrelated devices flagged
+			as async shutdown compatible may be shut down in
+			parallel and asynchronously. When disabled, device
+			shutdown is performed serially and synchronously.
+			Enabled by default.
+
 	coredump_filter=
 			[KNL] Change the default value for
 			/proc/<pid>/coredump_filter.
diff --git a/drivers/base/base.h b/drivers/base/base.h
index a5b7abc10ff0..40dbf588a5d6 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -103,6 +103,7 @@ struct driver_private {
  *			   dev_err_probe() for later retrieval via debugfs
  * @device: pointer back to the struct device that this structure is
  *	    associated with.
+ * @complete: completion for device shutdown ordering
  * @dead: This device is currently either in the process of or has been
  *	  removed from the system. Any asynchronous events scheduled for this
  *	  device should exit without taking any action.
@@ -119,6 +120,7 @@ struct device_private {
 	const struct device_driver *async_driver;
 	char *deferred_probe_reason;
 	struct device *device;
+	struct completion complete;
 	u8 dead:1;
 };
 #define to_device_private_parent(obj)	\
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 3b3d983b1747..c3c7d9c5cc4b 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/async.h>
 #include <linux/blkdev.h>
 #include <linux/cleanup.h>
 #include <linux/cpufreq.h>
@@ -37,6 +38,10 @@
 #include "physical_location.h"
 #include "power/power.h"
 
+static bool async_shutdown = true;
+module_param(async_shutdown, bool, 0644);
+MODULE_PARM_DESC(async_shutdown, "Enable asynchronous device shutdown support");
+
 /* Device links support. */
 static LIST_HEAD(deferred_sync);
 static unsigned int defer_sync_state_count = 1;
@@ -3606,6 +3611,7 @@ static int device_private_init(struct device *dev)
 	klist_init(&dev->p->klist_children, klist_children_get,
 		   klist_children_put);
 	INIT_LIST_HEAD(&dev->p->deferred_probe);
+	init_completion(&dev->p->complete);
 	return 0;
 }
 
@@ -3895,6 +3901,7 @@ bool kill_device(struct device *dev)
 	if (dev->p->dead)
 		return false;
 	dev->p->dead = true;
+	complete_all(&dev->p->complete);
 	return true;
 }
 EXPORT_SYMBOL_GPL(kill_device);
@@ -4865,6 +4872,37 @@ int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid)
 	return error;
 }
 
+static bool wants_async_shutdown(struct device *dev)
+{
+	return async_shutdown && dev_async_shutdown(dev);
+}
+
+static int wait_for_device_shutdown(struct device *dev, void *data)
+{
+	bool async = *(bool *)data;
+
+	if (async || wants_async_shutdown(dev))
+		wait_for_completion(&dev->p->complete);
+
+	return 0;
+}
+
+static void wait_for_shutdown_dependencies(struct device *dev, bool async)
+{
+	struct device_link *link;
+	int idx;
+
+	device_for_each_child(dev, &async, wait_for_device_shutdown);
+
+	idx = device_links_read_lock();
+
+	dev_for_each_link_to_consumer(link, dev)
+		if (!device_link_flag_is_sync_state_only(link->flags))
+			wait_for_device_shutdown(link->consumer, &async);
+
+	device_links_read_unlock(idx);
+}
+
 static void __shutdown_one_device(struct device *dev)
 {
 	if (dev->p->dead)
@@ -4888,6 +4926,8 @@ static void __shutdown_one_device(struct device *dev)
 			dev_info(dev, "shutdown\n");
 		dev->driver->shutdown(dev);
 	}
+
+	complete_all(&dev->p->complete);
 }
 
 static void shutdown_one_device(struct device *dev)
@@ -4917,6 +4957,87 @@ static void shutdown_one_device(struct device *dev)
 	put_device(dev);
 }
 
+static void async_shutdown_handler(void *data, async_cookie_t cookie)
+{
+	struct device *dev = data;
+
+	wait_for_shutdown_dependencies(dev, true);
+	shutdown_one_device(dev);
+}
+
+static bool shutdown_device_async(struct device *dev)
+{
+	if (async_schedule_dev_nocall(async_shutdown_handler, dev))
+		return true;
+
+	dev_clear_async_shutdown(dev);
+	return false;
+}
+
+
+static void start_async_shutdown_devices(void)
+{
+	struct device *dev, *next, *ndev, *needs_put = NULL;
+	bool clear_async = false;
+
+	if (!async_shutdown)
+		return;
+
+	spin_lock(&devices_kset->list_lock);
+restart:
+	list_for_each_entry_safe_reverse(dev, next, &devices_kset->list,
+					 kobj.entry) {
+		if (wants_async_shutdown(dev)) {
+			if (clear_async) {
+				dev_clear_async_shutdown(dev);
+				continue;
+			}
+			/* one device reference for this function */
+			get_device(dev);
+			/* another to pass to the async task */
+			get_device(dev);
+
+			if (!list_entry_is_head(next, &devices_kset->list,
+						kobj.entry))
+				ndev = get_device(next);
+			else
+				ndev = NULL;
+			spin_unlock(&devices_kset->list_lock);
+
+			if (shutdown_device_async(dev)) {
+				spin_lock(&devices_kset->list_lock);
+				list_del_init(&dev->kobj.entry);
+				spin_unlock(&devices_kset->list_lock);
+			} else {
+				/*
+				 * async failed, clean up extra reference
+				 * and run shutdown from the sync shutdown loop
+				 */
+				clear_async = true;
+				put_device(dev);
+			}
+			put_device(dev);
+
+			if (needs_put)
+				put_device(needs_put);
+			needs_put = ndev;
+			spin_lock(&devices_kset->list_lock);
+			/*
+			 * If the next device has been marked dead while the
+			 * spinlock was released, it may no longer be on the
+			 * devices_kset list. Restart the list walk to be safe
+			 */
+			if (ndev && ndev->p->dead)
+				goto restart;
+		}
+	}
+
+	spin_unlock(&devices_kset->list_lock);
+
+	if (needs_put)
+		put_device(needs_put);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
@@ -4929,6 +5050,12 @@ void device_shutdown(void)
 
 	cpufreq_suspend();
 
+	/*
+	 * Start async device threads where possible to maximize potential
+	 * parallelism and minimize false dependency on unrelated sync devices
+	 */
+	start_async_shutdown_devices();
+
 	spin_lock(&devices_kset->list_lock);
 	/*
 	 * Walk the devices list backward, shutting down each in turn.
@@ -4947,11 +5074,16 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		shutdown_one_device(dev);
+		if (!wants_async_shutdown(dev) || !shutdown_device_async(dev)) {
+			wait_for_shutdown_dependencies(dev, false);
+			shutdown_one_device(dev);
+		}
 
 		spin_lock(&devices_kset->list_lock);
 	}
 	spin_unlock(&devices_kset->list_lock);
+
+	async_synchronize_full();
 }
 
 /*
diff --git a/include/linux/device.h b/include/linux/device.h
index 7b2baffdd2f5..f913d72218f8 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -610,6 +610,7 @@ enum struct_device_flags {
 	DEV_FLAG_OF_NODE_REUSED = 7,
 	DEV_FLAG_OFFLINE_DISABLED = 8,
 	DEV_FLAG_OFFLINE = 9,
+	DEV_FLAG_ASYNC_SHUTDOWN = 10,
 
 	DEV_FLAG_COUNT
 };
@@ -827,6 +828,7 @@ __create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT);
 __create_dev_flag_accessors(of_node_reused, DEV_FLAG_OF_NODE_REUSED);
 __create_dev_flag_accessors(offline_disabled, DEV_FLAG_OFFLINE_DISABLED);
 __create_dev_flag_accessors(offline, DEV_FLAG_OFFLINE);
+__create_dev_flag_accessors(async_shutdown, DEV_FLAG_ASYNC_SHUTDOWN);
 
 #undef __create_dev_flag_accessors
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 4/5] PCI: Enable async shutdown support
  2026-07-01 13:50 [PATCH v18 0/5] shut down devices asynchronously David Jeffery
                   ` (2 preceding siblings ...)
  2026-07-01 13:50 ` [PATCH 3/5] driver core: async device shutdown infrastructure David Jeffery
@ 2026-07-01 13:50 ` David Jeffery
  2026-07-01 14:07   ` sashiko-bot
  2026-07-01 13:50 ` [PATCH 5/5] scsi: " David Jeffery
  4 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-07-01 13:50 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery,
	Pasha Tatashin, Bjorn Helgaas

Like its async suspend support, allow PCI device shutdown to be performed
asynchronously to reduce shutdown time.

Signed-off-by: David Jeffery <djeffery@redhat.com>
Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Bjorn Helgaas <bhelgaas@google.com>
---
 drivers/pci/probe.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index b63cd0c310bc..7cf3dabc885e 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1045,6 +1045,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
 
 	bus->bridge = get_device(&bridge->dev);
 	device_enable_async_suspend(bus->bridge);
+	dev_set_async_shutdown(bus->bridge);
 	pci_set_bus_of_node(bus);
 	pci_set_bus_msi_domain(bus);
 	if (bridge->msi_domain && !dev_get_msi_domain(&bus->dev) &&
@@ -2753,6 +2754,7 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
 	pci_reassigndev_resource_alignment(dev);
 
 	pci_init_capabilities(dev);
+	dev_set_async_shutdown(&dev->dev);
 
 	/*
 	 * Add the device to our list of discovered devices
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 5/5] scsi: Enable async shutdown support
  2026-07-01 13:50 [PATCH v18 0/5] shut down devices asynchronously David Jeffery
                   ` (3 preceding siblings ...)
  2026-07-01 13:50 ` [PATCH 4/5] PCI: Enable async shutdown support David Jeffery
@ 2026-07-01 13:50 ` David Jeffery
  2026-07-01 14:06   ` sashiko-bot
  4 siblings, 1 reply; 19+ messages in thread
From: David Jeffery @ 2026-07-01 13:50 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Pasha Tatashin,
	Michał Cłapiński, Jordan Richards, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Stuart Hayes,
	Laurence Oberman, Bart Van Assche, Bjorn Helgaas,
	Martin K . Petersen, John Garry, kexec, David Jeffery,
	Pasha Tatashin

Like scsi's async suspend support, allow scsi devices to be shut down
asynchronously to reduce system shutdown time.

Signed-off-by: David Jeffery <djeffery@redhat.com>
Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/hosts.c      | 2 ++
 drivers/scsi/scsi_sysfs.c | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e047747d4ecf..bf691acc7a67 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -273,6 +273,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 	pm_runtime_set_active(&shost->shost_gendev);
 	pm_runtime_enable(&shost->shost_gendev);
 	device_enable_async_suspend(&shost->shost_gendev);
+	dev_set_async_shutdown(&shost->shost_gendev);
 
 	error = device_add(&shost->shost_gendev);
 	if (error)
@@ -282,6 +283,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 	get_device(shost->shost_gendev.parent);
 
 	device_enable_async_suspend(&shost->shost_dev);
+	dev_set_async_shutdown(&shost->shost_dev);
 
 	get_device(&shost->shost_gendev);
 	error = device_add(&shost->shost_dev);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index dfc3559e7e04..8fd317aef37b 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1386,6 +1386,7 @@ static int scsi_target_add(struct scsi_target *starget)
 	pm_runtime_set_active(&starget->dev);
 	pm_runtime_enable(&starget->dev);
 	device_enable_async_suspend(&starget->dev);
+	dev_set_async_shutdown(&starget->dev);
 
 	return 0;
 }
@@ -1412,6 +1413,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
 	transport_configure_device(&starget->dev);
 
 	device_enable_async_suspend(&sdev->sdev_gendev);
+	dev_set_async_shutdown(&sdev->sdev_gendev);
 	scsi_autopm_get_target(starget);
 	pm_runtime_set_active(&sdev->sdev_gendev);
 	if (!sdev->rpm_autosuspend)
@@ -1431,6 +1433,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
 	}
 
 	device_enable_async_suspend(&sdev->sdev_dev);
+	dev_set_async_shutdown(&sdev->sdev_dev);
 	error = device_add(&sdev->sdev_dev);
 	if (error) {
 		sdev_printk(KERN_INFO, sdev,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/5] driver core: separate function to shutdown one device
  2026-07-01 13:50 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
@ 2026-07-01 13:58   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-07-01 13:58 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

> Make a separate function for the part of device_shutdown() that does the
> shutown for a single device.  This is in preparation for making device
> shutdown asynchronous.
> 
> Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
> Signed-off-by: David Jeffery <djeffery@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260701135015.81937-1-djeffery@redhat.com?part=1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/5] driver core: do not always lock parent in shutdown
  2026-07-01 13:50 ` [PATCH 2/5] driver core: do not always lock parent in shutdown David Jeffery
@ 2026-07-01 14:02   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-07-01 14:02 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

> Don't lock a parent device unless it is needed in device_shutdown. This
> is in preparation for making device shutdown asynchronous, when it will
> be needed to allow children of a common parent to shut down
> simultaneously.
> 
> And only acquire a reference to the parent device if the parent is to be
> locked.
> 
> Signed-off-by: Stuart Hayes <stuart.w.hayes@gmail.com>
> Signed-off-by: David Jeffery <djeffery@redhat.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260701135015.81937-1-djeffery@redhat.com?part=2


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 5/5] scsi: Enable async shutdown support
  2026-07-01 13:50 ` [PATCH 5/5] scsi: " David Jeffery
@ 2026-07-01 14:06   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-07-01 14:06 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Runtime PM usage counter and PM state leak on device_add error path.
- [High] Concurrent lockless bitfield mutations cause Data Race and PM state corruption.
--

commit 10ebc4309581b7d74ed1d3564b5f1af2a5cf5785
Author: David Jeffery <djeffery@redhat.com>

scsi: Enable async shutdown support

Like scsi's async suspend support, allow scsi devices to be shut down
asynchronously to reduce system shutdown time.

> diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
> index dfc3559e7e04f..8fd317aef37b4 100644
> --- a/drivers/scsi/scsi_sysfs.c
> +++ b/drivers/scsi/scsi_sysfs.c
> @@ -1386,6 +1386,7 @@ static int scsi_target_add(struct scsi_target *starget)
>  	pm_runtime_set_active(&starget->dev);
>  	pm_runtime_enable(&starget->dev);
>  	device_enable_async_suspend(&starget->dev);
> +	dev_set_async_shutdown(&starget->dev);

[Severity: High]
This is a pre-existing issue, but does adding dev_set_async_shutdown() here
exacerbate a data race with system suspend?

In scsi_target_add(), device_add() is called earlier, exposing the device
to the PM core. If a system-wide suspend happens concurrently,
device_prepare() may lock the device and set the is_prepared bitfield.

Since device_enable_async_suspend() and dev_set_async_shutdown() locklessly
mutate bitfields in dev->power in the same memory word, couldn't the
resulting read-modify-write instructions overwrite and clear the is_prepared
flag set by the PM core?

>  	return 0;
>  }

[ ... ]

> @@ -1412,6 +1413,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
>  	transport_configure_device(&starget->dev);
>  
>  	device_enable_async_suspend(&sdev->sdev_gendev);
> +	dev_set_async_shutdown(&sdev->sdev_gendev);

[Severity: High]
This isn't a bug introduced by this patch, but could the error path for
device_add(&sdev->sdev_gendev) leak the PM usage counter and runtime state?

When scsi_sysfs_add_sdev() is called, it increments the PM usage count via
scsi_autopm_get_device(sdev) and enables runtime PM. If the subsequent
device_add(&sdev->sdev_gendev) fails, the function returns the error directly
without dropping the usage counter or disabling runtime PM:

scsi_sysfs_add_sdev() {
    ...
    scsi_autopm_get_device(sdev);
    ...
    error = device_add(&sdev->sdev_gendev);
    if (error) {
        sdev_printk(...);
        return error;
    }
}

Would this prevent the device and its parent target from ever entering
runtime suspend, causing unbounded power drain?

>  	scsi_autopm_get_target(starget);
>  	pm_runtime_set_active(&sdev->sdev_gendev);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260701135015.81937-1-djeffery@redhat.com?part=5

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 4/5] PCI: Enable async shutdown support
  2026-07-01 13:50 ` [PATCH 4/5] PCI: Enable async shutdown support David Jeffery
@ 2026-07-01 14:07   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-07-01 14:07 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [Critical] In `pci_register_host_bridge()`, a failure in `device_register(&bus->dev)` leads to a double-free of the `bridge->dev` object.
- [High] In `pci_device_add()`, failure of `device_add()` is ignored, leaving a partially-initialized device in the subsystem lists.
--

commit 32aeb814f8bbaee48b0baf0572f23452cd1e50da
Author: David Jeffery <djeffery@redhat.com>

PCI: Enable async shutdown support

This commit adds asynchronous shutdown support for PCI devices. It aims to
reduce overall shutdown time in the same way async suspend does.

> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index dd0abbc63e18d..af23814464060 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -1046,6 +1046,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
>  
>  	bus->bridge = get_device(&bridge->dev);
>  	device_enable_async_suspend(bus->bridge);
> +	dev_set_async_shutdown(bus->bridge);

[Severity: Critical]
This is a pre-existing issue, and was not introduced by the patch under
review, but does the error handling in pci_register_host_bridge() lead to
a double-free of the bridge->dev object?

If device_register(&bus->dev) fails, the code jumps to the unregister label:

unregister:
	put_device(&bridge->dev);
	device_del(&bridge->dev);
free:
	...
	if (bus_registered)
		put_device(&bus->dev);

Because bus_registered is set to true right before checking the error from
device_register(), the free path calls put_device(&bus->dev).

Releasing the bus triggers release_pcibus_dev(), which implicitly drops the
pci_bus->bridge reference via put_device(pci_bus->bridge). Since the code
explicitly drops the reference at the unregister label as well, does this
drop the object's refcount to 0 prematurely?

[ ... ]

> @@ -2748,6 +2749,7 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
>  	pci_reassigndev_resource_alignment(dev);
>  
>  	pci_init_capabilities(dev);
> +	dev_set_async_shutdown(&dev->dev);
>  
>  	/*
>  	 * Add the device to our list of discovered devices

[Severity: High]
This isn't a bug introduced by this patch, but I noticed the failure of
device_add() is ignored later in pci_device_add():

	ret = device_add(&dev->dev);
	WARN_ON(ret < 0);

If device_add() fails, the device remains in the bus->devices list but is
not tracked by the driver core.

When the PCI device or bus is later removed, pci_destroy_dev() unconditionally
calls device_del(&dev->dev). Calling device_del() on a device that was never
successfully added via device_add() violates the driver core API.

Could this lead to a kernel crash or NULL pointer dereference when a PCI
device is enumerated under memory pressure?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260701135015.81937-1-djeffery@redhat.com?part=4

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 3/5] driver core: async device shutdown infrastructure
  2026-07-01 13:50 ` [PATCH 3/5] driver core: async device shutdown infrastructure David Jeffery
@ 2026-07-01 14:11   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-07-01 14:11 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Modifying the core.async_shutdown module parameter during shutdown causes synchronous suppliers to skip waiting for asynchronous consumers.
- [High] Unregistered consumer devices will cause a NULL pointer dereference during async shutdown dependency waiting.
- [High] Adding a new device with async shutdown enabled while its synchronous supplier is being processed leads to an unresolvable deadlock.
- [High] Dereferencing `dev->p->dead` without NULL checks causes a crash if a concurrent `device_add()` fails.
--

commit cf98cdf76df805443cc885868cb3d9e13da5501c
Author: David Jeffery <djeffery@redhat.com>

driver core: async device shutdown infrastructure

This commit allows devices to opt into asynchronous shutdown, executing in
parallel with unrelated devices after their dependencies shut down. Synchronous
shutdown devices will wait for their asynchronous dependencies. The feature is
enabled by default and can be toggled via the core.async_shutdown parameter.

> diff --git a/drivers/base/core.c b/drivers/base/core.c
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
[ ... ]
> +static bool wants_async_shutdown(struct device *dev)
> +{
> +	return async_shutdown && dev_async_shutdown(dev);
> +}
> +
> +static int wait_for_device_shutdown(struct device *dev, void *data)
> +{
> +	bool async = *(bool *)data;
> +
> +	if (async || wants_async_shutdown(dev))
> +		wait_for_completion(&dev->p->complete);
> +
> +	return 0;
> +}

[Severity: High]
If a user-space process toggles the 0644 core.async_shutdown sysfs parameter
from 1 to 0 during shutdown, could this cause synchronous suppliers to skip
waiting for their asynchronous consumers?

If start_async_shutdown_devices() schedules an async consumer, and then the
parameter is toggled before the sync loop processes its supplier, the call to
wants_async_shutdown(dev) here would evaluate to false. 

This would skip the wait_for_completion(), potentially causing the supplier
(e.g., a PCI bus) to shut down while its consumer is still active.

[Severity: High]
Could this result in a NULL pointer dereference if the consumer is not yet
registered?

The device_link_add() function allows creating links to consumer devices that
haven't been registered yet, meaning their dev->p is NULL because
device_private_init() hasn't run.

If wait_for_shutdown_dependencies() is called from the async pool, where the
async argument is forced to true, the wait_for_completion() will
unconditionally evaluate &dev->p->complete, crashing on the NULL pointer.

[ ... ]
> +static void __shutdown_one_device(struct device *dev)
>  {
>  	if (dev->p->dead)
>  		return;

[Severity: High]
Could this cause a NULL pointer dereference if a concurrent device_add() fails?

If a concurrent device_add() fails after calling kobject_add(), its error path
calls kobject_del() and then frees and nullifies dev->p. Evaluating
dev->p->dead without checking if dev->p is valid could crash here.

[ ... ]
> +static void start_async_shutdown_devices(void)
> +{
> +	struct device *dev, *next, *ndev, *needs_put = NULL;
[ ... ]
> +			needs_put = ndev;
> +			spin_lock(&devices_kset->list_lock);
> +			/*
> +			 * If the next device has been marked dead while the
> +			 * spinlock was released, it may no longer be on the
> +			 * devices_kset list. Restart the list walk to be safe
> +			 */
> +			if (ndev && ndev->p->dead)
> +				goto restart;

[Severity: High]
Could ndev->p be NULL if a concurrent device_add() fails?

If start_async_shutdown_devices() obtains a reference to next via
get_device(), and concurrently device_add() fails and clears dev->p,
reacquiring the lock and evaluating ndev->p->dead here could result in a NULL
pointer dereference.

[ ... ]
>  void device_shutdown(void)
>  {
[ ... ]
> -		shutdown_one_device(dev);
> +		if (!wants_async_shutdown(dev) || !shutdown_device_async(dev)) {
> +			wait_for_shutdown_dependencies(dev, false);
> +			shutdown_one_device(dev);
> +		}
>  
>  		spin_lock(&devices_kset->list_lock);
>  	}
>  	spin_unlock(&devices_kset->list_lock);

[Severity: High]
Can this sequence lead to an unresolvable deadlock if a new device is added
concurrently?

If a new device 'C' with DEV_FLAG_ASYNC_SHUTDOWN is added via hotplug
after its synchronous supplier 'S' has already been popped from
devices_kset by this loop, 'C' is appended to devices_kset.

'S' will call wait_for_shutdown_dependencies() and find 'C', blocking on
wait_for_completion(&C->p->complete) because wants_async_shutdown(C) is true.

Since 'C' was added after start_async_shutdown_devices() ran, it is not
running in the async pool. Because the sync loop is blocked on 'S', it will
never reach 'C' to start it, deadlocking the shutdown process permanently.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260701135015.81937-1-djeffery@redhat.com?part=3

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-07-01 14:11 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 13:50 [PATCH v18 0/5] shut down devices asynchronously David Jeffery
2026-07-01 13:50 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
2026-07-01 13:58   ` sashiko-bot
2026-07-01 13:50 ` [PATCH 2/5] driver core: do not always lock parent in shutdown David Jeffery
2026-07-01 14:02   ` sashiko-bot
2026-07-01 13:50 ` [PATCH 3/5] driver core: async device shutdown infrastructure David Jeffery
2026-07-01 14:11   ` sashiko-bot
2026-07-01 13:50 ` [PATCH 4/5] PCI: Enable async shutdown support David Jeffery
2026-07-01 14:07   ` sashiko-bot
2026-07-01 13:50 ` [PATCH 5/5] scsi: " David Jeffery
2026-07-01 14:06   ` sashiko-bot
  -- strict thread matches above, loose matches on Subject: below --
2026-06-16 15:22 [PATCH v17 0/5] shut down devices asynchronously David Jeffery
2026-06-16 15:22 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
2026-06-16 15:34   ` sashiko-bot
2026-05-18 19:31 [PATCH v16 0/5] shut down devices asynchronously David Jeffery
2026-05-18 19:32 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
2026-04-29 17:50 [PATCH v15 0/5] shut down devices asynchronously David Jeffery
2026-04-29 17:50 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
2026-04-20 15:26 [PATCH v14 0/5] shut down devices asynchronously David Jeffery
2026-04-20 15:26 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
2026-04-20 20:25   ` Pasha Tatashin
2026-04-07 15:35 [PATCH v13 0/5] shut down devices asynchronously David Jeffery
2026-04-07 15:35 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery
2026-03-19 14:11 [PATCH v12 0/5] shut down devices asynchronously David Jeffery
2026-03-19 14:11 ` [PATCH 1/5] driver core: separate function to shutdown one device David Jeffery

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox