From dfc73e7acd9925b434a355eeeed86d44cb435f9c Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 17 Apr 2014 19:46:15 +0200 Subject: PCI: Move Open Firmware devspec attribute to PCI common code Move the devspec OF attribute to PCI common code's set of device attributes since it's not architecture dependent. As a side effect microblaze and powerpc no longer need to use pcibios_add_platform_entries(). [bhelgaas: fold in #include for compile error] Link: https://lkml.kernel.org/r/alpine.LFD.2.11.1404141101500.1529@denkbrett Signed-off-by: Sebastian Ott Signed-off-by: Bjorn Helgaas Acked-by: Benjamin Herrenschmidt --- drivers/pci/pci-sysfs.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 4e0acefb7565..3db1c7ff5dd3 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -416,6 +417,20 @@ static ssize_t d3cold_allowed_show(struct device *dev, static DEVICE_ATTR_RW(d3cold_allowed); #endif +#ifdef CONFIG_OF +static ssize_t devspec_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct device_node *np = pci_device_to_OF_node(pdev); + + if (np == NULL || np->full_name == NULL) + return 0; + return sprintf(buf, "%s", np->full_name); +} +static DEVICE_ATTR_RO(devspec); +#endif + #ifdef CONFIG_PCI_IOV static ssize_t sriov_totalvfs_show(struct device *dev, struct device_attribute *attr, @@ -520,6 +535,9 @@ static struct attribute *pci_dev_attrs[] = { &dev_attr_msi_bus.attr, #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) &dev_attr_d3cold_allowed.attr, +#endif +#ifdef CONFIG_OF + &dev_attr_devspec.attr, #endif NULL, }; -- cgit v1.2.1 From 034cd97ebda4062eb4402a6cf963ccd262caa86a Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 14 Apr 2014 15:28:35 +0200 Subject: PCI/MSI: Remove pci_enable_msi_block() There are no users of pci_enable_msi_block() function left. Obsolete it in favor of pci_enable_msi_range() and pci_enable_msi_exact() functions. Previously, we called arch_setup_msi_irqs() once, requesting the same vector count we passed to arch_msi_check_device(). Now we may call it several times: if it returns failure, we may retry and request fewer vectors. We don't keep track of the vector count we initially passed to arch_msi_check_device(). We only keep track of the number of vectors successfully set up by arch_setup_msi_irqs(), and this is what we use to clean things up when disabling MSI. Therefore, we assume that arch_msi_check_device() does nothing that will have to be cleaned up later. [bhelgaas: changelog] Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 79 +++++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 46 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 04130c3f9cf6..36dd0caa1759 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -879,50 +879,6 @@ int pci_msi_vec_count(struct pci_dev *dev) } EXPORT_SYMBOL(pci_msi_vec_count); -/** - * pci_enable_msi_block - configure device's MSI capability structure - * @dev: device to configure - * @nvec: number of interrupts to configure - * - * Allocate IRQs for a device with the MSI capability. - * This function returns a negative errno if an error occurs. If it - * is unable to allocate the number of interrupts requested, it returns - * the number of interrupts it might be able to allocate. If it successfully - * allocates at least the number of interrupts requested, it returns 0 and - * updates the @dev's irq member to the lowest new interrupt number; the - * other interrupt numbers allocated to this device are consecutive. - */ -int pci_enable_msi_block(struct pci_dev *dev, int nvec) -{ - int status, maxvec; - - if (dev->current_state != PCI_D0) - return -EINVAL; - - maxvec = pci_msi_vec_count(dev); - if (maxvec < 0) - return maxvec; - if (nvec > maxvec) - return maxvec; - - status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); - if (status) - return status; - - WARN_ON(!!dev->msi_enabled); - - /* Check whether driver already requested MSI-X irqs */ - if (dev->msix_enabled) { - dev_info(&dev->dev, "can't enable MSI " - "(MSI-X already enabled)\n"); - return -EINVAL; - } - - status = msi_capability_init(dev, nvec); - return status; -} -EXPORT_SYMBOL(pci_enable_msi_block); - void pci_msi_shutdown(struct pci_dev *dev) { struct msi_desc *desc; @@ -1128,14 +1084,45 @@ void pci_msi_init_pci_dev(struct pci_dev *dev) **/ int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) { - int nvec = maxvec; + int nvec; int rc; + if (dev->current_state != PCI_D0) + return -EINVAL; + + WARN_ON(!!dev->msi_enabled); + + /* Check whether driver already requested MSI-X irqs */ + if (dev->msix_enabled) { + dev_info(&dev->dev, + "can't enable MSI (MSI-X already enabled)\n"); + return -EINVAL; + } + if (maxvec < minvec) return -ERANGE; + nvec = pci_msi_vec_count(dev); + if (nvec < 0) + return nvec; + else if (nvec < minvec) + return -EINVAL; + else if (nvec > maxvec) + nvec = maxvec; + + do { + rc = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); + if (rc < 0) { + return rc; + } else if (rc > 0) { + if (rc < minvec) + return -ENOSPC; + nvec = rc; + } + } while (rc); + do { - rc = pci_enable_msi_block(dev, nvec); + rc = msi_capability_init(dev, nvec); if (rc < 0) { return rc; } else if (rc > 0) { -- cgit v1.2.1 From 3cb30b73ad71b384c6289243d4ccd31ab90bce6f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 1 May 2014 14:36:31 -0600 Subject: PCI: Mark RTL8110SC INTx masking as broken INTx masking does not work on this device. To see this, configure the network device UP on an active network, note that the interrupt count continues to increment for the device in /proc/interrupts. Use setpci to set the PCI_COMMAND_INTX_DISABLE bit in the PCI_COMMAND register. As expected, the interrupt count ceases to increment. However, reading the PCI_STATUS_INTERRUPT bit of the PCI_STATUS register does not indicate that interrupts are pending and clearing PCI_COMMAND_INTX_DISABLE in the PCI_COMMAND register does not allow the device to continue operation. This does not affect operation of the host r8169 driver, but it does prevent the device from being functional when assigned to a VM, such as with QEMU and VFIO. The guest driver successfully probes the device, but there is no traffic. Mark INTx masking as broken, allowing the more restrictive APIC masking to be used instead. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5e4ac635368f..72faccfb107c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2991,6 +2991,14 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030, quirk_broken_intx_masking); DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */ quirk_broken_intx_masking); +/* + * Realtek RTL8169 PCI Gigabit Ethernet Controller (rev 10) + * Subsystem: Realtek RTL8169/8110 Family PCI Gigabit Ethernet NIC + * + * RTL8110SC - Fails under PCI device assignment using DisINTx masking. + */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169, + quirk_broken_intx_masking); static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) -- cgit v1.2.1 From 9edbcd2252b5ef148177c9f2c11a56469cf5db52 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 17 Apr 2014 19:48:07 +0200 Subject: PCI: Remove pcibios_add_platform_entries() Remove pcibios_add_platform_entries(). Architecture-specific attributes can be achieved by setting pdev->dev.groups. Link: https://lkml.kernel.org/r/alpine.LFD.2.11.1404141101500.1529@denkbrett Signed-off-by: Sebastian Ott Signed-off-by: Bjorn Helgaas Acked-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 3db1c7ff5dd3..b7333fa5f80d 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1273,11 +1273,6 @@ static struct bin_attribute pcie_config_attr = { .write = pci_write_config, }; -int __weak pcibios_add_platform_entries(struct pci_dev *dev) -{ - return 0; -} - static ssize_t reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1393,11 +1388,6 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) pdev->rom_attr = attr; } - /* add platform-specific attributes */ - retval = pcibios_add_platform_entries(pdev); - if (retval) - goto err_rom_file; - /* add sysfs entries for various capabilities */ retval = pci_create_capabilities_sysfs(pdev); if (retval) -- cgit v1.2.1 From 23b13bc76f359e99140baf083dc44314f4eb1b87 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Apr 2014 15:25:54 -0600 Subject: PCI: Fail safely if we can't handle BARs larger than 4GB We can only handle BARs larger than 4GB if both dma_addr_t and resource_size_t are 64 bits wide. If dma_addr_t is 32 bits, we can't represent all the bus addresses, and if resource_size_t is 32 bits, we can't represent all the CPU addresses. Previously we cleared res->flags (at "fail:") for resources that were too large. That means we think the BAR doesn't exist at all, which in turn means that we could enable the device even though we can't keep track of where the BAR is and we can't make sure it doesn't overlap something else. This preserves the type flags (MEM/IO) so we can keep from enabling the device. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ef09f5f2fe6c..c7f8b717c2e7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -171,6 +171,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int pos) { u32 l, sz, mask; + u64 l64, sz64, mask64; u16 orig_cmd; struct pci_bus_region region, inverted_region; bool bar_too_big = false, bar_disabled = false; @@ -226,9 +227,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, } if (res->flags & IORESOURCE_MEM_64) { - u64 l64 = l; - u64 sz64 = sz; - u64 mask64 = mask | (u64)~0 << 32; + l64 = l; + sz64 = sz; + mask64 = mask | (u64)~0 << 32; pci_read_config_dword(dev, pos + 4, &l); pci_write_config_dword(dev, pos + 4, ~0); @@ -243,9 +244,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if (!sz64) goto fail; - if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) { + if ((sizeof(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) && + sz64 > 0x100000000ULL) { + res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; + res->start = 0; + res->end = 0; bar_too_big = true; - goto fail; + goto out; } if ((sizeof(resource_size_t) < 8) && l) { @@ -303,7 +308,8 @@ out: pci_write_config_word(dev, PCI_COMMAND, orig_cmd); if (bar_too_big) - dev_err(&dev->dev, "reg 0x%x: can't handle 64-bit BAR\n", pos); + dev_err(&dev->dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n", + pos, (unsigned long long) sz64); if (res->flags && !bar_disabled) dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res); -- cgit v1.2.1 From d1a313e4b6ccbb61c746ee10ac198970516e9afc Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 29 Apr 2014 18:33:09 -0600 Subject: PCI: Reject BAR above 4GB if dma_addr_t is too small We can only handle BARs above 4GB if dma_addr_t (not resource_size_t) is 64 bits wide. If we have a 64-bit resource_size_t and a 32-bit dma_addr_t, we can't deal with BARs above 4GB. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index c7f8b717c2e7..afae3bf405fa 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -253,7 +253,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, goto out; } - if ((sizeof(resource_size_t) < 8) && l) { + if ((sizeof(dma_addr_t) < 8) && l) { /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); -- cgit v1.2.1 From 72dc5601fe5fec37cc1bd0efb19d99948fe7e54c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 29 Apr 2014 18:42:49 -0600 Subject: PCI: Don't convert BAR address to resource if dma_addr_t is too small If dma_addr_t is too small to represent the BAR value, pcibios_bus_to_resource() will fail, so just remember the BAR size directly in the resource. The resource is already marked UNSET, so we know the address isn't valid anyway. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index afae3bf405fa..82cd75f6118a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -258,9 +258,10 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); res->flags |= IORESOURCE_UNSET; - region.start = 0; - region.end = sz64; + res->start = 0; + res->end = sz64; bar_disabled = true; + goto out; } else { region.start = l64; region.end = l64 + sz64; -- cgit v1.2.1 From 31e9dd2565a6e27a3e698d7e3adf929db8d6c767 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 29 Apr 2014 18:37:47 -0600 Subject: PCI: Don't set BAR to zero if dma_addr_t is too small If a BAR is above 4GB and our dma_addr_t is too small, don't clear the BAR to zero: that doesn't disable the BAR, and it makes it more likely that the BAR will conflict with things if we turn on the memory enable bit (as we will at "out:" if the device was already enabled at the handoff). We should also print the BAR info and its original size so we can follow the process when we try to assign space to it. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 82cd75f6118a..dd710b12d34c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -174,7 +174,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, u64 l64, sz64, mask64; u16 orig_cmd; struct pci_bus_region region, inverted_region; - bool bar_too_big = false, bar_disabled = false; + bool bar_too_big = false, bar_too_high = false; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -254,13 +254,11 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, } if ((sizeof(dma_addr_t) < 8) && l) { - /* Address above 32-bit boundary; disable the BAR */ - pci_write_config_dword(dev, pos, 0); - pci_write_config_dword(dev, pos + 4, 0); + /* Above 32-bit boundary; try to reallocate */ res->flags |= IORESOURCE_UNSET; res->start = 0; res->end = sz64; - bar_disabled = true; + bar_too_high = true; goto out; } else { region.start = l64; @@ -311,7 +309,10 @@ out: if (bar_too_big) dev_err(&dev->dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n", pos, (unsigned long long) sz64); - if (res->flags && !bar_disabled) + if (bar_too_high) + dev_info(&dev->dev, "reg 0x%x: can't handle BAR above 4G (bus address %#010llx)\n", + pos, (unsigned long long) l64); + if (res->flags) dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res); return (res->flags & IORESOURCE_MEM_64) ? 1 : 0; -- cgit v1.2.1 From 26370fc6647b63eefb85a675382d661d0fed30a1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Apr 2014 15:26:50 -0600 Subject: PCI: Don't print anything while decoding is disabled If the console is a PCI device, and we try to print to it while its decoding is disabled, the system will hang. This particular printk hasn't caused a problem yet, but it could, so this fixes it. See also 0ff9514b579b ("PCI: Don't print anything while decoding is disabled"). Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dd710b12d34c..3bc149b848a8 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -174,7 +174,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, u64 l64, sz64, mask64; u16 orig_cmd; struct pci_bus_region region, inverted_region; - bool bar_too_big = false, bar_too_high = false; + bool bar_too_big = false, bar_too_high = false, bar_invalid = false; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -289,11 +289,10 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, * be claimed by the device. */ if (inverted_region.start != region.start) { - dev_info(&dev->dev, "reg 0x%x: initial BAR value %pa invalid; forcing reassignment\n", - pos, ®ion.start); res->flags |= IORESOURCE_UNSET; - res->end -= res->start; res->start = 0; + res->end = region.end - region.start; + bar_invalid = true; } goto out; @@ -312,6 +311,9 @@ out: if (bar_too_high) dev_info(&dev->dev, "reg 0x%x: can't handle BAR above 4G (bus address %#010llx)\n", pos, (unsigned long long) l64); + if (bar_invalid) + dev_info(&dev->dev, "reg 0x%x: initial BAR value %#010llx invalid\n", + pos, (unsigned long long) region.start); if (res->flags) dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res); -- cgit v1.2.1 From d739a099d0248c78d374b1b610cdb679c7bc052d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Apr 2014 16:10:54 -0600 Subject: PCI: Don't add disabled subtractive decode bus resources For a subtractive decode bridge, we previously added and printed all resources of the primary bus, even if they were not valid. In the example below, the bridge 00:1c.3 has no windows enabled, so there are no valid resources on bus 02. But since 02:00.0 is subtractive decode bridge, we add and print all those invalid resources, which don't really make sense: pci 0000:00:1c.3: PCI bridge to [bus 02-03] pci 0000:02:00.0: PCI bridge to [bus 03] (subtractive decode) pci 0000:02:00.0: bridge window [??? 0x00000000 flags 0x0] (subtractive decode) Add and print the subtractively-decoded resources only if they are valid. There's an example in the dmesg log attached to the bugzilla below (but this patch doesn't fix the bug reported there). Link: https://bugzilla.kernel.org/show_bug.cgi?id=73141 Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3bc149b848a8..966514010974 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -475,7 +475,7 @@ void pci_read_bridge_bases(struct pci_bus *child) if (dev->transparent) { pci_bus_for_each_resource(child->parent, res, i) { - if (res) { + if (res && res->flags) { pci_bus_add_resource(child, res, PCI_SUBTRACTIVE_DECODE); dev_printk(KERN_DEBUG, &dev->dev, -- cgit v1.2.1 From 14c8530dbc1b7cd5020c44b391e34bdb731fd098 Mon Sep 17 00:00:00 2001 From: Alan Date: Mon, 19 May 2014 14:03:14 +0100 Subject: PCI: Support BAR sizes up to 8GB This is needed for some of the Xeon Phi type systems. [bhelgaas: added Nikhil, use ARRAY_SIZE() to connect with decl, folded in Kevin's "order < 0" fix to ARRAY_SIZE() usage] Signed-off-by: Nikhil P Rao Signed-off-by: Alan Cox Signed-off-by: Kevin Hilman Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 138bdd6393be..9b3498c9b739 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -921,7 +921,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, { struct pci_dev *dev; resource_size_t min_align, align, size, size0, size1; - resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ + resource_size_t aligns[14]; /* Alignments from 1Mb to 8Gb */ int order, max_order; struct resource *b_res = find_free_bus_resource(bus, type); unsigned int mem64_mask = 0; @@ -957,10 +957,17 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, continue; } #endif - /* For bridges size != alignment */ + /* + * aligns[0] is for 1MB (since bridge memory + * windows are always at least 1MB aligned), so + * keep "order" from being negative for smaller + * resources. + */ align = pci_resource_alignment(dev, r); order = __ffs(align) - 20; - if (order > 11) { + if (order < 0) + order = 0; + if (order >= ARRAY_SIZE(aligns)) { dev_warn(&dev->dev, "disabling BAR %d: %pR " "(bad alignment %#llx)\n", i, r, (unsigned long long) align); @@ -968,8 +975,6 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, continue; } size += r_size; - if (order < 0) - order = 0; /* Exclude ranges with size > align from calculation of the alignment. */ if (r_size == align) -- cgit v1.2.1 From 5b28541552ef5eeffc41d6936105f38c2508e566 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 19 May 2014 17:01:55 -0600 Subject: PCI: Restrict 64-bit prefetchable bridge windows to 64-bit resources This patch changes the way we handle 64-bit prefetchable bridge windows to make it more likely that we can assign space to all devices. Previously we put all prefetchable resources in the prefetchable bridge window. If any of those resources was 32-bit only, we restricted the window to be below 4GB. After this patch, we only put 64-bit prefetchable resources in a 64-bit prefetchable window. We put all 32-bit prefetchable resources in the non-prefetchable window, even if there are no 64-bit prefetchable resources. With the previous approach, if there was a 32-bit prefetchable resource behind a bridge, we forced the bridge's prefetchable window below 4GB, which meant that even if there was plenty of space above 4GB available, we couldn't use it, and assignment of large 64-bit resources could fail, as in the bugzilla below. The new strategy is: 1) If the prefetchable window is 64 bits wide, we put only 64-bit prefetchable resources in it. Any 32-bit prefetchable resources go in the non-prefetchable window. 2) If the prefetchable window is 32 bits wide, we put both 32- and 64-bit prefetchable resources in it. 3) If there is no prefetchable window, all MMIO resources go in the non-prefetchable window. This reduces performance for 32-bit prefetchable resources below a bridge with a 64-bit prefetchable window. We previously assigned prefetchable space, but now we'll assign non-prefetchable space. This is the case even if there are no 64-bit prefetchable resources, or if they would all fit below 4GB. In those cases, the old strategy would work and would have better performance. [bhelgaas: write changelog, add bugzilla link, fold in mem64_mask removal] Link: https://bugzilla.kernel.org/show_bug.cgi?id=74151 Tested-by: Guo Chao Tested-by: Wei Yang Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 150 ++++++++++++++++++++++++++++++++---------------- drivers/pci/setup-res.c | 20 ++++++- 2 files changed, 117 insertions(+), 53 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 9b3498c9b739..b6585cb9bce7 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -713,12 +713,11 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) bus resource of a given type. Note: we intentionally skip the bus resources which have already been assigned (that is, have non-NULL parent resource). */ -static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned long type) +static struct resource *find_free_bus_resource(struct pci_bus *bus, + unsigned long type_mask, unsigned long type) { int i; struct resource *r; - unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH; pci_bus_for_each_resource(bus, r, i) { if (r == &ioport_resource || r == &iomem_resource) @@ -815,7 +814,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, resource_size_t add_size, struct list_head *realloc_head) { struct pci_dev *dev; - struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); + struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO, + IORESOURCE_IO); resource_size_t size = 0, size0 = 0, size1 = 0; resource_size_t children_add_size = 0; resource_size_t min_align, align; @@ -907,6 +907,8 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, * @bus : the bus * @mask: mask the resource flag, then compare it with type * @type: the type of free resource from bridge + * @type2: second match type + * @type3: third match type * @min_size : the minimum memory window that must to be allocated * @add_size : additional optional memory window * @realloc_head : track the additional memory window on this list @@ -915,16 +917,17 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, * 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, - resource_size_t add_size, - struct list_head *realloc_head) + unsigned long type, unsigned long type2, + unsigned long type3, + resource_size_t min_size, resource_size_t add_size, + struct list_head *realloc_head) { struct pci_dev *dev; resource_size_t min_align, align, size, size0, size1; resource_size_t aligns[14]; /* Alignments from 1Mb to 8Gb */ int order, max_order; - struct resource *b_res = find_free_bus_resource(bus, type); - unsigned int mem64_mask = 0; + struct resource *b_res = find_free_bus_resource(bus, + mask | IORESOURCE_PREFETCH, type); resource_size_t children_add_size = 0; if (!b_res) @@ -934,9 +937,6 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, max_order = 0; size = 0; - mem64_mask = b_res->flags & IORESOURCE_MEM_64; - b_res->flags &= ~IORESOURCE_MEM_64; - list_for_each_entry(dev, &bus->devices, bus_list) { int i; @@ -944,7 +944,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, struct resource *r = &dev->resource[i]; resource_size_t r_size; - if (r->parent || (r->flags & mask) != type) + if (r->parent || ((r->flags & mask) != type && + (r->flags & mask) != type2 && + (r->flags & mask) != type3)) continue; r_size = resource_size(r); #ifdef CONFIG_PCI_IOV @@ -981,7 +983,6 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, aligns[order] += align; if (order > max_order) max_order = order; - mem64_mask &= r->flags & IORESOURCE_MEM_64; if (realloc_head) children_add_size += get_res_add_size(realloc_head, r); @@ -1006,7 +1007,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, } b_res->start = min_align; b_res->end = size0 + min_align - 1; - b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask; + b_res->flags |= IORESOURCE_STARTALIGN; if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " @@ -1122,8 +1123,9 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; - unsigned long mask, prefmask; + unsigned long mask, prefmask, type2 = 0, type3 = 0; resource_size_t additional_mem_size = 0, additional_io_size = 0; + struct resource *b_res; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -1168,15 +1170,37 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, has already been allocated by arch code, try non-prefetchable range for both types of PCI memory resources. */ + b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES]; mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, prefmask, prefmask, + if (b_res[2].flags & IORESOURCE_MEM_64) { + prefmask |= IORESOURCE_MEM_64; + if (pbus_size_mem(bus, prefmask, prefmask, + prefmask, prefmask, realloc_head ? 0 : additional_mem_size, - additional_mem_size, realloc_head)) - mask = prefmask; /* Success, size non-prefetch only. */ - else - additional_mem_size += additional_mem_size; - pbus_size_mem(bus, mask, IORESOURCE_MEM, + additional_mem_size, realloc_head)) { + /* + * Success, with pref mmio64, + * next will size non-pref or + * non-mmio64 */ + mask = prefmask; + type2 = prefmask & ~IORESOURCE_MEM_64; + type3 = prefmask & ~IORESOURCE_PREFETCH; + } + } + if (!type2) { + prefmask &= ~IORESOURCE_MEM_64; + if (pbus_size_mem(bus, prefmask, prefmask, + prefmask, prefmask, + realloc_head ? 0 : additional_mem_size, + additional_mem_size, realloc_head)) { + /* Success, next will size non-prefetch. */ + mask = prefmask; + } else + additional_mem_size += additional_mem_size; + type2 = type3 = IORESOURCE_MEM; + } + pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3, realloc_head ? 0 : additional_mem_size, additional_mem_size, realloc_head); break; @@ -1262,42 +1286,66 @@ static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge, static void pci_bridge_release_resources(struct pci_bus *bus, unsigned long type) { - int idx; - bool changed = false; - struct pci_dev *dev; + struct pci_dev *dev = bus->self; struct resource *r; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH; + IORESOURCE_PREFETCH | IORESOURCE_MEM_64; + unsigned old_flags = 0; + struct resource *b_res; + int idx = 1; - dev = bus->self; - for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END; - idx++) { - r = &dev->resource[idx]; - if ((r->flags & type_mask) != type) - continue; - if (!r->parent) - continue; - /* - * if there are children under that, we should release them - * all - */ - release_child_resources(r); - if (!release_resource(r)) { - dev_printk(KERN_DEBUG, &dev->dev, - "resource %d %pR released\n", idx, r); - /* keep the old size */ - r->end = resource_size(r) - 1; - r->start = 0; - r->flags = 0; - changed = true; - } - } + b_res = &dev->resource[PCI_BRIDGE_RESOURCES]; + + /* + * 1. if there is io port assign fail, will release bridge + * io port. + * 2. if there is non pref mmio assign fail, release bridge + * nonpref mmio. + * 3. if there is 64bit pref mmio assign fail, and bridge pref + * is 64bit, release bridge pref mmio. + * 4. if there is pref mmio assign fail, and bridge pref is + * 32bit mmio, release bridge pref mmio + * 5. if there is pref mmio assign fail, and bridge pref is not + * assigned, release bridge nonpref mmio. + */ + if (type & IORESOURCE_IO) + idx = 0; + else if (!(type & IORESOURCE_PREFETCH)) + idx = 1; + else if ((type & IORESOURCE_MEM_64) && + (b_res[2].flags & IORESOURCE_MEM_64)) + idx = 2; + else if (!(b_res[2].flags & IORESOURCE_MEM_64) && + (b_res[2].flags & IORESOURCE_PREFETCH)) + idx = 2; + else + idx = 1; + + r = &b_res[idx]; + + if (!r->parent) + return; + + /* + * if there are children under that, we should release them + * all + */ + release_child_resources(r); + if (!release_resource(r)) { + type = old_flags = r->flags & type_mask; + dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n", + PCI_BRIDGE_RESOURCES + idx, r); + /* keep the old size */ + r->end = resource_size(r) - 1; + r->start = 0; + r->flags = 0; - if (changed) { /* avoiding touch the one without PREF */ if (type & IORESOURCE_PREFETCH) type = IORESOURCE_PREFETCH; __pci_setup_bridge(bus, type); + /* for next child res under same bridge */ + r->flags = old_flags; } } @@ -1476,7 +1524,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) LIST_HEAD(fail_head); struct pci_dev_resource *fail_res; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | - IORESOURCE_PREFETCH; + IORESOURCE_PREFETCH | IORESOURCE_MEM_64; int pci_try_num = 1; enum enable_type enable_local; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 7eed671d5586..2473f091a9cc 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -211,15 +211,31 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, /* First, try exact prefetching match.. */ ret = pci_bus_alloc_resource(bus, res, size, align, min, - IORESOURCE_PREFETCH, + IORESOURCE_PREFETCH | IORESOURCE_MEM_64, pcibios_align_resource, dev); - if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) { + if (ret < 0 && + (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == + (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { + /* + * That failed. + * + * Try 32bit pref + */ + ret = pci_bus_alloc_resource(bus, res, size, align, min, + IORESOURCE_PREFETCH, + pcibios_align_resource, dev); + } + + if (ret < 0 && + (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))) { /* * That failed. * * But a prefetching area can handle a non-prefetching * window (it will just not perform as well). + * + * Also can put 64bit under 32bit range. (below 4g). */ ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, pcibios_align_resource, dev); -- cgit v1.2.1 From 30afe8d00b994416b24c63f8c5bbf1c13869ec3c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 May 2014 18:28:37 -0600 Subject: PCI: Change pbus_size_mem() return values to be more conventional pbus_size_mem() previously returned 0 for failure and 1 for success. Change it to return -ENOSPC for failure and 0 for success. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b6585cb9bce7..12ab50ffdfea 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -915,6 +915,10 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, * * Calculate the size of the bus and minimal alignment which * guarantees that all child resources fit in this size. + * + * Returns -ENOSPC if there's no available bus resource of the desired type. + * Otherwise, sets the bus resource start/end to indicate the required + * size, adds things to realloc_head (if supplied), and returns 0. */ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type, unsigned long type2, @@ -931,7 +935,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, resource_size_t children_add_size = 0; if (!b_res) - return 0; + return -ENOSPC; memset(aligns, 0, sizeof(aligns)); max_order = 0; @@ -1003,7 +1007,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, "%pR to %pR (unused)\n", b_res, &bus->busn_res); b_res->flags = 0; - return 1; + return 0; } b_res->start = min_align; b_res->end = size0 + min_align - 1; @@ -1014,7 +1018,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, "%pR to %pR add_size %llx\n", b_res, &bus->busn_res, (unsigned long long)size1-size0); } - return 1; + return 0; } unsigned long pci_cardbus_resource_alignment(struct resource *res) @@ -1126,6 +1130,7 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, unsigned long mask, prefmask, type2 = 0, type3 = 0; resource_size_t additional_mem_size = 0, additional_io_size = 0; struct resource *b_res; + int ret; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -1175,25 +1180,27 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; if (b_res[2].flags & IORESOURCE_MEM_64) { prefmask |= IORESOURCE_MEM_64; - if (pbus_size_mem(bus, prefmask, prefmask, + ret = pbus_size_mem(bus, prefmask, prefmask, prefmask, prefmask, realloc_head ? 0 : additional_mem_size, - additional_mem_size, realloc_head)) { - /* - * Success, with pref mmio64, - * next will size non-pref or - * non-mmio64 */ - mask = prefmask; - type2 = prefmask & ~IORESOURCE_MEM_64; - type3 = prefmask & ~IORESOURCE_PREFETCH; + additional_mem_size, realloc_head); + if (ret == 0) { + /* + * Success, with pref mmio64, + * next will size non-pref or + * non-mmio64 */ + mask = prefmask; + type2 = prefmask & ~IORESOURCE_MEM_64; + type3 = prefmask & ~IORESOURCE_PREFETCH; } } if (!type2) { prefmask &= ~IORESOURCE_MEM_64; - if (pbus_size_mem(bus, prefmask, prefmask, + ret = pbus_size_mem(bus, prefmask, prefmask, prefmask, prefmask, realloc_head ? 0 : additional_mem_size, - additional_mem_size, realloc_head)) { + additional_mem_size, realloc_head); + if (ret == 0) { /* Success, next will size non-prefetch. */ mask = prefmask; } else -- cgit v1.2.1 From d3689df04445c568c8b3dfcd8db4b562e1b18cfb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 May 2014 18:39:07 -0600 Subject: PCI: Simplify __pci_assign_resource() coding style If an allocation succeeds, we can return success immediately. Then we don't have to test for success in the subsequent code. No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-res.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 2473f091a9cc..3bdac9dc4a88 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -213,9 +213,10 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, ret = pci_bus_alloc_resource(bus, res, size, align, min, IORESOURCE_PREFETCH | IORESOURCE_MEM_64, pcibios_align_resource, dev); + if (ret == 0) + return 0; - if (ret < 0 && - (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == + if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { /* * That failed. @@ -225,10 +226,11 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, ret = pci_bus_alloc_resource(bus, res, size, align, min, IORESOURCE_PREFETCH, pcibios_align_resource, dev); + if (ret == 0) + return 0; } - if (ret < 0 && - (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))) { + if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { /* * That failed. * -- cgit v1.2.1 From 67d29b5c6c40e91b124695e9250c2fd24915e24a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 19 May 2014 18:32:18 -0600 Subject: PCI: Add resource allocation comments Add comments in the code to match the allocation strategy of 7c671426dfc3 ("PCI: Restrict 64-bit prefetchable bridge windows to 64-bit resources"). No functional change. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 58 ++++++++++++++++++++++++++++++++++++------------- drivers/pci/setup-res.c | 35 +++++++++++++++-------------- 2 files changed, 62 insertions(+), 31 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 12ab50ffdfea..455ee0364956 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1164,17 +1164,16 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, additional_io_size = pci_hotplug_io_size; additional_mem_size = pci_hotplug_mem_size; } - /* - * Follow thru - */ + /* Fall through */ default: pbus_size_io(bus, realloc_head ? 0 : additional_io_size, additional_io_size, realloc_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 - non-prefetchable range for both types of PCI memory - resources. */ + + /* + * If there's a 64-bit prefetchable MMIO window, compute + * the size required to put all 64-bit prefetchable + * resources in it. + */ b_res = &bus->self->resource[PCI_BRIDGE_RESOURCES]; mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; @@ -1184,29 +1183,58 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus, prefmask, prefmask, realloc_head ? 0 : additional_mem_size, additional_mem_size, realloc_head); + + /* + * If successful, all non-prefetchable resources + * and any 32-bit prefetchable resources will go in + * the non-prefetchable window. + */ if (ret == 0) { - /* - * Success, with pref mmio64, - * next will size non-pref or - * non-mmio64 */ mask = prefmask; type2 = prefmask & ~IORESOURCE_MEM_64; type3 = prefmask & ~IORESOURCE_PREFETCH; } } + + /* + * If there is no 64-bit prefetchable window, compute the + * size required to put all prefetchable resources in the + * 32-bit prefetchable window (if there is one). + */ if (!type2) { prefmask &= ~IORESOURCE_MEM_64; ret = pbus_size_mem(bus, prefmask, prefmask, prefmask, prefmask, realloc_head ? 0 : additional_mem_size, additional_mem_size, realloc_head); - if (ret == 0) { - /* Success, next will size non-prefetch. */ + + /* + * If successful, only non-prefetchable resources + * will go in the non-prefetchable window. + */ + if (ret == 0) mask = prefmask; - } else + else additional_mem_size += additional_mem_size; + type2 = type3 = IORESOURCE_MEM; } + + /* + * Compute the size required to put everything else in the + * non-prefetchable window. This includes: + * + * - all non-prefetchable resources + * - 32-bit prefetchable resources if there's a 64-bit + * prefetchable window or no prefetchable window at all + * - 64-bit prefetchable resources if there's no + * prefetchable window at all + * + * Note that the strategy in __pci_assign_resource() must + * match that used here. Specifically, we cannot put a + * 32-bit prefetchable resource in a 64-bit prefetchable + * window. + */ pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3, realloc_head ? 0 : additional_mem_size, additional_mem_size, realloc_head); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3bdac9dc4a88..3da2542eb4df 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -209,20 +209,25 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - /* First, try exact prefetching match.. */ + /* + * First, try exact prefetching match. Even if a 64-bit + * prefetchable bridge window is below 4GB, we can't put a 32-bit + * prefetchable resource in it because pbus_size_mem() assumes a + * 64-bit window will contain no 32-bit resources. If we assign + * things differently than they were sized, not everything will fit. + */ ret = pci_bus_alloc_resource(bus, res, size, align, min, IORESOURCE_PREFETCH | IORESOURCE_MEM_64, pcibios_align_resource, dev); if (ret == 0) return 0; + /* + * If the prefetchable window is only 32 bits wide, we can put + * 64-bit prefetchable resources in it. + */ if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) == (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { - /* - * That failed. - * - * Try 32bit pref - */ ret = pci_bus_alloc_resource(bus, res, size, align, min, IORESOURCE_PREFETCH, pcibios_align_resource, dev); @@ -230,18 +235,16 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, return 0; } - if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) { - /* - * That failed. - * - * But a prefetching area can handle a non-prefetching - * window (it will just not perform as well). - * - * Also can put 64bit under 32bit range. (below 4g). - */ + /* + * If we didn't find a better match, we can put any memory resource + * in a non-prefetchable window. If this resource is 32 bits and + * non-prefetchable, the first call already tried the only possibility + * so we don't need to try again. + */ + if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, pcibios_align_resource, dev); - } + return ret; } -- cgit v1.2.1