From 386ed2ab85cf1c618e5797a66154a41908ed6aeb Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Fri, 10 Jun 2016 19:05:09 -0500 Subject: PCI: Fix comment typo Fix typo. Signed-off-by: Andrea Gelmini Signed-off-by: Bjorn Helgaas --- arch/x86/pci/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86') diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 8196054..7b6a9d1 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -133,7 +133,7 @@ static void pcibios_fixup_device_resources(struct pci_dev *dev) if (pci_probe & PCI_NOASSIGN_BARS) { /* * If the BIOS did not assign the BAR, zero out the - * resource so the kernel doesn't attmept to assign + * resource so the kernel doesn't attempt to assign * it later on in pci_assign_unassigned_resources */ for (bar = 0; bar <= PCI_STD_RESOURCE_END; bar++) { -- cgit v1.1 From ca8a8fabb10459753fba73cac0382076f0aab058 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 May 2016 11:13:24 -0600 Subject: x86/PCI: VMD: Select device dma ops to override VMD device doesn't usually have device archdata specific dma_ops, so we need to override the default ops for VMD devices. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Acked-by Jon Derrick: --- arch/x86/pci/vmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch/x86') diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c index 7792aba..b1662bf 100644 --- a/arch/x86/pci/vmd.c +++ b/arch/x86/pci/vmd.c @@ -261,7 +261,7 @@ static struct device *to_vmd_dev(struct device *dev) static struct dma_map_ops *vmd_dma_ops(struct device *dev) { - return to_vmd_dev(dev)->archdata.dma_ops; + return get_dma_ops(to_vmd_dev(dev)); } static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr, @@ -367,7 +367,7 @@ static void vmd_teardown_dma_ops(struct vmd_dev *vmd) { struct dma_domain *domain = &vmd->dma_domain; - if (vmd->dev->dev.archdata.dma_ops) + if (get_dma_ops(&vmd->dev->dev)) del_dma_domain(domain); } @@ -379,7 +379,7 @@ static void vmd_teardown_dma_ops(struct vmd_dev *vmd) static void vmd_setup_dma_ops(struct vmd_dev *vmd) { - const struct dma_map_ops *source = vmd->dev->dev.archdata.dma_ops; + const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev); struct dma_map_ops *dest = &vmd->dma_ops; struct dma_domain *domain = &vmd->dma_domain; -- cgit v1.1 From 97e92306357583c1741f0a111c7befe8673b91ee Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 17 May 2016 11:22:18 -0600 Subject: x86/PCI: VMD: Initialize list item in IRQ disable Multiple calls to disable an IRQ would have caused the driver to dereference a poisoned list item. This re-initializes the list to allow multiple requests to disable the IRQ. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Acked-by Jon Derrick: --- arch/x86/pci/vmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/x86') diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c index b1662bf..3519a15 100644 --- a/arch/x86/pci/vmd.c +++ b/arch/x86/pci/vmd.c @@ -135,6 +135,7 @@ static void vmd_irq_disable(struct irq_data *data) raw_spin_lock(&list_lock); list_del_rcu(&vmdirq->node); + INIT_LIST_HEAD_RCU(&vmdirq->node); raw_spin_unlock(&list_lock); } -- cgit v1.1 From 3f57ff4f9c78ee0fafb010287019126ce8c1fc01 Mon Sep 17 00:00:00 2001 From: Jon Derrick Date: Mon, 20 Jun 2016 09:39:51 -0600 Subject: x86/PCI: VMD: Use lock save/restore in interrupt enable path Enabling interrupts may result in an interrupt raised and serviced while VMD holds a lock, resulting in contention with the spin lock held while enabling interrupts. The solution is to disable preemption and save/restore the state during interrupt enable and disable. Fixes lockdep: ====================================================== [ INFO: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected ] 4.6.0-2016-06-16-lockdep+ #47 Tainted: G E ------------------------------------------------------ kworker/0:1/447 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: (list_lock){+.+...}, at: [] vmd_irq_enable+0x3c/0x70 [vmd] and this task is already holding: (&irq_desc_lock_class){-.-...}, at: [] __setup_irq+0xa6/0x610 which would create a new lock dependency: (&irq_desc_lock_class){-.-...} -> (list_lock){+.+...} but this new dependency connects a HARDIRQ-irq-safe lock: (&irq_desc_lock_class){-.-...} ... which became HARDIRQ-irq-safe at: [] __lock_acquire+0x981/0xe00 [] lock_acquire+0x119/0x220 [] _raw_spin_lock+0x3d/0x80 [] handle_level_irq+0x24/0x110 [] handle_irq+0x1a/0x30 [] do_IRQ+0x61/0x120 [] ret_from_intr+0x0/0x20 [] _raw_spin_unlock_irqrestore+0x40/0x60 [] __setup_irq+0x29e/0x610 [] setup_irq+0x41/0x90 [] setup_default_timer_irq+0x1e/0x20 [] hpet_time_init+0x17/0x19 [] x86_late_time_init+0xa/0x11 [] start_kernel+0x382/0x436 [] x86_64_start_reservations+0x2a/0x2c [] x86_64_start_kernel+0x13b/0x14a to a HARDIRQ-irq-unsafe lock: (list_lock){+.+...} ... which became HARDIRQ-irq-unsafe at: ... [] __lock_acquire+0x7ee/0xe00 [] lock_acquire+0x119/0x220 [] _raw_spin_lock+0x3d/0x80 [] vmd_msi_init+0x72/0x150 [vmd] [] msi_domain_alloc+0xb7/0x140 [] irq_domain_alloc_irqs_recursive+0x40/0xa0 [] __irq_domain_alloc_irqs+0x14a/0x330 [] msi_domain_alloc_irqs+0x8c/0x1d0 [] pci_msi_setup_msi_irqs+0x43/0x70 [] pci_enable_msi_range+0x131/0x280 [] pcie_port_device_register+0x320/0x4e0 [] pcie_portdrv_probe+0x34/0x60 [] local_pci_probe+0x45/0xa0 [] pci_device_probe+0xdb/0x130 [] driver_probe_device+0x22c/0x440 [] __device_attach_driver+0x94/0x110 [] bus_for_each_drv+0x5d/0x90 [] __device_attach+0xc0/0x140 [] device_attach+0x10/0x20 [] pci_bus_add_device+0x47/0x90 [] pci_bus_add_devices+0x39/0x70 [] pci_rescan_bus+0x27/0x30 [] vmd_probe+0x68f/0x76c [vmd] [] local_pci_probe+0x45/0xa0 [] work_for_cpu_fn+0x14/0x20 [] process_one_work+0x1f4/0x740 [] worker_thread+0x236/0x4f0 [] kthread+0xf2/0x110 [] ret_from_fork+0x22/0x50 other info that might help us debug this: Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(list_lock); local_irq_disable(); lock(&irq_desc_lock_class); lock(list_lock); lock(&irq_desc_lock_class); *** DEADLOCK *** Signed-off-by: Jon Derrick Signed-off-by: Bjorn Helgaas Acked-by: Keith Busch --- arch/x86/pci/vmd.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'arch/x86') diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c index 3519a15..7aa80dc 100644 --- a/arch/x86/pci/vmd.c +++ b/arch/x86/pci/vmd.c @@ -119,10 +119,11 @@ static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) static void vmd_irq_enable(struct irq_data *data) { struct vmd_irq *vmdirq = data->chip_data; + unsigned long flags; - raw_spin_lock(&list_lock); + raw_spin_lock_irqsave(&list_lock, flags); list_add_tail_rcu(&vmdirq->node, &vmdirq->irq->irq_list); - raw_spin_unlock(&list_lock); + raw_spin_unlock_irqrestore(&list_lock, flags); data->chip->irq_unmask(data); } @@ -130,13 +131,14 @@ static void vmd_irq_enable(struct irq_data *data) static void vmd_irq_disable(struct irq_data *data) { struct vmd_irq *vmdirq = data->chip_data; + unsigned long flags; data->chip->irq_mask(data); - raw_spin_lock(&list_lock); + raw_spin_lock_irqsave(&list_lock, flags); list_del_rcu(&vmdirq->node); INIT_LIST_HEAD_RCU(&vmdirq->node); - raw_spin_unlock(&list_lock); + raw_spin_unlock_irqrestore(&list_lock, flags); } /* @@ -170,13 +172,14 @@ static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info, static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd) { int i, best = 0; + unsigned long flags; - raw_spin_lock(&list_lock); + raw_spin_lock_irqsave(&list_lock, flags); for (i = 1; i < vmd->msix_count; i++) if (vmd->irqs[i].count < vmd->irqs[best].count) best = i; vmd->irqs[best].count++; - raw_spin_unlock(&list_lock); + raw_spin_unlock_irqrestore(&list_lock, flags); return &vmd->irqs[best]; } @@ -204,11 +207,12 @@ static void vmd_msi_free(struct irq_domain *domain, struct msi_domain_info *info, unsigned int virq) { struct vmd_irq *vmdirq = irq_get_chip_data(virq); + unsigned long flags; /* XXX: Potential optimization to rebalance */ - raw_spin_lock(&list_lock); + raw_spin_lock_irqsave(&list_lock, flags); vmdirq->irq->count--; - raw_spin_unlock(&list_lock); + raw_spin_unlock_irqrestore(&list_lock, flags); kfree_rcu(vmdirq, rcu); } -- cgit v1.1 From e382dffc904d14cb6e2c31e2eefebdca41343943 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Mon, 20 Jun 2016 09:39:52 -0600 Subject: x86/PCI: VMD: Use x86_vector_domain as parent domain Otherwise APIC code assumes VMD's IRQ domain can be managed by the APIC, resulting in an invalid cast of irq_data during irq_force_complete_move(). Signed-off-by: Jon Derrick Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas --- arch/x86/pci/vmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86') diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c index 7aa80dc..0f77cc1 100644 --- a/arch/x86/pci/vmd.c +++ b/arch/x86/pci/vmd.c @@ -599,7 +599,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd) sd->node = pcibus_to_node(vmd->dev->bus); vmd->irq_domain = pci_msi_create_irq_domain(NULL, &vmd_msi_domain_info, - NULL); + x86_vector_domain); if (!vmd->irq_domain) return -ENODEV; -- cgit v1.1 From 9c2053040ca7ae52f1143a47ae84502aa7970438 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Mon, 20 Jun 2016 09:39:53 -0600 Subject: x86/PCI: VMD: Separate MSI and MSI-X vector sharing Child devices in a VMD domain that want to use MSI are slowing down MSI-X using devices sharing the same vectors. Move all MSI usage to a single VMD vector, and MSI-X devices can share the rest. Signed-off-by: Keith Busch Signed-off-by: Bjorn Helgaas Acked-by: Jon Derrick --- arch/x86/pci/vmd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'arch/x86') diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c index 0f77cc1..fd582ab 100644 --- a/arch/x86/pci/vmd.c +++ b/arch/x86/pci/vmd.c @@ -169,11 +169,14 @@ static irq_hw_number_t vmd_get_hwirq(struct msi_domain_info *info, * XXX: We can be even smarter selecting the best IRQ once we solve the * affinity problem. */ -static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd) +static struct vmd_irq_list *vmd_next_irq(struct vmd_dev *vmd, struct msi_desc *desc) { - int i, best = 0; + int i, best = 1; unsigned long flags; + if (!desc->msi_attrib.is_msix || vmd->msix_count == 1) + return &vmd->irqs[0]; + raw_spin_lock_irqsave(&list_lock, flags); for (i = 1; i < vmd->msix_count; i++) if (vmd->irqs[i].count < vmd->irqs[best].count) @@ -188,14 +191,15 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info, unsigned int virq, irq_hw_number_t hwirq, msi_alloc_info_t *arg) { - struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(arg->desc)->bus); + struct msi_desc *desc = arg->desc; + struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus); struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL); if (!vmdirq) return -ENOMEM; INIT_LIST_HEAD(&vmdirq->node); - vmdirq->irq = vmd_next_irq(vmd); + vmdirq->irq = vmd_next_irq(vmd, desc); vmdirq->virq = virq; irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip, -- cgit v1.1