Linux PCI subsystem development
 help / color / mirror / Atom feed
* [PATCH 7/9] PCI: Link a virtual function to its physical function
  2026-08-21 14:24 [PATCH v20 0/9] shut down devices asynchronously David Jeffery
@ 2026-08-21 14:24 ` David Jeffery
  2026-08-21 14:34   ` sashiko-bot
  0 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-08-21 14:24 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, David Jeffery

With a PCI virtual function having the same bus for its parent
as the physical function it is associated with, there is no
explicit dependency between the two in the device tree. With
async shutdown, this can result in the PF being shutdown before the
VF has an opportunity to shutdown.

Link the VF as a consumer of the PF to note its dependency and
ensure clean shutdown ordering.

Signed-off-by: Tarun Sahu <tarunsahu@google.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
---
 drivers/pci/iov.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index b0d24839c084..c75da093d60c 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -382,14 +382,20 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
 	}
 
 	pci_device_add(virtfn, virtfn->bus);
+	if (!device_link_add(&virtfn->dev, &dev->dev, DL_FLAG_STATELESS)) {
+		rc = -ENOMEM;
+		goto failed1;
+	}
+
 	rc = pci_iov_sysfs_link(dev, virtfn, id);
 	if (rc)
-		goto failed1;
+		goto unlink;
 
 	pci_bus_add_device(virtfn);
 
 	return 0;
-
+unlink:
+	device_link_remove(&virtfn->dev, &dev->dev);
 failed1:
 	pci_stop_and_remove_bus_device(virtfn);
 	pci_dev_put(dev);
@@ -421,6 +427,7 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id)
 	if (virtfn->dev.kobj.sd)
 		sysfs_remove_link(&virtfn->dev.kobj, "physfn");
 
+	device_link_remove(&virtfn->dev, &dev->dev);
 	pci_stop_and_remove_bus_device(virtfn);
 	virtfn_remove_bus(dev->bus, virtfn->bus);
 
-- 
2.55.0


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

