Skip to content

Commit

Permalink
PCI: Add interfaces to store and load the device saved state
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alex Williamson authored and Jesse Barnes committed May 21, 2011
1 parent 24a4742 commit ffbdd3f
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 0 deletions.
98 changes: 98 additions & 0 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,104 @@ void pci_restore_state(struct pci_dev *dev)
dev->state_saved = false;
}

struct pci_saved_state {
u32 config_space[16];
struct pci_cap_saved_data cap[0];
};

/**
* pci_store_saved_state - Allocate and return an opaque struct containing
* the device saved state.
* @dev: PCI device that we're dealing with
*
* Rerturn NULL if no state or error.
*/
struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev)
{
struct pci_saved_state *state;
struct pci_cap_saved_state *tmp;
struct pci_cap_saved_data *cap;
struct hlist_node *pos;
size_t size;

if (!dev->state_saved)
return NULL;

size = sizeof(*state) + sizeof(struct pci_cap_saved_data);

hlist_for_each_entry(tmp, pos, &dev->saved_cap_space, next)
size += sizeof(struct pci_cap_saved_data) + tmp->cap.size;

state = kzalloc(size, GFP_KERNEL);
if (!state)
return NULL;

memcpy(state->config_space, dev->saved_config_space,
sizeof(state->config_space));

cap = state->cap;
hlist_for_each_entry(tmp, pos, &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 */

return state;
}
EXPORT_SYMBOL_GPL(pci_store_saved_state);

/**
* pci_load_saved_state - Reload the provided save state into struct pci_dev.
* @dev: PCI device that we're dealing with
* @state: Saved state returned from pci_store_saved_state()
*/
int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state)
{
struct pci_cap_saved_data *cap;

dev->state_saved = false;

if (!state)
return 0;

memcpy(dev->saved_config_space, state->config_space,
sizeof(state->config_space));

cap = state->cap;
while (cap->size) {
struct pci_cap_saved_state *tmp;

tmp = pci_find_saved_cap(dev, cap->cap_nr);
if (!tmp || tmp->cap.size != cap->size)
return -EINVAL;

memcpy(tmp->cap.data, cap->data, tmp->cap.size);
cap = (struct pci_cap_saved_data *)((u8 *)cap +
sizeof(struct pci_cap_saved_data) + cap->size);
}

dev->state_saved = true;
return 0;
}
EXPORT_SYMBOL_GPL(pci_load_saved_state);

/**
* pci_load_and_free_saved_state - Reload the save state pointed to by state,
* and free the memory allocated for it.
* @dev: PCI device that we're dealing with
* @state: Pointer to saved state returned from pci_store_saved_state()
*/
int pci_load_and_free_saved_state(struct pci_dev *dev,
struct pci_saved_state **state)
{
int ret = pci_load_saved_state(dev, *state);
kfree(*state);
*state = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state);

static int do_pci_enable_device(struct pci_dev *dev, int bars)
{
int err;
Expand Down
4 changes: 4 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,10 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size);
/* Power management related routines */
int pci_save_state(struct pci_dev *dev);
void pci_restore_state(struct pci_dev *dev);
struct pci_saved_state *pci_store_saved_state(struct pci_dev *dev);
int pci_load_saved_state(struct pci_dev *dev, struct pci_saved_state *state);
int pci_load_and_free_saved_state(struct pci_dev *dev,
struct pci_saved_state **state);
int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state);
int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
Expand Down

0 comments on commit ffbdd3f

Please sign in to comment.