genode/repos/base-nova/src/base/server/server.cc

253 lines
6.4 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief NOVA-specific support code for the server-side RPC API
* \author Norman Feske
* \author Sebastian Sumpf
* \author Alexander Boettcher
2011-12-22 16:19:25 +01:00
* \date 2010-01-13
*/
/*
2013-01-10 21:44:47 +01:00
* Copyright (C) 2010-2013 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* 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 <base/printf.h>
#include <base/rpc_server.h>
#include <base/env.h>
2011-12-22 16:19:25 +01:00
/* NOVA includes */
#include <nova/syscalls.h>
#include <nova/util.h>
2011-12-22 16:19:25 +01:00
using namespace Genode;
2013-01-11 23:10:21 +01:00
2011-12-22 16:19:25 +01:00
/***********************
** Server entrypoint **
***********************/
Untyped_capability Rpc_entrypoint::_manage(Rpc_object_base *obj)
{
using namespace Nova;
Untyped_capability ec_cap, ep_cap;
2011-12-22 16:19:25 +01:00
/* _ec_sel is invalid until thread gets started */
if (tid().ec_sel != Native_thread::INVALID_INDEX)
ec_cap = Native_capability(tid().ec_sel);
else
ec_cap = _thread_cap;
2011-12-22 16:19:25 +01:00
ep_cap = _cap_session->alloc(ec_cap, (addr_t)_activation_entry);
2011-12-22 16:19:25 +01:00
/* add server object to object pool */
obj->cap(ep_cap);
2011-12-22 16:19:25 +01:00
insert(obj);
/* return entrypoint capability */
return ep_cap;
2011-12-22 16:19:25 +01:00
}
void Rpc_entrypoint::_dissolve(Rpc_object_base *obj)
{
/* de-announce object from cap_session */
_cap_session->free(obj->cap());
/* avoid any incoming IPC */
Nova::revoke(Nova::Obj_crd(obj->cap().local_name(), 0), true);
2011-12-22 16:19:25 +01:00
/* make sure nobody is able to find this object */
remove_locked(obj);
2011-12-22 16:19:25 +01:00
/*
2013-01-11 23:10:21 +01:00
* The activation may execute a blocking operation in a dispatch function.
* Before resolving the corresponding object, we need to ensure that it is
* no longer used by an activation. Therefore, we to need cancel an
* eventually blocking operation and let the activation leave the context
* of the object.
2011-12-22 16:19:25 +01:00
*/
_leave_server_object(obj);
/* wait until nobody is inside dispatch */
obj->acquire();
}
2011-12-22 16:19:25 +01:00
void Rpc_entrypoint::_activation_entry()
{
/* retrieve portal id from eax/rdi */
#ifdef __x86_64__
addr_t id_pt; asm volatile ("" : "=D" (id_pt));
#else
addr_t id_pt; asm volatile ("" : "=a" (id_pt));
#endif
2011-12-22 16:19:25 +01:00
Rpc_entrypoint *ep = static_cast<Rpc_entrypoint *>(Thread_base::myself());
/* delay start if requested so */
if (ep->_curr_obj) {
ep->_delay_start.lock();
ep->_delay_start.unlock();
}
/* required to decrease ref count of capability used during last reply */
ep->_snd_buf.snd_reset();
/* prepare ipc server object (copying utcb content to message buffer */
2011-12-22 16:19:25 +01:00
Ipc_server srv(&ep->_snd_buf, &ep->_rcv_buf);
ep->_rcv_buf.post_ipc(reinterpret_cast<Nova::Utcb *>(ep->utcb()));
2011-12-22 16:19:25 +01:00
int opcode = 0;
srv >> IPC_WAIT >> opcode;
/* set default return value */
srv.ret(ERR_INVALID_OBJECT);
/* atomically lookup and lock referenced object */
ep->_curr_obj = ep->lookup_and_lock(id_pt);
if (!ep->_curr_obj) {
2013-01-11 23:10:21 +01:00
/*
* Badge is used to suppress error message solely.
* It's non zero during cleanup call of an
* rpc_object_base object, see _leave_server_object.
*/
if (!srv.badge())
PERR("could not look up server object, "
" return from call id_pt=%lx",
id_pt);
} else {
2011-12-22 16:19:25 +01:00
/* dispatch request */
try { srv.ret(ep->_curr_obj->dispatch(opcode, srv, srv)); }
catch (Blocking_canceled) { }
2011-12-22 16:19:25 +01:00
Rpc_object_base * tmp = ep->_curr_obj;
ep->_curr_obj = 0;
tmp->release();
}
2011-12-22 16:19:25 +01:00
/* if we can't setup receive window, die in order to recognize the issue */
if (!ep->_rcv_buf.prepare_rcv_window((Nova::Utcb *)ep->utcb()))
/* printf doesn't work here since for IPC also prepare_rcv_window is used */
nova_die();
2011-12-22 16:19:25 +01:00
srv << IPC_REPLY;
}
void Rpc_entrypoint::entry()
{
/*
* Thread entry is not used for activations on NOVA
*/
}
void Rpc_entrypoint::_leave_server_object(Rpc_object_base *)
{
using namespace Nova;
Utcb *utcb = reinterpret_cast<Utcb *>(Thread_base::myself()->utcb());
/* don't call ourself */
if (utcb == reinterpret_cast<Utcb *>(this->utcb()))
return;
/*
* Required outside of core. E.g. launchpad needs it to forcefully kill
* a client which blocks on a session opening request where the service
* is not up yet.
*/
cancel_blocking();
utcb->msg[0] = 0xdead;
utcb->set_msg_word(1);
if (uint8_t res = call(_cap.local_name()))
PERR("%8p - could not clean up entry point of thread 0x%p - res %u",
utcb, this->utcb(), res);
}
2011-12-22 16:19:25 +01:00
void Rpc_entrypoint::_block_until_cap_valid() { }
void Rpc_entrypoint::activate()
{
/*
* In contrast to a normal thread, a server activation is created at
* construction time. However, it executes no code because processing
* time is always provided by the caller of the server activation. To
* delay the processing of requests until the 'activate' function is
* called, we grab the '_delay_start' lock on construction and release it
* here.
2011-12-22 16:19:25 +01:00
*/
_delay_start.unlock();
2011-12-22 16:19:25 +01:00
}
Rpc_entrypoint::Rpc_entrypoint(Cap_session *cap_session, size_t stack_size,
const char *name, bool start_on_construction,
Affinity::Location location)
2011-12-22 16:19:25 +01:00
:
thread API & CPU session: accounting of CPU quota In the init configuration one can configure the donation of CPU time via 'resource' tags that have the attribute 'name' set to "CPU" and the attribute 'quantum' set to the percentage of CPU quota that init shall donate. The pattern is the same as when donating RAM quota. ! <start name="test"> ! <resource name="CPU" quantum="75"/> ! </start> This would cause init to try donating 75% of its CPU quota to the child "test". Init and core do not preserve CPU quota for their own requirements by default as it is done with RAM quota. The CPU quota that a process owns can be applied through the thread constructor. The constructor has been enhanced by an argument that indicates the percentage of the programs CPU quota that shall be granted to the new thread. So 'Thread(33, "test")' would cause the backing CPU session to try to grant 33% of the programs CPU quota to the thread "test". By now, the CPU quota of a thread can't be altered after construction. Constructing a thread with CPU quota 0 doesn't mean the thread gets never scheduled but that the thread has no guaranty to receive CPU time. Such threads have to live with excess CPU time. Threads that already existed in the official repositories of Genode were adapted in the way that they receive a quota of 0. This commit also provides a run test 'cpu_quota' in base-hw (the only kernel that applies the CPU-quota scheme currently). The test basically runs three threads with different physical CPU quota. The threads simply count for 30 seconds each and the test then checks wether the counter values relate to the CPU-quota distribution. fix #1275
2014-10-16 11:15:46 +02:00
Thread_base(0, name, stack_size),
_curr_obj(start_on_construction ? 0 : (Rpc_object_base *)~0UL),
_delay_start(Lock::LOCKED),
2011-12-22 16:19:25 +01:00
_cap_session(cap_session)
{
/* when not running in core set the affinity via cpu session */
if (_tid.ec_sel == Native_thread::INVALID_INDEX) {
/* place new thread on the specified CPU */
if (location.valid())
_cpu_session->affinity(_thread_cap, location);
/* magic value evaluated by thread_nova.cc to start a local thread */
_tid.ec_sel = Native_thread::INVALID_INDEX - 1;
} else {
/* tell affinity CPU in 'core' via stack */
reinterpret_cast<Affinity::Location *>(stack_base())[0] = location;
}
2011-12-22 16:19:25 +01:00
/* required to create a 'local' EC */
Thread_base::start();
/* create cleanup portal */
_cap = _cap_session->alloc(Native_capability(_tid.ec_sel),
(addr_t)_activation_entry);
if (!_cap.valid())
throw Cpu_session::Thread_creation_failed();
/* prepare portal receive window of new thread */
if (!_rcv_buf.prepare_rcv_window((Nova::Utcb *)&_context->utcb))
throw Cpu_session::Thread_creation_failed();
2011-12-22 16:19:25 +01:00
if (start_on_construction)
activate();
}
Rpc_entrypoint::~Rpc_entrypoint()
{
typedef Object_pool<Rpc_object_base> Pool;
if (Pool::first()) {
PWRN("Object pool not empty in %s", __func__);
/* dissolve all objects - objects are not destroyed! */
while (Rpc_object_base *obj = Pool::first())
_dissolve(obj);
}
if (!_cap.valid())
return;
/* de-announce object from cap_session */
_cap_session->free(_cap);
}