* Re: [PATCH 7/9] PCI: Link a virtual function to its physical function
  2026-08-21 14:24 ` [PATCH 7/9] PCI: Link a virtual function to its physical function David Jeffery
@ 2026-08-21 14:34   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-21 14:34 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

> With a PCI virtual function having the same bus for its parent
> as the physical function it is associated with, there is no
> explicit dependency between the two in the device tree. With
> async shutdown, this can result in the PF being shutdown before the
> VF has an opportunity to shutdown.
> 
> Link the VF as a consumer of the PF to note its dependency and
> ensure clean shutdown ordering.
> 
> Signed-off-by: Tarun Sahu <tarunsahu@google.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/20260821142414.150892-1-djeffery@redhat.com?part=7


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

* [PATCH v21 0/9] shut down devices asynchronously
@ 2026-09-02 17:07 David Jeffery
  2026-09-02 17:07 ` [PATCH 1/9] driver core: rely on put_device to free dev->p David Jeffery
                   ` (8 more replies)
  0 siblings, 9 replies; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	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 V20:
- Move PCI bridge async enablement to before its call to device_add
- Use srcu to prevent ensure any active device_add is completed before
  device_shutdown begins shutting down devices.

Changes from V19:
- Convert initial creation of shutdown_one_device to a code move with no
  changes or side effects
- Add a warning to device_move should some device ever mix need_parent_lock
  with device_move in the future
- Link a PCI virtual function as consumer of its associated physical
  function to ensure proper shutdown order

Changes from V18:
- Fix deadlock and race condition on concurrent device_add
- Use local variable to device_shutdown for caching async_shutdown
- Added dev->p check to avoid NULL pointer deference and tell if the
  device is not registered so no need to be shutdown. This is to handle
  the cases when devices consumers list might have devices that are not
  yet registered.

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)

David Jeffery (7):
  driver core: warn should device_move try to move a need_parent_lock
    device
  driver core: separate function to shutdown one device
  driver core: do not always lock parent in shutdown
  driver core: async device shutdown infrastructure
  PCI: Link a virtual function to its physical function
  PCI: Enable async shutdown support
  scsi: Enable async shutdown support

Tarun Sahu (2):
  driver core: rely on put_device to free dev->p
  driver core: prevent device_add() during system shutdown

 .../admin-guide/kernel-parameters.txt         |  10 +
 drivers/base/base.h                           |   2 +
 drivers/base/core.c                           | 269 +++++++++++++++---
 drivers/pci/iov.c                             |  11 +-
 drivers/pci/probe.c                           |   2 +
 drivers/scsi/hosts.c                          |   2 +
 drivers/scsi/scsi_sysfs.c                     |   3 +
 include/linux/device.h                        |   2 +
 8 files changed, 260 insertions(+), 41 deletions(-)

-- 
2.55.0


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

* [PATCH 1/9] driver core: rely on put_device to free dev->p
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:16   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 2/9] driver core: prevent device_add() during system shutdown David Jeffery
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	David Jeffery

From: Tarun Sahu <tarunsahu@google.com>

device_add allocate private_data for device and assigns to
dev->p. If device_add fails in later steps of the function,
it cleans up this dev->p which is not necessary because In
the next call, put_device free it anyway (if reference to
the device is 0 which will be unless someone concurrently
get the reference to this device).

This avoids unnecessary races introduced in system. After device
is added in device_kset->list by device_add and later steps in the
device_add function failures occur, it will free dev->p manually,
while in between there might be a user of device_kset->list will
take reference to the device just added by device_add. and might
try to access dev->p. So relying on put_device to free dev->p
prevents such problem.

Signed-off-by: Tarun Sahu <tarunsahu@google.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 4c0c373998a1..83263e3fa5d4 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2618,6 +2618,7 @@ static void device_release(struct kobject *kobj)
 	struct device *dev = kobj_to_dev(kobj);
 	struct device_private *p = dev->p;
 
+	dev->p = NULL;
 	/*
 	 * Some platform devices are driven without driver attached
 	 * and managed resources may have been acquired.  Make sure
@@ -3828,8 +3829,6 @@ int device_add(struct device *dev)
 parent_error:
 	put_device(parent);
 name_error:
-	kfree(dev->p);
-	dev->p = NULL;
 	goto done;
 }
 EXPORT_SYMBOL_GPL(device_add);
-- 
2.55.0


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

* [PATCH 2/9] driver core: prevent device_add() during system shutdown
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
  2026-09-02 17:07 ` [PATCH 1/9] driver core: rely on put_device to free dev->p David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:15   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 3/9] driver core: warn should device_move try to move a need_parent_lock device David Jeffery
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	David Jeffery

From: Tarun Sahu <tarunsahu@google.com>

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@google.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 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


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

* [PATCH 3/9] driver core: warn should device_move try to move a need_parent_lock device
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
  2026-09-02 17:07 ` [PATCH 1/9] driver core: rely on put_device to free dev->p David Jeffery
  2026-09-02 17:07 ` [PATCH 2/9] driver core: prevent device_add() during system shutdown David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:22   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 4/9] driver core: separate function to shutdown one device David Jeffery
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	David Jeffery

Currently, no device has need_parent_lock set and is moved by
device_move. need_parent_lock is only set by the usb bus and very
few device types ever use device_move.

Add a warning to device_move to catch should it ever be used on a
device with need_parent_lock set. The combination would break
the immutable relationship needed between parent and child for
need_parent_lock when locking and unlocking both.

