From: Jiang Liu <jiang.liu@linux.intel.com> To: Benjamin Herrenschmidt <benh@kernel.crashing.org>, Thomas Gleixner <tglx@linutronix.de>, Ingo Molnar <mingo@redhat.com>, "H. Peter Anvin" <hpa@zytor.com>, "Rafael J. Wysocki" <rjw@rjwysocki.net>, Bjorn Helgaas <bhelgaas@google.com>, Randy Dunlap <rdunlap@infradead.org>, Yinghai Lu <yinghai@kernel.org>, Borislav Petkov <bp@alien8.de>, Grant Likely <grant.likely@linaro.org>, Marc Zyngier <marc.zyngier@arm.com> Cc: Jiang Liu <jiang.liu@linux.intel.com>, Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>, Andrew Morton <akpm@linux-foundation.org>, Tony Luck <tony.luck@intel.com>, Joerg Roedel <joro@8bytes.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, x86@kernel.org, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [RFT v2 12/24] iommu/vt-d: Enhance Intel IR driver to suppport hierarchy irqdomain Date: Fri, 26 Sep 2014 22:02:13 +0800 [thread overview] Message-ID: <1411740145-30626-13-git-send-email-jiang.liu@linux.intel.com> (raw) In-Reply-To: <1411740145-30626-1-git-send-email-jiang.liu@linux.intel.com> Enhance Intel interrupt remapping driver to support hierarchy irqdomain, it will simplify the code eventually. It also implements intel_ir_chip to support stacked irq_chip. Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com> --- drivers/iommu/intel_irq_remapping.c | 357 +++++++++++++++++++++++++++++++++-- include/linux/intel-iommu.h | 4 + 2 files changed, 343 insertions(+), 18 deletions(-) diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 0c679369e08a..166a73a3b551 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -8,6 +8,7 @@ #include <linux/irq.h> #include <linux/intel-iommu.h> #include <linux/acpi.h> +#include <linux/irqdomain.h> #include <asm/io_apic.h> #include <asm/smp.h> #include <asm/cpu.h> @@ -31,12 +32,22 @@ struct hpet_scope { unsigned int devfn; }; +struct intel_ir_data { + struct irq_2_iommu irq_2_iommu; + struct irte irte_entry; + union { + struct msi_msg msi_entry; + struct IR_IO_APIC_route_entry ioapic_entry; + }; +}; + #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) #define IRTE_DEST(dest) ((x2apic_mode) ? dest : dest << 8) static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static struct hpet_scope ir_hpet[MAX_HPET_TBS]; static int ir_ioapic_num, ir_hpet_num; +static struct irq_domain_ops intel_ir_domain_ops; /* * Lock ordering: @@ -263,7 +274,7 @@ static int free_irte(int irq) unsigned long flags; int rc; - if (!irq_iommu) + if (!irq_iommu || irq_iommu->iommu == NULL) return -1; raw_spin_lock_irqsave(&irq_2_ir_lock, flags); @@ -481,36 +492,48 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu, int mode) struct page *pages; unsigned long *bitmap; - ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table), - GFP_ATOMIC); - - if (!iommu->ir_table) + ir_table = kzalloc(sizeof(struct ir_table), GFP_ATOMIC); + if (!ir_table) return -ENOMEM; pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, INTR_REMAP_PAGE_ORDER); - if (!pages) { pr_err("IR%d: failed to allocate pages of order %d\n", iommu->seq_id, INTR_REMAP_PAGE_ORDER); - kfree(iommu->ir_table); - return -ENOMEM; + goto out_free_table; } bitmap = kcalloc(BITS_TO_LONGS(INTR_REMAP_TABLE_ENTRIES), sizeof(long), GFP_ATOMIC); if (bitmap == NULL) { pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id); - __free_pages(pages, INTR_REMAP_PAGE_ORDER); - kfree(ir_table); - return -ENOMEM; + goto out_free_pages; + } + + iommu->ir_domain = irq_domain_add_linear(NULL, INTR_REMAP_TABLE_ENTRIES, + &intel_ir_domain_ops, iommu); + if (!iommu->ir_domain) { + pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id); + goto out_free_bitmap; } + iommu->ir_domain->parent = arch_get_ir_parent_domain(); + iommu->ir_msi_domain = arch_create_msi_irq_domain(iommu->ir_domain); ir_table->base = page_address(pages); ir_table->bitmap = bitmap; - + iommu->ir_table = ir_table; iommu_set_irq_remapping(iommu, mode); + return 0; + +out_free_bitmap: + kfree(bitmap); +out_free_pages: + __free_pages(pages, INTR_REMAP_PAGE_ORDER); +out_free_table: + kfree(ir_table); + return -ENOMEM; } /* @@ -1014,12 +1037,6 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irte irte; int err; - if (!config_enabled(CONFIG_SMP)) - return -EINVAL; - - if (!cpumask_intersects(mask, cpu_online_mask)) - return -EINVAL; - if (get_irte(irq, &irte)) return -EBUSY; @@ -1157,6 +1174,72 @@ static int intel_setup_hpet_msi(unsigned int irq, unsigned int id) return ret; } +static struct irq_domain *intel_get_ir_irq_domain(struct irq_alloc_info *info) +{ + struct intel_iommu *iommu = NULL; + + if (!info) + return NULL; + + switch (info->type) { + case X86_IRQ_ALLOC_TYPE_IOAPIC: + iommu = map_ioapic_to_ir(info->ioapic_id); + break; + case X86_IRQ_ALLOC_TYPE_HPET: + iommu = map_hpet_to_ir(info->hpet_id); + break; + case X86_IRQ_ALLOC_TYPE_MSI: + case X86_IRQ_ALLOC_TYPE_MSIX: + iommu = map_dev_to_ir(info->msi_dev); + break; + default: + BUG_ON(1); + break; + } + + return iommu ? iommu->ir_domain : NULL; +} + +static struct irq_domain *intel_get_irq_domain(struct irq_alloc_info *info) +{ + struct intel_iommu *iommu; + + if (!info) + return NULL; + + switch (info->type) { + case X86_IRQ_ALLOC_TYPE_MSI: + case X86_IRQ_ALLOC_TYPE_MSIX: + iommu = map_dev_to_ir(info->msi_dev); + if (iommu) + return iommu->ir_msi_domain; + break; + default: + break; + } + + return NULL; +} + +static int intel_get_ioapic_entry(struct irq_data *irq_data, + struct IR_IO_APIC_route_entry *entry) +{ + struct intel_ir_data *ir_data = irq_data->chip_data; + + *entry = ir_data->ioapic_entry; + + return 0; +} + +static int intel_get_msi_entry(struct irq_data *irq_data, struct msi_msg *msg) +{ + struct intel_ir_data *ir_data = irq_data->chip_data; + + *msg = ir_data->msi_entry; + + return 0; +} + struct irq_remap_ops intel_irq_remap_ops = { .supported = intel_irq_remapping_supported, .prepare = dmar_table_init, @@ -1171,4 +1254,242 @@ struct irq_remap_ops intel_irq_remap_ops = { .msi_alloc_irq = intel_msi_alloc_irq, .msi_setup_irq = intel_msi_setup_irq, .setup_hpet_msi = intel_setup_hpet_msi, + .get_ir_irq_domain = intel_get_ir_irq_domain, + .get_irq_domain = intel_get_irq_domain, + .get_ioapic_entry = intel_get_ioapic_entry, + .get_msi_entry = intel_get_msi_entry, +}; + +/* + * Migrate the IO-APIC irq in the presence of intr-remapping. + * + * For both level and edge triggered, irq migration is a simple atomic + * update(of vector and cpu destination) of IRTE and flush the hardware cache. + * + * For level triggered, we eliminate the io-apic RTE modification (with the + * updated vector information), by using a virtual vector (io-apic pin number). + * Real vector that is used for interrupting cpu will be coming from + * the interrupt-remapping table entry. + * + * As the migration is a simple atomic update of IRTE, the same mechanism + * is used to migrate MSI irq's in the presence of interrupt-remapping. + */ +static int +intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask, + bool force) +{ + struct intel_ir_data *ir_data = data->chip_data; + struct irte *irte = &ir_data->irte_entry; + struct irq_cfg *cfg = irqd_cfg(data); + struct irq_data *parent = data->parent_data; + int ret; + + ret = parent->chip->irq_set_affinity(parent, mask, force); + if (ret < 0) + return ret; + + /* + * Atomically updates the IRTE with the new destination, vector + * and flushes the interrupt entry cache. + */ + irte->vector = cfg->vector; + irte->dest_id = IRTE_DEST(cfg->dest_apicid); + modify_irte(&ir_data->irq_2_iommu, irte); + + /* + * After this point, all the interrupts will start arriving + * at the new destination. So, time to cleanup the previous + * vector allocation. + */ + if (cfg->move_in_progress) + send_cleanup_vector(cfg); + + return ret; +} + +static struct irq_chip intel_ir_chip = { + .irq_ack = ir_ack_apic_edge, + .irq_set_affinity = intel_ir_set_affinity, +}; + +static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data, + struct irq_cfg *irq_cfg, + struct irq_alloc_info *info, + int index, int sub_handle) +{ + struct irte *irte = &data->irte_entry; + struct IR_IO_APIC_route_entry *entry = &data->ioapic_entry; + struct msi_msg *msg = &data->msi_entry; + + prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid); + switch (info->type) { + case X86_IRQ_ALLOC_TYPE_IOAPIC: + /* Set source-id of interrupt request */ + set_ioapic_sid(irte, info->ioapic_id); + apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n", + info->ioapic_id, irte->present, irte->fpd, + irte->dst_mode, irte->redir_hint, + irte->trigger_mode, irte->dlvry_mode, + irte->avail, irte->vector, irte->dest_id, + irte->sid, irte->sq, irte->svt); + + memset(entry, 0, sizeof(*entry)); + entry->index2 = (index >> 15) & 0x1; + entry->zero = 0; + entry->format = 1; + entry->index = (index & 0x7fff); + /* + * IO-APIC RTE will be configured with virtual vector. + * irq handler will do the explicit EOI to the io-apic. + */ + entry->vector = info->ioapic_pin; + entry->mask = 0; /* enable IRQ */ + entry->trigger = info->ioapic_trigger; + entry->polarity = info->ioapic_polarity; + if (info->ioapic_trigger) + entry->mask = 1; /* Mask level triggered irqs. */ + break; + + case X86_IRQ_ALLOC_TYPE_HPET: + case X86_IRQ_ALLOC_TYPE_MSI: + case X86_IRQ_ALLOC_TYPE_MSIX: + if (info->type == X86_IRQ_ALLOC_TYPE_HPET) + set_hpet_sid(irte, info->hpet_id); + else + set_msi_sid(irte, info->msi_dev); + + msg->address_hi = MSI_ADDR_BASE_HI; + msg->data = sub_handle; + msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT | + MSI_ADDR_IR_SHV | + MSI_ADDR_IR_INDEX1(index) | + MSI_ADDR_IR_INDEX2(index); + break; + + default: + BUG_ON(1); + break; + } +} + +static void intel_free_irq_resources(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *irq_data; + struct intel_ir_data *data; + struct irq_2_iommu *irq_iommu; + unsigned long flags; + int i; + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_domain_get_irq_data(domain, virq + i); + if (irq_data && irq_data->chip_data) { + data = irq_data->chip_data; + irq_iommu = &data->irq_2_iommu; + raw_spin_lock_irqsave(&irq_2_ir_lock, flags); + clear_entries(irq_iommu); + raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags); + irq_domain_reset_irq_data(irq_data); + kfree(data); + } + } +} + +static int intel_irq_remapping_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + struct intel_iommu *iommu = domain->host_data; + struct irq_alloc_info *info = arg; + struct intel_ir_data *data; + struct irq_data *irq_data; + struct irq_cfg *irq_cfg; + int i, ret, index; + + if (!info || !iommu) + return -EINVAL; + if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI && + info->type != X86_IRQ_ALLOC_TYPE_MSIX) + return -EINVAL; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret < 0) + return ret; + + ret = -ENOMEM; + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out_free_parent; + + down_read(&dmar_global_lock); + index = alloc_irte(iommu, virq, &data->irq_2_iommu, nr_irqs); + up_read(&dmar_global_lock); + if (index < 0) { + pr_warn("Failed to allocate IRTE\n"); + kfree(data); + goto out_free_parent; + } + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_domain_get_irq_data(domain, virq + i); + irq_cfg = irqd_cfg(irq_data); + if (!irq_data || !irq_cfg) { + ret = -EINVAL; + goto out_free_data; + } + + if (i > 0) { + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out_free_data; + } + irq_data->hwirq = (index << 16) + i; + irq_data->chip_data = data; + irq_data->chip = &intel_ir_chip; + intel_irq_remapping_prepare_irte(data, irq_cfg, info, index, i); + irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT); + } + return 0; + +out_free_data: + intel_free_irq_resources(domain, virq, i); +out_free_parent: + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + return ret; +} + +static void intel_irq_remapping_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + intel_free_irq_resources(domain, virq, nr_irqs); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +static int intel_irq_remapping_activate(struct irq_domain *domain, + struct irq_data *irq_data) +{ + struct intel_ir_data *data = irq_data->chip_data; + + modify_irte(&data->irq_2_iommu, &data->irte_entry); + + return 0; +} + +static int intel_irq_remapping_deactivate(struct irq_domain *domain, + struct irq_data *irq_data) +{ + struct intel_ir_data *data = irq_data->chip_data; + struct irte entry; + + memset(&entry, 0, sizeof(entry)); + modify_irte(&data->irq_2_iommu, &entry); + + return 0; +} + +static struct irq_domain_ops intel_ir_domain_ops = { + .alloc = intel_irq_remapping_alloc, + .free = intel_irq_remapping_free, + .activate = intel_irq_remapping_activate, + .deactivate = intel_irq_remapping_deactivate, }; diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index a65208a8fe18..ecaf3a937845 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -286,6 +286,8 @@ struct q_inval { #define INTR_REMAP_TABLE_ENTRIES 65536 +struct irq_domain; + struct ir_table { struct irte *base; unsigned long *bitmap; @@ -335,6 +337,8 @@ struct intel_iommu { #ifdef CONFIG_IRQ_REMAP struct ir_table *ir_table; /* Interrupt remapping info */ + struct irq_domain *ir_domain; + struct irq_domain *ir_msi_domain; #endif struct device *iommu_dev; /* IOMMU-sysfs device */ int node; -- 1.7.10.4
WARNING: multiple messages have this Message-ID (diff)
From: jiang.liu@linux.intel.com (Jiang Liu) To: linux-arm-kernel@lists.infradead.org Subject: [RFT v2 12/24] iommu/vt-d: Enhance Intel IR driver to suppport hierarchy irqdomain Date: Fri, 26 Sep 2014 22:02:13 +0800 [thread overview] Message-ID: <1411740145-30626-13-git-send-email-jiang.liu@linux.intel.com> (raw) In-Reply-To: <1411740145-30626-1-git-send-email-jiang.liu@linux.intel.com> Enhance Intel interrupt remapping driver to support hierarchy irqdomain, it will simplify the code eventually. It also implements intel_ir_chip to support stacked irq_chip. Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com> --- drivers/iommu/intel_irq_remapping.c | 357 +++++++++++++++++++++++++++++++++-- include/linux/intel-iommu.h | 4 + 2 files changed, 343 insertions(+), 18 deletions(-) diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 0c679369e08a..166a73a3b551 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -8,6 +8,7 @@ #include <linux/irq.h> #include <linux/intel-iommu.h> #include <linux/acpi.h> +#include <linux/irqdomain.h> #include <asm/io_apic.h> #include <asm/smp.h> #include <asm/cpu.h> @@ -31,12 +32,22 @@ struct hpet_scope { unsigned int devfn; }; +struct intel_ir_data { + struct irq_2_iommu irq_2_iommu; + struct irte irte_entry; + union { + struct msi_msg msi_entry; + struct IR_IO_APIC_route_entry ioapic_entry; + }; +}; + #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) #define IRTE_DEST(dest) ((x2apic_mode) ? dest : dest << 8) static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static struct hpet_scope ir_hpet[MAX_HPET_TBS]; static int ir_ioapic_num, ir_hpet_num; +static struct irq_domain_ops intel_ir_domain_ops; /* * Lock ordering: @@ -263,7 +274,7 @@ static int free_irte(int irq) unsigned long flags; int rc; - if (!irq_iommu) + if (!irq_iommu || irq_iommu->iommu == NULL) return -1; raw_spin_lock_irqsave(&irq_2_ir_lock, flags); @@ -481,36 +492,48 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu, int mode) struct page *pages; unsigned long *bitmap; - ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table), - GFP_ATOMIC); - - if (!iommu->ir_table) + ir_table = kzalloc(sizeof(struct ir_table), GFP_ATOMIC); + if (!ir_table) return -ENOMEM; pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, INTR_REMAP_PAGE_ORDER); - if (!pages) { pr_err("IR%d: failed to allocate pages of order %d\n", iommu->seq_id, INTR_REMAP_PAGE_ORDER); - kfree(iommu->ir_table); - return -ENOMEM; + goto out_free_table; } bitmap = kcalloc(BITS_TO_LONGS(INTR_REMAP_TABLE_ENTRIES), sizeof(long), GFP_ATOMIC); if (bitmap == NULL) { pr_err("IR%d: failed to allocate bitmap\n", iommu->seq_id); - __free_pages(pages, INTR_REMAP_PAGE_ORDER); - kfree(ir_table); - return -ENOMEM; + goto out_free_pages; + } + + iommu->ir_domain = irq_domain_add_linear(NULL, INTR_REMAP_TABLE_ENTRIES, + &intel_ir_domain_ops, iommu); + if (!iommu->ir_domain) { + pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id); + goto out_free_bitmap; } + iommu->ir_domain->parent = arch_get_ir_parent_domain(); + iommu->ir_msi_domain = arch_create_msi_irq_domain(iommu->ir_domain); ir_table->base = page_address(pages); ir_table->bitmap = bitmap; - + iommu->ir_table = ir_table; iommu_set_irq_remapping(iommu, mode); + return 0; + +out_free_bitmap: + kfree(bitmap); +out_free_pages: + __free_pages(pages, INTR_REMAP_PAGE_ORDER); +out_free_table: + kfree(ir_table); + return -ENOMEM; } /* @@ -1014,12 +1037,6 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irte irte; int err; - if (!config_enabled(CONFIG_SMP)) - return -EINVAL; - - if (!cpumask_intersects(mask, cpu_online_mask)) - return -EINVAL; - if (get_irte(irq, &irte)) return -EBUSY; @@ -1157,6 +1174,72 @@ static int intel_setup_hpet_msi(unsigned int irq, unsigned int id) return ret; } +static struct irq_domain *intel_get_ir_irq_domain(struct irq_alloc_info *info) +{ + struct intel_iommu *iommu = NULL; + + if (!info) + return NULL; + + switch (info->type) { + case X86_IRQ_ALLOC_TYPE_IOAPIC: + iommu = map_ioapic_to_ir(info->ioapic_id); + break; + case X86_IRQ_ALLOC_TYPE_HPET: + iommu = map_hpet_to_ir(info->hpet_id); + break; + case X86_IRQ_ALLOC_TYPE_MSI: + case X86_IRQ_ALLOC_TYPE_MSIX: + iommu = map_dev_to_ir(info->msi_dev); + break; + default: + BUG_ON(1); + break; + } + + return iommu ? iommu->ir_domain : NULL; +} + +static struct irq_domain *intel_get_irq_domain(struct irq_alloc_info *info) +{ + struct intel_iommu *iommu; + + if (!info) + return NULL; + + switch (info->type) { + case X86_IRQ_ALLOC_TYPE_MSI: + case X86_IRQ_ALLOC_TYPE_MSIX: + iommu = map_dev_to_ir(info->msi_dev); + if (iommu) + return iommu->ir_msi_domain; + break; + default: + break; + } + + return NULL; +} + +static int intel_get_ioapic_entry(struct irq_data *irq_data, + struct IR_IO_APIC_route_entry *entry) +{ + struct intel_ir_data *ir_data = irq_data->chip_data; + + *entry = ir_data->ioapic_entry; + + return 0; +} + +static int intel_get_msi_entry(struct irq_data *irq_data, struct msi_msg *msg) +{ + struct intel_ir_data *ir_data = irq_data->chip_data; + + *msg = ir_data->msi_entry; + + return 0; +} + struct irq_remap_ops intel_irq_remap_ops = { .supported = intel_irq_remapping_supported, .prepare = dmar_table_init, @@ -1171,4 +1254,242 @@ struct irq_remap_ops intel_irq_remap_ops = { .msi_alloc_irq = intel_msi_alloc_irq, .msi_setup_irq = intel_msi_setup_irq, .setup_hpet_msi = intel_setup_hpet_msi, + .get_ir_irq_domain = intel_get_ir_irq_domain, + .get_irq_domain = intel_get_irq_domain, + .get_ioapic_entry = intel_get_ioapic_entry, + .get_msi_entry = intel_get_msi_entry, +}; + +/* + * Migrate the IO-APIC irq in the presence of intr-remapping. + * + * For both level and edge triggered, irq migration is a simple atomic + * update(of vector and cpu destination) of IRTE and flush the hardware cache. + * + * For level triggered, we eliminate the io-apic RTE modification (with the + * updated vector information), by using a virtual vector (io-apic pin number). + * Real vector that is used for interrupting cpu will be coming from + * the interrupt-remapping table entry. + * + * As the migration is a simple atomic update of IRTE, the same mechanism + * is used to migrate MSI irq's in the presence of interrupt-remapping. + */ +static int +intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask, + bool force) +{ + struct intel_ir_data *ir_data = data->chip_data; + struct irte *irte = &ir_data->irte_entry; + struct irq_cfg *cfg = irqd_cfg(data); + struct irq_data *parent = data->parent_data; + int ret; + + ret = parent->chip->irq_set_affinity(parent, mask, force); + if (ret < 0) + return ret; + + /* + * Atomically updates the IRTE with the new destination, vector + * and flushes the interrupt entry cache. + */ + irte->vector = cfg->vector; + irte->dest_id = IRTE_DEST(cfg->dest_apicid); + modify_irte(&ir_data->irq_2_iommu, irte); + + /* + * After this point, all the interrupts will start arriving + * at the new destination. So, time to cleanup the previous + * vector allocation. + */ + if (cfg->move_in_progress) + send_cleanup_vector(cfg); + + return ret; +} + +static struct irq_chip intel_ir_chip = { + .irq_ack = ir_ack_apic_edge, + .irq_set_affinity = intel_ir_set_affinity, +}; + +static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data, + struct irq_cfg *irq_cfg, + struct irq_alloc_info *info, + int index, int sub_handle) +{ + struct irte *irte = &data->irte_entry; + struct IR_IO_APIC_route_entry *entry = &data->ioapic_entry; + struct msi_msg *msg = &data->msi_entry; + + prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid); + switch (info->type) { + case X86_IRQ_ALLOC_TYPE_IOAPIC: + /* Set source-id of interrupt request */ + set_ioapic_sid(irte, info->ioapic_id); + apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n", + info->ioapic_id, irte->present, irte->fpd, + irte->dst_mode, irte->redir_hint, + irte->trigger_mode, irte->dlvry_mode, + irte->avail, irte->vector, irte->dest_id, + irte->sid, irte->sq, irte->svt); + + memset(entry, 0, sizeof(*entry)); + entry->index2 = (index >> 15) & 0x1; + entry->zero = 0; + entry->format = 1; + entry->index = (index & 0x7fff); + /* + * IO-APIC RTE will be configured with virtual vector. + * irq handler will do the explicit EOI to the io-apic. + */ + entry->vector = info->ioapic_pin; + entry->mask = 0; /* enable IRQ */ + entry->trigger = info->ioapic_trigger; + entry->polarity = info->ioapic_polarity; + if (info->ioapic_trigger) + entry->mask = 1; /* Mask level triggered irqs. */ + break; + + case X86_IRQ_ALLOC_TYPE_HPET: + case X86_IRQ_ALLOC_TYPE_MSI: + case X86_IRQ_ALLOC_TYPE_MSIX: + if (info->type == X86_IRQ_ALLOC_TYPE_HPET) + set_hpet_sid(irte, info->hpet_id); + else + set_msi_sid(irte, info->msi_dev); + + msg->address_hi = MSI_ADDR_BASE_HI; + msg->data = sub_handle; + msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT | + MSI_ADDR_IR_SHV | + MSI_ADDR_IR_INDEX1(index) | + MSI_ADDR_IR_INDEX2(index); + break; + + default: + BUG_ON(1); + break; + } +} + +static void intel_free_irq_resources(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *irq_data; + struct intel_ir_data *data; + struct irq_2_iommu *irq_iommu; + unsigned long flags; + int i; + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_domain_get_irq_data(domain, virq + i); + if (irq_data && irq_data->chip_data) { + data = irq_data->chip_data; + irq_iommu = &data->irq_2_iommu; + raw_spin_lock_irqsave(&irq_2_ir_lock, flags); + clear_entries(irq_iommu); + raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags); + irq_domain_reset_irq_data(irq_data); + kfree(data); + } + } +} + +static int intel_irq_remapping_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + struct intel_iommu *iommu = domain->host_data; + struct irq_alloc_info *info = arg; + struct intel_ir_data *data; + struct irq_data *irq_data; + struct irq_cfg *irq_cfg; + int i, ret, index; + + if (!info || !iommu) + return -EINVAL; + if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI && + info->type != X86_IRQ_ALLOC_TYPE_MSIX) + return -EINVAL; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret < 0) + return ret; + + ret = -ENOMEM; + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out_free_parent; + + down_read(&dmar_global_lock); + index = alloc_irte(iommu, virq, &data->irq_2_iommu, nr_irqs); + up_read(&dmar_global_lock); + if (index < 0) { + pr_warn("Failed to allocate IRTE\n"); + kfree(data); + goto out_free_parent; + } + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_domain_get_irq_data(domain, virq + i); + irq_cfg = irqd_cfg(irq_data); + if (!irq_data || !irq_cfg) { + ret = -EINVAL; + goto out_free_data; + } + + if (i > 0) { + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out_free_data; + } + irq_data->hwirq = (index << 16) + i; + irq_data->chip_data = data; + irq_data->chip = &intel_ir_chip; + intel_irq_remapping_prepare_irte(data, irq_cfg, info, index, i); + irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT); + } + return 0; + +out_free_data: + intel_free_irq_resources(domain, virq, i); +out_free_parent: + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + return ret; +} + +static void intel_irq_remapping_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + intel_free_irq_resources(domain, virq, nr_irqs); + irq_domain_free_irqs_parent(domain, virq, nr_irqs); +} + +static int intel_irq_remapping_activate(struct irq_domain *domain, + struct irq_data *irq_data) +{ + struct intel_ir_data *data = irq_data->chip_data; + + modify_irte(&data->irq_2_iommu, &data->irte_entry); + + return 0; +} + +static int intel_irq_remapping_deactivate(struct irq_domain *domain, + struct irq_data *irq_data) +{ + struct intel_ir_data *data = irq_data->chip_data; + struct irte entry; + + memset(&entry, 0, sizeof(entry)); + modify_irte(&data->irq_2_iommu, &entry); + + return 0; +} + +static struct irq_domain_ops intel_ir_domain_ops = { + .alloc = intel_irq_remapping_alloc, + .free = intel_irq_remapping_free, + .activate = intel_irq_remapping_activate, + .deactivate = intel_irq_remapping_deactivate, }; diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index a65208a8fe18..ecaf3a937845 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -286,6 +286,8 @@ struct q_inval { #define INTR_REMAP_TABLE_ENTRIES 65536 +struct irq_domain; + struct ir_table { struct irte *base; unsigned long *bitmap; @@ -335,6 +337,8 @@ struct intel_iommu { #ifdef CONFIG_IRQ_REMAP struct ir_table *ir_table; /* Interrupt remapping info */ + struct irq_domain *ir_domain; + struct irq_domain *ir_msi_domain; #endif struct device *iommu_dev; /* IOMMU-sysfs device */ int node; -- 1.7.10.4
next prev parent reply other threads:[~2014-09-26 14:01 UTC|newest] Thread overview: 83+ messages / expand[flat|nested] mbox.gz Atom feed top 2014-09-26 14:02 [RFT Part2 v2 00/24] Enable hierarchy irqdomian on x86 platforms Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 01/24] irqdomain: Introduce new interfaces to support hierarchy irqdomains Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-29 12:22 ` Abel 2014-09-29 12:22 ` Abel 2014-09-29 12:22 ` Abel 2014-09-29 15:53 ` Thomas Gleixner 2014-09-29 15:53 ` Thomas Gleixner 2014-09-30 10:56 ` Abel 2014-09-30 10:56 ` Abel 2014-09-30 10:56 ` Abel 2014-09-30 21:49 ` Thomas Gleixner 2014-09-30 21:49 ` Thomas Gleixner 2014-10-07 1:50 ` Jiang Liu 2014-10-07 1:50 ` Jiang Liu 2014-10-07 1:26 ` Jiang Liu 2014-10-07 1:26 ` Jiang Liu 2014-10-01 14:23 ` Joe.C 2014-10-01 14:23 ` Joe.C 2014-10-22 7:24 ` Jiang Liu 2014-10-22 7:24 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 02/24] genirq: Introduce helper functions to support stacked irq_chip Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 03/24] x86, irq: Save destination CPU ID in irq_cfg Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 04/24] x86, irq: Use hierarchy irqdomain to manage CPU interrupt vectors Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 05/24] x86, hpet: Use new irqdomain interfaces to allocate/free IRQ Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 06/24] x86, MSI: " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 07/24] x86, uv: " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 08/24] x86, htirq: " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 09/24] x86, dmar: " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 10/24] x86: irq_remapping: Introduce new interfaces to support hierarchy irqdomain Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 11/24] iommu/vt-d: Change prototypes to prepare for enabling " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu [this message] 2014-09-26 14:02 ` [RFT v2 12/24] iommu/vt-d: Enhance Intel IR driver to suppport " Jiang Liu 2014-09-26 14:02 ` [RFT v2 13/24] iommu/amd: Enhance AMD " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 14/24] x86, hpet: Enhance HPET IRQ to support " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 15/24] x86, MSI: Use hierarchy irqdomain to manage MSI interrupts Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 16/24] x86, irq: Directly call native_compose_msi_msg() for DMAR IRQ Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 17/24] iommu/vt-d: Clean up unused MSI related code Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 18/24] iommu/amd: " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 19/24] x86: irq_remapping: " Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 20/24] x86, irq: Clean up unused MSI related code and interfaces Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 21/24] iommu/vt-d: Refine the interfaces to create IRQ for DMAR unit Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 22/24] x86, irq: Use hierarchy irqdomain to manage DMAR interrupts Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 23/24] x86, htirq: Use hierarchy irqdomain to manage Hypertransport interrupts Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:02 ` [RFT v2 24/24] x86, uv: Use hierarchy irqdomain to manage UV interrupts Jiang Liu 2014-09-26 14:02 ` Jiang Liu 2014-09-26 14:29 ` [RFT Part2 v2 00/24] Enable hierarchy irqdomian on x86 platforms Borislav Petkov 2014-09-26 14:29 ` Borislav Petkov 2014-09-26 16:30 ` Aravind Gopalakrishnan 2014-09-26 16:30 ` Aravind Gopalakrishnan 2014-09-26 16:30 ` Aravind Gopalakrishnan 2014-09-28 11:05 ` Borislav Petkov 2014-09-28 11:05 ` Borislav Petkov 2014-09-28 11:15 ` Jiang Liu 2014-09-28 11:15 ` Jiang Liu
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1411740145-30626-13-git-send-email-jiang.liu@linux.intel.com \ --to=jiang.liu@linux.intel.com \ --cc=akpm@linux-foundation.org \ --cc=benh@kernel.crashing.org \ --cc=bhelgaas@google.com \ --cc=bp@alien8.de \ --cc=grant.likely@linaro.org \ --cc=gregkh@linuxfoundation.org \ --cc=hpa@zytor.com \ --cc=joro@8bytes.org \ --cc=konrad.wilk@oracle.com \ --cc=linux-acpi@vger.kernel.org \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-pci@vger.kernel.org \ --cc=marc.zyngier@arm.com \ --cc=mingo@redhat.com \ --cc=rdunlap@infradead.org \ --cc=rjw@rjwysocki.net \ --cc=tglx@linutronix.de \ --cc=tony.luck@intel.com \ --cc=x86@kernel.org \ --cc=yinghai@kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.