x86, irq: Refine mp_register_ioapic() to prepare for IOAPIC hotplug
authorJiang Liu <jiang.liu@linux.intel.com>
Mon, 27 Oct 2014 05:21:43 +0000 (13:21 +0800)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 16 Dec 2014 13:08:15 +0000 (14:08 +0100)
Refine 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

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Prarit Bhargava <prarit@redhat.com>
Link: http://lkml.kernel.org/r/1414387308-27148-14-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/include/asm/io_apic.h
arch/x86/kernel/apic/io_apic.c

index 0b31aebd9405a2251e96e6ddd0e87af8af8780f6..94d05bd6586f6426bfc631de6bc0b5e43044a7ae 100644 (file)
@@ -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);
index 60f25e88734b3b1c2c7105418f5fbedc938486fa..4333a751937d85c5ee6705ba8e90647f6e774342 100644 (file)
@@ -3840,20 +3840,6 @@ int mp_find_ioapic_pin(int ioapic, u32 gsi)
        return gsi - gsi_cfg->gsi_base;
 }
 
-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",
-                       MAX_IO_APICS, nr_ioapics);
-               return 1;
-       }
-       if (!address) {
-               pr_warn("WARNING: Bogus (zero) I/O APIC address found in table, skipping!\n");
-               return 1;
-       }
-       return 0;
-}
-
 static int bad_ioapic_register(int idx)
 {
        union IO_APIC_reg_00 reg_00;
@@ -3873,29 +3859,51 @@ static int bad_ioapic_register(int idx)
        return 0;
 }
 
-void mp_register_ioapic(int id, u32 address, u32 gsi_base,
-                       struct ioapic_domain_cfg *cfg)
+static int find_free_ioapic_entry(void)
+{
+       return nr_ioapics;
+}
+
+/**
+ * mp_register_ioapic - Register an IOAPIC device
+ * @id:                hardware IOAPIC ID
+ * @address:   physical address of IOAPIC register area
+ * @gsi_base:  base of GSI associated with the IOAPIC
+ * @cfg:       configuration information for the IOAPIC
+ */
+int mp_register_ioapic(int id, u32 address, u32 gsi_base,
+                      struct ioapic_domain_cfg *cfg)
 {
-       int idx = 0;
-       int entries;
        struct mp_ioapic_gsi *gsi_cfg;
+       int idx, ioapic, entries;
+       u32 gsi_end;
 
-       if (bad_ioapic(address))
-               return;
+       if (!address) {
+               pr_warn("Bogus (zero) I/O APIC address found, skipping!\n");
+               return -EINVAL;
+       }
+       for_each_ioapic(ioapic)
+               if (ioapics[ioapic].mp_config.apicaddr == address) {
+                       pr_warn("address 0x%x conflicts with IOAPIC%d\n",
+                               address, ioapic);
+                       return -EEXIST;
+               }
 
-       idx = nr_ioapics;
+       idx = find_free_ioapic_entry();
+       if (idx >= MAX_IO_APICS) {
+               pr_warn("Max # of I/O APICs (%d) exceeded (found %d), skipping\n",
+                       MAX_IO_APICS, idx);
+               return -ENOSPC;
+       }
 
        ioapics[idx].mp_config.type = MP_IOAPIC;
        ioapics[idx].mp_config.flags = MPC_APIC_USABLE;
        ioapics[idx].mp_config.apicaddr = address;
-       ioapics[idx].irqdomain = NULL;
-       ioapics[idx].irqdomain_cfg = *cfg;
 
        set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address);
-
        if (bad_ioapic_register(idx)) {
                clear_fixmap(FIX_IO_APIC_BASE_0 + idx);
-               return;
+               return -ENODEV;
        }
 
        ioapics[idx].mp_config.apicid = io_apic_unique_id(idx, id);
@@ -3906,24 +3914,41 @@ void mp_register_ioapic(int id, u32 address, u32 gsi_base,
         * and to prevent reprogramming of IOAPIC pins (PCI GSIs).
         */
        entries = io_apic_get_redir_entries(idx);
+       gsi_end = gsi_base + entries - 1;
+       for_each_ioapic(ioapic) {
+               gsi_cfg = mp_ioapic_gsi_routing(ioapic);
+               if ((gsi_base >= gsi_cfg->gsi_base &&
+                    gsi_base <= gsi_cfg->gsi_end) ||
+                   (gsi_end >= gsi_cfg->gsi_base &&
+                    gsi_end <= gsi_cfg->gsi_end)) {
+                       pr_warn("GSI range [%u-%u] for new IOAPIC conflicts with GSI[%u-%u]\n",
+                               gsi_base, gsi_end,
+                               gsi_cfg->gsi_base, gsi_cfg->gsi_end);
+                       clear_fixmap(FIX_IO_APIC_BASE_0 + idx);
+                       return -ENOSPC;
+               }
+       }
        gsi_cfg = mp_ioapic_gsi_routing(idx);
        gsi_cfg->gsi_base = gsi_base;
-       gsi_cfg->gsi_end = gsi_base + entries - 1;
+       gsi_cfg->gsi_end = gsi_end;
 
-       /*
-        * The number of IO-APIC IRQ registers (== #pins):
-        */
-       ioapics[idx].nr_registers = entries;
+       ioapics[idx].irqdomain = NULL;
+       ioapics[idx].irqdomain_cfg = *cfg;
 
        if (gsi_cfg->gsi_end >= gsi_top)
                gsi_top = gsi_cfg->gsi_end + 1;
+       if (nr_ioapics <= idx)
+               nr_ioapics = idx + 1;
+
+       /* Set nr_registers to mark entry present */
+       ioapics[idx].nr_registers = entries;
 
        pr_info("IOAPIC[%d]: apic_id %d, version %d, address 0x%x, GSI %d-%d\n",
                idx, mpc_ioapic_id(idx),
                mpc_ioapic_ver(idx), mpc_ioapic_addr(idx),
                gsi_cfg->gsi_base, gsi_cfg->gsi_end);
 
-       nr_ioapics++;
+       return 0;
 }
 
 int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq,