Commit c7111c13 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Linus Torvalds

[PATCH] x86_64 irq: Allocate a vector across all cpus for genapic_flat.

The problem we can't take advantage of lowest priority delivery mode if
the vectors are allocated for only one cpu at a time.  Nor can we work
around hardware that assumes lowest priority delivery mode is always
used with several cpus.

So this patch introduces the concept of a vector_allocation_domain.  A
set of cpus that will receive an irq on the same vector.  Currently the
code for implementing this is placed in the genapic structure so we can
vary this depending on how we are using the io_apics.

This allows us to restore the previous behaviour of genapic_flat without
removing the benefits of having separate vector allocation for large
machines.

This should also fix the problem report where a hyperthreaded cpu was
receving the irq on the wrong hyperthread when in logical delivery mode
because the previous behaviour is restored.

This patch properly records our allocation of the first 16 irqs to the
first 16 available vectors on all cpus.  This should be fine but it may
run into problems with multiple interrupts at the same interrupt level.
Except for some badly maintained comments in the code and the behaviour
of the interrupt allocator I have no real understanding of that problem.
Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Acked-by: default avatarMuli Ben-Yehuda <muli@il.ibm.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b940d22d
...@@ -63,6 +63,13 @@ static cpumask_t cluster_target_cpus(void) ...@@ -63,6 +63,13 @@ static cpumask_t cluster_target_cpus(void)
return cpumask_of_cpu(0); return cpumask_of_cpu(0);
} }
static cpumask_t cluster_vector_allocation_domain(int cpu)
{
cpumask_t domain = CPU_MASK_NONE;
cpu_set(cpu, domain);
return domain;
}
static void cluster_send_IPI_mask(cpumask_t mask, int vector) static void cluster_send_IPI_mask(cpumask_t mask, int vector)
{ {
send_IPI_mask_sequence(mask, vector); send_IPI_mask_sequence(mask, vector);
...@@ -119,6 +126,7 @@ struct genapic apic_cluster = { ...@@ -119,6 +126,7 @@ struct genapic apic_cluster = {
.int_delivery_mode = dest_Fixed, .int_delivery_mode = dest_Fixed,
.int_dest_mode = (APIC_DEST_PHYSICAL != 0), .int_dest_mode = (APIC_DEST_PHYSICAL != 0),
.target_cpus = cluster_target_cpus, .target_cpus = cluster_target_cpus,
.vector_allocation_domain = cluster_vector_allocation_domain,
.apic_id_registered = cluster_apic_id_registered, .apic_id_registered = cluster_apic_id_registered,
.init_apic_ldr = cluster_init_apic_ldr, .init_apic_ldr = cluster_init_apic_ldr,
.send_IPI_all = cluster_send_IPI_all, .send_IPI_all = cluster_send_IPI_all,
......
...@@ -22,6 +22,20 @@ static cpumask_t flat_target_cpus(void) ...@@ -22,6 +22,20 @@ static cpumask_t flat_target_cpus(void)
return cpu_online_map; return cpu_online_map;
} }
static cpumask_t flat_vector_allocation_domain(int cpu)
{
/* Careful. Some cpus do not strictly honor the set of cpus
* specified in the interrupt destination when using lowest
* priority interrupt delivery mode.
*
* In particular there was a hyperthreading cpu observed to
* deliver interrupts to the wrong hyperthread when only one
* hyperthread was specified in the interrupt desitination.
*/
cpumask_t domain = { { [0] = APIC_ALL_CPUS, } };
return domain;
}
/* /*
* Set up the logical destination ID. * Set up the logical destination ID.
* *
...@@ -121,6 +135,7 @@ struct genapic apic_flat = { ...@@ -121,6 +135,7 @@ struct genapic apic_flat = {
.int_delivery_mode = dest_LowestPrio, .int_delivery_mode = dest_LowestPrio,
.int_dest_mode = (APIC_DEST_LOGICAL != 0), .int_dest_mode = (APIC_DEST_LOGICAL != 0),
.target_cpus = flat_target_cpus, .target_cpus = flat_target_cpus,
.vector_allocation_domain = flat_vector_allocation_domain,
.apic_id_registered = flat_apic_id_registered, .apic_id_registered = flat_apic_id_registered,
.init_apic_ldr = flat_init_apic_ldr, .init_apic_ldr = flat_init_apic_ldr,
.send_IPI_all = flat_send_IPI_all, .send_IPI_all = flat_send_IPI_all,
...@@ -141,6 +156,14 @@ static cpumask_t physflat_target_cpus(void) ...@@ -141,6 +156,14 @@ static cpumask_t physflat_target_cpus(void)
return cpumask_of_cpu(0); return cpumask_of_cpu(0);
} }
static cpumask_t physflat_vector_allocation_domain(int cpu)
{
cpumask_t domain = CPU_MASK_NONE;
cpu_set(cpu, domain);
return domain;
}
static void physflat_send_IPI_mask(cpumask_t cpumask, int vector) static void physflat_send_IPI_mask(cpumask_t cpumask, int vector)
{ {
send_IPI_mask_sequence(cpumask, vector); send_IPI_mask_sequence(cpumask, vector);
...@@ -179,6 +202,7 @@ struct genapic apic_physflat = { ...@@ -179,6 +202,7 @@ struct genapic apic_physflat = {
.int_delivery_mode = dest_Fixed, .int_delivery_mode = dest_Fixed,
.int_dest_mode = (APIC_DEST_PHYSICAL != 0), .int_dest_mode = (APIC_DEST_PHYSICAL != 0),
.target_cpus = physflat_target_cpus, .target_cpus = physflat_target_cpus,
.vector_allocation_domain = physflat_vector_allocation_domain,
.apic_id_registered = flat_apic_id_registered, .apic_id_registered = flat_apic_id_registered,
.init_apic_ldr = flat_init_apic_ldr,/*not needed, but shouldn't hurt*/ .init_apic_ldr = flat_init_apic_ldr,/*not needed, but shouldn't hurt*/
.send_IPI_all = physflat_send_IPI_all, .send_IPI_all = physflat_send_IPI_all,
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
#include <asm/msidef.h> #include <asm/msidef.h>
#include <asm/hypertransport.h> #include <asm/hypertransport.h>
static int assign_irq_vector(int irq, cpumask_t mask); static int assign_irq_vector(int irq, cpumask_t mask, cpumask_t *result);
#define __apicdebuginit __init #define __apicdebuginit __init
...@@ -174,12 +174,10 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) ...@@ -174,12 +174,10 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
cpus_and(mask, tmp, CPU_MASK_ALL); cpus_and(mask, tmp, CPU_MASK_ALL);
vector = assign_irq_vector(irq, mask); vector = assign_irq_vector(irq, mask, &tmp);
if (vector < 0) if (vector < 0)
return; return;
cpus_clear(tmp);
cpu_set(vector >> 8, tmp);
dest = cpu_mask_to_apicid(tmp); dest = cpu_mask_to_apicid(tmp);
/* /*
...@@ -188,7 +186,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) ...@@ -188,7 +186,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
dest = SET_APIC_LOGICAL_ID(dest); dest = SET_APIC_LOGICAL_ID(dest);
spin_lock_irqsave(&ioapic_lock, flags); spin_lock_irqsave(&ioapic_lock, flags);
__target_IO_APIC_irq(irq, dest, vector & 0xff); __target_IO_APIC_irq(irq, dest, vector);
set_native_irq_info(irq, mask); set_native_irq_info(irq, mask);
spin_unlock_irqrestore(&ioapic_lock, flags); spin_unlock_irqrestore(&ioapic_lock, flags);
} }
...@@ -563,9 +561,45 @@ static inline int IO_APIC_irq_trigger(int irq) ...@@ -563,9 +561,45 @@ static inline int IO_APIC_irq_trigger(int irq)
} }
/* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */
unsigned int irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_EXTERNAL_VECTOR, 0 }; static u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = {
[0] = FIRST_EXTERNAL_VECTOR + 0,
[1] = FIRST_EXTERNAL_VECTOR + 1,
[2] = FIRST_EXTERNAL_VECTOR + 2,
[3] = FIRST_EXTERNAL_VECTOR + 3,
[4] = FIRST_EXTERNAL_VECTOR + 4,
[5] = FIRST_EXTERNAL_VECTOR + 5,
[6] = FIRST_EXTERNAL_VECTOR + 6,
[7] = FIRST_EXTERNAL_VECTOR + 7,
[8] = FIRST_EXTERNAL_VECTOR + 8,
[9] = FIRST_EXTERNAL_VECTOR + 9,
[10] = FIRST_EXTERNAL_VECTOR + 10,
[11] = FIRST_EXTERNAL_VECTOR + 11,
[12] = FIRST_EXTERNAL_VECTOR + 12,
[13] = FIRST_EXTERNAL_VECTOR + 13,
[14] = FIRST_EXTERNAL_VECTOR + 14,
[15] = FIRST_EXTERNAL_VECTOR + 15,
};
static cpumask_t irq_domain[NR_IRQ_VECTORS] __read_mostly = {
[0] = CPU_MASK_ALL,
[1] = CPU_MASK_ALL,
[2] = CPU_MASK_ALL,
[3] = CPU_MASK_ALL,
[4] = CPU_MASK_ALL,
[5] = CPU_MASK_ALL,
[6] = CPU_MASK_ALL,
[7] = CPU_MASK_ALL,
[8] = CPU_MASK_ALL,
[9] = CPU_MASK_ALL,
[10] = CPU_MASK_ALL,
[11] = CPU_MASK_ALL,
[12] = CPU_MASK_ALL,
[13] = CPU_MASK_ALL,
[14] = CPU_MASK_ALL,
[15] = CPU_MASK_ALL,
};
static int __assign_irq_vector(int irq, cpumask_t mask) static int __assign_irq_vector(int irq, cpumask_t mask, cpumask_t *result)
{ {
/* /*
* NOTE! The local APIC isn't very good at handling * NOTE! The local APIC isn't very good at handling
...@@ -589,14 +623,22 @@ static int __assign_irq_vector(int irq, cpumask_t mask) ...@@ -589,14 +623,22 @@ static int __assign_irq_vector(int irq, cpumask_t mask)
if (irq_vector[irq] > 0) if (irq_vector[irq] > 0)
old_vector = irq_vector[irq]; old_vector = irq_vector[irq];
if ((old_vector > 0) && cpu_isset(old_vector >> 8, mask)) { if (old_vector > 0) {
cpus_and(*result, irq_domain[irq], mask);
if (!cpus_empty(*result))
return old_vector; return old_vector;
} }
for_each_cpu_mask(cpu, mask) { for_each_cpu_mask(cpu, mask) {
cpumask_t domain;
int first, new_cpu;
int vector, offset; int vector, offset;
vector = pos[cpu].vector;
offset = pos[cpu].offset; domain = vector_allocation_domain(cpu);
first = first_cpu(domain);
vector = pos[first].vector;
offset = pos[first].offset;
next: next:
vector += 8; vector += 8;
if (vector >= FIRST_SYSTEM_VECTOR) { if (vector >= FIRST_SYSTEM_VECTOR) {
...@@ -604,35 +646,40 @@ static int __assign_irq_vector(int irq, cpumask_t mask) ...@@ -604,35 +646,40 @@ static int __assign_irq_vector(int irq, cpumask_t mask)
offset = (offset + 1) % 8; offset = (offset + 1) % 8;
vector = FIRST_DEVICE_VECTOR + offset; vector = FIRST_DEVICE_VECTOR + offset;
} }
if (unlikely(pos[cpu].vector == vector)) if (unlikely(pos[first].vector == vector))
continue; continue;
if (vector == IA32_SYSCALL_VECTOR) if (vector == IA32_SYSCALL_VECTOR)
goto next; goto next;
for_each_cpu_mask(new_cpu, domain)
if (per_cpu(vector_irq, cpu)[vector] != -1) if (per_cpu(vector_irq, cpu)[vector] != -1)
goto next; goto next;
/* Found one! */ /* Found one! */
for_each_cpu_mask(new_cpu, domain) {
pos[cpu].vector = vector; pos[cpu].vector = vector;
pos[cpu].offset = offset; pos[cpu].offset = offset;
}
if (old_vector >= 0) { if (old_vector >= 0) {
int old_cpu = old_vector >> 8; int old_cpu;
old_vector &= 0xff; for_each_cpu_mask(old_cpu, domain)
per_cpu(vector_irq, old_cpu)[old_vector] = -1; per_cpu(vector_irq, old_cpu)[old_vector] = -1;
} }
per_cpu(vector_irq, cpu)[vector] = irq; for_each_cpu_mask(new_cpu, domain)
vector |= cpu << 8; per_cpu(vector_irq, new_cpu)[vector] = irq;
irq_vector[irq] = vector; irq_vector[irq] = vector;
irq_domain[irq] = domain;
cpus_and(*result, domain, mask);
return vector; return vector;
} }
return -ENOSPC; return -ENOSPC;
} }
static int assign_irq_vector(int irq, cpumask_t mask) static int assign_irq_vector(int irq, cpumask_t mask, cpumask_t *result)
{ {
int vector; int vector;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&vector_lock, flags); spin_lock_irqsave(&vector_lock, flags);
vector = __assign_irq_vector(irq, mask); vector = __assign_irq_vector(irq, mask, result);
spin_unlock_irqrestore(&vector_lock, flags); spin_unlock_irqrestore(&vector_lock, flags);
return vector; return vector;
} }
...@@ -704,14 +751,12 @@ static void __init setup_IO_APIC_irqs(void) ...@@ -704,14 +751,12 @@ static void __init setup_IO_APIC_irqs(void)
if (IO_APIC_IRQ(irq)) { if (IO_APIC_IRQ(irq)) {
cpumask_t mask; cpumask_t mask;
vector = assign_irq_vector(irq, TARGET_CPUS); vector = assign_irq_vector(irq, TARGET_CPUS, &mask);
if (vector < 0) if (vector < 0)
continue; continue;
cpus_clear(mask);
cpu_set(vector >> 8, mask);
entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask);
entry.vector = vector & 0xff; entry.vector = vector;
ioapic_register_intr(irq, vector, IOAPIC_AUTO); ioapic_register_intr(irq, vector, IOAPIC_AUTO);
if (!apic && (irq < 16)) if (!apic && (irq < 16))
...@@ -1430,12 +1475,13 @@ static inline void check_timer(void) ...@@ -1430,12 +1475,13 @@ static inline void check_timer(void)
{ {
int apic1, pin1, apic2, pin2; int apic1, pin1, apic2, pin2;
int vector; int vector;
cpumask_t mask;
/* /*
* get/set the timer IRQ vector: * get/set the timer IRQ vector:
*/ */
disable_8259A_irq(0); disable_8259A_irq(0);
vector = assign_irq_vector(0, TARGET_CPUS); vector = assign_irq_vector(0, TARGET_CPUS, &mask);
/* /*
* Subtle, code in do_timer_interrupt() expects an AEOI * Subtle, code in do_timer_interrupt() expects an AEOI
...@@ -1667,6 +1713,7 @@ int create_irq(void) ...@@ -1667,6 +1713,7 @@ int create_irq(void)
int new; int new;
int vector = 0; int vector = 0;
unsigned long flags; unsigned long flags;
cpumask_t mask;
irq = -ENOSPC; irq = -ENOSPC;
spin_lock_irqsave(&vector_lock, flags); spin_lock_irqsave(&vector_lock, flags);
...@@ -1675,7 +1722,7 @@ int create_irq(void) ...@@ -1675,7 +1722,7 @@ int create_irq(void)
continue; continue;
if (irq_vector[new] != 0) if (irq_vector[new] != 0)
continue; continue;
vector = __assign_irq_vector(new, TARGET_CPUS); vector = __assign_irq_vector(new, TARGET_CPUS, &mask);
if (likely(vector > 0)) if (likely(vector > 0))
irq = new; irq = new;
break; break;
...@@ -1707,13 +1754,10 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms ...@@ -1707,13 +1754,10 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms
{ {
int vector; int vector;
unsigned dest; unsigned dest;
vector = assign_irq_vector(irq, TARGET_CPUS);
if (vector >= 0) {
cpumask_t tmp; cpumask_t tmp;
cpus_clear(tmp); vector = assign_irq_vector(irq, TARGET_CPUS, &tmp);
cpu_set(vector >> 8, tmp); if (vector >= 0) {
dest = cpu_mask_to_apicid(tmp); dest = cpu_mask_to_apicid(tmp);
msg->address_hi = MSI_ADDR_BASE_HI; msg->address_hi = MSI_ADDR_BASE_HI;
...@@ -1752,12 +1796,10 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) ...@@ -1752,12 +1796,10 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask)
cpus_and(mask, tmp, CPU_MASK_ALL); cpus_and(mask, tmp, CPU_MASK_ALL);
vector = assign_irq_vector(irq, mask); vector = assign_irq_vector(irq, mask, &tmp);
if (vector < 0) if (vector < 0)
return; return;
cpus_clear(tmp);
cpu_set(vector >> 8, tmp);
dest = cpu_mask_to_apicid(tmp); dest = cpu_mask_to_apicid(tmp);
read_msi_msg(irq, &msg); read_msi_msg(irq, &msg);
...@@ -1844,12 +1886,10 @@ static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) ...@@ -1844,12 +1886,10 @@ static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask)
cpus_and(mask, tmp, CPU_MASK_ALL); cpus_and(mask, tmp, CPU_MASK_ALL);
vector = assign_irq_vector(irq, mask); vector = assign_irq_vector(irq, mask, &tmp);
if (vector < 0) if (vector < 0)
return; return;
cpus_clear(tmp);
cpu_set(vector >> 8, tmp);
dest = cpu_mask_to_apicid(tmp); dest = cpu_mask_to_apicid(tmp);
target_ht_irq(irq, dest, vector & 0xff); target_ht_irq(irq, dest, vector & 0xff);
...@@ -1871,15 +1911,13 @@ static struct hw_interrupt_type ht_irq_chip = { ...@@ -1871,15 +1911,13 @@ static struct hw_interrupt_type ht_irq_chip = {
int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
{ {
int vector; int vector;
cpumask_t tmp;
vector = assign_irq_vector(irq, TARGET_CPUS); vector = assign_irq_vector(irq, TARGET_CPUS, &tmp);
if (vector >= 0) { if (vector >= 0) {
u32 low, high; u32 low, high;
unsigned dest; unsigned dest;
cpumask_t tmp;
cpus_clear(tmp);
cpu_set(vector >> 8, tmp);
dest = cpu_mask_to_apicid(tmp); dest = cpu_mask_to_apicid(tmp);
high = HT_IRQ_HIGH_DEST_ID(dest); high = HT_IRQ_HIGH_DEST_ID(dest);
...@@ -1945,13 +1983,10 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p ...@@ -1945,13 +1983,10 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int p
add_pin_to_irq(irq, ioapic, pin); add_pin_to_irq(irq, ioapic, pin);
vector = assign_irq_vector(irq, TARGET_CPUS); vector = assign_irq_vector(irq, TARGET_CPUS, &mask);
if (vector < 0) if (vector < 0)
return vector; return vector;
cpus_clear(mask);
cpu_set(vector >> 8, mask);
/* /*
* Generate a PCI IRQ routing entry and program the IOAPIC accordingly. * Generate a PCI IRQ routing entry and program the IOAPIC accordingly.
* Note that we mask (disable) IRQs now -- these get enabled when the * Note that we mask (disable) IRQs now -- these get enabled when the
......
...@@ -18,6 +18,7 @@ struct genapic { ...@@ -18,6 +18,7 @@ struct genapic {
u32 int_dest_mode; u32 int_dest_mode;
int (*apic_id_registered)(void); int (*apic_id_registered)(void);
cpumask_t (*target_cpus)(void); cpumask_t (*target_cpus)(void);
cpumask_t (*vector_allocation_domain)(int cpu);
void (*init_apic_ldr)(void); void (*init_apic_ldr)(void);
/* ipi */ /* ipi */
void (*send_IPI_mask)(cpumask_t mask, int vector); void (*send_IPI_mask)(cpumask_t mask, int vector);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#define INT_DELIVERY_MODE (genapic->int_delivery_mode) #define INT_DELIVERY_MODE (genapic->int_delivery_mode)
#define INT_DEST_MODE (genapic->int_dest_mode) #define INT_DEST_MODE (genapic->int_dest_mode)
#define TARGET_CPUS (genapic->target_cpus()) #define TARGET_CPUS (genapic->target_cpus())
#define vector_allocation_domain (genapic->vector_allocation_domain)
#define apic_id_registered (genapic->apic_id_registered) #define apic_id_registered (genapic->apic_id_registered)
#define init_apic_ldr (genapic->init_apic_ldr) #define init_apic_ldr (genapic->init_apic_ldr)
#define send_IPI_mask (genapic->send_IPI_mask) #define send_IPI_mask (genapic->send_IPI_mask)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment