mirror of
https://github.com/torvalds/linux.git
synced 2024-12-22 10:56:40 +00:00
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:
parent
3cb96f0c97
commit
52f518a3a7
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
7
arch/x86/include/asm/msi.h
Normal file
7
arch/x86/include/asm/msi.h
Normal 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 */
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user