base-hw: do direct syscall when run/pause a VCPU

Instead of calling core to run/pause a VCPU, go directly to the kernel.
Apart from the performance win, it would otherwise involve a more complex
protocol, when a VCPU on another core has to be removed from the scheduler.
Core's entrypoint handling those request runs on the boot-cpu only.

Ref #3926
This commit is contained in:
Stefan Kalkowski 2020-10-09 10:33:44 +02:00 committed by Christian Helmuth
parent 40445d7011
commit 1d826a2c48
11 changed files with 130 additions and 42 deletions

View File

@ -42,6 +42,8 @@ namespace Kernel
constexpr Call_arg call_id_timeout() { return 16; }
constexpr Call_arg call_id_timeout_max_us() { return 17; }
constexpr Call_arg call_id_time() { return 18; }
constexpr Call_arg call_id_run_vm() { return 19; }
constexpr Call_arg call_id_pause_vm() { return 20; }
/*****************************************************************
@ -369,6 +371,28 @@ namespace Kernel
{
call(call_id_delete_cap(), cap);
}
/**
* Execute a virtual-machine (again)
*
* \param vm pointer to vm kernel object
*/
inline void run_vm(capid_t const cap)
{
call(call_id_run_vm(), cap);
}
/**
* Stop execution of a virtual-machine
*
* \param vm pointer to vm kernel object
*/
inline void pause_vm(capid_t const cap)
{
call(call_id_pause_vm(), cap);
}
}
#endif /* _INCLUDE__KERNEL__INTERFACE_H_ */

View File

@ -51,8 +51,6 @@ namespace Kernel
constexpr Call_arg call_id_delete_signal_context() { return 112; }
constexpr Call_arg call_id_delete_signal_receiver() { return 113; }
constexpr Call_arg call_id_new_vm() { return 114; }
constexpr Call_arg call_id_run_vm() { return 115; }
constexpr Call_arg call_id_pause_vm() { return 116; }
constexpr Call_arg call_id_delete_vm() { return 117; }
constexpr Call_arg call_id_new_irq() { return 118; }
constexpr Call_arg call_id_delete_irq() { return 119; }
@ -152,27 +150,6 @@ namespace Kernel
}
/**
* Execute a virtual-machine (again)
*
* \param vm pointer to vm kernel object
*/
inline void run_vm(Vm & vm)
{
call(call_id_run_vm(), (Call_arg) &vm);
}
/**
* Stop execution of a virtual-machine
*
* \param vm pointer to vm kernel object
*/
inline void pause_vm(Vm & vm)
{
call(call_id_pause_vm(), (Call_arg) &vm);
}
/**
* Acknowledge interrupt
*

View File

@ -740,6 +740,8 @@ void Thread::_call()
case call_id_timeout(): _call_timeout(); return;
case call_id_timeout_max_us(): _call_timeout_max_us(); return;
case call_id_time(): _call_time(); return;
case call_id_run_vm(): _call_run_vm(); return;
case call_id_pause_vm(): _call_pause_vm(); return;
default:
/* check wether this is a core thread */
if (!_core) {
@ -777,8 +779,6 @@ void Thread::_call()
case call_id_delete_signal_receiver(): _call_delete<Signal_receiver>(); return;
case call_id_new_vm(): _call_new_vm(); return;
case call_id_delete_vm(): _call_delete_vm(); return;
case call_id_run_vm(): _call_run_vm(); return;
case call_id_pause_vm(): _call_pause_vm(); return;
case call_id_pause_thread(): _call_pause_thread(); return;
case call_id_new_irq(): _call_new_irq(); return;
case call_id_delete_irq(): _call_delete<User_irq>(); return;

View File

@ -33,7 +33,7 @@ namespace Kernel
}
class Kernel::Vm : public Cpu_job
class Kernel::Vm : private Kernel::Object, public Cpu_job
{
private:

View File

@ -34,13 +34,31 @@ void Kernel::Thread::_call_delete_vm() { _call_delete<Vm>(); }
void Kernel::Thread::_call_run_vm()
{
reinterpret_cast<Vm*>(user_arg_1())->run();
Object_identity_reference * ref = pd().cap_tree().find(user_arg_1());
Vm * vm = ref ? ref->object<Vm>() : nullptr;
if (!vm) {
Genode::raw("Invalid VM cap");
user_arg_0(-1);
return;
}
vm->run();
user_arg_0(0);
}
void Kernel::Thread::_call_pause_vm()
{
reinterpret_cast<Vm*>(user_arg_1())->pause();
Object_identity_reference * ref = pd().cap_tree().find(user_arg_1());
Vm * vm = ref ? ref->object<Vm>() : nullptr;
if (!vm) {
Genode::raw("Invalid VM cap");
user_arg_0(-1);
return;
}
vm->pause();
user_arg_0(0);
}

View File

@ -24,7 +24,7 @@ Kernel::Vm::Vm(unsigned,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const)
:
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_state(state),
_context(context),

View File

@ -112,7 +112,8 @@ Kernel::Vm::Vm(unsigned, /* FIXME: smp support */
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const table)
: Cpu_job(Cpu_priority::MIN, 0),
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_id(alloc().alloc()),
_state(state),
_context(context),

View File

@ -119,7 +119,8 @@ Vm::Vm(unsigned cpu,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const table)
: Cpu_job(Cpu_priority::MIN, 0),
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_id(alloc().alloc()),
_state(state),
_context(context),

View File

@ -22,7 +22,8 @@ Kernel::Vm::Vm(unsigned,
Board::Vm_state & state,
Kernel::Signal_context & context,
void * const)
: Cpu_job(Cpu_priority::MIN, 0),
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_state(state),
_context(context),
_table(nullptr),

View File

@ -41,18 +41,10 @@ addr_t Vm_session_component::_alloc_ds()
}
void Vm_session_component::_run(Vcpu_id id)
{
if (_valid_id(id) && _vcpus[id.id].kobj.constructed())
Kernel::run_vm(*_vcpus[id.id].kobj);
}
void Vm_session_component::_run(Vcpu_id) { }
void Vm_session_component::_pause(Vcpu_id id)
{
if (_valid_id(id) && _vcpus[id.id].kobj.constructed())
Kernel::pause_vm(*_vcpus[id.id].kobj);
}
void Vm_session_component::_pause(Vcpu_id) { }
Capability<Vm_session::Native_vcpu> Vm_session_component::_native_vcpu(Vcpu_id id)

