diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2008-12-07 16:02:58 -0500 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-01-07 14:12:40 -0500 |
commit | 63f4898ace2788a89ed685672aab092e1c3e50e6 (patch) | |
tree | ed57eeeb486466697c3d97feaf34396dd2a2b992 /drivers | |
parent | 894886e5d3de0bde2eded8a39bf7e76023fbd791 (diff) |
PCI: handle PCI state saving with interrupts disabled
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pci.c | 73 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 | ||||
-rw-r--r-- | drivers/pci/probe.c | 3 |
3 files changed, 55 insertions, 22 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 47663dc0daf7..3222f9022707 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -640,19 +640,14 @@ static int pci_save_pcie_state(struct pci_dev *dev) | |||
640 | int pos, i = 0; | 640 | int pos, i = 0; |
641 | struct pci_cap_saved_state *save_state; | 641 | struct pci_cap_saved_state *save_state; |
642 | u16 *cap; | 642 | u16 *cap; |
643 | int found = 0; | ||
644 | 643 | ||
645 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | 644 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); |
646 | if (pos <= 0) | 645 | if (pos <= 0) |
647 | return 0; | 646 | return 0; |
648 | 647 | ||
649 | save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); | 648 | save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); |
650 | if (!save_state) | ||
651 | save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); | ||
652 | else | ||
653 | found = 1; | ||
654 | if (!save_state) { | 649 | if (!save_state) { |
655 | dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n"); | 650 | dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); |
656 | return -ENOMEM; | 651 | return -ENOMEM; |
657 | } | 652 | } |
658 | cap = (u16 *)&save_state->data[0]; | 653 | cap = (u16 *)&save_state->data[0]; |
@@ -661,9 +656,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) | |||
661 | pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); | 656 | pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); |
662 | pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); | 657 | pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); |
663 | pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); | 658 | pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); |
664 | save_state->cap_nr = PCI_CAP_ID_EXP; | 659 | |
665 | if (!found) | ||
666 | pci_add_saved_cap(dev, save_state); | ||
667 | return 0; | 660 | return 0; |
668 | } | 661 | } |
669 | 662 | ||
@@ -688,30 +681,21 @@ static void pci_restore_pcie_state(struct pci_dev *dev) | |||
688 | 681 | ||
689 | static int pci_save_pcix_state(struct pci_dev *dev) | 682 | static int pci_save_pcix_state(struct pci_dev *dev) |
690 | { | 683 | { |
691 | int pos, i = 0; | 684 | int pos; |
692 | struct pci_cap_saved_state *save_state; | 685 | struct pci_cap_saved_state *save_state; |
693 | u16 *cap; | ||
694 | int found = 0; | ||
695 | 686 | ||
696 | pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); | 687 | pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); |
697 | if (pos <= 0) | 688 | if (pos <= 0) |
698 | return 0; | 689 | return 0; |
699 | 690 | ||
700 | save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); | 691 | save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); |
701 | if (!save_state) | ||
702 | save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); | ||
703 | else | ||
704 | found = 1; | ||
705 | if (!save_state) { | 692 | if (!save_state) { |
706 | dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n"); | 693 | dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); |
707 | return -ENOMEM; | 694 | return -ENOMEM; |
708 | } | 695 | } |
709 | cap = (u16 *)&save_state->data[0]; | ||
710 | 696 | ||
711 | pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]); | 697 | pci_read_config_word(dev, pos + PCI_X_CMD, (u16 *)save_state->data); |
712 | save_state->cap_nr = PCI_CAP_ID_PCIX; | 698 | |
713 | if (!found) | ||
714 | pci_add_saved_cap(dev, save_state); | ||
715 | return 0; | 699 | return 0; |
716 | } | 700 | } |
717 | 701 | ||
@@ -1301,6 +1285,51 @@ void pci_pm_init(struct pci_dev *dev) | |||
1301 | } | 1285 | } |
1302 | 1286 | ||
1303 | /** | 1287 | /** |
1288 | * pci_add_save_buffer - allocate buffer for saving given capability registers | ||
1289 | * @dev: the PCI device | ||
1290 | * @cap: the capability to allocate the buffer for | ||
1291 | * @size: requested size of the buffer | ||
1292 | */ | ||
1293 | static int pci_add_cap_save_buffer( | ||
1294 | struct pci_dev *dev, char cap, unsigned int size) | ||
1295 | { | ||
1296 | int pos; | ||
1297 | struct pci_cap_saved_state *save_state; | ||
1298 | |||
1299 | pos = pci_find_capability(dev, cap); | ||
1300 | if (pos <= 0) | ||
1301 | return 0; | ||
1302 | |||
1303 | save_state = kzalloc(sizeof(*save_state) + size, GFP_KERNEL); | ||
1304 | if (!save_state) | ||
1305 | return -ENOMEM; | ||
1306 | |||
1307 | save_state->cap_nr = cap; | ||
1308 | pci_add_saved_cap(dev, save_state); | ||
1309 | |||
1310 | return 0; | ||
1311 | } | ||
1312 | |||
1313 | /** | ||
1314 | * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities | ||
1315 | * @dev: the PCI device | ||
1316 | */ | ||
1317 | void pci_allocate_cap_save_buffers(struct pci_dev *dev) | ||
1318 | { | ||
1319 | int error; | ||
1320 | |||
1321 | error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP, 4 * sizeof(u16)); | ||
1322 | if (error) | ||
1323 | dev_err(&dev->dev, | ||
1324 | "unable to preallocate PCI Express save buffer\n"); | ||
1325 | |||
1326 | error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_PCIX, sizeof(u16)); | ||
1327 | if (error) | ||
1328 | dev_err(&dev->dev, | ||
1329 | "unable to preallocate PCI-X save buffer\n"); | ||
1330 | } | ||
1331 | |||
1332 | /** | ||
1304 | * pci_enable_ari - enable ARI forwarding if hardware support it | 1333 | * pci_enable_ari - enable ARI forwarding if hardware support it |
1305 | * @dev: the PCI device | 1334 | * @dev: the PCI device |
1306 | */ | 1335 | */ |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9162e242b99e..7242b511a93f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -45,6 +45,7 @@ struct pci_platform_pm_ops { | |||
45 | 45 | ||
46 | extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); | 46 | extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); |
47 | extern void pci_pm_init(struct pci_dev *dev); | 47 | extern void pci_pm_init(struct pci_dev *dev); |
48 | extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); | ||
48 | 49 | ||
49 | extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); | 50 | extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); |
50 | extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); | 51 | extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); |
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index eb2b985beb48..5dcf2b65e3f9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -958,6 +958,9 @@ static void pci_init_capabilities(struct pci_dev *dev) | |||
958 | /* MSI/MSI-X list */ | 958 | /* MSI/MSI-X list */ |
959 | pci_msi_init_pci_dev(dev); | 959 | pci_msi_init_pci_dev(dev); |
960 | 960 | ||
961 | /* Buffers for saving PCIe and PCI-X capabilities */ | ||
962 | pci_allocate_cap_save_buffers(dev); | ||
963 | |||
961 | /* Power Management */ | 964 | /* Power Management */ |
962 | pci_pm_init(dev); | 965 | pci_pm_init(dev); |
963 | 966 | ||