CPU session: apply quota via relative weightings

Physical CPU quota was previously given to a thread on construction only
by directly specifying a percentage of the quota of the according CPU
session. Now, a new thread is given a weighting that can be any value.
The physical counter-value of such a weighting depends on the weightings
of the other threads at the CPU session. Thus, the physical quota of all
threads of a CPU session must be updated when a weighting is added or
removed. This is each time the session creates or destroys a thread.

This commit also adapts the "cpu_quota" test in base-hw accordingly.

Ref #1464
This commit is contained in:
Martin Stein 2015-03-27 14:05:55 +01:00 committed by Christian Helmuth
parent 955977b516
commit c9272937e7
71 changed files with 1230 additions and 483 deletions

View File

@ -55,7 +55,8 @@ void Thread_base::start()
/* create thread at core */
char buf[48];
name(buf, sizeof(buf));
_thread_cap = _cpu_session->create_thread(0, buf);
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
_thread_cap = _cpu_session->create_thread(WEIGHT, buf);
/* assign thread to protection domain */
env()->pd_session()->bind_thread(_thread_cap);

View File

@ -26,3 +26,5 @@ Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_ca
return Ram_dataspace_capability();
}
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -152,6 +152,11 @@ namespace Genode {
***********************/
addr_t utcb() const { return _utcb; }
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota) { /* not supported*/ }
};
}

View File

