From 0f12a4e29368a9476076515881d9ef4e5876c6e2 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 13 Jan 2011 19:47:56 +0000 Subject: [PATCH 01/16] PCI: sysfs: Fix failure path for addition of "vpd" attribute Commit 280c73d ("PCI: centralize the capabilities code in pci-sysfs.c") changed the initialisation of the "rom" and "vpd" attributes, and made the failure path for the "vpd" attribute incorrect. We must free the new attribute structure (attr), but instead we currently free dev->vpd->attr. That will normally be NULL, resulting in a memory leak, but it might be a stale pointer, resulting in a double-free. Found by inspection; compile-tested only. Cc: stable@kernel.org Signed-off-by: Ben Hutchings Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 8ecaac983923..cf2b3654b4a8 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1087,7 +1087,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) attr->write = write_vpd_attr; retval = sysfs_create_bin_file(&dev->dev.kobj, attr); if (retval) { - kfree(dev->vpd->attr); + kfree(attr); return retval; } dev->vpd->attr = attr; From c13ff2ff3ad1479f222e18f9caba3db5af68d549 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Mon, 10 Jan 2011 13:08:37 -0800 Subject: [PATCH 02/16] PCI/lpc: irq and pci_ids patch for Intel DH89xxCC DeviceIDs This patch adds the LPC Controller DeviceIDs for the Intel DH89xxCC PCH. The code for capturing ranges of LPC Controller DeviceIDs has also been updated. Acked-by: Jean Delvare Signed-off-by: Seth Heasley Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 15 ++++++--------- include/linux/pci_ids.h | 3 +++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 87e6c8323117..8201165bae28 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -597,21 +597,18 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route return 1; } - if ((device >= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN) && - (device <= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX)) { + if ((device >= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN && + device <= PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX) + || (device >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN && + device <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX) + || (device >= PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN && + device <= PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX)) { r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; return 1; } - if ((device >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN) && - (device <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX)) { - r->name = "PIIX/ICH"; - r->get = pirq_piix_get; - r->set = pirq_piix_set; - return 1; - } return 0; } diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3adb06ebf841..46f23999458d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2480,6 +2480,9 @@ #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS 0x1d22 #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_0 0x1d40 #define PCI_DEVICE_ID_INTEL_PATSBURG_LPC_1 0x1d41 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MIN 0x2310 +#define PCI_DEVICE_ID_INTEL_DH89XXCC_LPC_MAX 0x231f +#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 From 7c867c8899e873652ef98a890d2e647c092bec25 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 24 Jan 2011 21:14:33 +0100 Subject: [PATCH 03/16] PCI: Avoid potential NULL pointer dereference in pci_scan_bridge pci_add_new_bus() calls pci_alloc_child_bus() which calls pci_alloc_bus() that allocates memory dynamically with kzalloc(). The return value of kzalloc() is the pointer that's eventually returned from pci_add_new_bus(), so since kzalloc() can fail and return NULL so can pci_add_new_bus(). Thus we may end up dereferencing a NULL pointer in drivers/pci/probe.c::pci_scan_bridge(). Seems to me we should test for this and bail out if it happens rather than crashing. Also removed some trailing whitespace that bugged me while looking at this. Signed-off-by: Jesper Juhl Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index c84900da3c59..44cbbbaa499d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -764,6 +764,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, if (pci_find_bus(pci_domain_nr(bus), max+1)) goto out; child = pci_add_new_bus(bus, dev, ++max); + if (!child) + goto out; buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->secondary) << 8) @@ -777,7 +779,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, buses &= ~0xff000000; buses |= CARDBUS_LATENCY_TIMER << 24; } - + /* * We need to blast all three values with a single write. */ From b99af4b002e4908d1a5cdaf424529bdf1dc69768 Mon Sep 17 00:00:00 2001 From: "Brandeburg, Jesse" Date: Mon, 14 Feb 2011 09:05:02 -0800 Subject: [PATCH 04/16] PCI: remove quirk for pre-production systems Revert commit 7eb93b175d4de9438a4b0af3a94a112cb5266944 Author: Yu Zhao Date: Fri Apr 3 15:18:11 2009 +0800 PCI: SR-IOV quirk for Intel 82576 NIC If BIOS doesn't allocate resources for the SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the old Flash Memory Space. Please refer to Intel 82576 Gigabit Ethernet Controller Datasheet section 7.9.2.14.2 for details. http://download.intel.com/design/network/datashts/82576_Datasheet.pdf Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes This quirk was added before SR-IOV was in production and now all machines that originally had this issue alreayd have bios updates to correct the issue. The quirk itself is no longer needed and in fact causes bugs if run. Remove it. Signed-off-by: Jesse Brandeburg CC: Yu Zhao CC: Jesse Barnes Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 52 -------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 53a786fd0d40..ff01bfb3cc29 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2618,58 +2618,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ -#ifdef CONFIG_PCI_IOV - -/* - * For Intel 82576 SR-IOV NIC, if BIOS doesn't allocate resources for the - * SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the - * old Flash Memory Space. - */ -static void __devinit quirk_i82576_sriov(struct pci_dev *dev) -{ - int pos, flags; - u32 bar, start, size; - - if (PAGE_SIZE > 0x10000) - return; - - flags = pci_resource_flags(dev, 0); - if ((flags & PCI_BASE_ADDRESS_SPACE) != - PCI_BASE_ADDRESS_SPACE_MEMORY || - (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != - PCI_BASE_ADDRESS_MEM_TYPE_32) - return; - - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); - if (!pos) - return; - - pci_read_config_dword(dev, pos + PCI_SRIOV_BAR, &bar); - if (bar & PCI_BASE_ADDRESS_MEM_MASK) - return; - - start = pci_resource_start(dev, 1); - size = pci_resource_len(dev, 1); - if (!start || size != 0x400000 || start & (size - 1)) - return; - - pci_resource_flags(dev, 1) = 0; - pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); - pci_write_config_dword(dev, pos + PCI_SRIOV_BAR, start); - pci_write_config_dword(dev, pos + PCI_SRIOV_BAR + 12, start + size / 2); - - dev_info(&dev->dev, "use Flash Memory Space for SR-IOV BARs\n"); -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10c9, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e6, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e7, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e8, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150a, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150d, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1518, quirk_i82576_sriov); - -#endif /* CONFIG_PCI_IOV */ - /* Allow manual resource allocation for PCI hotplug bridges * via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For * some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6), From 3449248c8731e8474980856d76bbf9bac9b0682f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 14 Feb 2011 12:27:50 -0800 Subject: [PATCH 05/16] PCI: fix tlan build when CONFIG_PCI is not enabled When CONFIG_PCI is not enabled, tlan.c has a build error: drivers/net/tlan.c:503: error: implicit declaration of function 'pci_wake_from_d3' so add an inline function stub for this function to pci.h when PCI is not enabled, similar to other stubbed PCI functions. Signed-off-by: Randy Dunlap Acked-by: David S. Miller Acked-by: Sakari Ailus Signed-off-by: Jesse Barnes --- include/linux/pci.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index 559d02897075..c77e730bcd38 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1191,6 +1191,11 @@ static inline int pci_set_power_state(struct pci_dev *dev, pci_power_t state) return 0; } +static inline int pci_wake_from_d3(struct pci_dev *dev, bool enable) +{ + return 0; +} + static inline pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) { From 457d9d088b026e26dbab98cad9d299c1faf4c343 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Tue, 11 Jan 2011 15:34:35 -0500 Subject: [PATCH 06/16] PCI: aer-inject: Override PCIe AER Mask Registers I have several systems which have the same problem: The PCIe AER corrected and uncorrected masks have all the error bits set. This results in the inablility to test with the aer_inject module & utility on those systems. Add the 'aer_mask_override' module parameter which will override the corrected or uncorrected masks for a PCI device. The mask will have the bit corresponding to the status passed into the aer_inject() function. After this patch it is possible to successfully use the aer_inject utility on those PCI slots. Successfully tested by me on a Dell and Intel whitebox which exhibited the mask problem. Signed-off-by: Prarit Bhargava Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aer_inject.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index b3cf6223f63a..f62079ff06dd 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -27,6 +27,10 @@ #include #include "aerdrv.h" +/* Override the existing corrected and uncorrected error masks */ +static int aer_mask_override; +module_param(aer_mask_override, bool, 0); + struct aer_error_inj { u8 bus; u8 dev; @@ -322,7 +326,7 @@ static int aer_inject(struct aer_error_inj *einj) unsigned long flags; unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); int pos_cap_err, rp_pos_cap_err; - u32 sever, cor_mask, uncor_mask; + u32 sever, cor_mask, uncor_mask, cor_mask_orig, uncor_mask_orig; int ret = 0; dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn); @@ -361,6 +365,18 @@ static int aer_inject(struct aer_error_inj *einj) goto out_put; } + if (aer_mask_override) { + cor_mask_orig = cor_mask; + cor_mask &= !(einj->cor_status); + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, + cor_mask); + + uncor_mask_orig = uncor_mask; + uncor_mask &= !(einj->uncor_status); + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, + uncor_mask); + } + spin_lock_irqsave(&inject_lock, flags); err = __find_aer_error_by_dev(dev); @@ -378,14 +394,16 @@ static int aer_inject(struct aer_error_inj *einj) err->header_log2 = einj->header_log2; err->header_log3 = einj->header_log3; - if (einj->cor_status && !(einj->cor_status & ~cor_mask)) { + if (!aer_mask_override && einj->cor_status && + !(einj->cor_status & ~cor_mask)) { ret = -EINVAL; printk(KERN_WARNING "The correctable error(s) is masked " "by device\n"); spin_unlock_irqrestore(&inject_lock, flags); goto out_put; } - if (einj->uncor_status && !(einj->uncor_status & ~uncor_mask)) { + if (!aer_mask_override && einj->uncor_status && + !(einj->uncor_status & ~uncor_mask)) { ret = -EINVAL; printk(KERN_WARNING "The uncorrectable error(s) is masked " "by device\n"); @@ -425,6 +443,13 @@ static int aer_inject(struct aer_error_inj *einj) } spin_unlock_irqrestore(&inject_lock, flags); + if (aer_mask_override) { + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_COR_MASK, + cor_mask_orig); + pci_write_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK, + uncor_mask_orig); + } + ret = pci_bus_set_aer_ops(dev->bus); if (ret) goto out_put; From cdb9755849fbaf2bb9c0a009ba5baa817a0f152d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:45:09 +0100 Subject: [PATCH 07/16] PCI: add more checking to ICH region quirks Per ICH4 and ICH6 specs, ACPI and GPIO regions are valid iff ACPI_EN and GPIO_EN bits are set to 1. Add checks for these bits into the quirks prior to the region creation. While at it, name the constants by macros. Signed-off-by: Jiri Slaby Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Thomas Renninger Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 45 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ff01bfb3cc29..9e23912c97ac 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -533,6 +533,17 @@ static void __devinit quirk_piix4_acpi(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, quirk_piix4_acpi); +#define ICH_PMBASE 0x40 +#define ICH_ACPI_CNTL 0x44 +#define ICH4_ACPI_EN 0x10 +#define ICH6_ACPI_EN 0x80 +#define ICH4_GPIOBASE 0x58 +#define ICH4_GPIO_CNTL 0x5c +#define ICH4_GPIO_EN 0x10 +#define ICH6_GPIOBASE 0x48 +#define ICH6_GPIO_CNTL 0x4c +#define ICH6_GPIO_EN 0x10 + /* * ICH4, ICH4-M, ICH5, ICH5-M ACPI: Three IO regions pointed to by longwords at * 0x40 (128 bytes of ACPI, GPIO & TCO registers) @@ -541,12 +552,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, qui static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) { u32 region; + u8 enable; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH4 ACPI/GPIO/TCO"); + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); + if (enable & ICH4_ACPI_EN) { + pci_read_config_dword(dev, ICH_PMBASE, ®ion); + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); + } - pci_read_config_dword(dev, 0x58, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH4 GPIO"); + pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); + if (enable & ICH4_GPIO_EN) { + pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); + quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, + "ICH4 GPIO"); + } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, quirk_ich4_lpc_acpi); @@ -562,12 +582,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, qui static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) { u32 region; + u8 enable; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH6 ACPI/GPIO/TCO"); + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); + if (enable & ICH6_ACPI_EN) { + pci_read_config_dword(dev, ICH_PMBASE, ®ion); + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); + } - pci_read_config_dword(dev, 0x48, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO"); + pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); + if (enable & ICH4_GPIO_EN) { + pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); + quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, + "ICH6 GPIO"); + } } static void __devinit ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) From 6058989bad05b82e78baacce69ec14f27a11b5fd Mon Sep 17 00:00:00 2001 From: "Narendra_K@Dell.com" Date: Wed, 2 Mar 2011 22:34:17 +0530 Subject: [PATCH 08/16] PCI: Export ACPI _DSM provided firmware instance number and string name to sysfs This patch exports ACPI _DSM (Device Specific Method) provided firmware instance number and string name of PCI devices as defined by 'PCI Firmware Specification Revision 3.1' section 4.6.7.( DSM for Naming a PCI or PCI Express Device Under Operating Systems) to sysfs. New files created are: /sys/bus/pci/devices/.../label which contains the firmware name for the device in question, and /sys/bus/pci/devices/.../acpi_index which contains the firmware device type instance for the given device. cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/acpi_index 1 cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/label Embedded Broadcom 5709C NIC 1 cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/acpi_index 2 cat /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.1/label Embedded Broadcom 5709C NIC 2 The ACPI _DSM provided firmware 'instance number' and 'string name' will be given priority if the firmware also provides 'SMBIOS type 41 device type instance and string'. Signed-off-by: Matthew Garrett Signed-off-by: Jordan Hargrave Signed-off-by: Narendra K Signed-off-by: Jesse Barnes --- Documentation/ABI/testing/sysfs-bus-pci | 31 ++- drivers/pci/Kconfig | 2 + drivers/pci/Makefile | 3 +- drivers/pci/pci-label.c | 247 +++++++++++++++++++++++- drivers/pci/pci.h | 2 +- 5 files changed, 270 insertions(+), 15 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index f979d825d112..36bf454ba855 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -145,9 +145,11 @@ Date: July 2010 Contact: Narendra K , linux-bugs@dell.com Description: Reading this attribute will provide the firmware - given name(SMBIOS type 41 string) of the PCI device. - The attribute will be created only if the firmware - has given a name to the PCI device. + given name (SMBIOS type 41 string or ACPI _DSM string) of + the PCI device. The attribute will be created only + if the firmware has given a name to the PCI device. + ACPI _DSM string name will be given priority if the + system firmware provides SMBIOS type 41 string also. Users: Userspace applications interested in knowing the firmware assigned name of the PCI device. @@ -157,12 +159,27 @@ Date: July 2010 Contact: Narendra K , linux-bugs@dell.com Description: Reading this attribute will provide the firmware - given instance(SMBIOS type 41 device type instance) - of the PCI device. The attribute will be created - only if the firmware has given a device type instance - to the PCI device. + given instance (SMBIOS type 41 device type instance) of the + PCI device. The attribute will be created only if the firmware + has given an instance number to the PCI device. Users: Userspace applications interested in knowing the firmware assigned device type instance of the PCI device that can help in understanding the firmware intended order of the PCI device. + +What: /sys/bus/pci/devices/.../acpi_index +Date: July 2010 +Contact: Narendra K , linux-bugs@dell.com +Description: + Reading this attribute will provide the firmware + given instance (ACPI _DSM instance number) of the PCI device. + The attribute will be created only if the firmware has given + an instance number to the PCI device. ACPI _DSM instance number + will be given priority if the system firmware provides SMBIOS + type 41 device type instance also. +Users: + Userspace applications interested in knowing the + firmware assigned instance number of the PCI + device that can help in understanding the firmware + intended order of the PCI device. diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index a9523fdc6911..c8ff646c0b05 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -87,3 +87,5 @@ config PCI_IOAPIC depends on ACPI depends on HOTPLUG default y + +select NLS if (DMI || ACPI) diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 98e6fdf34d30..bb1d3b2d81ec 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -53,8 +53,9 @@ obj-$(CONFIG_TILE) += setup-bus.o setup-irq.o # # ACPI Related PCI FW Functions +# ACPI _DSM provided firmware instance and string name # -obj-$(CONFIG_ACPI) += pci-acpi.o +obj-$(CONFIG_ACPI) += pci-acpi.o pci-label.o # SMBIOS provided firmware instance and labels obj-$(CONFIG_DMI) += pci-label.o diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 90c0a729cd3a..824e247edc58 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -5,6 +5,13 @@ * by Narendra K , * Jordan Hargrave * + * PCI Firmware Specification Revision 3.1 section 4.6.7 (DSM for Naming a + * PCI or PCI Express Device Under Operating Systems) defines an instance + * number and string name. This code retrieves them and exports them to sysfs. + * If the system firmware does not provide the ACPI _DSM (Device Specific + * Method), then the SMBIOS type 41 instance number and string is exported to + * sysfs. + * * SMBIOS defines type 41 for onboard pci devices. This code retrieves * the instance number and string from the type 41 record and exports * it to sysfs. @@ -19,8 +26,30 @@ #include #include #include +#include +#include +#include +#include +#include #include "pci.h" +#define DEVICE_LABEL_DSM 0x07 + +#ifndef CONFIG_DMI + +static inline int +pci_create_smbiosname_file(struct pci_dev *pdev) +{ + return -1; +} + +static inline void +pci_remove_smbiosname_file(struct pci_dev *pdev) +{ +} + +#else + enum smbios_attr_enum { SMBIOS_ATTR_NONE = 0, SMBIOS_ATTR_LABEL_SHOW, @@ -120,9 +149,7 @@ static struct attribute_group smbios_attr_group = { static int pci_create_smbiosname_file(struct pci_dev *pdev) { - if (!sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group)) - return 0; - return -ENODEV; + return sysfs_create_group(&pdev->dev.kobj, &smbios_attr_group); } static void @@ -131,13 +158,221 @@ pci_remove_smbiosname_file(struct pci_dev *pdev) sysfs_remove_group(&pdev->dev.kobj, &smbios_attr_group); } +#endif + +#ifndef CONFIG_ACPI + +static inline int +pci_create_acpi_index_label_files(struct pci_dev *pdev) +{ + return -1; +} + +static inline int +pci_remove_acpi_index_label_files(struct pci_dev *pdev) +{ + return -1; +} + +#else + +static const char device_label_dsm_uuid[] = { + 0xD0, 0x37, 0xC9, 0xE5, 0x53, 0x35, 0x7A, 0x4D, + 0x91, 0x17, 0xEA, 0x4D, 0x19, 0xC3, 0x43, 0x4D +}; + +enum acpi_attr_enum { + ACPI_ATTR_NONE = 0, + ACPI_ATTR_LABEL_SHOW, + ACPI_ATTR_INDEX_SHOW, +}; + +static void dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf) +{ + int len; + len = utf16s_to_utf8s((const wchar_t *)obj-> + package.elements[1].string.pointer, + obj->package.elements[1].string.length, + UTF16_LITTLE_ENDIAN, + buf, PAGE_SIZE); + buf[len] = '\n'; +} + +static int +dsm_get_label(acpi_handle handle, int func, + struct acpi_buffer *output, + char *buf, enum acpi_attr_enum attribute) +{ + struct acpi_object_list input; + union acpi_object params[4]; + union acpi_object *obj; + int len = 0; + + int err; + + input.count = 4; + input.pointer = params; + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(device_label_dsm_uuid); + params[0].buffer.pointer = (char *)device_label_dsm_uuid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = 0x02; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = func; + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; + + err = acpi_evaluate_object(handle, "_DSM", &input, output); + if (err) + return -1; + + obj = (union acpi_object *)output->pointer; + + switch (obj->type) { + case ACPI_TYPE_PACKAGE: + if (obj->package.count != 2) + break; + len = obj->package.elements[0].integer.value; + if (buf) { + if (attribute == ACPI_ATTR_INDEX_SHOW) + scnprintf(buf, PAGE_SIZE, "%llu\n", + obj->package.elements[0].integer.value); + else if (attribute == ACPI_ATTR_LABEL_SHOW) + dsm_label_utf16s_to_utf8s(obj, buf); + kfree(output->pointer); + return strlen(buf); + } + kfree(output->pointer); + return len; + break; + default: + kfree(output->pointer); + } + return -1; +} + +static bool +device_has_dsm(struct device *dev) +{ + acpi_handle handle; + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) + return FALSE; + + if (dsm_get_label(handle, DEVICE_LABEL_DSM, &output, NULL, + ACPI_ATTR_NONE) > 0) + return TRUE; + + return FALSE; +} + +static mode_t +acpi_index_string_exist(struct kobject *kobj, struct attribute *attr, int n) +{ + struct device *dev; + + dev = container_of(kobj, struct device, kobj); + + if (device_has_dsm(dev)) + return S_IRUGO; + + return 0; +} + +static ssize_t +acpilabel_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_handle handle; + int length; + + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) + return -1; + + length = dsm_get_label(handle, DEVICE_LABEL_DSM, + &output, buf, ACPI_ATTR_LABEL_SHOW); + + if (length < 1) + return -1; + + return length; +} + +static ssize_t +acpiindex_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_handle handle; + int length; + + handle = DEVICE_ACPI_HANDLE(dev); + + if (!handle) + return -1; + + length = dsm_get_label(handle, DEVICE_LABEL_DSM, + &output, buf, ACPI_ATTR_INDEX_SHOW); + + if (length < 0) + return -1; + + return length; + +} + +static struct device_attribute acpi_attr_label = { + .attr = {.name = "label", .mode = 0444}, + .show = acpilabel_show, +}; + +static struct device_attribute acpi_attr_index = { + .attr = {.name = "acpi_index", .mode = 0444}, + .show = acpiindex_show, +}; + +static struct attribute *acpi_attributes[] = { + &acpi_attr_label.attr, + &acpi_attr_index.attr, + NULL, +}; + +static struct attribute_group acpi_attr_group = { + .attrs = acpi_attributes, + .is_visible = acpi_index_string_exist, +}; + +static int +pci_create_acpi_index_label_files(struct pci_dev *pdev) +{ + return sysfs_create_group(&pdev->dev.kobj, &acpi_attr_group); +} + +static int +pci_remove_acpi_index_label_files(struct pci_dev *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &acpi_attr_group); + return 0; +} +#endif + void pci_create_firmware_label_files(struct pci_dev *pdev) { - if (!pci_create_smbiosname_file(pdev)) - ; + if (device_has_dsm(&pdev->dev)) + pci_create_acpi_index_label_files(pdev); + else + pci_create_smbiosname_file(pdev); } void pci_remove_firmware_label_files(struct pci_dev *pdev) { - pci_remove_smbiosname_file(pdev); + if (device_has_dsm(&pdev->dev)) + pci_remove_acpi_index_label_files(pdev); + else + pci_remove_smbiosname_file(pdev); } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f69d6e0fda75..a6ec200fe5ee 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -11,7 +11,7 @@ extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env); extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev); -#ifndef CONFIG_DMI +#if !defined(CONFIG_DMI) && !defined(CONFIG_ACPI) static inline void pci_create_firmware_label_files(struct pci_dev *pdev) { return; } static inline void pci_remove_firmware_label_files(struct pci_dev *pdev) From 47e9037ac16637cd7f12b8790ea7ce6680e42168 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 28 Feb 2011 16:20:11 +0000 Subject: [PATCH 09/16] PCI hotplug: acpiphp: set current_state to D0 in register_slot If a device doesn't support power management (pm_cap == 0) but it is acpi_pci_power_manageable() because there is a _PS0 method declared for it and _EJ0 is also declared for the slot then nobody is going to set current_state = PCI_D0 for this device. This is what I think it is happening: pci_enable_device | __pci_enable_device_flags /* here we do not set current_state because !pm_cap */ | do_pci_enable_device | pci_set_power_state | __pci_start_power_transition | pci_platform_power_transition /* platform_pci_power_manageable() calls acpi_pci_power_manageable that * returns true */ | platform_pci_set_power_state /* acpi_pci_set_power_state gets called and does nothing because the * acpi device has _EJ0, see the comment "If the ACPI device has _EJ0, * ignore the device" */ at this point if we refer to the commit message that introduced the comment above (10b3dcae0f275e2546e55303d64ddbb58cec7599), it is up to the hotplug driver to set the state to D0. However AFAICT the pci hotplug driver never does, in fact drivers/pci/hotplug/acpiphp_glue.c:register_slot sets the slot flags to (SLOT_ENABLED | SLOT_POWEREDON) but it does not set the pci device current state to PCI_D0. So my proposed fix is also to set current_state = PCI_D0 in register_slot. Comments are very welcome. Signed-off-by: Stefano Stabellini Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index cb23aa2ebf96..e610cfe4f07b 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -212,6 +212,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); if (pdev) { + pdev->current_state = PCI_D0; slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); pci_dev_put(pdev); } From 87e3dc3855430bd254370afc79f2ed92250f5b7c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:45:10 +0100 Subject: [PATCH 10/16] PCI: do not create quirk I/O regions below PCIBIOS_MIN_IO for ICH Some broken BIOSes on ICH4 chipset report an ACPI region which is in conflict with legacy IDE ports when ACPI is disabled. Even though the regions overlap, IDE ports are working correctly (we cannot find out the decoding rules on chipsets). So the only problem is the reported region itself, if we don't reserve the region in the quirk everything works as expected. This patch avoids reserving any quirk regions below PCIBIOS_MIN_IO which is 0x1000. Some regions might be (and are by a fast google query) below this border, but the only difference is that they won't be reserved anymore. They should still work though the same as before. The conflicts look like (1f.0 is bridge, 1f.1 is IDE ctrl): pci 0000:00:1f.1: address space collision: [io 0x0170-0x0177] conflicts with 0000:00:1f.0 [io 0x0100-0x017f] At 0x0100 a 128 bytes long ACPI region is reported in the quirk for ICH4. ata_piix then fails to find disks because the IDE legacy ports are zeroed: ata_piix 0000:00:1f.1: device not available (can't reserve [io 0x0000-0x0007]) References: https://bugzilla.novell.com/show_bug.cgi?id=558740 Signed-off-by: Jiri Slaby Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Thomas Renninger Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9e23912c97ac..bd80f6378463 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -554,18 +554,30 @@ static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) u32 region; u8 enable; + /* + * The check for PCIBIOS_MIN_IO is to ensure we won't create a conflict + * with low legacy (and fixed) ports. We don't know the decoding + * priority and can't tell whether the legacy device or the one created + * here is really at that address. This happens on boards with broken + * BIOSes. + */ + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); if (enable & ICH4_ACPI_EN) { pci_read_config_dword(dev, ICH_PMBASE, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH4 ACPI/GPIO/TCO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); } pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); if (enable & ICH4_GPIO_EN) { pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, - "ICH4 GPIO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 64, + PCI_BRIDGE_RESOURCES + 1, "ICH4 GPIO"); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); @@ -587,15 +599,19 @@ static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); if (enable & ICH6_ACPI_EN) { pci_read_config_dword(dev, ICH_PMBASE, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH6 ACPI/GPIO/TCO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); } pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); if (enable & ICH4_GPIO_EN) { pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, - "ICH6 GPIO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 64, + PCI_BRIDGE_RESOURCES + 1, "ICH6 GPIO"); } } From 13583b16592a9a85093dd9aa3d4e8cd76e4f0cd4 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 14 Feb 2011 17:43:17 -0800 Subject: [PATCH 11/16] PCI: refactor io size calculation code Refactor code that calculates the io size in pbus_size_io() and pbus_mem_io() into separate functions. Signed-off-by: Ram Pai Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 66 ++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 66cb8f4cc5f4..21212155eaba 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -404,6 +404,43 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon return NULL; } +static resource_size_t calculate_iosize(resource_size_t size, + resource_size_t min_size, + resource_size_t size1, + resource_size_t old_size, + resource_size_t align) +{ + if (size < min_size) + size = min_size; + if (old_size == 1 ) + old_size = 0; + /* To be fixed in 2.5: we should have sort of HAVE_ISA + flag in the struct pci_bus. */ +#if defined(CONFIG_ISA) || defined(CONFIG_EISA) + size = (size & 0xff) + ((size & ~0xffUL) << 2); +#endif + size = ALIGN(size + size1, align); + if (size < old_size) + size = old_size; + return size; +} + +static resource_size_t calculate_memsize(resource_size_t size, + resource_size_t min_size, + resource_size_t size1, + resource_size_t old_size, + resource_size_t align) +{ + if (size < min_size) + size = min_size; + if (old_size == 1 ) + old_size = 0; + if (size < old_size) + size = old_size; + size = ALIGN(size + size1, align); + return size; +} + /* Sizing the IO windows of the PCI-PCI bridge is trivial, since these windows have 4K granularity and the IO ranges of non-bridge PCI devices are limited to 256 bytes. @@ -412,7 +449,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); - unsigned long size = 0, size1 = 0, old_size; + unsigned long size = 0, size1 = 0; if (!b_res) return; @@ -435,19 +472,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) size1 += r_size; } } - if (size < min_size) - size = min_size; - old_size = resource_size(b_res); - if (old_size == 1) - old_size = 0; -/* To be fixed in 2.5: we should have sort of HAVE_ISA - flag in the struct pci_bus. */ -#if defined(CONFIG_ISA) || defined(CONFIG_EISA) - size = (size & 0xff) + ((size & ~0xffUL) << 2); -#endif - size = ALIGN(size + size1, 4096); - if (size < old_size) - size = old_size; + size = calculate_iosize(size, min_size, size1, + resource_size(b_res), 4096); if (!size) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " @@ -468,7 +494,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type, resource_size_t min_size) { struct pci_dev *dev; - resource_size_t min_align, align, size, old_size; + resource_size_t min_align, align, size; resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ int order, max_order; struct resource *b_res = find_free_bus_resource(bus, type); @@ -516,14 +542,6 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, mem64_mask &= r->flags & IORESOURCE_MEM_64; } } - if (size < min_size) - size = min_size; - old_size = resource_size(b_res); - if (old_size == 1) - old_size = 0; - if (size < old_size) - size = old_size; - align = 0; min_align = 0; for (order = 0; order <= max_order; order++) { @@ -537,7 +555,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, min_align = align1 >> 1; align += aligns[order]; } - size = ALIGN(size, min_align); + size = calculate_memsize(size, min_size, 0, resource_size(b_res), align); if (!size) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " From 094732a520caae81ae1532af29da82a4fa953472 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 14 Feb 2011 17:43:18 -0800 Subject: [PATCH 12/16] PCI: data structure agnostic free list function Replace free_failed_list() with a free_list() call. free_list() can handle 'resource_list_x', 'resource_list' and any linked list linked through ->next Signed-off-by: Ram Pai Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 21212155eaba..bcf5752f6907 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -36,6 +36,16 @@ struct resource_list_x { unsigned long flags; }; +#define free_list(type, head) do { \ + struct type *list, *tmp; \ + for (list = (head)->next; list;) { \ + tmp = list; \ + list = list->next; \ + kfree(tmp); \ + } \ + (head)->next = NULL; \ +} while (0) + static void add_to_failed_list(struct resource_list_x *head, struct pci_dev *dev, struct resource *res) { @@ -58,19 +68,6 @@ static void add_to_failed_list(struct resource_list_x *head, list->next = tmp; } -static void free_failed_list(struct resource_list_x *head) -{ - struct resource_list_x *list, *tmp; - - for (list = head->next; list;) { - tmp = list; - list = list->next; - kfree(tmp); - } - - head->next = NULL; -} - static void __dev_sort_resources(struct pci_dev *dev, struct resource_list *head) { @@ -900,7 +897,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) if (tried_times >= 2) { /* still fail, don't need to try more */ - free_failed_list(&head); + free_list(resource_list_x, &head); goto enable_all; } @@ -931,7 +928,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) list = list->next; } - free_failed_list(&head); + free_list(resource_list_x, &head); goto again; From fc075e1da1b96ef65c32e83648055606b8204b58 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 14 Feb 2011 17:43:19 -0800 Subject: [PATCH 13/16] PCI: introduce reset_resource() Introduce reset_resource() which factors out resource reset logic. Signed-off-by: Ram Pai Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index bcf5752f6907..a94ecc161208 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -88,6 +88,13 @@ static void __dev_sort_resources(struct pci_dev *dev, pdev_sort_resources(dev, head); } +static inline void reset_resource(struct resource *res) +{ + res->start = 0; + res->end = 0; + res->flags = 0; +} + static void __assign_resources_sorted(struct resource_list *head, struct resource_list_x *fail_head) { @@ -109,9 +116,7 @@ static void __assign_resources_sorted(struct resource_list *head, (!(res->flags & IORESOURCE_ROM_ENABLE)))) add_to_failed_list(fail_head, list->dev, res); } - res->start = 0; - res->end = 0; - res->flags = 0; + reset_resource(res); } tmp = list; list = list->next; From c8adf9a3e873eddaaec11ac410a99ef6b9656938 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 14 Feb 2011 17:43:20 -0800 Subject: [PATCH 14/16] PCI: pre-allocate additional resources to devices only after successful allocation of essential resources. Linux tries to pre-allocate minimal resources to hotplug bridges. This works fine as long as there are enough resources to satisfy all other genuine resource requirements. However if enough resources are not available to satisfy any of these nice-to-have pre-allocations, the resource-allocator reports errors and returns failure. This patch distinguishes between must-have resource from nice-to-have resource. Any failure to allocate nice-to-have resources are ignored. This behavior can be particularly useful to trigger automatic reallocation when the OS discovers genuine allocation-conflicts or genuine unallocated-requests caused by buggy allocation behavior of the native BIOS/uEFI. https://bugzilla.kernel.org/show_bug.cgi?id=15960 captures the movitation behind the patch. This patch is verified to resolve the above bug. changelog v2: o fixed a bug where pci_assign_resource() was called on a resource of zero resource size. changelog v3: addressed Bjorn's comment o "Please don't indent and right-justify the changelog". o removed add_size from struct resource. The additional size is now tracked using a linked list. changelog v4: o moved freeing up of elements in head list from assign_requested_resources_sorted() to __assign_resources_sorted(). o removed a wrong reference to 'add_size' in pbus_size_mem(). o some code optimizations in adjust_resources_sorted() and assign_requested_resources_sorted() changelog v5: o moved freeing up of elements in head list from assign_requested_resources_sorted() to __assign_resources_sorted(). o removed a wrong reference to 'add_size' in pbus_size_mem(). o some code optimizations in adjust_resources_sorted() and assign_requested_resources_sorted() changelog v5: o factored out common code and made them into separate independent patches o added comments in kdoc format o added a BUG_ON in pci_assign_unassigned_resources() to catch for memory leak. Signed-off-by: Ram Pai Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 231 ++++++++++++++++++++++++++++++++-------- 1 file changed, 184 insertions(+), 47 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index a94ecc161208..89d0a6a88df7 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -33,6 +33,7 @@ struct resource_list_x { struct pci_dev *dev; resource_size_t start; resource_size_t end; + resource_size_t add_size; unsigned long flags; }; @@ -46,8 +47,18 @@ struct resource_list_x { (head)->next = NULL; \ } while (0) -static void add_to_failed_list(struct resource_list_x *head, - struct pci_dev *dev, struct resource *res) +/** + * add_to_list() - add a new resource tracker to the list + * @head: Head of the list + * @dev: device corresponding to which the resource + * belongs + * @res: The resource to be tracked + * @add_size: additional size to be optionally added + * to the resource + */ +static void add_to_list(struct resource_list_x *head, + struct pci_dev *dev, struct resource *res, + resource_size_t add_size) { struct resource_list_x *list = head; struct resource_list_x *ln = list->next; @@ -55,7 +66,7 @@ static void add_to_failed_list(struct resource_list_x *head, tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) { - pr_warning("add_to_failed_list: kmalloc() failed!\n"); + pr_warning("add_to_list: kmalloc() failed!\n"); return; } @@ -65,9 +76,16 @@ static void add_to_failed_list(struct resource_list_x *head, tmp->start = res->start; tmp->end = res->end; tmp->flags = res->flags; + tmp->add_size = add_size; list->next = tmp; } +static void add_to_failed_list(struct resource_list_x *head, + struct pci_dev *dev, struct resource *res) +{ + add_to_list(head, dev, res, 0); +} + static void __dev_sort_resources(struct pci_dev *dev, struct resource_list *head) { @@ -95,18 +113,81 @@ static inline void reset_resource(struct resource *res) res->flags = 0; } -static void __assign_resources_sorted(struct resource_list *head, +/** + * adjust_resources_sorted() - satisfy any additional resource requests + * + * @add_head : head of the list tracking requests requiring additional + * resources + * @head : head of the list tracking requests with allocated + * resources + * + * Walk through each element of the add_head and try to procure + * additional resources for the element, provided the element + * is in the head list. + */ +static void adjust_resources_sorted(struct resource_list_x *add_head, + struct resource_list *head) +{ + struct resource *res; + struct resource_list_x *list, *tmp, *prev; + struct resource_list *hlist; + resource_size_t add_size; + int idx; + + prev = add_head; + for (list = add_head->next; list;) { + res = list->res; + /* skip resource that has been reset */ + if (!res->flags) + goto out; + + /* skip this resource if not found in head list */ + for (hlist = head->next; hlist && hlist->res != res; + hlist = hlist->next); + if (!hlist) { /* just skip */ + prev = list; + list = list->next; + continue; + } + + idx = res - &list->dev->resource[0]; + add_size=list->add_size; + if (!resource_size(res) && add_size) { + res->end = res->start + add_size - 1; + if(pci_assign_resource(list->dev, idx)) + reset_resource(res); + } else if (add_size) { + adjust_resource(res, res->start, + resource_size(res) + add_size); + } +out: + tmp = list; + prev->next = list = list->next; + kfree(tmp); + } +} + +/** + * assign_requested_resources_sorted() - satisfy resource requests + * + * @head : head of the list tracking requests for resources + * @failed_list : head of the list tracking requests that could + * not be allocated + * + * Satisfy resource requests of each element in the list. Add + * requests that could not satisfied to the failed_list. + */ +static void assign_requested_resources_sorted(struct resource_list *head, struct resource_list_x *fail_head) { struct resource *res; - struct resource_list *list, *tmp; + struct resource_list *list; int idx; - for (list = head->next; list;) { + for (list = head->next; list; list = list->next) { res = list->res; idx = res - &list->dev->resource[0]; - - if (pci_assign_resource(list->dev, idx)) { + if (resource_size(res) && pci_assign_resource(list->dev, idx)) { if (fail_head && !pci_is_root_bus(list->dev->bus)) { /* * if the failed res is for ROM BAR, and it will @@ -118,12 +199,23 @@ static void __assign_resources_sorted(struct resource_list *head, } reset_resource(res); } - tmp = list; - list = list->next; - kfree(tmp); } } +static void __assign_resources_sorted(struct resource_list *head, + struct resource_list_x *add_head, + struct resource_list_x *fail_head) +{ + /* Satisfy the must-have resource requests */ + assign_requested_resources_sorted(head, fail_head); + + /* Try to satisfy any additional nice-to-have resource + requests */ + if (add_head) + adjust_resources_sorted(add_head, head); + free_list(resource_list, head); +} + static void pdev_assign_resources_sorted(struct pci_dev *dev, struct resource_list_x *fail_head) { @@ -131,11 +223,12 @@ static void pdev_assign_resources_sorted(struct pci_dev *dev, head.next = NULL; __dev_sort_resources(dev, &head); - __assign_resources_sorted(&head, fail_head); + __assign_resources_sorted(&head, NULL, fail_head); } static void pbus_assign_resources_sorted(const struct pci_bus *bus, + struct resource_list_x *add_head, struct resource_list_x *fail_head) { struct pci_dev *dev; @@ -145,7 +238,7 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, list_for_each_entry(dev, &bus->devices, bus_list) __dev_sort_resources(dev, &head); - __assign_resources_sorted(&head, fail_head); + __assign_resources_sorted(&head, add_head, fail_head); } void pci_setup_cardbus(struct pci_bus *bus) @@ -443,15 +536,25 @@ static resource_size_t calculate_memsize(resource_size_t size, return size; } -/* Sizing the IO windows of the PCI-PCI bridge is trivial, - since these windows have 4K granularity and the IO ranges - of non-bridge PCI devices are limited to 256 bytes. - We must be careful with the ISA aliasing though. */ -static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) +/** + * pbus_size_io() - size the io window of a given bus + * + * @bus : the bus + * @min_size : the minimum io window that must to be allocated + * @add_size : additional optional io window + * @add_head : track the additional io window on this list + * + * Sizing the IO windows of the PCI-PCI bridge is trivial, + * since these windows have 4K granularity and the IO ranges + * of non-bridge PCI devices are limited to 256 bytes. + * We must be careful with the ISA aliasing though. + */ +static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, + resource_size_t add_size, struct resource_list_x *add_head) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); - unsigned long size = 0, size1 = 0; + unsigned long size = 0, size0 = 0, size1 = 0; if (!b_res) return; @@ -474,9 +577,12 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) size1 += r_size; } } - size = calculate_iosize(size, min_size, size1, + size0 = calculate_iosize(size, min_size, size1, resource_size(b_res), 4096); - if (!size) { + size1 = !add_size? size0: + calculate_iosize(size, min_size+add_size, size1, + resource_size(b_res), 4096); + if (!size0 && !size1) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " "%pR to [bus %02x-%02x] (unused)\n", b_res, @@ -486,17 +592,30 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) } /* Alignment of the IO window is always 4K */ b_res->start = 4096; - b_res->end = b_res->start + size - 1; + b_res->end = b_res->start + size0 - 1; b_res->flags |= IORESOURCE_STARTALIGN; + if (size1 > size0 && add_head) + add_to_list(add_head, bus->self, b_res, size1-size0); } -/* Calculate the size of the bus and minimal alignment which - guarantees that all child resources fit in this size. */ +/** + * pbus_size_mem() - size the memory window of a given bus + * + * @bus : the bus + * @min_size : the minimum memory window that must to be allocated + * @add_size : additional optional memory window + * @add_head : track the additional memory window on this list + * + * Calculate the size of the bus and minimal alignment which + * guarantees that all child resources fit in this size. + */ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, - unsigned long type, resource_size_t min_size) + unsigned long type, resource_size_t min_size, + resource_size_t add_size, + struct resource_list_x *add_head) { struct pci_dev *dev; - resource_size_t min_align, align, size; + resource_size_t min_align, align, size, size0, size1; resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ int order, max_order; struct resource *b_res = find_free_bus_resource(bus, type); @@ -557,8 +676,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, min_align = align1 >> 1; align += aligns[order]; } - size = calculate_memsize(size, min_size, 0, resource_size(b_res), align); - if (!size) { + size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), align); + size1 = !add_size ? size : + calculate_memsize(size, min_size+add_size, 0, + resource_size(b_res), align); + if (!size0 && !size1) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " "%pR to [bus %02x-%02x] (unused)\n", b_res, @@ -567,9 +689,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, return 1; } b_res->start = min_align; - b_res->end = size + min_align - 1; - b_res->flags |= IORESOURCE_STARTALIGN; - b_res->flags |= mem64_mask; + b_res->end = size0 + min_align - 1; + b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask; + if (size1 > size0 && add_head) + add_to_list(add_head, bus->self, b_res, size1-size0); return 1; } @@ -622,11 +745,12 @@ static void pci_bus_size_cardbus(struct pci_bus *bus) } } -void __ref pci_bus_size_bridges(struct pci_bus *bus) +void __ref __pci_bus_size_bridges(struct pci_bus *bus, + struct resource_list_x *add_head) { struct pci_dev *dev; unsigned long mask, prefmask; - resource_size_t min_mem_size = 0, min_io_size = 0; + resource_size_t additional_mem_size = 0, additional_io_size = 0; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -640,7 +764,7 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) case PCI_CLASS_BRIDGE_PCI: default: - pci_bus_size_bridges(b); + __pci_bus_size_bridges(b, add_head); break; } } @@ -657,11 +781,14 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) case PCI_CLASS_BRIDGE_PCI: pci_bridge_check_ranges(bus); if (bus->self->is_hotplug_bridge) { - min_io_size = pci_hotplug_io_size; - min_mem_size = pci_hotplug_mem_size; + additional_io_size = pci_hotplug_io_size; + additional_mem_size = pci_hotplug_mem_size; } + /* + * Follow thru + */ default: - pbus_size_io(bus, min_io_size); + pbus_size_io(bus, 0, additional_io_size, add_head); /* If the bridge supports prefetchable range, size it separately. If it doesn't, or its prefetchable window has already been allocated by arch code, try @@ -669,30 +796,36 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) resources. */ mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, prefmask, prefmask, min_mem_size)) + if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, add_head)) mask = prefmask; /* Success, size non-prefetch only. */ else - min_mem_size += min_mem_size; - pbus_size_mem(bus, mask, IORESOURCE_MEM, min_mem_size); + additional_mem_size += additional_mem_size; + pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, add_head); break; } } + +void __ref pci_bus_size_bridges(struct pci_bus *bus) +{ + __pci_bus_size_bridges(bus, NULL); +} EXPORT_SYMBOL(pci_bus_size_bridges); static void __ref __pci_bus_assign_resources(const struct pci_bus *bus, + struct resource_list_x *add_head, struct resource_list_x *fail_head) { struct pci_bus *b; struct pci_dev *dev; - pbus_assign_resources_sorted(bus, fail_head); + pbus_assign_resources_sorted(bus, add_head, fail_head); list_for_each_entry(dev, &bus->devices, bus_list) { b = dev->subordinate; if (!b) continue; - __pci_bus_assign_resources(b, fail_head); + __pci_bus_assign_resources(b, add_head, fail_head); switch (dev->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -714,7 +847,7 @@ static void __ref __pci_bus_assign_resources(const struct pci_bus *bus, void __ref pci_bus_assign_resources(const struct pci_bus *bus) { - __pci_bus_assign_resources(bus, NULL); + __pci_bus_assign_resources(bus, NULL, NULL); } EXPORT_SYMBOL(pci_bus_assign_resources); @@ -729,7 +862,7 @@ static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge, if (!b) return; - __pci_bus_assign_resources(b, fail_head); + __pci_bus_assign_resources(b, NULL, fail_head); switch (bridge->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -862,17 +995,21 @@ void __init pci_assign_unassigned_resources(void) { struct pci_bus *bus; - + struct resource_list_x add_list; /* list of resources that + want additional resources */ + add_list.next = NULL; /* Depth first, calculate sizes and alignments of all subordinate buses. */ list_for_each_entry(bus, &pci_root_buses, node) { - pci_bus_size_bridges(bus); + __pci_bus_size_bridges(bus, &add_list); } + /* Depth last, allocate resources and update the hardware. */ list_for_each_entry(bus, &pci_root_buses, node) { - pci_bus_assign_resources(bus); + __pci_bus_assign_resources(bus, &add_list, NULL); pci_enable_bridges(bus); } + BUG_ON(add_list.next); /* dump the resource on buses */ list_for_each_entry(bus, &pci_root_buses, node) { From 07eefe1ca542129c24833c96ceb83b9aaa3b458d Mon Sep 17 00:00:00 2001 From: "Narendra_K@Dell.com" Date: Mon, 7 Mar 2011 12:55:56 -0800 Subject: [PATCH 15/16] PCI: label: Fix compilation error when CONFIG_ACPI is unset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes compilation error descibed below introduced by the commit 6058989bad05b82e78baacce69ec14f27a11b5fd drivers/pci/pci-label.c: In function ‘pci_create_firmware_label_files’: drivers/pci/pci-label.c:366:2: error: implicit declaration of function ‘device_has_dsm’ Signed-off-by: Narendra K Signed-off-by: Jesse Barnes --- drivers/pci/pci-label.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 824e247edc58..8c80138bb66f 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -174,6 +174,12 @@ pci_remove_acpi_index_label_files(struct pci_dev *pdev) return -1; } +static inline bool +device_has_dsm(struct device *dev) +{ + return false; +} + #else static const char device_label_dsm_uuid[] = { From 65d8defe2e13fbebd74f96d2b5ca9aad435e6648 Mon Sep 17 00:00:00 2001 From: "Shyam_Iyer@Dell.com" Date: Fri, 11 Mar 2011 03:35:56 +0530 Subject: [PATCH 16/16] PCI: label: remove #include of ACPI header to avoid warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I found that including acpi/apci_drivers.h is not necessary and introduces these warnings: In file included from drivers/pci/pci-label.c:32: include/acpi/acpi_drivers.h:103: warning: ‘struct acpi_device’ declared inside parameter list include/acpi/acpi_drivers.h:103: warning: its scope is only this definition or declaration, which is probably not what you want include/acpi/acpi_drivers.h:107: warning: ‘struct acpi_pci_root’ declared inside parameter list Signed-off-by: Shyam Iyer Signed-off-by: Jesse Barnes --- drivers/pci/pci-label.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 8c80138bb66f..77cb2a14c896 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "pci.h"