nova: support EC time in trace subject info

The vanilla NOVA kernel solely supports tracking and exporting of execution
times per SC kernel object, but not per thread (EC object). The commit extends
to track execution times per EC in the NOVA kernel, exporting it to Genode's
'core' roottask and populating Genode's Trace::Subject_info structure.

Fixes #4481
This commit is contained in:
Alexander Boettcher 2022-04-23 22:15:35 +02:00 committed by Christian Helmuth
parent f6fedd5348
commit 858505918a
9 changed files with 193 additions and 52 deletions

View File

@ -242,6 +242,14 @@ namespace Nova {
EC_DONATE_SC = 2U,
EC_RESCHEDULE = 3U,
EC_MIGRATE = 4U,
EC_TIME = 5U,
};
enum Sc_op {
SC_TIME_IDLE = 0,
SC_TIME_CROSS = 1,
SC_TIME_KILLED = 2,
SC_EC_TIME = 3,
};
/**

View File

@ -182,6 +182,40 @@ namespace Nova {
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_6(Syscall s, uint8_t flags, unsigned sel,
mword_t &p1, mword_t &p2, mword_t &p3,
mword_t &p4)
{
mword_t status = eax(s, flags, sel);
asm volatile (" push %%ebp;"
" push %%ebx;"
" mov %%ecx, %%ebx;"
" mov %%esp, %%ecx;"
" mov %%edx, %%ebp;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
"sysenter;"
"1:"
" mov %%ebp, %%edx;"
" mov %%ebx, %%ecx;"
" pop %%ebx;"
" pop %%ebp;"
: "+a" (status), "+D" (p1), "+S" (p2), "+c" (p3),
"+d" (p4)
:
: "memory");
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t call(unsigned pt)
{
@ -239,14 +273,51 @@ namespace Nova {
}
ALWAYS_INLINE
inline uint8_t util_time(Syscall const syscall, mword_t const cap,
uint8_t const op, unsigned long long &time)
{
mword_t time_h = 0, time_l = 0;
uint8_t res = syscall_5(syscall, op, cap, time_h, time_l);
time = (uint64_t(time_h) << 32ULL) | uint64_t(time_l);
return res;
}
ALWAYS_INLINE
inline uint8_t sc_ec_time(mword_t const cap_sc, mword_t const cap_ec,
unsigned long long &time_sc,
unsigned long long &time_ec)
{
mword_t time_h_sc = cap_ec, time_l_sc = 0;
mword_t time_h_ec = 0, time_l_ec = 0;
uint8_t res = syscall_6(NOVA_SC_CTRL, Sc_op::SC_EC_TIME, cap_sc,
time_h_sc, time_l_sc, time_h_ec,
time_l_ec);
time_sc = (uint64_t(time_h_sc) << 32ULL) | uint64_t(time_l_sc);
time_ec = (uint64_t(time_h_ec) << 32ULL) | uint64_t(time_l_ec);
return res;
}
ALWAYS_INLINE
inline uint8_t ec_ctrl(Ec_op op, mword_t ec = ~0UL, mword_t para = ~0UL,
Crd crd = 0)
{
if (op == EC_TIME)
return NOVA_INV_HYPERCALL;
return syscall_2(NOVA_EC_CTRL, op, ec, para, crd.value());
}
ALWAYS_INLINE
inline uint8_t ec_time(mword_t const ec, unsigned long long &time)
{
return util_time(NOVA_EC_CTRL, ec, Ec_op::EC_TIME, time);
}
ALWAYS_INLINE
inline uint8_t create_sc(unsigned sc, unsigned pd, unsigned ec, Qpd qpd)
{
@ -396,13 +467,9 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t sc_ctrl(unsigned sc, unsigned long long &time, uint8_t op = 0)
inline uint8_t sc_ctrl(unsigned const sc, unsigned long long &time, uint8_t op = 0)
{
mword_t time_h = 0, time_l = 0;
uint8_t res = syscall_5(NOVA_SC_CTRL, op, sc, time_h, time_l);
time = time_h;
time = (time << 32ULL) | time_l;
return res;
return util_time(NOVA_SC_CTRL, sc, op, time);
}
}
#endif /* _INCLUDE__SPEC__32BIT__NOVA__SYSCALLS_H_ */

View File

