From 3e9054255995a2332f2fc6547d2c026f12087104 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Tue, 12 May 2015 14:52:29 +0200 Subject: [PATCH] nova: use signals with irqs in core Avoids the need to have per IRQ a thread that blocks synchronously for next interrupt. Now a thread may wait for multiple IRQs as other signals simultaneously. In core no threads are required anymore for IRQs/MSI - the clients (either the pci_drv or in case of MSI the driver) gets the IRQ delivered directly as a ordinary Genode signal. Useful since #1216 and #1487 is now available. Commit applies feature of #1446 also to IRQ/MSIs. --- repos/base-nova/include/32bit/nova/syscalls.h | 22 +- repos/base-nova/include/64bit/nova/syscalls.h | 9 +- repos/base-nova/ports/nova.hash | 2 +- repos/base-nova/ports/nova.port | 2 +- repos/base-nova/src/core/include/irq_object.h | 24 +- .../src/core/irq_session_component.cc | 216 +++++++----------- 6 files changed, 111 insertions(+), 164 deletions(-) diff --git a/repos/base-nova/include/32bit/nova/syscalls.h b/repos/base-nova/include/32bit/nova/syscalls.h index 579fc9a5f..25f2b297b 100644 --- a/repos/base-nova/include/32bit/nova/syscalls.h +++ b/repos/base-nova/include/32bit/nova/syscalls.h @@ -157,17 +157,23 @@ namespace Nova { ALWAYS_INLINE inline uint8_t syscall_5(Syscall s, uint8_t flags, mword_t sel, - mword_t &p1, mword_t &p2) + mword_t &p1, mword_t &p2, mword_t p3 = ~0UL) { mword_t status = eax(s, flags, sel); - asm volatile (" movl %%esp, %%ecx;" + asm volatile (" push %%ebx;" + + " mov %%ecx, %%ebx;" + " movl %%esp, %%ecx;" " movl $1f, %%edx;" + "sysenter;" "1:" - : "+a" (status), "+D" (p1), "+S" (p2) - : - : "ecx", "edx", "memory"); + + " pop %%ebx;" + : "+a" (status), "+D" (p1), "+S" (p2), "+c" (p3) + : + : "edx", "memory"); return status; } @@ -310,12 +316,14 @@ namespace Nova { ALWAYS_INLINE - inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, mword_t &msi_addr, mword_t &msi_data) + inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, + mword_t &msi_addr, mword_t &msi_data, + mword_t si = ~0UL) { msi_addr = dev; msi_data = cpu; - return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data); + return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si); } } #endif /* _PLATFORM__NOVA_SYSCALLS_H_ */ diff --git a/repos/base-nova/include/64bit/nova/syscalls.h b/repos/base-nova/include/64bit/nova/syscalls.h index da017d0eb..159960e0b 100644 --- a/repos/base-nova/include/64bit/nova/syscalls.h +++ b/repos/base-nova/include/64bit/nova/syscalls.h @@ -121,13 +121,13 @@ namespace Nova { ALWAYS_INLINE inline uint8_t syscall_5(Syscall s, uint8_t flags, mword_t sel, - mword_t &p1, mword_t &p2) + mword_t &p1, mword_t &p2, mword_t p3 = ~0UL) { mword_t status = rdi(s, flags, sel); asm volatile ("syscall" : "+D" (status), "+S"(p1), "+d"(p2) - : + : "a" (p3) : "rcx", "r11", "memory"); return status; } @@ -280,11 +280,12 @@ namespace Nova { ALWAYS_INLINE inline uint8_t assign_gsi(mword_t sm, mword_t dev, mword_t cpu, - mword_t &msi_addr, mword_t &msi_data) + mword_t &msi_addr, mword_t &msi_data, + mword_t si = ~0UL) { msi_addr = dev; msi_data = cpu; - return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data); + return syscall_5(NOVA_ASSIGN_GSI, 0, sm, msi_addr, msi_data, si); } } #endif /* _PLATFORM__NOVA_SYSCALLS_H_ */ diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index fb0a7a590..b86e98aa0 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -64d63799ff123098285ae3f74e2c17ce00e580ad +70a0463d4ae4f5cfc6146b43d7bb2e904d208d55 diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index 5bff2bf2b..4fb9c59e3 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git URL(nova) := https://github.com/alex-ab/NOVA.git # r8 branch -REV(nova) := 123f1e73080b7129a707c7e9547e14052ea6fe32 +REV(nova) := 9916e624f59462b8bb157dad3b38592e4ffbc561 DIR(nova) := src/kernel/nova PATCHES := $(wildcard $(REP_DIR)/patches/*.patch) diff --git a/repos/base-nova/src/core/include/irq_object.h b/repos/base-nova/src/core/include/irq_object.h index 057696cb1..1f9c21bd7 100644 --- a/repos/base-nova/src/core/include/irq_object.h +++ b/repos/base-nova/src/core/include/irq_object.h @@ -12,29 +12,22 @@ #pragma once -#include - namespace Genode { class Irq_object; } -class Genode::Irq_object : public Thread<4096> { - +class Genode::Irq_object +{ private: - Signal_context_capability _sig_cap; - Lock _sync_ack; - Lock _sync_life; + Signal_context_capability _sigh_cap; Genode::addr_t _kernel_caps; Genode::addr_t _msi_addr; Genode::addr_t _msi_data; - enum { UNDEFINED, SHUTDOWN } volatile _state; + Genode::addr_t _device_phys; /* PCI config extended address */ - void entry() override; - - enum { KERNEL_CAP_COUNT_LOG2 = 1 }; + enum { KERNEL_CAP_COUNT_LOG2 = 0 }; Genode::addr_t const irq_sel() { return _kernel_caps; } - Genode::addr_t const sc_sel() { return _kernel_caps + 1; } public: @@ -44,11 +37,8 @@ class Genode::Irq_object : public Thread<4096> { Genode::addr_t msi_address() const { return _msi_addr; } Genode::addr_t msi_value() const { return _msi_data; } - void sigh(Signal_context_capability cap) { _sig_cap = cap; } - void notify() { Genode::Signal_transmitter(_sig_cap).submit(1); } + void sigh(Signal_context_capability cap); + void ack_irq(); - void ack_irq() { _sync_ack.unlock(); } - - void start() override; void start(unsigned irq, Genode::addr_t); }; diff --git a/repos/base-nova/src/core/irq_session_component.cc b/repos/base-nova/src/core/irq_session_component.cc index bb3ee2d8c..e130e962d 100644 --- a/repos/base-nova/src/core/irq_session_component.cc +++ b/repos/base-nova/src/core/irq_session_component.cc @@ -15,65 +15,59 @@ /* Genode includes */ #include -#include /* core includes */ #include #include -#include /* NOVA includes */ #include -#include #include using namespace Genode; -static void thread_start() +static bool irq_ctrl(Genode::addr_t irq_sel, + Genode::addr_t &msi_addr, Genode::addr_t &msi_data, + Genode::addr_t sig_sel, Genode::addr_t virt_addr = 0) { - Thread_base::myself()->entry(); - sleep_forever(); -} - - -static bool associate(unsigned irq, Genode::addr_t irq_sel, - Genode::addr_t &msi_addr, Genode::addr_t &msi_data, - Genode::addr_t virt_addr = 0) -{ - /* map IRQ SM cap from kernel to core at irq_sel selector */ - using Nova::Obj_crd; - - Obj_crd src(platform_specific()->gsi_base_sel() + irq, 0); - Obj_crd dst(irq_sel, 0); - enum { MAP_FROM_KERNEL_TO_CORE = true }; - - int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(), - src, dst, MAP_FROM_KERNEL_TO_CORE); - if (ret) { - PERR("Could not map IRQ %u", irq); - return false; - } - /* assign IRQ to CPU && request msi data to be used by driver */ uint8_t res = Nova::assign_gsi(irq_sel, virt_addr, boot_cpu(), - msi_addr, msi_data); + msi_addr, msi_data, sig_sel); - if (virt_addr && res != Nova::NOVA_OK) { - PERR("setting up MSI %u failed - error %u", irq, res); - return false; - } + if (res != Nova::NOVA_OK) + PERR("setting up MSI failed - error %u", res); /* nova syscall interface specifies msi addr/data to be 32bit */ msi_addr = msi_addr & ~0U; msi_data = msi_data & ~0U; - return true; + return res == Nova::NOVA_OK; } -static bool msi(unsigned irq, Genode::addr_t irq_sel, Genode::addr_t phys_mem, - Genode::addr_t &msi_addr, Genode::addr_t &msi_data) +static bool associate(Genode::addr_t irq_sel, + Genode::addr_t &msi_addr, Genode::addr_t &msi_data, + Genode::Signal_context_capability sig_cap, + Genode::addr_t virt_addr = 0) +{ + return irq_ctrl(irq_sel, msi_addr, msi_data, sig_cap.local_name(), + virt_addr); +} + + +static void deassociate(Genode::addr_t irq_sel) +{ + addr_t dummy1 = 0, dummy2 = 0; + + if (!irq_ctrl(irq_sel, dummy1, dummy2, irq_sel)) + PWRN("Irq could not be de-associated"); +} + + +static bool msi(Genode::addr_t irq_sel, Genode::addr_t phys_mem, + Genode::addr_t &msi_addr, Genode::addr_t &msi_data, + Genode::Signal_context_capability sig_cap) { void * virt = 0; if (platform()->region_alloc()->alloc_aligned(4096, &virt, 12).is_error()) @@ -96,7 +90,7 @@ static bool msi(unsigned irq, Genode::addr_t irq_sel, Genode::addr_t phys_mem, } /* try to assign MSI to device */ - bool res = associate(irq, irq_sel, msi_addr, msi_data, virt_addr); + bool res = associate(irq_sel, msi_addr, msi_data, sig_cap, virt_addr); unmap_local(Nova::Mem_crd(virt_addr >> 12, 0, Rights(true, true, true))); platform()->region_alloc()->free(virt, 4096); @@ -105,129 +99,83 @@ static bool msi(unsigned irq, Genode::addr_t irq_sel, Genode::addr_t phys_mem, } -void Irq_object::start() +void Irq_object::sigh(Signal_context_capability cap) { - PERR("wrong start method called"); - throw Root::Unavailable(); + if (!_sigh_cap.valid() && !cap.valid()) + return; + + if ((_sigh_cap.valid() && !cap.valid())) { + deassociate(irq_sel()); + _sigh_cap = Signal_context_capability(); + return; + } + + bool ok = false; + if (_device_phys) + ok = msi(irq_sel(), _device_phys, _msi_addr, _msi_data, cap); + else + ok = associate(irq_sel(), _msi_addr, _msi_data, cap); + + if (!ok) { + deassociate(irq_sel()); + _sigh_cap = Signal_context_capability(); + return; + } + + _sigh_cap = cap; +} + + +void Irq_object::ack_irq() +{ + if (Nova::NOVA_OK != Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_DOWN)) + PERR("Unmasking irq of selector 0x%lx failed", irq_sel()); } -/** - * Create global EC, associate it to SC - */ void Irq_object::start(unsigned irq, Genode::addr_t const device_phys) { + /* map IRQ SM cap from kernel to core at irq_sel selector */ + using Nova::Obj_crd; + + Obj_crd src(platform_specific()->gsi_base_sel() + irq, 0); + Obj_crd dst(irq_sel(), 0); + enum { MAP_FROM_KERNEL_TO_CORE = true }; + + int ret = map_local((Nova::Utcb *)Thread_base::myself()->utcb(), + src, dst, MAP_FROM_KERNEL_TO_CORE); + if (ret) { + PERR("Getting IRQ from kernel failed - %u", irq); + throw Root::Unavailable(); + } + /* associate GSI or MSI to device belonging to device_phys */ bool ok = false; if (device_phys) - ok = msi(irq, irq_sel(), device_phys, _msi_addr, _msi_data); + ok = msi(irq_sel(), device_phys, _msi_addr, _msi_data, _sigh_cap); else - ok = associate(irq, irq_sel(), _msi_addr, _msi_data); + ok = associate(irq_sel(), _msi_addr, _msi_data, _sigh_cap); if (!ok) throw Root::Unavailable(); - /* start thread having a SC */ - using namespace Nova; - addr_t pd_sel = Platform_pd::pd_core_sel(); - addr_t utcb = reinterpret_cast(&_context->utcb); - - /* put IP on stack, it will be read from core pager in platform.cc */ - addr_t *sp = reinterpret_cast(_context->stack_top() - sizeof(addr_t)); - *sp = reinterpret_cast(thread_start); - - /* create global EC */ - enum { GLOBAL = true }; - uint8_t res = create_ec(_tid.ec_sel, pd_sel, boot_cpu(), - utcb, (mword_t)sp, _tid.exc_pt_sel, GLOBAL); - if (res != NOVA_OK) { - PERR("%p - create_ec returned %d", this, res); - throw Root::Unavailable(); - } - - /* remap startup portal from main thread */ - if (map_local((Utcb *)Thread_base::myself()->utcb(), - Obj_crd(PT_SEL_STARTUP, 0), - Obj_crd(_tid.exc_pt_sel + PT_SEL_STARTUP, 0))) { - PERR("could not create startup portal"); - throw Root::Unavailable(); - } - - /* remap debugging page fault portal for core threads */ - if (map_local((Utcb *)Thread_base::myself()->utcb(), - Obj_crd(PT_SEL_PAGE_FAULT, 0), - Obj_crd(_tid.exc_pt_sel + PT_SEL_PAGE_FAULT, 0))) { - PERR("could not create page fault portal"); - throw Root::Unavailable(); - } - - /* default: we don't accept any mappings or translations */ - Utcb * utcb_obj = reinterpret_cast(Thread_base::utcb()); - utcb_obj->crd_rcv = Obj_crd(); - utcb_obj->crd_xlt = Obj_crd(); - - /* create SC */ - Qpd qpd(Qpd::DEFAULT_QUANTUM, Qpd::DEFAULT_PRIORITY + 1); - res = create_sc(sc_sel(), pd_sel, _tid.ec_sel, qpd); - if (res != NOVA_OK) { - PERR("%p - create_sc returned returned %d", this, res); - throw Root::Unavailable(); - } - - _sync_life.lock(); -} - - -void Irq_object::entry() -{ - /* signal that thread is up and ready */ - _sync_life.unlock(); - - /* wait for first ack_irq */ - _sync_ack.lock(); - - while (true) { - - if (Nova::NOVA_OK != Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_DOWN)) - PERR("Error: blocking for irq_sel 0x%lx failed", irq_sel()); - - if (_state == SHUTDOWN) { - /* signal end of life to entrypoint thread */ - _sync_life.unlock(); - while (1) nova_die(); - } - - if (!_sig_cap.valid()) - continue; - - notify(); - - _sync_ack.lock(); - } + _device_phys = device_phys; } Irq_object::Irq_object() : - Thread<4096>("irq"), - _sync_ack(Lock::LOCKED), _sync_life(Lock::LOCKED), _kernel_caps(cap_map()->insert(KERNEL_CAP_COUNT_LOG2)), - _msi_addr(0UL), _msi_data(0UL), _state(UNDEFINED) + _msi_addr(0UL), _msi_data(0UL) { } Irq_object::~Irq_object() { - /* tell interrupt thread to get in a defined dead state */ - _state = SHUTDOWN; - /* send ack - thread maybe got not the first ack */ - _sync_ack.unlock(); - /* unblock thread if it is waiting for interrupts */ - Nova::sm_ctrl(irq_sel(), Nova::SEMAPHORE_UP); - /* wait until thread signals end of life */ - _sync_life.lock(); + if (_sigh_cap.valid()) + deassociate(irq_sel()); - /* revoke SC and SM of interrupt source */ + /* revoke IRQ SM */ Nova::revoke(Nova::Obj_crd(_kernel_caps, KERNEL_CAP_COUNT_LOG2)); enum { NO_REVOKE_REQUIRED = false }; cap_map()->remove(_kernel_caps, KERNEL_CAP_COUNT_LOG2, NO_REVOKE_REQUIRED); @@ -259,7 +207,7 @@ Irq_session_component::Irq_session_component(Range_allocator *irq_alloc, long device_phys = Arg_string::find_arg(args, "device_config_phys").long_value(0); if (device_phys) { - if (irq_number >= kernel_hip()->sel_gsi) + if ((unsigned long)irq_number >= kernel_hip()->sel_gsi) throw Root::Unavailable(); irq_number = kernel_hip()->sel_gsi - 1 - irq_number;