Skip to content

Commit

Permalink
PCI: handle PCI state saving with interrupts disabled
Browse files Browse the repository at this point in the history
Since interrupts will soon be disabled at PCI resume time, we need to
pre-allocate memory to save/restore PCI config space (or use GFP_ATOMIC,
but this is safer).

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: "Rafael J. Wysocki" <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Rafael J. Wysocki authored and Jesse Barnes committed Jan 7, 2009
1 parent 894886e commit 63f4898
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 22 deletions.
73 changes: 51 additions & 22 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -640,19 +640,14 @@ static int pci_save_pcie_state(struct pci_dev *dev)
int pos, i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
int found = 0;

pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (pos <= 0)
return 0;

save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
if (!save_state)
save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
else
found = 1;
if (!save_state) {
dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__);
return -ENOMEM;
}
cap = (u16 *)&save_state->data[0];
Expand All @@ -661,9 +656,7 @@ static int pci_save_pcie_state(struct pci_dev *dev)
pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
save_state->cap_nr = PCI_CAP_ID_EXP;
if (!found)
pci_add_saved_cap(dev, save_state);

return 0;
}

Expand All @@ -688,30 +681,21 @@ static void pci_restore_pcie_state(struct pci_dev *dev)

static int pci_save_pcix_state(struct pci_dev *dev)
{
int pos, i = 0;
int pos;
struct pci_cap_saved_state *save_state;
u16 *cap;
int found = 0;

pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (pos <= 0)
return 0;

save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX);
if (!save_state)
save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL);
else
found = 1;
if (!save_state) {
dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n");
dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__);
return -ENOMEM;
}
cap = (u16 *)&save_state->data[0];

pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]);
save_state->cap_nr = PCI_CAP_ID_PCIX;
if (!found)
pci_add_saved_cap(dev, save_state);
pci_read_config_word(dev, pos + PCI_X_CMD, (u16 *)save_state->data);

return 0;
}

Expand Down Expand Up @@ -1300,6 +1284,51 @@ void pci_pm_init(struct pci_dev *dev)
}
}

/**
* pci_add_save_buffer - allocate buffer for saving given capability registers
* @dev: the PCI device
* @cap: the capability to allocate the buffer for
* @size: requested size of the buffer
*/
static int pci_add_cap_save_buffer(
struct pci_dev *dev, char cap, unsigned int size)
{
int pos;
struct pci_cap_saved_state *save_state;

pos = pci_find_capability(dev, cap);
if (pos <= 0)
return 0;

save_state = kzalloc(sizeof(*save_state) + size, GFP_KERNEL);
if (!save_state)
return -ENOMEM;

save_state->cap_nr = cap;
pci_add_saved_cap(dev, save_state);

return 0;
}

/**
* pci_allocate_cap_save_buffers - allocate buffers for saving capabilities
* @dev: the PCI device
*/
void pci_allocate_cap_save_buffers(struct pci_dev *dev)
{
int error;

error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP, 4 * sizeof(u16));
if (error)
dev_err(&dev->dev,
"unable to preallocate PCI Express save buffer\n");

error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_PCIX, sizeof(u16));
if (error)
dev_err(&dev->dev,
"unable to preallocate PCI-X save buffer\n");
}

/**
* pci_enable_ari - enable ARI forwarding if hardware support it
* @dev: the PCI device
Expand Down
1 change: 1 addition & 0 deletions drivers/pci/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct pci_platform_pm_ops {

extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
extern void pci_pm_init(struct pci_dev *dev);
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);

extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
Expand Down
3 changes: 3 additions & 0 deletions drivers/pci/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,9 @@ static void pci_init_capabilities(struct pci_dev *dev)
/* MSI/MSI-X list */
pci_msi_init_pci_dev(dev);

/* Buffers for saving PCIe and PCI-X capabilities */
pci_allocate_cap_save_buffers(dev);

/* Power Management */
pci_pm_init(dev);

Expand Down

0 comments on commit 63f4898

Please sign in to comment.