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
					
				
			
		
					 7 changed files with 103 additions and 82 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jiang Liu
				Jiang Liu