Suggested-by: Tarun Sahu <tarunsahu@google.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index bce555dd74f6..e8a62c8f0ad3 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4705,6 +4705,14 @@ int device_move(struct device *dev, struct device *new_parent,
 	if (!dev)
 		return -EINVAL;
 
+	/*
+	 * device_move() should not be used on devices with need_parent_lock
+	 * set. Concurrent reparenting will violate the immutable
+	 * relationship needed while locking and unlocking both parent and
+	 * child.
+	 */
+	WARN_ON(dev->bus && dev->bus->need_parent_lock);
+
 	device_pm_lock();
 	new_parent = get_device(new_parent);
 	new_parent_kobj = get_device_parent(dev, new_parent);
-- 
2.55.0


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

* [PATCH 4/9] driver core: separate function to shutdown one device
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
                   ` (2 preceding siblings ...)
  2026-09-02 17:07 ` [PATCH 3/9] driver core: warn should device_move try to move a need_parent_lock device David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:13   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 5/9] driver core: do not always lock parent in shutdown David Jeffery
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	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 | 65 ++++++++++++++++++++++++---------------------
 1 file changed, 35 insertions(+), 30 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index e8a62c8f0ad3..7fe3fa6870a7 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4903,6 +4903,40 @@ 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)
+{
+	/* 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);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
@@ -4940,36 +4974,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, parent);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.55.0


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

* [PATCH 5/9] driver core: do not always lock parent in shutdown
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
                   ` (3 preceding siblings ...)
  2026-09-02 17:07 ` [PATCH 4/9] driver core: separate function to shutdown one device David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:25   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 6/9] driver core: async device shutdown infrastructure David Jeffery
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	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>
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/base/core.c | 44 ++++++++++++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 16 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 7fe3fa6870a7..05285fae143d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -4903,12 +4903,10 @@ 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)
+static void __shutdown_one_device(struct device *dev)
 {
-	/* hold lock to avoid race with probe/release */
-	if (parent)
-		device_lock(parent);
-	device_lock(dev);
+	if (!dev->p || dev->p->dead)
+		return;
 
 	/* Don't allow any more runtime suspends */
 	pm_runtime_get_noresume(dev);
@@ -4928,13 +4926,33 @@ static void shutdown_one_device(struct device *dev, struct device *parent)
 			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 && !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(dev);
-	put_device(parent);
 }
 
 /**
@@ -4942,7 +4960,7 @@ static void shutdown_one_device(struct device *dev, struct device *parent)
  */
 void device_shutdown(void)
 {
-	struct device *dev, *parent;
+	struct device *dev;
 
 	wait_for_device_probe();
 	device_block_probing();
@@ -4960,12 +4978,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
-		 */
-		parent = get_device(dev->parent);
 		get_device(dev);
 		/*
 		 * Make sure the device is off the kset list, in the
@@ -4974,7 +4986,7 @@ void device_shutdown(void)
 		list_del_init(&dev->kobj.entry);
 		spin_unlock(&devices_kset->list_lock);
 
-		shutdown_one_device(dev, parent);
+		shutdown_one_device(dev);
 
 		spin_lock(&devices_kset->list_lock);
 	}
-- 
2.55.0


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

* [PATCH 6/9] driver core: async device shutdown infrastructure
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
                   ` (4 preceding siblings ...)
  2026-09-02 17:07 ` [PATCH 5/9] driver core: do not always lock parent in shutdown David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:17   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 7/9] PCI: Link a virtual function to its physical function David Jeffery
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	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>
Signed-off-by: Tarun Sahu <tarunsahu@google.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 .../admin-guide/kernel-parameters.txt         |  10 ++
 drivers/base/base.h                           |   2 +
 drivers/base/core.c                           | 141 +++++++++++++++++-
 include/linux/device.h                        |   2 +
 4 files changed, 154 insertions(+), 1 deletion(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd2..6532bd0bbfdf 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -995,6 +995,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 05285fae143d..30caa7151750 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,11 @@
 #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");
+static bool async_shutdown_enabled;
+
 /* Device links support. */
 static LIST_HEAD(deferred_sync);
 static unsigned int defer_sync_state_count = 1;
@@ -3627,6 +3633,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;
 }
 
