Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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.com


Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 3cb96f0c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+8 −1
Original line number Diff line number Diff line
@@ -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;
+1 −5
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
#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 */
+75 −66
Original line number Diff line number Diff line
@@ -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,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
Loading