Commit 52f518a3 authored by Jiang Liu's avatar Jiang Liu Committed by Thomas Gleixner

x86/MSI: Use hierarchical irqdomains to manage MSI interrupts

Enhance MSI code to support hierarchical irqdomains, it helps to make
the architecture more clear.
Signed-off-by: default avatarJiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: David Cohen <david.a.cohen@linux.intel.com>
Cc: Sander Eikelenboom <linux@eikelenboom.it>
Cc: David Vrabel <david.vrabel@citrix.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: iommu@lists.linux-foundation.org
Cc: Joerg Roedel <jroedel@suse.de>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dimitri Sivanich <sivanich@sgi.com>
Cc: Joerg Roedel <joro@8bytes.org>
Link: http://lkml.kernel.org/r/1428905519-23704-14-git-send-email-jiang.liu@linux.intel.comSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 3cb96f0c
...@@ -914,6 +914,7 @@ config X86_LOCAL_APIC ...@@ -914,6 +914,7 @@ config X86_LOCAL_APIC
depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
select IRQ_DOMAIN_HIERARCHY select IRQ_DOMAIN_HIERARCHY
select PCI_MSI_IRQ_DOMAIN if PCI_MSI
config X86_IO_APIC config X86_IO_APIC
def_bool y def_bool y
......
...@@ -110,9 +110,10 @@ struct irq_2_irte { ...@@ -110,9 +110,10 @@ struct irq_2_irte {
}; };
#endif /* CONFIG_IRQ_REMAP */ #endif /* CONFIG_IRQ_REMAP */
struct irq_domain;
#ifdef CONFIG_X86_LOCAL_APIC #ifdef CONFIG_X86_LOCAL_APIC
struct irq_data; struct irq_data;
struct irq_domain;
struct pci_dev; struct pci_dev;
struct msi_desc; struct msi_desc;
...@@ -214,6 +215,12 @@ static inline void lock_vector_lock(void) {} ...@@ -214,6 +215,12 @@ static inline void lock_vector_lock(void) {}
static inline void unlock_vector_lock(void) {} static inline void unlock_vector_lock(void) {}
#endif /* CONFIG_X86_LOCAL_APIC */ #endif /* CONFIG_X86_LOCAL_APIC */
#ifdef CONFIG_PCI_MSI
extern void arch_init_msi_domain(struct irq_domain *domain);
#else
static inline void arch_init_msi_domain(struct irq_domain *domain) { }
#endif
/* Statistics */ /* Statistics */
extern atomic_t irq_err_count; extern atomic_t irq_err_count;
extern atomic_t irq_mis_count; extern atomic_t irq_mis_count;
......
...@@ -66,11 +66,7 @@ irq_remapping_get_irq_domain(struct irq_alloc_info *info); ...@@ -66,11 +66,7 @@ irq_remapping_get_irq_domain(struct irq_alloc_info *info);
extern void irq_remapping_print_chip(struct irq_data *data, struct seq_file *p); extern void irq_remapping_print_chip(struct irq_data *data, struct seq_file *p);
/* Create PCI MSI/MSIx irqdomain, use @parent as the parent irqdomain. */ /* Create PCI MSI/MSIx irqdomain, use @parent as the parent irqdomain. */
static inline struct irq_domain * extern struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent);
arch_create_msi_irq_domain(struct irq_domain *parent)
{
return NULL;
}
/* Get parent irqdomain for interrupt remapping irqdomain */ /* Get parent irqdomain for interrupt remapping irqdomain */
static inline struct irq_domain *arch_get_ir_parent_domain(void) static inline struct irq_domain *arch_get_ir_parent_domain(void)
......
#ifndef _ASM_X86_MSI_H
#define _ASM_X86_MSI_H
#include <asm/hw_irq.h>
typedef struct irq_alloc_info msi_alloc_info_t;
#endif /* _ASM_X86_MSI_H */
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
* *
* Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
* Moved from arch/x86/kernel/apic/io_apic.c. * Moved from arch/x86/kernel/apic/io_apic.c.
* Jiang Liu <jiang.liu@linux.intel.com>
* Convert to hierarchical irqdomain
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -21,6 +23,8 @@ ...@@ -21,6 +23,8 @@
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/irq_remapping.h> #include <asm/irq_remapping.h>
static struct irq_domain *msi_default_domain;
void native_compose_msi_msg(struct pci_dev *pdev, void native_compose_msi_msg(struct pci_dev *pdev,
unsigned int irq, unsigned int dest, unsigned int irq, unsigned int dest,
struct msi_msg *msg, u8 hpet_id) struct msi_msg *msg, u8 hpet_id)
...@@ -114,102 +118,107 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, ...@@ -114,102 +118,107 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq,
return 0; return 0;
} }
static int
msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
{
struct irq_cfg *cfg = irqd_cfg(data);
struct msi_msg msg;
unsigned int dest;
int ret;
ret = apic_set_affinity(data, mask, &dest);
if (ret)
return ret;
__get_cached_msi_msg(data->msi_desc, &msg);
msg.data &= ~MSI_DATA_VECTOR_MASK;
msg.data |= MSI_DATA_VECTOR(cfg->vector);
msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
msg.address_lo |= MSI_ADDR_DEST_ID(dest);
__pci_write_msi_msg(data->msi_desc, &msg);
return IRQ_SET_MASK_OK_NOCOPY;
}
/* /*
* IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
* which implement the MSI or MSI-X Capability Structure. * which implement the MSI or MSI-X Capability Structure.
*/ */
static struct irq_chip msi_chip = { static struct irq_chip pci_msi_controller = {
.name = "PCI-MSI", .name = "PCI-MSI",
.irq_unmask = pci_msi_unmask_irq, .irq_unmask = pci_msi_unmask_irq,
.irq_mask = pci_msi_mask_irq, .irq_mask = pci_msi_mask_irq,
.irq_ack = apic_ack_edge, .irq_ack = irq_chip_ack_parent,
.irq_set_affinity = msi_set_affinity, .irq_set_affinity = msi_domain_set_affinity,
.irq_retrigger = apic_retrigger_irq, .irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_print_chip = irq_remapping_print_chip,
.irq_compose_msi_msg = irq_msi_compose_msg,
.irq_write_msi_msg = pci_msi_domain_write_msg,
.flags = IRQCHIP_SKIP_SET_WAKE, .flags = IRQCHIP_SKIP_SET_WAKE,
}; };
int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
unsigned int irq_base, unsigned int irq_offset)
{ {
struct irq_chip *chip = &msi_chip; struct irq_domain *domain;
struct msi_msg msg; struct irq_alloc_info info;
unsigned int irq = irq_base + irq_offset;
int ret;
ret = msi_compose_msg(dev, irq, &msg, -1); init_irq_alloc_info(&info, NULL);
if (ret < 0) info.type = X86_IRQ_ALLOC_TYPE_MSI;
return ret; info.msi_dev = dev;
irq_set_msi_desc_off(irq_base, irq_offset, msidesc); domain = irq_remapping_get_irq_domain(&info);
if (domain == NULL)
domain = msi_default_domain;
if (domain == NULL)
return -ENOSYS;
/* return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
* MSI-X message is written per-IRQ, the offset is always 0. }
* MSI message denotes a contiguous group of IRQs, written for 0th IRQ.
*/
if (!irq_offset)
pci_write_msi_msg(irq, &msg);
setup_remapped_irq(irq, irq_cfg(irq), chip); void native_teardown_msi_irq(unsigned int irq)
{
irq_domain_free_irqs(irq, 1);
}
irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge"); static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info,
msi_alloc_info_t *arg)
{
return arg->msi_hwirq;
}
dev_dbg(&dev->dev, "irq %d for MSI/MSI-X\n", irq); static int pci_msi_prepare(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *arg)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct msi_desc *desc = first_pci_msi_entry(pdev);
init_irq_alloc_info(arg, NULL);
arg->msi_dev = pdev;
if (desc->msi_attrib.is_msix) {
arg->type = X86_IRQ_ALLOC_TYPE_MSIX;
} else {
arg->type = X86_IRQ_ALLOC_TYPE_MSI;
arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
}
return 0; return 0;
} }
int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) static void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{ {
struct msi_desc *msidesc; arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc);
int irq, ret; }
/* Multiple MSI vectors only supported with interrupt remapping */ static struct msi_domain_ops pci_msi_domain_ops = {
if (type == PCI_CAP_ID_MSI && nvec > 1) .get_hwirq = pci_msi_get_hwirq,
return 1; .msi_prepare = pci_msi_prepare,
.set_desc = pci_msi_set_desc,
};
list_for_each_entry(msidesc, &dev->msi_list, list) { static struct msi_domain_info pci_msi_domain_info = {
irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL); .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
if (irq <= 0) MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
return -ENOSPC; .ops = &pci_msi_domain_ops,
.chip = &pci_msi_controller,
.handler = handle_edge_irq,
.handler_name = "edge",
};
ret = setup_msi_irq(dev, msidesc, irq, 0); void arch_init_msi_domain(struct irq_domain *parent)
if (ret < 0) { {
irq_domain_free_irqs(irq, 1); if (disable_apic)
return ret; return;
}
} msi_default_domain = pci_msi_create_irq_domain(NULL,
return 0; &pci_msi_domain_info, parent);
if (!msi_default_domain)
pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n");
} }
void native_teardown_msi_irq(unsigned int irq) #ifdef CONFIG_IRQ_REMAP
struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent)
{ {
irq_domain_free_irqs(irq, 1); return msi_create_irq_domain(NULL, &pci_msi_domain_info, parent);
} }
#endif
#ifdef CONFIG_DMAR_TABLE #ifdef CONFIG_DMAR_TABLE
static int static int
......
...@@ -364,6 +364,8 @@ int __init arch_early_irq_init(void) ...@@ -364,6 +364,8 @@ int __init arch_early_irq_init(void)
BUG_ON(x86_vector_domain == NULL); BUG_ON(x86_vector_domain == NULL);
irq_set_default_host(x86_vector_domain); irq_set_default_host(x86_vector_domain);
arch_init_msi_domain(x86_vector_domain);
return arch_early_ioapic_init(); return arch_early_ioapic_init();
} }
......
...@@ -170,7 +170,6 @@ static void __init irq_remapping_modify_x86_ops(void) ...@@ -170,7 +170,6 @@ static void __init irq_remapping_modify_x86_ops(void)
x86_io_apic_ops.set_affinity = set_remapped_irq_affinity; x86_io_apic_ops.set_affinity = set_remapped_irq_affinity;
x86_io_apic_ops.setup_entry = setup_ioapic_remapped_entry; x86_io_apic_ops.setup_entry = setup_ioapic_remapped_entry;
x86_io_apic_ops.eoi_ioapic_pin = eoi_ioapic_pin_remapped; x86_io_apic_ops.eoi_ioapic_pin = eoi_ioapic_pin_remapped;
x86_msi.setup_msi_irqs = irq_remapping_setup_msi_irqs;
x86_msi.setup_hpet_msi = setup_hpet_msi_remapped; x86_msi.setup_hpet_msi = setup_hpet_msi_remapped;
x86_msi.compose_msi_msg = compose_remapped_msi_msg; x86_msi.compose_msi_msg = compose_remapped_msi_msg;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment