kernel: Allow reassigning IRQs to logical processors

This commit is contained in:
Pawel Dziepak
2013-11-18 04:55:25 +01:00
parent 955c7edec2
commit d897a478d7
12 changed files with 197 additions and 31 deletions
+1
View File
@@ -34,6 +34,7 @@ void arch_int_enable_io_interrupt(int irq);
void arch_int_disable_io_interrupt(int irq); void arch_int_disable_io_interrupt(int irq);
void arch_int_configure_io_interrupt(int irq, uint32 config); void arch_int_configure_io_interrupt(int irq, uint32 config);
bool arch_int_are_interrupts_enabled(void); bool arch_int_are_interrupts_enabled(void);
void arch_int_assign_to_cpu(int32 irq, int32 cpu);
#ifdef __cplusplus #ifdef __cplusplus
} }
@@ -68,6 +68,7 @@ typedef struct interrupt_controller_s {
bool (*is_spurious_interrupt)(int32 num); bool (*is_spurious_interrupt)(int32 num);
bool (*is_level_triggered_interrupt)(int32 num); bool (*is_level_triggered_interrupt)(int32 num);
bool (*end_of_interrupt)(int32 num); bool (*end_of_interrupt)(int32 num);
void (*assign_interrupt_to_cpu)(int32 num, int32 cpu);
} interrupt_controller; } interrupt_controller;
@@ -99,4 +99,18 @@ enum {
MP_INTR_TYPE_ExtINT, MP_INTR_TYPE_ExtINT,
}; };
#ifdef __cplusplus
extern "C" {
#endif
uint32 x86_get_cpu_apic_id(int32 cpu);
#ifdef __cplusplus
}
#endif
#endif /* _KERNEL_ARCH_x86_ARCH_SMP_H */ #endif /* _KERNEL_ARCH_x86_ARCH_SMP_H */
+6 -1
View File
@@ -11,6 +11,7 @@
#include <setjmp.h> #include <setjmp.h>
#include <int.h>
#include <smp.h> #include <smp.h>
#include <timer.h> #include <timer.h>
#include <arch/cpu.h> #include <arch/cpu.h>
@@ -79,8 +80,12 @@ typedef struct cpu_ent {
int topology_id[CPU_TOPOLOGY_LEVELS]; int topology_id[CPU_TOPOLOGY_LEVELS];
int cache_id[CPU_MAX_CACHE_LEVEL]; int cache_id[CPU_MAX_CACHE_LEVEL];
// IRQs assigned to this CPU
struct list irqs;
spinlock irqs_lock;
// arch-specific stuff // arch-specific stuff
arch_cpu_info arch; arch_cpu_info arch;
} cpu_ent __attribute__((aligned(64))); } cpu_ent __attribute__((aligned(64)));
+16
View File
@@ -12,6 +12,8 @@
#include <KernelExport.h> #include <KernelExport.h>
#include <arch/int.h> #include <arch/int.h>
#include <util/list.h>
// private install_io_interrupt_handler() flags // private install_io_interrupt_handler() flags
#define B_NO_LOCK_VECTOR 0x100 #define B_NO_LOCK_VECTOR 0x100
#define B_NO_HANDLED_INFO 0x200 #define B_NO_HANDLED_INFO 0x200
@@ -28,6 +30,18 @@ enum interrupt_type {
INTERRUPT_TYPE_UNKNOWN INTERRUPT_TYPE_UNKNOWN
}; };
struct irq_assignment {
list_link link;
uint32 irq;
spinlock load_lock;
bigtime_t last_measure_time;
bigtime_t last_measure_active;
int32 load;
int32 cpu;
};
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -68,4 +82,6 @@ status_t reserve_io_interrupt_vectors(long count, long startVector,
status_t allocate_io_interrupt_vectors(long count, long *startVector); status_t allocate_io_interrupt_vectors(long count, long *startVector);
void free_io_interrupt_vectors(long count, long startVector); void free_io_interrupt_vectors(long count, long startVector);
void assign_io_interrupt_to_cpu(long vector, int32 cpu);
#endif /* _KERNEL_INT_H */ #endif /* _KERNEL_INT_H */
+8
View File
@@ -389,6 +389,14 @@ arch_int_are_interrupts_enabled(void)
} }
void
arch_int_assign_to_cpu(int32 irq, int32 cpu)
{
if (sCurrentPIC->assign_interrupt_to_cpu != NULL)
sCurrentPIC->assign_interrupt_to_cpu(irq, cpu);
}
status_t status_t
arch_int_init(kernel_args* args) arch_int_init(kernel_args* args)
{ {
+8
View File
@@ -70,6 +70,14 @@ x86_smp_error_interrupt(void *data)
} }
uint32
x86_get_cpu_apic_id(int32 cpu)
{
ASSERT(cpu >= 0 && cpu < B_MAX_CPU_COUNT);
return sCPUAPICIds[cpu];
}
status_t status_t
arch_smp_init(kernel_args *args) arch_smp_init(kernel_args *args)
{ {
+36 -10
View File
@@ -18,6 +18,7 @@
#include <arch/x86/apic.h> #include <arch/x86/apic.h>
#include <arch/x86/arch_int.h> #include <arch/x86/arch_int.h>
#include <arch/x86/arch_smp.h>
#include <arch/x86/pic.h> #include <arch/x86/pic.h>
// to gain access to the ACPICA types // to gain access to the ACPICA types
@@ -26,9 +27,9 @@
//#define TRACE_IOAPIC //#define TRACE_IOAPIC
#ifdef TRACE_IOAPIC #ifdef TRACE_IOAPIC
# define TRACE(x) dprintf x # define TRACE(...) dprintf(__VA_ARGS__)
#else #else
# define TRACE(x) ; # define TRACE(...) (void)0
#endif #endif
@@ -242,6 +243,30 @@ ioapic_end_of_interrupt(int32 num)
} }
static void
ioapic_assign_interrupt_to_cpu(int32 gsi, int32 cpu)
{
if (gsi < ISA_INTERRUPT_COUNT && sSourceOverrides[gsi] != 0)
gsi = sSourceOverrides[gsi];
struct ioapic* ioapic = find_ioapic(gsi);
if (ioapic == NULL)
return;
uint32 apicid = x86_get_cpu_apic_id(cpu);
uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE("ioapic_assign_interrupt_to_cpu: gsi %ld (io-apic %u pin %u) to"
" cpu %ld (apic_id %lu)\n", gsi, ioapic->number, pin, cpu, apicid);
uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
entry &= ~(uint64(IO_APIC_DESTINATION_FIELD_MASK)
<< IO_APIC_DESTINATION_FIELD_SHIFT);
entry |= uint64(apicid) << IO_APIC_DESTINATION_FIELD_SHIFT;
ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry, false);
}
static void static void
ioapic_enable_io_interrupt(int32 gsi) ioapic_enable_io_interrupt(int32 gsi)
{ {
@@ -256,8 +281,8 @@ ioapic_enable_io_interrupt(int32 gsi)
return; return;
uint8 pin = gsi - ioapic->global_interrupt_base; uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE(("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", TRACE("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
gsi, ioapic->number, pin)); gsi, ioapic->number, pin);
uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
entry &= ~IO_APIC_INTERRUPT_MASKED; entry &= ~IO_APIC_INTERRUPT_MASKED;
@@ -273,8 +298,8 @@ ioapic_disable_io_interrupt(int32 gsi)
return; return;
uint8 pin = gsi - ioapic->global_interrupt_base; uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE(("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", TRACE("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
gsi, ioapic->number, pin)); gsi, ioapic->number, pin);
uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
entry |= IO_APIC_INTERRUPT_MASKED; entry |= IO_APIC_INTERRUPT_MASKED;
@@ -290,8 +315,8 @@ ioapic_configure_io_interrupt(int32 gsi, uint32 config)
return; return;
uint8 pin = gsi - ioapic->global_interrupt_base; uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE(("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " TRACE("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; "
"config 0x%08lx\n", gsi, ioapic->number, pin, config)); "config 0x%08lx\n", gsi, ioapic->number, pin, config);
ioapic_configure_pin(*ioapic, pin, gsi, config, ioapic_configure_pin(*ioapic, pin, gsi, config,
IO_APIC_DELIVERY_MODE_FIXED); IO_APIC_DELIVERY_MODE_FIXED);
@@ -310,7 +335,7 @@ ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
return ioapic.register_area; return ioapic.register_area;
} }
TRACE(("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers)); TRACE("mapped io-apic %u to %p\n", ioapic.number, ioapic.registers);
ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION); ioapic.version = ioapic_read_32(ioapic, IO_APIC_VERSION);
if (ioapic.version == 0xffffffff) { if (ioapic.version == 0xffffffff) {
@@ -650,7 +675,8 @@ ioapic_init(kernel_args* args)
&ioapic_configure_io_interrupt, &ioapic_configure_io_interrupt,
&ioapic_is_spurious_interrupt, &ioapic_is_spurious_interrupt,
&ioapic_is_level_triggered_interrupt, &ioapic_is_level_triggered_interrupt,
&ioapic_end_of_interrupt &ioapic_end_of_interrupt,
&ioapic_assign_interrupt_to_cpu,
}; };
if (args->arch_args.apic == NULL) if (args->arch_args.apic == NULL)
+2 -1
View File
@@ -196,7 +196,8 @@ pic_init()
&pic_configure_io_interrupt, &pic_configure_io_interrupt,
&pic_is_spurious_interrupt, &pic_is_spurious_interrupt,
&pic_is_level_triggered_interrupt, &pic_is_level_triggered_interrupt,
&pic_end_of_interrupt &pic_end_of_interrupt,
NULL
}; };
// Start initialization sequence for the master and slave PICs // Start initialization sequence for the master and slave PICs
+3
View File
@@ -99,6 +99,9 @@ cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
memset(&gCPU[curr_cpu], 0, sizeof(gCPU[curr_cpu])); memset(&gCPU[curr_cpu], 0, sizeof(gCPU[curr_cpu]));
gCPU[curr_cpu].cpu_num = curr_cpu; gCPU[curr_cpu].cpu_num = curr_cpu;
list_init(&gCPU[curr_cpu].irqs);
B_INITIALIZE_SPINLOCK(&gCPU[curr_cpu].irqs_lock);
return arch_cpu_preboot_init_percpu(args, curr_cpu); return arch_cpu_preboot_init_percpu(args, curr_cpu);
} }
+100 -17
View File
@@ -1,4 +1,7 @@
/* /*
* Copyright 2013, Paweł Dziepak, [email protected].
* Distributed under the terms of the MIT License.
* Copyright 2011, Michael Lotz, [email protected]. * Copyright 2011, Michael Lotz, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
@@ -54,10 +57,7 @@ struct io_vector {
bool no_lock_vector; bool no_lock_vector;
interrupt_type type; interrupt_type type;
spinlock load_lock; irq_assignment assigned_cpu;
bigtime_t last_measure_time;
bigtime_t last_measure_active;
int32 load;
#if DEBUG_INTERRUPTS #if DEBUG_INTERRUPTS
int64 handled_count; int64 handled_count;
@@ -67,6 +67,7 @@ struct io_vector {
#endif #endif
}; };
static uint32 sLastCPU;
static struct io_vector sVectors[NUM_IO_VECTORS]; static struct io_vector sVectors[NUM_IO_VECTORS];
static bool sAllocatedIOInterruptVectors[NUM_IO_VECTORS]; static bool sAllocatedIOInterruptVectors[NUM_IO_VECTORS];
static mutex sIOInterruptVectorAllocationLock static mutex sIOInterruptVectorAllocationLock
@@ -137,10 +138,20 @@ dump_int_load(int argc, char** argv)
continue; continue;
kprintf("int %3d, type %s, enabled %" B_PRId32 ", load %" B_PRId32 kprintf("int %3d, type %s, enabled %" B_PRId32 ", load %" B_PRId32
"%%%s\n", i, typeNames[min_c(sVectors[i].type, "%%", i, typeNames[min_c(sVectors[i].type,
INTERRUPT_TYPE_UNKNOWN)], INTERRUPT_TYPE_UNKNOWN)],
sVectors[i].enable_count, sVectors[i].load / 10, sVectors[i].enable_count, sVectors[i].assigned_cpu.load / 10);
B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock) ? ", ACTIVE" : "");
if (sVectors[i].type == INTERRUPT_TYPE_IRQ) {
if (sVectors[i].assigned_cpu.cpu != -1)
kprintf(", cpu %" B_PRId32, sVectors[i].assigned_cpu.cpu);
else
kprintf(", cpu -");
}
if (B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock))
kprintf(", ACTIVE");
kprintf("\n");
} }
return 0; return 0;
@@ -178,10 +189,15 @@ int_init_post_vm(kernel_args* args)
sVectors[i].no_lock_vector = false; sVectors[i].no_lock_vector = false;
sVectors[i].type = INTERRUPT_TYPE_UNKNOWN; sVectors[i].type = INTERRUPT_TYPE_UNKNOWN;
B_INITIALIZE_SPINLOCK(&sVectors[i].load_lock); irq_assignment* assigned_cpu = &sVectors[i].assigned_cpu;
sVectors[i].last_measure_time = 0; assigned_cpu->irq = i;
sVectors[i].last_measure_active = 0;
sVectors[i].load = 0; B_INITIALIZE_SPINLOCK(&assigned_cpu->load_lock);
assigned_cpu->last_measure_time = 0;
assigned_cpu->last_measure_active = 0;
assigned_cpu->load = 0;
assigned_cpu->cpu = -1;
#if DEBUG_INTERRUPTS #if DEBUG_INTERRUPTS
sVectors[i].handled_count = 0; sVectors[i].handled_count = 0;
@@ -223,11 +239,14 @@ int_init_post_device_manager(kernel_args* args)
static void static void
update_int_load(int i) update_int_load(int i)
{ {
if (!try_acquire_spinlock(&sVectors[i].load_lock)) if (!try_acquire_spinlock(&sVectors[i].assigned_cpu.load_lock))
return; return;
compute_load(sVectors[i].last_measure_time, sVectors[i].last_measure_active,
sVectors[i].load); compute_load(sVectors[i].assigned_cpu.last_measure_time,
release_spinlock(&sVectors[i].load_lock); sVectors[i].assigned_cpu.last_measure_active,
sVectors[i].assigned_cpu.load);
release_spinlock(&sVectors[i].assigned_cpu.load_lock);
} }
@@ -325,9 +344,9 @@ int_io_interrupt_handler(int vector, bool levelTriggered)
if (!sVectors[vector].no_lock_vector) if (!sVectors[vector].no_lock_vector)
release_spinlock(&sVectors[vector].vector_lock); release_spinlock(&sVectors[vector].vector_lock);
SpinLocker locker(sVectors[vector].load_lock); SpinLocker locker(sVectors[vector].assigned_cpu.load_lock);
bigtime_t deltaTime = system_time() - start; bigtime_t deltaTime = system_time() - start;
sVectors[vector].last_measure_active += deltaTime; sVectors[vector].assigned_cpu.last_measure_active += deltaTime;
locker.Unlock(); locker.Unlock();
atomic_add64(&get_cpu_struct()->interrupt_time, deltaTime); atomic_add64(&get_cpu_struct()->interrupt_time, deltaTime);
@@ -402,6 +421,23 @@ install_io_interrupt_handler(long vector, interrupt_handler handler, void *data,
state = disable_interrupts(); state = disable_interrupts();
acquire_spinlock(&sVectors[vector].vector_lock); acquire_spinlock(&sVectors[vector].vector_lock);
// Initial attempt to balance IRQs, the scheduler will correct this
// if some cores end up being overloaded.
if (sVectors[vector].type == INTERRUPT_TYPE_IRQ
&& sVectors[vector].handler_list == NULL) {
int32 cpuID = atomic_add(&sLastCPU, 1) % smp_get_num_cpus();
arch_int_assign_to_cpu(vector, cpuID);
ASSERT(sVectors[vector].assigned_cpu.cpu == -1);
sVectors[vector].assigned_cpu.cpu = cpuID;
cpu_ent* cpu = &gCPU[cpuID];
SpinLocker _(cpu->irqs_lock);
list_add_item(&cpu->irqs, &sVectors[vector].assigned_cpu);
}
if ((flags & B_NO_HANDLED_INFO) != 0 if ((flags & B_NO_HANDLED_INFO) != 0
&& sVectors[vector].handler_list != NULL) { && sVectors[vector].handler_list != NULL) {
// The driver registering this interrupt handler doesn't know // The driver registering this interrupt handler doesn't know
@@ -441,6 +477,7 @@ install_io_interrupt_handler(long vector, interrupt_handler handler, void *data,
sVectors[vector].no_lock_vector = true; sVectors[vector].no_lock_vector = true;
release_spinlock(&sVectors[vector].vector_lock); release_spinlock(&sVectors[vector].vector_lock);
restore_interrupts(state); restore_interrupts(state);
return B_OK; return B_OK;
@@ -486,6 +523,28 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
last = io; last = io;
} }
if (sVectors[vector].handler_list == NULL
&& sVectors[vector].type == INTERRUPT_TYPE_IRQ) {
int32 oldCPU;
SpinLocker locker;
cpu_ent* cpu;
do {
locker.Unlock();
oldCPU = sVectors[vector].assigned_cpu.cpu;
ASSERT(oldCPU != -1);
cpu = &gCPU[oldCPU];
locker.SetTo(cpu->irqs_lock, false);
} while (sVectors[vector].assigned_cpu.cpu != oldCPU);
sVectors[vector].assigned_cpu.cpu = -1;
list_remove_item(&cpu->irqs, &sVectors[vector].assigned_cpu);
}
release_spinlock(&sVectors[vector].vector_lock); release_spinlock(&sVectors[vector].vector_lock);
restore_interrupts(state); restore_interrupts(state);
@@ -596,3 +655,27 @@ free_io_interrupt_vectors(long count, long startVector)
sAllocatedIOInterruptVectors[startVector + i] = false; sAllocatedIOInterruptVectors[startVector + i] = false;
} }
} }
void assign_io_interrupt_to_cpu(long vector, int32 newCPU)
{
ASSERT(sVectors[vector].type == INTERRUPT_TYPE_IRQ);
int32 oldCPU = sVectors[vector].assigned_cpu.cpu;
ASSERT(oldCPU != -1);
cpu_ent* cpu = &gCPU[oldCPU];
SpinLocker locker(cpu->irqs_lock);
sVectors[vector].assigned_cpu.cpu = -1;
list_remove_item(&cpu->irqs, &sVectors[vector].assigned_cpu);
locker.Unlock();
cpu = &gCPU[newCPU];
locker.SetTo(cpu->irqs_lock, false);
sVectors[vector].assigned_cpu.cpu = newCPU;
arch_int_assign_to_cpu(vector, newCPU);
list_add_item(&cpu->irqs, &sVectors[vector].assigned_cpu);
locker.Unlock();
}
+2 -2
View File
@@ -53,8 +53,8 @@ dump_info(int argc, char **argv)
kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus()); kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus());
for (int32 i = 0; i < smp_get_num_cpus(); i++) for (int32 i = 0; i < smp_get_num_cpus(); i++)
kprintf(" [%" B_PRId32 "] active time: %12" B_PRId64 ", interrupt" kprintf(" [%" B_PRId32 "] active time: %10" B_PRId64 ", interrupt"
" time: %12" B_PRId64 ", irq time: %12" B_PRId64 "\n", i + 1, " time: %10" B_PRId64 ", irq time: %10" B_PRId64 "\n", i + 1,
gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time); gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time);
// ToDo: Add page_faults // ToDo: Add page_faults