added 3 changesets to branch 'refs/remotes/pdziepak-github/scheduler' old head: 288a2664a2de429f159d746beaab87373184cd3d new head: d897a478d7c01054aad29b23f7f545073c797530 overview: https://github.com/pdziepak/Haiku/compare/288a266...d897a47 ---------------------------------------------------------------------------- 6a164da: kernel: Track load produced by interrupt handlers 955c7ed: kernel: Measure time spent in interrupt handlers d897a47: kernel: Allow reassigning IRQs to logical processors [ Pawel Dziepak <pdziepak@xxxxxxxxxxx> ] ---------------------------------------------------------------------------- 16 files changed, 332 insertions(+), 69 deletions(-) headers/private/kernel/arch/int.h | 1 + headers/private/kernel/arch/x86/arch_int.h | 1 + headers/private/kernel/arch/x86/arch_smp.h | 14 ++ headers/private/kernel/cpu.h | 9 +- headers/private/kernel/int.h | 29 +++- headers/private/kernel/load_tracking.h | 55 ++++++++ src/system/kernel/arch/x86/32/descriptors.cpp | 2 +- src/system/kernel/arch/x86/arch_int.cpp | 8 ++ src/system/kernel/arch/x86/arch_smp.cpp | 11 +- src/system/kernel/arch/x86/ioapic.cpp | 48 +++++-- src/system/kernel/arch/x86/pic.cpp | 5 +- src/system/kernel/arch/x86/timers/x86_apic.cpp | 3 +- src/system/kernel/cpu.cpp | 3 + src/system/kernel/int.cpp | 154 ++++++++++++++++++++- src/system/kernel/scheduler/scheduler.cpp | 52 +------ src/system/kernel/system_info.cpp | 6 +- ############################################################################ Commit: 6a164daad43169daf0a407fb1f6b0484671ff466 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon Nov 18 00:17:44 2013 UTC kernel: Track load produced by interrupt handlers ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/int.h b/headers/private/kernel/int.h index b3642c4..215deb0 100644 --- a/headers/private/kernel/int.h +++ b/headers/private/kernel/int.h @@ -19,6 +19,16 @@ struct kernel_args; +enum interrupt_type { + INTERRUPT_TYPE_EXCEPTION, + INTERRUPT_TYPE_IRQ, + INTERRUPT_TYPE_LOCAL_IRQ, + INTERRUPT_TYPE_SYSCALL, + INTERRUPT_TYPE_ICI, + INTERRUPT_TYPE_UNKNOWN +}; + + #ifdef __cplusplus extern "C" { #endif @@ -53,7 +63,8 @@ are_interrupts_enabled(void) #define restore_interrupts(status) arch_int_restore_interrupts(status) -status_t reserve_io_interrupt_vectors(long count, long startVector); +status_t reserve_io_interrupt_vectors(long count, long startVector, + enum interrupt_type type); status_t allocate_io_interrupt_vectors(long count, long *startVector); void free_io_interrupt_vectors(long count, long startVector); diff --git a/headers/private/kernel/load_tracking.h b/headers/private/kernel/load_tracking.h new file mode 100644 index 0000000..5b45c29 --- /dev/null +++ b/headers/private/kernel/load_tracking.h @@ -0,0 +1,55 @@ +/* + * Copyright 2013 Paweł Dziepak, pdziepak@xxxxxxxxxxx. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_LOAD_TRACKING_H +#define _KERNEL_LOAD_TRACKING_H + + +const int32 kMaxLoad = 1000; +const bigtime_t kLoadMeasureInterval = 50000; +const bigtime_t kIntervalInaccuracy = kLoadMeasureInterval / 4; + + +static int32 +compute_load(bigtime_t& measureTime, bigtime_t& measureActiveTime, int32& load) +{ + bigtime_t now = system_time(); + + if (measureTime == 0) { + measureTime = now; + return -1; + } + + bigtime_t deltaTime = now - measureTime; + + if (deltaTime < kLoadMeasureInterval) + return -1; + + int32 oldLoad = load; + ASSERT(oldLoad >= 0 && oldLoad <= kMaxLoad); + + int32 newLoad = measureActiveTime * kMaxLoad; + newLoad /= max_c(deltaTime, 1); + newLoad = max_c(min_c(newLoad, kMaxLoad), 0); + + measureActiveTime = 0; + measureTime = now; + + deltaTime += kIntervalInaccuracy; + int n = deltaTime / kLoadMeasureInterval; + ASSERT(n > 0); + + if (n > 10) + load = newLoad; + else { + newLoad *= (1 << n) - 1; + load = (load + newLoad) / (1 << n); + ASSERT(load >= 0 && load <= kMaxLoad); + } + + return oldLoad; +} + + +#endif // _KERNEL_LOAD_TRACKING_H diff --git a/src/system/kernel/arch/x86/32/descriptors.cpp b/src/system/kernel/arch/x86/32/descriptors.cpp index 465aab7..d215dc0 100644 --- a/src/system/kernel/arch/x86/32/descriptors.cpp +++ b/src/system/kernel/arch/x86/32/descriptors.cpp @@ -311,7 +311,7 @@ x86_descriptors_init(kernel_args* args) set_trap_gate(0, 98, &trap98); // for performance testing only set_trap_gate(0, 99, &trap99); // syscall interrupt - reserve_io_interrupt_vectors(2, 98); + reserve_io_interrupt_vectors(2, 98, INTERRUPT_TYPE_SYSCALL); // configurable msi or msi-x interrupts set_interrupt_gate(0, 100, &trap100); diff --git a/src/system/kernel/arch/x86/arch_smp.cpp b/src/system/kernel/arch/x86/arch_smp.cpp index 8c2bc75..f9a56ba 100644 --- a/src/system/kernel/arch/x86/arch_smp.cpp +++ b/src/system/kernel/arch/x86/arch_smp.cpp @@ -90,7 +90,8 @@ arch_smp_init(kernel_args *args) if (args->num_cpus > 1) { // I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted - reserve_io_interrupt_vectors(3, 0xfd - ARCH_INTERRUPT_BASE); + reserve_io_interrupt_vectors(3, 0xfd - ARCH_INTERRUPT_BASE, + INTERRUPT_TYPE_ICI); install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &x86_ici_interrupt, NULL, B_NO_LOCK_VECTOR); install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &x86_smp_error_interrupt, NULL, B_NO_LOCK_VECTOR); install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &x86_spurious_interrupt, NULL, B_NO_LOCK_VECTOR); diff --git a/src/system/kernel/arch/x86/ioapic.cpp b/src/system/kernel/arch/x86/ioapic.cpp index 28d5ba8..87dd94c 100644 --- a/src/system/kernel/arch/x86/ioapic.cpp +++ b/src/system/kernel/arch/x86/ioapic.cpp @@ -774,7 +774,7 @@ ioapic_init(kernel_args* args) current = sIOAPICs; while (current != NULL) { reserve_io_interrupt_vectors(current->max_redirection_entry + 1, - current->global_interrupt_base); + current->global_interrupt_base, INTERRUPT_TYPE_IRQ); current = current->next; } diff --git a/src/system/kernel/arch/x86/pic.cpp b/src/system/kernel/arch/x86/pic.cpp index 7e08631..96cd171 100644 --- a/src/system/kernel/arch/x86/pic.cpp +++ b/src/system/kernel/arch/x86/pic.cpp @@ -231,7 +231,7 @@ pic_init() TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts)); - reserve_io_interrupt_vectors(16, 0); + reserve_io_interrupt_vectors(16, 0, INTERRUPT_TYPE_EXCEPTION); // make the pic controller the current one arch_int_set_interrupt_controller(picController); diff --git a/src/system/kernel/arch/x86/timers/x86_apic.cpp b/src/system/kernel/arch/x86/timers/x86_apic.cpp index 3569e75..6ac3e59 100644 --- a/src/system/kernel/arch/x86/timers/x86_apic.cpp +++ b/src/system/kernel/arch/x86/timers/x86_apic.cpp @@ -106,7 +106,8 @@ apic_timer_init(struct kernel_args *args) sApicTicsPerSec = args->arch_args.apic_time_cv_factor; - reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE); + reserve_io_interrupt_vectors(1, 0xfb - ARCH_INTERRUPT_BASE, + INTERRUPT_TYPE_LOCAL_IRQ); install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &apic_timer_interrupt, NULL, B_NO_LOCK_VECTOR); diff --git a/src/system/kernel/int.cpp b/src/system/kernel/int.cpp index 6f9de63..df35b67 100644 --- a/src/system/kernel/int.cpp +++ b/src/system/kernel/int.cpp @@ -20,6 +20,7 @@ #include <arch/int.h> #include <boot/kernel_args.h> #include <elf.h> +#include <load_tracking.h> #include <util/AutoLock.h> #include <util/kqueue.h> #include <smp.h> @@ -51,6 +52,13 @@ struct io_vector { spinlock vector_lock; int32 enable_count; bool no_lock_vector; + interrupt_type type; + + spinlock load_lock; + bigtime_t last_measure_time; + bigtime_t last_measure_active; + int32 load; + #if DEBUG_INTERRUPTS int64 handled_count; int64 unhandled_count; @@ -116,6 +124,29 @@ dump_int_statistics(int argc, char **argv) #endif +static int +dump_int_load(int argc, char** argv) +{ + static const char* typeNames[] + = { "exception", "irq", "local irq", "syscall", "ici", "unknown" }; + + for (int i = 0; i < NUM_IO_VECTORS; i++) { + if (!B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock) + && sVectors[i].handler_list == NULL + && sVectors[i].enable_count == 0) + continue; + + kprintf("int %3d, type %s, enabled %" B_PRId32 ", load %" B_PRId32 + "%%%s\n", i, typeNames[min_c(sVectors[i].type, + INTERRUPT_TYPE_UNKNOWN)], + sVectors[i].enable_count, sVectors[i].load / 10, + B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock) ? ", ACTIVE" : ""); + } + + return 0; +} + + // #pragma mark - private kernel API @@ -145,6 +176,13 @@ int_init_post_vm(kernel_args* args) B_INITIALIZE_SPINLOCK(&sVectors[i].vector_lock); sVectors[i].enable_count = 0; sVectors[i].no_lock_vector = false; + sVectors[i].type = INTERRUPT_TYPE_UNKNOWN; + + B_INITIALIZE_SPINLOCK(&sVectors[i].load_lock); + sVectors[i].last_measure_time = 0; + sVectors[i].last_measure_active = 0; + sVectors[i].load = 0; + #if DEBUG_INTERRUPTS sVectors[i].handled_count = 0; sVectors[i].unhandled_count = 0; @@ -159,6 +197,9 @@ int_init_post_vm(kernel_args* args) "list interrupt statistics"); #endif + add_debugger_command("int_load", &dump_int_load, + "list interrupt usage statistics"); + return arch_int_init_post_vm(args); } @@ -179,6 +220,17 @@ int_init_post_device_manager(kernel_args* args) } +static void +update_int_load(int i) +{ + if (!try_acquire_spinlock(&sVectors[i].load_lock)) + return; + compute_load(sVectors[i].last_measure_time, sVectors[i].last_measure_active, + sVectors[i].load); + release_spinlock(&sVectors[i].load_lock); +} + + /*! Actually process an interrupt via the handlers registered for that vector (IRQ). */ @@ -189,6 +241,8 @@ int_io_interrupt_handler(int vector, bool levelTriggered) struct io_handler* io; bool handled = false; + bigtime_t start = system_time(); + if (!sVectors[vector].no_lock_vector) acquire_spinlock(&sVectors[vector].vector_lock); @@ -267,6 +321,12 @@ int_io_interrupt_handler(int vector, bool levelTriggered) if (!sVectors[vector].no_lock_vector) release_spinlock(&sVectors[vector].vector_lock); + SpinLocker locker(sVectors[vector].load_lock); + sVectors[vector].last_measure_active += system_time() - start; + locker.Unlock(); + + update_int_load(vector); + if (levelTriggered) return status; @@ -435,7 +495,7 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data) vectors using allocate_io_interrupt_vectors() instead. */ status_t -reserve_io_interrupt_vectors(long count, long startVector) +reserve_io_interrupt_vectors(long count, long startVector, interrupt_type type) { MutexLocker locker(&sIOInterruptVectorAllocationLock); @@ -448,6 +508,7 @@ reserve_io_interrupt_vectors(long count, long startVector) return B_BUSY; } + sVectors[startVector + i].type = type; sAllocatedIOInterruptVectors[startVector + i] = true; } diff --git a/src/system/kernel/scheduler/scheduler.cpp b/src/system/kernel/scheduler/scheduler.cpp index d8a3d98..86760d5 100644 --- a/src/system/kernel/scheduler/scheduler.cpp +++ b/src/system/kernel/scheduler/scheduler.cpp @@ -23,6 +23,7 @@ #include <kernel.h> #include <kscheduler.h> #include <listeners.h> +#include <load_tracking.h> #include <scheduler_defs.h> #include <smp.h> #include <thread.h> @@ -61,10 +62,9 @@ const bigtime_t kMinimalWaitTime = kThreadQuantum / 4; const bigtime_t kCacheExpire = 100000; -const int kTargetLoad = 550; -const int kHighLoad = 700; -const int kMaxLoad = 1000; -const int kLoadDifference = 200; +const int kTargetLoad = kMaxLoad * 55 / 100; +const int kHighLoad = kMaxLoad * 70 / 100; +const int kLoadDifference = kMaxLoad * 20 / 100; static bigtime_t sDisableSmallTaskPacking; static int32 sSmallTaskCore; @@ -924,50 +924,6 @@ should_rebalance(Thread* thread) } -static inline int -compute_load(bigtime_t& measureTime, bigtime_t& measureActiveTime, int32& load) -{ - const bigtime_t kLoadMeasureInterval = 50000; - const bigtime_t kIntervalInaccuracy = kLoadMeasureInterval / 4; - - bigtime_t now = system_time(); - - if (measureTime == 0) { - measureTime = now; - return -1; - } - - bigtime_t deltaTime = now - measureTime; - - if (deltaTime < kLoadMeasureInterval) - return -1; - - int oldLoad = load; - ASSERT(oldLoad >= 0 && oldLoad <= kMaxLoad); - - int newLoad = measureActiveTime * kMaxLoad; - newLoad /= max_c(deltaTime, 1); - newLoad = max_c(min_c(newLoad, kMaxLoad), 0); - - measureActiveTime = 0; - measureTime = now; - - deltaTime += kIntervalInaccuracy; - int n = deltaTime / kLoadMeasureInterval; - ASSERT(n > 0); - - if (n > 10) - load = newLoad; - else { - newLoad *= (1 << n) - 1; - load = (load + newLoad) / (1 << n); - ASSERT(load >= 0 && load <= kMaxLoad); - } - - return oldLoad; -} - - static inline void compute_cpu_load(int32 cpu) { ############################################################################ Commit: 955c7edec2e0d6f52e5245ce449804cede5de34b Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon Nov 18 00:50:37 2013 UTC kernel: Measure time spent in interrupt handlers ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/cpu.h b/headers/private/kernel/cpu.h index c1524b1..7d1dcd3 100644 --- a/headers/private/kernel/cpu.h +++ b/headers/private/kernel/cpu.h @@ -60,6 +60,8 @@ typedef struct cpu_ent { // keeping track of CPU activity bigtime_t active_time; + bigtime_t irq_time; + bigtime_t interrupt_time; bigtime_t last_kernel_time; bigtime_t last_user_time; diff --git a/src/system/kernel/int.cpp b/src/system/kernel/int.cpp index df35b67..6e1b1fd 100644 --- a/src/system/kernel/int.cpp +++ b/src/system/kernel/int.cpp @@ -243,6 +243,10 @@ int_io_interrupt_handler(int vector, bool levelTriggered) bigtime_t start = system_time(); + // exceptions and syscalls have their own handlers + ASSERT(sVectors[vector].type != INTERRUPT_TYPE_EXCEPTION + && sVectors[vector].type != INTERRUPT_TYPE_SYSCALL); + if (!sVectors[vector].no_lock_vector) acquire_spinlock(&sVectors[vector].vector_lock); @@ -322,9 +326,13 @@ int_io_interrupt_handler(int vector, bool levelTriggered) release_spinlock(&sVectors[vector].vector_lock); SpinLocker locker(sVectors[vector].load_lock); - sVectors[vector].last_measure_active += system_time() - start; + bigtime_t deltaTime = system_time() - start; + sVectors[vector].last_measure_active += deltaTime; locker.Unlock(); + atomic_add64(&get_cpu_struct()->interrupt_time, deltaTime); + if (sVectors[vector].type == INTERRUPT_TYPE_IRQ) + atomic_add64(&get_cpu_struct()->irq_time, deltaTime); update_int_load(vector); if (levelTriggered) diff --git a/src/system/kernel/system_info.cpp b/src/system/kernel/system_info.cpp index a97cdca..d6a9fab 100644 --- a/src/system/kernel/system_info.cpp +++ b/src/system/kernel/system_info.cpp @@ -50,10 +50,12 @@ dump_info(int argc, char **argv) __VERSION__); kprintf("revision: %s\n\n", get_haiku_revision()); - kprintf("cpu count: %" B_PRId32 ", active times:\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++) - kprintf(" [%" B_PRId32 "] %" B_PRId64 "\n", i + 1, gCPU[i].active_time); + kprintf(" [%" B_PRId32 "] active time: %12" B_PRId64 ", interrupt" + " time: %12" B_PRId64 ", irq time: %12" B_PRId64 "\n", i + 1, + gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time); // ToDo: Add page_faults kprintf("pages:\t\t%" B_PRIuPHYSADDR " (%" B_PRIuPHYSADDR " max)\n", ############################################################################ Commit: d897a478d7c01054aad29b23f7f545073c797530 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon Nov 18 03:55:25 2013 UTC kernel: Allow reassigning IRQs to logical processors ---------------------------------------------------------------------------- diff --git a/headers/private/kernel/arch/int.h b/headers/private/kernel/arch/int.h index 9ebe6b0..6d4afc6 100644 --- a/headers/private/kernel/arch/int.h +++ b/headers/private/kernel/arch/int.h @@ -34,6 +34,7 @@ void arch_int_enable_io_interrupt(int irq); void arch_int_disable_io_interrupt(int irq); void arch_int_configure_io_interrupt(int irq, uint32 config); bool arch_int_are_interrupts_enabled(void); +void arch_int_assign_to_cpu(int32 irq, int32 cpu); #ifdef __cplusplus } diff --git a/headers/private/kernel/arch/x86/arch_int.h b/headers/private/kernel/arch/x86/arch_int.h index da49437..5de0dcb 100644 --- a/headers/private/kernel/arch/x86/arch_int.h +++ b/headers/private/kernel/arch/x86/arch_int.h @@ -68,6 +68,7 @@ typedef struct interrupt_controller_s { bool (*is_spurious_interrupt)(int32 num); bool (*is_level_triggered_interrupt)(int32 num); bool (*end_of_interrupt)(int32 num); + void (*assign_interrupt_to_cpu)(int32 num, int32 cpu); } interrupt_controller; diff --git a/headers/private/kernel/arch/x86/arch_smp.h b/headers/private/kernel/arch/x86/arch_smp.h index 5b2d654..4eef8eb 100644 --- a/headers/private/kernel/arch/x86/arch_smp.h +++ b/headers/private/kernel/arch/x86/arch_smp.h @@ -99,4 +99,18 @@ enum { 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 */ diff --git a/headers/private/kernel/cpu.h b/headers/private/kernel/cpu.h index 7d1dcd3..4a70428 100644 --- a/headers/private/kernel/cpu.h +++ b/headers/private/kernel/cpu.h @@ -11,6 +11,7 @@ #include <setjmp.h> +#include <int.h> #include <smp.h> #include <timer.h> #include <arch/cpu.h> @@ -79,8 +80,12 @@ typedef struct cpu_ent { int topology_id[CPU_TOPOLOGY_LEVELS]; int cache_id[CPU_MAX_CACHE_LEVEL]; + // IRQs assigned to this CPU + struct list irqs; + spinlock irqs_lock; + // arch-specific stuff - arch_cpu_info arch; + arch_cpu_info arch; } cpu_ent __attribute__((aligned(64))); diff --git a/headers/private/kernel/int.h b/headers/private/kernel/int.h index 215deb0..6922aa0 100644 --- a/headers/private/kernel/int.h +++ b/headers/private/kernel/int.h @@ -12,6 +12,8 @@ #include <KernelExport.h> #include <arch/int.h> +#include <util/list.h> + // private install_io_interrupt_handler() flags #define B_NO_LOCK_VECTOR 0x100 #define B_NO_HANDLED_INFO 0x200 @@ -28,6 +30,18 @@ enum interrupt_type { 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 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); void free_io_interrupt_vectors(long count, long startVector); +void assign_io_interrupt_to_cpu(long vector, int32 cpu); + #endif /* _KERNEL_INT_H */ diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index 3591f90..abcaf5f 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -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 arch_int_init(kernel_args* args) { diff --git a/src/system/kernel/arch/x86/arch_smp.cpp b/src/system/kernel/arch/x86/arch_smp.cpp index f9a56ba..340854b 100644 --- a/src/system/kernel/arch/x86/arch_smp.cpp +++ b/src/system/kernel/arch/x86/arch_smp.cpp @@ -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 arch_smp_init(kernel_args *args) { diff --git a/src/system/kernel/arch/x86/ioapic.cpp b/src/system/kernel/arch/x86/ioapic.cpp index 87dd94c..3f6c544 100644 --- a/src/system/kernel/arch/x86/ioapic.cpp +++ b/src/system/kernel/arch/x86/ioapic.cpp @@ -18,6 +18,7 @@ #include <arch/x86/apic.h> #include <arch/x86/arch_int.h> +#include <arch/x86/arch_smp.h> #include <arch/x86/pic.h> // to gain access to the ACPICA types @@ -26,9 +27,9 @@ //#define TRACE_IOAPIC #ifdef TRACE_IOAPIC -# define TRACE(x) dprintf x +# define TRACE(...) dprintf(__VA_ARGS__) #else -# define TRACE(x) ; +# define TRACE(...) (void)0 #endif @@ -243,6 +244,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 ioapic_enable_io_interrupt(int32 gsi) { // If enabling an overriden source is attempted, enable the override entry @@ -256,8 +281,8 @@ ioapic_enable_io_interrupt(int32 gsi) return; uint8 pin = gsi - ioapic->global_interrupt_base; - TRACE(("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", - gsi, ioapic->number, pin)); + TRACE("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", + gsi, ioapic->number, pin); uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); entry &= ~IO_APIC_INTERRUPT_MASKED; @@ -273,8 +298,8 @@ ioapic_disable_io_interrupt(int32 gsi) return; uint8 pin = gsi - ioapic->global_interrupt_base; - TRACE(("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", - gsi, ioapic->number, pin)); + TRACE("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n", + gsi, ioapic->number, pin); uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2); entry |= IO_APIC_INTERRUPT_MASKED; @@ -290,8 +315,8 @@ ioapic_configure_io_interrupt(int32 gsi, uint32 config) return; uint8 pin = gsi - ioapic->global_interrupt_base; - TRACE(("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " - "config 0x%08lx\n", gsi, ioapic->number, pin, config)); + TRACE("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; " + "config 0x%08lx\n", gsi, ioapic->number, pin, config); ioapic_configure_pin(*ioapic, pin, gsi, config, IO_APIC_DELIVERY_MODE_FIXED); @@ -310,7 +335,7 @@ ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress) 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); if (ioapic.version == 0xffffffff) { @@ -650,7 +675,8 @@ ioapic_init(kernel_args* args) &ioapic_configure_io_interrupt, &ioapic_is_spurious_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) diff --git a/src/system/kernel/arch/x86/pic.cpp b/src/system/kernel/arch/x86/pic.cpp index 96cd171..a802ff3 100644 --- a/src/system/kernel/arch/x86/pic.cpp +++ b/src/system/kernel/arch/x86/pic.cpp @@ -196,7 +196,8 @@ pic_init() &pic_configure_io_interrupt, &pic_is_spurious_interrupt, &pic_is_level_triggered_interrupt, - &pic_end_of_interrupt + &pic_end_of_interrupt, + NULL }; // Start initialization sequence for the master and slave PICs diff --git a/src/system/kernel/cpu.cpp b/src/system/kernel/cpu.cpp index b98ad77..433d567 100644 --- a/src/system/kernel/cpu.cpp +++ b/src/system/kernel/cpu.cpp @@ -99,6 +99,9 @@ cpu_preboot_init_percpu(kernel_args *args, int curr_cpu) memset(&gCPU[curr_cpu], 0, sizeof(gCPU[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); } diff --git a/src/system/kernel/int.cpp b/src/system/kernel/int.cpp index 6e1b1fd..cfea6fe 100644 --- a/src/system/kernel/int.cpp +++ b/src/system/kernel/int.cpp @@ -1,4 +1,7 @@ /* + * Copyright 2013, Paweł Dziepak, pdziepak@xxxxxxxxxxx. + * Distributed under the terms of the MIT License. + * Copyright 2011, Michael Lotz, mmlr@xxxxxxxx. * Distributed under the terms of the MIT License. * @@ -54,10 +57,7 @@ struct io_vector { bool no_lock_vector; interrupt_type type; - spinlock load_lock; - bigtime_t last_measure_time; - bigtime_t last_measure_active; - int32 load; + irq_assignment assigned_cpu; #if DEBUG_INTERRUPTS int64 handled_count; @@ -67,6 +67,7 @@ struct io_vector { #endif }; +static uint32 sLastCPU; static struct io_vector sVectors[NUM_IO_VECTORS]; static bool sAllocatedIOInterruptVectors[NUM_IO_VECTORS]; static mutex sIOInterruptVectorAllocationLock @@ -137,10 +138,20 @@ dump_int_load(int argc, char** argv) continue; 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)], - sVectors[i].enable_count, sVectors[i].load / 10, - B_SPINLOCK_IS_LOCKED(&sVectors[i].vector_lock) ? ", ACTIVE" : ""); + sVectors[i].enable_count, sVectors[i].assigned_cpu.load / 10); + + 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; @@ -178,10 +189,15 @@ int_init_post_vm(kernel_args* args) sVectors[i].no_lock_vector = false; sVectors[i].type = INTERRUPT_TYPE_UNKNOWN; - B_INITIALIZE_SPINLOCK(&sVectors[i].load_lock); - sVectors[i].last_measure_time = 0; - sVectors[i].last_measure_active = 0; - sVectors[i].load = 0; + irq_assignment* assigned_cpu = &sVectors[i].assigned_cpu; + assigned_cpu->irq = i; + + 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 sVectors[i].handled_count = 0; @@ -223,11 +239,14 @@ int_init_post_device_manager(kernel_args* args) static void update_int_load(int i) { - if (!try_acquire_spinlock(&sVectors[i].load_lock)) + if (!try_acquire_spinlock(&sVectors[i].assigned_cpu.load_lock)) return; - compute_load(sVectors[i].last_measure_time, sVectors[i].last_measure_active, - sVectors[i].load); - release_spinlock(&sVectors[i].load_lock); + + compute_load(sVectors[i].assigned_cpu.last_measure_time, + 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) 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; - sVectors[vector].last_measure_active += deltaTime; + sVectors[vector].assigned_cpu.last_measure_active += deltaTime; locker.Unlock(); 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(); 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 && sVectors[vector].handler_list != NULL) { // 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; release_spinlock(&sVectors[vector].vector_lock); + restore_interrupts(state); return B_OK; @@ -486,6 +523,28 @@ remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data) 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); restore_interrupts(state); @@ -596,3 +655,27 @@ free_io_interrupt_vectors(long count, long startVector) 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(); +} + diff --git a/src/system/kernel/system_info.cpp b/src/system/kernel/system_info.cpp index d6a9fab..d6713b0 100644 --- a/src/system/kernel/system_info.cpp +++ b/src/system/kernel/system_info.cpp @@ -53,8 +53,8 @@ dump_info(int argc, char **argv) kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus()); for (int32 i = 0; i < smp_get_num_cpus(); i++) - kprintf(" [%" B_PRId32 "] active time: %12" B_PRId64 ", interrupt" - " time: %12" B_PRId64 ", irq time: %12" B_PRId64 "\n", i + 1, + kprintf(" [%" B_PRId32 "] active time: %10" B_PRId64 ", interrupt" + " time: %10" B_PRId64 ", irq time: %10" B_PRId64 "\n", i + 1, gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time); // ToDo: Add page_faults