On Tue, Jul 28, 2026 at 10:10:04PM +0000, David Matlack wrote: > Freeze a device's outgoing preservation status (preserved or not > preserved) during shutdown. This enables the PCI core and drivers to > safely make decisions based on the device's preservation status during > shutdown. > > Note that pci_liveupdate_freeze() is triggered by the PCI core rather > than from drivers participating in Live Update so that all devices can > have their status frozen (i.e. prevent non-preserved devices from > getting preserved late).
This calls pci_liveupdate_freeze() from pci_device_shutdown(), the pci_bus_type .shutdown() method, which I think is only called from device_shutdown() in the kexec or restart paths. Since there's no error exit after we get as far as device_shutdown(), and this applies to all devices and dev->liveupdate.frozen is never set to false (at least at this point in the series), is there benefit to tracking this per-device? > Reviewed-by: Pranjal Shrivastava <[email protected]> > Reviewed-by: Pasha Tatashin <[email protected]> > Signed-off-by: David Matlack <[email protected]> Reviewed-by: Bjorn Helgaas <[email protected]> > --- > drivers/pci/liveupdate.c | 16 ++++++++++++++++ > drivers/pci/liveupdate.h | 4 ++++ > drivers/pci/pci-driver.c | 2 ++ > include/linux/pci_liveupdate.h | 3 +++ > 4 files changed, 25 insertions(+) > > diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c > index 744513ee11ed..65bcecb1b64e 100644 > --- a/drivers/pci/liveupdate.c > +++ b/drivers/pci/liveupdate.c > @@ -394,6 +394,11 @@ static int pci_liveupdate_unpreserve_device(struct > pci_flb_outgoing *outgoing, > { > struct pci_dev_ser *dev_ser = dev->liveupdate.outgoing; > > + if (dev->liveupdate.frozen) { > + pci_warn(dev, "Cannot unpreserve device after it is frozen!\n"); > + return -EINVAL; > + } > + > if (!dev_ser) { > pci_warn(dev, "Cannot unpreserve device that is not > preserved\n"); > return -EINVAL; > @@ -447,6 +452,11 @@ static int pci_liveupdate_preserve_device(struct > pci_flb_outgoing *outgoing, > return -EINVAL; > } > > + if (dev->liveupdate.frozen) { > + pci_warn(dev, "Cannot preserve device after it is frozen!\n"); > + return -EINVAL; > + } > + > if (dev->liveupdate.outgoing) { > if (!dev->liveupdate.outgoing->refcount) { > pci_WARN(dev, 1, "Preserved device with 0 refcount!\n"); > @@ -718,6 +728,12 @@ void pci_liveupdate_cleanup_device(struct pci_dev *dev) > pci_WARN(dev, 1, "Destroying incoming-preserved device!\n"); > } > > +void pci_liveupdate_freeze(struct pci_dev *dev) > +{ > + guard(rwsem_write)(&pci_liveupdate.rwsem); > + dev->liveupdate.frozen = 1; Nit, the series assigns "true" to other booleans (preserve_bus_numbers, was_preserved). > +} > + > static int pci_liveupdate_finish_device(struct pci_ser *ser, struct pci_dev > *dev) > { > if (!dev->liveupdate.incoming) { > diff --git a/drivers/pci/liveupdate.h b/drivers/pci/liveupdate.h > index ccc23031e99d..49d07fd02132 100644 > --- a/drivers/pci/liveupdate.h > +++ b/drivers/pci/liveupdate.h > @@ -13,6 +13,7 @@ > #ifdef CONFIG_PCI_LIVEUPDATE > void pci_liveupdate_setup_device(struct pci_dev *dev); > void pci_liveupdate_cleanup_device(struct pci_dev *dev); > +void pci_liveupdate_freeze(struct pci_dev *dev); > bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus, > struct pci_dev *dev); > void pci_liveupdate_scan_bridge_end(struct pci_dev *dev); > @@ -28,6 +29,9 @@ static inline void pci_liveupdate_cleanup_device(struct > pci_dev *dev) > { > } > > +static inline void pci_liveupdate_freeze(struct pci_dev *dev) > +{ > +} > static inline bool pci_liveupdate_preserve_bus_numbers(struct pci_bus *bus, > struct pci_dev *dev) > { > diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c > index f36778e62ac1..51616e4327d3 100644 > --- a/drivers/pci/pci-driver.c > +++ b/drivers/pci/pci-driver.c > @@ -21,6 +21,7 @@ > #include <linux/acpi.h> > #include <linux/dma-map-ops.h> > #include <linux/iommu.h> > +#include "liveupdate.h" > #include "pci.h" > #include "pcie/portdrv.h" > > @@ -536,6 +537,7 @@ static void pci_device_shutdown(struct device *dev) > struct pci_dev *pci_dev = to_pci_dev(dev); > struct pci_driver *drv = pci_dev->driver; > > + pci_liveupdate_freeze(pci_dev); > pm_runtime_resume(dev); > > if (drv && drv->shutdown) > diff --git a/include/linux/pci_liveupdate.h b/include/linux/pci_liveupdate.h > index 04a2b2a3102a..31898685a406 100644 > --- a/include/linux/pci_liveupdate.h > +++ b/include/linux/pci_liveupdate.h > @@ -24,6 +24,8 @@ > * @was_preserved: True if this struct pci_dev was preserved by the previous > * kernel. Unlike @incoming, this field is not cleared after > * the device is finished participating in Live Update. > + * @frozen: True if the outgoing preservation status of this device is frozen > + * and thus cannot be changed. > */ > struct pci_liveupdate { > struct pci_dev_ser *outgoing; > @@ -31,6 +33,7 @@ struct pci_liveupdate { > u16 acs_ctrl; > bool preserve_bus_numbers; > bool was_preserved; > + bool frozen; > }; > > struct pci_dev; > -- > 2.55.0.487.gaf234c4eb3-goog >
