Enable support of IOAPIC hotplug on x86 platforms
Subsystems (4)
v8 cover Enable support of IOAPIC hotplug on x86 platforms
This patch set enhances IOAPIC core and ACPI drivers to support IOAPIC hotplug on x86 platforms. It's based on v3.18-rc2 at
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
You may pull it from
https://github.com/jiangliu/linux.git ioapic/hotplug_v8
We have pick up several patches from Yinghai's original IOAPIC hotplug patch set and reimplemented IOAPIC driver as an ACPI driver instead of a PCI driver.
It has been tested on a 4-socket Intel SDV with socket hot-addition capability. Any suggestions are welcomed!
Patch 1-3 are bugfixes against v3.17 and should target v3.18. Patch 1 has been merged into tip/x86/urgent.
Patch 4-7 are bugfixes and enhancements to ACPI subsystem Patch 8 killes PCI IOAPIC driver
Patch 9-17 enhances IOAPIC core to support IOAPIC hotplug Patch 18 reimplements ACPI IOAPIC driver and enables IOAPIC hotplug
V7-V8:
1) Use mutex instead of rwsem to protect IOAPIC from hotplug V6->V7:
1) Rebase to v3.18-rc2
2) Three bugfixes for v3.18V5->V6:
1) Rebase to the latest v3.17-16
2) Minor fixes for commentsV4->V5:
1) Fix a building error
2) Don't rename processor_core.c as apic_id.c
3) Improve comments
4) Reorder patches to get rid of some temporary code V3->V4:
1) Fix a bug in manage IOAPIC reference count
2) Rebase to v3.17-rc2
3) Refine commit messagesV2->V3:
1) Refine ACPI resource walk functions for PCI root bus and IOAPIC
2) Improve commit messages
3) Reorder patch order for better maintenence
Jiang Liu (15):
x86: ACPI: Do not translate GSI number if IOAPIC is disabled x86, intel-mid: Create IRQs for APB timers and RTC timers x86, irq, ACPI: Return IRQ instead of GSI in mp_register_gsi() x86, PCI, ACPI: Kill private function resource_to_addr() in
arch/x86/pci/acpi.c
ACPI: Correct return value of acpi_dev_resource_address_space()
ACPI: Fix minor syntax issues in processor_core.c
PCI: Remove PCI ioapic driver
x86, irq: Remove __init marker for functions will be used by IOAPIC
hotplug
x86, irq: Keep balance of IOAPIC pin reference count x86, irq: Refine mp_register_ioapic() to prepare for IOAPIC hotplug x86, irq, ACPI: Introduce a rwsem to protect IOAPIC operations from
hotplug
x86, irq, ACPI: Implement interface to support ACPI based IOAPIC
hot-addition
x86, irq, ACPI: Implement interfaces to support ACPI based IOAPIC
hot-removal
x86, irq: Introduce helper to check whether an IOAPIC has been
registered
x86, irq, ACPI: Implement ACPI driver to support IOAPIC hotplug
Yinghai Lu (3):
ACPI: Add interfaces to parse IOAPIC ID for IOAPIC hotplug x86, irq: Split out alloc_ioapic_save_registers() x86, irq: Prefer assigned ID in APIC ID register for x86_64
arch/x86/include/asm/io_apic.h | 6 +- arch/x86/kernel/acpi/boot.c | 102 ++++++++++++++-- arch/x86/kernel/apb_timer.c | 2 - arch/x86/kernel/apic/io_apic.c | 234 +++++++++++++++++++++++++++++------- arch/x86/pci/acpi.c | 144 +++++++++-------------- arch/x86/pci/intel_mid_pci.c | 10 +- arch/x86/pci/irq.c | 7 +- arch/x86/platform/intel-mid/sfi.c | 2 + drivers/acpi/Kconfig | 6 + drivers/acpi/Makefile | 1 + drivers/acpi/internal.h | 7 ++ drivers/acpi/ioapic.c | 235 +++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_irq.c | 11 +- drivers/acpi/pci_root.c | 3 + drivers/acpi/processor_core.c | 131 ++++++++++++++++++--- drivers/acpi/resource.c | 2 +- drivers/pci/Kconfig | 7 -- drivers/pci/Makefile | 2 - drivers/pci/ioapic.c | 121 ------------------- include/linux/acpi.h | 5 + include/linux/pci.h | 1 + 21 files changed, 739 insertions(+), 300 deletions(-) create mode 100644 drivers/acpi/ioapic.c delete mode 100644 drivers/pci/ioapic.c
Signature · 1 line
1.7.10.4
PATCH v8 1/18 x86: ACPI: Do not translate GSI number if IOAPIC is disabled
When IOAPIC is disabled, acpi_gsi_to_irq() should return gsi directly instead of calling mp_map_gsi_to_irq() to translate gsi to IRQ by IOAPIC. It fixes https://bugzilla.kernel.org/show_bug.cgi?id=84381.
This regression was introduced with commit 6b9fb7082409 "x86, ACPI,
irq: Consolidate algorithm of mapping (ioapic, pin) to IRQ number"
--- arch/x86/kernel/acpi/boot.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
arch/x86/kernel/acpi/boot.c +9 -4
@@ -604,14 +604,19 @@ void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger)int acpi_gsi_to_irq (u32 gsi,unsigned int *irqp) { -int irq =mp_map_gsi_to_irq (gsi,IOAPIC_MAP_ALLOC |IOAPIC_MAP_CHECK ); +int irq; -if (irq >=0 ) { +if (acpi_irq_model ==ACPI_IRQ_MODEL_PIC ) { + *irqp = gsi; + }else { + irq =mp_map_gsi_to_irq (gsi, +IOAPIC_MAP_ALLOC |IOAPIC_MAP_CHECK ); +if (irq <0 ) +return -1 ; *irqp = irq; -return 0 ; } -return -1 ; +return 0 ; }EXPORT_SYMBOL_GPL (acpi_gsi_to_irq);
Signature · 1 line
1.7.10.4
PATCH v8 2/18 x86, intel-mid: Create IRQs for APB timers and RTC timers
In reply to Jiang LiuIntel MID platforms has no legacy interrupts, so no IRQ descriptors preallocated. We need to call mp_map_gsi_to_irq() to create IRQ descriptors for APB timers and RTC timers, otherwise it may cause invalid memory access as:
[ 0.116839] BUG: unable to handle kernel NULL pointer dereference at 0000003a [ 0.123803] IP: [<c1071c0e>] setup_irq+0xf/0x4d
--- arch/x86/kernel/apb_timer.c | 2 -- arch/x86/platform/intel-mid/sfi.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-)
arch/x86/kernel/apb_timer.c +0 -2
@@ -185,8 +185,6 @@ static void apbt_setup_irq(struct apbt_dev *adev)irq_modify_status (adev->irq,0 ,IRQ_MOVE_PCNTXT );irq_set_affinity (adev->irq,cpumask_of (adev->cpu)); -/* APB timer irqs are set up as mp_irqs, timer is edge type */ -__irq_set_handler (adev->irq, handle_edge_irq,0 ,"edge" ); }/* Should be called with per cpu */
arch/x86/platform/intel-mid/sfi.c +2 -0
@@ -106,6 +106,7 @@ int __init sfi_parse_mtmr(struct sfi_table_header *table) mp_irq.dstapic =MP_APIC_ALL ; mp_irq.dstirq = pentry->irq;mp_save_irq (&mp_irq); +mp_map_gsi_to_irq (pentry->irq,IOAPIC_MAP_ALLOC ); }return 0 ;
@@ -176,6 +177,7 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table) mp_irq.dstapic =MP_APIC_ALL ; mp_irq.dstirq = pentry->irq;mp_save_irq (&mp_irq); +mp_map_gsi_to_irq (pentry->irq,IOAPIC_MAP_ALLOC ); }return 0 ; }
Signature · 1 line
1.7.10.4
PATCH v8 3/18 x86, irq, ACPI: Return IRQ instead of GSI in mp_register_gsi()
In reply to Jiang LiuFunction mp_register_gsi() returns blindly the GSI number for the ACPI SCI interrupt. That causes a regression when the GSI for ACPI SCI is shared with other devices.
The regression was caused by commit 84245af7297ced9e8fe "x86, irq, ACPI:
Change __acpi_register_gsi to return IRQ number instead of GSI" and exposed on a SuperMicro system, which shares one GSI between ACPI SCI and PCI device, with following failure:
http://sourceforge.net/p/linux1394/mailman/linux1394-user/?viewmonth=201410
[ 0.000000] ACPI: INT_SRC_OVR (bus 0 bus_irq 9 global_irq 20 low level) [ 2.699224] firewire_ohci 0000:06:00.0: failed to allocate interrupt
20
Return mp_map_gsi_to_irq(gsi, 0) instead of the GSI number.
--- arch/x86/kernel/acpi/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/acpi/boot.c +1 -1
@@ -397,7 +397,7 @@ static int mp_register_gsi(struct device *dev, u32 gsi, int trigger,/* Don't set up the ACPI SCI because it's already set up */ if (acpi_gbl_FADT.sci_interrupt == gsi) -return gsi; +return mp_map_gsi_to_irq (gsi,IOAPIC_MAP_ALLOC ); trigger = trigger ==ACPI_EDGE_SENSITIVE ?0 :1 ; polarity = polarity ==ACPI_ACTIVE_HIGH ?0 :1 ;
Signature · 1 line
1.7.10.4
PATCH v8 4/18 x86, PCI, ACPI: Kill private function resource_to_addr() in arch/x86/pci/acpi.c
In reply to Jiang LiuPrivate function resource_to_addr() is used to parse ACPI resources for PCI host bridge. There are public interfaces available for that purpose, so replace resource_to_addr() with public interfaces.
--- arch/x86/pci/acpi.c | 144 +++++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 91 deletions(-)
arch/x86/pci/acpi.c +53 -91
@@ -218,114 +218,76 @@ static void teardown_mcfg_map(struct pci_root_info *info) }#endif -static acpi_status resource_to_addr (struct acpi_resource *resource, -struct acpi_resource_address64 *addr) -{ -acpi_status status; -struct acpi_resource_memory24 *memory24; -struct acpi_resource_memory32 *memory32; -struct acpi_resource_fixed_memory32 *fixed_memory32; - -memset (addr,0 ,sizeof (*addr)); -switch (resource->type) { -case ACPI_RESOURCE_TYPE_MEMORY24 : - memory24 = &resource->data.memory24; - addr->resource_type =ACPI_MEMORY_RANGE ; - addr->minimum = memory24->minimum; - addr->address_length = memory24->address_length; - addr->maximum = addr->minimum + addr->address_length -1 ; -return AE_OK ; -case ACPI_RESOURCE_TYPE_MEMORY32 : - memory32 = &resource->data.memory32; - addr->resource_type =ACPI_MEMORY_RANGE ; - addr->minimum = memory32->minimum; - addr->address_length = memory32->address_length; - addr->maximum = addr->minimum + addr->address_length -1 ; -return AE_OK ; -case ACPI_RESOURCE_TYPE_FIXED_MEMORY32 : - fixed_memory32 = &resource->data.fixed_memory32; - addr->resource_type =ACPI_MEMORY_RANGE ; - addr->minimum = fixed_memory32->address; - addr->address_length = fixed_memory32->address_length; - addr->maximum = addr->minimum + addr->address_length -1 ; -return AE_OK ; -case ACPI_RESOURCE_TYPE_ADDRESS16 : -case ACPI_RESOURCE_TYPE_ADDRESS32 : -case ACPI_RESOURCE_TYPE_ADDRESS64 : - status =acpi_resource_to_address64 (resource, addr); -if (ACPI_SUCCESS (status) && - (addr->resource_type ==ACPI_MEMORY_RANGE || - addr->resource_type ==ACPI_IO_RANGE ) && - addr->address_length >0 ) { -return AE_OK ; - } -break ; - } -return AE_ERROR ; -} -static acpi_status count_resource (struct acpi_resource *acpi_res,void *data) {struct pci_root_info *info = data; -struct acpi_resource_address64 addr; -acpi_status status; +struct resource r = { + .flags =0 + }; - status =resource_to_addr (acpi_res, &addr); -if (ACPI_SUCCESS (status)) +if (!acpi_dev_resource_memory (acpi_res, &r) && + !acpi_dev_resource_address_space (acpi_res, &r)) +return AE_OK ; + +if ((r.flags & (IORESOURCE_IO |IORESOURCE_MEM )) &&resource_size (&r)) info->res_num++; +return AE_OK ; }static acpi_status setup_resource (struct acpi_resource *acpi_res,void *data) {struct pci_root_info *info = data; -struct resource *res; -struct acpi_resource_address64 addr; -acpi_status status; -unsigned long flags; -u64 start, orig_end, end; +u64 translation_offset =0 ; +struct resource r = { + .flags =0 + }; + +if (acpi_dev_resource_memory (acpi_res, &r)) { + r.flags &=IORESOURCE_MEM |IORESOURCE_IO ; + }else if (acpi_dev_resource_address_space (acpi_res, &r)) { +u64 orig_end; +struct acpi_resource_address64 addr; + + r.flags &=IORESOURCE_MEM |IORESOURCE_IO ; +if (r.flags ==0 ) +return AE_OK ; - status =resource_to_addr (acpi_res, &addr); -if (!ACPI_SUCCESS (status)) -return AE_OK ; +if (ACPI_FAILURE (acpi_resource_to_address64 (acpi_res, &addr))) +return AE_OK ; -if (addr.resource_type ==ACPI_MEMORY_RANGE ) { - flags =IORESOURCE_MEM ; -if (addr.info.mem.caching ==ACPI_PREFETCHABLE_MEMORY ) - flags |=IORESOURCE_PREFETCH ; - }else if (addr.resource_type ==ACPI_IO_RANGE ) { - flags =IORESOURCE_IO ; - }else -return AE_OK ; +if (addr.resource_type ==ACPI_MEMORY_RANGE && + addr.info.mem.caching ==ACPI_PREFETCHABLE_MEMORY ) + r.flags |=IORESOURCE_PREFETCH ; - start = addr.minimum + addr.translation_offset; - orig_end = end = addr.maximum + addr.translation_offset; + translation_offset = addr.translation_offset; + orig_end = r.end; + r.start += translation_offset; + r.end += translation_offset; -/* Exclude non-addressable range or non-addressable portion of range */ - end =min (end, (u64 )iomem_resource.end); -if (end <= start) { -dev_info (&info->bridge->dev, -"host bridge window [%#llx-%#llx] " -"(ignored, not CPU addressable)\n" , start, orig_end); -return AE_OK ; - }else if (orig_end != end) { -dev_info (&info->bridge->dev, -"host bridge window [%#llx-%#llx] " -"([%#llx-%#llx] ignored, not CPU addressable)\n" , - start, orig_end, end +1 , orig_end); +/* Exclude non-addressable range or non-addressable portion of range */ + r.end =min (r.end, iomem_resource.end); +if (r.end <= r.start) { +dev_info (&info->bridge->dev, +"host bridge window [%#llx-%#llx] (ignored, not CPU addressable)\n" , + r.start, orig_end); +return AE_OK ; + }else if (orig_end != r.end) { +dev_info (&info->bridge->dev, +"host bridge window [%#llx-%#llx] ([%#llx-%#llx] ignored, not CPU addressable)\n" , + r.start, orig_end, r.end +1 , orig_end); + } } - res = &info->res[info->res_num]; - res->name = info->name; - res->flags = flags; - res->start = start; - res->end = end; - info->res_offset[info->res_num] = addr.translation_offset; - info->res_num++; - -if (!pci_use_crs) -dev_printk (KERN_DEBUG , &info->bridge->dev, -"host bridge window %pR (ignored)\n" , res); +if (r.flags &&resource_size (&r)) { + r.name = info->name; + info->res[info->res_num] = r; + info->res_offset[info->res_num] = translation_offset; + info->res_num++; +if (!pci_use_crs) +dev_printk (KERN_DEBUG , &info->bridge->dev, +"host bridge window %pR (ignored)\n" , &r); + }return AE_OK ; }
Signature · 1 line
1.7.10.4
PATCH v8 5/18 ACPI: Correct return value of acpi_dev_resource_address_space()
In reply to Jiang LiuChange acpi_dev_resource_address_space() to return failure if the acpi_resource structure can't be converted to an ACPI address64 structure, so caller could correctly detect failure.
--- drivers/acpi/resource.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/acpi/resource.c +1 -1
@@ -200,7 +200,7 @@ bool acpi_dev_resource_address_space(struct acpi_resource *ares, status =acpi_resource_to_address64 (ares, &addr);if (ACPI_FAILURE (status)) -return true; +return false; res->start = addr.minimum; res->end = addr.maximum;
Signature · 1 line
1.7.10.4
PATCH v8 6/18 ACPI: Fix minor syntax issues in processor_core.c
In reply to Jiang LiuFix minor syntax issues in processor_core.c to follow coding styles.
--- drivers/acpi/processor_core.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/acpi/processor_core.c +4 -5
@@ -125,13 +125,12 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) } header = (struct acpi_subtable_header *)obj->buffer.pointer; -if (header->type ==ACPI_MADT_TYPE_LOCAL_APIC ) { +if (header->type ==ACPI_MADT_TYPE_LOCAL_APIC )map_lapic_id (header, acpi_id, &apic_id); - }else if (header->type ==ACPI_MADT_TYPE_LOCAL_SAPIC ) { +else if (header->type ==ACPI_MADT_TYPE_LOCAL_SAPIC )map_lsapic_id (header, type, acpi_id, &apic_id); - }else if (header->type ==ACPI_MADT_TYPE_LOCAL_X2APIC ) { +else if (header->type ==ACPI_MADT_TYPE_LOCAL_X2APIC )map_x2apic_id (header, type, acpi_id, &apic_id); - } exit:kfree (buffer.pointer);
@@ -164,7 +163,7 @@ int acpi_map_cpuid(int apic_id, u32 acpi_id)* For example, * * Scope (_PR) -* { +* { * Processor (CPU0, 0x00, 0x00000410, 0x06) {} * Processor (CPU1, 0x01, 0x00000410, 0x06) {} * Processor (CPU2, 0x02, 0x00000410, 0x06) {}
Signature · 1 line
1.7.10.4
PATCH v8 7/18 ACPI: Add interfaces to parse IOAPIC ID for IOAPIC hotplug
In reply to Jiang LiuFrom: Yinghai Lu <[email protected]>
We need to parse APIC ID for IOAPIC registration for IOAPIC hotplug. ACPI _MAT method and MADT table are used to figure out IOAPIC ID, just like parsing CPU APIC ID for CPU hotplug.
--- drivers/acpi/processor_core.c | 122 ++++++++++++++++++++++++++++++++++++++--- include/linux/acpi.h | 4 ++ 2 files changed, 117 insertions(+), 9 deletions(-)
drivers/acpi/processor_core.c +113 -9
@@ -4,6 +4,10 @@* * Alex Chiang <[email protected]> * - Unified x86/ia64 implementations +* +* I/O APIC hotplug support +* Yinghai Lu <[email protected]> +* Jiang Liu <[email protected]> */ #include <linux/export.h> #include <linux/acpi.h>
@@ -12,6 +16,21 @@#define _COMPONENT ACPI_PROCESSOR_COMPONENTACPI_MODULE_NAME ("processor_core" ); +static struct acpi_table_madt *get_madt_table (void ) +{ +static struct acpi_table_madt *madt; +static int read_madt; + +if (!read_madt) { +if (ACPI_FAILURE (acpi_get_table (ACPI_SIG_MADT ,0 , + (struct acpi_table_header **)&madt))) + madt =NULL ; + read_madt++; + } + +return madt; +} +static int map_lapic_id (struct acpi_subtable_header *entry,u32 acpi_id,int *apic_id) {
@@ -67,17 +86,10 @@ static int map_lsapic_id(struct acpi_subtable_header *entry,static int map_madt_entry (int type,u32 acpi_id) {unsigned long madt_end, entry; -static struct acpi_table_madt *madt; -static int read_madt;int apic_id =-1 ; +struct acpi_table_madt *madt; -if (!read_madt) { -if (ACPI_FAILURE (acpi_get_table (ACPI_SIG_MADT ,0 , - (struct acpi_table_header **)&madt))) - madt =NULL ; - read_madt++; - } - + madt =get_madt_table ();if (!madt)return apic_id;
@@ -203,3 +215,95 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)return acpi_map_cpuid (apic_id, acpi_id); }EXPORT_SYMBOL_GPL (acpi_get_cpuid); + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +static int get_ioapic_id (struct acpi_subtable_header *entry,u32 gsi_base, +u64 *phys_addr,int *ioapic_id) +{ +struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; + +if (ioapic->global_irq_base != gsi_base) +return 0 ; + + *phys_addr = ioapic->address; + *ioapic_id = ioapic->id; +return 1 ; +} + +static int parse_madt_ioapic_entry (u32 gsi_base,u64 *phys_addr) +{ +struct acpi_subtable_header *hdr; +unsigned long madt_end, entry; +int apic_id =-1 ; +struct acpi_table_madt *madt; + + madt =get_madt_table (); +if (!madt) +return apic_id; + + entry = (unsigned long )madt; + madt_end = entry + madt->header.length; + +/* Parse all entries looking for a match. */ + entry +=sizeof (struct acpi_table_madt ); +while (entry +sizeof (struct acpi_subtable_header ) < madt_end) { + hdr = (struct acpi_subtable_header *)entry; +if (hdr->type ==ACPI_MADT_TYPE_IO_APIC && +get_ioapic_id (hdr, gsi_base, phys_addr, &apic_id)) +break ; +else + entry += hdr->length; + } + +return apic_id; +} + +static int parse_mat_ioapic_entry (acpi_handle handle,u32 gsi_base, +u64 *phys_addr) +{ +struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER ,NULL }; +union acpi_object *obj; +struct acpi_subtable_header *header; +int apic_id =-1 ; + +if (ACPI_FAILURE (acpi_evaluate_object (handle,"_MAT" ,NULL , &buffer))) + goto exit; + +if (!buffer.length || !buffer.pointer) + goto exit; + + obj = buffer.pointer; +if (obj->type !=ACPI_TYPE_BUFFER || + obj->buffer.length <sizeof (struct acpi_subtable_header )) + goto exit; + + header = (struct acpi_subtable_header *)obj->buffer.pointer; +if (header->type ==ACPI_MADT_TYPE_IO_APIC ) +get_ioapic_id (header, gsi_base, phys_addr, &apic_id); + +exit: +kfree (buffer.pointer); +return apic_id; +} + +/** +* acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base +* @handle: ACPI object for IOAPIC device +* @gsi_base: GSI base to match with +* @phys_add: hold physical address of matching IOAPIC record +* +* Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search +* for an ACPI IOAPIC record matching @gsi_base. +* Return IOAPIC id and physical address if found a match, otherwise return <0. +*/ +int acpi_get_ioapic_id (acpi_handle handle,u32 gsi_base,u64 *phys_addr) +{ +int apic_id; + + apic_id =parse_mat_ioapic_entry (handle, gsi_base, phys_addr); +if (apic_id ==-1 ) + apic_id =parse_madt_ioapic_entry (gsi_base, phys_addr); + +return apic_id; +} +#endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */
include/linux/acpi.h +4 -0
@@ -146,6 +146,10 @@ int acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu);int acpi_unmap_lsapic (int cpu);#endif /* CONFIG_ACPI_HOTPLUG_CPU */ +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_get_ioapic_id (acpi_handle handle,u32 gsi_base,u64 *phys_addr); +#endif +int acpi_register_ioapic (acpi_handle handle,u64 phys_addr,u32 gsi_base);int acpi_unregister_ioapic (acpi_handle handle,u32 gsi_base);void acpi_irq_stats_init (void );
Signature · 1 line
1.7.10.4
PATCH v8 8/18 PCI: Remove PCI ioapic driver
In reply to Jiang LiuTo support IOAPIC hotplug on x86 and IA64 platforms, OS needs to figure out global interrupt source number(GSI) and IOAPIC enumeration ID through ACPI interfaces. So BIOS must implement an ACPI IOAPIC device with _GSB/_UID or _MAT method to support IOAPIC hotplug. OS also needs to figure out base physical address to access IOAPIC registers. OS may get the base physical address through PCI BARs if IOAPIC device is visible in PCI domain, otherwise OS may get the address by ACPI _CRS method if IOAPIC device is hidden from PCI domain by BIOS.
When adding a PCI subtree, we need to add IOAPIC devices before enabling all other PCI devices because other PCI devices may use the IOAPIC to allocate PCI interrupts.
So we plan to reimplement IOAPIC driver as an ACPI instead of PCI driver due to:
1) hot-pluggable IOAPIC devices are always visible in ACPI domain, but may or may not be visible in PCI domain.
2) we could explicitly control the order between IOAPIC and other PCI devices.
We also have another choice to use a PCI driver to manage IOAPIC device if it's visible in PCI domain and use an ACPI driver if it's only visible in ACPI domain. But this solution is a little complex.
It shouldn't cause serious backward compatibility issues because:
1) IOAPIC hotplug is never supported on x86 yet because it hasn't implemented the required acpi_register_ioapic() and acpi_unregister_ioapic().
2) Currently only ACPI based IOAPIC hotplug is possible on x86 and IA64, we don't know other specifications and interfaces to support IOAPIC hotplug yet.
3) We will reimplement an ACPI IOAPICtdriver support IOAPIC hotplug.
This change also helps to get rid of the false alarm on all current Linux distributions:
[ 6.952497] ioapic: probe of 0000:00:05.4 failed with error -22 [ 6.959542] ioapic: probe of 0000:80:05.4 failed with error -22
--- drivers/pci/Kconfig | 7 --- drivers/pci/Makefile | 2 - drivers/pci/ioapic.c | 121 -------------------------------------------------- 3 files changed, 130 deletions(-) delete mode 100644 drivers/pci/ioapic.c
drivers/pci/Kconfig +0 -7
@@ -104,13 +104,6 @@ config PCI_PASID If unsure, say N. -config PCI_IOAPIC - bool "PCI IO-APIC hotplug support" if X86 - depends on PCI - depends on ACPI - depends on X86_IO_APIC - default !X86 - config PCI_LABEL def_bool y if (DMI || ACPI) select NLS
drivers/pci/Makefile +0 -2
@@ -13,8 +13,6 @@ obj-$(CONFIG_PCI_QUIRKS) += quirks.o # Build PCI Express stuff if needed obj-$(CONFIG_PCIEPORTBUS) += pcie/ -obj-$(CONFIG_PCI_IOAPIC) += ioapic.o - # Build the PCI Hotplug drivers if we were asked to obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ ifdef CONFIG_HOTPLUG_PCI
drivers/pci/ioapic.c deleted +0 -121
121 lines not on this page · raw message
Signature · 1 line
1.7.10.4
PATCH v8 9/18 x86, irq: Split out alloc_ioapic_save_registers()
In reply to Jiang LiuFrom: Yinghai Lu <[email protected]>
Split out alloc_ioapic_save_registers() from early_irq_init(), so it could be used for ioapic hotplug later.
--- arch/x86/kernel/apic/io_apic.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
arch/x86/kernel/apic/io_apic.c +15 -7
@@ -237,6 +237,19 @@ static struct irq_pin_list *alloc_irq_pin_list(int node)return kzalloc_node (sizeof (struct irq_pin_list ),GFP_KERNEL , node); } +static void alloc_ioapic_saved_registers (int idx) +{ +size_t size; + +if (ioapics[idx].saved_registers) +return ; + + size =sizeof (struct IO_APIC_route_entry ) * ioapics[idx].nr_registers; + ioapics[idx].saved_registers =kzalloc (size,GFP_KERNEL ); +if (!ioapics[idx].saved_registers) +pr_err ("IOAPIC %d: suspend/resume impossible!\n" , idx); +} +int __init arch_early_irq_init (void ) {struct irq_cfg *cfg;
@@ -245,13 +258,8 @@ int __init arch_early_irq_init(void)if (!nr_legacy_irqs ()) io_apic_irqs = ~0UL ; -for_each_ioapic (i) { - ioapics[i].saved_registers = -kzalloc (sizeof (struct IO_APIC_route_entry ) * - ioapics[i].nr_registers,GFP_KERNEL ); -if (!ioapics[i].saved_registers) -pr_err ("IOAPIC %d: suspend/resume impossible!\n" , i); - } +for_each_ioapic (i) +alloc_ioapic_saved_registers (i);/* * For legacy IRQ's, start with assigning irq0 to irq15 to
Signature · 1 line
1.7.10.4
PATCH v8 10/18 x86, irq: Prefer assigned ID in APIC ID register for x86_64
In reply to Jiang LiuFrom: Yinghai Lu <[email protected]>
Perfer the assigned ID in APIC ID register for x86_64 if it's still available.
--- arch/x86/kernel/apic/io_apic.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-)
arch/x86/kernel/apic/io_apic.c +30 -5
@@ -3580,26 +3580,51 @@ static int __init io_apic_get_unique_id(int ioapic, int apic_id)return apic_id; } -static u8 __init io_apic_unique_id (u8 id) +static u8 __init io_apic_unique_id (int idx,u8 id) {if ((boot_cpu_data.x86_vendor ==X86_VENDOR_INTEL ) && !APIC_XAPIC (apic_version[boot_cpu_physical_apicid])) -return io_apic_get_unique_id (nr_ioapics, id); +return io_apic_get_unique_id (idx, id);else return id; }#else -static u8 __init io_apic_unique_id (u8 id) +static u8 __init io_apic_unique_id (int idx,u8 id) {int i; +u8 new_id; +unsigned long flags;DECLARE_BITMAP (used,256 ); +union IO_APIC_reg_00 reg_00;bitmap_zero (used,256 );for_each_ioapic (i)__set_bit (mpc_ioapic_id (i), used);if (!test_bit (id, used))return id; -return find_first_zero_bit (used,256 ); + +/* check register at first */ +raw_spin_lock_irqsave (&ioapic_lock, flags); + reg_00.raw =io_apic_read (idx,0 ); +raw_spin_unlock_irqrestore (&ioapic_lock, flags); + new_id = reg_00.bits.ID; +if (!test_bit (new_id, used)) { +apic_printk (APIC_VERBOSE ,KERN_INFO +"IOAPIC[%d]: Using reg apic_id %d instead of %d\n" , + idx, new_id, id); +return new_id; + } + + new_id =find_first_zero_bit (used,256 ); + reg_00.bits.ID = new_id; +raw_spin_lock_irqsave (&ioapic_lock, flags); +io_apic_write (idx,0 , reg_00.raw); + reg_00.raw =io_apic_read (idx,0 ); +raw_spin_unlock_irqrestore (&ioapic_lock, flags); +/* Sanity check */ +BUG_ON (reg_00.bits.ID != new_id); + +return new_id; }#endif
@@ -3865,7 +3890,7 @@ void __init mp_register_ioapic(int id, u32 address, u32 gsi_base,return ; } - ioapics[idx].mp_config.apicid =io_apic_unique_id (id); + ioapics[idx].mp_config.apicid =io_apic_unique_id (idx, id); ioapics[idx].mp_config.apicver =io_apic_get_version (idx);/*
Signature · 1 line
1.7.10.4
PATCH v8 11/18 x86, irq: Remove __init marker for functions will be used by IOAPIC hotplug
In reply to Jiang LiuRemove __init marker for functions which will be used by IOAPIC hotplug at runtime.
--- arch/x86/include/asm/io_apic.h | 4 ++-- arch/x86/kernel/apic/io_apic.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-)
arch/x86/include/asm/io_apic.h +2 -2
@@ -188,8 +188,8 @@ extern int mp_find_ioapic_pin(int ioapic, u32 gsi);extern u32 mp_pin_to_gsi (int ioapic,int pin);extern int mp_map_gsi_to_irq (u32 gsi,unsigned int flags);extern void mp_unmap_irq (int irq); -extern void __init mp_register_ioapic (int id,u32 address,u32 gsi_base, -struct ioapic_domain_cfg *cfg); +extern void mp_register_ioapic (int id,u32 address,u32 gsi_base, +struct ioapic_domain_cfg *cfg);extern int mp_irqdomain_map (struct irq_domain *domain,unsigned int virq,irq_hw_number_t hwirq);extern void mp_irqdomain_unmap (struct irq_domain *domain,unsigned int virq);
arch/x86/kernel/apic/io_apic.c +9 -9
@@ -3459,7 +3459,7 @@ io_apic_setup_irq_pin(unsigned int irq, int node, struct io_apic_irq_attr *attr)return ret; } -static int __init io_apic_get_redir_entries (int ioapic) +static int io_apic_get_redir_entries (int ioapic) {union IO_APIC_reg_01 reg_01;unsigned long flags;
@@ -3505,7 +3505,7 @@ int __init arch_probe_nr_irqs(void) }#ifdef CONFIG_X86_32 -static int __init io_apic_get_unique_id (int ioapic,int apic_id) +static int io_apic_get_unique_id (int ioapic,int apic_id) {union IO_APIC_reg_00 reg_00;static physid_mask_t apic_id_map =PHYSID_MASK_NONE ;
@@ -3580,7 +3580,7 @@ static int __init io_apic_get_unique_id(int ioapic, int apic_id)return apic_id; } -static u8 __init io_apic_unique_id (int idx,u8 id) +static u8 io_apic_unique_id (int idx,u8 id) {if ((boot_cpu_data.x86_vendor ==X86_VENDOR_INTEL ) && !APIC_XAPIC (apic_version[boot_cpu_physical_apicid]))
@@ -3589,7 +3589,7 @@ static u8 __init io_apic_unique_id(int idx, u8 id) return id; }#else -static u8 __init io_apic_unique_id (int idx,u8 id) +static u8 io_apic_unique_id (int idx,u8 id) {int i;u8 new_id;
@@ -3628,7 +3628,7 @@ static u8 __init io_apic_unique_id(int idx, u8 id) }#endif -static int __init io_apic_get_version (int ioapic) +static int io_apic_get_version (int ioapic) {union IO_APIC_reg_01 reg_01;unsigned long flags;
@@ -3832,7 +3832,7 @@ int mp_find_ioapic_pin(int ioapic, u32 gsi)return gsi - gsi_cfg->gsi_base; } -static __init int bad_ioapic (unsigned long address) +static int bad_ioapic (unsigned long address) {if (nr_ioapics >=MAX_IO_APICS ) { pr_warn("WARNING: Max # of I/O APICs (%d) exceeded (found %d), skipping\n",
@@ -3846,7 +3846,7 @@ static __init int bad_ioapic(unsigned long address)return 0 ; } -static __init int bad_ioapic_register (int idx) +static int bad_ioapic_register (int idx) {union IO_APIC_reg_00 reg_00;union IO_APIC_reg_01 reg_01;
@@ -3865,8 +3865,8 @@ static __init int bad_ioapic_register(int idx)return 0 ; } -void __init mp_register_ioapic (int id,u32 address,u32 gsi_base, -struct ioapic_domain_cfg *cfg) +void mp_register_ioapic (int id,u32 address,u32 gsi_base, +struct ioapic_domain_cfg *cfg) {int idx =0 ;int entries;
Signature · 1 line
1.7.10.4
PATCH v8 12/18 x86, irq: Keep balance of IOAPIC pin reference count
In reply to Jiang LiuTo keep balance of IOAPIC pin reference count, we need to protect pirq_enable_irq(), acpi_pci_irq_enable() and intel_mid_pci_irq_enable() from reentrance. There are two cases which will cause reentrance.
The first case is caused by suspend/hibernation. If pcibios_disable_irq is called during suspending/hibernating, we don't release the assigned IRQ number, otherwise it may break the suspend/hibernation. So late when pcibios_enable_irq is called during resume, we shouldn't allocate IRQ number again.
The second case is that function acpi_pci_irq_enable() may be called twice for PCI devices present at boot time as below:
1) pci_acpi_init()
--> acpi_pci_irq_enable() if pci_routeirq is true 2) pci_enable_device() --> pcibios_enable_device() --> acpi_pci_irq_enable()
We can't kill kernel parameter pci_routeirq yet because it's still needed for debugging purpose.
So flag irq_managed is introduced to track whether IRQ number is assigned by OS and to protect pirq_enable_irq(), acpi_pci_irq_enable() and intel_mid_pci_irq_enable() from reentrance.
--- arch/x86/pci/intel_mid_pci.c | 10 +++++++++- arch/x86/pci/irq.c | 7 ++++++- drivers/acpi/pci_irq.c | 11 +++++++++-- include/linux/pci.h | 1 + 4 files changed, 25 insertions(+), 4 deletions(-)
arch/x86/pci/intel_mid_pci.c +9 -1
@@ -210,6 +210,9 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev) {int polarity; +if (dev->irq_managed && dev->irq >0 ) +return 0 ; +if (intel_mid_identify_cpu () ==INTEL_MID_CPU_CHIP_TANGIER ) polarity =0 ;/* active high */ else
@@ -224,13 +227,18 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)if (mp_map_gsi_to_irq (dev->irq,IOAPIC_MAP_ALLOC ) <0 )return -EBUSY ; + dev->irq_managed =1 ; +return 0 ; }static void intel_mid_pci_irq_disable (struct pci_dev *dev) { -if (!mp_should_keep_irq (&dev->dev) && dev->irq >0 ) +if (!mp_should_keep_irq (&dev->dev) && dev->irq_managed && + dev->irq >0 ) {mp_unmap_irq (dev->irq); + dev->irq_managed =0 ; + } }struct pci_ops intel_mid_pci_ops = {
arch/x86/pci/irq.c +6 -1
@@ -1202,6 +1202,9 @@ static int pirq_enable_irq(struct pci_dev *dev)int irq;struct io_apic_irq_attr irq_attr; +if (dev->irq_managed && dev->irq >0 ) +return 0 ; + irq =IO_APIC_get_PCI_irq_vector (dev->bus->number,PCI_SLOT (dev->devfn), pin -1 , &irq_attr);
@@ -1228,6 +1231,7 @@ static int pirq_enable_irq(struct pci_dev *dev) } dev = temp_dev;if (irq >=0 ) { + dev->irq_managed =1 ; dev->irq = irq;dev_info (&dev->dev,"PCI->APIC IRQ transform: " "INT %c -> IRQ %d\n" ,'A' + pin -1 , irq);
@@ -1257,8 +1261,9 @@ static int pirq_enable_irq(struct pci_dev *dev)static void pirq_disable_irq (struct pci_dev *dev) {if (io_apic_assign_pci_irqs && !mp_should_keep_irq (&dev->dev) && - dev->irq) { + dev->irq_managed && dev->irq) {mp_unmap_irq (dev->irq); dev->irq =0 ; + dev->irq_managed =0 ; } }
drivers/acpi/pci_irq.c +9 -2
@@ -413,6 +413,9 @@ int acpi_pci_irq_enable(struct pci_dev *dev)return 0 ; } +if (dev->irq_managed && dev->irq >0 ) +return 0 ; + entry =acpi_pci_irq_lookup (dev, pin);if (!entry) {/*
@@ -456,6 +459,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)return rc; } dev->irq = rc; + dev->irq_managed =1 ;if (link)snprintf (link_desc,sizeof (link_desc)," -> Link[%s]" , link);
@@ -478,7 +482,7 @@ void acpi_pci_irq_disable(struct pci_dev *dev)u8 pin; pin = dev->pin; -if (!pin) +if (!pin || !dev->irq_managed || dev->irq <=0 )return ;/* Keep IOAPIC pin configuration when suspending */
@@ -506,6 +510,9 @@ void acpi_pci_irq_disable(struct pci_dev *dev)*/ dev_dbg (&dev->dev,"PCI INT %c disabled\n" ,pin_name (pin)); -if (gsi >=0 && dev->irq >0 ) +if (gsi >=0 ) {acpi_unregister_gsi (gsi); + dev->irq =0 ; + dev->irq_managed =0 ; + } }
include/linux/pci.h +1 -0
@@ -348,6 +348,7 @@ struct pci_dev { unsigned int __aer_firmware_first:1; unsigned int broken_intx_masking:1; unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */ + unsigned int irq_managed:1;pci_dev_flags_t dev_flags;atomic_t enable_cnt;/* pci_enable_device has been called */
Signature · 1 line
1.7.10.4
PATCH v8 13/18 x86, irq: Refine mp_register_ioapic() to prepare for IOAPIC hotplug
In reply to Jiang LiuRefine mp_register_ioapic() to prepare for IOAPIC hotplug by:
1) change return value from void to int.
2) check for gsi range conflicts
3) check for IOAPIC physical address conflicts
4) enhance the way to allocate IOAPIC index
--- arch/x86/include/asm/io_apic.h | 4 +- arch/x86/kernel/apic/io_apic.c | 87 ++++++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 33 deletions(-)
arch/x86/include/asm/io_apic.h +2 -2
@@ -188,8 +188,8 @@ extern int mp_find_ioapic_pin(int ioapic, u32 gsi);extern u32 mp_pin_to_gsi (int ioapic,int pin);extern int mp_map_gsi_to_irq (u32 gsi,unsigned int flags);extern void mp_unmap_irq (int irq); -extern void mp_register_ioapic (int id,u32 address,u32 gsi_base, -struct ioapic_domain_cfg *cfg); +extern int mp_register_ioapic (int id,u32 address,u32 gsi_base, +struct ioapic_domain_cfg *cfg);extern int mp_irqdomain_map (struct irq_domain *domain,unsigned int virq,irq_hw_number_t hwirq);extern void mp_irqdomain_unmap (struct irq_domain *domain,unsigned int virq);
arch/x86/kernel/apic/io_apic.c +56 -31
129 lines not on this page · raw message
Signature · 1 line
1.7.10.4
PATCH v8 14/18 x86, irq, ACPI: Introduce a rwsem to protect IOAPIC operations from hotplug
In reply to Jiang LiuWe are going to support ACPI based IOAPIC hotplug, so introduce a rwsem to protect IOAPIC data structures from IOAPIC hotplug. We choose to serialize in ACPI instead of in the IOAPIC core because:
1) currently we are only plan to support ACPI based IOAPIC hotplug
2) it's much more cleaner and easier
3) It does't affect IOAPIC discovered by devicetree, SFI and mpparse.
--- arch/x86/kernel/acpi/boot.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
arch/x86/kernel/acpi/boot.c +21 -0
@@ -76,6 +76,19 @@ int acpi_fix_pin2_polarity __initdata;static u64 acpi_lapic_addr__initdata =APIC_DEFAULT_PHYS_BASE ;#endif +/* +* Locks related to IOAPIC hotplug +* Hotplug side: +* ->lock_device_hotplug() //device_hotplug_lock +* ->acpi_ioapic_lock +* ->ioapic_lock +* Interrupt mapping side: +* ->acpi_ioapic_lock +* ->ioapic_mutex +* ->ioapic_lock +*/ +static DEFINE_MUTEX (acpi_ioapic_lock); +/* -------------------------------------------------------------------------- Boot-time Configuration -------------------------------------------------------------------------- */
@@ -609,8 +622,10 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)if (acpi_irq_model ==ACPI_IRQ_MODEL_PIC ) { *irqp = gsi; }else { +mutex_lock (&acpi_ioapic_lock); irq =mp_map_gsi_to_irq (gsi,IOAPIC_MAP_ALLOC |IOAPIC_MAP_CHECK ); +mutex_unlock (&acpi_ioapic_lock);if (irq <0 )return -1 ; *irqp = irq;
@@ -651,7 +666,9 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,int irq = gsi;#ifdef CONFIG_X86_IO_APIC +mutex_lock (&acpi_ioapic_lock); irq =mp_register_gsi (dev, gsi, trigger, polarity); +mutex_unlock (&acpi_ioapic_lock);#endif return irq;
@@ -660,7 +677,9 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,static void acpi_unregister_gsi_ioapic (u32 gsi) {#ifdef CONFIG_X86_IO_APIC +mutex_lock (&acpi_ioapic_lock);mp_unregister_gsi (gsi); +mutex_unlock (&acpi_ioapic_lock);#endif }
@@ -1186,7 +1205,9 @@ static void __init acpi_process_madt(void)/* * Parse MADT IO-APIC entries */ +mutex_lock (&acpi_ioapic_lock); error =acpi_parse_madt_ioapic_entries (); +mutex_unlock (&acpi_ioapic_lock);if (!error) {acpi_set_irq_model_ioapic ();
Signature · 1 line
1.7.10.4
tip-bot for Jiang Liu
merge bot[tip:x86/apic] x86, irq, ACPI: Introduce a mutex to protect IOAPIC operations from hotplug
x86, irq, ACPI: Introduce a mutex to protect IOAPIC operations from hotplug
Commit-ID: bca74a854c10cbbbf13690e92704e81f6afdb697 Gitweb: http://git.kernel.org/tip/bca74a854c10cbbbf13690e92704e81f6afdb697 Author: Jiang Liu <[email protected]> AuthorDate: Sun, 2 Nov 2014 14:04:29 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Mon, 3 Nov 2014 11:56:08 +0100
x86, irq, ACPI: Introduce a mutex to protect IOAPIC operations from hotplug
We are going to support ACPI based IOAPIC hotplug, so introduce a mutex to protect IOAPIC data structures from IOAPIC hotplug. We choose to serialize in ACPI instead of in the IOAPIC core because:
1) currently we only plan to support ACPI based IOAPIC hotplug
2) it's much more cleaner and easier
3) It does't affect IOAPIC discovered by devicetree, SFI and mpparse.
--- arch/x86/kernel/acpi/boot.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
arch/x86/kernel/acpi/boot.c +21 -0
@@ -76,6 +76,19 @@ int acpi_fix_pin2_polarity __initdata;static u64 acpi_lapic_addr__initdata =APIC_DEFAULT_PHYS_BASE ;#endif +/* +* Locks related to IOAPIC hotplug +* Hotplug side: +* ->device_hotplug_lock +* ->acpi_ioapic_lock +* ->ioapic_lock +* Interrupt mapping side: +* ->acpi_ioapic_lock +* ->ioapic_mutex +* ->ioapic_lock +*/ +static DEFINE_MUTEX (acpi_ioapic_lock); +/* -------------------------------------------------------------------------- Boot-time Configuration -------------------------------------------------------------------------- */
@@ -609,8 +622,10 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)if (acpi_irq_model ==ACPI_IRQ_MODEL_PIC ) { *irqp = gsi; }else { +mutex_lock (&acpi_ioapic_lock); irq =mp_map_gsi_to_irq (gsi,IOAPIC_MAP_ALLOC |IOAPIC_MAP_CHECK ); +mutex_unlock (&acpi_ioapic_lock);if (irq <0 )return -1 ; *irqp = irq;
@@ -650,7 +665,9 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,int irq = gsi;#ifdef CONFIG_X86_IO_APIC +mutex_lock (&acpi_ioapic_lock); irq =mp_register_gsi (dev, gsi, trigger, polarity); +mutex_unlock (&acpi_ioapic_lock);#endif return irq;
@@ -659,7 +676,9 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,static void acpi_unregister_gsi_ioapic (u32 gsi) {#ifdef CONFIG_X86_IO_APIC +mutex_lock (&acpi_ioapic_lock);mp_unregister_gsi (gsi); +mutex_unlock (&acpi_ioapic_lock);#endif }
@@ -1185,7 +1204,9 @@ static void __init acpi_process_madt(void)/* * Parse MADT IO-APIC entries */ +mutex_lock (&acpi_ioapic_lock); error =acpi_parse_madt_ioapic_entries (); +mutex_unlock (&acpi_ioapic_lock);if (!error) {acpi_set_irq_model_ioapic ();
tip-bot for Jiang Liu
merge bot[tip:x86/apic] x86, irq, ACPI: Introduce a mutex to protect IOAPIC operations from hotplug
In reply to Jiang Liux86, irq, ACPI: Introduce a mutex to protect IOAPIC operations from hotplug
Commit-ID: 5da2fd26193433006255d534b3a01eae37e58428 Gitweb: http://git.kernel.org/tip/5da2fd26193433006255d534b3a01eae37e58428 Author: Jiang Liu <[email protected]> AuthorDate: Sun, 2 Nov 2014 14:04:29 +0800 Committer: Thomas Gleixner <[email protected]> CommitDate: Tue, 16 Dec 2014 14:08:15 +0100
x86, irq, ACPI: Introduce a mutex to protect IOAPIC operations from hotplug
We are going to support ACPI based IOAPIC hotplug, so introduce a mutex to protect IOAPIC data structures from IOAPIC hotplug. We choose to serialize in ACPI instead of in the IOAPIC core because:
1) currently we only plan to support ACPI based IOAPIC hotplug
2) it's much more cleaner and easier
3) It does't affect IOAPIC discovered by devicetree, SFI and mpparse.
--- arch/x86/kernel/acpi/boot.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
arch/x86/kernel/acpi/boot.c +21 -0
@@ -76,6 +76,19 @@ int acpi_fix_pin2_polarity __initdata;static u64 acpi_lapic_addr__initdata =APIC_DEFAULT_PHYS_BASE ;#endif +/* +* Locks related to IOAPIC hotplug +* Hotplug side: +* ->device_hotplug_lock +* ->acpi_ioapic_lock +* ->ioapic_lock +* Interrupt mapping side: +* ->acpi_ioapic_lock +* ->ioapic_mutex +* ->ioapic_lock +*/ +static DEFINE_MUTEX (acpi_ioapic_lock); +/* -------------------------------------------------------------------------- Boot-time Configuration -------------------------------------------------------------------------- */
@@ -609,8 +622,10 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)if (acpi_irq_model ==ACPI_IRQ_MODEL_PIC ) { *irqp = gsi; }else { +mutex_lock (&acpi_ioapic_lock); irq =mp_map_gsi_to_irq (gsi,IOAPIC_MAP_ALLOC |IOAPIC_MAP_CHECK ); +mutex_unlock (&acpi_ioapic_lock);if (irq <0 )return -1 ; *irqp = irq;
@@ -650,7 +665,9 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,int irq = gsi;#ifdef CONFIG_X86_IO_APIC +mutex_lock (&acpi_ioapic_lock); irq =mp_register_gsi (dev, gsi, trigger, polarity); +mutex_unlock (&acpi_ioapic_lock);#endif return irq;
@@ -659,7 +676,9 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,static void acpi_unregister_gsi_ioapic (u32 gsi) {#ifdef CONFIG_X86_IO_APIC +mutex_lock (&acpi_ioapic_lock);mp_unregister_gsi (gsi); +mutex_unlock (&acpi_ioapic_lock);#endif }
@@ -1187,7 +1206,9 @@ static void __init acpi_process_madt(void)/* * Parse MADT IO-APIC entries */ +mutex_lock (&acpi_ioapic_lock); error =acpi_parse_madt_ioapic_entries (); +mutex_unlock (&acpi_ioapic_lock);if (!error) {acpi_set_irq_model_ioapic ();
PATCH v8 15/18 x86, irq, ACPI: Implement interface to support ACPI based IOAPIC hot-addition
In reply to Jiang LiuImplement acpi_register_ioapic() and enhance mp_register_ioapic() to support ACPI based IOAPIC hot-addition.
--- arch/x86/kernel/acpi/boot.c | 31 +++++++++++++++++++++++++++++-- arch/x86/kernel/apic/io_apic.c | 22 +++++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-)
arch/x86/kernel/acpi/boot.c +29 -2
@@ -779,8 +779,35 @@ EXPORT_SYMBOL(acpi_unmap_lsapic);int acpi_register_ioapic (acpi_handle handle,u64 phys_addr,u32 gsi_base) { -/* TBD */ -return -EINVAL ; +int ret = -ENOSYS ; +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int ioapic_id; +u64 addr; +struct ioapic_domain_cfg cfg = { + .type =IOAPIC_DOMAIN_DYNAMIC , + .ops = &acpi_irqdomain_ops, + }; + + ioapic_id =acpi_get_ioapic_id (handle, gsi_base, &addr); +if (ioapic_id <0 ) { +unsigned long long uid; +acpi_status status; + + status =acpi_evaluate_integer (handle,METHOD_NAME__UID , +NULL , &uid); +if (ACPI_FAILURE (status)) { +acpi_handle_warn (handle,"failed to get IOAPIC ID.\n" ); +return -EINVAL ; + } + ioapic_id = (int )uid; + } + +down_write (&acpi_ioapic_rwsem); + ret =mp_register_ioapic (ioapic_id, phys_addr, gsi_base, &cfg); +up_write (&acpi_ioapic_rwsem); +#endif + +return ret; }EXPORT_SYMBOL (acpi_register_ioapic);
arch/x86/kernel/apic/io_apic.c +21 -1
@@ -3853,7 +3853,13 @@ static int bad_ioapic_register(int idx)static int find_free_ioapic_entry (void ) { -return nr_ioapics; +int idx; + +for (idx =0 ; idx <MAX_IO_APICS ; idx++) +if (ioapics[idx].nr_registers ==0 ) +return idx; + +return MAX_IO_APICS ; }/**
@@ -3869,6 +3875,7 @@ int mp_register_ioapic(int id, u32 address, u32 gsi_base,u32 gsi_end;int idx, ioapic, entries;struct mp_ioapic_gsi *gsi_cfg; +bool hotplug = !!ioapic_initialized;if (!address) {pr_warn ("Bogus (zero) I/O APIC address found, skipping!\n" );
@@ -3927,6 +3934,19 @@ int mp_register_ioapic(int id, u32 address, u32 gsi_base, ioapics[idx].irqdomain =NULL ; ioapics[idx].irqdomain_cfg = *cfg; +/* +* If mp_register_ioapic() is called during early boot stage when +* walking ACPI/SFI/DT tables, it's too early to create irqdomain, +* we are still using bootmem allocator. So delay it to setup_IO_APIC(). +*/ +if (hotplug) { +if (mp_irqdomain_create (idx)) { +clear_fixmap (FIX_IO_APIC_BASE_0 + idx); +return -ENOMEM ; + } +alloc_ioapic_saved_registers (idx); + } +if (gsi_cfg->gsi_end >= gsi_top) gsi_top = gsi_cfg->gsi_end +1 ;if (nr_ioapics <= idx)
Signature · 1 line
1.7.10.4
Re: [Patch v8 15/18] x86, irq, ACPI: Implement interface to support ACPI based IOAPIC hot-addition
arch/x86/kernel/acpi/boot.c EXPORT_SYMBOL(acpi_unmap_lsapic); · line 805
Quoted 1 line · Jiang Liu
arch/x86/kernel/acpi/boot.c +1
-0+down_write (&acpi_ioapic_rwsem);
That wants to be acpi_ioapic_lock. Same issue in the following patches. You fixed it up in the last patch.
Please don't resend. I folded the hunks back to the proper places already.
Thanks,
tglx
Re: [Patch v8 15/18] x86, irq, ACPI: Implement interface to support ACPI based IOAPIC hot-addition
Quoted 8 lines · Thomas Gleixner
arch/x86/kernel/acpi/boot.c EXPORT_SYMBOL(acpi_unmap_lsapic); · line 805
Quoted 1 line · Jiang Liu
arch/x86/kernel/acpi/boot.c +1
-0+down_write (&acpi_ioapic_rwsem);That wants to be
acpi_ioapic_lock. Same issue in the following patches. You fixed it up in the last patch.Please don't resend. I folded the hunks back to the proper places already.
Thanks, Thomas.
There's no changes for the second patch set "[Patch Part1 v3 00/20] Prepare for enabling hierarchy irqdomain on x86" at
https://lkml.org/lkml/2014/10/27/122.
It should apply cleanly onto this patch set, so I won't resend it.
I have also solved all issues related to interrupt remapping and is testing new implementation for third and fourth patch sets. Will send them out once passing all tests.
Regards!
Gerry
Quoted 12 lines
Thanks,
tglx
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
PATCH v8 16/18 x86, irq, ACPI: Implement interfaces to support ACPI based IOAPIC hot-removal
In reply to Jiang LiuImplement acpi_unregister_ioapic() to support ACPI based IOAPIC hot-removal. An IOAPIC could only be removed when all its pins are unused.
--- arch/x86/include/asm/io_apic.h | 1 + arch/x86/kernel/acpi/boot.c | 13 +++++++--- arch/x86/kernel/apic/io_apic.c | 55 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-)
arch/x86/include/asm/io_apic.h +1 -0
@@ -190,6 +190,7 @@ extern int mp_map_gsi_to_irq(u32 gsi, unsigned int flags);extern void mp_unmap_irq (int irq);extern int mp_register_ioapic (int id,u32 address,u32 gsi_base,struct ioapic_domain_cfg *cfg); +extern int mp_unregister_ioapic (u32 gsi_base);extern int mp_irqdomain_map (struct irq_domain *domain,unsigned int virq,irq_hw_number_t hwirq);extern void mp_irqdomain_unmap (struct irq_domain *domain,unsigned int virq);
arch/x86/kernel/acpi/boot.c +9 -4
@@ -809,15 +809,20 @@ int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)return ret; } -EXPORT_SYMBOL (acpi_register_ioapic);int acpi_unregister_ioapic (acpi_handle handle,u32 gsi_base) { -/* TBD */ -return -EINVAL ; -} +int ret = -ENOSYS ; + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +down_write (&acpi_ioapic_rwsem); + ret =mp_unregister_ioapic (gsi_base); +up_write (&acpi_ioapic_rwsem); +#endif +return ret; +}EXPORT_SYMBOL (acpi_unregister_ioapic);static int __init acpi_parse_sbf (struct acpi_table_header *table)
arch/x86/kernel/apic/io_apic.c +55 -0
85 lines not on this page · raw message
Signature · 1 line
1.7.10.4
PATCH v8 17/18 x86, irq: Introduce helper to check whether an IOAPIC has been registered
In reply to Jiang LiuIntroduce acpi_ioapic_registered() to check whether an IOAPIC has already been registered, it will be used when enabling IOAPIC hotplug.
--- arch/x86/include/asm/io_apic.h | 1 + arch/x86/kernel/acpi/boot.c | 22 ++++++++++++++++++++++ arch/x86/kernel/apic/io_apic.c | 11 +++++++++++ include/linux/acpi.h | 1 + 4 files changed, 35 insertions(+)
arch/x86/include/asm/io_apic.h +1 -0
@@ -191,6 +191,7 @@ extern void mp_unmap_irq(int irq);extern int mp_register_ioapic (int id,u32 address,u32 gsi_base,struct ioapic_domain_cfg *cfg);extern int mp_unregister_ioapic (u32 gsi_base); +extern int mp_ioapic_registered (u32 gsi_base);extern int mp_irqdomain_map (struct irq_domain *domain,unsigned int virq,irq_hw_number_t hwirq);extern void mp_irqdomain_unmap (struct irq_domain *domain,unsigned int virq);
arch/x86/kernel/acpi/boot.c +22 -0
@@ -825,6 +825,28 @@ int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base) }EXPORT_SYMBOL (acpi_unregister_ioapic); +/** +* acpi_ioapic_registered - Check whether IOAPIC assoicatied with @gsi_base +* has been registered +* @handle: ACPI handle of the IOAPIC deivce +* @gsi_base: GSI base associated with the IOAPIC +* +* Assume caller holds some type of lock to serialize acpi_ioapic_registered() +* with acpi_register_ioapic()/acpi_unregister_ioapic(). +*/ +int acpi_ioapic_registered (acpi_handle handle,u32 gsi_base) +{ +int ret =0 ; + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +down_read (&acpi_ioapic_rwsem); + ret =mp_ioapic_registered (gsi_base); +up_read (&acpi_ioapic_rwsem); +#endif + +return ret; +} +static int __init acpi_parse_sbf (struct acpi_table_header *table) {struct acpi_table_boot *sb;
arch/x86/kernel/apic/io_apic.c +11 -0
@@ -4018,6 +4018,17 @@ int mp_unregister_ioapic(u32 gsi_base)return 0 ; } +int mp_ioapic_registered (u32 gsi_base) +{ +int ioapic; + +for_each_ioapic (ioapic) +if (ioapics[ioapic].gsi_config.gsi_base == gsi_base) +return 1 ; + +return 0 ; +} +int mp_irqdomain_map (struct irq_domain *domain,unsigned int virq,irq_hw_number_t hwirq) {
include/linux/acpi.h +1 -0
@@ -152,6 +152,7 @@ int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr);int acpi_register_ioapic (acpi_handle handle,u64 phys_addr,u32 gsi_base);int acpi_unregister_ioapic (acpi_handle handle,u32 gsi_base); +int acpi_ioapic_registered (acpi_handle handle,u32 gsi_base);void acpi_irq_stats_init (void );extern u32 acpi_irq_handled;extern u32 acpi_irq_not_handled;
Signature · 1 line
1.7.10.4
PATCH v8 18/18 x86, irq, ACPI: Implement ACPI driver to support IOAPIC hotplug
In reply to Jiang LiuEnable support of IOAPIC hotplug by:
1) reintroducing ACPI based IOAPIC driver
2) enhance pci_root driver to hook hotplug events
The ACPI IOAPIC driver is always enabled if all of ACPI, PCI and IOAPIC are enabled.
--- arch/x86/kernel/acpi/boot.c | 12 +-- drivers/acpi/Kconfig | 6 ++ drivers/acpi/Makefile | 1 + drivers/acpi/internal.h | 7 ++ drivers/acpi/ioapic.c | 235 +++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 3 + 6 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 drivers/acpi/ioapic.c
arch/x86/kernel/acpi/boot.c +6 -6
@@ -802,9 +802,9 @@ int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base) ioapic_id = (int)uid; } -down_write (&acpi_ioapic_rwsem); +mutex_lock (&acpi_ioapic_lock); ret =mp_register_ioapic (ioapic_id, phys_addr, gsi_base, &cfg); -up_write (&acpi_ioapic_rwsem); +mutex_unlock (&acpi_ioapic_lock);#endif return ret;
@@ -816,9 +816,9 @@ int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)int ret = -ENOSYS ;#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC -down_write (&acpi_ioapic_rwsem); +mutex_lock (&acpi_ioapic_lock); ret =mp_unregister_ioapic (gsi_base); -up_write (&acpi_ioapic_rwsem); +mutex_unlock (&acpi_ioapic_lock);#endif return ret;
@@ -839,9 +839,9 @@ int acpi_ioapic_registered(acpi_handle handle, u32 gsi_base)int ret =0 ;#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC -down_read (&acpi_ioapic_rwsem); +mutex_lock (&acpi_ioapic_lock); ret =mp_ioapic_registered (gsi_base); -up_read (&acpi_ioapic_rwsem); +mutex_unlock (&acpi_ioapic_lock);#endif return ret;
drivers/acpi/Kconfig +6 -0
@@ -315,6 +315,12 @@ config ACPI_HOTPLUG_MEMORY To compile this driver as a module, choose M here: the module will be called acpi_memhotplug. +config ACPI_HOTPLUG_IOAPIC + bool + depends on PCI + depends on X86_IO_APIC + default y + config ACPI_SBS tristate "Smart Battery System" depends on X86
drivers/acpi/Makefile +1 -0
@@ -69,6 +69,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-y += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-y += acpi_memhotplug.o +obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_SBS) += sbshc.o obj-$(CONFIG_ACPI_SBS) += sbs.o
drivers/acpi/internal.h +7 -0
@@ -35,6 +35,13 @@ void acpi_int340x_thermal_init(void);int acpi_sysfs_init (void );void acpi_container_init (void );void acpi_memory_hotplug_init (void ); +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_ioapic_add (struct acpi_pci_root *root); +int acpi_ioapic_remove (struct acpi_pci_root *root); +#else +static inline int acpi_ioapic_add (struct acpi_pci_root *root) {return 0 ; } +static inline int acpi_ioapic_remove (struct acpi_pci_root *root) {return 0 ; } +#endif #ifdef CONFIG_ACPI_DOCK void register_dock_dependent_device (struct acpi_device *adev,acpi_handle dshandle);
drivers/acpi/ioapic.c new file +235 -0
235 lines not on this page · raw message
drivers/acpi/pci_root.c +3 -0
@@ -614,6 +614,7 @@ static int acpi_pci_root_add(struct acpi_device *device,if (system_state !=SYSTEM_BOOTING ) {pcibios_resource_survey_bus (root->bus);pci_assign_unassigned_root_bus_resources (root->bus); +acpi_ioapic_add (root); }pci_lock_rescan_remove ();
@@ -634,6 +635,8 @@ static void acpi_pci_root_remove(struct acpi_device *device)pci_stop_root_bus (root->bus); +WARN_ON (acpi_ioapic_remove (root)); +device_set_run_wake (root->bus->bridge, false);pci_acpi_remove_bus_pm_notifier (device);
Signature · 1 line
1.7.10.4