Store a device's saved state as a bitmap of the configuration space DWORDs that were saved plus their values, instead of a copy of the configuration space header followed by a list of pci_cap_saved_data records. The old layout is described entirely by the kernel: which capabilities have a record, how large each record is, and what each word within a record means are all properties of the code that happens to be saving them. That ties the blob to the kernel that produced it, so VFIO cannot carry a device's saved state across a Live Update. The new layout is described by the device: a value's position is the offset of its register in the device's own configuration space. Keep refusing state that the device has nowhere to put in pci_load_saved_state(), the equivalent of the old check that the capability existed and its record was the expected size. Use pci_saved_cap_reserved() for the check, since state saved from another device or kernel can legitimately name an offset that this device did not reserve. No capability uses the store yet, and the patches that follow move them into it one at a time, so keep appending the records of the capabilities that still have their own buffer after the saved DWORDs. The blob therefore continues to describe all of a device's saved state at every step; a later patch drops the records once there are none left. struct pci_saved_state is opaque to everything outside drivers/pci, so no caller needs to change. Signed-off-by: David Matlack --- drivers/pci/pci.c | 89 +++++++++++++++++++++++++++++++++++----- drivers/pci/pci.h | 4 ++ drivers/pci/saved-caps.c | 14 +++++++ 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e5b960ca26e6..007f9bb289d5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2032,11 +2032,40 @@ void pci_restore_state(struct pci_dev *dev) } EXPORT_SYMBOL(pci_restore_state); +/** + * struct pci_saved_state - a device's saved configuration space + * @dword_map: one bit per DWORD of configuration space, set if that DWORD + * was saved + * @dword_val: saved values, in ascending configuration space offset order + * + * Laid out so that the position of every value is described by the device's + * configuration space rather than by the kernel. + * + * Capabilities that have not moved to the saved capability store yet keep + * their own buffer, and their records trail @dword_val until the last of + * those buffers goes away. Both areas vary in length and only one of them + * can be a flexible array member, so the records are reached with + * pci_saved_state_records() rather than declared here. + */ struct pci_saved_state { - u32 config_space[16]; - struct pci_cap_saved_data cap[]; + unsigned long dword_map[BITS_TO_LONGS(PCI_CFG_SPACE_EXP_DWORDS)]; + u32 dword_val[]; + /* struct pci_cap_saved_data cap[] follows dword_val */ }; +/* + * The records that follow the saved DWORDs, one per capability that still has + * its own save buffer, terminated by an empty record. + */ +static struct pci_cap_saved_data * +pci_saved_state_records(struct pci_saved_state *state) +{ + unsigned int nr_dwords = bitmap_weight(state->dword_map, + PCI_CFG_SPACE_EXP_DWORDS); + + return (struct pci_cap_saved_data *)&state->dword_val[nr_dwords]; +} + /** * pci_store_saved_state - Allocate and return an opaque struct containing * the device saved state. @@ -2046,16 +2075,28 @@ struct pci_saved_state { */ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev) { - struct pci_saved_state *state; + DECLARE_BITMAP(map, PCI_CFG_SPACE_EXP_DWORDS); + struct pci_saved_caps *caps = &dev->saved_caps; struct pci_cap_saved_state *tmp; struct pci_cap_saved_data *cap; + struct pci_saved_state *state; + unsigned int dword, nr_dwords, i = 0; size_t size; if (!dev->state_saved) return NULL; - size = sizeof(*state) + sizeof(struct pci_cap_saved_data); + bitmap_zero(map, PCI_CFG_SPACE_EXP_DWORDS); + bitmap_set(map, 0, PCI_STD_HEADER_DWORDS); + if (caps->dword_val) + bitmap_or(map, map, caps->dword_map, PCI_CFG_SPACE_EXP_DWORDS); + + nr_dwords = bitmap_weight(map, PCI_CFG_SPACE_EXP_DWORDS); + + size = struct_size(state, dword_val, nr_dwords); + /* Room for the buffers that are left, and for the terminator */ + size += sizeof(struct pci_cap_saved_data); hlist_for_each_entry(tmp, &dev->saved_cap_space, next) size += sizeof(struct pci_cap_saved_data) + tmp->cap.size; @@ -2063,16 +2104,25 @@ struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev) if (!state) return NULL; - memcpy(state->config_space, dev->saved_config_space, - sizeof(state->config_space)); + bitmap_copy(state->dword_map, map, PCI_CFG_SPACE_EXP_DWORDS); - cap = state->cap; + for_each_set_bit(dword, state->dword_map, PCI_CFG_SPACE_EXP_DWORDS) { + u32 *val = &state->dword_val[i++]; + + if (dword < PCI_STD_HEADER_DWORDS) + *val = dev->saved_config_space[dword]; + else + pci_read_saved_cap_dword(dev, dword * sizeof(u32), val); + } + + cap = pci_saved_state_records(state); hlist_for_each_entry(tmp, &dev->saved_cap_space, next) { size_t len = sizeof(struct pci_cap_saved_data) + tmp->cap.size; + memcpy(cap, &tmp->cap, len); cap = (struct pci_cap_saved_data *)((u8 *)cap + len); } - /* Empty cap_save terminates list */ + /* Empty record terminates the list */ return state; } @@ -2087,16 +2137,33 @@ int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state) { struct pci_cap_saved_data *cap; + unsigned int dword, i = 0; dev->state_saved = false; if (!state) return 0; - memcpy(dev->saved_config_space, state->config_space, - sizeof(state->config_space)); + for_each_set_bit(dword, state->dword_map, PCI_CFG_SPACE_EXP_DWORDS) { + unsigned int off = dword * sizeof(u32); + u32 val = state->dword_val[i++]; + + if (dword < PCI_STD_HEADER_DWORDS) { + dev->saved_config_space[dword] = val; + continue; + } + + /* + * Refuse state that the device has nowhere to put, e.g. + * because it was saved from a different device. + */ + if (!pci_saved_cap_reserved(dev, off)) + return -EINVAL; + + pci_write_saved_cap_dword(dev, off, val); + } - cap = state->cap; + cap = pci_saved_state_records(state); while (cap->size) { struct pci_cap_saved_state *tmp; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6676177706ba..206e0d04e06e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -258,6 +258,9 @@ struct pci_cap_saved_state *pci_find_saved_cap(struct pci_dev *dev, char cap); struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, u16 cap); +/* DWORDs of the configuration space header, i.e. pci_dev.saved_config_space */ +#define PCI_STD_HEADER_DWORDS (PCI_STD_HEADER_SIZEOF / sizeof(u32)) + void pci_saved_caps_finalize(struct pci_dev *dev); void pci_saved_caps_release(struct pci_dev *dev); int pci_reserve_saved_cap(struct pci_dev *dev, unsigned int off, unsigned int len); @@ -269,6 +272,7 @@ void pci_restore_cap_dword(struct pci_dev *dev, unsigned int off); bool pci_read_saved_cap_word(struct pci_dev *dev, unsigned int off, u16 *val); bool pci_read_saved_cap_dword(struct pci_dev *dev, unsigned int off, u32 *val); void pci_write_saved_cap_word(struct pci_dev *dev, unsigned int off, u16 val); +void pci_write_saved_cap_dword(struct pci_dev *dev, unsigned int off, u32 val); #define PCI_PM_D2_DELAY 200 /* usec; see PCIe r4.0, sec 5.9.1 */ #define PCI_PM_D3HOT_WAIT 10 /* msec */ diff --git a/drivers/pci/saved-caps.c b/drivers/pci/saved-caps.c index 92865891514e..4647ce59ce74 100644 --- a/drivers/pci/saved-caps.c +++ b/drivers/pci/saved-caps.c @@ -211,6 +211,20 @@ void pci_write_saved_cap_word(struct pci_dev *dev, unsigned int off, u16 val) *slot |= (u32)val << shift; } +/** + * pci_write_saved_cap_dword - change the saved value of a 32-bit register + * @dev: the PCI device + * @off: offset of the register in configuration space + * @val: value to save + */ +void pci_write_saved_cap_dword(struct pci_dev *dev, unsigned int off, u32 val) +{ + u32 *slot = pci_saved_cap_slot(dev, off); + + if (slot) + *slot = val; +} + /** * pci_restore_cap_dword - restore a 32-bit capability register * @dev: the PCI device -- 2.56.0.455.g6c5a503b38-goog