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: Jiang 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.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
Jiang Liu 2015-04-13 14:11:35 +08:00 committed by Thomas Gleixner
parent 3cb96f0c97
commit 52f518a3a7
7 changed files with 103 additions and 82 deletions

View File

@ -914,6 +914,7 @@ config X86_LOCAL_APIC
depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
select IRQ_DOMAIN_HIERARCHY
select PCI_MSI_IRQ_DOMAIN if PCI_MSI
config X86_IO_APIC
def_bool y

View File

@ -110,9 +110,10 @@ struct irq_2_irte {
};
#endif /* CONFIG_IRQ_REMAP */
struct irq_domain;
#ifdef CONFIG_X86_LOCAL_APIC
struct irq_data;
struct irq_domain;
struct pci_dev;
struct msi_desc;
@ -214,6 +215,12 @@ static inline void lock_vector_lock(void) {}
static inline void unlock_vector_lock(void) {}
#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 */
extern atomic_t irq_err_count;
extern atomic_t irq_mis_count;

View File

@ -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);
/* Create PCI MSI/MSIx irqdomain, use @parent as the parent irqdomain. */
static inline struct irq_domain *
arch_create_msi_irq_domain(struct irq_domain *parent)
{
return NULL;
}
extern struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent);
/* Get parent irqdomain for interrupt remapping irqdomain */
static inline struct irq_domain *arch_get_ir_parent_domain(void)

View File

@ -0,0 +1,7 @@
#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 */

View File

@ -3,6 +3,8 @@
*
* Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
* 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
* it under the terms of the GNU General Public License version 2 as
@ -21,6 +23,8 @@
#include <asm/apic.h>
#include <asm/irq_remapping.h>
static struct irq_domain *msi_default_domain;
void native_compose_msi_msg(struct pci_dev *pdev,
unsigned int irq, unsigned int dest,
struct msi_msg *msg, u8 hpet_id)
@ -114,96 +118,39 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq,
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,
* 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",
.irq_unmask = pci_msi_unmask_irq,
.irq_mask = pci_msi_mask_irq,
.irq_ack = apic_ack_edge,
.irq_set_affinity = msi_set_affinity,
.irq_retrigger = apic_retrigger_irq,
.irq_ack = irq_chip_ack_parent,
.irq_set_affinity = msi_domain_set_affinity,
.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,
};
int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
unsigned int irq_base, unsigned int irq_offset)
{
struct irq_chip *chip = &msi_chip;
struct msi_msg msg;
unsigned int irq = irq_base + irq_offset;
int ret;
ret = msi_compose_msg(dev, irq, &msg, -1);
if (ret < 0)
return ret;
irq_set_msi_desc_off(irq_base, irq_offset, msidesc);
/*
* 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);
irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge");
dev_dbg(&dev->dev, "irq %d for MSI/MSI-X\n", irq);
return 0;
}
int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct msi_desc *msidesc;
int irq, ret;
struct irq_domain *domain;
struct irq_alloc_info info;
/* Multiple MSI vectors only supported with interrupt remapping */
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
init_irq_alloc_info(&info, NULL);
info.type = X86_IRQ_ALLOC_TYPE_MSI;
info.msi_dev = dev;
list_for_each_entry(msidesc, &dev->msi_list, list) {
irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL);
if (irq <= 0)
return -ENOSPC;
domain = irq_remapping_get_irq_domain(&info);
if (domain == NULL)
domain = msi_default_domain;
if (domain == NULL)
return -ENOSYS;
ret = setup_msi_irq(dev, msidesc, irq, 0);
if (ret < 0) {
irq_domain_free_irqs(irq, 1);
return ret;
}
}
return 0;
return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
}
void native_teardown_msi_irq(unsigned int irq)
@ -211,6 +158,68 @@ void native_teardown_msi_irq(unsigned int irq)
irq_domain_free_irqs(irq, 1);
}
static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info,
msi_alloc_info_t *arg)
{
return arg->msi_hwirq;
}
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;
}
static void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc);
}
static struct msi_domain_ops pci_msi_domain_ops = {
.get_hwirq = pci_msi_get_hwirq,
.msi_prepare = pci_msi_prepare,
.set_desc = pci_msi_set_desc,
};
static struct msi_domain_info pci_msi_domain_info = {
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
.ops = &pci_msi_domain_ops,
.chip = &pci_msi_controller,
.handler = handle_edge_irq,
.handler_name = "edge",
};
void arch_init_msi_domain(struct irq_domain *parent)
{
if (disable_apic)
return;
msi_default_domain = pci_msi_create_irq_domain(NULL,
&pci_msi_domain_info, parent);
if (!msi_default_domain)
pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n");
}
#ifdef CONFIG_IRQ_REMAP
struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent)
{
return msi_create_irq_domain(NULL, &pci_msi_domain_info, parent);
}
#endif
#ifdef CONFIG_DMAR_TABLE
static int
dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask,

View File

@ -364,6 +364,8 @@ int __init arch_early_irq_init(void)
BUG_ON(x86_vector_domain == NULL);
irq_set_default_host(x86_vector_domain);
arch_init_msi_domain(x86_vector_domain);
return arch_early_ioapic_init();
}

View File

@ -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.setup_entry = setup_ioapic_remapped_entry;
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.compose_msi_msg = compose_remapped_msi_msg;
}