@@ -3925,6 +3932,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);
@@ -4903,6 +4911,40 @@ 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_enabled && dev_async_shutdown(dev);
+}
+
+static int wait_for_device_shutdown(struct device *dev, void *data)
+{
+	bool async = *(bool *)data;
+
+	if (!dev->p || !device_is_registered(dev))
+		return 0;
+
+	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 || dev->p->dead)
@@ -4926,6 +4968,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)
@@ -4955,6 +4999,88 @@ 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_enabled)
+		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, or if it has been unlinked
+			 * from the list, it may no longer be on the
+			 * devices_kset list. Restart the list walk to be safe.
+			 */
+			if (ndev && (ndev->p->dead || list_empty(&ndev->kobj.entry)))
+				goto restart;
+		}
+	}
+
+	spin_unlock(&devices_kset->list_lock);
+
+	if (needs_put)
+		put_device(needs_put);
+}
+
 /**
  * device_shutdown - call ->shutdown() on each device to shutdown.
  */
@@ -4968,6 +5094,14 @@ void device_shutdown(void)
 
 	cpufreq_suspend();
 
+	async_shutdown_enabled = async_shutdown;
+
+	/*
+	 * 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.
@@ -4986,11 +5120,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 aee79fd6b32b..1ebafa798462 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -612,6 +612,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
 };
@@ -829,6 +830,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.55.0


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

* [PATCH 7/9] PCI: Link a virtual function to its physical function
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
                   ` (5 preceding siblings ...)
  2026-09-02 17:07 ` [PATCH 6/9] driver core: async device shutdown infrastructure David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:17   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 8/9] PCI: Enable async shutdown support David Jeffery
  2026-09-02 17:07 ` [PATCH 9/9] scsi: " David Jeffery
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	David Jeffery

With a PCI virtual function having the same bus for its parent
as the physical function it is associated with, there is no
explicit dependency between the two in the device tree. With
async shutdown, this can result in the PF being shutdown before the
VF has an opportunity to shutdown.

Link the VF as a consumer of the PF to note its dependency and
ensure clean shutdown ordering.

Signed-off-by: Tarun Sahu <tarunsahu@google.com>
Signed-off-by: David Jeffery <djeffery@redhat.com>
Tested-by: Laurence Oberman <loberman@redhat.com>
---
 drivers/pci/iov.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 9d408fb8ac25..9b74211c6ab4 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -382,14 +382,20 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
 	}
 
 	pci_device_add(virtfn, virtfn->bus);
+	if (!device_link_add(&virtfn->dev, &dev->dev, DL_FLAG_STATELESS)) {
+		rc = -ENOMEM;
+		goto failed1;
+	}
+
 	rc = pci_iov_sysfs_link(dev, virtfn, id);
 	if (rc)
-		goto failed1;
+		goto unlink;
 
 	pci_bus_add_device(virtfn);
 
 	return 0;
-
+unlink:
+	device_link_remove(&virtfn->dev, &dev->dev);
 failed1:
 	pci_stop_and_remove_bus_device(virtfn);
 	pci_dev_put(dev);
@@ -421,6 +427,7 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id)
 	if (virtfn->dev.kobj.sd)
 		sysfs_remove_link(&virtfn->dev.kobj, "physfn");
 
+	device_link_remove(&virtfn->dev, &dev->dev);
 	pci_stop_and_remove_bus_device(virtfn);
 	virtfn_remove_bus(dev->bus, virtfn->bus);
 
-- 
2.55.0


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

* [PATCH 8/9] PCI: Enable async shutdown support
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
                   ` (6 preceding siblings ...)
  2026-09-02 17:07 ` [PATCH 7/9] PCI: Link a virtual function to its physical function David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:24   ` sashiko-bot
  2026-09-02 17:07 ` [PATCH 9/9] scsi: " David Jeffery
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	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 27008e2ea5af..101a96718acd 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1038,6 +1038,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
 	if (err)
 		goto free;
 
+	dev_set_async_shutdown(&bridge->dev);
 	/* Temporarily move resources off the list */
 	list_splice_init(&bridge->windows, &resources);
 	err = device_add(&bridge->dev);
@@ -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);
 
 	platform_pci_configure_wake(dev);
 
-- 
2.55.0


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

* [PATCH 9/9] scsi: Enable async shutdown support
  2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
                   ` (7 preceding siblings ...)
  2026-09-02 17:07 ` [PATCH 8/9] PCI: Enable async shutdown support David Jeffery
