diff --git a/os/run/resource_yield.run b/os/run/resource_yield.run
new file mode 100644
index 0000000000..8b6ad71a2e
--- /dev/null
+++ b/os/run/resource_yield.run
@@ -0,0 +1,40 @@
+build "core init test/resource_yield drivers/timer"
+
+create_boot_directory
+
+install_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"
diff --git a/os/src/test/resource_yield/main.cc b/os/src/test/resource_yield/main.cc
new file mode 100644
index 0000000000..6583e79cd7
--- /dev/null
+++ b/os/src/test/resource_yield/main.cc
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/****************
+ ** 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::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_chunks;
+ Timer::Connection _timer;
+ Genode::Signal_receiver _sig_rec;
+ Genode::Signal_dispatcher _periodic_timeout_dispatcher;
+ Genode::Signal_dispatcher _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(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("");
+ }
+
+ 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 '' 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();
+ }
+}
diff --git a/os/src/test/resource_yield/target.mk b/os/src/test/resource_yield/target.mk
new file mode 100644
index 0000000000..fa32b1c469
--- /dev/null
+++ b/os/src/test/resource_yield/target.mk
@@ -0,0 +1,3 @@
+TARGET = test-resource_yield
+SRC_CC = main.cc
+LIBS = base config
diff --git a/tool/autopilot.list b/tool/autopilot.list
index 751db82f93..2f05c85e92 100644
--- a/tool/autopilot.list
+++ b/tool/autopilot.list
@@ -30,3 +30,4 @@ affinity
mp_server
seoul-auto
resource_request
+resource_yield