Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI: Fix regression in pci_restore_state(), v3

Commit 26f41062f28d ("PCI: check for pci bar restore completion and
retry") attempted to address problems with PCI BAR restoration on
systems where FLR had not been completed before pci_restore_state() was
called, but it did that in an utterly wrong way.

First off, instead of retrying the writes for the BAR registers only, it
did that for all of the PCI config space of the device, including the
status register (whose value after the write quite obviously need not be
the same as the written one). Second, it added arbitrary delay to
pci_restore_state() even for systems where the PCI config space
restoration was successful at first attempt. Finally, the mdelay(10) it
added to every iteration of the writing loop was way too much of a delay
for any reasonable device.

All of this actually caused resume failures for some devices on Mikko's
system.

To fix the regression, make pci_restore_state() only retry the writes
for BAR registers and only wait if the first read from the register
doesn't return the written value. Additionaly, make it wait for 1 ms,
instead of 10 ms, after every failing attempt to write into config
space.

Reported-by: Mikko Vinni <mmvinni@yahoo.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Rafael J. Wysocki and committed by
Linus Torvalds
ebfc5b80 6c23b8e9

+39 -18
+39 -18
drivers/pci/pci.c
··· 967 967 return 0; 968 968 } 969 969 970 + static void pci_restore_config_dword(struct pci_dev *pdev, int offset, 971 + u32 saved_val, int retry) 972 + { 973 + u32 val; 974 + 975 + pci_read_config_dword(pdev, offset, &val); 976 + if (val == saved_val) 977 + return; 978 + 979 + for (;;) { 980 + dev_dbg(&pdev->dev, "restoring config space at offset " 981 + "%#x (was %#x, writing %#x)\n", offset, val, saved_val); 982 + pci_write_config_dword(pdev, offset, saved_val); 983 + if (retry-- <= 0) 984 + return; 985 + 986 + pci_read_config_dword(pdev, offset, &val); 987 + if (val == saved_val) 988 + return; 989 + 990 + mdelay(1); 991 + } 992 + } 993 + 994 + static void pci_restore_config_space(struct pci_dev *pdev, int start, int end, 995 + int retry) 996 + { 997 + int index; 998 + 999 + for (index = end; index >= start; index--) 1000 + pci_restore_config_dword(pdev, 4 * index, 1001 + pdev->saved_config_space[index], 1002 + retry); 1003 + } 1004 + 970 1005 /** 971 1006 * pci_restore_state - Restore the saved state of a PCI device 972 1007 * @dev: - PCI device that we're dealing with 973 1008 */ 974 1009 void pci_restore_state(struct pci_dev *dev) 975 1010 { 976 - int i; 977 - u32 val; 978 - int tries; 979 - 980 1011 if (!dev->state_saved) 981 1012 return; 982 1013 ··· 1015 984 pci_restore_pcie_state(dev); 1016 985 pci_restore_ats_state(dev); 1017 986 987 + pci_restore_config_space(dev, 10, 15, 0); 1018 988 /* 1019 989 * The Base Address register should be programmed before the command 1020 990 * register(s) 1021 991 */ 1022 - for (i = 15; i >= 0; i--) { 1023 - pci_read_config_dword(dev, i * 4, &val); 1024 - tries = 10; 1025 - while (tries && val != dev->saved_config_space[i]) { 1026 - dev_dbg(&dev->dev, "restoring config " 1027 - "space at offset %#x (was %#x, writing %#x)\n", 1028 - i, val, (int)dev->saved_config_space[i]); 1029 - pci_write_config_dword(dev,i * 4, 1030 - dev->saved_config_space[i]); 1031 - pci_read_config_dword(dev, i * 4, &val); 1032 - mdelay(10); 1033 - tries--; 1034 - } 1035 - } 992 + pci_restore_config_space(dev, 4, 9, 10); 993 + pci_restore_config_space(dev, 0, 3, 0); 994 + 1036 995 pci_restore_pcix_state(dev); 1037 996 pci_restore_msi_state(dev); 1038 997 pci_restore_iov_state(dev);