From 3dcc8d39cf15fa3ceabedcffcbd3958fe953555a Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Mon, 9 Nov 2015 20:00:27 +0100 Subject: PCI: Prevent out of bounds access in numa_node override Commit 1266963170f5 ("PCI: Prevent out of bounds access in numa_node override") missed that the user-provided node could also be negative. Handle this case as well to avoid out-of-bounds accesses to the node_states[] array. However, allow the special value -1, i.e. NUMA_NO_NODE, to be able to set the 'no specific node' configuration. Fixes: 1266963170f5 ("PCI: Prevent out of bounds access in numa_node override") Fixes: 63692df103e9 ("PCI: Allow numa_node override via sysfs") Signed-off-by: Mathias Krause Signed-off-by: Bjorn Helgaas CC: Sasha Levin CC: Prarit Bhargava CC: stable@vger.kernel.org # v3.19+ --- drivers/pci/pci-sysfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 92618686604c..eead54cd01b2 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -216,7 +216,10 @@ static ssize_t numa_node_store(struct device *dev, if (ret) return ret; - if (node >= MAX_NUMNODES || !node_online(node)) + if ((node < 0 && node != NUMA_NO_NODE) || node >= MAX_NUMNODES) + return -EINVAL; + + if (node != NUMA_NO_NODE && !node_online(node)) return -EINVAL; add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); -- cgit v1.2.3 From 5228e39e3f709c82e6c4643402fc25de54391e32 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Tue, 24 Nov 2015 11:36:52 +0200 Subject: PCI: designware: Remove incorrect io_base assignment "pp->io" is an I/O resource, e.g., "[io 0x0000-0xffff]"; "pp->io_base" is the CPU physical address of a region where the host bridge converts CPU memory accesses into PCI I/O transactions. Corrupting pp->io_base by assigning pp->io->start to it breaks access to the PCI I/O space, as reported by Kishon. Remove the invalid assignment. [bhelgaas: changelog] Fixes: 0021d22b73d6 ("PCI: designware: Use of_pci_get_host_bridge_resources() to parse DT") Reported-and-tested-by: Kishon Vijay Abraham I Signed-off-by: Stanimir Varbanov Signed-off-by: Bjorn Helgaas Reviewed-by: Arnd Bergmann --- drivers/pci/host/pcie-designware.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 540f077c37ea..02a7452bdf23 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -440,7 +440,6 @@ int dw_pcie_host_init(struct pcie_port *pp) ret, pp->io); continue; } - pp->io_base = pp->io->start; break; case IORESOURCE_MEM: pp->mem = win->res; -- cgit v1.2.3 From 9f55cf5654fff70de8675ea128b53281edc794d3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 24 Nov 2015 15:38:07 -0600 Subject: PCI: hisi: Fix deferred probing The hisi_pcie_probe() function is incorrectly marked as __init, as Kconfig tells us: WARNING: drivers/pci/host/built-in.o(.data+0x7780): Section mismatch in reference from the variable hisi_pcie_driver to the function .init.text:hisi_pcie_probe() If the probe for this device gets deferred past the point where __init functions are removed, or the device is unbound and then reattached to the driver, we branch into uninitialized memory, which is bad. Remove the __init annotation from hisi_pcie_probe() and hisi_add_pcie_port(). Fixes: 500a1d9a43e0 ("PCI: hisi: Add HiSilicon SoC Hip05 PCIe driver") Signed-off-by: Arnd Bergmann Signed-off-by: Bjorn Helgaas Reviewed-by: Hanjun Guo Acked-by: Zhou Wang --- drivers/pci/host/pcie-hisi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/host/pcie-hisi.c index 35457ecd8e70..163671a4f798 100644 --- a/drivers/pci/host/pcie-hisi.c +++ b/drivers/pci/host/pcie-hisi.c @@ -111,7 +111,7 @@ static struct pcie_host_ops hisi_pcie_host_ops = { .link_up = hisi_pcie_link_up, }; -static int __init hisi_add_pcie_port(struct pcie_port *pp, +static int hisi_add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) { int ret; @@ -139,7 +139,7 @@ static int __init hisi_add_pcie_port(struct pcie_port *pp, return 0; } -static int __init hisi_pcie_probe(struct platform_device *pdev) +static int hisi_pcie_probe(struct platform_device *pdev) { struct hisi_pcie *hisi_pcie; struct pcie_port *pp; -- cgit v1.2.3 From 7c7a0e945349a3d0d497d7f32db6ed33d4031110 Mon Sep 17 00:00:00 2001 From: Gabriele Paoloni Date: Wed, 11 Nov 2015 09:12:25 +0800 Subject: ARM/PCI: Move align_resource function pointer to pci_host_bridge structure Commit b3a72384fe29 ("ARM/PCI: Replace pci_sys_data->align_resource with global function pointer") introduced an ARM-specific align_resource() function pointer. This is not portable to other arches and doesn't work for platforms with two different PCIe host bridge controllers. Move the function pointer to the pci_host_bridge structure so each host bridge driver can specify its own align_resource() function. Signed-off-by: Gabriele Paoloni Signed-off-by: Bjorn Helgaas Reviewed-by: Arnd Bergmann --- arch/arm/kernel/bios32.c | 19 +++++++++++-------- drivers/pci/pci.h | 2 -- include/linux/pci.h | 9 +++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers/pci') diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 6551d28c27e6..066f7f9ba411 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -17,11 +17,6 @@ #include static int debug_pci; -static resource_size_t (*align_resource)(struct pci_dev *dev, - const struct resource *res, - resource_size_t start, - resource_size_t size, - resource_size_t align) = NULL; /* * We can't use pci_get_device() here since we are @@ -461,7 +456,6 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, sys->busnr = busnr; sys->swizzle = hw->swizzle; sys->map_irq = hw->map_irq; - align_resource = hw->align_resource; INIT_LIST_HEAD(&sys->resources); if (hw->private_data) @@ -470,6 +464,8 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, ret = hw->setup(nr, sys); if (ret > 0) { + struct pci_host_bridge *host_bridge; + ret = pcibios_init_resources(nr, sys); if (ret) { kfree(sys); @@ -491,6 +487,9 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw, busnr = sys->bus->busn_res.end + 1; list_add(&sys->node, head); + + host_bridge = pci_find_host_bridge(sys->bus); + host_bridge->align_resource = hw->align_resource; } else { kfree(sys); if (ret < 0) @@ -578,14 +577,18 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, { struct pci_dev *dev = data; resource_size_t start = res->start; + struct pci_host_bridge *host_bridge; if (res->flags & IORESOURCE_IO && start & 0x300) start = (start + 0x3ff) & ~0x3ff; start = (start + align - 1) & ~(align - 1); - if (align_resource) - return align_resource(dev, res, start, size, align); + host_bridge = pci_find_host_bridge(dev->bus); + + if (host_bridge->align_resource) + return host_bridge->align_resource(dev, res, + start, size, align); return start; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fd2f03fa53f3..d390fc1475ec 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -337,6 +337,4 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe) } #endif -struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); - #endif /* DRIVERS_PCI_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index e828e7b4afec..6ae25aae88fd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -412,9 +412,18 @@ struct pci_host_bridge { void (*release_fn)(struct pci_host_bridge *); void *release_data; unsigned int ignore_reset_delay:1; /* for entire hierarchy */ + /* Resource alignment requirements */ + resource_size_t (*align_resource)(struct pci_dev *dev, + const struct resource *res, + resource_size_t start, + resource_size_t size, + resource_size_t align); }; #define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev) + +struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus); + void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void (*release_fn)(struct pci_host_bridge *), void *release_data); -- cgit v1.2.3 From 06bf403de344a8a0811ebd24992d2a08022c5225 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 30 Nov 2015 21:02:55 +0200 Subject: PCI / PM: Tune down retryable runtime suspend error messages The runtime PM core doesn't treat EBUSY and EAGAIN retvals from the driver suspend hooks as errors, but they still show up as errors in dmesg. Tune them down. See rpm_suspend() for details of handling these return values. Note that we use dev_dbg() for the retryable retvals, so after this change you'll need either CONFIG_DYNAMIC_DEBUG or CONFIG_PCI_DEBUG for them to show up in the log. One problem caused by this was noticed by Daniel: the i915 driver returns EAGAIN to signal a temporary failure to suspend and as a request towards the RPM core for scheduling a suspend again. This is a normal event, but the resulting error message flags a breakage during the driver's automated testing which parses dmesg and picks up the error. Reported-by: Daniel Vetter Link: https://bugs.freedesktop.org/show_bug.cgi?id=92992 Signed-off-by: Imre Deak Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 4446fcb5effd..d7ffd66814bb 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1146,9 +1146,21 @@ static int pci_pm_runtime_suspend(struct device *dev) pci_dev->state_saved = false; pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); - suspend_report_result(pm->runtime_suspend, error); - if (error) + if (error) { + /* + * -EBUSY and -EAGAIN is used to request the runtime PM core + * to schedule a new suspend, so log the event only with debug + * log level. + */ + if (error == -EBUSY || error == -EAGAIN) + dev_dbg(dev, "can't suspend now (%pf returned %d)\n", + pm->runtime_suspend, error); + else + dev_err(dev, "can't suspend (%pf returned %d)\n", + pm->runtime_suspend, error); + return error; + } if (!pci_dev->d3cold_allowed) pci_dev->no_d3cold = true; -- cgit v1.2.3 From 3845d2953aacf00ad069806ba8d1495675069f23 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 4 Dec 2015 10:28:14 -0600 Subject: PCI/MSI: Only use the generic MSI layer when domain is hierarchical Since d8a1cb757550 ("PCI/MSI: Let pci_msi_get_domain use struct device::msi_domain"), we use the MSI domain associated with the PCI device. But finding an MSI domain doesn't mean that the domain is implemented using the generic MSI domain API, and a number of MSI controllers are still using arch_setup_msi_irq() and arch_teardown_msi_irqs(). Check that the domain we just obtained is hierarchical. If it is, we can use the new generic MSI stuff. Otherwise we have to fall back to the old arch_setup_msi_irq() and arch_teardown_msi_irqs() interfaces. This avoids an oops in msi_domain_alloc_irqs() on systems with R-Car, Tegra, Armada 370, and probably other DesignWare-based host controllers. Fixes: d8a1cb757550 ("PCI/MSI: Let pci_msi_get_domain use struct device::msi_domain") Reported-by: Phil Edworthy Tested-by: Phil Edworthy Signed-off-by: Marc Zyngier Signed-off-by: Bjorn Helgaas Acked-by: Thomas Gleixner CC: stable@vger.kernel.org # v4.3+ --- drivers/pci/msi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 53e463244bb7..7eaa4c87fec7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -54,7 +54,7 @@ static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) struct irq_domain *domain; domain = pci_msi_get_domain(dev); - if (domain) + if (domain && irq_domain_is_hierarchy(domain)) return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); return arch_setup_msi_irqs(dev, nvec, type); @@ -65,7 +65,7 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev) struct irq_domain *domain; domain = pci_msi_get_domain(dev); - if (domain) + if (domain && irq_domain_is_hierarchy(domain)) pci_msi_domain_free_irqs(domain, dev); else arch_teardown_msi_irqs(dev); -- cgit v1.2.3 From 7f52f31443ae9b2e6a71ec54cfc5a2c89006ea27 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Dec 2015 16:21:08 -0600 Subject: PCI: altera: Fix loop in tlp_read_packet() TLP_LOOP is 500 and the "loop" variable was a u8 so "loop < TLP_LOOP" is always true. We only need this condition to work if there is a problem so it would have been easy to miss this in testing. Make it a normal for loop with "int i" instead of over thinking things and making it complicated. Fixes: 6bb4dd154ae8 ("PCI: altera: Add Altera PCIe host controller driver") Signed-off-by: Dan Carpenter Signed-off-by: Bjorn Helgaas Acked-by: Ley Foon Tan --- drivers/pci/host/pcie-altera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index e5dda38bdde5..f0820d3fadf6 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -166,7 +166,7 @@ static bool altera_pcie_valid_config(struct altera_pcie *pcie, static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) { - u8 loop; + int i; bool sop = 0; u32 ctrl; u32 reg0, reg1; @@ -175,7 +175,7 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) * Minimum 2 loops to read TLP headers and 1 loop to read data * payload. */ - for (loop = 0; loop < TLP_LOOP; loop++) { + for (i = 0; i < TLP_LOOP; i++) { ctrl = cra_readl(pcie, RP_RXCPL_STATUS); if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) { reg0 = cra_readl(pcie, RP_RXCPL_REG0); -- cgit v1.2.3 From 23ec56708af7155414a979675dc8132a5d6e16ef Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 4 Dec 2015 16:21:12 -0600 Subject: PCI: altera: Fix Requester ID for config accesses The Requester ID should use the Root Port devfn and it should be always 0. Previously we constructed the Requester ID using the *Completer* devfn, i.e., the devfn of the Function we expect to respond to the config access. This causes issues when accessing configuration space for devices other than the Root Port. Build the Requester ID using the Root Port devfn. Tested on Ethernet adapter card with multi-functions. Signed-off-by: Ley Foon Tan Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-altera.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index f0820d3fadf6..ed736a94eade 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -57,6 +57,7 @@ #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) #define TLP_HDR_SIZE 3 #define TLP_LOOP 500 +#define RP_DEVFN 0 #define INTX_NUM 4 @@ -233,7 +234,7 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, else headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1); - headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn), + headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN), TLP_READ_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); @@ -253,7 +254,7 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, else headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1); - headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn), + headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN), TLP_WRITE_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); -- cgit v1.2.3 From ea1d3795f65e14619642b5f67382800f58e43f3d Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 4 Dec 2015 16:21:16 -0600 Subject: PCI: altera: Check TLP completion status Check TLP packet successful completion status. This fix the issue when accessing multi-function devices in enumeration process, TLP will return error when accessing non-exist function number. Returns PCI error code instead of generic errno. Tested on Ethernet adapter card with multi-functions. [bhelgaas: simplify completion status checking code] Signed-off-by: Ley Foon Tan Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-altera.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index ed736a94eade..072255d5b31c 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -55,6 +55,7 @@ #define TLP_CFG_DW2(bus, devfn, offset) \ (((bus) << 24) | ((devfn) << 16) | (offset)) #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) +#define TLP_COMP_STATUS(s) (((s) >> 12) & 7) #define TLP_HDR_SIZE 3 #define TLP_LOOP 500 #define RP_DEVFN 0 @@ -171,6 +172,7 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) bool sop = 0; u32 ctrl; u32 reg0, reg1; + u32 comp_status = 1; /* * Minimum 2 loops to read TLP headers and 1 loop to read data @@ -182,19 +184,25 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) reg0 = cra_readl(pcie, RP_RXCPL_REG0); reg1 = cra_readl(pcie, RP_RXCPL_REG1); - if (ctrl & RP_RXCPL_SOP) + if (ctrl & RP_RXCPL_SOP) { sop = true; + comp_status = TLP_COMP_STATUS(reg1); + } if (ctrl & RP_RXCPL_EOP) { + if (comp_status) + return PCIBIOS_DEVICE_NOT_FOUND; + if (value) *value = reg0; + return PCIBIOS_SUCCESSFUL; } } udelay(5); } - return -ENOENT; + return PCIBIOS_DEVICE_NOT_FOUND; } static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers, -- cgit v1.2.3 From 99496bd2971fc378226ad4413e5b72c4545714bd Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 4 Dec 2015 16:21:21 -0600 Subject: PCI: altera: Fix error when INTx is 4 PCI interrupt lines start at 1, not at 0. So, creates additional one interrupt when register for irq domain. Error when PCIe devices have 4 INTx: WARNING: CPU: 1 PID: 1 at kernel/irq/irqdomain.c:280 irq_domain_associate+0x17c/0x1cc() error: hwirq 0x4 is too large for dummy Tested on Ethernet adapter card with multi-functions. Signed-off-by: Ley Foon Tan Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-altera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 072255d5b31c..99da549d5d06 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -467,7 +467,7 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) struct device_node *node = dev->of_node; /* Setup INTx */ - pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM, + pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM + 1, &intx_domain_ops, pcie); if (!pcie->irq_domain) { dev_err(dev, "Failed to get a INTx IRQ domain\n"); -- cgit v1.2.3