@ -26,3 +26,5 @@ Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_ca
return Ram_dataspace_capability();
}
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -150,6 +150,11 @@ namespace Genode {
unsigned long pager_object_badge() const {
return convert_native_thread_id_to_badge(_l4_thread_id); }
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota) { /* not supported*/ }
/*******************************
** Fiasco-specific Accessors **

View File

@ -25,8 +25,8 @@ namespace Genode {
explicit Foc_cpu_session_client(Cpu_session_capability session)
: Rpc_client<Foc_cpu_session>(static_cap_cast<Foc_cpu_session>(session)) { }
Thread_capability create_thread(size_t, Name const &name, addr_t utcb = 0) {
return call<Rpc_create_thread>(0, name, utcb); }
Thread_capability create_thread(size_t weight, Name const &name, addr_t utcb = 0) {
return call<Rpc_create_thread>(weight, name, utcb); }
Ram_dataspace_capability utcb(Thread_capability thread) {
return call<Rpc_utcb>(thread); }
@ -100,9 +100,7 @@ namespace Genode {
int transfer_quota(Cpu_session_capability session, size_t amount) {
return call<Rpc_transfer_quota>(session, amount); }
size_t quota() { return call<Rpc_quota>(); }
size_t used() { return call<Rpc_used>(); }
Quota quota() override { return call<Rpc_quota>(); }
};
}

View File

@ -186,7 +186,7 @@ void Thread_base::free_secondary_stack(void* stack_addr)
}
Thread_base::Thread_base(size_t, const char *name, size_t stack_size,
Thread_base::Thread_base(size_t weight, const char *name, size_t stack_size,
Type const type, Cpu_session *cpu_session)
:
_cpu_session(cpu_session),
@ -195,13 +195,13 @@ Thread_base::Thread_base(size_t, const char *name, size_t stack_size,
_join_lock(Lock::LOCKED)
{
strncpy(_context->name, name, sizeof(_context->name));
_init_platform_thread(0, type);
_init_platform_thread(weight, type);
}
Thread_base::Thread_base(size_t, const char *name, size_t stack_size,
Thread_base::Thread_base(size_t weight, const char *name, size_t stack_size,
Type type)
: Thread_base(0, name, stack_size, type, nullptr) { }
: Thread_base(weight, name, stack_size, type, nullptr) { }
Thread_base::~Thread_base()

View File

@ -39,7 +39,7 @@ void Thread_base::_deinit_platform_thread()
}
void Thread_base::_init_platform_thread(size_t, Type type)
void Thread_base::_init_platform_thread(size_t weight, Type type)
{
/* if no cpu session is given, use it from the environment */
if (!_cpu_session)
@ -50,7 +50,7 @@ void Thread_base::_init_platform_thread(size_t, Type type)
/* create thread at core */
char buf[48];
name(buf, sizeof(buf));
_thread_cap = _cpu_session->create_thread(0, buf);
_thread_cap = _cpu_session->create_thread(weight, buf);
/* assign thread to protection domain */
if (!_thread_cap.valid() ||

View File

@ -107,3 +107,6 @@ void Genode::Cpu_session_component::single_step(Genode::Thread_capability thread
Fiasco::l4_thread_ex_regs(tid, ~0UL, ~0UL, flags);
}
Genode::Cpu_session::Quota Genode::Cpu_session_component::quota() { return Quota(); }

View File

@ -64,7 +64,9 @@ namespace Genode {
public:
Cpu_thread_component(size_t, Session_label const &label,
Cpu_thread_component(size_t const weight,
size_t const quota,
Session_label const &label,
Thread_name const &name,
unsigned priority, addr_t utcb,
Signal_context_capability sigh,
@ -88,7 +90,8 @@ namespace Genode {
bool bound() const { return _bound; }
void bound(bool b) { _bound = b; }
Trace::Source *trace_source() { return &_trace_source; }
size_t quota() { return 0; }
size_t weight() const { return Cpu_session::DEFAULT_WEIGHT; }
void sigh(Signal_context_capability sigh)
{
@ -138,24 +141,26 @@ namespace Genode {
session */
Trace::Source_registry &_trace_sources;
Trace::Control_area _trace_control_area;
Cpu_session_component * _ref;
size_t _used;
size_t _weight;
size_t _quota;
Cpu_session_component * _ref;
List<Cpu_session_component> _ref_members;
Lock _ref_members_lock;
size_t _global_to_local(size_t const q) const { return 0; }
size_t _avail() { return 0; }
void _incr_weight(size_t);
void _decr_weight(size_t);
size_t _weight_to_quota(size_t) const;
void _decr_quota(size_t);
void _incr_quota(size_t);
void _update_thread_quota(Cpu_thread_component *) const;
void _update_each_thread_quota();
void _transfer_quota(Cpu_session_component *, size_t);
void _insert_ref_member(Cpu_session_component *) { }
void _unsync_remove_ref_member(Cpu_session_component *) { }
void _remove_ref_member(Cpu_session_component *) { }
void _deinit_ref_account();
void _deinit_threads();
size_t _local_to_global(size_t) const { return 0; }
void _insuff_for_consume(size_t);
int _insuff_for_transfer(size_t);
int _transfer_back(size_t) { return -1; }
int _transfer_forth(Cpu_session_component *, size_t) { return -1; }
void _insert_ref_member(Cpu_session_component *) { }
void _remove_ref_member(Cpu_session_component *) { }
void _unsync_remove_ref_member(Cpu_session_component *) { }
/**
* Exception handler that will be invoked unless overridden by a
@ -224,8 +229,7 @@ namespace Genode {
Dataspace_capability trace_policy(Thread_capability);
int ref_account(Cpu_session_capability c);
int transfer_quota(Cpu_session_capability c, size_t q);
size_t used();
size_t quota();
Quota quota() override;
/***********************************

View File

@ -167,6 +167,11 @@ namespace Genode {
unsigned long pager_object_badge() {
return (unsigned long) _thread.local.dst(); }
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota) { /* not supported*/ }
/*******************************
** Fiasco-specific Accessors **

View File

@ -1,161 +0,0 @@
#
# Build
#
build "core init drivers/timer test/cpu_quota"
#
# Boot image
#
create_boot_directory
install_config {
<config prio_levels="4">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service><parent/><any-child/></any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="10M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="init_1" priority="-1">
<binary name="init"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="10"/>
<config prio_levels="2">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service><parent/><any-child/></any-service>
</default-route>
<!-- should receive 10 % of the CPU time -->
<start name="test_slow" priority="-1">
<binary name="test-cpu_quota"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="50"/>
</start>
</config>
</start>
<start name="init_2" priority="-2">
<binary name="init"/>
<resource name="RAM" quantum="100M"/>
<resource name="CPU" quantum="80"/>
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="CPU"/>
<service name="RM"/>
<service name="CAP"/>
<service name="PD"/>
<service name="LOG"/>
<service name="SIGNAL"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service><parent/></any-service>
</default-route>
<!-- should receive 25 % of the CPU time -->
<start name="test_middle">
<binary name="test-cpu_quota"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="25"/>
</start>
<!-- should receive 65 % of the CPU time -->
<start name="test_fast">
<binary name="test-cpu_quota"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="75"/>
</start>
</config>
</start>
</config>
}
build_boot_image "core init timer test-cpu_quota"
#
# Execution
#
append qemu_args "-nographic -m 64"
run_genode_until "test.*\n.*test.*\n.*test.*\n" 60
#
# Conclusion
#
set slow_opt 0.10
set middle_opt 0.25
set fast_opt 0.65
set err 0.02
regexp {[0-9]+} [regexp -inline {slow.*[0-9]+} $output] slow_cnt
regexp {[0-9]+} [regexp -inline {middle.*[0-9]+} $output] middle_cnt
regexp {[0-9]+} [regexp -inline {fast.*[0-9]+} $output] fast_cnt
set total_cnt [expr $fast_cnt + $middle_cnt + $slow_cnt]
set slow_fac [expr $slow_cnt / double($total_cnt) ]
set middle_fac [expr $middle_cnt / double($total_cnt) ]
set fast_fac [expr $fast_cnt / double($total_cnt) ]
set failed 0
if {[expr $slow_fac > $slow_opt + $err || $slow_fac < $slow_opt - $err]} {
set is_pc [expr round($slow_fac * 10000) / 100]
set opt_pc [expr round($slow_opt * 10000) / 100]
puts stderr "Error: Slow counter received $is_pc% of the CPU time."
puts stderr " Should receive $opt_pc%."
set failed 1
}
if {[expr $middle_fac > $middle_opt + $err || $middle_fac < $middle_opt - $err]} {
set is_pc [expr round($middle_fac * 10000) / 100]
set opt_pc [expr round($middle_opt * 10000) / 100]
puts stderr "Error: Middle counter received $is_pc% of the CPU time."
puts stderr " Should receive $opt_pc%."
set failed 1
}
if {[expr $fast_fac > $fast_opt + $err || $fast_fac < $fast_opt - $err]} {
set is_pc [expr round($fast_fac * 10000) / 100]
set opt_pc [expr round($fast_opt * 10000) / 100]
puts stderr "Error: Fast counter received $is_pc% of the CPU time."
puts stderr " Should receive $opt_pc%."
set failed 1
}
if {$failed} {
exit -1
} else {
puts "Test succeeded"
}

View File

@ -30,7 +30,7 @@ extern Native_thread_id _main_thread_id;
** Thread_base **
*****************/
void Thread_base::_init_platform_thread(size_t quota, Type type)
void Thread_base::_init_platform_thread(size_t weight, Type type)
{
if (!_cpu_session) { _cpu_session = env()->cpu_session(); }
if (type == NORMAL) {
@ -38,7 +38,8 @@ void Thread_base::_init_platform_thread(size_t quota, Type type)
/* create server object */
char buf[48];
name(buf, sizeof(buf));
_thread_cap = _cpu_session->create_thread(quota, buf, (addr_t)&_context->utcb);
addr_t const utcb = (addr_t)&_context->utcb;
_thread_cap = _cpu_session->create_thread(weight, buf, utcb);
return;
}
/* if we got reinitialized we have to get rid of the old UTCB */

View File

@ -16,6 +16,7 @@
/* core includes */
#include <cpu_session_component.h>
#include <kernel/configuration.h>
using namespace Genode;
@ -30,3 +31,10 @@ Cpu_session_component::utcb(Thread_capability thread_cap)
return t->platform_thread()->utcb();
}
Cpu_session::Quota Cpu_session_component::quota()
{
size_t const spu = Kernel::cpu_quota_ms * 1000;
size_t const u = quota_lim_downscale<sizet_arithm_t>(_quota, spu);
return { spu, u };
}

View File

@ -157,6 +157,11 @@ class Kernel::Thread
*/
int _read_reg(addr_t const id, addr_t & value) const;
/**
* Return amount of timer tics that 'quota' is worth
*/
size_t _core_to_kernel_quota(size_t const quota) const;
/**
* Override a thread register
*

View File

@ -75,12 +75,6 @@ namespace Genode {
*/
bool _attaches_utcb_by_itself();
static size_t _generic_to_platform_quota(size_t const q)
{
assert(Kernel::cpu_quota_ms <= Cpu_session::QUOTA_LIMIT);
return (q * Kernel::cpu_quota_ms) >> 15;
}
public:
/**
@ -143,6 +137,11 @@ namespace Genode {
*/
void cancel_blocking() { resume(); }
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota);
/**
* Get raw thread state
*/

View File

@ -29,6 +29,8 @@ namespace Genode
* CPU driver for core
*/
class Arm;
typedef Genode::uint64_t sizet_arithm_t;
}
class Genode::Arm

View File

@ -13,8 +13,8 @@
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CPU_H_
#define _CPU_H_
#ifndef _SPEC__X86__CPU_SUPPORT_H_
#define _SPEC__X86__CPU_SUPPORT_H_
/* Genode includes */
#include <util/register.h>
@ -445,4 +445,4 @@ class Genode::Cpu
};
#endif /* _CPU_H_ */
#endif /* _SPEC__X86__CPU_SUPPORT_H_ */

View File

@ -0,0 +1,22 @@
/*
* \brief CPU driver for core
* \author Martin stein
* \date 2015-04-20
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CPU_H_
#define _CPU_H_
/* core includes */
#include <spec/x86/cpu_support.h>
namespace Genode { typedef __uint128_t sizet_arithm_t; }
#endif /* _CPU_H_ */

View File

@ -46,6 +46,8 @@ extern void * _start_secondary_cpus;
extern int _prog_img_beg;
extern int _prog_img_end;
static_assert(sizeof(Genode::sizet_arithm_t) >= 2 * sizeof(size_t),
"Bad result type for size_t arithmetics.");
namespace Kernel
{

View File

@ -15,6 +15,7 @@
/* Genode includes */
#include <base/thread_state.h>
#include <unmanaged_singleton.h>
#include <cpu_session/cpu_session.h>
/* core includes */
#include <kernel/kernel.h>
@ -224,12 +225,21 @@ void Thread::_call_new_pd()
void Thread::_call_delete_pd() { reinterpret_cast<Pd*>(user_arg_1())->~Pd(); }
size_t Thread::_core_to_kernel_quota(size_t const quota) const
{
using Genode::Cpu_session;
using Genode::sizet_arithm_t;
size_t const tics = cpu_pool()->timer()->ms_to_tics(Kernel::cpu_quota_ms);
return Cpu_session::quota_lim_downscale<sizet_arithm_t>(quota, tics);
}
void Thread::_call_new_thread()
{
/* create new thread */
void * const p = (void *)user_arg_1();
unsigned const priority = user_arg_2();
unsigned const quota = cpu_pool()->timer()->ms_to_tics(user_arg_3());
unsigned const quota = _core_to_kernel_quota(user_arg_3());
char const * const label = (char *)user_arg_4();
Thread * const t = new (p) Thread(priority, quota, label);
user_arg_0(t->id());
@ -239,8 +249,7 @@ void Thread::_call_new_thread()
void Thread::_call_thread_quota()
{
Thread * const thread = (Thread *)user_arg_1();
unsigned const quota = cpu_pool()->timer()->ms_to_tics(user_arg_2());
thread->Cpu_job::quota(quota);
thread->Cpu_job::quota(_core_to_kernel_quota(user_arg_2()));
}

View File

@ -78,6 +78,10 @@ Platform_thread::~Platform_thread()
}
void Platform_thread::quota(size_t const quota) {
Kernel::thread_quota((Kernel::Thread *)_kernel_thread, quota); }
Platform_thread::Platform_thread(const char * const label,
Native_utcb * utcb)
: _pd(Kernel::core_pd()->platform_pd()),
@ -110,7 +114,8 @@ Platform_thread::Platform_thread(const char * const label,
}
Platform_thread::Platform_thread(size_t quota, const char * const label,
Platform_thread::Platform_thread(size_t const quota,
const char * const label,
unsigned const virt_prio,
addr_t const utcb)
:
@ -139,7 +144,6 @@ Platform_thread::Platform_thread(size_t quota, const char * const label,
/* create kernel object */
constexpr unsigned max_prio = Kernel::Cpu_priority::max;
auto const phys_prio = Cpu_session::scale_priority(max_prio, virt_prio);
quota = _generic_to_platform_quota(quota);
_id = Kernel::new_thread(_kernel_thread, phys_prio, quota, _label);
if (!_id) {
PERR("failed to create kernel object");

View File

@ -0,0 +1,30 @@
/*
* \brief Sync-session capability type
* \author Martin Stein
* \date 2015-04-07
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SYNC_SESSION__CAPABILITY_H_
#define _SYNC_SESSION__CAPABILITY_H_
/* Genode includes */
#include <base/capability.h>
/* local includes */
#include <sync_session/sync_session.h>
namespace Sync
{
using Genode::Capability;
typedef Capability<Session> Session_capability;
}
#endif /* _SYNC_SESSION__CAPABILITY_H_ */

View File

@ -0,0 +1,43 @@
/*
* \brief Client-side Sync-session interface
* \author Martin Stein
* \date 2015-04-07
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SYNC_SESSION__CLIENT_H_
#define _SYNC_SESSION__CLIENT_H_
/* Genode includes */
#include <base/rpc_client.h>
/* local includes */
#include <sync_session/capability.h>
namespace Sync
{
using Genode::Rpc_client;
struct Session_client;
}
struct Sync::Session_client : Rpc_client<Session>
{
explicit Session_client(Session_capability session)
: Rpc_client<Session>(session) { }
void threshold(unsigned id, unsigned threshold) override {
call<Rpc_threshold>(id, threshold); }
void submit(unsigned id, Signal_context_capability sigc) override {
call<Rpc_submit>(id, sigc); }
};
#endif /* _SYNC_SESSION__CLIENT_H_ */

View File

@ -0,0 +1,59 @@
/*
* \brief Connection to Sync service
* \author Martin Stein
* \date 2015-04-07
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SYNC_SESSION__CONNECTION_H_
#define _SYNC_SESSION__CONNECTION_H_
/* Genode includes */
#include <base/connection.h>
/* local includes */
#include <sync_session/client.h>
namespace Sync
{
using Genode::Parent;
class Connection;
}
class Sync::Connection : public Genode::Connection<Session>,
public Session_client
{
public:
class Connection_failed : public Parent::Exception { };
private:
Session_capability _create_session()
{
try { return session("ram_quota=4K"); }
catch (...) { throw Connection_failed(); }
}
public:
/**
* Constructor
*
* \throw Connection_failed
*/
Connection() :
Genode::Connection<Session>(_create_session()),
Session_client(cap())
{ }
};
#endif /* _SYNC_SESSION__CONNECTION_H_ */

View File

@ -0,0 +1,56 @@
/*
* \brief Sync session interface
* \author Martin Stein
* \date 2015-04-07
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SYNC_SESSION__SYNC_SESSION_H_
#define _SYNC_SESSION__SYNC_SESSION_H_
/* Genode includes */
#include <session/session.h>
#include <signal_session/signal_session.h>
namespace Sync
{
using Genode::Signal_context_capability;
struct Session;
}
struct Sync::Session : Genode::Session
{
static const char *service_name() { return "Sync"; }
virtual ~Session() { }
/**
* Set the submission threshold of a synchronization signal
*/
virtual void threshold(unsigned id, unsigned threshold) = 0;
/**
* Submit to a synchronization signal
*/
virtual void submit(unsigned id, Signal_context_capability sigc) = 0;
/*********************
** RPC declaration **
*********************/
GENODE_RPC(Rpc_threshold, void, threshold, unsigned, unsigned);
GENODE_RPC(Rpc_submit, void, submit, unsigned, Signal_context_capability);
GENODE_RPC_INTERFACE(Rpc_threshold, Rpc_submit);
};
#endif /* _SYNC_SESSION__SYNC_SESSION_H_ */

View File

@ -1,5 +1,5 @@
/*
* \brief Diversified test of the Register and MMIO framework
* \brief Test the distribution and application of CPU quota
* \author Martin Stein
* \date 2012-01-09
*/
@ -14,50 +14,150 @@
/* Genode includes */
#include <base/printf.h>
#include <base/thread.h>
#include <base/env.h>
#include <base/sleep.h>
#include <timer_session/connection.h>
#include <sync_session/connection.h>
using namespace Genode;
class My_thread : public Thread<8 * 1024>
enum { SYNC_SIG = 0 };
namespace Sync { class Signal; }
class Single_signal
{
private:
Signal_receiver * const _sigr;
bool volatile _stop;
Signal_receiver _sigr;
Signal_context _sigx;
Signal_context_capability _sigc;
Signal_transmitter _sigt;
public:
My_thread(Signal_receiver * const sigr)
: Thread(Cpu_session::pc_to_quota(100), "counter"),
_sigr(sigr), _stop(0) { }
Single_signal() : _sigc(_sigr.manage(&_sigx)), _sigt(_sigc) { }
~Single_signal() { _sigr.dissolve(&_sigx); }
void receive() { _sigr.wait_for_signal(); }
void submit() { _sigt.submit(); }
};
class Sync::Signal
{
private:
Signal_receiver _sigr;
Signal_context _sigx;
Signal_context_capability _sigc;
Session * const _session;
unsigned const _id;
public:
Signal(Session * const session, unsigned const id)
: _sigc(_sigr.manage(&_sigx)), _session(session), _id(id) { }
~Signal() { _sigr.dissolve(&_sigx); }
void threshold(unsigned const threshold) {
_session->threshold(_id, threshold); }
void sync()
{
_session->submit(_id, _sigc);
_sigr.wait_for_signal();
}
};
class Counter : private Thread<8 * 1024>
{
private:
char const _name;
unsigned volatile _value;
Sync::Signal _sync_sig;
unsigned volatile _stage;
Single_signal _stage_1_end;
Single_signal _stage_2_reached;
inline void _stage_0_and_1(unsigned volatile & value)
{
_stage_1_end.receive();
_stage = 0;
_sync_sig.sync();
while(_stage == 0) { value++; }
}
void entry()
{
_sigr->wait_for_signal();
unsigned volatile i = 0;
while(!_stop) { i++; }
printf("%u\n", i);
unsigned volatile value = 0;
while (_stage < 2) { _stage_0_and_1(value); }
_value = value;
_stage_2_reached.submit();
sleep_forever();
}
void stop() { _stop = 1; }
public:
Counter(char const name, size_t const weight,
Sync::Session * const sync)
:
Thread(weight, "counter"), _name(name), _value(0) ,
_sync_sig(sync, SYNC_SIG), _stage(1)
{
Thread::start();
}
void destruct()
{
_stage = 2;
_stage_2_reached.receive();
this->~Counter();
}
void pause() { _stage = 1; }
void go() { _stage_1_end.submit(); }
void result() { printf("counter %c %u\n", _name, _value); }
};
int main()
{
/* prepare */
Timer::Connection timer;
Signal_receiver sigr;
Signal_context sigx;
Signal_context_capability sigc = sigr.manage(&sigx);
Signal_transmitter sigt(sigc);
My_thread thread(&sigr);
thread.start();
timer.msleep(3000);
sigt.submit();
timer.msleep(30000);
thread.stop();
Sync::Connection sync;
Sync::Signal sync_sig(&sync, SYNC_SIG);
Counter counter_a('A', Cpu_session::quota_lim_upscale(10, 100), &sync);
Counter counter_b('B', Cpu_session::quota_lim_upscale(90, 100), &sync);
/* measure stage 1 */
sync_sig.threshold(9);
counter_a.go();
counter_b.go();
sync_sig.sync();
timer.msleep(45000);
counter_a.pause();
counter_b.destruct();
/* measure stage 2 */
sync_sig.threshold(6);
counter_a.go();
sync_sig.sync();
timer.msleep(15000);
counter_a.destruct();
/* print results */
sync_sig.threshold(3);
sync_sig.sync();
Cpu_session::Quota quota = Genode::env()->cpu_session()->quota();
Genode::printf("quota super period %zu\n", quota.super_period_us);
Genode::printf("quota %zu\n", quota.us);
counter_a.result();
counter_b.result();
printf("done\n");
sleep_forever();
}

View File

@ -0,0 +1,152 @@
/*
* \brief Provide sync signals for cross-component synchronization
* \author Martin Stein
* \date 2015-04-07
*/
/*
* Copyright (C) 2015 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/server.h>
#include <root/component.h>
/* local includes */
#include <sync_session/connection.h>
namespace Sync
{
enum { NR_OF_SIGNALS = 1 };
using Server::Entrypoint;
using Genode::Rpc_object;
using Genode::env;
using Genode::Root_component;
using Genode::Allocator;
using Genode::Signal_transmitter;
class Signal;
class Session_component;
class Root;
struct Main;
}
class Sync::Signal
{
friend class Root;
private:
enum { NR_OF_TRANSMITTERS = 9 };
Signal_transmitter _transmitters[NR_OF_TRANSMITTERS];
unsigned _submitted;
unsigned _threshold;
void _check()
{
if (_submitted < _threshold) { return; }
for (unsigned i = 0; i < _submitted; i++) {
_transmitters[i].submit(); }
_submitted = 0;
}
void _reset()
{
_submitted = 0;
_threshold = 0;
}
public:
void threshold(unsigned const threshold)
{
_threshold = threshold;
_check();
}
void submit(Signal_context_capability & sigc)
{
_transmitters[_submitted] = Signal_transmitter(sigc);
_submitted++;
_check();
}
};
class Sync::Session_component : public Rpc_object<Session>
{
private:
Signal * const _signals;
public:
Session_component(Signal * const signals) : _signals(signals) { }
void threshold(unsigned id, unsigned threshold) override
{
if (id >= NR_OF_SIGNALS) { return; }
_signals[id].threshold(threshold);
}
void
submit(unsigned const id, Signal_context_capability sigc) override
{
if (id >= NR_OF_SIGNALS) { return; }
_signals[id].submit(sigc);
}
};
class Sync::Root : public Root_component<Session_component>
{
private:
Signal _signals[NR_OF_SIGNALS];
protected:
Session_component *_create_session(const char *args)
{
try { return new (md_alloc()) Session_component(_signals); }
catch (...) { throw Root::Exception(); }
}
public:
Root(Entrypoint & ep, Allocator & md_alloc)
: Root_component<Session_component>(&ep.rpc_ep(), &md_alloc)
{
for (unsigned i = 0; i < NR_OF_SIGNALS; i++) {
_signals[i]._reset(); }
}
};
struct Sync::Main
{
Server::Entrypoint & ep;
Root root;
Main(Server::Entrypoint & ep) : ep(ep), root(ep, *env()->heap()) {
env()->parent()->announce(ep.manage(root)); }
};
/************
** Server **
************/
namespace Server
{
using namespace Sync;
char const *name() { return "sync_ep"; }
size_t stack_size() { return 2 * 1024 * sizeof(long); }
void construct(Entrypoint & ep) { static Main main(ep); }
}

View File

@ -0,0 +1,17 @@
#
# \brief Provide cross-component synchronization
# \author Martin Stein
# \date 2014-10-13
#
# Set program name
TARGET = test-sync
# Add C++ sources
SRC_CC = main.cc
# Add include paths
INC_DIR += $(PRG_DIR)/../include
# Add libraries
LIBS = base server

View File

@ -1,5 +1,5 @@
#
# \brief Test static configuration of CPU-time distribution
# \brief Test the distribution and application of CPU quota
# \author Martin Stein
# \date 2014-10-13
#
@ -10,5 +10,8 @@ TARGET = test-cpu_quota
# Add C++ sources
SRC_CC += main.cc
# Add include paths
INC_DIR += $(PRG_DIR)/include
# Add libraries
LIBS += base

View File

@ -24,8 +24,8 @@ namespace Genode {
explicit Linux_cpu_session_client(Capability<Linux_cpu_session> session)
: Rpc_client<Linux_cpu_session>(session) { }
Thread_capability create_thread(size_t, Name const &name, addr_t utcb = 0) {
return call<Rpc_create_thread>(0, name, utcb); }
Thread_capability create_thread(size_t weight, Name const &name, addr_t utcb = 0) {
return call<Rpc_create_thread>(weight, name, utcb); }
Ram_dataspace_capability utcb(Thread_capability thread) {
return call<Rpc_utcb>(thread); }
@ -84,9 +84,7 @@ namespace Genode {
int transfer_quota(Cpu_session_capability session, size_t amount) {
return call<Rpc_transfer_quota>(session, amount); }
size_t quota() { return call<Rpc_quota>(); }
size_t used() { return call<Rpc_used>(); }
Quota quota() override { return call<Rpc_quota>(); }
/*****************************
* Linux-specific extension **

View File

@ -44,10 +44,10 @@ struct Genode::Expanding_cpu_session_client
Expanding_cpu_session_client(Genode::Capability<Linux_cpu_session> cap)
: Upgradeable_client<Genode::Linux_cpu_session_client>(cap) { }
Thread_capability create_thread(size_t, Name const &name, addr_t utcb)
Thread_capability create_thread(size_t weight, Name const &name, addr_t utcb)
{
return retry<Cpu_session::Out_of_metadata>(
[&] () { return Linux_cpu_session_client::create_thread(0, name, utcb); },
[&] () { return Linux_cpu_session_client::create_thread(weight, name, utcb); },
[&] () { upgrade_ram(8*1024); });
}
};

View File

@ -80,7 +80,8 @@ Process::Process(Dataspace_capability elf_data_ds_cap,
* thread. Those information will be provided to core by the constructor of
* the 'Platform_env' of the new process.
*/
_thread0_cap = _cpu_session_client.create_thread(0, name);
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
_thread0_cap = _cpu_session_client.create_thread(WEIGHT, name);
Linux_pd_session_client lx_pd(static_cap_cast<Linux_pd_session>(_pd.cap()));

View File

@ -69,7 +69,7 @@ void Thread_base::_thread_start()
}
void Thread_base::_init_platform_thread(size_t, Type type)
void Thread_base::_init_platform_thread(size_t weight, Type type)
{
/* if no cpu session is given, use it from the environment */
if (!_cpu_session)
@ -77,7 +77,7 @@ void Thread_base::_init_platform_thread(size_t, Type type)
/* for normal threads create an object at the CPU session */
if (type == NORMAL) {
_thread_cap = _cpu_session->create_thread(0, _context->name);
_thread_cap = _cpu_session->create_thread(weight, _context->name);
return;
}
/* adjust initial object state for main threads */

View File

@ -26,3 +26,5 @@ Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_ca
return Ram_dataspace_capability();
}
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -63,7 +63,9 @@ namespace Genode {
public:
Cpu_thread_component(size_t, Session_label const &label,
Cpu_thread_component(size_t const weight,
size_t const quota,
Session_label const &label,
Thread_name const &name,
unsigned priority, addr_t utcb,
Signal_context_capability sigh,
@ -87,7 +89,8 @@ namespace Genode {
bool bound() const { return _bound; }
void bound(bool b) { _bound = b; }
Trace::Source *trace_source() { return &_trace_source; }
size_t quota() { return 0; }
size_t weight() const { return Cpu_session::DEFAULT_WEIGHT; }
void sigh(Signal_context_capability sigh)
{
@ -131,24 +134,26 @@ namespace Genode {
session */
Trace::Source_registry &_trace_sources;
Trace::Control_area _trace_control_area;
Cpu_session_component * _ref;
size_t _used;
size_t _weight;
size_t _quota;
Cpu_session_component * _ref;
List<Cpu_session_component> _ref_members;
Lock _ref_members_lock;
size_t _global_to_local(size_t const q) const { return 0; }
size_t _avail() { return 0; }
void _incr_weight(size_t);
void _decr_weight(size_t);
size_t _weight_to_quota(size_t) const;
void _decr_quota(size_t);
void _incr_quota(size_t);
void _update_thread_quota(Cpu_thread_component *) const;
void _update_each_thread_quota();
void _transfer_quota(Cpu_session_component *, size_t);
void _insert_ref_member(Cpu_session_component *) { }
void _unsync_remove_ref_member(Cpu_session_component *) { }
void _remove_ref_member(Cpu_session_component *) { }
void _deinit_ref_account();
void _deinit_threads();
size_t _local_to_global(size_t) const { return 0; }
void _insuff_for_consume(size_t);
int _insuff_for_transfer(size_t);
int _transfer_back(size_t) { return -1; }
int _transfer_forth(Cpu_session_component *, size_t) { return -1; }
void _insert_ref_member(Cpu_session_component *) { }
void _remove_ref_member(Cpu_session_component *) { }
void _unsync_remove_ref_member(Cpu_session_component *) { }
/**
* Exception handler that will be invoked unless overridden by a
@ -214,8 +219,7 @@ namespace Genode {
Dataspace_capability trace_policy(Thread_capability);
int ref_account(Cpu_session_capability c);
int transfer_quota(Cpu_session_capability c, size_t q);
size_t used();
size_t quota();
Quota quota() override;
/*******************************

View File

@ -153,6 +153,11 @@ namespace Genode {
{
_registry()->submit_exception(pid);
}
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota) { /* not supported*/ }
};
}

View File

@ -401,7 +401,7 @@ void Thread_base::join()
}
Thread_base::Thread_base(size_t, const char *name, size_t stack_size,
Thread_base::Thread_base(size_t weight, const char *name, size_t stack_size,
Type type, Cpu_session * cpu_sess)
: _cpu_session(cpu_sess)
{
@ -420,13 +420,14 @@ Thread_base::Thread_base(size_t, const char *name, size_t stack_size,
Linux_cpu_session *cpu = cpu_session(_cpu_session);
_thread_cap = cpu->create_thread(0, name);
_thread_cap = cpu->create_thread(weight, name);
cpu->thread_id(_thread_cap, _tid.pid, _tid.tid);
}
Thread_base::Thread_base(size_t, const char *name, size_t stack_size, Type type)
: Thread_base(0, name, stack_size, type, env()->cpu_session()) { }
Thread_base::Thread_base(size_t weight, const char *name, size_t stack_size,
Type type)
: Thread_base(weight, name, stack_size, type, env()->cpu_session()) { }
void Thread_base::cancel_blocking()
{

View File

@ -28,8 +28,8 @@ namespace Genode {
explicit Cpu_session_client(Cpu_session_capability session)
: Rpc_client<Nova_cpu_session>(static_cap_cast<Nova_cpu_session>(session)) { }
Thread_capability create_thread(size_t, Name const &name, addr_t utcb = 0) {
return call<Rpc_create_thread>(0, name, utcb); }
Thread_capability create_thread(size_t weight, Name const &name, addr_t utcb = 0) {
return call<Rpc_create_thread>(weight, name, utcb); }
Ram_dataspace_capability utcb(Thread_capability thread) {
return call<Rpc_utcb>(thread); }
@ -100,9 +100,7 @@ namespace Genode {
int transfer_quota(Cpu_session_capability session, size_t amount) {
return call<Rpc_transfer_quota>(session, amount); }
size_t quota() { return call<Rpc_quota>(); }
size_t used() { return call<Rpc_used>(); }
Quota quota() override { return call<Rpc_quota>(); }
private:

View File

@ -599,7 +599,7 @@ Pager_object::~Pager_object()
Pager_activation_base::Pager_activation_base(const char *name, size_t stack_size)
:
Thread_base(0, name, stack_size),
Thread_base(Cpu_session::DEFAULT_WEIGHT, name, stack_size),
_cap(Native_capability()), _ep(0), _cap_valid(Lock::LOCKED)
{
/* tell thread starting code on which CPU to let run the pager */

View File

@ -221,7 +221,7 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
const char *name, bool start_on_construction,
Affinity::Location location)
:
Thread_base(0, name, stack_size),
Thread_base(Cpu_session::DEFAULT_WEIGHT, name, stack_size),
_curr_obj(start_on_construction ? 0 : (Rpc_object_base *)~0UL),
_delay_start(Lock::LOCKED),
_cap_session(cap_session)

View File

@ -65,7 +65,7 @@ void Thread_base::_thread_start()
** Thread base **
*****************/
void Thread_base::_init_platform_thread(size_t, Type type)
void Thread_base::_init_platform_thread(size_t weight, Type type)
{
using namespace Nova;
@ -101,7 +101,7 @@ void Thread_base::_init_platform_thread(size_t, Type type)
char buf[48];
name(buf, sizeof(buf));
_thread_cap = _cpu_session->create_thread(0, buf);
_thread_cap = _cpu_session->create_thread(weight, buf);
if (!_thread_cap.valid())
throw Cpu_session::Thread_creation_failed();

View File

@ -26,3 +26,5 @@ Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_ca
return Ram_dataspace_capability();
}
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -64,7 +64,9 @@ namespace Genode {
public:
Cpu_thread_component(size_t, Session_label const &label,
Cpu_thread_component(size_t const weight,
size_t const quota,
Session_label const &label,
Thread_name const &name,
unsigned priority, addr_t utcb,
Signal_context_capability sigh,
@ -88,7 +90,8 @@ namespace Genode {
bool bound() const { return _bound; }
void bound(bool b) { _bound = b; }
Trace::Source *trace_source() { return &_trace_source; }
size_t quota() { return 0; }
size_t weight() const { return Cpu_session::DEFAULT_WEIGHT; }
void sigh(Signal_context_capability sigh)
{
@ -138,24 +141,26 @@ namespace Genode {
session */
Trace::Source_registry &_trace_sources;
Trace::Control_area _trace_control_area;
Cpu_session_component * _ref;
size_t _used;
size_t _weight;
size_t _quota;
Cpu_session_component * _ref;
List<Cpu_session_component> _ref_members;
Lock _ref_members_lock;
size_t _global_to_local(size_t const q) const { return 0; }
size_t _avail() { return 0; }
void _incr_weight(size_t);
void _decr_weight(size_t);
size_t _weight_to_quota(size_t) const;
void _decr_quota(size_t);
void _incr_quota(size_t);
void _update_thread_quota(Cpu_thread_component *) const;
void _update_each_thread_quota();
void _transfer_quota(Cpu_session_component *, size_t);
void _insert_ref_member(Cpu_session_component *) { }
void _unsync_remove_ref_member(Cpu_session_component *) { }
void _remove_ref_member(Cpu_session_component *) { }
void _deinit_ref_account();
void _deinit_threads();
size_t _local_to_global(size_t) const { return 0; }
void _insuff_for_consume(size_t);
int _insuff_for_transfer(size_t);
int _transfer_back(size_t) { return -1; }
int _transfer_forth(Cpu_session_component *, size_t) { return -1; }
void _insert_ref_member(Cpu_session_component *) { }
void _remove_ref_member(Cpu_session_component *) { }
void _unsync_remove_ref_member(Cpu_session_component *) { }
/**
* Exception handler that will be invoked unless overridden by a
@ -222,8 +227,7 @@ namespace Genode {
Dataspace_capability trace_policy(Thread_capability);
int ref_account(Cpu_session_capability c);
int transfer_quota(Cpu_session_capability c, size_t q);
size_t used();
size_t quota();
Quota quota() override;
/******************************

View File

@ -164,6 +164,11 @@ namespace Genode {
}
Native_capability single_step(bool on);
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota) { /* not supported*/ }
};
}

View File

@ -41,6 +41,9 @@ class Irq_thread : public Thread_base
{
private:
enum { STACK_SIZE = 1024 * sizeof(addr_t) };
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
static void _thread_start()
{
Thread_base::myself()->entry();
@ -49,7 +52,8 @@ class Irq_thread : public Thread_base
public:
Irq_thread(char const *name) : Thread_base(0, name, 1024 * sizeof(addr_t)) { }
Irq_thread(char const *name) : Thread_base(WEIGHT, name, STACK_SIZE)
{ }
/**
* Create global EC, associate it to SC

View File

@ -26,3 +26,5 @@ Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_ca
return Ram_dataspace_capability();
}
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -147,6 +147,11 @@ namespace Genode {
*/
Affinity::Location affinity() { return Affinity::Location(); }
/**
* Set CPU quota of the thread
*/
void quota(size_t) { /* not supported */ }
/*****************************
** OKL4-specific Accessors **

View File

@ -32,3 +32,5 @@ Ram_dataspace_capability Cpu_session_component::utcb(Thread_capability thread_ca
return Ram_dataspace_capability();
}
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -148,6 +148,11 @@ namespace Genode {
*/
Affinity::Location affinity();
/**
* Set CPU quota of the thread to 'quota'
*/
void quota(size_t const quota) { /* not supported*/ }
/**********************************
** Pistachio-specific Accessors **

View File

@ -132,6 +132,8 @@ class Genode::Pager_activation_base: public Thread_base
{
private:
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
Native_capability _cap;
Pager_entrypoint *_ep; /* entry point to which the
activation belongs */
@ -142,9 +144,9 @@ class Genode::Pager_activation_base: public Thread_base
public:
Pager_activation_base(const char *name, size_t stack_size) :
Thread_base(0, name, stack_size),
_cap(Native_capability()), _ep(0), _cap_valid(Lock::LOCKED) { }
Pager_activation_base(const char *name, size_t stack_size)
: Thread_base(WEIGHT, name, stack_size), _cap(Native_capability()),
_ep(0), _cap_valid(Lock::LOCKED) { }
/**
* Set entry point, which the activation serves

View File

@ -330,10 +330,10 @@ class Genode::Thread_base
/**
* Hook for platform-specific constructor supplements
*
* \param quota CPU quota that shall be granted to the thread
* \param type enables selection of special initialization
* \param weight weighting regarding the CPU session quota
* \param type enables selection of special initialization
*/
void _init_platform_thread(size_t quota, Type type);
void _init_platform_thread(size_t weight, Type type);
public:
@ -347,14 +347,14 @@ class Genode::Thread_base
* at least set Context::ds_cap in a way that it references
* the dataspace of the already attached stack.
*/
Thread_base(size_t quota, const char *name, size_t stack_size,
Thread_base(size_t weight, const char *name, size_t stack_size,
Type type);
/**
* Constructor
*
* \param quota CPU quota that shall be granted to the thread
* \param name thread name for DEBUGging
* \param weight weighting regarding the CPU session quota
* \param name thread name (for debugging)
* \param stack_size stack size
*
* \throw Stack_too_large
@ -366,10 +366,8 @@ class Genode::Thread_base
* internally used by the framework for storing thread-context
* information such as the thread's name ('Context').
*/
Thread_base(size_t quota, const char *name, size_t stack_size)
:
Thread_base(quota, name, stack_size, NORMAL)
{ }
Thread_base(size_t weight, const char *name, size_t stack_size)
: Thread_base(weight, name, stack_size, NORMAL) { }
/**
* Constructor
@ -380,8 +378,8 @@ class Genode::Thread_base
* \noapi Using multiple CPU sessions within a single component is
* an experimental feature.
*
* \param quota CPU quota that shall be granted to the thread
* \param name thread name for debugging
* \param weight weighting regarding the CPU session quota
* \param name thread name (for debugging)
* \param stack_size stack size
* \param type enables selection of special construction
* \param cpu_session capability to cpu session used for construction
@ -390,7 +388,7 @@ class Genode::Thread_base
* \throw Stack_alloc_failed
* \throw Context_alloc_failed
*/
Thread_base(size_t quota, const char *name, size_t stack_size,
Thread_base(size_t weight, const char *name, size_t stack_size,
Type type, Cpu_session *);
/**
@ -536,53 +534,54 @@ class Genode::Thread : public Thread_base
/**
* Constructor
*
* \param quota CPU quota that shall be granted to the thread
* \param name thread name (for debugging)
* \param type enables selection of special construction
* \param weight weighting regarding the CPU session quota
* \param name thread name (for debugging)
* \param type enables selection of special construction
*/
explicit Thread(size_t quota, const char *name)
: Thread_base(quota, name, STACK_SIZE, Type::NORMAL) { }
explicit Thread(size_t weight, const char *name)
: Thread_base(weight, name, STACK_SIZE, Type::NORMAL) { }
/**
* Constructor
*
* \param quota CPU quota that shall be granted to the thread
* \param name thread name (for debugging)
* \param type enables selection of special construction
* \param weight weighting regarding the CPU session quota
* \param name thread name (for debugging)
* \param type enables selection of special construction
*
* \noapi
*/
explicit Thread(size_t quota, const char *name, Type type)
: Thread_base(quota, name, STACK_SIZE, type) { }
explicit Thread(size_t weight, const char *name, Type type)
: Thread_base(weight, name, STACK_SIZE, type) { }
/**
* Constructor
*
* \param quota CPU quota that shall be granted to the thread
* \param weight weighting regarding the CPU session quota
* \param name thread name (for debugging)
* \param cpu_session thread created via specific cpu session
*
* \noapi
*/
explicit Thread(size_t quota, const char *name, Cpu_session * cpu_session)
: Thread_base(quota, name, STACK_SIZE, Type::NORMAL, cpu_session)
{ }
explicit Thread(size_t weight, const char *name,
Cpu_session * cpu_session)
: Thread_base(weight, name, STACK_SIZE, Type::NORMAL, cpu_session) { }
/**
* Shortcut for 'Thread(0, name, type)'
* Shortcut for 'Thread(DEFAULT_WEIGHT, name, type)'
*
* \noapi
*/
explicit Thread(const char *name, Type type = NORMAL)
: Thread_base(0, name, STACK_SIZE, type) { }
: Thread_base(Cpu_session::DEFAULT_WEIGHT, name, STACK_SIZE, type) { }
/**
* Shortcut for 'Thread(0, name, cpu_session)'
* Shortcut for 'Thread(DEFAULT_WEIGHT, name, cpu_session)'
*
* \noapi
*/
explicit Thread(const char *name, Cpu_session * cpu_session)
: Thread_base(0, name, STACK_SIZE, Type::NORMAL, cpu_session)
: Thread_base(Cpu_session::DEFAULT_WEIGHT, name, STACK_SIZE,
Type::NORMAL, cpu_session)
{ }
};

View File

@ -86,9 +86,7 @@ struct Genode::Cpu_session_client : Rpc_client<Cpu_session>
int transfer_quota(Cpu_session_capability session, size_t amount) override {
return call<Rpc_transfer_quota>(session, amount); }
size_t quota() override { return call<Rpc_quota>(); }
size_t used() override { return call<Rpc_used>(); }
Quota quota() override { return call<Rpc_quota>(); }
};
#endif /* _INCLUDE__CPU_SESSION__CLIENT_H_ */

View File

@ -47,9 +47,15 @@ struct Genode::Cpu_session : Session
enum { QUOTA_LIMIT_LOG2 = 15 };
enum { QUOTA_LIMIT = 1 << QUOTA_LIMIT_LOG2 };
enum { DEFAULT_PRIORITY = 0 };
enum { DEFAULT_WEIGHT = 10 };
typedef Rpc_in_buffer<THREAD_NAME_LEN> Name;
/**
* Physical quota configuration
*/
struct Quota;
virtual ~Cpu_session() { }
/**
@ -264,7 +270,8 @@ struct Genode::Cpu_session : Session
* Transfer quota to another CPU session
*
* \param cpu_session receiver of quota donation
* \param amount amount of quota to donate
* \param amount percentage of the session quota scaled up to
* the 'QUOTA_LIMIT' space
* \return 0 on success
*
* Quota can only be transfered if the specified CPU session is
@ -274,29 +281,23 @@ struct Genode::Cpu_session : Session
size_t amount) = 0;
/**
* Return current quota limit
* Return quota configuration of the session
*/
virtual size_t quota() = 0;
virtual Quota quota() = 0;
/**
* Return amount of used quota
* Scale up 'value' from its space with 'limit' to the 'QUOTA_LIMIT' space
*/
virtual size_t used() = 0;
template<typename T = size_t>
static size_t quota_lim_upscale(size_t const value, size_t const limit) {
return ((T)value << Cpu_session::QUOTA_LIMIT_LOG2) / limit; }
/**
* Return amount of available quota
* Scale down 'value' from the 'QUOTA_LIMIT' space to a space with 'limit'
*/
size_t avail()
{
size_t q = quota(), u = used();
return q > u ? q - u : 0;
}
/**
* Transform percentage of CPU utilization into CPU quota
*/
static size_t pc_to_quota(size_t const pc) {
return (pc << QUOTA_LIMIT_LOG2) / 100; }
template<typename T = size_t>
static size_t quota_lim_downscale(size_t const value, size_t const limit) {
return ((T)value * limit) >> Cpu_session::QUOTA_LIMIT_LOG2; }
/*********************
** RPC declaration **
@ -329,8 +330,7 @@ struct Genode::Cpu_session : Session
GENODE_RPC(Rpc_trace_policy, Dataspace_capability, trace_policy, Thread_capability);
GENODE_RPC(Rpc_ref_account, int, ref_account, Cpu_session_capability);
GENODE_RPC(Rpc_transfer_quota, int, transfer_quota, Cpu_session_capability, size_t);
GENODE_RPC(Rpc_quota, size_t, quota);
GENODE_RPC(Rpc_used, size_t, used);
GENODE_RPC(Rpc_quota, Quota, quota);
/*
* 'GENODE_RPC_INTERFACE' declaration done manually
@ -361,9 +361,14 @@ struct Genode::Cpu_session : Session
Meta::Type_tuple<Rpc_ref_account,
Meta::Type_tuple<Rpc_transfer_quota,
Meta::Type_tuple<Rpc_quota,
Meta::Type_tuple<Rpc_used,
Meta::Empty>
> > > > > > > > > > > > > > > > > > > > > Rpc_functions;
> > > > > > > > > > > > > > > > > > > > Rpc_functions;
};
struct Genode::Cpu_session::Quota
{
size_t super_period_us;
size_t us;
};
#endif /* _INCLUDE__CPU_SESSION__CPU_SESSION_H_ */

View File

@ -198,7 +198,8 @@ Process::Process(Dataspace_capability elf_ds_cap,
/* create thread0 */
try {
_thread0_cap = _cpu_session_client.create_thread(0, name);
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
_thread0_cap = _cpu_session_client.create_thread(WEIGHT, name);
} catch (Cpu_session::Thread_creation_failed) {
PERR("Creation of thread0 failed");
throw THREAD_FAIL;

View File

@ -102,7 +102,7 @@ Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
char const *name, bool start_on_construction,
Affinity::Location location)
:
Thread_base(0, name, stack_size),
Thread_base(Cpu_session::DEFAULT_WEIGHT, name, stack_size),
_cap(Untyped_capability()),
_curr_obj(0), _cap_valid(Lock::LOCKED), _delay_start(Lock::LOCKED),
_delay_exit(Lock::LOCKED),

View File

@ -54,7 +54,9 @@ void Thread_base::start()
/* create thread at core */
char buf[48];
name(buf, sizeof(buf));
_thread_cap = _cpu_session->create_thread(0, buf, (addr_t)&_context->utcb);
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
addr_t const utcb = (addr_t)&_context->utcb;
_thread_cap = _cpu_session->create_thread(WEIGHT, buf, utcb);
if (!_thread_cap.valid())
throw Cpu_session::Thread_creation_failed();

View File

@ -33,14 +33,10 @@ void Cpu_thread_component::update_exception_sigh()
};
Thread_capability Cpu_session_component::create_thread(size_t quota,
Thread_capability Cpu_session_component::create_thread(size_t weight,
Name const &name,
addr_t utcb)
{
/* check for sufficient quota */
quota = _local_to_global(quota);
if (quota > avail()) { _insuff_for_consume(quota); }
unsigned trace_control_index = 0;
if (!_trace_control_area.alloc(trace_control_index))
throw Out_of_metadata();
@ -51,16 +47,27 @@ Thread_capability Cpu_session_component::create_thread(size_t quota,
Trace::Thread_name thread_name(name.string());
Cpu_thread_component *thread = 0;
if (weight == 0) {
PWRN("Thread %s: Bad weight 0, using %i instead.",
name.string(), DEFAULT_WEIGHT);
weight = DEFAULT_WEIGHT;
}
if (weight > QUOTA_LIMIT) {
PWRN("Thread %s: Oversized weight %zu, using %i instead.",
name.string(), weight, QUOTA_LIMIT);
weight = QUOTA_LIMIT;
}
Lock::Guard thread_list_lock_guard(_thread_list_lock);
_incr_weight(weight);
try {
Lock::Guard slab_lock_guard(_thread_alloc_lock);
thread = new(&_thread_alloc)
Cpu_thread_component(
quota, _label, thread_name, _priority, utcb,
_default_exception_handler, trace_control_index,
*trace_control);
/* account quota */
_used += quota;
weight, _weight_to_quota(weight), _label, thread_name,
_priority, utcb, _default_exception_handler,
trace_control_index, *trace_control);
/* set default affinity defined by CPU session */
thread->platform_thread()->affinity(_location);
@ -68,7 +75,6 @@ Thread_capability Cpu_session_component::create_thread(size_t quota,
throw Out_of_metadata();
}
Lock::Guard thread_list_lock_guard(_thread_list_lock);
_thread_list.insert(thread);
_trace_sources.insert(thread->trace_source());
@ -86,7 +92,7 @@ void Cpu_session_component::_unsynchronized_kill_thread(Cpu_thread_component *th
unsigned const trace_control_index = thread->trace_control_index();
_used -= thread->quota();
_decr_weight(thread->weight());
Lock::Guard lock_guard(_thread_alloc_lock);
destroy(&_thread_alloc, thread);
@ -290,39 +296,72 @@ static size_t remaining_session_ram_quota(char const *args)
}
int Cpu_session_component::transfer_quota(Cpu_session_capability c, size_t q)
void Cpu_session_component::_transfer_quota(Cpu_session_component * const dst,
size_t const quota)
{
/* lookup targeted CPU-session */
Object_pool<Cpu_session_component>::Guard s(_session_ep->lookup_and_lock(c));
if (!s) { return -1; }
/* translate quota argument and check limits */
q = _local_to_global(q);
if (q > avail()) { return _insuff_for_transfer(q); }
/* transfer quota to targeted CPU-session */
if (s->_ref == this) { return _transfer_forth(s, q); }
if (s == _ref) { return _transfer_back(q); }
return -2;
if (!quota) { return; }
_decr_quota(quota);
dst->_incr_quota(quota);
}
int Cpu_session_component::ref_account(Cpu_session_capability c)
int Cpu_session_component::transfer_quota(Cpu_session_capability dst_cap,
size_t amount)
{
/* lookup targeted CPU session */
Object_pool<Cpu_session_component>::Guard
dst(_session_ep->lookup_and_lock(dst_cap));
if (!dst) {
PWRN("Transfer CPU quota, %s, targeted session not found",
_label.string());
return -1;
}
/* check reference relationship */
if (dst->_ref != this && dst != _ref) {
PWRN("Transfer CPU quota, %s -> %s, no reference relation",
_label.string(), dst->_label.string());
return -2;
}
/* check quota availability */
size_t const quota = quota_lim_downscale(_quota, amount);
if (quota > _quota) {
PWRN("Transfer CPU quota, %s -> %s, insufficient quota %zu, need %zu",
_label.string(), dst->_label.string(), _quota, quota);
return -3;
}
/* transfer quota */
_transfer_quota(dst, quota);
return 0;
}
int Cpu_session_component::ref_account(Cpu_session_capability ref_cap)
{
/*
* Ensure that the ref account is set only once
*
* FIXME Add check for cycles along the tree of reference accounts
*/
if (_ref) { return -2; }
/* lookup targeted CPU-session */
Object_pool<Cpu_session_component>::Guard s(_session_ep->lookup_and_lock(c));
if (!s) { return -1; }
if (s == this) { return -3; }
if (_ref) {
PWRN("Set ref account, %s, set already",
_label.string());
return -2; }
/* lookup and check targeted CPU-session */
Object_pool<Cpu_session_component>::Guard
ref(_session_ep->lookup_and_lock(ref_cap));
if (!ref) {
PWRN("Set ref account, %s, targeted session not found",
_label.string());
return -1;
}
if (ref == this) {
PWRN("Set ref account, %s, self reference not allowed",
_label.string());
return -3;
}
/* establish ref-account relation from targeted CPU-session to us */
_ref = s;
_ref = ref;
_ref->_insert_ref_member(this);
return 0;
}
@ -335,7 +374,7 @@ Cpu_session_component::Cpu_session_component(Rpc_entrypoint *session_ep,
Trace::Source_registry &trace_sources,
char const *args,
Affinity const &affinity,
size_t quota)
size_t const quota)
:
_session_ep(session_ep),
_thread_ep(thread_ep), _pager_ep(pager_ep),
@ -345,7 +384,7 @@ Cpu_session_component::Cpu_session_component(Rpc_entrypoint *session_ep,
/* map affinity to a location within the physical affinity space */
_location(affinity.scale_to(platform()->affinity_space())),
_trace_sources(trace_sources), _ref(0), _used(0), _quota(quota)
_trace_sources(trace_sources), _quota(quota), _ref(0)
{
/* remember session label */
char buf[Session_label::size()];
@ -375,7 +414,7 @@ void Cpu_session_component::_deinit_ref_account()
if (!_ref) { return; }
/* give back our remaining quota to our ref account */
_transfer_back(_quota);
_transfer_quota(_ref, _quota);
/* remove ref-account relation between us and our ref-account */
Cpu_session_component * const orig_ref = _ref;
@ -405,30 +444,53 @@ void Cpu_session_component::_deinit_threads()
}
int Cpu_session_component::_insuff_for_transfer(size_t const q)
void Cpu_session_component::
_update_thread_quota(Cpu_thread_component * const thread) const
{
if (verbose) {
PWRN("Insufficient CPU quota for transfer: %s", _label.string());
PWRN(" avail %zu", _avail());
PWRN(" needed %zu", q);
}
return -3;
thread->platform_thread()->quota(_weight_to_quota(thread->weight()));
}
void Cpu_session_component::_insuff_for_consume(size_t const q)
void Cpu_session_component::_incr_weight(size_t const weight)
{
if (verbose) {
PWRN("Insufficient CPU quota for consumption: %s", _label.string());
PWRN(" avail %zu", _avail());
PWRN(" needed %zu", q);
}
throw Quota_exceeded();
_weight += weight;
if (_quota) { _update_each_thread_quota(); }
}
size_t Cpu_session_component::used() { return _global_to_local(_used); }
size_t Cpu_session_component::quota() { return _global_to_local(_quota); }
void Cpu_session_component::_decr_weight(size_t const weight)
{
_weight -= weight;
if (_quota) { _update_each_thread_quota(); }
}
void Cpu_session_component::_decr_quota(size_t const quota)
{
Lock::Guard lock_guard(_thread_list_lock);
_quota -= quota;
_update_each_thread_quota();
}
void Cpu_session_component::_incr_quota(size_t const quota)
{
Lock::Guard lock_guard(_thread_list_lock);
_quota += quota;
_update_each_thread_quota();
}
void Cpu_session_component::_update_each_thread_quota()
{
Cpu_thread_component * thread = _thread_list.first();
for (; thread; thread = thread->next()) { _update_thread_quota(thread); }
}
size_t
Cpu_session_component::_weight_to_quota(size_t const weight) const {
return (weight * _quota) / _weight; }
/****************************

View File

@ -53,7 +53,7 @@ namespace Genode {
private:
size_t const _quota;
size_t const _weight;
Thread_name const _name;
Platform_thread _platform_thread;
bool _bound; /* pd binding flag */
@ -63,14 +63,27 @@ namespace Genode {
public:
Cpu_thread_component(size_t quota, Session_label const &label,
/**
* Constructor
*
* \param weight weighting regarding the CPU session quota
* \param quota initial quota counter-value of the weight
* \param labal label of the threads session
* \param name name for the thread
* \param priority scheduling priority
* \param utcb user-local UTCB base
* \param sigh initial exception handler
*/
Cpu_thread_component(size_t const weight,
size_t const quota,
Session_label const &label,
Thread_name const &name,
unsigned priority, addr_t utcb,
Signal_context_capability sigh,
unsigned trace_control_index,
Trace::Control &trace_control)
:
_quota(quota), _name(name),
_weight(weight), _name(name),
_platform_thread(quota, name.string(), priority, utcb),
_bound(false), _sigh(sigh),
_trace_control_index(trace_control_index),
@ -88,7 +101,7 @@ namespace Genode {
bool bound() const { return _bound; }
void bound(bool b) { _bound = b; }
Trace::Source *trace_source() { return &_trace_source; }
size_t quota() const { return _quota; }
size_t weight() const { return _weight; }
void sigh(Signal_context_capability sigh)
{
@ -138,9 +151,9 @@ namespace Genode {
* Members for quota accounting
*/
Cpu_session_component * _ref;
size_t _used;
size_t _weight;
size_t _quota;
Cpu_session_component * _ref;
List<Cpu_session_component> _ref_members;
Lock _ref_members_lock;
@ -148,32 +161,22 @@ namespace Genode {
* Utilities for quota accounting
*/
size_t _avail() { return _quota - _used; }
void _incr_weight(size_t const weight);
size_t _local_to_global(size_t const q) const {
return (q * _quota) >> Cpu_session::QUOTA_LIMIT_LOG2; }
void _decr_weight(size_t const weight);
size_t _global_to_local(size_t const q) const {
if (!_quota) { return 0; }
return (q << Cpu_session::QUOTA_LIMIT_LOG2) / _quota; }
size_t _weight_to_quota(size_t const weight) const;
int _insuff_for_transfer(size_t const q);
void _decr_quota(size_t const quota);
void _insuff_for_consume(size_t const q);
void _incr_quota(size_t const quota);
int _transfer_back(size_t const q)
{
_quota -= q;
_ref->_used -= q;
return 0;
}
void _update_thread_quota(Cpu_thread_component *) const;
int _transfer_forth(Cpu_session_component * const s, size_t const q)
{
s->_quota += q;
_used += q;
return 0;
}
void _update_each_thread_quota();
void _transfer_quota(Cpu_session_component * const dst,
size_t const quota);
void _insert_ref_member(Cpu_session_component * const s)
{
@ -261,9 +264,8 @@ namespace Genode {
Dataspace_capability trace_buffer(Thread_capability);
Dataspace_capability trace_policy(Thread_capability);
int ref_account(Cpu_session_capability c);
int transfer_quota(Cpu_session_capability c, size_t q);
size_t used();
size_t quota();
int transfer_quota(Cpu_session_capability, size_t);
Quota quota() override;
};
}

View File

@ -263,14 +263,13 @@ int main()
Ram_session_client(init_ram_session_cap).ref_account(env()->ram_session_cap());
/* create CPU session for init and transfer all of the CPU quota to it */
constexpr size_t cpu_quota = Cpu_session::QUOTA_LIMIT;
static Cpu_session_component
cpu(e, e, rm_root.pager_ep(), &sliced_heap, trace_sources,
"label=\"core\"", Affinity(), cpu_quota);
"label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT);
Cpu_session_capability cpu_cap = core_env()->entrypoint()->manage(&cpu);
Cpu_connection init_cpu("init");
init_cpu.ref_account(cpu_cap);
cpu.transfer_quota(init_cpu, cpu_quota);
cpu.transfer_quota(init_cpu, Cpu_session::quota_lim_upscale(100, 100));
Rm_connection init_rm;

View File

@ -38,10 +38,12 @@ extern "C" {
void *(*_start_routine) (void *);
void *_arg;
enum { WEIGHT = Genode::Cpu_session::DEFAULT_WEIGHT };
pthread(pthread_attr_t attr, void *(*start_routine) (void *),
void *arg, size_t stack_size, char const * name,
Genode::Cpu_session * cpu)
: Thread_base(0, name, stack_size, Type::NORMAL, cpu),
: Thread_base(WEIGHT, name, stack_size, Type::NORMAL, cpu),
_attr(attr),
_start_routine(start_routine),
_arg(arg)

View File

@ -42,6 +42,12 @@ namespace Init {
extern bool config_verbose;
static void warn_insuff_quota(Genode::size_t const avail)
{
if (!config_verbose) { return; }
Genode::printf("Warning: Specified quota exceeds available quota.\n");
Genode::printf(" Proceeding with a quota of %zu.\n", avail);
}
inline long read_priority(Genode::Xml_node start_node)
{
@ -103,13 +109,6 @@ namespace Init {
}
/**
* Return amount of CPU time that is currently unused
*/
static inline Genode::size_t avail_slack_cpu_quota() {
return Genode::env()->cpu_session()->avail(); }
/**
* Return true if service XML node matches the specified service name
*/
@ -395,21 +394,12 @@ class Init::Child : Genode::Child_policy
struct Read_quota
{
void warn_unsuff_quota(Genode::size_t const avail)
{
using namespace Genode;
if (!config_verbose) { return; }
Genode::printf("Warning: Specified quota exceeds available quota.\n");
Genode::printf(" Proceeding with a quota of %zu.\n", avail);
}
Read_quota(Genode::Xml_node start_node,
Genode::size_t & ram_quota,
Genode::size_t & cpu_quota,
Genode::size_t & cpu_quota_pc,
bool & constrain_phys)
{
Genode::Number_of_bytes ram_bytes = 0;
Genode::size_t cpu_percent = 0;
try {
Genode::Xml_node rsc = start_node.sub_node("resource");
@ -419,12 +409,11 @@ class Init::Child : Genode::Child_policy
rsc.attribute("quantum").value(&ram_bytes);
constrain_phys = rsc.attribute("constrain_phys").has_value("yes");
} else if (rsc.attribute("name").has_value("CPU")) {
rsc.attribute("quantum").value(&cpu_percent); }
rsc.attribute("quantum").value(&cpu_quota_pc); }
} catch (...) { }
}
} catch (...) { }
ram_quota = ram_bytes;
cpu_quota = Genode::Cpu_session::pc_to_quota(cpu_percent);
/*
* If the configured RAM quota exceeds our own quota, we donate
@ -435,13 +424,7 @@ class Init::Child : Genode::Child_policy
Genode::size_t const ram_avail = avail_slack_ram_quota();
if (ram_quota > ram_avail) {
ram_quota = ram_avail;
warn_unsuff_quota(ram_avail);
}
Genode::size_t const cpu_avail = avail_slack_cpu_quota();
if (cpu_quota > cpu_avail) {
cpu_quota = cpu_avail;
warn_unsuff_quota(cpu_avail);
warn_insuff_quota(ram_avail);
}
}
};
@ -455,17 +438,19 @@ class Init::Child : Genode::Child_policy
long priority;
Genode::Affinity affinity;
Genode::size_t ram_quota;
Genode::size_t cpu_quota;
Genode::size_t cpu_quota_pc;
bool constrain_phys;
Genode::Ram_connection ram;
Genode::Cpu_connection cpu;
Genode::Rm_connection rm;
inline void transfer_cpu_quota();
Resources(Genode::Xml_node start_node, const char *label,
long prio_levels_log2,
Genode::Affinity::Space const &affinity_space)
:
Read_quota(start_node, ram_quota, cpu_quota, constrain_phys),
Read_quota(start_node, ram_quota, cpu_quota_pc, constrain_phys),
prio_levels_log2(prio_levels_log2),
priority(read_priority(start_node)),
affinity(affinity_space,
@ -487,8 +472,7 @@ class Init::Child : Genode::Child_policy
ram.ref_account(Genode::env()->ram_session_cap());
Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota);
cpu.ref_account(Genode::env()->cpu_session_cap());
Genode::env()->cpu_session()->transfer_quota(cpu.cap(), cpu_quota);
transfer_cpu_quota();
}
} _resources;
@ -801,4 +785,24 @@ class Init::Child : Genode::Child_policy
Genode::Native_pd_args const *pd_args() const { return &_pd_args; }
};
void Init::Child::Resources::transfer_cpu_quota()
{
using Genode::Cpu_session;
using Genode::size_t;
static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
size_t const need = Cpu_session::quota_lim_upscale(cpu_quota_pc, 100);
size_t need_adj;
if (need > avail) {
warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100));
need_adj = Cpu_session::quota_lim_upscale(100, 100);
avail = 0;
} else {
need_adj = Cpu_session::quota_lim_upscale(need, avail);
avail -= need;
}
cpu.ref_account(Genode::env()->cpu_session_cap());
Genode::env()->cpu_session()->transfer_quota(cpu.cap(), need_adj);
}
#endif /* _INCLUDE__INIT__CHILD_H_ */

View File

@ -41,6 +41,8 @@ class Genode::Irq_activation : Thread_base
{
private:
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
int _number;
Irq_connection _connection;
Irq_handler &_handler;
@ -72,7 +74,7 @@ class Genode::Irq_activation : Thread_base
*/
Irq_activation(int irq_number, Irq_handler &handler, size_t stack_size)
:
Thread_base(0, _create_thread_name(irq_number), stack_size),
Thread_base(WEIGHT, _create_thread_name(irq_number), stack_size),
_number(irq_number), _connection(irq_number), _handler(handler),
_dispatcher(_sig_rec, *this, &Irq_activation::_handle)
{

244
repos/os/run/cpu_quota.run Normal file
View File

@ -0,0 +1,244 @@
#
# Check platform
#
# HW is the only kernel that provides appliance of quota to the scheduling.
# On X86, the timer driver uses the PIT with a timeout of max. 54 ms. Thus,
# the driver needs to restart the timer permanently which is a hard job with 6
# high-priority counter-threads in the background. As a consequence, timeouts
# take much longer than requested and the test fails. However, as the PIT
# tends to be replaced by a better timing source, we simply skip X86 for now.
#
#
assert_spec hw
assert_spec arm
#
# Build
#
build "core init drivers/timer test/cpu_quota"
#
# Boot image
#
create_boot_directory
install_config {
<config prio_levels="4">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service><parent/><any-child/></any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="10M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="test-sync">
<resource name="RAM" quantum="10M"/>
<provides><service name="Sync"/></provides>
</start>
<start name="init_1" priority="-1">
<binary name="init"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="10"/>
<config prio_levels="2">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
<service name="Timer"/>
<service name="Sync"/>
</parent-provides>
<default-route>
<any-service><parent/><any-child/></any-service>
</default-route>
<start name="test_slow" priority="-1">
<binary name="test-cpu_quota"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="50"/>
</start>
</config>
</start>
<start name="init_2" priority="-2">
<binary name="init"/>
<resource name="RAM" quantum="100M"/>
<resource name="CPU" quantum="80"/>
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
<service name="Timer"/>
<service name="Sync"/>
</parent-provides>
<default-route>
<any-service><parent/></any-service>
</default-route>
<start name="test_midl">
<binary name="test-cpu_quota"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="25"/>
</start>
<start name="test_fast">
<binary name="test-cpu_quota"/>
<resource name="RAM" quantum="10M"/>
<resource name="CPU" quantum="75"/>
</start>
</config>
</start>
</config>
}
build_boot_image "core init timer test-cpu_quota test-sync"
#
# Execution
#
append qemu_args "-nographic -m 64"
run_genode_until ".*done.*\n.*done.*\n.*done.*\n" 100
#
# Conclusion
#
proc check_counter { name opt cnt total_cnt } {
set err 1
set is [expr double($cnt) / $total_cnt ]
set is_pc [expr double(round($is * 100000)) / 1000]
set opt_pc [expr double(round($opt * 100000)) / 1000]
if {[expr $is > $opt + $err || $is < $opt - $err]} {
puts stderr "Error: $name received $is_pc % of the CPU time."
puts stderr " Should receive $opt_pc %."
exit -1
}
puts "$name: $is_pc % (optimal $opt_pc %)"
}
proc check_quota { name opt_sp quota_sp opt quota } {
if {[expr $quota != $opt]} {
puts stderr "Error: $name has CPU quota of $quota us."
puts stderr " Should have $opt us."
exit -1
}
if {[expr $quota_sp != $opt_sp]} {
puts stderr "Error: $name has CPU quota super-period of $quota_sp us."
puts stderr " Should have $opt_sp us."
exit -1
}
}
regexp {[0-9]+} [regexp -inline {slow. quota [0-9]+} $output] slow_quota
regexp {[0-9]+} [regexp -inline {midl. quota [0-9]+} $output] midl_quota
regexp {[0-9]+} [regexp -inline {fast. quota [0-9]+} $output] fast_quota
regexp {[0-9]+} [regexp -inline {slow. quota super period [0-9]+} $output] slow_quota_sp
regexp {[0-9]+} [regexp -inline {midl. quota super period [0-9]+} $output] midl_quota_sp
regexp {[0-9]+} [regexp -inline {fast. quota super period [0-9]+} $output] fast_quota_sp
#
# We have to consider the rounding errors as the two translations from init to
# core and then from core to the user are distinct.
#
# Slow quota (1000000 * (0x8000 * 5 / 100)) / 0x8000 = 49987
# Slow quota (1000000 * (0x8000 * 20 / 100)) / 0x8000 = 199981
# Slow quota (1000000 * (0x8000 * 60 / 100)) / 0x8000 = 599975
#
check_quota "Slow test" 1000000 $slow_quota_sp 49987 $slow_quota
check_quota "Middle test" 1000000 $midl_quota_sp 199981 $midl_quota
check_quota "Fast test" 1000000 $fast_quota_sp 599975 $fast_quota
regexp {[0-9]+} [regexp -inline {slow. counter A [0-9]+} $output] slow_a_cnt
regexp {[0-9]+} [regexp -inline {midl. counter A [0-9]+} $output] midl_a_cnt
regexp {[0-9]+} [regexp -inline {fast. counter A [0-9]+} $output] fast_a_cnt
regexp {[0-9]+} [regexp -inline {slow. counter B [0-9]+} $output] slow_b_cnt
regexp {[0-9]+} [regexp -inline {midl. counter B [0-9]+} $output] midl_b_cnt
regexp {[0-9]+} [regexp -inline {fast. counter B [0-9]+} $output] fast_b_cnt
set total_cnt [expr $fast_a_cnt + $midl_a_cnt + $slow_a_cnt + $fast_b_cnt + $midl_b_cnt + $slow_b_cnt]
#
# Slow 5.0 % claim + 5.0 % fill = 10 %
# Stage 1
# A 0.5 % claim + 2.5 % fill = 3 %
# B 4.5 % claim + 2.5 % fill = 7 %
# Stage 2
# A 5.0 % claim + 5.0 % fill = 10 %
# Total
# A 3/4 * 3 + 1/4 * 10 = 4.75 %
# A 3/4 * 7 + 1/4 * 0 = 5.25 %
#
check_counter "Slow counter A" 0.0475 $slow_a_cnt $total_cnt
check_counter "Slow counter B" 0.0525 $slow_b_cnt $total_cnt
#
# Middle 20 % claim + 5.0 % fill = 25.0 %
# Stage 1
# A 2 % claim + 2.5 % fill = 4.5 %
# B 18 % claim + 2.5 % fill = 20.5 %
# Stage 2
# A 20 % claim + 5.0 % fill = 25.0 %
# Total
# A 3/4 * 4.5 + 1/4 * 25 = 9.625 %
# A 3/4 * 20.5 + 1/4 * 0 = 15.375 %
#
check_counter "Middle counter A" 0.09625 $midl_a_cnt $total_cnt
check_counter "Middle counter B" 0.15375 $midl_b_cnt $total_cnt
#
# Fast 60 % claim + 5.0 % fill = 65.0 %
# Stage 1
# A 6 % claim + 2.5 % fill = 8.5 %
# B 54 % claim + 2.5 % fill = 56.5 %
# Stage 2
# A 60 % claim + 5.0 % fill = 65.0 %
# Total
# A 3/4 * 8.5 + 1/4 * 65 = 22.625 %
# A 3/4 * 56.5 + 1/4 * 0 = 42.375 %
#
check_counter "Fast counter A" 0.22625 $fast_a_cnt $total_cnt
check_counter "Fast counter B" 0.42375 $fast_b_cnt $total_cnt
puts "Test succeeded"

View File

@ -35,6 +35,8 @@ namespace L4lx {
{
private:
enum { WEIGHT = Genode::Cpu_session::DEFAULT_WEIGHT };
Genode::Lock _lock;
L4_CV void (*_func)(void *data);
unsigned long _data;
@ -50,7 +52,7 @@ namespace L4lx {
Genode::size_t stack_size,
Genode::addr_t vcpu_state,
unsigned cpu_nr)
: Genode::Thread_base(0, str, stack_size),
: Genode::Thread_base(WEIGHT, str, stack_size),
_lock(Genode::Cancelable_lock::LOCKED),
_func(func),
_data(data ? *data : 0),

View File

@ -31,6 +31,8 @@ class Vmm::Vcpu_dispatcher : public T
{
private:
enum { WEIGHT = Genode::Cpu_session::DEFAULT_WEIGHT };
Cap_connection &_cap;
/**
@ -61,7 +63,7 @@ class Vmm::Vcpu_dispatcher : public T
Cpu_session * cpu_session,
Genode::Affinity::Location location)
:
T(0, "vCPU dispatcher", stack_size),
T(WEIGHT, "vCPU dispatcher", stack_size),
_cap(cap)
{
using namespace Genode;

View File

@ -62,8 +62,10 @@ class Vmm::Vcpu_other_pd : public Vmm::Vcpu_thread
{
using namespace Genode;
Thread_capability vcpu_vm = _cpu_session->create_thread(0, "vCPU");
enum { WEIGHT = Cpu_session::DEFAULT_WEIGHT };
Thread_capability vcpu_vm =
_cpu_session->create_thread(WEIGHT, "vCPU");
/* assign thread to protection domain */
_pd_session.bind_thread(vcpu_vm);
@ -104,12 +106,14 @@ class Vmm::Vcpu_other_pd : public Vmm::Vcpu_thread
class Vmm::Vcpu_same_pd : public Vmm::Vcpu_thread, Genode::Thread_base
{
enum { WEIGHT = Genode::Cpu_session::DEFAULT_WEIGHT };
public:
Vcpu_same_pd(size_t stack_size, Cpu_session * cpu_session,
Genode::Affinity::Location location)
:
Thread_base(0, "vCPU", stack_size, Type::NORMAL, cpu_session)
Thread_base(WEIGHT, "vCPU", stack_size, Type::NORMAL, cpu_session)
{
/* release pre-allocated selectors of Thread */
Genode::cap_map()->remove(tid().exc_pt_sel, Nova::NUM_INITIAL_PT_LOG2);

View File

@ -63,10 +63,12 @@ Thread_capability Cpu_session_component::thread_cap(unsigned long lwpid)
}
Thread_capability Cpu_session_component::create_thread(size_t, Cpu_session::Name const &name, addr_t utcb)
Thread_capability
Cpu_session_component::create_thread(size_t weight, Name const &name,
addr_t utcb)
{
Thread_capability thread_cap =
_parent_cpu_session.create_thread(0, name.string(), utcb);
_parent_cpu_session.create_thread(weight, name.string(), utcb);
if (thread_cap.valid()) {
Thread_info *thread_info = new (env()->heap()) Thread_info(thread_cap, new_lwpid++);
@ -239,10 +241,8 @@ Cpu_session_component::~Cpu_session_component()
{
}
size_t Cpu_session_component::quota() { return 0; }
size_t Cpu_session_component::used() { return 0; }
int Cpu_session_component::ref_account(Cpu_session_capability) { return -1; }
int Cpu_session_component::transfer_quota(Cpu_session_capability, size_t) { return -1; }
Cpu_session::Quota Cpu_session_component::quota() { return Quota(); }

View File

@ -78,8 +78,7 @@ class Cpu_session_component : public Rpc_object<Cpu_session>
Dataspace_capability trace_policy(Thread_capability);
int ref_account(Cpu_session_capability c);
int transfer_quota(Cpu_session_capability c, size_t q);
size_t used();
size_t quota();
Quota quota() override;
};
#endif /* _CPU_SESSION_COMPONENT_H_ */

View File

@ -71,7 +71,7 @@ namespace Noux {
** Cpu_session interface **
***************************/
Thread_capability create_thread(size_t, Name const &name,
Thread_capability create_thread(size_t weight, Name const &name,
addr_t utcb)
{
/*
@ -83,7 +83,7 @@ namespace Noux {
while (1);
return Thread_capability();
}
_main_thread = _cpu.create_thread(0, name, utcb);
_main_thread = _cpu.create_thread(weight, name, utcb);
return _main_thread;
}
@ -147,10 +147,13 @@ namespace Noux {
Dataspace_capability trace_policy(Thread_capability thread) {
return _cpu.trace_policy(thread); }
size_t quota() { return 0; }
size_t used() { return 0; }
int ref_account(Cpu_session_capability) { return -1; }
int transfer_quota(Cpu_session_capability, size_t) { return -1; }
Quota quota() override { return _cpu.quota(); }
int ref_account(Cpu_session_capability c) override {
return _cpu.ref_account(c); }
int transfer_quota(Cpu_session_capability c, size_t q) override {
return _cpu.transfer_quota(c, q); }
};
}

View File

@ -50,3 +50,4 @@ vbox_auto_win8
tz_vmm
vmm
bomb
cpu_quota