@ 2026-09-02 17:07 ` David Jeffery
  2026-09-02 17:26   ` sashiko-bot
  8 siblings, 1 reply; 21+ messages in thread
From: David Jeffery @ 2026-09-02 17:07 UTC (permalink / raw)
  To: driver-core, Greg Kroah-Hartman, Rafael J. Wysocki,
	Danilo Krummrich
  Cc: linux-kernel, linux-pci, linux-scsi, Tarun Sahu, Stuart Hayes,
	Laurence Oberman, Bjorn Helgaas, kexec, Ewan Milne,
	John Meneghini, Lombardi, Maurizio, Bart Van Assche, John Garry,
	Jeremy Allison, Martin K . Petersen, Pasha Tatashin,
	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 e72055c1ebbb..75592f41ef39 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -272,6 +272,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 9480432f650b..52e1e38dc027 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1387,6 +1387,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;
 }
@@ -1413,6 +1414,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)
@@ -1432,6 +1434,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.55.0


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

* Re: [PATCH 4/9] driver core: separate function to shutdown one device
  2026-09-02 17:07 ` [PATCH 4/9] driver core: separate function to shutdown one device David Jeffery
@ 2026-09-02 17:13   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:13 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

> 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/20260902170734.95504-1-djeffery@redhat.com?part=4


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

* Re: [PATCH 2/9] driver core: prevent device_add() during system shutdown
  2026-09-02 17:07 ` [PATCH 2/9] driver core: prevent device_add() during system shutdown David Jeffery
@ 2026-09-02 17:15   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:15 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

> From: Tarun Sahu <tarunsahu@google.com>
> 
> 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@google.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/20260902170734.95504-1-djeffery@redhat.com?part=2


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

* Re: [PATCH 1/9] driver core: rely on put_device to free dev->p
  2026-09-02 17:07 ` [PATCH 1/9] driver core: rely on put_device to free dev->p David Jeffery
@ 2026-09-02 17:16   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:16 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

> From: Tarun Sahu <tarunsahu@google.com>
> 
> device_add allocate private_data for device and assigns to
> dev->p. If device_add fails in later steps of the function,
> it cleans up this dev->p which is not necessary because In
> the next call, put_device free it anyway (if reference to
> the device is 0 which will be unless someone concurrently
> get the reference to this device).
> 
> This avoids unnecessary races introduced in system. After device
> is added in device_kset->list by device_add and later steps in the
> device_add function failures occur, it will free dev->p manually,
> while in between there might be a user of device_kset->list will
> take reference to the device just added by device_add. and might
> try to access dev->p. So relying on put_device to free dev->p
> prevents such problem.
> 
> Signed-off-by: Tarun Sahu <tarunsahu@google.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/20260902170734.95504-1-djeffery@redhat.com?part=1


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

* Re: [PATCH 7/9] PCI: Link a virtual function to its physical function
  2026-09-02 17:07 ` [PATCH 7/9] PCI: Link a virtual function to its physical function David Jeffery
@ 2026-09-02 17:17   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:17 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

> With a PCI virtual function having the same bus for its parent
> as the physical function it is associated with, there is no
> explicit dependency between the two in the device tree. With
> async shutdown, this can result in the PF being shutdown before the
> VF has an opportunity to shutdown.
> 
> Link the VF as a consumer of the PF to note its dependency and
> ensure clean shutdown ordering.
> 
> Signed-off-by: Tarun Sahu <tarunsahu@google.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/20260902170734.95504-1-djeffery@redhat.com?part=7


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

* Re: [PATCH 6/9] driver core: async device shutdown infrastructure
  2026-09-02 17:07 ` [PATCH 6/9] driver core: async device shutdown infrastructure David Jeffery
@ 2026-09-02 17:17   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:17 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-scsi, linux-pci

> 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>
> Signed-off-by: Tarun Sahu <tarunsahu@google.com>

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

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


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

* Re: [PATCH 3/9] driver core: warn should device_move try to move a need_parent_lock device
  2026-09-02 17:07 ` [PATCH 3/9] driver core: warn should device_move try to move a need_parent_lock device David Jeffery