View File

@ -0,0 +1,74 @@
/*
* \brief Client-side VM session interface
* \author Alexander Boettcher
* \date 2018-08-27
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/allocator.h>
#include <base/env.h>
#include <base/registry.h>
#include <base/internal/capability_space.h>
#include <kernel/interface.h>
#include <vm_session/client.h>
using namespace Genode;
struct Vcpu;
static Genode::Registry<Genode::Registered<Vcpu>> vcpus;
struct Vcpu {
Vm_session_client::Vcpu_id const id;
Capability<Vm_session::Native_vcpu> const cap;
Vcpu(Vm_session::Vcpu_id const id,
Capability<Vm_session::Native_vcpu> const cap)
: id(id), cap(cap) {}
virtual ~Vcpu() {}
};
Vm_session::Vcpu_id
Vm_session_client::create_vcpu(Allocator & alloc, Env &, Vm_handler_base & handler)
{
Vcpu_id const id =
call<Rpc_create_vcpu>(reinterpret_cast<Thread *>(&handler._rpc_ep)->cap());
call<Rpc_exception_handler>(handler._cap, id);
Vcpu * vcpu = new (alloc) Registered<Vcpu> (vcpus, id, call<Rpc_native_vcpu>(id));
return vcpu->id;
}
void Vm_session_client::run(Vcpu_id const vcpu_id)
{
vcpus.for_each([&] (Vcpu & vcpu) {
if (vcpu.id.id != vcpu_id.id) { return; }
Kernel::run_vm(Capability_space::capid(vcpu.cap));
});
}
void Vm_session_client::pause(Vcpu_id const vcpu_id)
{
vcpus.for_each([&] (Vcpu & vcpu) {
if (vcpu.id.id != vcpu_id.id) { return; }
Kernel::pause_vm(Capability_space::capid(vcpu.cap));
});
}
Dataspace_capability Vm_session_client::cpu_state(Vcpu_id const vcpu_id)
{
return call<Rpc_cpu_state>(vcpu_id);
}
Vm_session::~Vm_session()
{ }