2012-09-26 18:44:45 +08:00
|
|
|
#include <linux/seq_file.h>
|
2012-09-26 18:44:35 +08:00
|
|
|
#include <linux/cpumask.h>
|
2012-03-31 02:47:00 +08:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/errno.h>
|
2012-07-06 19:28:37 +08:00
|
|
|
#include <linux/msi.h>
|
2012-09-26 18:44:38 +08:00
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/pci.h>
|
2015-04-13 14:11:29 +08:00
|
|
|
#include <linux/irqdomain.h>
|
2012-07-06 19:28:37 +08:00
|
|
|
|
|
|
|
#include <asm/hw_irq.h>
|
|
|
|
#include <asm/irq_remapping.h>
|
2012-09-26 18:44:35 +08:00
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/x86_init.h>
|
|
|
|
#include <asm/apic.h>
|
2014-09-17 17:32:19 +08:00
|
|
|
#include <asm/hpet.h>
|
2012-03-31 02:47:00 +08:00
|
|
|
|
2012-03-31 02:47:08 +08:00
|
|
|
#include "irq_remapping.h"
|
2012-03-31 02:47:00 +08:00
|
|
|
|
2012-03-31 02:47:07 +08:00
|
|
|
int irq_remapping_enabled;
|
2013-04-17 04:38:32 +08:00
|
|
|
int irq_remap_broken;
|
2012-03-31 02:47:00 +08:00
|
|
|
int disable_sourceid_checking;
|
|
|
|
int no_x2apic_optout;
|
|
|
|
|
2015-09-18 22:29:56 +08:00
|
|
|
int disable_irq_post = 0;
|
2015-06-09 13:20:35 +08:00
|
|
|
|
2015-01-07 15:31:42 +08:00
|
|
|
static int disable_irq_remap;
|
2012-03-31 02:47:00 +08:00
|
|
|
static struct irq_remap_ops *remap_ops;
|
|
|
|
|
2018-02-14 13:46:55 +08:00
|
|
|
static void irq_remapping_restore_boot_irq_mode(void)
|
2012-09-26 18:44:35 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* With interrupt-remapping, for now we will use virtual wire A
|
|
|
|
* mode, as virtual wire B is little complex (need to configure
|
|
|
|
* both IOAPIC RTE as well as interrupt-remapping table entry).
|
|
|
|
* As this gets called during crash dump, keep this simple for
|
|
|
|
* now.
|
|
|
|
*/
|
2016-04-05 04:25:00 +08:00
|
|
|
if (boot_cpu_has(X86_FEATURE_APIC) || apic_from_smp_config())
|
2012-09-26 18:44:35 +08:00
|
|
|
disconnect_bsp_APIC(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __init irq_remapping_modify_x86_ops(void)
|
|
|
|
{
|
2018-02-14 13:46:55 +08:00
|
|
|
x86_apic_ops.restore = irq_remapping_restore_boot_irq_mode;
|
2012-09-26 18:44:35 +08:00
|
|
|
}
|
|
|
|
|
2012-03-31 02:47:00 +08:00
|
|
|
static __init int setup_nointremap(char *str)
|
|
|
|
{
|
2012-03-31 02:47:07 +08:00
|
|
|
disable_irq_remap = 1;
|
2012-03-31 02:47:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
early_param("nointremap", setup_nointremap);
|
|
|
|
|
2012-03-31 02:47:07 +08:00
|
|
|
static __init int setup_irqremap(char *str)
|
2012-03-31 02:47:00 +08:00
|
|
|
{
|
|
|
|
if (!str)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
while (*str) {
|
2015-09-18 22:29:56 +08:00
|
|
|
if (!strncmp(str, "on", 2)) {
|
2012-03-31 02:47:07 +08:00
|
|
|
disable_irq_remap = 0;
|
2015-09-18 22:29:56 +08:00
|
|
|
disable_irq_post = 0;
|
|
|
|
} else if (!strncmp(str, "off", 3)) {
|
2012-03-31 02:47:07 +08:00
|
|
|
disable_irq_remap = 1;
|
2015-09-18 22:29:56 +08:00
|
|
|
disable_irq_post = 1;
|
|
|
|
} else if (!strncmp(str, "nosid", 5))
|
2012-03-31 02:47:00 +08:00
|
|
|
disable_sourceid_checking = 1;
|
|
|
|
else if (!strncmp(str, "no_x2apic_optout", 16))
|
|
|
|
no_x2apic_optout = 1;
|
2015-09-18 22:29:56 +08:00
|
|
|
else if (!strncmp(str, "nopost", 6))
|
|
|
|
disable_irq_post = 1;
|
2012-03-31 02:47:00 +08:00
|
|
|
|
|
|
|
str += strcspn(str, ",");
|
|
|
|
while (*str == ',')
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-03-31 02:47:07 +08:00
|
|
|
early_param("intremap", setup_irqremap);
|
2012-03-31 02:47:00 +08:00
|
|
|
|
2013-04-17 04:38:32 +08:00
|
|
|
void set_irq_remapping_broken(void)
|
|
|
|
{
|
|
|
|
irq_remap_broken = 1;
|
|
|
|
}
|
|
|
|
|
2015-06-09 13:20:36 +08:00
|
|
|
bool irq_remapping_cap(enum irq_remap_cap cap)
|
|
|
|
{
|
|
|
|
if (!remap_ops || disable_irq_post)
|
2015-08-13 17:13:17 +08:00
|
|
|
return false;
|
2015-06-09 13:20:36 +08:00
|
|
|
|
|
|
|
return (remap_ops->capability & (1 << cap));
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(irq_remapping_cap);
|
|
|
|
|
2015-01-07 15:31:40 +08:00
|
|
|
int __init irq_remapping_prepare(void)
|
2012-03-31 02:47:00 +08:00
|
|
|
{
|
2012-03-31 02:47:07 +08:00
|
|
|
if (disable_irq_remap)
|
2015-01-07 15:31:40 +08:00
|
|
|
return -ENOSYS;
|
2012-03-31 02:47:00 +08:00
|
|
|
|
2015-01-07 15:31:41 +08:00
|
|
|
if (intel_irq_remap_ops.prepare() == 0)
|
|
|
|
remap_ops = &intel_irq_remap_ops;
|
|
|
|
else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
|
|
|
|
amd_iommu_irq_ops.prepare() == 0)
|
2015-01-07 15:31:28 +08:00
|
|
|
remap_ops = &amd_iommu_irq_ops;
|
2015-01-07 15:31:41 +08:00
|
|
|
else
|
|
|
|
return -ENOSYS;
|
|
|
|
|
|
|
|
return 0;
|
2012-03-31 02:47:00 +08:00
|
|
|
}
|
|
|
|
|
2012-03-31 02:47:07 +08:00
|
|
|
int __init irq_remapping_enable(void)
|
2012-03-31 02:47:00 +08:00
|
|
|
{
|
2012-09-26 18:44:35 +08:00
|
|
|
int ret;
|
|
|
|
|
2015-01-07 15:31:43 +08:00
|
|
|
if (!remap_ops->enable)
|
2012-03-31 02:47:00 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
2012-09-26 18:44:35 +08:00
|
|
|
ret = remap_ops->enable();
|
|
|
|
|
|
|
|
if (irq_remapping_enabled)
|
|
|
|
irq_remapping_modify_x86_ops();
|
|
|
|
|
|
|
|
return ret;
|
2012-03-31 02:47:00 +08:00
|
|
|
}
|
2012-03-31 02:47:01 +08:00
|
|
|
|
2012-03-31 02:47:07 +08:00
|
|
|
void irq_remapping_disable(void)
|
2012-03-31 02:47:01 +08:00
|
|
|
{
|
2015-01-07 15:31:43 +08:00
|
|
|
if (irq_remapping_enabled && remap_ops->disable)
|
|
|
|
remap_ops->disable();
|
2012-03-31 02:47:01 +08:00
|
|
|
}
|
|
|
|
|
2012-03-31 02:47:07 +08:00
|
|
|
int irq_remapping_reenable(int mode)
|
2012-03-31 02:47:01 +08:00
|
|
|
{
|
2015-01-07 15:31:43 +08:00
|
|
|
if (irq_remapping_enabled && remap_ops->reenable)
|
|
|
|
return remap_ops->reenable(mode);
|
2012-03-31 02:47:01 +08:00
|
|
|
|
2015-01-07 15:31:43 +08:00
|
|
|
return 0;
|
2012-03-31 02:47:01 +08:00
|
|
|
}
|
|
|
|
|
2012-03-31 02:47:07 +08:00
|
|
|
int __init irq_remap_enable_fault_handling(void)
|
2012-03-31 02:47:01 +08:00
|
|
|
{
|
2012-09-26 18:44:33 +08:00
|
|
|
if (!irq_remapping_enabled)
|
|
|
|
return 0;
|
|
|
|
|
2015-01-07 15:31:43 +08:00
|
|
|
if (!remap_ops->enable_faulting)
|
2012-03-31 02:47:01 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return remap_ops->enable_faulting();
|
|
|
|
}
|
2012-03-31 02:47:02 +08:00
|
|
|
|
2012-09-26 18:44:41 +08:00
|
|
|
void panic_if_irq_remap(const char *msg)
|
|
|
|
{
|
|
|
|
if (irq_remapping_enabled)
|
|
|
|
panic(msg);
|
|
|
|
}
|
2012-09-26 18:44:45 +08:00
|
|
|
|
irq_remapping: Introduce new interfaces to support hierarchical irqdomains
Introduce new interfaces for interrupt remapping drivers to support
hierarchical irqdomains:
1) irq_remapping_get_ir_irq_domain(): get irqdomain associated with an
interrupt remapping unit. IOAPIC/HPET drivers use this interface to
get parent interrupt remapping irqdomain.
2) irq_remapping_get_irq_domain(): get irqdomain for an IRQ allocation.
This is mainly used to support MSI irqdomain. We must build one MSI
irqdomain for each interrupt remapping unit. MSI driver calls this
interface to get MSI irqdomain associated with an IR irqdomain which
manages the PCI devices. In a further step we will store the irqdomain
pointer in the device struct to avoid this call in the irq allocation
path.
Architecture specific hooks:
1) arch_get_ir_parent_domain(): get parent irqdomain for IR irqdomain,
which is x86_vector_domain on x86 platforms.
2) arch_create_msi_irq_domain(): create an MSI irqdomain associated with
the interrupt remapping unit.
We also add following callbacks into struct irq_remap_ops:
struct irq_domain *(*get_ir_irq_domain)(struct irq_alloc_info *);
struct irq_domain *(*get_irq_domain)(struct irq_alloc_info *);
Once all clients of IR have been converted to the new hierarchical irqdomain
interfaces, we will:
1) Remove set_ioapic_entry, set_affinity, free_irq, compose_msi_msg,
msi_alloc_irq, msi_setup_irq, setup_hpet_msi from struct remap_osp
2) Remove setup_ioapic_remapped_entry, free_remapped_irq,
compose_remapped_msi_msg, setup_hpet_msi_remapped, setup_remapped_irq.
3) Simplify x86_io_apic_ops and x86_msi.
We can achieve a way clearer architecture with all these changes
applied.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Acked-by: Joerg Roedel <jroedel@suse.de>
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: 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-9-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2015-04-13 14:11:30 +08:00
|
|
|
/**
|
|
|
|
* irq_remapping_get_ir_irq_domain - Get the irqdomain associated with the IOMMU
|
|
|
|
* device serving request @info
|
|
|
|
* @info: interrupt allocation information, used to identify the IOMMU device
|
|
|
|
*
|
|
|
|
* It's used to get parent irqdomain for HPET and IOAPIC irqdomains.
|
|
|
|
* Returns pointer to IRQ domain, or NULL on failure.
|
|
|
|
*/
|
|
|
|
struct irq_domain *
|
|
|
|
irq_remapping_get_ir_irq_domain(struct irq_alloc_info *info)
|
|
|
|
{
|
|
|
|
if (!remap_ops || !remap_ops->get_ir_irq_domain)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return remap_ops->get_ir_irq_domain(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* irq_remapping_get_irq_domain - Get the irqdomain serving the request @info
|
|
|
|
* @info: interrupt allocation information, used to identify the IOMMU device
|
|
|
|
*
|
|
|
|
* There will be one PCI MSI/MSIX irqdomain associated with each interrupt
|
|
|
|
* remapping device, so this interface is used to retrieve the PCI MSI/MSIX
|
|
|
|
* irqdomain serving request @info.
|
|
|
|
* Returns pointer to IRQ domain, or NULL on failure.
|
|
|
|
*/
|
|
|
|
struct irq_domain *
|
|
|
|
irq_remapping_get_irq_domain(struct irq_alloc_info *info)
|
|
|
|
{
|
|
|
|
if (!remap_ops || !remap_ops->get_irq_domain)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return remap_ops->get_irq_domain(info);
|
|
|
|
}
|