@ 2026-09-02 17:22   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:22 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] Sysfs tree corruption during rollback of device_move_class_links failure.
- [High] Unconditional cleanup on failed rollback of device_move_class_links leads to Use-After-Free of new_parent and its glue directory.
--

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

driver core: warn should device_move try to move a need_parent_lock device

This commit adds a warning to device_move() to catch any attempts to move a
device with need_parent_lock set. Moving such a device would break the
immutable parent-child relationship required for locking.

> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index bce555dd74f69..e8a62c8f0ad3d 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -4705,6 +4705,14 @@ int device_move(struct device *dev, struct device *new_parent,
>  	if (!dev)
>  		return -EINVAL;
>  
> +	/*
> +	 * device_move() should not be used on devices with need_parent_lock
> +	 * set. Concurrent reparenting will violate the immutable
> +	 * relationship needed while locking and unlocking both parent and
> +	 * child.
> +	 */
> +	WARN_ON(dev->bus && dev->bus->need_parent_lock);
> +
>  	device_pm_lock();
>  	new_parent = get_device(new_parent);
>  	new_parent_kobj = get_device_parent(dev, new_parent);

Later in device_move():

> 	if (dev->class) {
> 		error = device_move_class_links(dev, old_parent, new_parent);
> 		if (error) {
> 			/* We ignore errors on cleanup since we're hosed anyway... */
> 			device_move_class_links(dev, new_parent, old_parent);
> 			if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
                                                      ^^^^^^^^^^^^^^^^^^^

[Severity: High]
This is a pre-existing issue, but does this rollback correctly restore the
device's sysfs location?

The rollback explicitly uses &old_parent->kobj instead of the original glue
directory.

If a device uses a class glue directory, rolling back a failed move bypasses
the glue directory and places the device directly under the old parent's
kobject.

Can this break sysfs expectations for userspace and leak the original glue
directory during subsequent device removal if device_move_class_links()
fails?

> 				if (new_parent)
> 					klist_remove(&dev->p->knode_parent);
> 				dev->parent = old_parent;
> 				if (old_parent) {
> 					klist_add_tail(&dev->p->knode_parent,
> 						       &old_parent->p->klist_children);
> 					set_dev_node(dev, dev_to_node(old_parent));
> 				}
> 			}
> 			cleanup_glue_dir(dev, new_parent_kobj);
> 			put_device(new_parent);
                        ^^^^^^^^^^^^^^^^^^^^^^^

[Severity: High]
This isn't a bug introduced by this patch, but is it safe to unconditionally
drop these references here?

These lines drop the references to the new parent and glue directory, even if
the rollback failed and the device still holds pointers to them.

If device_move_class_links() fails and the subsequent rollback attempt via
kobject_move() also fails, the device remains partially moved with its parent
and kobj.parent pointers attached to the new parent.

Can dropping the references unconditionally leave these pointers dangling,
leading to a use-after-free or double-free?

> 			goto out;
> 		}
> 	}

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

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