@ -132,6 +132,24 @@ namespace Nova {
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t syscall_6(Syscall s, uint8_t flags, mword_t sel,
mword_t &p1, mword_t &p2, mword_t &p3,
mword_t &p4)
{
mword_t status = rdi(s, flags, sel);
register mword_t r8 asm ("r8") = p4;
asm volatile ("syscall"
: "+D" (status), "+S"(p1), "+d"(p2), "+a"(p3), "+r"(r8)
:
: "rcx", "r11", "memory");
p4 = r8;
return (uint8_t)status;
}
ALWAYS_INLINE
inline uint8_t call(mword_t pt)
@ -191,14 +209,51 @@ namespace Nova {
}
ALWAYS_INLINE
inline uint8_t util_time(Syscall const syscall, mword_t const cap,
uint8_t const op, unsigned long long &time)
{
mword_t time_h = 0, time_l = 0;
uint8_t res = syscall_5(syscall, op, cap, time_h, time_l);
time = (time_h << 32ULL) | (time_l & 0xFFFFFFFFULL);
return res;
}
ALWAYS_INLINE
inline uint8_t sc_ec_time(mword_t const cap_sc, mword_t const cap_ec,
unsigned long long &time_sc,
unsigned long long &time_ec)
{
mword_t time_h_sc = cap_ec, time_l_sc = 0;
mword_t time_h_ec = 0, time_l_ec = 0;
uint8_t res = syscall_6(NOVA_SC_CTRL, Sc_op::SC_EC_TIME, cap_sc,
time_h_sc, time_l_sc, time_h_ec,
time_l_ec);
time_sc = (time_h_sc << 32ULL) | (time_l_sc & 0xFFFFFFFFULL);
time_ec = (time_h_ec << 32ULL) | (time_l_ec & 0xFFFFFFFFULL);
return res;
}
ALWAYS_INLINE
inline uint8_t ec_ctrl(Ec_op op, mword_t ec = ~0UL, mword_t para = ~0UL,
Crd crd = 0)
{
if (op == EC_TIME)
return NOVA_INV_HYPERCALL;
return syscall_2(NOVA_EC_CTRL, op, ec, para, crd.value());
}
ALWAYS_INLINE
inline uint8_t ec_time(mword_t const ec, unsigned long long &time)
{
return util_time(NOVA_EC_CTRL, ec, Ec_op::EC_TIME, time);
}
ALWAYS_INLINE
inline uint8_t create_sc(mword_t sc, mword_t pd, mword_t ec, Qpd qpd)
{
@ -316,13 +371,10 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t sc_ctrl(mword_t sm, unsigned long long &time, uint8_t op = 0)
inline uint8_t sc_ctrl(mword_t const sc, unsigned long long &time,
Sc_op const op)
{
mword_t time_h = 0, time_l = 0;
uint8_t res = syscall_5(NOVA_SC_CTRL, op, sm, time_h, time_l);
time = time_h;
time = (time << 32ULL) | (time_l & 0xFFFFFFFFULL);
return res;
return util_time(NOVA_SC_CTRL, sc, op, time);
}

View File

@ -1 +1 @@
6a1f0e6e6386e60748607c3c460c51a36a162307
33a2fa953ec52b0f63b921f4d33d68891c0aada0

View File

@ -4,7 +4,7 @@ DOWNLOADS := nova.git
# r10 branch
URL(nova) := https://github.com/alex-ab/NOVA.git
REV(nova) := 7e3b5d3916825fb32e011cff4ee9b09ecdcb8d05
REV(nova) := 00dc49bc18e7f72a9c85487e8f94fd859511d89d
DIR(nova) := src/kernel/nova
PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch))

View File

