mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-07 11:27:29 +00:00
Test for yielding resources
This commit is contained in:
parent
9cf677cb0a
commit
d78ba43f64
40
os/run/resource_yield.run
Normal file
40
os/run/resource_yield.run
Normal file
@ -0,0 +1,40 @@
|
||||
build "core init test/resource_yield drivers/timer"
|
||||
|
||||
create_boot_directory
|
||||
|
||||
install_config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="RAM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="RM"/>
|
||||
<service name="CAP"/>
|
||||
<service name="PD"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="SIGNAL"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="test-resource_yield">
|
||||
<resource name="RAM" quantum="16M"/>
|
||||
<provides> <service name="ROM" /> </provides>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
build_boot_image "core init timer test-resource_yield"
|
||||
|
||||
append qemu_args "-nographic -m 64"
|
||||
|
||||
run_genode_until {--- test-resource_yield finished ---\s*\n} 50
|
||||
|
||||
puts "Test succeeded"
|
357
os/src/test/resource_yield/main.cc
Normal file
357
os/src/test/resource_yield/main.cc
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* \brief Test for yielding resources
|
||||
* \author Norman Feske
|
||||
* \date 2013-10-05
|
||||
*
|
||||
* This test exercises the protocol between a parent and child, which is used
|
||||
* by the parent to regain resources from a child subsystem.
|
||||
*
|
||||
* The program acts in either one of two roles, the parent or the child. The
|
||||
* role is determined by reading a config argument.
|
||||
*
|
||||
* The child periodically allocates chunks of RAM until its RAM quota is
|
||||
* depleted. Once it observes a yield request from the parent, however, it
|
||||
* cooperatively releases as much resources as requested by the parent.
|
||||
*
|
||||
* The parent wait a while to give the child the chance to allocate RAM. It
|
||||
* then sends a yield request and waits for a response. When getting the
|
||||
* response, it validates whether the child complied to the request or not.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 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 <util/arg_string.h>
|
||||
#include <base/printf.h>
|
||||
#include <base/signal.h>
|
||||
#include <cap_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <os/config.h>
|
||||
#include <os/slave.h>
|
||||
|
||||
|
||||
/****************
|
||||
** Child role **
|
||||
****************/
|
||||
|
||||
/**
|
||||
* The child eats more and more RAM. However, when receiving a yield request,
|
||||
* it releases the requested amount of resources.
|
||||
*/
|
||||
class Child
|
||||
{
|
||||
private:
|
||||
|
||||
typedef Genode::size_t size_t;
|
||||
|
||||
struct Ram_chunk : Genode::List<Ram_chunk>::Element
|
||||
{
|
||||
size_t const size;
|
||||
|
||||
Genode::Ram_dataspace_capability ds_cap;
|
||||
|
||||
Ram_chunk(size_t size)
|
||||
:
|
||||
size(size),
|
||||
ds_cap(Genode::env()->ram_session()->alloc(size))
|
||||
{ }
|
||||
|
||||
~Ram_chunk()
|
||||
{
|
||||
Genode::env()->ram_session()->free(ds_cap);
|
||||
}
|
||||
};
|
||||
|
||||
bool const _expand;
|
||||
Genode::List<Ram_chunk> _ram_chunks;
|
||||
Timer::Connection _timer;
|
||||
Genode::Signal_receiver _sig_rec;
|
||||
Genode::Signal_dispatcher<Child> _periodic_timeout_dispatcher;
|
||||
Genode::Signal_dispatcher<Child> _yield_dispatcher;
|
||||
unsigned long const _period_ms;
|
||||
|
||||
void _dispatch_periodic_timeout(unsigned);
|
||||
void _dispatch_yield(unsigned);
|
||||
|
||||
void _schedule_next_timeout()
|
||||
{
|
||||
_timer.trigger_once(_period_ms*1000);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Child();
|
||||
void main();
|
||||
};
|
||||
|
||||
|
||||
void Child::_dispatch_periodic_timeout(unsigned)
|
||||
{
|
||||
size_t const chunk_size = 1024*1024;
|
||||
|
||||
if (Genode::env()->ram_session()->avail() < chunk_size) {
|
||||
|
||||
if (_expand) {
|
||||
PLOG("quota consumed, request additional resources");
|
||||
|
||||
/*
|
||||
* The attempt to allocate RAM will result in a resource request to
|
||||
* the parent. The resource request will block until the parent
|
||||
* responds.
|
||||
*/
|
||||
|
||||
} else {
|
||||
PLOG("consumed all of our quota, stop allocating");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform allocation and remember chunk in list */
|
||||
_ram_chunks.insert(new (Genode::env()->heap()) Ram_chunk(chunk_size));
|
||||
|
||||
PLOG("allocated chunk of %zd KiB", chunk_size / 1024);
|
||||
|
||||
_schedule_next_timeout();
|
||||
}
|
||||
|
||||
|
||||
void Child::_dispatch_yield(unsigned)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/* request yield request arguments */
|
||||
Parent::Resource_args const args = env()->parent()->yield_request();
|
||||
|
||||
PLOG("yield request: %s", args.string());
|
||||
|
||||
size_t const requested_ram_quota =
|
||||
Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0);
|
||||
|
||||
/* free chunks of RAM to comply with the request */
|
||||
size_t released_quota = 0;
|
||||
while (released_quota < requested_ram_quota) {
|
||||
|
||||
Ram_chunk *chunk = _ram_chunks.first();
|
||||
if (!chunk) {
|
||||
PWRN("no chunk left to release");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t const chunk_size = chunk->size;
|
||||
_ram_chunks.remove(chunk);
|
||||
destroy(env()->heap(), chunk);
|
||||
released_quota += chunk_size;
|
||||
|
||||
PLOG("released chunk of %zd bytes", chunk_size);
|
||||
}
|
||||
|
||||
/* acknowledge yield request */
|
||||
env()->parent()->yield_response();
|
||||
|
||||
_schedule_next_timeout();
|
||||
}
|
||||
|
||||
|
||||
static inline unsigned long read_period_ms_from_config()
|
||||
{
|
||||
unsigned long period_ms = 500;
|
||||
if (Genode::config()->xml_node().has_attribute("period_ms"))
|
||||
Genode::config()->xml_node().attribute("period_ms").value(&period_ms);
|
||||
return period_ms;
|
||||
}
|
||||
|
||||
|
||||
Child::Child()
|
||||
:
|
||||
_expand(Genode::config()->xml_node().has_attribute("expand")
|
||||
&& Genode::config()->xml_node().attribute("expand").has_value("yes")),
|
||||
_periodic_timeout_dispatcher(_sig_rec, *this,
|
||||
&Child::_dispatch_periodic_timeout),
|
||||
_yield_dispatcher(_sig_rec, *this,
|
||||
&Child::_dispatch_yield),
|
||||
_period_ms(read_period_ms_from_config())
|
||||
{
|
||||
/* register yield signal handler */
|
||||
Genode::env()->parent()->yield_sigh(_yield_dispatcher);
|
||||
|
||||
/* register timeout signal handler and schedule periodic timeouts */
|
||||
_timer.sigh(_periodic_timeout_dispatcher);
|
||||
|
||||
_schedule_next_timeout();
|
||||
}
|
||||
|
||||
|
||||
void Child::main()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
for (;;) {
|
||||
Signal sig = _sig_rec.wait_for_signal();
|
||||
|
||||
Signal_dispatcher_base *dispatcher =
|
||||
dynamic_cast<Signal_dispatcher_base *>(sig.context());
|
||||
|
||||
if (dispatcher)
|
||||
dispatcher->dispatch(sig.num());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
** Parent role **
|
||||
*****************/
|
||||
|
||||
/**
|
||||
* The parent grants resource requests as long as it has free resources.
|
||||
* Once in a while, it politely requests the child to yield resources.
|
||||
*/
|
||||
class Parent : Genode::Slave_policy
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Return singleton entrypoint instance
|
||||
*
|
||||
* The entrypoint cannot be a regular member because we need to pass
|
||||
* it into the constructor of the 'Slave_policy' base class.
|
||||
*/
|
||||
static Genode::Rpc_entrypoint &_entrypoint();
|
||||
|
||||
typedef Genode::size_t size_t;
|
||||
|
||||
size_t const slave_quota = 10*1024*1024;
|
||||
|
||||
Genode::Slave _slave = { _entrypoint(), *this, slave_quota };
|
||||
|
||||
Timer::Connection _timer;
|
||||
|
||||
Genode::Lock _yield_blockade;
|
||||
|
||||
void _print_status()
|
||||
{
|
||||
PLOG("quota: %zd KiB used: %zd KiB",
|
||||
_slave.ram().quota() / 1024,
|
||||
_slave.ram().used() / 1024);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Parent()
|
||||
:
|
||||
Genode::Slave_policy("test-resource_yield", _entrypoint(),
|
||||
Genode::env()->ram_session()),
|
||||
_yield_blockade(Genode::Lock::LOCKED)
|
||||
{
|
||||
configure("<config child=\"yes\" />");
|
||||
}
|
||||
|
||||
int main();
|
||||
|
||||
/****************************
|
||||
** Slave_policy interface **
|
||||
****************************/
|
||||
|
||||
char const **_permitted_services() const
|
||||
{
|
||||
static char const *services[] = { "RM", "SIGNAL", "LOG", "Timer" };
|
||||
return services;
|
||||
}
|
||||
|
||||
void yield_response()
|
||||
{
|
||||
_yield_blockade.unlock();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
Genode::Rpc_entrypoint &Parent::_entrypoint()
|
||||
{
|
||||
using namespace Genode;
|
||||
static Cap_connection cap;
|
||||
size_t const stack_size = sizeof(addr_t)*1024;
|
||||
static Rpc_entrypoint ep(&cap, stack_size, "ep", false);
|
||||
return ep;
|
||||
}
|
||||
|
||||
|
||||
int Parent::main()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
_entrypoint().activate();
|
||||
|
||||
/* perform the test three times */
|
||||
for (unsigned j = 0; j < 3; j++) {
|
||||
|
||||
/* wait five seconds and observe growth of resource usage */
|
||||
for (unsigned i = 0; i < 5; i++) {
|
||||
_timer.msleep(1000);
|
||||
_print_status();
|
||||
}
|
||||
|
||||
/* remember quantum of resources used by the child */
|
||||
size_t const used_prior_yield = _slave.ram().used();
|
||||
|
||||
/* issue yield request */
|
||||
Genode::Parent::Resource_args yield_args("ram_quota=5M");
|
||||
_slave.yield(yield_args);
|
||||
|
||||
/*
|
||||
* Synchronously wait for a yield response. Note that a careful parent
|
||||
* would never trust its child to comply to the yield request.
|
||||
*/
|
||||
PLOG("wait for yield response");
|
||||
_yield_blockade.lock();
|
||||
PLOG("got yield response");
|
||||
|
||||
_print_status();
|
||||
|
||||
/* validate that the amount of yielded resources matches the request */
|
||||
size_t const used_after_yield = _slave.ram().used();
|
||||
if (used_after_yield + 5*1024*1024 > used_prior_yield) {
|
||||
PERR("child has not yielded enough resources");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("--- test-resource_yield finished ---\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
** Main program **
|
||||
******************/
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
/*
|
||||
* Read value '<config child="" />' attribute to decide whether to perform
|
||||
* the child or the parent role.
|
||||
*/
|
||||
bool const is_child = config()->xml_node().has_attribute("child")
|
||||
&& config()->xml_node().attribute("child").has_value("yes");
|
||||
|
||||
if (is_child) {
|
||||
printf("--- test-resource_yield child role started ---\n");
|
||||
static ::Child child;
|
||||
child.main();
|
||||
return -1; /* the child should never reach this point */
|
||||
} else {
|
||||
printf("--- test-resource_yield parent role started ---\n");
|
||||
static ::Parent parent;
|
||||
return parent.main();
|
||||
}
|
||||
}
|
3
os/src/test/resource_yield/target.mk
Normal file
3
os/src/test/resource_yield/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = test-resource_yield
|
||||
SRC_CC = main.cc
|
||||
LIBS = base config
|
@ -30,3 +30,4 @@ affinity
|
||||
mp_server
|
||||
seoul-auto
|
||||
resource_request
|
||||
resource_yield
|
||||
|
Loading…
x
Reference in New Issue
Block a user