* Re: [PATCH 8/9] PCI: Enable async shutdown support
  2026-09-02 17:07 ` [PATCH 8/9] PCI: Enable async shutdown support David Jeffery
@ 2026-09-02 17:24   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:24 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

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

Pre-existing issues:
- [Critical] Double free and subsequent use-after-free of `bridge->dev` on `pci_register_host_bridge` error path.
--

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

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

> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index 27008e2ea5afc..101a96718acd9 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -1038,6 +1038,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
>  	if (err)
>  		goto free;
>  
> +	dev_set_async_shutdown(&bridge->dev);
>  	/* Temporarily move resources off the list */
>  	list_splice_init(&bridge->windows, &resources);
>  	err = device_add(&bridge->dev);

[Severity: Critical]
This isn't a bug introduced by this patch, but does the error handling path
for pci_register_host_bridge() have a double free and use-after-free of
bridge->dev?

If device_register(&bus->dev) fails later in this function, it jumps to
unregister:

drivers/pci/probe.c:pci_register_host_bridge() {
	...
unregister:
	put_device(&bridge->dev);
	device_del(&bridge->dev);
free:
	...
	if (bus_registered)
		put_device(&bus->dev);
}

The first explicit put_device(&bridge->dev) drops a reference to the
bridge. Then if bus_registered is true, it calls put_device(&bus->dev).

This triggers the bus release function release_pcibus_dev(), which
unconditionally drops a second reference to bridge->dev:

drivers/pci/probe.c:release_pcibus_dev() {
	...
	put_device(pci_bus->bridge);
	...
}

This drops the reference count of the bridge to 0, freeing the bridge object.

When pci_register_host_bridge() returns the error, callers like
pci_create_root_bus() will then try to drop their initial reference:

drivers/pci/probe.c:pci_create_root_bus() {
	...
err_out:
	put_device(&bridge->dev);
	...
}

Will this dereference the already freed bridge object, causing a
use-after-free and double free?

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

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

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

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Incorrect condition ordering leads to a potential Use-After-Free of `dev->bus` during device shutdown.
--

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

driver core: do not always lock parent in shutdown

This commit avoids unconditionally locking a parent device during device
shutdown. It introduces __shutdown_one_device and updates
shutdown_one_device to only acquire the parent lock when needed to prevent
races with probe or release operations.

> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 7fe3fa6870a72..05285fae143d3 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
[ ... ]
> @@ -4928,13 +4926,33 @@ static void shutdown_one_device(struct device *dev, struct device *parent)
>  			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 && !dev->p->dead &&
[Severity: High]
Could this condition ordering lead to a use-after-free during device shutdown?

If a module is unloaded concurrently with system shutdown, device_del() sets
dev->p->dead to true and removes the device from the bus.

Since device_shutdown() already incremented the device's refcount, the
device structure is preserved. However, when the module finishes unloading, it
unregisters the bus and frees the bus_type structure.

Because of left-to-right evaluation in this if statement,
dev->bus->need_parent_lock is accessed before checking !dev->p->dead.

Would checking dev->p && !dev->p->dead before accessing bus properties
prevent reading from the potentially unmapped bus structure?

> +	    (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(dev);
> -	put_device(parent);
>  }

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

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

* Re: [PATCH 9/9] scsi: Enable async shutdown support
  2026-09-02 17:07 ` [PATCH 9/9] scsi: " David Jeffery
@ 2026-09-02 17:26   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-09-02 17:26 UTC (permalink / raw)
  To: David Jeffery; +Cc: linux-pci, linux-scsi

> 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>

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

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


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

end of thread, other threads:[~2026-09-02 17:26 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 17:07 [PATCH v21 0/9] shut down devices asynchronously David Jeffery
2026-09-02 17:07 ` [PATCH 1/9] driver core: rely on put_device to free dev->p David Jeffery
2026-09-02 17:16   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 2/9] driver core: prevent device_add() during system shutdown David Jeffery
2026-09-02 17:15   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 3/9] driver core: warn should device_move try to move a need_parent_lock device David Jeffery
2026-09-02 17:22   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 4/9] driver core: separate function to shutdown one device David Jeffery
2026-09-02 17:13   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 5/9] driver core: do not always lock parent in shutdown David Jeffery
2026-09-02 17:25   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 6/9] driver core: async device shutdown infrastructure David Jeffery
2026-09-02 17:17   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 7/9] PCI: Link a virtual function to its physical function David Jeffery
2026-09-02 17:17   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 8/9] PCI: Enable async shutdown support David Jeffery
2026-09-02 17:24   ` sashiko-bot
2026-09-02 17:07 ` [PATCH 9/9] scsi: " David Jeffery
2026-09-02 17:26   ` sashiko-bot
  -- strict thread matches above, loose matches on Subject: below --
2026-08-21 14:24 [PATCH v20 0/9] shut down devices asynchronously David Jeffery
2026-08-21 14:24 ` [PATCH 7/9] PCI: Link a virtual function to its physical function David Jeffery
2026-08-21 14:34   ` sashiko-bot

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