mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 02:40:08 +00:00
parent
6872fdb0de
commit
aa7f5bc95f
3
repos/os/recipes/pkg/cpu_balancer/README
Normal file
3
repos/os/recipes/pkg/cpu_balancer/README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
A dynamic CPU balancer component.
|
||||||
|
|
1
repos/os/recipes/pkg/cpu_balancer/archives
Normal file
1
repos/os/recipes/pkg/cpu_balancer/archives
Normal file
@ -0,0 +1 @@
|
|||||||
|
_/src/cpu_balancer
|
1
repos/os/recipes/pkg/cpu_balancer/hash
Normal file
1
repos/os/recipes/pkg/cpu_balancer/hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
2020-11-11 5977b1e4fb0a81bd364c2f33ef587c16b6b26dc7
|
18
repos/os/recipes/pkg/cpu_balancer/runtime
Normal file
18
repos/os/recipes/pkg/cpu_balancer/runtime
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<runtime ram="2M" caps="150" binary="cpu_balancer">
|
||||||
|
|
||||||
|
<provides> <cpu/> </provides>
|
||||||
|
|
||||||
|
<requires>
|
||||||
|
<trace/>
|
||||||
|
<report/>
|
||||||
|
<timer/>
|
||||||
|
</requires>
|
||||||
|
|
||||||
|
<content>
|
||||||
|
<rom label="ld.lib.so"/>
|
||||||
|
<rom label="cpu_balancer"/>
|
||||||
|
</content>
|
||||||
|
|
||||||
|
<config interval_us="1000000" report="yes" trace="yes"/>
|
||||||
|
|
||||||
|
</runtime>
|
3
repos/os/recipes/pkg/cpu_balancer_config/README
Normal file
3
repos/os/recipes/pkg/cpu_balancer_config/README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
A dynamic CPU balancer component reading config and reporting state via file.
|
||||||
|
|
4
repos/os/recipes/pkg/cpu_balancer_config/archives
Normal file
4
repos/os/recipes/pkg/cpu_balancer_config/archives
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
_/src/cpu_balancer
|
||||||
|
_/src/init
|
||||||
|
_/src/fs_report
|
||||||
|
_/src/fs_rom
|
1
repos/os/recipes/pkg/cpu_balancer_config/hash
Normal file
1
repos/os/recipes/pkg/cpu_balancer_config/hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
2020-10-02-b 2488f0453a59f0b3582cd4a041b4845ca3b74ca1
|
73
repos/os/recipes/pkg/cpu_balancer_config/runtime
Normal file
73
repos/os/recipes/pkg/cpu_balancer_config/runtime
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<runtime ram="7M" caps="700" binary="init">
|
||||||
|
|
||||||
|
<provides> <cpu/> </provides>
|
||||||
|
|
||||||
|
<requires>
|
||||||
|
<trace/>
|
||||||
|
<timer/>
|
||||||
|
<file_system label="recall" writeable="yes"/>
|
||||||
|
</requires>
|
||||||
|
|
||||||
|
<config>
|
||||||
|
|
||||||
|
<service name="CPU">
|
||||||
|
<default-policy> <child name="cpu_balancer"/> </default-policy>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<default-route> <any-service> <parent/> </any-service> </default-route>
|
||||||
|
|
||||||
|
<parent-provides>
|
||||||
|
<service name="PD"/>
|
||||||
|
<service name="CPU"/>
|
||||||
|
<service name="LOG"/>
|
||||||
|
<service name="ROM"/>
|
||||||
|
<service name="TRACE"/>
|
||||||
|
<service name="File_system"/>
|
||||||
|
<service name="Gui"/>
|
||||||
|
<service name="Timer"/>
|
||||||
|
</parent-provides>
|
||||||
|
|
||||||
|
<start name="fs_report" caps="100">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides> <service name="Report"/> </provides>
|
||||||
|
<config> <vfs> <fs/> </vfs> </config>
|
||||||
|
<route>
|
||||||
|
<service name="File_system"> <parent label="recall"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
|
||||||
|
<start name="fs_rom" caps="100">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides> <service name="ROM"/> </provides>
|
||||||
|
<config/>
|
||||||
|
<route>
|
||||||
|
<service name="File_system"> <parent label="recall"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
|
||||||
|
<start name="cpu_balancer" caps="200">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
|
||||||
|
<provides><service name="CPU"/></provides>
|
||||||
|
<!--
|
||||||
|
<config interval_us="1000000" report="yes" trace="yes"/>
|
||||||
|
-->
|
||||||
|
<route>
|
||||||
|
<service name="ROM" label="config"> <child name="fs_rom" label="config"/> </service>
|
||||||
|
<service name="Report" label="components"> <child name="fs_report" label="components"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<content>
|
||||||
|
<rom label="ld.lib.so"/>
|
||||||
|
<rom label="cpu_balancer"/>
|
||||||
|
<rom label="init"/>
|
||||||
|
<rom label="fs_report"/>
|
||||||
|
<rom label="fs_rom"/>
|
||||||
|
</content>
|
||||||
|
|
||||||
|
</runtime>
|
2
repos/os/recipes/src/cpu_balancer/content.mk
Normal file
2
repos/os/recipes/src/cpu_balancer/content.mk
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SRC_DIR = src/server/cpu_balancer
|
||||||
|
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
1
repos/os/recipes/src/cpu_balancer/hash
Normal file
1
repos/os/recipes/src/cpu_balancer/hash
Normal file
@ -0,0 +1 @@
|
|||||||
|
2020-11-11 0cc7b984701f2f0ab0b20a59c8311ce147609834
|
4
repos/os/recipes/src/cpu_balancer/used_apis
Normal file
4
repos/os/recipes/src/cpu_balancer/used_apis
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
base
|
||||||
|
os
|
||||||
|
timer_session
|
||||||
|
report_session
|
117
repos/os/run/cpu_balancer.run
Normal file
117
repos/os/run/cpu_balancer.run
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
build "core init timer server/cpu_balancer app/cpu_burner app/top"
|
||||||
|
|
||||||
|
if {![have_include "power_on/qemu"]} {
|
||||||
|
puts "Run script is not supported on this platform"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
if {[have_spec foc] && ([have_spec pbxa9] || [have_spec rpi3])} {
|
||||||
|
# foc kernel does detect solely 1 CPU */
|
||||||
|
puts "Run script is not supported on this platform"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
if {![have_spec nova] && ![have_spec foc] && ![have_spec sel4]} {
|
||||||
|
puts "Run script is not supported on this platform"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
set cpu_width 4
|
||||||
|
set cpu_height 1
|
||||||
|
set report_config "yes"
|
||||||
|
set use_trace "yes"
|
||||||
|
|
||||||
|
create_boot_directory
|
||||||
|
|
||||||
|
import_from_depot [depot_user]/src/report_rom
|
||||||
|
|
||||||
|
append config {
|
||||||
|
<config prio_levels="2">
|
||||||
|
<affinity-space width="} $cpu_width {" height="} $cpu_height {"/>
|
||||||
|
<parent-provides>
|
||||||
|
<service name="LOG"/>
|
||||||
|
<service name="CPU"/>
|
||||||
|
<service name="ROM"/>
|
||||||
|
<service name="PD"/>
|
||||||
|
<service name="IO_PORT"/> <!-- timer on some kernels -->
|
||||||
|
<service name="IRQ"/> <!-- timer on some kernels -->
|
||||||
|
<service name="TRACE"/>
|
||||||
|
</parent-provides>
|
||||||
|
|
||||||
|
<default-route>
|
||||||
|
<service name="LOG"> <parent/> </service>
|
||||||
|
<service name="PD"> <parent/> </service>
|
||||||
|
<service name="ROM"> <parent/> </service>
|
||||||
|
</default-route>
|
||||||
|
<default caps="100"/>
|
||||||
|
|
||||||
|
<start name="timer">
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides><service name="Timer"/></provides>
|
||||||
|
<route>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>}
|
||||||
|
|
||||||
|
append_if [expr $report_config eq "yes"] config {
|
||||||
|
<start name="report_rom">
|
||||||
|
<binary name="report_rom"/>
|
||||||
|
<resource name="RAM" quantum="1M"/>
|
||||||
|
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||||
|
<config verbose="yes"/>
|
||||||
|
<route>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>}
|
||||||
|
|
||||||
|
append config {
|
||||||
|
<start name="cpu_balancer">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<provides><service name="CPU"/></provides>
|
||||||
|
<config interval_us="2000000"
|
||||||
|
report="} $report_config {"
|
||||||
|
trace="} $use_trace {"
|
||||||
|
verbose="no">
|
||||||
|
<component label="cpu_burner" default_policy="none">
|
||||||
|
<!--
|
||||||
|
<thread name="signal handler" policy="pin" xpos="1" ypos="0"/>
|
||||||
|
-->
|
||||||
|
<thread name="signal handler" policy="max-utilize"/>
|
||||||
|
<thread name="burn_0x0" policy="round-robin"/>
|
||||||
|
<thread name="burn_1x0" policy="round-robin"/>
|
||||||
|
</component>
|
||||||
|
</config>
|
||||||
|
<route>
|
||||||
|
<service name="Timer"> <child name="timer"/> </service>
|
||||||
|
<service name="Report"> <child name="report_rom"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
|
||||||
|
<start name="cpu_burner" priority="-1">
|
||||||
|
<affinity xpos="1" ypos="0" width="2" height="1"/>
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<route>
|
||||||
|
<service name="CPU"> <child name="cpu_balancer"/> </service>
|
||||||
|
<service name="Timer"> <child name="timer"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
<!--
|
||||||
|
<start name="top">
|
||||||
|
<resource name="RAM" quantum="2M"/>
|
||||||
|
<route>
|
||||||
|
<service name="Timer"> <child name="timer"/> </service>
|
||||||
|
<service name="CPU"> <child name="cpu_balancer"/> </service>
|
||||||
|
<any-service> <parent/> </any-service>
|
||||||
|
</route>
|
||||||
|
</start>
|
||||||
|
-->
|
||||||
|
</config>}
|
||||||
|
|
||||||
|
install_config $config
|
||||||
|
|
||||||
|
build_boot_image { core ld.lib.so init timer cpu_balancer cpu_burner top }
|
||||||
|
|
||||||
|
append qemu_args " -nographic"
|
||||||
|
append qemu_args " -smp [expr $cpu_width * $cpu_height],cores=$cpu_width,threads=$cpu_height"
|
||||||
|
|
||||||
|
run_genode_until {.*thread xpos="1" ypos="0" name="signal handler" policy="max-utilize".*\n} 60
|
353
repos/os/src/server/cpu_balancer/component.cc
Normal file
353
repos/os/src/server/cpu_balancer/component.cc
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
/*
|
||||||
|
* \brief CPU service proxy that migrates threads depending on policies
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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/attached_rom_dataspace.h>
|
||||||
|
#include <base/component.h>
|
||||||
|
#include <base/heap.h>
|
||||||
|
#include <base/registry.h>
|
||||||
|
#include <base/signal.h>
|
||||||
|
|
||||||
|
#include <cpu_session/cpu_session.h>
|
||||||
|
#include <timer_session/connection.h>
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
namespace Cpu {
|
||||||
|
struct Balancer;
|
||||||
|
|
||||||
|
using Genode::Affinity;
|
||||||
|
using Genode::Attached_rom_dataspace;
|
||||||
|
using Genode::Constructible;
|
||||||
|
using Genode::Cpu_session;
|
||||||
|
using Genode::Insufficient_ram_quota;
|
||||||
|
using Genode::Ram_quota;
|
||||||
|
using Genode::Rpc_object;
|
||||||
|
using Genode::Session_capability;
|
||||||
|
using Genode::Signal_handler;
|
||||||
|
using Genode::Sliced_heap;
|
||||||
|
using Genode::Typed_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EXC, typename T, typename FUNC, typename HANDLER>
|
||||||
|
auto retry(T &env, FUNC func, HANDLER handler,
|
||||||
|
unsigned attempts = ~0U) -> decltype(func())
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
for (unsigned i = 0; attempts == ~0U || i < attempts; i++)
|
||||||
|
try { return func(); }
|
||||||
|
catch (EXC) {
|
||||||
|
if ((i + 1) % 5 == 0 || env.pd().avail_ram().value < 8192)
|
||||||
|
Genode::warning(i, ". attempt to extend dialog report "
|
||||||
|
"size, ram_avail=", env.pd().avail_ram());
|
||||||
|
|
||||||
|
if (env.pd().avail_ram().value < 8192)
|
||||||
|
throw;
|
||||||
|
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw EXC();
|
||||||
|
} catch (Genode::Xml_generator::Buffer_exceeded) {
|
||||||
|
Genode::error("not enough memory for xml");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Genode::Registry<Genode::Registered<Cpu::Sleeper> > Sleeper_list;
|
||||||
|
typedef Genode::Tslab<Genode::Registered<Cpu::Sleeper>, 4096> Tslab_sleeper;
|
||||||
|
|
||||||
|
struct Cpu::Balancer : Rpc_object<Typed_root<Cpu_session>>
|
||||||
|
{
|
||||||
|
Genode::Env &env;
|
||||||
|
Attached_rom_dataspace config { env, "config" };
|
||||||
|
Timer::Connection timer { env };
|
||||||
|
Sliced_heap slice { env.ram(), env.rm() };
|
||||||
|
Child_list list { };
|
||||||
|
Constructible<Trace> trace { };
|
||||||
|
Constructible<Reporter> reporter { };
|
||||||
|
uint64_t timer_us { 1000 * 1000UL };
|
||||||
|
Session_label label { };
|
||||||
|
unsigned report_size { 4096 * 1 };
|
||||||
|
Tslab_sleeper alloc_thread { slice };
|
||||||
|
Sleeper_list sleeper { };
|
||||||
|
bool verbose { false };
|
||||||
|
bool update_report { false };
|
||||||
|
bool use_sleeper { true };
|
||||||
|
|
||||||
|
Signal_handler<Balancer> signal_config {
|
||||||
|
env.ep(), *this, &Balancer::handle_config };
|
||||||
|
|
||||||
|
void handle_config();
|
||||||
|
void handle_timeout();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need extra EP to avoid dead-lock/live-lock (depending on kernel)
|
||||||
|
* due to down-calls by this component, e.g. parent.upgrade(...), and
|
||||||
|
* up-calls by parent using this CPU service, e.g. to create initial thread
|
||||||
|
*
|
||||||
|
* Additionally, a list_mutex is required due to having 2 EPs now.
|
||||||
|
*/
|
||||||
|
Entrypoint ep { env, 4 * 4096, "live/dead-lock", Affinity::Location() };
|
||||||
|
|
||||||
|
Signal_handler<Balancer> signal_timeout {
|
||||||
|
ep, *this, &Balancer::handle_timeout };
|
||||||
|
|
||||||
|
Genode::Mutex list_mutex { };
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
Session_capability _withdraw_quota(Root::Session_args const &args, FUNC const &fn)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We need to decrease 'ram_quota' by
|
||||||
|
* the size of the session object.
|
||||||
|
*/
|
||||||
|
Ram_quota const ram_quota = ram_quota_from_args(args.string());
|
||||||
|
|
||||||
|
size_t needed = sizeof(Session) + slice.overhead(sizeof(Session));
|
||||||
|
|
||||||
|
if (needed > ram_quota.value)
|
||||||
|
throw Insufficient_ram_quota();
|
||||||
|
|
||||||
|
Ram_quota const remaining_ram_quota { ram_quota.value - needed };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate that the client provided the amount of caps as mandated
|
||||||
|
* for the session interface.
|
||||||
|
*/
|
||||||
|
Cap_quota const cap_quota = cap_quota_from_args(args.string());
|
||||||
|
|
||||||
|
if (cap_quota.value < Session::CAP_QUOTA)
|
||||||
|
throw Insufficient_cap_quota();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Account for the dataspace capability needed for allocating the
|
||||||
|
* session object from the sliced heap.
|
||||||
|
*/
|
||||||
|
if (cap_quota.value < 1)
|
||||||
|
throw Insufficient_cap_quota();
|
||||||
|
|
||||||
|
Cap_quota const remaining_cap_quota { cap_quota.value - 1 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deduce ram quota needed for allocating the session object from the
|
||||||
|
* donated ram quota.
|
||||||
|
*/
|
||||||
|
char argbuf[Parent::Session_args::MAX_SIZE];
|
||||||
|
copy_cstring(argbuf, args.string(), sizeof(argbuf));
|
||||||
|
|
||||||
|
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", remaining_ram_quota.value);
|
||||||
|
Arg_string::set_arg(argbuf, sizeof(argbuf), "cap_quota", remaining_cap_quota.value);
|
||||||
|
|
||||||
|
return fn(argbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************
|
||||||
|
** Session interface **
|
||||||
|
***********************/
|
||||||
|
|
||||||
|
Session_capability session(Root::Session_args const &args,
|
||||||
|
Affinity const &affinity) override
|
||||||
|
{
|
||||||
|
return _withdraw_quota(args, [&] (char const * const session_args) {
|
||||||
|
if (verbose)
|
||||||
|
log("new session '", args.string(), "' -> '", session_args, "' ",
|
||||||
|
affinity.space().width(), "x", affinity.space().height(), " ",
|
||||||
|
affinity.location().xpos(), "x", affinity.location().ypos(),
|
||||||
|
" ", affinity.location().width(), "x", affinity.location().height());
|
||||||
|
|
||||||
|
Mutex::Guard guard(list_mutex);
|
||||||
|
|
||||||
|
auto * session = new (slice) Registered<Session>(list, env,
|
||||||
|
affinity,
|
||||||
|
session_args,
|
||||||
|
list, verbose);
|
||||||
|
|
||||||
|
/* check for config of new session */
|
||||||
|
Cpu::Config::apply(config.xml(), list);
|
||||||
|
|
||||||
|
return session->cap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void upgrade(Session_capability const cap, Root::Upgrade_args const &args) override
|
||||||
|
{
|
||||||
|
if (!args.valid_string()) return;
|
||||||
|
|
||||||
|
auto lambda = [&] (Rpc_object_base *rpc_obj) {
|
||||||
|
if (!rpc_obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Session *session = dynamic_cast<Session *>(rpc_obj);
|
||||||
|
if (!session)
|
||||||
|
return;
|
||||||
|
|
||||||
|
session->upgrade(args, [&](auto id, auto const &adjusted_args) {
|
||||||
|
env.upgrade(id, adjusted_args);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Mutex::Guard guard(list_mutex);
|
||||||
|
|
||||||
|
env.ep().rpc_ep().apply(cap, lambda);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(Session_capability const cap) override
|
||||||
|
{
|
||||||
|
if (!cap.valid()) return;
|
||||||
|
|
||||||
|
Mutex::Guard guard(list_mutex);
|
||||||
|
|
||||||
|
Session *object = nullptr;
|
||||||
|
|
||||||
|
env.ep().rpc_ep().apply(cap,
|
||||||
|
[&] (Session *source) {
|
||||||
|
object = source;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!object)
|
||||||
|
return;
|
||||||
|
|
||||||
|
destroy(slice, object);
|
||||||
|
|
||||||
|
update_report = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************
|
||||||
|
** Constructor **
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
Balancer(Genode::Env &env) : env(env)
|
||||||
|
{
|
||||||
|
config.sigh(signal_config);
|
||||||
|
timer.sigh(signal_timeout);
|
||||||
|
|
||||||
|
Affinity::Space const space = env.cpu().affinity_space();
|
||||||
|
Genode::log("affinity space=",
|
||||||
|
space.width(), "x", space.height());
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < space.total(); i++) {
|
||||||
|
Affinity::Location location = env.cpu().affinity_space().location_of_index(i);
|
||||||
|
Sleeper *t = new (alloc_thread) Genode::Registered<Sleeper>(sleeper, env, location);
|
||||||
|
t->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_config();
|
||||||
|
|
||||||
|
/* first time start ever timeout */
|
||||||
|
timer.trigger_periodic(timer_us);
|
||||||
|
|
||||||
|
env.parent().announce(env.ep().manage(*this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Cpu::Balancer::handle_config()
|
||||||
|
{
|
||||||
|
config.update();
|
||||||
|
|
||||||
|
bool use_trace = true;
|
||||||
|
bool use_report = true;
|
||||||
|
uint64_t time_us = timer_us;
|
||||||
|
|
||||||
|
if (config.valid()) {
|
||||||
|
use_trace = config.xml().attribute_value("trace", use_trace);
|
||||||
|
use_report = config.xml().attribute_value("report", use_report);
|
||||||
|
time_us = config.xml().attribute_value("interval_us", timer_us);
|
||||||
|
verbose = config.xml().attribute_value("verbose", verbose);
|
||||||
|
use_sleeper = config.xml().attribute_value("sleeper", use_sleeper);
|
||||||
|
|
||||||
|
/* read in components configuration */
|
||||||
|
Cpu::Config::apply(config.xml(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
log("config update - verbose=", verbose, ", trace=", use_trace,
|
||||||
|
", report=", use_report, ", interval=", timer_us,"us");
|
||||||
|
|
||||||
|
/* also start all subsystem if no valid config is available */
|
||||||
|
trace.conditional(use_trace, env);
|
||||||
|
if (use_trace && !label.valid())
|
||||||
|
label = trace->lookup_my_label();
|
||||||
|
|
||||||
|
reporter.conditional(use_report, env, "components", "components", report_size);
|
||||||
|
if (use_report)
|
||||||
|
reporter->enabled(true);
|
||||||
|
|
||||||
|
if (timer_us != time_us)
|
||||||
|
timer.trigger_periodic(time_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cpu::Balancer::handle_timeout()
|
||||||
|
{
|
||||||
|
Mutex::Guard guard(list_mutex);
|
||||||
|
|
||||||
|
if (use_sleeper) {
|
||||||
|
/* wake all sleepers to get more accurate idle CPU utilization times */
|
||||||
|
sleeper.for_each([](auto &thread) {
|
||||||
|
thread._block.wakeup(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remember current reread state */
|
||||||
|
unsigned reread_subjects = 0;
|
||||||
|
if (trace.constructed()) {
|
||||||
|
reread_subjects = trace->subject_id_reread();
|
||||||
|
trace->read_idle_times();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update all sessions */
|
||||||
|
list.for_each([&](auto &session) {
|
||||||
|
if (trace.constructed()) {
|
||||||
|
session.update_threads(*trace, label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
session.update_threads();
|
||||||
|
|
||||||
|
if (session.report_update())
|
||||||
|
update_report = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* reset reread state if it did not change in between */
|
||||||
|
if (trace.constructed() && trace->subject_id_reread() &&
|
||||||
|
reread_subjects == trace->subject_id_reread())
|
||||||
|
trace->subject_id_reread_reset();
|
||||||
|
|
||||||
|
if (reporter.constructed() && update_report) {
|
||||||
|
bool reset_report = false;
|
||||||
|
|
||||||
|
retry<Genode::Xml_generator::Buffer_exceeded>(env, [&] () {
|
||||||
|
Reporter::Xml_generator xml(*reporter, [&] () {
|
||||||
|
list.for_each([&](auto &session) {
|
||||||
|
reset_report |= session.report_state(xml);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [&] () {
|
||||||
|
report_size += 4096;
|
||||||
|
reporter.construct(env, "components", "components", report_size);
|
||||||
|
reporter->enabled(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reset_report) {
|
||||||
|
list.for_each([](auto &session) {
|
||||||
|
session.reset_report_state();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
update_report = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::construct(Genode::Env &env)
|
||||||
|
{
|
||||||
|
static Cpu::Balancer server(env);
|
||||||
|
}
|
61
repos/os/src/server/cpu_balancer/config.cc
Normal file
61
repos/os/src/server/cpu_balancer/config.cc
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* \brief Config evaluation
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-20
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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/session_label.h>
|
||||||
|
#include <base/thread.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void Cpu::Config::apply(Xml_node const &start, Child_list &sessions)
|
||||||
|
{
|
||||||
|
using Genode::Session_label;
|
||||||
|
using Genode::String;
|
||||||
|
|
||||||
|
typedef String<Session_label::capacity()> Label;
|
||||||
|
|
||||||
|
start.for_each_sub_node("component", [&](Xml_node const &node) {
|
||||||
|
if (!node.has_attribute("label"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Label const label = node.attribute_value("label", Label(""));
|
||||||
|
|
||||||
|
sessions.for_each([&](auto &session) {
|
||||||
|
if (!session.match(label))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node.has_attribute("default_policy")) {
|
||||||
|
Cpu::Policy::Name const policy = node.attribute_value("default_policy", Cpu::Policy::Name());
|
||||||
|
session.default_policy(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.for_each_sub_node("thread", [&](Xml_node const &thread) {
|
||||||
|
if (!thread.has_attribute("name") || !thread.has_attribute("policy"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Thread::Name const name = thread.attribute_value("name", Thread::Name());
|
||||||
|
Cpu::Policy::Name const policy = thread.attribute_value("policy", Cpu::Policy::Name());
|
||||||
|
|
||||||
|
/* explicitly create invalid width/height */
|
||||||
|
/* used during thread construction in policy static case */
|
||||||
|
Affinity::Location location { 0, 0, 0, 0};
|
||||||
|
|
||||||
|
if (thread.has_attribute("xpos") && thread.has_attribute("ypos"))
|
||||||
|
location = Affinity::Location(thread.attribute_value("xpos", 0U),
|
||||||
|
thread.attribute_value("ypos", 0U),
|
||||||
|
1, 1);
|
||||||
|
|
||||||
|
session.config(name, policy, location);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
34
repos/os/src/server/cpu_balancer/config.h
Normal file
34
repos/os/src/server/cpu_balancer/config.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* \brief Config evaluation
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-20
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CONFIG_H_
|
||||||
|
#define _CONFIG_H_
|
||||||
|
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
namespace Cpu {
|
||||||
|
class Config;
|
||||||
|
|
||||||
|
using Genode::Xml_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Cpu::Config {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void apply(Xml_node const &, Child_list &);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _CONFIG_H_ */
|
42
repos/os/src/server/cpu_balancer/config.xsd
Normal file
42
repos/os/src/server/cpu_balancer/config.xsd
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||||
|
|
||||||
|
<xs:include schemaLocation="base_types.xsd"/>
|
||||||
|
|
||||||
|
<xs:simpleType name="Policy">
|
||||||
|
<xs:restriction base="xs:string">
|
||||||
|
<xs:enumeration value="none" />
|
||||||
|
<xs:enumeration value="pin" />
|
||||||
|
<xs:enumeration value="round-robin" />
|
||||||
|
<xs:enumeration value="max-utilize" />
|
||||||
|
</xs:restriction>
|
||||||
|
</xs:simpleType><!-- Policy -->
|
||||||
|
|
||||||
|
<xs:element name="config">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xs:element name="component">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||||
|
<xs:element name="thread">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute name="name" type="xs:string" />
|
||||||
|
<xs:attribute name="policy" type="Policy" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element> <!-- thread -->
|
||||||
|
</xs:choice>
|
||||||
|
<xs:attribute name="default_policy" type="xs:string" />
|
||||||
|
<xs:attribute name="label" type="Session_label" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element> <!-- component -->
|
||||||
|
</xs:choice>
|
||||||
|
|
||||||
|
<xs:attribute name="verbose" type="Boolean" />
|
||||||
|
<xs:attribute name="interval_us" type="xs:positiveInteger" />
|
||||||
|
<xs:attribute name="report" type="Boolean" />
|
||||||
|
<xs:attribute name="trace" type="Boolean" />
|
||||||
|
<xs:attribute name="sleeper" type="Boolean" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element> <!-- config -->
|
||||||
|
|
||||||
|
</xs:schema>
|
315
repos/os/src/server/cpu_balancer/policy.h
Normal file
315
repos/os/src/server/cpu_balancer/policy.h
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* \brief Policy evaluation
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-10-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _POLICY_H_
|
||||||
|
#define _POLICY_H_
|
||||||
|
|
||||||
|
#include <base/affinity.h>
|
||||||
|
#include <base/output.h>
|
||||||
|
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
namespace Cpu
|
||||||
|
{
|
||||||
|
typedef Genode::Affinity::Location Location;
|
||||||
|
typedef Genode::Trace::Execution_time Execution_time;
|
||||||
|
typedef Genode::Cpu_session::Name Name;
|
||||||
|
|
||||||
|
class Policy;
|
||||||
|
class Policy_none;
|
||||||
|
class Policy_pin;
|
||||||
|
class Policy_round_robin;
|
||||||
|
class Policy_max_utilize;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Policy {
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool _update(Location const &base, Location ¤t)
|
||||||
|
{
|
||||||
|
Location now = Location(base.xpos() + location.xpos(),
|
||||||
|
base.ypos() + location.ypos());
|
||||||
|
|
||||||
|
if ((now.xpos() == current.xpos()) && (now.ypos() == current.ypos()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (current.xpos() < base.xpos() || current.ypos() < base.ypos()) {
|
||||||
|
Genode::error("affinity location strange, current below base");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned const xpos = current.xpos() - base.xpos();
|
||||||
|
unsigned const ypos = current.ypos() - base.ypos();
|
||||||
|
|
||||||
|
if (xpos >= base.width() || ypos >= base.height()) {
|
||||||
|
Genode::error("affinity dimension raised");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = Location(xpos, ypos);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef Genode::String<16> Name;
|
||||||
|
|
||||||
|
Location location { };
|
||||||
|
|
||||||
|
virtual ~Policy() { }
|
||||||
|
virtual void config(Location const &) = 0;
|
||||||
|
virtual bool update(Location const &, Location &, Execution_time const &) = 0;
|
||||||
|
virtual void thread_create(Location const &) = 0;
|
||||||
|
virtual bool migrate(Location const &, Location &, Trace *) = 0;
|
||||||
|
|
||||||
|
virtual void print(Genode::Output &output) const = 0;
|
||||||
|
|
||||||
|
virtual bool same_type(Name const &) const = 0;
|
||||||
|
virtual char const * string() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Policy_none : public Cpu::Policy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void config(Location const &) override { };
|
||||||
|
void thread_create(Location const &loc) override { location = loc; }
|
||||||
|
bool migrate(Location const &, Location &, Trace *) override {
|
||||||
|
return false; }
|
||||||
|
|
||||||
|
bool update(Location const &, Location &, Execution_time const &) override {
|
||||||
|
return false; }
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const override {
|
||||||
|
Genode::print(output, "none"); }
|
||||||
|
|
||||||
|
bool same_type(Name const &name) const override {
|
||||||
|
return name == "none"; }
|
||||||
|
|
||||||
|
char const * string() const override {
|
||||||
|
return "none"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Policy_pin : public Cpu::Policy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void config(Location const &rel) override {
|
||||||
|
location = rel; };
|
||||||
|
|
||||||
|
void thread_create(Location const &loc) override {
|
||||||
|
/* for static case with valid location, don't overwrite config */
|
||||||
|
if (location.width() * location.height() == 0)
|
||||||
|
location = loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool migrate(Location const &base, Location ¤t, Trace *) override
|
||||||
|
{
|
||||||
|
Location to = Location(base.xpos() + location.xpos(),
|
||||||
|
base.ypos() + location.ypos());
|
||||||
|
|
||||||
|
if ((to.xpos() == current.xpos()) && (to.ypos() == current.ypos()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
current = to;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update(Location const &, Location &, Execution_time const &) override {
|
||||||
|
return false; }
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const override {
|
||||||
|
Genode::print(output, "pin"); }
|
||||||
|
|
||||||
|
bool same_type(Name const &name) const override {
|
||||||
|
return name == "pin"; }
|
||||||
|
|
||||||
|
char const * string() const override {
|
||||||
|
return "pin"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Policy_round_robin : public Cpu::Policy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void config(Location const &) override { };
|
||||||
|
void thread_create(Location const &loc) override { location = loc; }
|
||||||
|
|
||||||
|
bool migrate(Location const &base, Location &out, Trace *) override
|
||||||
|
{
|
||||||
|
int const xpos = (location.xpos() + 1) % base.width();
|
||||||
|
int const step = (xpos <= location.xpos()) ? 1 : 0;
|
||||||
|
int const ypos = (location.ypos() + step) % base.height();
|
||||||
|
|
||||||
|
Location rel { xpos, ypos, 1, 1 };
|
||||||
|
|
||||||
|
out = Location { int(base.xpos() + rel.xpos()),
|
||||||
|
base.ypos() + rel.ypos(), 1, 1 };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update(Location const &base, Location ¤t, Execution_time const &) override {
|
||||||
|
return _update(base, current); }
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const override {
|
||||||
|
Genode::print(output, "round-robin"); }
|
||||||
|
|
||||||
|
bool same_type(Name const &name) const override {
|
||||||
|
return name == "round-robin"; }
|
||||||
|
|
||||||
|
char const * string() const override {
|
||||||
|
return "round-robin"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Policy_max_utilize : public Cpu::Policy
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Execution_time _last { };
|
||||||
|
Execution_time _time { };
|
||||||
|
|
||||||
|
bool _last_valid { false };
|
||||||
|
bool _time_valid { false };
|
||||||
|
|
||||||
|
Execution_time _last_utilization() const
|
||||||
|
{
|
||||||
|
using Genode::uint64_t;
|
||||||
|
|
||||||
|
uint64_t ec = (_last.thread_context < _time.thread_context) ?
|
||||||
|
_time.thread_context - _last.thread_context : 0;
|
||||||
|
uint64_t sc = (_last.scheduling_context < _time.scheduling_context) ?
|
||||||
|
_time.scheduling_context - _last.scheduling_context : 0;
|
||||||
|
return Execution_time(ec, sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void config(Location const &) override { };
|
||||||
|
void thread_create(Location const &loc) override { location = loc; }
|
||||||
|
|
||||||
|
bool update(Location const &base, Location ¤t, Execution_time const &time) override {
|
||||||
|
_last = _time;
|
||||||
|
_last_valid = _time_valid;
|
||||||
|
|
||||||
|
_time = time;
|
||||||
|
_time_valid = true;
|
||||||
|
|
||||||
|
return _update(base, current); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool _migrate(T const current_idle, T const thread_time,
|
||||||
|
T const remote_idle, Location const ¤t,
|
||||||
|
Location const &to, T const max_idle)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* T - thread
|
||||||
|
* It - idle time on CPU T is on
|
||||||
|
* Ir - idle time on remote CPU with was most idle
|
||||||
|
*
|
||||||
|
* T % | It % | Ir % | desired behaviour
|
||||||
|
* ------------------------|-----------------------------------------------------------------
|
||||||
|
* A: x | ~0 | z | no migration - on current CPU is idle time
|
||||||
|
* B: x | y | z | migrate if z > x -- check whether remote CPU could handle this extra utilization
|
||||||
|
* C:
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool case_a = current_idle > 1000; /* XXX which threshold ? */
|
||||||
|
bool case_b = thread_time > remote_idle;
|
||||||
|
|
||||||
|
if (false)
|
||||||
|
Genode::log("at ", current.xpos(), "x", current.ypos(),
|
||||||
|
" idle=", current_idle,
|
||||||
|
" last=", thread_time,
|
||||||
|
", at ", to.xpos(), "x", to.ypos(),
|
||||||
|
" most_idle=", remote_idle,
|
||||||
|
" (max_idle=", max_idle, ")",
|
||||||
|
" case_b=", case_b);
|
||||||
|
|
||||||
|
if (case_a)
|
||||||
|
return false;
|
||||||
|
if (case_b)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool migrate(Location const &base, Location ¤t, Trace * trace) override
|
||||||
|
{
|
||||||
|
if (!trace || !_last_valid || !_time_valid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Execution_time most_idle { 0UL, 0UL };
|
||||||
|
Execution_time current_idle { 0UL, 0UL };
|
||||||
|
Location to { current }; /* in case of no idle info */
|
||||||
|
|
||||||
|
for (unsigned x = base.xpos(); x < base.xpos() + base.width(); x++) {
|
||||||
|
for (unsigned y = base.ypos(); y < base.ypos() + base.height(); y++) {
|
||||||
|
|
||||||
|
Location const loc(x, y);
|
||||||
|
Execution_time const idle = trace->diff_idle_times(loc);
|
||||||
|
|
||||||
|
if (idle.scheduling_context) {
|
||||||
|
if (idle.scheduling_context > most_idle.scheduling_context) {
|
||||||
|
most_idle = idle;
|
||||||
|
to = loc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (idle.thread_context > most_idle.thread_context) {
|
||||||
|
most_idle = idle;
|
||||||
|
to = loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((loc.xpos() == current.xpos()) && (loc.ypos() == current.ypos()))
|
||||||
|
current_idle = idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((to.xpos() == current.xpos()) && (to.ypos() == current.ypos()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Execution_time const last_util = _last_utilization();
|
||||||
|
|
||||||
|
/* heuristics to not migrate and better stay on same CPU */
|
||||||
|
if (last_util.scheduling_context && !last_util.thread_context) {
|
||||||
|
if (!_migrate(current_idle.scheduling_context, last_util.scheduling_context,
|
||||||
|
most_idle.scheduling_context, current, to,
|
||||||
|
trace->read_max_idle(current).scheduling_context))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!_migrate(current_idle.thread_context, last_util.thread_context,
|
||||||
|
most_idle.thread_context, current, to,
|
||||||
|
trace->read_max_idle(current).thread_context))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = to;
|
||||||
|
_last_valid = false;
|
||||||
|
_time_valid = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(Genode::Output &output) const override {
|
||||||
|
Genode::print(output, "max-utilize"); }
|
||||||
|
|
||||||
|
bool same_type(Name const &name) const override {
|
||||||
|
return name == "max-utilize"; }
|
||||||
|
|
||||||
|
char const * string() const override {
|
||||||
|
return "max-utilize"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
107
repos/os/src/server/cpu_balancer/schedule.cc
Normal file
107
repos/os/src/server/cpu_balancer/schedule.cc
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* \brief Taking migrate decision depending on policy
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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 <cpu_thread/client.h>
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
void Cpu::Session::update_threads(Trace &trace, Session_label const &cpu_balancer)
|
||||||
|
{
|
||||||
|
apply([&](Thread_capability const &cap,
|
||||||
|
Name const &name,
|
||||||
|
Subject_id &subject_id,
|
||||||
|
Cpu::Policy &policy, bool)
|
||||||
|
{
|
||||||
|
if (!subject_id.id || trace.subject_id_reread()) {
|
||||||
|
Session_label const label(cpu_balancer, " -> ", _label);
|
||||||
|
subject_id = trace.lookup_missing_id(label, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subject_id.id) {
|
||||||
|
Genode::error("subject id ", name, " missing - still !!!!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Affinity::Location const &base = _affinity.location();
|
||||||
|
Affinity::Location current { base.xpos() + policy.location.xpos(),
|
||||||
|
base.ypos() + policy.location.ypos(), 1, 1 };
|
||||||
|
Execution_time time { };
|
||||||
|
|
||||||
|
/* request execution time and current location */
|
||||||
|
try {
|
||||||
|
trace.retrieve(subject_id.id, [&] (Execution_time const time_current,
|
||||||
|
Affinity::Location const current_loc)
|
||||||
|
{
|
||||||
|
current = current_loc;
|
||||||
|
time = time_current;
|
||||||
|
|
||||||
|
if (_verbose)
|
||||||
|
log("[", _label, "] name='", name, "' at ",
|
||||||
|
current_loc.xpos(), "x", current_loc.ypos(),
|
||||||
|
" has ec/sc time ", time.thread_context, "/",
|
||||||
|
time.scheduling_context);
|
||||||
|
});
|
||||||
|
} catch (Genode::Trace::Nonexistent_subject) {
|
||||||
|
/* how could that be ? */
|
||||||
|
error("[", _label, "] name='", name, "'"
|
||||||
|
" subject id invalid ?? ", subject_id.id);
|
||||||
|
|
||||||
|
subject_id = Subject_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update current location of thread if changed */
|
||||||
|
if (policy.update(base, current, time))
|
||||||
|
_report = true;
|
||||||
|
|
||||||
|
Affinity::Location migrate_to = current;
|
||||||
|
if (policy.migrate(_affinity.location(), migrate_to, &trace)) {
|
||||||
|
if (_verbose)
|
||||||
|
log("[", _label, "] name='", name, "' request to",
|
||||||
|
" migrate from ", current.xpos(), "x", current.ypos(),
|
||||||
|
" to most idle CPU at ",
|
||||||
|
migrate_to.xpos(), "x", migrate_to.ypos());
|
||||||
|
|
||||||
|
Cpu_thread_client thread(cap);
|
||||||
|
thread.affinity(migrate_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cpu::Session::update_threads()
|
||||||
|
{
|
||||||
|
apply([&](Thread_capability const &cap,
|
||||||
|
Name const &,
|
||||||
|
Subject_id &,
|
||||||
|
Cpu::Policy &policy, bool)
|
||||||
|
{
|
||||||
|
Affinity::Location const &base = _affinity.location();
|
||||||
|
Location current = Location(base.xpos() + policy.location.xpos(),
|
||||||
|
base.ypos() + policy.location.xpos(),
|
||||||
|
1, 1);
|
||||||
|
|
||||||
|
if (!cap.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Affinity::Location migrate_to = current;
|
||||||
|
if (!policy.migrate(_affinity.location(), migrate_to, nullptr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Cpu_thread_client thread(cap);
|
||||||
|
thread.affinity(migrate_to);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
229
repos/os/src/server/cpu_balancer/session.cc
Normal file
229
repos/os/src/server/cpu_balancer/session.cc
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* \brief CPU session implementation
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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 "session.h"
|
||||||
|
|
||||||
|
using namespace Genode;
|
||||||
|
using namespace Cpu;
|
||||||
|
|
||||||
|
Thread_capability
|
||||||
|
Cpu::Session::create_thread(Pd_session_capability const pd,
|
||||||
|
Name const &name_by_client,
|
||||||
|
Affinity::Location const location,
|
||||||
|
Weight const weight,
|
||||||
|
addr_t const utcb)
|
||||||
|
{
|
||||||
|
Thread_capability cap { };
|
||||||
|
|
||||||
|
Name name = name_by_client;
|
||||||
|
if (!name.valid())
|
||||||
|
name = Name("nobody");
|
||||||
|
|
||||||
|
/* quirk since init can't handle Out_of_* during first create_thread call */
|
||||||
|
if ((_reclaim_ram.value || _reclaim_cap.value) && _one_valid_thread()) {
|
||||||
|
if (_reclaim_ram.value)
|
||||||
|
throw Out_of_ram();
|
||||||
|
if (_reclaim_cap.value)
|
||||||
|
throw Out_of_caps();
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup(name, [&](Thread_capability &store_cap,
|
||||||
|
Cpu::Policy &policy)
|
||||||
|
{
|
||||||
|
if (store_cap.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cap = _parent.create_thread(pd, name, location, weight, utcb);
|
||||||
|
if (!cap.valid())
|
||||||
|
/* stop creation attempt by saying done */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* policy and name are set beforehand */
|
||||||
|
store_cap = cap;
|
||||||
|
|
||||||
|
/* for static case with valid location, don't overwrite config */
|
||||||
|
policy.thread_create(location);
|
||||||
|
|
||||||
|
if (_verbose)
|
||||||
|
log("[", _label, "] new thread at ",
|
||||||
|
policy.location.xpos(), "x", policy.location.ypos(),
|
||||||
|
", policy=", policy, ", name='", name, "'");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cap.valid()) {
|
||||||
|
_report = true;
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
cap = _parent.create_thread(pd, name, location, weight, utcb);
|
||||||
|
if (!cap.valid())
|
||||||
|
return cap;
|
||||||
|
|
||||||
|
/* unknown thread without any configuration */
|
||||||
|
construct(_default_policy, [&](Thread_capability &store_cap,
|
||||||
|
Name &store_name, Cpu::Policy &policy)
|
||||||
|
{
|
||||||
|
policy.location = location;
|
||||||
|
|
||||||
|
store_cap = cap;
|
||||||
|
store_name = name;
|
||||||
|
|
||||||
|
if (_verbose)
|
||||||
|
log("[", _label, "] new thread at ",
|
||||||
|
location.xpos(), "x", location.ypos(),
|
||||||
|
", no policy defined",
|
||||||
|
", name='", name, "'");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cap.valid())
|
||||||
|
_report = true;
|
||||||
|
|
||||||
|
return cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Cpu::Session::kill_thread(Thread_capability const thread_cap)
|
||||||
|
{
|
||||||
|
if (!thread_cap.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
kill(thread_cap, [&](Thread_capability &cap, Thread::Name &name,
|
||||||
|
Subject_id &, Cpu::Policy &)
|
||||||
|
{
|
||||||
|
cap = Thread_capability();
|
||||||
|
name = Thread::Name();
|
||||||
|
|
||||||
|
_parent.kill_thread(thread_cap);
|
||||||
|
|
||||||
|
_report = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cpu::Session::exception_sigh(Signal_context_capability const h)
|
||||||
|
{
|
||||||
|
_parent.exception_sigh(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
Affinity::Space Cpu::Session::affinity_space() const
|
||||||
|
{
|
||||||
|
return _parent.affinity_space();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dataspace_capability Cpu::Session::trace_control()
|
||||||
|
{
|
||||||
|
return _parent.trace_control();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cpu::Session::Session(Env &env,
|
||||||
|
Affinity const &affinity,
|
||||||
|
char const * args,
|
||||||
|
Child_list &list, bool const verbose)
|
||||||
|
:
|
||||||
|
_list(list),
|
||||||
|
_env(env),
|
||||||
|
_ram_guard(ram_quota_from_args(args)),
|
||||||
|
_cap_guard(cap_quota_from_args(args)),
|
||||||
|
_parent(_env.session<Cpu_session>(_id.id(), _withdraw_quota(args), affinity)),
|
||||||
|
_label(session_label_from_args(args)),
|
||||||
|
_affinity(affinity.space().total() ? affinity : Affinity(Affinity::Space(1,1), Affinity::Location(0,0,1,1))),
|
||||||
|
_verbose(verbose)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_env.ep().rpc_ep().manage(this);
|
||||||
|
} catch (...) {
|
||||||
|
env.close(_id.id());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cpu::Session::~Session()
|
||||||
|
{
|
||||||
|
/* _threads don't need to be cleaned up, but cause warnings */
|
||||||
|
|
||||||
|
_env.ep().rpc_ep().dissolve(this);
|
||||||
|
|
||||||
|
_env.close(_id.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Cpu::Session::ref_account(Cpu_session_capability const cap)
|
||||||
|
{
|
||||||
|
return _parent.ref_account(cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Cpu::Session::transfer_quota(Cpu_session_capability const cap,
|
||||||
|
size_t const size)
|
||||||
|
{
|
||||||
|
return _parent.transfer_quota(cap, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cpu_session::Quota Cpu::Session::quota()
|
||||||
|
{
|
||||||
|
return _parent.quota();
|
||||||
|
}
|
||||||
|
|
||||||
|
Capability<Cpu_session::Native_cpu> Cpu::Session::native_cpu()
|
||||||
|
{
|
||||||
|
return _parent.native_cpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cpu::Session::report_state(Xml_generator &xml) const
|
||||||
|
{
|
||||||
|
xml.node("component", [&] () {
|
||||||
|
xml.attribute("xpos", _affinity.location().xpos());
|
||||||
|
xml.attribute("ypos", _affinity.location().ypos());
|
||||||
|
xml.attribute("width", _affinity.location().width());
|
||||||
|
xml.attribute("height", _affinity.location().height());
|
||||||
|
xml.attribute("label", _label);
|
||||||
|
|
||||||
|
xml.attribute("default_policy", _default_policy);
|
||||||
|
|
||||||
|
apply([&](Thread_capability const &,
|
||||||
|
Thread::Name const &name,
|
||||||
|
Subject_id const &, Cpu::Policy const &policy,
|
||||||
|
bool const enforced_policy)
|
||||||
|
{
|
||||||
|
xml.node("thread", [&] () {
|
||||||
|
xml.attribute("xpos", policy.location.xpos());
|
||||||
|
xml.attribute("ypos", policy.location.ypos());
|
||||||
|
xml.attribute("name", name);
|
||||||
|
xml.attribute("policy", policy.string());
|
||||||
|
if (enforced_policy)
|
||||||
|
xml.attribute("enforced", enforced_policy);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return _report;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cpu::Session::config(Thread::Name const &thread,
|
||||||
|
Cpu::Policy::Name const &policy_name,
|
||||||
|
Affinity::Location const &relativ)
|
||||||
|
{
|
||||||
|
reconstruct(policy_name, thread, [&](Thread_capability const &,
|
||||||
|
Cpu::Policy &policy)
|
||||||
|
{
|
||||||
|
policy.config(relativ);
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
String<12> const loc { policy.location.xpos(), "x", policy.location.ypos() };
|
||||||
|
log("[", _label, "] name='", thread, "' "
|
||||||
|
"update policy to '", policy, "' ", loc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_report = true;
|
||||||
|
}
|
421
repos/os/src/server/cpu_balancer/session.h
Normal file
421
repos/os/src/server/cpu_balancer/session.h
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
/*
|
||||||
|
* \brief CPU session definition
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SESSION_H_
|
||||||
|
#define _SESSION_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <base/env.h>
|
||||||
|
#include <base/heap.h>
|
||||||
|
#include <base/registry.h>
|
||||||
|
#include <base/rpc_server.h>
|
||||||
|
#include <base/trace/types.h>
|
||||||
|
#include <cpu_session/client.h>
|
||||||
|
#include <os/reporter.h>
|
||||||
|
|
||||||
|
#include "policy.h"
|
||||||
|
|
||||||
|
namespace Cpu {
|
||||||
|
using namespace Genode;
|
||||||
|
|
||||||
|
using Genode::Trace::Subject_id;
|
||||||
|
|
||||||
|
class Session;
|
||||||
|
class Trace;
|
||||||
|
class Policy;
|
||||||
|
struct Thread_client;
|
||||||
|
typedef Id_space<Parent::Client>::Element Client_id;
|
||||||
|
typedef Registry<Registered<Session> > Child_list;
|
||||||
|
typedef Registry<Registered<Thread_client> > Thread_list;
|
||||||
|
typedef Constrained_ram_allocator Ram_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Cpu::Thread_client : Interface
|
||||||
|
{
|
||||||
|
Thread_capability _cap { };
|
||||||
|
Genode::Thread::Name _name { };
|
||||||
|
Subject_id _id { };
|
||||||
|
Cpu::Policy * _policy { nullptr };
|
||||||
|
bool _fix { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Session : public Rpc_object<Cpu_session>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Child_list &_list;
|
||||||
|
Env &_env;
|
||||||
|
|
||||||
|
Ram_quota_guard _ram_guard;
|
||||||
|
Cap_quota_guard _cap_guard;
|
||||||
|
Ram_allocator _ram { _env.pd(), _ram_guard, _cap_guard };
|
||||||
|
Heap _md_alloc { _ram, _env.rm() };
|
||||||
|
|
||||||
|
Ram_quota _reclaim_ram { 0 };
|
||||||
|
Cap_quota _reclaim_cap { 0 };
|
||||||
|
|
||||||
|
Parent::Client _parent_client { };
|
||||||
|
Client_id const _id { _parent_client,
|
||||||
|
_env.id_space() };
|
||||||
|
Cpu_session_client _parent;
|
||||||
|
Cpu::Policy::Name _default_policy { "none" };
|
||||||
|
|
||||||
|
Session::Label const _label;
|
||||||
|
Affinity const _affinity;
|
||||||
|
|
||||||
|
Thread_list _threads { };
|
||||||
|
|
||||||
|
bool _report { true };
|
||||||
|
bool _verbose;
|
||||||
|
bool _by_us { false };
|
||||||
|
|
||||||
|
void construct_policy(Thread::Name const &name, Cpu::Policy **policy,
|
||||||
|
Affinity::Location const loc)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (name == "pin")
|
||||||
|
*policy = new (_md_alloc) Policy_pin();
|
||||||
|
else if (name == "round-robin")
|
||||||
|
*policy = new (_md_alloc) Policy_round_robin();
|
||||||
|
else if (name == "max-utilize")
|
||||||
|
*policy = new (_md_alloc) Policy_max_utilize();
|
||||||
|
else
|
||||||
|
*policy = new (_md_alloc) Policy_none();
|
||||||
|
|
||||||
|
(*policy)->location = loc;
|
||||||
|
} catch (Out_of_ram) { _by_us = true; throw;
|
||||||
|
} catch (Out_of_caps) { _by_us = true; throw;
|
||||||
|
} catch (Insufficient_ram_quota) { _by_us = true; throw;
|
||||||
|
} catch (Insufficient_cap_quota) { _by_us = true; throw; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _one_valid_thread() const
|
||||||
|
{
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
_threads.for_each([&](auto &thread) {
|
||||||
|
if (valid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (thread._cap.valid())
|
||||||
|
valid = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void _for_each_thread(FUNC const &fn)
|
||||||
|
{
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
_threads.for_each([&](auto &thread) {
|
||||||
|
if (done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
done = fn(thread);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void _for_each_thread(FUNC const &fn) const
|
||||||
|
{
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
_threads.for_each([&](auto const &thread) {
|
||||||
|
if (done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
done = fn(thread);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void kill(Thread_capability const &cap, FUNC const &fn)
|
||||||
|
{
|
||||||
|
_for_each_thread([&](Thread_client &thread) {
|
||||||
|
if (!(thread._cap.valid()) || !(thread._cap == cap))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fn(thread._cap, thread._name, thread._id, *thread._policy);
|
||||||
|
|
||||||
|
destroy(_md_alloc, thread._policy);
|
||||||
|
destroy(_md_alloc, &thread);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void apply(FUNC const &fn)
|
||||||
|
{
|
||||||
|
_for_each_thread([&](Thread_client &thread) {
|
||||||
|
if (!thread._cap.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return fn(thread._cap, thread._name, thread._id,
|
||||||
|
*thread._policy, thread._fix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void apply(FUNC const &fn) const
|
||||||
|
{
|
||||||
|
_for_each_thread([&](Thread_client const &thread) {
|
||||||
|
if (!thread._cap.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return fn(thread._cap, thread._name,
|
||||||
|
thread._id, *thread._policy, thread._fix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void lookup(Thread::Name const &name, FUNC const &fn)
|
||||||
|
{
|
||||||
|
if (!name.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_for_each_thread([&](Thread_client &thread) {
|
||||||
|
if (thread._name != name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return fn(thread._cap, *thread._policy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void reconstruct(Cpu::Policy::Name const &policy_name,
|
||||||
|
Thread::Name const &thread_name,
|
||||||
|
FUNC const &fn)
|
||||||
|
{
|
||||||
|
if (!thread_name.valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
_for_each_thread([&](Thread_client &thread) {
|
||||||
|
if (thread._name != thread_name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (thread._fix) {
|
||||||
|
done = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!thread._policy->same_type(policy_name)) {
|
||||||
|
Cpu::Policy * new_policy = nullptr;
|
||||||
|
construct_policy(policy_name, &new_policy, thread._policy->location);
|
||||||
|
|
||||||
|
/* construct policy may throw, so we keep old policy up to here */
|
||||||
|
destroy(_md_alloc, thread._policy);
|
||||||
|
thread._policy = new_policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(thread._cap, *thread._policy);
|
||||||
|
done = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
construct(policy_name, [&](Thread_capability const &cap,
|
||||||
|
Thread::Name &store_name,
|
||||||
|
Cpu::Policy &policy) {
|
||||||
|
store_name = thread_name;
|
||||||
|
fn(cap, policy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void construct(Cpu::Policy::Name const &policy_name, FUNC const &fn)
|
||||||
|
{
|
||||||
|
Thread_client * thread = nullptr;
|
||||||
|
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
thread = new (_md_alloc) Registered<Thread_client>(_threads);
|
||||||
|
|
||||||
|
construct_policy(policy_name, &thread->_policy, Affinity::Location());
|
||||||
|
|
||||||
|
fn(thread->_cap, thread->_name, *thread->_policy);
|
||||||
|
|
||||||
|
/* XXX - heuristic */
|
||||||
|
thread->_fix = (thread->_name == _label.last_element()) ||
|
||||||
|
(thread->_name == "ep") ||
|
||||||
|
(thread->_name == "signal_proxy") ||
|
||||||
|
(thread->_name == "root");
|
||||||
|
|
||||||
|
if (thread->_fix) {
|
||||||
|
if (thread->_policy) {
|
||||||
|
destroy(_md_alloc, thread->_policy);
|
||||||
|
thread->_policy = nullptr;
|
||||||
|
}
|
||||||
|
construct_policy("none", &thread->_policy, Affinity::Location());
|
||||||
|
}
|
||||||
|
} catch (Out_of_ram) { _by_us = true; throw;
|
||||||
|
} catch (Out_of_caps) { _by_us = true; throw;
|
||||||
|
} catch (Insufficient_ram_quota) { _by_us = true; throw;
|
||||||
|
} catch (Insufficient_cap_quota) { _by_us = true; throw; }
|
||||||
|
} catch (...) {
|
||||||
|
if (thread) {
|
||||||
|
if (thread->_policy)
|
||||||
|
destroy(_md_alloc, thread->_policy);
|
||||||
|
|
||||||
|
destroy(_md_alloc, thread);
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char const * _withdraw_quota(char const * args)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Sandbox library can't handle the case of insufficient ram/cap
|
||||||
|
* exception during session creation nor during first
|
||||||
|
* create_thread RPC XXX
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char argbuf[Parent::Session_args::MAX_SIZE];
|
||||||
|
copy_cstring(argbuf, args, sizeof(argbuf));
|
||||||
|
|
||||||
|
size_t extra_ram = (_ram_guard.avail().value < 24 * 1024) ? 24 * 1024 - _ram_guard.avail().value : 0;
|
||||||
|
|
||||||
|
Ram_quota ram { _ram_guard.avail().value + extra_ram };
|
||||||
|
Cap_quota cap { _cap_guard.avail().value };
|
||||||
|
|
||||||
|
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", ram.value);
|
||||||
|
Arg_string::set_arg(argbuf, sizeof(argbuf), "cap_quota", cap.value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
_ram_guard.withdraw(ram);
|
||||||
|
_cap_guard.withdraw(cap);
|
||||||
|
*/
|
||||||
|
|
||||||
|
_reclaim_ram.value += ram.value;
|
||||||
|
_reclaim_cap.value += cap.value;
|
||||||
|
|
||||||
|
return argbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Noncopyable
|
||||||
|
*/
|
||||||
|
Session(Session const &);
|
||||||
|
Session &operator = (Session const &);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Session(Env &, Affinity const &, char const *, Child_list &, bool);
|
||||||
|
~Session();
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
** CPU session interface **
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
Thread_capability create_thread(Pd_session_capability,
|
||||||
|
Thread::Name const &,
|
||||||
|
Affinity::Location, Weight,
|
||||||
|
addr_t) override;
|
||||||
|
void kill_thread(Thread_capability) override;
|
||||||
|
void exception_sigh(Signal_context_capability) override;
|
||||||
|
Affinity::Space affinity_space() const override;
|
||||||
|
Dataspace_capability trace_control() override;
|
||||||
|
int ref_account(Cpu_session_capability) override;
|
||||||
|
int transfer_quota(Cpu_session_capability, size_t) override;
|
||||||
|
Quota quota() override;
|
||||||
|
Capability<Cpu_session::Native_cpu> native_cpu() override;
|
||||||
|
|
||||||
|
/************************
|
||||||
|
** internal interface **
|
||||||
|
************************/
|
||||||
|
bool match(Label const &label) const { return _label == label; };
|
||||||
|
void config(Thread::Name const &, Cpu::Policy::Name const &,
|
||||||
|
Affinity::Location const &);
|
||||||
|
void update_threads();
|
||||||
|
void update_threads(Trace &, Session_label const &);
|
||||||
|
bool report_state(Xml_generator &) const;
|
||||||
|
void reset_report_state() { _report = false; }
|
||||||
|
bool report_update() const { return _report; }
|
||||||
|
|
||||||
|
void default_policy(Cpu::Policy::Name const &policy)
|
||||||
|
{
|
||||||
|
if (policy != _default_policy)
|
||||||
|
_report = true;
|
||||||
|
_default_policy = policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void upgrade(Root::Upgrade_args const &args, FUNC const &fn)
|
||||||
|
{
|
||||||
|
Ram_quota ram_args = ram_quota_from_args(args.string());
|
||||||
|
Cap_quota cap_args = cap_quota_from_args(args.string());
|
||||||
|
|
||||||
|
bool recreate_args = false;
|
||||||
|
|
||||||
|
if (_reclaim_ram.value) {
|
||||||
|
Ram_quota remove { min(_reclaim_ram.value, ram_args.value) };
|
||||||
|
|
||||||
|
_reclaim_ram.value -= remove.value;
|
||||||
|
ram_args.value -= remove.value;
|
||||||
|
recreate_args = true;
|
||||||
|
|
||||||
|
if (remove.value > _ram_guard.avail().value)
|
||||||
|
_ram_guard.upgrade(Ram_quota{remove.value - _ram_guard.avail().value});
|
||||||
|
_ram_guard.withdraw(remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_reclaim_cap.value) {
|
||||||
|
Cap_quota remove { min(_reclaim_cap.value, cap_args.value) };
|
||||||
|
|
||||||
|
_reclaim_cap.value -= remove.value;
|
||||||
|
cap_args.value -= remove.value;
|
||||||
|
recreate_args = true;
|
||||||
|
|
||||||
|
if (remove.value > _cap_guard.avail().value)
|
||||||
|
_cap_guard.upgrade(Cap_quota{remove.value - _cap_guard.avail().value});
|
||||||
|
_cap_guard.withdraw(remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ram_guard.upgrade(ram_args);
|
||||||
|
_cap_guard.upgrade(cap_args);
|
||||||
|
|
||||||
|
/* request originated by us */
|
||||||
|
if (_by_us) {
|
||||||
|
_by_us = false;
|
||||||
|
/* due to upgrade ram/cap the next call should succeed */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* track how many resources we donated to parent done by fn() call */
|
||||||
|
_ram_guard.withdraw(ram_args);
|
||||||
|
_cap_guard.withdraw(cap_args);
|
||||||
|
|
||||||
|
/* rewrite args if we removed some for reclaim quirk */
|
||||||
|
if (recreate_args) {
|
||||||
|
if (ram_args.value || cap_args.value) {
|
||||||
|
char argbuf[Root::Upgrade_args::MAX_SIZE];
|
||||||
|
copy_cstring(argbuf, args.string(), sizeof(argbuf));
|
||||||
|
|
||||||
|
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", ram_args.value);
|
||||||
|
Arg_string::set_arg(argbuf, sizeof(argbuf), "cap_quota", cap_args.value);
|
||||||
|
|
||||||
|
fn(_id.id(), argbuf);
|
||||||
|
} /* else no upgrade to parent, we consumed all */
|
||||||
|
} else
|
||||||
|
fn(_id.id(), args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _SESSION_H_ */
|
5
repos/os/src/server/cpu_balancer/target.mk
Normal file
5
repos/os/src/server/cpu_balancer/target.mk
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
TARGET = cpu_balancer
|
||||||
|
SRC_CC = component.cc session.cc config.cc trace.cc schedule.cc
|
||||||
|
LIBS = base
|
||||||
|
|
||||||
|
CONFIG_XSD = config.xsd
|
182
repos/os/src/server/cpu_balancer/trace.cc
Normal file
182
repos/os/src/server/cpu_balancer/trace.cc
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* \brief Data from Trace session,
|
||||||
|
* e.g. CPU idle times && thread execution time
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-22
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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 "trace.h"
|
||||||
|
|
||||||
|
void Cpu::Trace::_read_idle_times(bool skip_max_idle)
|
||||||
|
{
|
||||||
|
if (!_trace.constructed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_idle_slot = (_idle_slot + 1) % HISTORY;
|
||||||
|
|
||||||
|
for (unsigned x = 0; x < _space.width(); x++) {
|
||||||
|
for (unsigned y = 0; y < _space.height(); y++) {
|
||||||
|
Affinity::Location const idle_location(x,y);
|
||||||
|
Subject_id const &subject_id = _idle_id[x][y];
|
||||||
|
|
||||||
|
if (!subject_id.id || _subject_id_reread)
|
||||||
|
_lookup_missing_idle_id(idle_location);
|
||||||
|
|
||||||
|
if (!subject_id.id) {
|
||||||
|
_idle_times[x][y][_idle_slot] = Execution_time(0, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Subject_info const info = _trace->subject_info(subject_id);
|
||||||
|
|
||||||
|
Affinity::Location location = info.affinity();
|
||||||
|
|
||||||
|
if (location.xpos() != int(x) || location.ypos() != int(y)) {
|
||||||
|
Subject_id const subject_id_old = subject_id;
|
||||||
|
|
||||||
|
_lookup_missing_idle_id(idle_location);
|
||||||
|
|
||||||
|
Genode::warning("idle location mismatch ", x, "x", y, " vs ",
|
||||||
|
location.xpos(), "x", location.ypos(), " subject id=",
|
||||||
|
subject_id_old.id, " vs ", _idle_id[x][y].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
_idle_times[x][y][_idle_slot] = info.execution_time();
|
||||||
|
|
||||||
|
if (skip_max_idle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* determine max available execution time by monitoring idle */
|
||||||
|
auto const time = diff_idle_times(idle_location);
|
||||||
|
auto &max = _idle_max[x][y];
|
||||||
|
if (time.thread_context > max.thread_context ||
|
||||||
|
time.scheduling_context > max.scheduling_context)
|
||||||
|
max = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cpu::Trace::_lookup_missing_idle_id(Affinity::Location const &location)
|
||||||
|
{
|
||||||
|
Subject_id found_id { };
|
||||||
|
|
||||||
|
do {
|
||||||
|
auto count = _trace->for_each_subject_info([&](Subject_id const &id,
|
||||||
|
Subject_info const &info)
|
||||||
|
{
|
||||||
|
if (found_id.id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (info.affinity().xpos() != location.xpos() ||
|
||||||
|
info.affinity().ypos() != location.ypos())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (info.session_label() != "kernel" || info.thread_name() != "idle")
|
||||||
|
return;
|
||||||
|
|
||||||
|
_idle_id[location.xpos()][location.ypos()] = id;
|
||||||
|
found_id = id;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (count.count == count.limit) {
|
||||||
|
Genode::log("reconstruct trace session, subject_count=", count.count);
|
||||||
|
_reconstruct();
|
||||||
|
found_id = Subject_id();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_id.id) {
|
||||||
|
Genode::error("idle trace id missing");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!found_id.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::Trace::Subject_id
|
||||||
|
Cpu::Trace::lookup_missing_id(Session_label const &label,
|
||||||
|
Thread_name const &thread)
|
||||||
|
{
|
||||||
|
Subject_id found_id { };
|
||||||
|
|
||||||
|
do {
|
||||||
|
auto count = _trace->for_each_subject_info([&](Subject_id const &id,
|
||||||
|
Subject_info const &info)
|
||||||
|
{
|
||||||
|
if (found_id.id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (thread != info.thread_name())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (label != info.session_label())
|
||||||
|
return;
|
||||||
|
|
||||||
|
found_id = id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (count.count == count.limit) {
|
||||||
|
Genode::log("reconstruct trace session, subject_count=", count.count);
|
||||||
|
_reconstruct();
|
||||||
|
found_id = Subject_id();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_id.id) {
|
||||||
|
Genode::error("trace id missing");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!found_id.id);
|
||||||
|
|
||||||
|
return found_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Genode::Session_label Cpu::Trace::lookup_my_label()
|
||||||
|
{
|
||||||
|
Subject_id found_id { };
|
||||||
|
Session_label label("cpu_balancer");
|
||||||
|
|
||||||
|
do {
|
||||||
|
auto count = _trace->for_each_subject_info([&](Subject_id const &id,
|
||||||
|
Subject_info const &info)
|
||||||
|
{
|
||||||
|
if (info.thread_name() != label)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Session_label match { info.session_label().prefix(), " -> ", label };
|
||||||
|
if (info.session_label() != match)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (found_id.id)
|
||||||
|
Genode::warning("Multiple CPU balancer are running, "
|
||||||
|
"can't determine myself for sure.");
|
||||||
|
|
||||||
|
found_id = id;
|
||||||
|
label = info.session_label();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (count.count == count.limit) {
|
||||||
|
Genode::log("reconstruct trace session, subject_count=", count.count);
|
||||||
|
found_id = Subject_id();
|
||||||
|
_reconstruct();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found_id.id) {
|
||||||
|
Genode::error("could not lookup my label");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!found_id.id);
|
||||||
|
|
||||||
|
if (found_id.id)
|
||||||
|
warning("My label seems to be: '", label, "'");
|
||||||
|
|
||||||
|
return label;
|
||||||
|
}
|
189
repos/os/src/server/cpu_balancer/trace.h
Normal file
189
repos/os/src/server/cpu_balancer/trace.h
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* \brief Data from Trace session,
|
||||||
|
* e.g. CPU idle times && thread execution time
|
||||||
|
* \author Alexander Boettcher
|
||||||
|
* \date 2020-07-22
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TRACE_H_
|
||||||
|
#define _TRACE_H_
|
||||||
|
|
||||||
|
#include <util/reconstructible.h>
|
||||||
|
#include <trace_session/connection.h>
|
||||||
|
|
||||||
|
namespace Cpu {
|
||||||
|
class Trace;
|
||||||
|
class Sleeper;
|
||||||
|
|
||||||
|
using Genode::Constructible;
|
||||||
|
using Genode::Trace::Subject_id;
|
||||||
|
using Genode::Trace::Subject_info;
|
||||||
|
using Genode::Affinity;
|
||||||
|
using Genode::Session_label;
|
||||||
|
using Genode::Trace::Thread_name;
|
||||||
|
using Genode::Trace::Connection;
|
||||||
|
using Genode::Trace::Execution_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Cpu::Trace
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Genode::Env &_env;
|
||||||
|
Affinity::Space const _space;
|
||||||
|
Constructible<Connection> _trace { };
|
||||||
|
|
||||||
|
Genode::size_t _arg_quota { 12 * 4096 };
|
||||||
|
Genode::size_t _ram_quota { _arg_quota + 4 * 4096 };
|
||||||
|
|
||||||
|
enum { MAX_CORES = 64, MAX_THREADS = 2, HISTORY = 4 };
|
||||||
|
|
||||||
|
Subject_id _idle_id [MAX_CORES][MAX_THREADS];
|
||||||
|
Execution_time _idle_times[MAX_CORES][MAX_THREADS][HISTORY];
|
||||||
|
Execution_time _idle_max [MAX_CORES][MAX_THREADS];
|
||||||
|
unsigned _idle_slot { HISTORY - 1 };
|
||||||
|
|
||||||
|
unsigned _subject_id_reread { 0 };
|
||||||
|
|
||||||
|
void _lookup_missing_idle_id(Affinity::Location const &);
|
||||||
|
|
||||||
|
void _reconstruct(Genode::size_t const upgrade = 4 * 4096)
|
||||||
|
{
|
||||||
|
_ram_quota += upgrade;
|
||||||
|
_arg_quota += upgrade;
|
||||||
|
|
||||||
|
_trace.destruct();
|
||||||
|
_trace.construct(_env, _ram_quota, _arg_quota, 0 /* parent levels */);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Explicitly re-trigger import of subjects. Otherwise
|
||||||
|
* stored trace ids are not valid if used with subject_info(id)
|
||||||
|
* and we get exception thrown about unknown ids.
|
||||||
|
*/
|
||||||
|
_trace->_retry([&] () {
|
||||||
|
_trace->call<Genode::Trace::Session_client::Rpc_subjects>();
|
||||||
|
});
|
||||||
|
|
||||||
|
_subject_id_reread ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Affinity::Space _sanitize_space(Affinity::Space const space)
|
||||||
|
{
|
||||||
|
unsigned width = space.width();
|
||||||
|
unsigned height = space.height();
|
||||||
|
|
||||||
|
if (_space.width() > MAX_CORES)
|
||||||
|
width = MAX_CORES;
|
||||||
|
if (_space.height() > MAX_THREADS)
|
||||||
|
height = MAX_THREADS;
|
||||||
|
|
||||||
|
if (width != space.width() || height != space.height())
|
||||||
|
Genode::error("supported affinity space too small");
|
||||||
|
|
||||||
|
return Affinity::Space(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _read_idle_times(bool);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Trace(Genode::Env &env)
|
||||||
|
: _env(env), _space(_sanitize_space(env.cpu().affinity_space()))
|
||||||
|
{
|
||||||
|
_reconstruct();
|
||||||
|
_read_idle_times(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_idle_times() { _read_idle_times(false); }
|
||||||
|
|
||||||
|
unsigned subject_id_reread() const { return _subject_id_reread; }
|
||||||
|
void subject_id_reread_reset() { _subject_id_reread = 0; }
|
||||||
|
|
||||||
|
Execution_time read_max_idle(Affinity::Location const &location)
|
||||||
|
{
|
||||||
|
unsigned const xpos = location.xpos();
|
||||||
|
unsigned const ypos = location.ypos();
|
||||||
|
|
||||||
|
if (xpos >= MAX_CORES || ypos >= MAX_THREADS)
|
||||||
|
return Execution_time(0, 0);
|
||||||
|
|
||||||
|
return _idle_max[xpos][ypos];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Subject_id lookup_missing_id(Session_label const &,
|
||||||
|
Thread_name const &);
|
||||||
|
|
||||||
|
Session_label lookup_my_label();
|
||||||
|
|
||||||
|
template <typename FUNC>
|
||||||
|
void retrieve(Subject_id const id, FUNC const &fn)
|
||||||
|
{
|
||||||
|
if (!_trace.constructed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Ieegs, XXX, avoid copying whole object */
|
||||||
|
Subject_info info = _trace->subject_info(id);
|
||||||
|
fn(info.execution_time(), info.affinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
Execution_time abs_idle_times(Affinity::Location const &location)
|
||||||
|
{
|
||||||
|
if (location.xpos() >= MAX_CORES || location.ypos() >= MAX_THREADS)
|
||||||
|
return Execution_time(0, 0);
|
||||||
|
return _idle_times[location.xpos()][location.ypos()][_idle_slot];
|
||||||
|
}
|
||||||
|
|
||||||
|
Execution_time diff_idle_times(Affinity::Location const &location)
|
||||||
|
{
|
||||||
|
unsigned const xpos = location.xpos();
|
||||||
|
unsigned const ypos = location.ypos();
|
||||||
|
|
||||||
|
if (xpos >= MAX_CORES || ypos >= MAX_THREADS)
|
||||||
|
return Execution_time(0, 0);
|
||||||
|
|
||||||
|
Execution_time const &prev = _idle_times[xpos][ypos][((_idle_slot == 0) ? unsigned(HISTORY) : _idle_slot) - 1];
|
||||||
|
Execution_time const &curr = _idle_times[location.xpos()][location.ypos()][_idle_slot];
|
||||||
|
|
||||||
|
using Genode::uint64_t;
|
||||||
|
|
||||||
|
uint64_t ec = (prev.thread_context < curr.thread_context) ?
|
||||||
|
curr.thread_context - prev.thread_context : 0;
|
||||||
|
uint64_t sc = (prev.scheduling_context < curr.scheduling_context) ?
|
||||||
|
curr.scheduling_context - prev.scheduling_context : 0;
|
||||||
|
|
||||||
|
/* strange case where idle times are not reported if no threads are on CPU */
|
||||||
|
if (!ec && !sc && curr.thread_context == 0 && curr.scheduling_context == 0)
|
||||||
|
return Execution_time { 1, 1 };
|
||||||
|
|
||||||
|
return Execution_time { ec, sc };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cpu::Sleeper : Genode::Thread
|
||||||
|
{
|
||||||
|
Genode::Env &_env;
|
||||||
|
Genode::Blockade _block { };
|
||||||
|
|
||||||
|
Sleeper(Genode::Env &env, Location const &location)
|
||||||
|
:
|
||||||
|
Genode::Thread(env, Name("sleep_", location.xpos(), "x", location.ypos()),
|
||||||
|
2 * 4096, location, Weight(), env.cpu()),
|
||||||
|
_env(env)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void entry() override
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
_block.block();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif /* _TRACE_H_ */
|
@ -2,6 +2,7 @@ aes_cbc_4k
|
|||||||
bomb
|
bomb
|
||||||
cbe_tester
|
cbe_tester
|
||||||
cpu_bench
|
cpu_bench
|
||||||
|
cpu_balancer
|
||||||
cpu_quota
|
cpu_quota
|
||||||
cpu_sampler
|
cpu_sampler
|
||||||
demo
|
demo
|
||||||
|
Loading…
Reference in New Issue
Block a user