From 045cc22e53a1a0caaa9a0b6b64246d2690049906 Mon Sep 17 00:00:00 2001 From: "ethan.zhao" Date: Wed, 6 Nov 2013 22:49:13 +0800 Subject: [PATCH 1/5] PCI: Clear NumVFs when disabling SR-IOV in sriov_init() When SR-IOV is disabled (VF Enable is cleared), NumVFs is not very useful, so this patch clears it out to prevent confusing lspci output like that below. We already clear NumVFs in sriov_disable(), and this does the same when we disable SR-IOV as part of parsing the SR-IOV capability. $ lspci -vvv -s 13:00.0 13:00.0 Ethernet controller: Intel Corporation 82599EB 10-Gigabit SFI/SFP+ Network Connection (rev 01) Capabilities: [160 v1] Single Root I/O Virtualization (SR-IOV) IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy+ Initial VFs: 64, Total VFs: 64, Number of VFs: 64, ... [bhelgaas: changelog] Signed-off-by: ethan.zhao Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 1fe2d6fb19d5..68311ec849ee 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -441,6 +441,7 @@ static int sriov_init(struct pci_dev *dev, int pos) found: pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, ctrl); + pci_write_config_word(dev, pos + PCI_SRIOV_NUM_VF, 0); pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); if (!offset || (total > 1 && !stride)) From 6f825b73e249a2b46f0349d1f86adfbb3ea703e0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 12 Nov 2013 20:07:17 +0100 Subject: [PATCH 2/5] PCI/portdrv: Remove superfluous name cast device_driver.name is "const char *" Signed-off-by: Geert Uytterhoeven Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/portdrv_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 0b6e76604068..ce9d9ae17bfd 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -554,7 +554,7 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) if (pcie_ports_disabled) return -ENODEV; - new->driver.name = (char *)new->name; + new->driver.name = new->name; new->driver.bus = &pcie_port_bus_type; new->driver.probe = pcie_port_probe_service; new->driver.remove = pcie_port_remove_service; From ac8344c4c0bf74c7efaf962cf2a6404331678ce4 Mon Sep 17 00:00:00 2001 From: DuanZhenzhong Date: Wed, 4 Dec 2013 13:09:16 +0800 Subject: [PATCH 3/5] PCI: Drop "irq" param from *_restore_msi_irqs() Change x86_msi.restore_msi_irqs(struct pci_dev *dev, int irq) to x86_msi.restore_msi_irqs(struct pci_dev *dev). restore_msi_irqs() restores multiple MSI-X IRQs, so param 'int irq' is unneeded. This makes code more consistent between vm and bare metal. Dom0 MSI-X restore code can also be optimized as XEN only has a hypercall to restore all MSI-X vectors at one time. Tested-by: Sucheta Chakraborty Signed-off-by: Zhenzhong Duan Signed-off-by: Bjorn Helgaas Acked-by: Konrad Rzeszutek Wilk --- arch/x86/include/asm/pci.h | 2 +- arch/x86/include/asm/x86_init.h | 2 +- arch/x86/kernel/x86_init.c | 4 ++-- arch/x86/pci/xen.c | 2 +- drivers/pci/msi.c | 19 ++++++++++++++----- include/linux/msi.h | 4 ++-- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 947b5c417e83..0de52c5bf9a2 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -104,7 +104,7 @@ extern void pci_iommu_alloc(void); struct msi_desc; int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); void native_teardown_msi_irq(unsigned int irq); -void native_restore_msi_irqs(struct pci_dev *dev, int irq); +void native_restore_msi_irqs(struct pci_dev *dev); int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, unsigned int irq_base, unsigned int irq_offset); #else diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index 0f1be11e43d2..e45e4da96bf1 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -181,7 +181,7 @@ struct x86_msi_ops { u8 hpet_id); void (*teardown_msi_irq)(unsigned int irq); void (*teardown_msi_irqs)(struct pci_dev *dev); - void (*restore_msi_irqs)(struct pci_dev *dev, int irq); + void (*restore_msi_irqs)(struct pci_dev *dev); int (*setup_hpet_msi)(unsigned int irq, unsigned int id); u32 (*msi_mask_irq)(struct msi_desc *desc, u32 mask, u32 flag); u32 (*msix_mask_irq)(struct msi_desc *desc, u32 flag); diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 021783b1f46a..e48b674639cc 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -136,9 +136,9 @@ void arch_teardown_msi_irq(unsigned int irq) x86_msi.teardown_msi_irq(irq); } -void arch_restore_msi_irqs(struct pci_dev *dev, int irq) +void arch_restore_msi_irqs(struct pci_dev *dev) { - x86_msi.restore_msi_irqs(dev, irq); + x86_msi.restore_msi_irqs(dev); } u32 arch_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index 5eee4959785d..103e702ec5a7 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -337,7 +337,7 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return ret; } -static void xen_initdom_restore_msi_irqs(struct pci_dev *dev, int irq) +static void xen_initdom_restore_msi_irqs(struct pci_dev *dev) { int ret = 0; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3fcd67a16677..51bf0400a889 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -116,7 +116,7 @@ void __weak arch_teardown_msi_irqs(struct pci_dev *dev) return default_teardown_msi_irqs(dev); } -void default_restore_msi_irqs(struct pci_dev *dev, int irq) +static void default_restore_msi_irq(struct pci_dev *dev, int irq) { struct msi_desc *entry; @@ -134,9 +134,9 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq) write_msi_msg(irq, &entry->msg); } -void __weak arch_restore_msi_irqs(struct pci_dev *dev, int irq) +void __weak arch_restore_msi_irqs(struct pci_dev *dev) { - return default_restore_msi_irqs(dev, irq); + return default_restore_msi_irqs(dev); } static void msi_set_enable(struct pci_dev *dev, int enable) @@ -262,6 +262,15 @@ void unmask_msi_irq(struct irq_data *data) msi_set_mask_bit(data, 0); } +void default_restore_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry; + + list_for_each_entry(entry, &dev->msi_list, list) { + default_restore_msi_irq(dev, entry->irq); + } +} + void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { BUG_ON(entry->dev->current_state != PCI_D0); @@ -430,7 +439,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pci_intx_for_msi(dev, 0); msi_set_enable(dev, 0); - arch_restore_msi_irqs(dev, dev->irq); + arch_restore_msi_irqs(dev); pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); msi_mask_irq(entry, msi_capable_mask(control), entry->masked); @@ -455,8 +464,8 @@ static void __pci_restore_msix_state(struct pci_dev *dev) control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); + arch_restore_msi_irqs(dev); list_for_each_entry(entry, &dev->msi_list, list) { - arch_restore_msi_irqs(dev, entry->irq); msix_mask_irq(entry, entry->masked); } diff --git a/include/linux/msi.h b/include/linux/msi.h index 009b02481436..92a2f991262a 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -60,10 +60,10 @@ void arch_teardown_msi_irq(unsigned int irq); int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); void arch_teardown_msi_irqs(struct pci_dev *dev); int arch_msi_check_device(struct pci_dev* dev, int nvec, int type); -void arch_restore_msi_irqs(struct pci_dev *dev, int irq); +void arch_restore_msi_irqs(struct pci_dev *dev); void default_teardown_msi_irqs(struct pci_dev *dev); -void default_restore_msi_irqs(struct pci_dev *dev, int irq); +void default_restore_msi_irqs(struct pci_dev *dev); u32 default_msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); u32 default_msix_mask_irq(struct msi_desc *desc, u32 flag); From d2f54d9b3ee32bc006a7ff25d716b32feba4b522 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 27 Nov 2013 15:31:07 -0700 Subject: [PATCH 4/5] PCI: Prevent bus conflicts while checking for bridge apertures pci_bridge_check_ranges() determines whether the bridge supports an I/O aperture and a prefetchable memory aperture. Previously, if the I/O aperture was unsupported, disabled, or configured at [io 0x0000-0x0fff], we wrote 0xf0 to PCI_IO_BASE and PCI_IO_LIMIT, which, if the bridge supports it, enables the I/O aperture at [io 0xf000-0xffff]. The enabled aperture may conflict with other devices in the system. Similarly, we wrote 0xfff0 to PCI_PREF_MEMORY_BASE and PCI_PREF_MEMORY_LIMIT, which enables the prefetchable memory aperture at [mem 0xfff00000-0xffffffff], and that may also conflict with other devices. All we need to know is whether the base and limit registers are writable, so we can use values that leave the apertures disabled, e.g., PCI_IO_BASE = 0xf0, PCI_IO_LIMIT = 0xe0, PCI_PREF_MEMORY_BASE = 0xfff0, PCI_PREF_MEMORY_LIMIT = 0xffe0. Writing non-zero values to both the base and limit registers means we detect whether either or both are writable, as we did before. Reported-by: Jason Gunthorpe Based-on-patch-by: Jason Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 219a4106480a..80350299a6ea 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -665,21 +665,23 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) pci_read_config_word(bridge, PCI_IO_BASE, &io); if (!io) { - pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0); + pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0); pci_read_config_word(bridge, PCI_IO_BASE, &io); pci_write_config_word(bridge, PCI_IO_BASE, 0x0); } if (io) b_res[0].flags |= IORESOURCE_IO; + /* DECchip 21050 pass 2 errata: the bridge may miss an address disconnect boundary by one PCI data phase. Workaround: do not use prefetching on this device. */ if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001) return; + pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); if (!pmem) { pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, - 0xfff0fff0); + 0xffe0fff0); pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); } From 5b764b834ea925b7fc87df4a87a0fde95acbecb9 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 27 Nov 2013 17:24:50 -0700 Subject: [PATCH 5/5] PCI: Stop clearing bridge Secondary Status when setting up I/O aperture pci_setup_bridge_io() accessed PCI_IO_BASE and PCI_IO_LIMIT using dword (32-bit) reads and writes, which also access the Secondary Status register. Since the Secondary Status register is in the upper 16 bits of the dword, and we preserved those upper 16 bits, this had the effect of clearing any of the write-1-to-clear bits that happened to be set in the Secondary Status register. That's not what we want, so use word (16-bit) accesses to update only PCI_IO_BASE and PCI_IO_LIMIT. Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 80350299a6ea..2e344a5581ae 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -538,7 +538,8 @@ static void pci_setup_bridge_io(struct pci_bus *bus) struct pci_bus_region region; unsigned long io_mask; u8 io_base_lo, io_limit_lo; - u32 l, io_upper16; + u16 l; + u32 io_upper16; io_mask = PCI_IO_RANGE_MASK; if (bridge->io_window_1k) @@ -548,11 +549,10 @@ static void pci_setup_bridge_io(struct pci_bus *bus) res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); if (res->flags & IORESOURCE_IO) { - pci_read_config_dword(bridge, PCI_IO_BASE, &l); - l &= 0xffff0000; + pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; - l |= ((u32) io_limit_lo << 8) | io_base_lo; + l = ((u16) io_limit_lo << 8) | io_base_lo; /* Set up upper 16 bits of I/O base/limit. */ io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); dev_info(&bridge->dev, " bridge window %pR\n", res); @@ -564,7 +564,7 @@ static void pci_setup_bridge_io(struct pci_bus *bus) /* Temporarily disable the I/O range before updating PCI_IO_BASE. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); /* Update lower 16 bits of I/O base/limit. */ - pci_write_config_dword(bridge, PCI_IO_BASE, l); + pci_write_config_word(bridge, PCI_IO_BASE, l); /* Update upper 16 bits of I/O base/limit. */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); }