diff options
| author | Alex Williamson <alex.williamson@redhat.com> | 2011-05-10 12:02:27 -0400 |
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2011-05-21 15:17:09 -0400 |
| commit | ffbdd3f7931fb7cb7e36d00d16303ec433be5145 (patch) | |
| tree | 503e1ad819bb3f1d682de24de5271935849ba5ff | |
| parent | 24a4742f0be6226eb0106fbb17caf4d711d1ad43 (diff) | |
PCI: Add interfaces to store and load the device saved state
For KVM device assignment, we'd like to save off the state of a device
prior to passing it to the guest and restore it later. We also want
to allow pci_reset_funciton() to be called while the device is owned
by the guest. This however overwrites and invalidates the struct pci_dev
buffers, so we can't just manually call save and restore. Add generic
interfaces for the saved state to be stored and reloaded back into
struct pci_dev at a later time.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
| -rw-r--r-- | drivers/pci/pci.c | 98 | ||||
| -rw-r--r-- | include/linux/pci.h | 4 |
2 files changed, 102 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d6e5b8ea9194..22c9b27fdd8d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -976,6 +976,104 @@ void pci_restore_state(struct pci_dev *dev) | |||
| 976 | dev->state_saved = false; | 976 | dev->state_saved = false; |
| 977 | } | 977 | } |
| 978 | 978 | ||
| 979 | struct pci_saved_state { | ||
| 980 | u32 config_space[16]; | ||
| 981 | struct pci_cap_saved_data cap[0]; | ||
| 982 | }; | ||
| 983 | |||
| 984 | /** | ||
| 985 | * pci_store_saved_state - Allocate and return an opaque struct containing | ||
| 986 | * the device saved state. | ||
| 987 | * @dev: PCI device that we're dealing with | ||
| 988 | * | ||
| 989 | * Rerturn NULL if no state or error. | ||
| 990 | */ | ||
| 991 | struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev) | ||
| 992 | { | ||
| 993 | struct pci_saved_state *state; | ||
| 994 | struct pci_cap_saved_state *tmp; | ||
| 995 | struct pci_cap_saved_data *cap; | ||
| 996 | struct hlist_node *pos; | ||
| 997 | size_t size; | ||
| 998 | |||
| 999 | if (!dev->state_saved) | ||
| 1000 | return NULL; | ||
| 1001 | |||
| 1002 | size = sizeof(*state) + sizeof(struct pci_cap_saved_data); | ||
| 1003 | |||
| 1004 | hlist_for_each_entry(tmp, pos, &dev->saved_cap_space, next) | ||
| 1005 | size += sizeof(struct pci_cap_saved_data) + tmp->cap.size; | ||
| 1006 | |||
| 1007 | state = kzalloc(size, GFP_KERNEL); | ||
| 1008 | if (!state) | ||
| 1009 | return NULL; | ||
| 1010 | |||
| 1011 | memcpy(state->config_space, dev->saved_config_space, | ||
| 1012 | sizeof(state->config_space)); | ||
| 1013 | |||
| 1014 | cap = state->cap; | ||
| 1015 | hlist_for_each_entry(tmp, pos, &dev->saved_cap_space, next) { | ||
| 1016 | size_t len = sizeof(struct pci_cap_saved_data) + tmp->cap.size; | ||
| 1017 | memcpy(cap, &tmp->cap, len); | ||
| 1018 | cap = (struct pci_cap_saved_data *)((u8 *)cap + len); | ||
| 1019 | } | ||
| 1020 | /* Empty cap_save terminates list */ | ||
| 1021 | |||
| 1022 | return state; | ||
| 1023 | } | ||
| 1024 | EXPORT_SYMBOL_GPL(pci_store_saved_state); | ||
| 1025 | |||
| 1026 | /** | ||
| 1027 | * pci_load_saved_state - Reload the provided save state into struct pci_dev. | ||
| 1028 | * @dev: PCI device that we're dealing with | ||
| 1029 | * @state: Saved state returned from pci_store_saved_state() | ||
| 1030 | */ | ||
| 1031 | int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state) | ||
| 1032 | { | ||
| 1033 | struct pci_cap_saved_data *cap; | ||
| 1034 | |||
| 1035 | dev->state_saved = false; | ||
| 1036 | |||
| 1037 | if (!state) | ||
| 1038 | return 0; | ||
| 1039 | |||
| 1040 | memcpy(dev->saved_config_space, state->config_space, | ||
| 1041 | sizeof(state->config_space)); | ||
| 1042 | |||
| 1043 | cap = state->cap; | ||
| 1044 | while (cap->size) { | ||
| 1045 | struct pci_cap_saved_state *tmp; | ||
| 1046 | |||
| 1047 | tmp = pci_find_saved_cap(dev, cap->cap_nr); | ||
| 1048 | if (!tmp || tmp->cap.size != cap->size) | ||
| 1049 | return -EINVAL; | ||
| 1050 | |||
| 1051 | memcpy(tmp->cap.data, cap->data, tmp->cap.size); | ||
| 1052 | cap = (struct pci_cap_saved_data *)((u8 *)cap + | ||
| 1053 | sizeof(struct pci_cap_saved_data) + cap->size); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | dev->state_saved = true; | ||
| 1057 | return 0; | ||
| 1058 | } | ||
| 1059 | EXPORT_SYMBOL_GPL(pci_load_saved_state); | ||
| 1060 | |||
| 1061 | /** | ||
| 1062 | * pci_load_and_free_saved_state - Reload the save state pointed to by state, | ||
| 1063 | * and free the memory allocated for it. | ||
| 1064 | * @dev: PCI device that we're dealing with | ||
| 1065 | * @state: Pointer to saved state returned from pci_store_saved_state() | ||
| 1066 | */ | ||
| 1067 | int pci_load_and_free_saved_state(struct pci_dev *dev, | ||
| 1068 | struct pci_saved_state **state) | ||
| 1069 | { | ||
| 1070 | int ret = pci_load_saved_state(dev, *state); | ||
| 1071 | kfree(*state); | ||
| 1072 | *state = NULL; | ||
| 1073 | return ret; | ||
| 1074 | } | ||
| 1075 | EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state); | ||
| 1076 | |||
| 979 | static int do_pci_enable_device(struct pci_dev *dev, int bars) | 1077 | static int do_pci_enable_device(struct pci_dev *dev, int bars) |
| 980 | { | 1078 | { |
| 981 | int err; | 1079 | int err; |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 61ef8f2f9b19..4604d1d5514d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -812,6 +812,10 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size); | |||
| 812 | /* Power management related routines */ | 812 | /* Power management related routines */ |
| 813 | int pci_save_state(struct pci_dev *dev); | 813 | int pci_save_state(struct pci_dev *dev); |
| 814 | void pci_restore_state(struct pci_dev *dev); | 814 | void pci_restore_state(struct pci_dev *dev); |
| 815 | struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev); | ||
| 816 | int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state); | ||
| 817 | int pci_load_and_free_saved_state(struct pci_dev *dev, | ||
| 818 | struct pci_saved_state **state); | ||
| 815 | int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state); | 819 | int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state); |
| 816 | int pci_set_power_state(struct pci_dev *dev, pci_power_t state); | 820 | int pci_set_power_state(struct pci_dev *dev, pci_power_t state); |
| 817 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); | 821 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); |
