From a085b4f53697aa40e5b3cfcc438f430a7c346930 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 4 Nov 2016 13:01:09 +0100 Subject: [PATCH] nova: remap kernel cpu ids to genode cpu ids to have a predictable order. First all CPU ids with hyper-thread id 0 are taken, next the hyper-threads with 1 of all cores and so on. Fixes #2163 --- .../base-nova/include/nova/syscall-generic.h | 66 +++++++++++++++++-- repos/base-nova/src/core/include/platform.h | 12 ++++ repos/base-nova/src/core/pager.cc | 45 ++++++++----- repos/base-nova/src/core/platform.cc | 56 +++++++++++----- repos/base-nova/src/core/platform_thread.cc | 7 +- repos/base-nova/src/core/thread_start.cc | 4 +- 6 files changed, 148 insertions(+), 42 deletions(-) diff --git a/repos/base-nova/include/nova/syscall-generic.h b/repos/base-nova/include/nova/syscall-generic.h index dbaeeca2c2..ab7b5008b8 100644 --- a/repos/base-nova/include/nova/syscall-generic.h +++ b/repos/base-nova/include/nova/syscall-generic.h @@ -126,6 +126,13 @@ namespace Nova { bool has_feature_vmx() const { return feature_flags & (1 << 1); } bool has_feature_svm() const { return feature_flags & (1 << 2); } + struct Cpu_desc { + uint8_t flags; + uint8_t thread; + uint8_t core; + uint8_t package; + } __attribute__((packed)); + unsigned cpu_max() const { return (mem_desc_offset - cpu_desc_offset) / cpu_desc_size; } @@ -139,16 +146,63 @@ namespace Nova { return cpu_num; } - bool is_cpu_enabled(unsigned i) const { + Cpu_desc const * cpu_desc_of_cpu(unsigned i) const { if (i >= cpu_max()) - return false; + return nullptr; - const char * cpu_desc = reinterpret_cast(this) + - cpu_desc_offset + i * cpu_desc_size; - - return (*cpu_desc) & 0x1; + unsigned long desc_addr = reinterpret_cast(this) + + cpu_desc_offset + i * cpu_desc_size; + return reinterpret_cast(desc_addr); } + bool is_cpu_enabled(unsigned i) const { + Cpu_desc const * const desc = cpu_desc_of_cpu(i); + return desc ? desc->flags & 0x1 : false; + } + + /** + * Map kernel cpu ids to virtual cpu ids. + * Assign first all cores on all packages with thread 0 to virtual + * cpu id numbers, afterwards all (hyper-)threads. + */ + bool remap_cpu_ids(uint8_t *map_cpus, unsigned const boot_cpu) const { + unsigned const num_cpus = cpus(); + unsigned cpu_i = 0; + + /* assign boot cpu ever the virtual cpu id 0 */ + Cpu_desc const * const boot = cpu_desc_of_cpu(boot_cpu); + if (!boot || !is_cpu_enabled(boot_cpu)) + return false; + + map_cpus[cpu_i++] = boot_cpu; + if (cpu_i >= num_cpus) + return true; + + /* assign remaining cores and afterwards all threads to the ids */ + for (uint8_t thread = 0; thread < 255; thread++) { + for (uint8_t package = 0; package < 255; package++) { + for (uint8_t core = 0; core < 255; core++) { + for (unsigned i = 0; i < cpu_max(); i++) { + if (i == boot_cpu || !is_cpu_enabled(i)) + continue; + + Cpu_desc const * const c = cpu_desc_of_cpu(i); + if (!c) + continue; + + if (!(c->package == package && c->core == core && + c->thread == thread)) + continue; + + map_cpus [cpu_i++] = i; + if (cpu_i >= num_cpus) + return true; + } + } + } + } + return false; + } } __attribute__((packed)); diff --git a/repos/base-nova/src/core/include/platform.h b/repos/base-nova/src/core/include/platform.h index b1ca4ea0ac..81bea3eee3 100644 --- a/repos/base-nova/src/core/include/platform.h +++ b/repos/base-nova/src/core/include/platform.h @@ -23,6 +23,10 @@ namespace Genode { class Platform : public Platform_generic { + public: + + enum { MAX_SUPPORTED_CPUS = 64}; + private: Core_mem_allocator _core_mem_alloc; /* core-accessible memory */ @@ -41,6 +45,9 @@ namespace Genode { /* available CPUs */ Affinity::Space _cpus; + /* map of virtual cpu ids in Genode to kernel cpu ids */ + uint8_t map_cpu_ids[MAX_SUPPORTED_CPUS]; + addr_t _map_pages(addr_t phys_page, addr_t pages); public: @@ -88,6 +95,11 @@ namespace Genode { */ size_t region_alloc_size_at(void * addr) { return (*_core_mem_alloc.virt_alloc())()->size_at(addr); } + + /** + * Return kernel CPU ID for given Genode CPU + */ + unsigned kernel_cpu_id(unsigned genode_cpu_id); }; } diff --git a/repos/base-nova/src/core/pager.cc b/repos/base-nova/src/core/pager.cc index 44fde2f784..b0fcc4c1dd 100644 --- a/repos/base-nova/src/core/pager.cc +++ b/repos/base-nova/src/core/pager.cc @@ -14,7 +14,6 @@ */ /* Genode includes */ -#include #include #include @@ -23,6 +22,7 @@ /* core-local includes */ #include +#include #include #include @@ -49,7 +49,7 @@ static Nova::Hip * kernel_hip() } /* pager activation threads storage and handling - one thread per CPU */ -enum { PAGER_CPUS = 128, PAGER_STACK_SIZE = 2*4096 }; +enum { PAGER_CPUS = Platform::MAX_SUPPORTED_CPUS, PAGER_STACK_SIZE = 2*4096 }; static char pager_activation_mem[sizeof (Pager_activation) * PAGER_CPUS]; static Pager_activation_base * pager_threads[PAGER_CPUS]; @@ -67,7 +67,7 @@ static unsigned which_cpu(Pager_activation_base * pager) } /* pager of boot CPU */ - return Affinity::Location().xpos(); + return 0; } @@ -510,14 +510,16 @@ template void Exception_handlers::register_handler(Pager_object *obj, Mtd mtd, void (* __attribute__((regparm(1))) func)(addr_t)) { - unsigned use_cpu = obj->location().xpos(); + unsigned const genode_cpu_id = obj->location().xpos(); + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(genode_cpu_id); - if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) { + if (!kernel_hip()->is_cpu_enabled(kernel_cpu_id) || + !pager_threads[genode_cpu_id]) { warning("invalid CPU parameter used in pager object"); throw Region_map::Invalid_thread(); } - addr_t const ec_sel = pager_threads[use_cpu]->native_thread().ec_sel; + addr_t const ec_sel = pager_threads[genode_cpu_id]->native_thread().ec_sel; /* compiler generates instance of exception entry if not specified */ addr_t entry = func ? (addr_t)func : (addr_t)(&_handler); @@ -604,13 +606,15 @@ Pager_object::Pager_object(Cpu_session_capability cpu_session_cap, } /* place Pager_object on specified CPU by selecting proper pager thread */ - unsigned use_cpu = location.xpos(); - if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) { + unsigned const genode_cpu_id = location.xpos(); + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(genode_cpu_id); + if (!kernel_hip()->is_cpu_enabled(kernel_cpu_id) || + !pager_threads[genode_cpu_id]) { warning("invalid CPU parameter used in pager object"); throw Region_map::Invalid_thread(); } - addr_t ec_sel = pager_threads[use_cpu]->native_thread().ec_sel; + addr_t ec_sel = pager_threads[genode_cpu_id]->native_thread().ec_sel; /* create portal for page-fault handler - 14 */ _exceptions.register_handler<14>(this, Mtd::QUAL | Mtd::EIP, @@ -870,12 +874,14 @@ void Pager_object::_oom_handler(addr_t pager_dst, addr_t pager_src, addr_t Pager_object::get_oom_portal() { - addr_t const pt_oom = sel_oom_portal(); - unsigned const use_cpu = _location.xpos(); + addr_t const pt_oom = sel_oom_portal(); + unsigned const genode_cpu_id = _location.xpos(); + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(genode_cpu_id); - if (kernel_hip()->is_cpu_enabled(use_cpu) && pager_threads[use_cpu]) { - addr_t const ec_sel = pager_threads[use_cpu]->native_thread().ec_sel; + if (kernel_hip()->is_cpu_enabled(kernel_cpu_id) && + pager_threads[genode_cpu_id]) { + addr_t const ec_sel = pager_threads[genode_cpu_id]->native_thread().ec_sel; uint8_t res = create_portal(pt_oom, __core_pd_sel, ec_sel, Mtd(0), reinterpret_cast(_oom_handler), this); @@ -940,8 +946,9 @@ Pager_entrypoint::Pager_entrypoint(Rpc_cap_factory &cap_factory) typedef Pager_activation Pager; Pager * pager_of_cpu = reinterpret_cast(&pager_activation_mem); - for (unsigned i = 0; i < kernel_hip()->cpu_max(); i++, pager_of_cpu++) { - if (!kernel_hip()->is_cpu_enabled(i)) + for (unsigned i = 0; i < kernel_hip()->cpus(); i++, pager_of_cpu++) { + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(i); + if (!kernel_hip()->is_cpu_enabled(kernel_cpu_id)) continue; pager_threads[i] = pager_of_cpu; @@ -954,13 +961,15 @@ Pager_entrypoint::Pager_entrypoint(Rpc_cap_factory &cap_factory) Pager_capability Pager_entrypoint::manage(Pager_object *obj) { /* let handle pager_object of pager thread on same CPU */ - unsigned use_cpu = obj->location().xpos(); - if (!kernel_hip()->is_cpu_enabled(use_cpu) || !pager_threads[use_cpu]) { + unsigned const genode_cpu_id = obj->location().xpos(); + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(genode_cpu_id); + if (!kernel_hip()->is_cpu_enabled(kernel_cpu_id) || + !pager_threads[genode_cpu_id]) { warning("invalid CPU parameter used in pager object"); return Pager_capability(); } Native_capability pager_thread_cap = - Capability_space::import(pager_threads[use_cpu]->native_thread().ec_sel); + Capability_space::import(pager_threads[genode_cpu_id]->native_thread().ec_sel); /* request creation of portal bind to pager thread */ Native_capability cap_session = diff --git a/repos/base-nova/src/core/platform.cc b/repos/base-nova/src/core/platform.cc index 5725f0d086..5a90e19fce 100644 --- a/repos/base-nova/src/core/platform.cc +++ b/repos/base-nova/src/core/platform.cc @@ -63,13 +63,6 @@ Utcb *__main_thread_utcb; */ extern unsigned _prog_img_beg, _prog_img_end; - - -extern addr_t sc_idle_base; -addr_t sc_idle_base = 0; - - - /** * Capability selector of root PD */ @@ -335,6 +328,17 @@ Platform::Platform() : nova_die(); } + /* init genode cpu ids based on kernel cpu ids (used for syscalls) */ + if (sizeof(map_cpu_ids) / sizeof(map_cpu_ids[0]) < hip->cpu_max()) { + error("number of max CPUs is larger than expected - ", hip->cpu_max(), + " vs ", sizeof(map_cpu_ids) / sizeof(map_cpu_ids[0])); + nova_die(); + } + if (!hip->remap_cpu_ids(map_cpu_ids, boot_cpu())) { + error("re-ording cpu_id failed"); + nova_die(); + } + /* map idle SCs */ unsigned const log2cpu = log2(hip->cpu_max()); if ((1U << log2cpu) != hip->cpu_max()) { @@ -342,13 +346,13 @@ Platform::Platform() : nova_die(); } - sc_idle_base = cap_map()->insert(log2cpu + 1); + addr_t sc_idle_base = cap_map()->insert(log2cpu + 1); if (sc_idle_base & ((1UL << log2cpu) - 1)) { error("unaligned sc_idle_base value ", Hex(sc_idle_base)); nova_die(); } - if(map_local(__main_thread_utcb, Obj_crd(0, log2cpu), - Obj_crd(sc_idle_base, log2cpu), true)) + if (map_local(__main_thread_utcb, Obj_crd(0, log2cpu), + Obj_crd(sc_idle_base, log2cpu), true)) nova_die(); /* test reading out idle SCs */ @@ -384,9 +388,18 @@ Platform::Platform() : if (hip->has_feature_svm()) log("Hypervisor features SVM"); log("Hypervisor reports ", _cpus.width(), "x", _cpus.height(), " " - "CPU", _cpus.total() > 1 ? "s" : " ", " - boot CPU is ", boot_cpu()); + "CPU", _cpus.total() > 1 ? "s" : " "); if (!cpuid_invariant_tsc()) warning("CPU has no invariant TSC."); + + log("CPU ID (genode->kernel:package:core:thread) remapping"); + unsigned const cpus = hip->cpus(); + for (unsigned i = 0; i < cpus; i++) + log(" remap (", i, "->", map_cpu_ids[i], ":", + hip->cpu_desc_of_cpu(map_cpu_ids[i])->package, ":", + hip->cpu_desc_of_cpu(map_cpu_ids[i])->core, ":", + hip->cpu_desc_of_cpu(map_cpu_ids[i])->thread, ") ", + boot_cpu() == map_cpu_ids[i] ? "boot cpu" : ""); } /* initialize core allocators */ @@ -653,9 +666,11 @@ Platform::Platform() : } /* add idle ECs to trace sources */ - for (unsigned i = 0; i < hip->cpu_max(); i++) { + for (unsigned genode_cpu_id = 0; genode_cpu_id < _cpus.width(); genode_cpu_id++) { - if (!hip->is_cpu_enabled(i)) + unsigned kernel_cpu_id = Platform::kernel_cpu_id(genode_cpu_id); + + if (!hip->is_cpu_enabled(kernel_cpu_id)) continue; struct Idle_trace_source : Trace::Source::Info_accessor, Trace::Control, @@ -686,14 +701,25 @@ Platform::Platform() : }; Idle_trace_source *source = new (core_mem_alloc()) - Idle_trace_source(Affinity::Location(i, 0, hip->cpu_max(), 1), - sc_idle_base + i); + Idle_trace_source(Affinity::Location(kernel_cpu_id, 0, + _cpus.width(), 1), + sc_idle_base + kernel_cpu_id); Trace::sources().insert(source); } } +unsigned Platform::kernel_cpu_id(unsigned genode_cpu_id) +{ + if (genode_cpu_id >= sizeof(map_cpu_ids) / sizeof(map_cpu_ids[0])) { + error("invalid genode cpu id ", genode_cpu_id); + return ~0U; + } + return map_cpu_ids[genode_cpu_id]; +} + + /**************************************** ** Support for core memory management ** ****************************************/ diff --git a/repos/base-nova/src/core/platform_thread.cc b/repos/base-nova/src/core/platform_thread.cc index 74c642e79d..68f40b3888 100644 --- a/repos/base-nova/src/core/platform_thread.cc +++ b/repos/base-nova/src/core/platform_thread.cc @@ -18,6 +18,7 @@ /* core includes */ #include +#include #include #include #include @@ -81,7 +82,8 @@ int Platform_thread::start(void *ip, void *sp) uint8_t res; do { - res = create_ec(_sel_ec(), _pd->pd_sel(), _location.xpos(), + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(_location.xpos()); + res = create_ec(_sel_ec(), _pd->pd_sel(), kernel_cpu_id, utcb, initial_sp, _sel_exc_base, !worker()); if (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK != _pager->handle_oom()) { _pager->assign_pd(Native_thread::INVALID_INDEX); @@ -145,7 +147,8 @@ int Platform_thread::start(void *ip, void *sp) /* create first thread in task */ enum { THREAD_GLOBAL = true }; - res = create_ec(_sel_ec(), pd_sel, _location.xpos(), pd_utcb, 0, 0, + res = create_ec(_sel_ec(), pd_sel, + platform_specific()->kernel_cpu_id(_location.xpos()), pd_utcb, 0, 0, THREAD_GLOBAL); if (res != NOVA_OK) { error("create_ec returned ", res); diff --git a/repos/base-nova/src/core/thread_start.cc b/repos/base-nova/src/core/thread_start.cc index 3583b579e0..582be70d3e 100644 --- a/repos/base-nova/src/core/thread_start.cc +++ b/repos/base-nova/src/core/thread_start.cc @@ -25,6 +25,7 @@ /* core includes */ #include +#include #include using namespace Genode; @@ -102,7 +103,8 @@ void Thread::start() /* create local EC */ enum { LOCAL_THREAD = false }; - uint8_t res = create_ec(native_thread().ec_sel, pd_sel, location.xpos(), + unsigned const kernel_cpu_id = platform_specific()->kernel_cpu_id(location.xpos()); + uint8_t res = create_ec(native_thread().ec_sel, pd_sel, kernel_cpu_id, utcb, sp, native_thread().exc_pt_sel, LOCAL_THREAD); if (res != NOVA_OK) { error("create_ec returned ", res, " cpu=", location.xpos());