b1a6db090f
* Perform sanity check before calculating memory available to rump kernel * Distinguish between 'Hard_context' and 'Hard_context_thread', so no dead threads (that will not be started) are created * Install signal-handler for memory-resource requests. This way the fs server will not block forever when the quota is execeeded and a resource requests fails, leaving the rump kernels to perform necessary actions Fixes #1127
317 lines
5.9 KiB
C++
317 lines
5.9 KiB
C++
/**
|
|
* \brief Rump hypercall-interface implementation
|
|
* \author Sebastian Sumpf
|
|
* \author Josef Soentgen
|
|
* \date 2013-12-06
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2013-2014 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.
|
|
*/
|
|
|
|
#include "sched.h"
|
|
|
|
#include <base/env.h>
|
|
#include <base/printf.h>
|
|
#include <base/sleep.h>
|
|
#include <os/timed_semaphore.h>
|
|
#include <util/allocator_fap.h>
|
|
#include <util/string.h>
|
|
|
|
|
|
extern "C" void wait_for_continue();
|
|
enum { SUPPORTED_RUMP_VERSION = 17 };
|
|
|
|
static bool verbose = false;
|
|
|
|
/* upcalls to rump kernel */
|
|
struct rumpuser_hyperup _rump_upcalls;
|
|
|
|
|
|
/********************
|
|
** Initialization **
|
|
********************/
|
|
|
|
int rumpuser_init(int version, const struct rumpuser_hyperup *hyp)
|
|
{
|
|
PDBG("RUMP ver: %d", version);
|
|
if (version != SUPPORTED_RUMP_VERSION) {
|
|
PERR("Unsupported rump-kernel version (%d) - supported is %d)",
|
|
version, SUPPORTED_RUMP_VERSION);
|
|
return -1;
|
|
}
|
|
|
|
_rump_upcalls = *hyp;
|
|
|
|
/*
|
|
* Start 'Timeout_thread' so it does not get constructed concurrently (which
|
|
* causes one thread to spin in cxa_guard_aqcuire), making emulation *really*
|
|
* slow
|
|
*/
|
|
Genode::Timeout_thread::alarm_timer();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*************
|
|
** Threads **
|
|
*************/
|
|
|
|
static Hard_context _main_thread(0);
|
|
|
|
static Hard_context *myself()
|
|
{
|
|
Hard_context *h = dynamic_cast<Hard_context *>(Genode::Thread_base::myself());
|
|
return h ? h : &_main_thread;
|
|
}
|
|
|
|
|
|
Timer::Connection *Hard_context::timer()
|
|
{
|
|
static Timer::Connection _timer;
|
|
return &_timer;
|
|
}
|
|
|
|
|
|
void rumpuser_curlwpop(int enum_rumplwpop, struct lwp *l)
|
|
{
|
|
Hard_context *h = myself();
|
|
switch (enum_rumplwpop) {
|
|
case RUMPUSER_LWP_CREATE:
|
|
case RUMPUSER_LWP_DESTROY:
|
|
break;
|
|
case RUMPUSER_LWP_SET:
|
|
h->set_lwp(l);
|
|
break;
|
|
case RUMPUSER_LWP_CLEAR:
|
|
h->set_lwp(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
struct lwp * rumpuser_curlwp(void)
|
|
{
|
|
return myself()->get_lwp();
|
|
}
|
|
|
|
|
|
int rumpuser_thread_create(func f, void *arg, const char *name,
|
|
int mustjoin, int priority, int cpui_dx, void **cookie)
|
|
{
|
|
static int count = 0;
|
|
|
|
if (mustjoin)
|
|
*cookie = (void *)++count;
|
|
|
|
new (Genode::env()->heap()) Hard_context_thread(name, f, arg, mustjoin ? count : 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int errno;
|
|
void rumpuser_seterrno(int e) { errno = e; }
|
|
|
|
|
|
/*************************
|
|
** Parameter retrieval **
|
|
*************************/
|
|
|
|
int rumpuser_getparam(const char *name, void *buf, size_t buflen)
|
|
{
|
|
enum { RESERVE_MEM = 2U * 1024 * 1024 };
|
|
|
|
/* support one cpu */
|
|
PDBG("%s", name);
|
|
if (!Genode::strcmp(name, "_RUMPUSER_NCPU")) {
|
|
Genode::strncpy((char *)buf, "1", 2);
|
|
return 0;
|
|
}
|
|
|
|
/* return out cool host name */
|
|
if (!Genode::strcmp(name, "_RUMPUSER_HOSTNAME")) {
|
|
Genode::strncpy((char *)buf, "rump4genode", 12);
|
|
return 0;
|
|
}
|
|
|
|
if (!Genode::strcmp(name, "RUMP_MEMLIMIT")) {
|
|
|
|
/* leave 2 MB for the Genode */
|
|
size_t rump_ram = Genode::env()->ram_session()->avail();
|
|
|
|
if (rump_ram <= RESERVE_MEM) {
|
|
PERR("Insufficient quota need left: %zu < %u bytes", rump_ram, RESERVE_MEM);
|
|
return -1;
|
|
}
|
|
|
|
rump_ram -= RESERVE_MEM;
|
|
|
|
/* convert to string */
|
|
Genode::snprintf((char *)buf, buflen, "%zu", rump_ram);
|
|
PERR("Asserting rump kernel %zu KB of RAM", rump_ram / 1024);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*************
|
|
** Console **
|
|
*************/
|
|
|
|
void rumpuser_putchar(int ch)
|
|
{
|
|
static unsigned char buf[256];
|
|
static int count = 0;
|
|
|
|
buf[count++] = (unsigned char)ch;
|
|
|
|
if (ch == '\n') {
|
|
buf[count] = 0;
|
|
int nlocks;
|
|
if (myself() != &_main_thread)
|
|
rumpkern_unsched(&nlocks, 0);
|
|
|
|
PLOG("rump: %s", buf);
|
|
|
|
if (myself() != &_main_thread)
|
|
rumpkern_sched(nlocks, 0);
|
|
|
|
count = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/************
|
|
** Memory **
|
|
************/
|
|
|
|
struct Allocator_policy
|
|
{
|
|
static int block()
|
|
{
|
|
int nlocks;
|
|
|
|
if (myself() != &_main_thread)
|
|
rumpkern_unsched(&nlocks, 0);
|
|
return nlocks;
|
|
}
|
|
|
|
static void unblock(int nlocks)
|
|
{
|
|
if (myself() != &_main_thread)
|
|
rumpkern_sched(nlocks, 0);
|
|
}
|
|
};
|
|
|
|
|
|
typedef Allocator::Fap<128 * 1024 * 1024, Allocator_policy> Rump_alloc;
|
|
static Genode::Lock _alloc_lock;
|
|
|
|
static Rump_alloc* allocator()
|
|
{
|
|
static Rump_alloc _fap(true);
|
|
return &_fap;
|
|
}
|
|
|
|
int rumpuser_malloc(size_t len, int alignment, void **memp)
|
|
{
|
|
Genode::Lock::Guard guard(_alloc_lock);
|
|
|
|
int align = Genode::log2(alignment);
|
|
*memp = allocator()->alloc(len, align);
|
|
|
|
if (verbose)
|
|
PWRN("ALLOC: p: %p, s: %zx, a: %d", *memp, len, align);
|
|
|
|
|
|
return *memp ? 0 : -1;
|
|
}
|
|
|
|
|
|
void rumpuser_free(void *mem, size_t len)
|
|
{
|
|
Genode::Lock::Guard guard(_alloc_lock);
|
|
|
|
allocator()->free(mem, len);
|
|
|
|
if (verbose)
|
|
PWRN("FREE: p: %p, s: %zx", mem, len);
|
|
}
|
|
|
|
|
|
/************
|
|
** Clocks **
|
|
************/
|
|
|
|
int rumpuser_clock_gettime(int enum_rumpclock, int64_t *sec, long *nsec)
|
|
{
|
|
Hard_context *h = myself();
|
|
unsigned long t = h->timer()->elapsed_ms();
|
|
*sec = (int64_t)t / 1000;
|
|
*nsec = (t % 1000) * 1000;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int rumpuser_clock_sleep(int enum_rumpclock, int64_t sec, long nsec)
|
|
{
|
|
int nlocks;
|
|
unsigned int msec = 0;
|
|
|
|
Timer::Connection *timer = myself()->timer();
|
|
|
|
rumpkern_unsched(&nlocks, 0);
|
|
switch (enum_rumpclock) {
|
|
case RUMPUSER_CLOCK_RELWALL:
|
|
msec = sec * 1000 + nsec / (1000*1000UL);
|
|
break;
|
|
case RUMPUSER_CLOCK_ABSMONO:
|
|
msec = timer->elapsed_ms();
|
|
msec = ((sec * 1000) + (nsec / (1000 * 1000))) - msec;
|
|
break;
|
|
}
|
|
|
|
timer->msleep(msec);
|
|
rumpkern_sched(nlocks, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*****************
|
|
** Random pool **
|
|
*****************/
|
|
|
|
int rumpuser_getrandom(void *buf, size_t buflen, int flags, size_t *retp)
|
|
{
|
|
Timer::Connection *timer = myself()->timer();
|
|
|
|
uint8_t *rndbuf;
|
|
for (*retp = 0, rndbuf = (uint8_t *)buf; *retp < buflen; (*retp)++) {
|
|
*rndbuf++ = timer->elapsed_ms() & 0xff;
|
|
timer->msleep(timer->elapsed_ms() & 0xf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**********
|
|
** Exit **
|
|
**********/
|
|
|
|
void genode_exit(int) __attribute__((noreturn));
|
|
|
|
void rumpuser_exit(int status)
|
|
{
|
|
if (status == RUMPUSER_PANIC)
|
|
PERR("Rump panic");
|
|
|
|
genode_exit(status);
|
|
}
|