Skip to content

Commit

Permalink
x86/MSI: Use hierarchical irqdomains to manage MSI interrupts
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jiang Liu authored and Thomas Gleixner committed Apr 24, 2015
1 parent 3cb96f0 commit 52f518a
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 73 deletions.
1 change: 1 addition & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion arch/x86/include/asm/hw_irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
6 changes: 1 addition & 5 deletions arch/x86/include/asm/irq_remapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions arch/x86/include/asm/msi.h
Original file line number Diff line number Diff line change
@@ -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 */
141 changes: 75 additions & 66 deletions arch/x86/kernel/apic/msi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -114,102 +118,107 @@ 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)
int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct irq_chip *chip = &msi_chip;
struct msi_msg msg;
unsigned int irq = irq_base + irq_offset;
int ret;
struct irq_domain *domain;
struct irq_alloc_info info;

ret = msi_compose_msg(dev, irq, &msg, -1);
if (ret < 0)
return ret;
init_irq_alloc_info(&info, NULL);
info.type = X86_IRQ_ALLOC_TYPE_MSI;
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;

/*
* 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);
return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
}

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;
}

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;
int irq, ret;
arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc);
}

/* Multiple MSI vectors only supported with interrupt remapping */
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
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,
};

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;
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",
};

ret = setup_msi_irq(dev, msidesc, irq, 0);
if (ret < 0) {
irq_domain_free_irqs(irq, 1);
return ret;
}
void arch_init_msi_domain(struct irq_domain *parent)
{
if (disable_apic)
return;

}
return 0;
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");
}

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
static int
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/kernel/apic/vector.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
1 change: 0 additions & 1 deletion drivers/iommu/irq_remapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 52f518a

Please sign in to comment.