Add a PCI device saved state member to indicate the device is requested vs depended. Restore the PCI subsystem saved state folio during PCI enumeration. When a new PCI device is created, restore the per device state pointer into the dev->lu.dev_state if the device is found in the saved devices array, by matching the device path. Also restore the dev->lu.requested or dev->lu.depended base on the saved "requested" field. Add such devices to the "probed_devices" list. Signed-off-by: Chris Li <chrisl@xxxxxxxxxx> --- drivers/pci/liveupdate.c | 54 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 6 +++++ drivers/pci/probe.c | 2 ++ include/linux/dev_liveupdate.h | 2 ++ 4 files changed, 64 insertions(+) diff --git a/drivers/pci/liveupdate.c b/drivers/pci/liveupdate.c index bbff9b314f99185dfe8941b711cdf0db16b1ed8a..4d13071f5edd6520adb64003262f08d1f79e26c4 100644 --- a/drivers/pci/liveupdate.c +++ b/drivers/pci/liveupdate.c @@ -16,6 +16,7 @@ #define PCI_SUBSYSTEM_NAME "pci" static LIST_HEAD(preserved_devices); +static LIST_HEAD(probe_devices); struct pci_dev_ser { u32 path; /* domain + bus + slot + fn */ @@ -91,6 +92,7 @@ static int build_liveupdate_devices(struct list_head *head) static void dev_cleanup_liveupdate(struct device *dev) { dev->lu.depended = 0; + dev->lu.dev_state = NULL; list_del_init(&dev->lu.lu_next); } @@ -310,6 +312,58 @@ struct liveupdate_subsystem pci_liveupdate_ops = { .name = PCI_SUBSYSTEM_NAME, }; +static struct pci_ser *pci_state_get(void) +{ + static struct pci_ser *pci_state; + struct folio *folio; + phys_addr_t data = 0; + int ret; + + if (pci_state) + return pci_state; + + ret = liveupdate_get_subsystem_data(&pci_liveupdate_ops, &data); + if (ret || !data) + panic("PCI liveupdate: get subsystem data: [%llx] ret: %d", data, ret); + + folio = kho_restore_folio(data); + if (!folio) + panic("PCI liveupdate: restore folio from %llx failed", data); + + /* Cache the value for future callers. */ + pci_state = folio_address(folio); + return pci_state; +} + +static void pci_dev_do_restore(struct pci_dev *dev, struct pci_dev_ser *s) +{ + dev->dev.lu.dev_state = s; + if (s->requested) + dev->dev.lu.requested = 1; + else + dev->dev.lu.depended = 1; + pci_info(dev, "liveupdate restore [%s] data: [%llx]\n", + s->requested ? "requested" : "depended", + s->driver_data); + list_move_tail(&dev->dev.lu.lu_next, &probe_devices); +} + +void pci_liveupdate_restore(struct pci_dev *dev) +{ + int path; + struct pci_dev_ser *s, *end; + + if (!liveupdate_state_updated()) + return; + + path = pci_get_device_path(dev); + s = pci_state_get()->devs; + end = s + pci_state_get()->count; + for (; s < end; s++) + if (s->path == path) + return pci_dev_do_restore(dev, s); +} + static int __init pci_liveupdate_init(void) { int ret; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 12215ee72afb682b669c0e3a582b5379828e70c4..c9a7383753949994e031dc362920286a475fe2ab 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1159,4 +1159,10 @@ static inline int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int inde (PCI_CONF1_ADDRESS(bus, dev, func, reg) | \ PCI_CONF1_EXT_REG(reg)) +#ifdef CONFIG_LIVEUPDATE +void pci_liveupdate_restore(struct pci_dev *dev); +#else +static inline void pci_liveupdate_restore(struct pci_dev *dev) {} +#endif + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dddd7ebc03d1a6e6ee456e0bf02ab9833a819509..a0605af1a699cd07b09897172803dcba1d2da9f9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2017,6 +2017,8 @@ int pci_setup_device(struct pci_dev *dev) if (pci_early_dump) early_dump_pci_device(dev); + pci_liveupdate_restore(dev); + /* Need to have dev->class ready */ dev->cfg_size = pci_cfg_space_size(dev); diff --git a/include/linux/dev_liveupdate.h b/include/linux/dev_liveupdate.h index fa664976f9f5e90b8a5a17cfbed8bd2fdc87b7a1..dc65e2b2d92c02bf15440b6745c62cd748721eef 100644 --- a/include/linux/dev_liveupdate.h +++ b/include/linux/dev_liveupdate.h @@ -19,6 +19,7 @@ struct device; * @lu_next: List head for linking the device into live update * related lists (e.g., a list of devices participating * in a live update sequence). + * @dev_state: Set to the device state at restore. * @requested: Set if a live update has been requested for this * device (i.e. device will participate in live update). * @depended: Set if the device participtate the live update due to @@ -32,6 +33,7 @@ struct device; */ struct dev_liveupdate { struct list_head lu_next; + void *dev_state; bool requested:1; bool depended:1; bool visited:1; -- 2.50.1.487.gc89ff58d15-goog