@ -391,23 +391,6 @@ Platform::Platform()
Obj_crd(sc_idle_base, log2cpu), true))
nova_die();
/* test reading out idle SCs */
bool sc_init = true;
for (unsigned i = 0; i < hip.cpu_max(); i++) {
if (!hip.is_cpu_enabled(i))
continue;
uint64_t n_time;
uint8_t res = Nova::sc_ctrl(sc_idle_base + i, n_time);
if (res != Nova::NOVA_OK) {
sc_init = false;
error(i, " ", res, " ", n_time, " - failed");
}
}
if (!sc_init)
nova_die();
/* configure virtual address spaces */
#ifdef __x86_64__
_vm_size = 0x7fffc0000000UL - _vm_base;
@ -865,19 +848,30 @@ Platform::Platform()
Info trace_source_info() const override
{
uint64_t sc_time = 0;
uint64_t ec_time = 0;
uint8_t res = 0;
enum SYSCALL_OP { IDLE_SC = 0, CROSS_SC = 1,
KILLED_SC = 2 };
uint8_t syscall_op = (name == "cross") ? CROSS_SC :
((name == "killed") ? KILLED_SC : IDLE_SC);
if (name == "killed") {
res = Nova::sc_ec_time(sc_sel, sc_sel,
sc_time, ec_time);
} else {
auto syscall_op = (name == "cross")
? Sc_op::SC_TIME_CROSS
: Sc_op::SC_TIME_IDLE;
res = Nova::sc_ctrl(sc_sel, sc_time,
syscall_op);
if (syscall_op == Sc_op::SC_TIME_IDLE)
ec_time = sc_time;
}
uint8_t res = Nova::sc_ctrl(sc_sel, sc_time, syscall_op);
if (res != Nova::NOVA_OK)
warning("sc_ctrl on idle SC cap, op=", syscall_op,
warning("sc_ctrl on ", name, " failed"
", res=", res);
return { Session_label("kernel"), Trace::Thread_name(name),
Trace::Execution_time(sc_time, sc_time), affinity };
Trace::Execution_time(ec_time, sc_time), affinity };
}
Trace_source(Trace::Source_registry &registry,
@ -920,16 +914,26 @@ Platform::Platform()
*/
Info trace_source_info() const override
{
uint64_t ec_time = 0;
uint64_t sc_time = 0;
if (name == "root") {
uint8_t res = Nova::sc_ctrl(ec_sc_sel + 1, sc_time);
uint8_t res = Nova::sc_ec_time(ec_sc_sel + 1,
ec_sc_sel,
sc_time,
ec_time);
if (res != Nova::NOVA_OK)
warning("sc_ctrl for ", name, " thread failed res=", res);
warning("sc_ec_time for root failed "
"res=", res);
} else {
uint8_t res = Nova::ec_time(ec_sc_sel, ec_time);
if (res != Nova::NOVA_OK)
warning("ec_time for", name, " thread "
"failed res=", res);
}
return { Session_label("core"), name,
Trace::Execution_time(sc_time, sc_time), location };
Trace::Execution_time(ec_time, sc_time), location };
}
Core_trace_source(Trace::Source_registry &registry,

View File

@ -314,17 +314,22 @@ const char * Platform_thread::pd_name() const {
Trace::Execution_time Platform_thread::execution_time() const
{
unsigned long long time = 0;
uint64_t sc_time = 0;
uint64_t ec_time = 0;
/* for ECs without a SC we simply return 0 */
if (!sc_created())
return { time, time, Nova::Qpd::DEFAULT_QUANTUM, _priority };
if (!sc_created()) {
/* time executed by EC (on whatever SC) */
uint8_t res = Nova::ec_time(_sel_ec(), ec_time);
if (res != Nova::NOVA_OK)
warning("ec_time failed res=", res);
return { ec_time, sc_time, Nova::Qpd::DEFAULT_QUANTUM, _priority };
}
uint8_t res = Nova::sc_ctrl(_sel_sc(), time);
uint8_t res = Nova::sc_ec_time(_sel_sc(), _sel_ec(), sc_time, ec_time);
if (res != Nova::NOVA_OK)
warning("sc_ctrl failed res=", res);
return { time, time, Nova::Qpd::DEFAULT_QUANTUM, _priority };
return { ec_time, sc_time, Nova::Qpd::DEFAULT_QUANTUM, _priority };
}

View File

@ -127,10 +127,14 @@ void Thread::start()
*/
Info trace_source_info() const override
{
uint64_t sc_time = 0;
uint64_t ec_time = 0;
uint8_t res = Nova::ec_time(thread.native_thread().ec_sel, ec_time);
if (res != Nova::NOVA_OK)
warning("ec_time for core thread failed res=", res);
return { Session_label("core"), thread.name(),
Trace::Execution_time(sc_time, sc_time), thread._affinity };
Trace::Execution_time(ec_time, 0), thread._affinity };
}
Core_trace_source(Trace::Source_registry &registry, Thread &t)

View File

@ -85,14 +85,15 @@ static uint8_t _with_kernel_quota_upgrade(addr_t const pd_target,
Trace::Source::Info Vm_session_component::Vcpu::trace_source_info() const
{
uint64_t ec_time = 0;
uint64_t sc_time = 0;
uint8_t res = Nova::sc_ctrl(sc_sel(), sc_time);
uint8_t res = Nova::sc_ec_time(sc_sel(), ec_sel(), sc_time, ec_time);
if (res != Nova::NOVA_OK)
warning("sc_ctrl failed res=", res);
warning("vCPU sc_ec_time failed res=", res);
return { _label, String<5>("vCPU"),
Trace::Execution_time(sc_time, sc_time,
Trace::Execution_time(ec_time, sc_time,
Nova::Qpd::DEFAULT_QUANTUM, _priority),
_location };
}