nova: use kernel branch with quota handling

Fixes #1601
This commit is contained in:
Alexander Boettcher 2015-06-09 19:33:53 +02:00 committed by Christian Helmuth
parent eafe5e81e3
commit df662cc2f3
13 changed files with 491 additions and 74 deletions

View File

@ -67,7 +67,8 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t syscall_1(Syscall s, uint8_t flags, unsigned sel, mword_t p1)
inline uint8_t syscall_1(Syscall s, uint8_t flags, mword_t sel, mword_t p1,
mword_t * p2 = 0)
{
mword_t status = eax(s, flags, sel);
@ -78,9 +79,10 @@ namespace Nova {
" mov (%%esp), %%edx;"
" sysenter;"
"1:"
: "+a" (status)
: "D" (p1)
: "ecx", "edx");
: "+a" (status), "+D" (p1)
:
: "ecx", "edx", "memory");
if (p2) *p2 = p1;
return status;
}
@ -180,7 +182,7 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t call(unsigned pt)
{
return syscall_0(NOVA_CALL, 0, pt);
return syscall_1(NOVA_CALL, 0, pt, 0);
}
@ -199,17 +201,34 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t create_pd(unsigned pd0, unsigned pd, Crd crd)
inline uint8_t create_pd(unsigned pd0, unsigned pd, Crd crd,
unsigned short lower_limit, unsigned upper_limit)
{
return syscall_2(NOVA_CREATE_PD, 0, pd0, pd, crd.value());
return syscall_3(NOVA_CREATE_PD, 0, pd0, pd, crd.value(),
upper_limit << 16 | lower_limit);
}
/**
* Create an EC.
*
* \param ec two selectors - ec && ec + 1
* First selector must be unused and second selector is
* either unused or must be a valid portal selector.
* The thread will call this portal if the PD it runs in runs
* out of kernel memory.
* \param pd selector of PD the EC will created in
* \param cpu CPU number the EC will run on
* \param utcb PD local address where the UTCB of the EC will be appear
* \param esp initial stack address
* \param evt base selector for all exception portals of the EC
* \param global if true - thread requires a SC to be runnable
* if false - thread is runnable solely if it receives a IPC
* (worker thread)
*/
ALWAYS_INLINE
inline uint8_t create_ec(unsigned ec, unsigned pd,
mword_t cpu, mword_t utcb,
mword_t esp, mword_t evt,
bool global = 0)
inline uint8_t create_ec(mword_t ec, mword_t pd, mword_t cpu, mword_t utcb,
mword_t esp, mword_t evt, bool global = false)
{
return syscall_4(NOVA_CREATE_EC, global, ec, pd,
(cpu & 0xfff) | (utcb & ~0xfff),
@ -265,32 +284,39 @@ namespace Nova {
}
/**
* Revoke memory, capabilities or i/o ports from a PD
*
* \param crd describes region and type of resource
* \param self also revoke from source PD iif self == true
* \param remote if true the 'pd' parameter below is used, otherwise
* current PD is used as source PD
* \param pd selector describing remote PD
* \param sm SM selector which gets an up() by the kernel if the
* memory of the current revoke invocation gets freed up
* (end of RCU period)
*/
ALWAYS_INLINE
inline uint8_t revoke(Crd crd, bool self = true)
inline uint8_t revoke(Crd crd, bool self = true, bool remote = false,
mword_t pd = 0, mword_t sm = 0)
{
return syscall_1(NOVA_REVOKE, self, 0, crd.value());
uint8_t flags = self ? 0x1 : 0;
if (remote)
flags |= 0x2;
mword_t value_crd = crd.value();
return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd);
}
ALWAYS_INLINE
inline uint8_t lookup(Crd &crd)
{
mword_t status = eax(NOVA_LOOKUP, 0, 0);
mword_t raw = crd.value();
asm volatile (" mov %%esp, %%ecx;"
" call 0f;"
"0:"
" addl $(1f-0b), (%%esp);"
" mov (%%esp), %%edx;"
" sysenter;"
"1:"
: "+a" (status), "+D" (raw)
:
: "ecx", "edx", "memory");
crd = Crd(raw);
return status;
mword_t crd_r;
uint8_t res = syscall_1(NOVA_LOOKUP, 0, 0, crd.value(), &crd_r);
crd = Crd(crd_r);
return res;
}
@ -308,6 +334,21 @@ namespace Nova {
}
ALWAYS_INLINE
inline uint8_t pd_ctrl(mword_t pd_src, Pd_op op, mword_t pd_dst,
mword_t transfer)
{
return syscall_5(NOVA_PD_CTRL, op, pd_src, pd_dst, transfer);
}
ALWAYS_INLINE
inline uint8_t pd_ctrl_debug(mword_t pd, mword_t &limit, mword_t &usage)
{
return syscall_5(NOVA_PD_CTRL, Pd_op::PD_DEBUG, pd, limit, usage);
}
ALWAYS_INLINE
inline uint8_t assign_pci(mword_t pd, mword_t mem, mword_t rid)
{

View File

@ -136,7 +136,7 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t call(mword_t pt)
{
return syscall_0(NOVA_CALL, 0, pt);
return syscall_1(NOVA_CALL, 0, pt, 0);
}
@ -156,17 +156,34 @@ namespace Nova {
ALWAYS_INLINE
inline uint8_t create_pd(mword_t pd0, mword_t pd, Crd crd)
inline uint8_t create_pd(mword_t pd0, mword_t pd, Crd crd,
unsigned lower_limit, unsigned long upper_limit)
{
return syscall_2(NOVA_CREATE_PD, 0, pd0, pd, crd.value());
return syscall_3(NOVA_CREATE_PD, 0, pd0, pd, crd.value(),
upper_limit << 32 | lower_limit);
}
/**
* Create an EC.
*
* \param ec two selectors - ec && ec + 1
* First selector must be unused and second selector is
* either unused or must be a valid portal selector.
* The thread will call this portal if the PD it runs in runs
* out of kernel memory.
* \param pd selector of PD the EC will created in
* \param cpu CPU number the EC will run on
* \param utcb PD local address where the UTCB of the EC will be appear
* \param esp initial stack address
* \param evt base selector for all exception portals of the EC
* \param global if true - thread requires a SC to be runnable
* if false - thread is runnable solely if it receives a IPC
* (worker thread)
*/
ALWAYS_INLINE
inline uint8_t create_ec(mword_t ec, mword_t pd,
mword_t cpu, mword_t utcb,
mword_t esp, mword_t evt,
bool global = 0)
inline uint8_t create_ec(mword_t ec, mword_t pd, mword_t cpu, mword_t utcb,
mword_t esp, mword_t evt, bool global = false)
{
return syscall_4(NOVA_CREATE_EC, global, ec, pd,
(cpu & 0xfff) | (utcb & ~0xfff),
@ -222,10 +239,29 @@ namespace Nova {
}
/**
* Revoke memory, capabilities or i/o ports from a PD
*
* \param crd describes region and type of resource
* \param self also revoke from source PD iif self == true
* \param remote if true the 'pd' parameter below is used, otherwise
* current PD is used as source PD
* \param pd selector describing remote PD
* \param sm SM selector which gets an up() by the kernel if the
* memory of the current revoke invocation gets freed up
* (end of RCU period)
*/
ALWAYS_INLINE
inline uint8_t revoke(Crd crd, bool self = true)
inline uint8_t revoke(Crd crd, bool self = true, bool remote = false,
mword_t pd = 0, mword_t sm = 0)
{
return syscall_1(NOVA_REVOKE, self, 0, crd.value());
uint8_t flags = self ? 0x1 : 0;
if (remote)
flags |= 0x2;
mword_t value_crd = crd.value();
return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd);
}
@ -265,6 +301,20 @@ namespace Nova {
}
ALWAYS_INLINE
inline uint8_t pd_ctrl(mword_t pd_src, Pd_op op, mword_t pd_dst, mword_t transfer)
{
return syscall_5(NOVA_PD_CTRL, op, pd_src, pd_dst, transfer);
}
ALWAYS_INLINE
inline uint8_t pd_ctrl_debug(mword_t pd, mword_t &limit, mword_t &usage)
{
return syscall_5(NOVA_PD_CTRL, Pd_op::PD_DEBUG, pd, limit, usage);
}
ALWAYS_INLINE
inline uint8_t assign_pci(mword_t pd, mword_t mem, mword_t rid)
{

View File

@ -63,6 +63,7 @@ namespace Nova {
NOVA_SM_CTRL = 0xc,
NOVA_ASSIGN_PCI = 0xd,
NOVA_ASSIGN_GSI = 0xe,
NOVA_PD_CTRL = 0xf,
};
/**
@ -79,6 +80,7 @@ namespace Nova {
NOVA_INV_FEATURE = 6,
NOVA_INV_CPU = 7,
NOVA_INVD_DEVICE_ID = 8,
NOVA_PD_OOM = 9,
};
/**
@ -160,6 +162,11 @@ namespace Nova {
*/
enum Ec_op { EC_RECALL = 0U, EC_YIELD = 1U, EC_DONATE_SC = 2U, EC_RESCHEDULE = 3U };
/**
* Pd operations
*/
enum Pd_op { TRANSFER_QUOTA = 0U, PD_DEBUG = 2U };
class Descriptor
{

View File

@ -1 +1 @@
50a7e018fac5bd0bbb7c43ccb9681beae1c93db8
9f2aef990ef7fcf148a53e63ba9bebdb0e3fff10

View File

@ -2,9 +2,9 @@ LICENSE := GPLv2
VERSION := git
DOWNLOADS := nova.git
# r9 branch
URL(nova) := https://github.com/alex-ab/NOVA.git
# r9_pae branch
REV(nova) := 902ccd998fddbf140bb6f87b75d7c9df97e6380f
REV(nova) := 7de4ddd3b7af33affdd8f36aa997b20b52bd2ef6
DIR(nova) := src/kernel/nova
PATCHES := $(wildcard $(REP_DIR)/patches/*.patch)

View File

@ -22,7 +22,8 @@
enum {
ECHO_STACK_SIZE = 512,
ECHO_GLOBAL = false,
ECHO_EXC_BASE = 0
ECHO_EXC_BASE = 0,
ECHO_LOG2_COUNT = 1 /* selector for EC and out-of-memory portal */
};
@ -61,7 +62,7 @@ static void echo_reply()
Echo::Echo(Genode::addr_t utcb_addr)
:
_ec_sel(Genode::cap_map()->insert()),
_ec_sel(Genode::cap_map()->insert(ECHO_LOG2_COUNT)),
_pt_sel(Genode::cap_map()->insert()),
_utcb((Nova::Utcb *)utcb_addr)
{

View File

@ -120,11 +120,14 @@ namespace Genode {
Thread_capability _thread_cap;
Exception_handlers _exceptions;
addr_t _pd;
void _copy_state(Nova::Utcb * utcb);
addr_t sel_pt_cleanup() { return _selectors; }
addr_t sel_sm_notify() { return _selectors + 1; }
addr_t sel_sm_block() { return _selectors + 2; }
addr_t sel_pt_cleanup() const { return _selectors; }
addr_t sel_sm_notify() const { return _selectors + 1; }
addr_t sel_sm_block() const { return _selectors + 2; }
addr_t sel_oom_portal() const { return _selectors + 3; }
__attribute__((regparm(1)))
static void _page_fault_handler(addr_t pager_obj);
@ -138,6 +141,9 @@ namespace Genode {
__attribute__((regparm(1)))
static void _recall_handler(addr_t pager_obj);
__attribute__((regparm(3)))
static void _oom_handler(addr_t, addr_t, addr_t);
public:
const Affinity::Location location;
@ -159,6 +165,13 @@ namespace Genode {
_exception_sigh = sigh;
}
/**
* Assign PD selector to PD
*/
void assign_pd(addr_t pd_sel) { _pd = pd_sel; }
addr_t pd_sel() const { return _pd; }
void dump_kernel_quota_usage(Pager_object * = (Pager_object *)~0UL);
void exception(uint8_t exit_id);
/**
@ -301,6 +314,41 @@ namespace Genode {
{
_client_exc_vcpu = cap_map()->insert(Nova::NUM_INITIAL_VCPU_PT_LOG2);
}
/**
* Portal called by thread that causes a out of memory in kernel.
*/
addr_t get_oom_portal();
enum Policy {
STOP = 1,
UPGRADE_CORE_TO_DST = 2,
UPGRADE_PREFER_SRC_TO_DST = 3,
};
enum Oom {
SEND = 1, REPLY = 2, SELF = 4,
SRC_CORE_PD = ~0UL, SRC_PD_UNKNOWN = 0,
};
/**
* Implements policy on how to react on out of memory in kernel.
*
* Used solely inside core. On Genode core creates all the out
* of memory portals per EC. If the PD of a EC runs out of kernel
* memory it causes a OOM portal traversal, which is handled
* by the pager object of the causing thread.
*
* /param pd_sel PD selector from where to transfer kernel memory
* resources. The PD of this pager_object is the
* target PD.
* /param pd debug feature - string of PD (transfer_from)
* /param thread debug feature - string of EC (transfer_from)
*/
uint8_t handle_oom(addr_t pd_sel = SRC_CORE_PD,
const char * pd = "core",
const char * thread = "unknown",
Policy = Policy::UPGRADE_CORE_TO_DST);
};
/**
@ -338,6 +386,12 @@ namespace Genode {
*/
void ep(Pager_entrypoint *ep) { _ep = ep; }
/*
* Used for diagnostic/debugging purposes
* - see Pager_object::dump_kernel_quota_usage
*/
Pager_object * pager_head();
/**
* Thread interface
*/

View File

@ -48,8 +48,9 @@ namespace Genode {
char _name[Thread_base::Context::NAME_LEN];
addr_t _sel_ec() const { return _id_base; }
addr_t _sel_sc() const { return _id_base + 1; }
addr_t _sel_ec() const { return _id_base; }
addr_t _sel_pt_oom() const { return _id_base + 1; }
addr_t _sel_sc() const { return _id_base + 2; }
/* convenience function to access _feature variable */
inline bool is_main_thread() { return _features & MAIN_THREAD; }

View File

@ -25,6 +25,8 @@
#include <nova/syscalls.h>
#include <nova_util.h> /* map_local */
static bool verbose_oom = false;
using namespace Genode;
using namespace Nova;
@ -409,14 +411,19 @@ void Pager_object::cleanup_call()
static uint8_t create_portal(addr_t pt, addr_t pd, addr_t ec, Mtd mtd,
addr_t eip, addr_t localname)
addr_t eip, Pager_object * oom_handler)
{
uint8_t res = create_pt(pt, pd, ec, mtd, eip);
addr_t const badge_localname = reinterpret_cast<addr_t>(oom_handler);
uint8_t res;
do {
res = create_pt(pt, pd, ec, mtd, eip);
} while (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK == oom_handler->handle_oom());
if (res != NOVA_OK)
return res;
res = pt_ctrl(pt, localname);
res = pt_ctrl(pt, badge_localname);
if (res == NOVA_OK)
revoke(Obj_crd(pt, 0, Obj_crd::RIGHT_PT_CTRL));
else
@ -443,8 +450,7 @@ void Exception_handlers::register_handler(Pager_object *obj, Mtd mtd,
/* compiler generates instance of exception entry if not specified */
addr_t entry = func ? (addr_t)func : (addr_t)(&_handler<EV>);
uint8_t res = create_portal(obj->exc_pt_sel_client() + EV,
__core_pd_sel, ec_sel, mtd, entry,
reinterpret_cast<addr_t>(obj));
__core_pd_sel, ec_sel, mtd, entry, obj);
if (res != Nova::NOVA_OK)
throw Rm_session::Invalid_thread();
}
@ -494,6 +500,29 @@ Exception_handlers::Exception_handlers(Pager_object *obj)
******************/
void Pager_object::dump_kernel_quota_usage(Pager_object *obj)
{
if (obj == (Pager_object *)~0UL) {
unsigned use_cpu = location.xpos();
obj = pager_threads[use_cpu]->pager_head();
PINF("-- kernel memory usage of Genode PDs --");
}
if (!obj)
return;
addr_t limit = 0; addr_t usage = 0;
Nova::pd_ctrl_debug(obj->pd_sel(), limit, usage);
char const * thread_name = reinterpret_cast<char const *>(obj->badge());
PINF("pd=0x%lx pager=%p thread='%s' limit=0x%lx usage=0x%lx",
obj->pd_sel(), obj, thread_name, limit, usage);
dump_kernel_quota_usage(static_cast<Pager_object *>(obj->child(Genode::Avl_node_base::LEFT)));
dump_kernel_quota_usage(static_cast<Pager_object *>(obj->child(Genode::Avl_node_base::RIGHT)));
}
Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
:
_badge(badge),
@ -552,8 +581,7 @@ Pager_object::Pager_object(unsigned long badge, Affinity::Location location)
/* create portal for final cleanup call used during destruction */
res = create_portal(sel_pt_cleanup(), pd_sel, ec_sel, Mtd(0),
reinterpret_cast<addr_t>(_invoke_handler),
reinterpret_cast<addr_t>(this));
reinterpret_cast<addr_t>(_invoke_handler), this);
if (res != Nova::NOVA_OK) {
PERR("could not create pager cleanup portal, error = %u\n", res);
throw Rm_session::Invalid_thread();
@ -592,6 +620,210 @@ Pager_object::~Pager_object()
cap_map()->remove(_client_exc_vcpu, NUM_INITIAL_VCPU_PT_LOG2, false);
}
uint8_t Pager_object::handle_oom(addr_t transfer_from,
char const * src_pd, char const * src_thread,
enum Pager_object::Policy policy)
{
const char * dst_pd = "unknown";
const char * dst_thread = reinterpret_cast<char *>(badge());
enum { QUOTA_TRANSFER_PAGES = 2 };
if (transfer_from == SRC_CORE_PD)
transfer_from = __core_pd_sel;
/* request current kernel quota usage of target pd */
addr_t limit_before = 0, usage_before = 0;
Nova::pd_ctrl_debug(pd_sel(), limit_before, usage_before);
if (verbose_oom) {
addr_t limit_source = 0, usage_source = 0;
/* request current kernel quota usage of source pd */
Nova::pd_ctrl_debug(transfer_from, limit_source, usage_source);
PINF("oom - '%s:%s' (%lu/%lu) - transfer %u pages from '%s:%s' (%lu/%lu)",
dst_pd, dst_thread,
usage_before, limit_before, QUOTA_TRANSFER_PAGES,
src_pd, src_thread, usage_source, limit_source);
}
uint8_t res = Nova::NOVA_PD_OOM;
if (transfer_from != pd_sel()) {
/* upgrade quota */
uint8_t res = Nova::pd_ctrl(transfer_from, Pd_op::TRANSFER_QUOTA,
pd_sel(), QUOTA_TRANSFER_PAGES);
if (res == Nova::NOVA_OK)
return res;
}
/* retry upgrade using core quota if policy permits */
if (policy == UPGRADE_PREFER_SRC_TO_DST) {
if (transfer_from != __core_pd_sel) {
res = Nova::pd_ctrl(__core_pd_sel, Pd_op::TRANSFER_QUOTA,
pd_sel(), QUOTA_TRANSFER_PAGES);
if (res == Nova::NOVA_OK)
return res;
}
}
PWRN("kernel memory quota upgrade failed - trigger memory free up for "
"causing '%s:%s' - donator is '%s:%s', policy=%u",
dst_pd, dst_thread, src_pd, src_thread, policy);
/* if nothing helps try to revoke memory */
enum { REMOTE_REVOKE = true, PD_SELF = true };
Mem_crd crd_all(0, ~0U, Rights(true, true, true));
Nova::revoke(crd_all, PD_SELF, REMOTE_REVOKE, pd_sel(), sel_sm_block());
/* re-request current kernel quota usage of target pd */
addr_t limit_after = 0, usage_after = 0;
Nova::pd_ctrl_debug(pd_sel(), limit_after, usage_after);
/* if we could free up memory we continue */
if (usage_after < usage_before)
return Nova::NOVA_OK;
/*
* There is still the chance that memory gets freed up, but one has to
* wait until RCU period is over. If we are in the pager code, we can
* instruct the kernel to block the faulting client thread during the reply
* syscall. If we are in a normal (non-pagefault) RPC service call,
* we can't block. The caller of this function can decide based on
* the return value what to do and whether blocking is ok.
*/
return Nova::NOVA_PD_OOM;
}
void Pager_object::_oom_handler(addr_t pager_dst, addr_t pager_src,
addr_t reason)
{
if (sizeof(void *) == 4) {
/* On 32 bit edx and ecx as second and third regparm parameter is not
* available. It is used by the kernel internally to store ip/sp.
*/
asm volatile ("" : "=D" (pager_src));
asm volatile ("" : "=S" (reason));
}
Thread_base * myself = Thread_base::myself();
Utcb * utcb = reinterpret_cast<Utcb *>(myself->utcb());
Pager_object * obj_dst = reinterpret_cast<Pager_object *>(pager_dst);
Pager_object * obj_src = reinterpret_cast<Pager_object *>(pager_src);
/* Policy used if the Process of the paged thread runs out of memory */
enum Policy policy = Policy::UPGRADE_CORE_TO_DST;
/* check assertions - cases that should not happen on Genode@Nova */
enum { NO_OOM_PT = ~0UL, EC_OF_PT_OOM_OUTSIDE_OF_CORE };
/* all relevant (user) threads should have a OOM PT */
bool assert = pager_dst == NO_OOM_PT;
/*
* PT OOM solely created by core and they have to point to the pager
* thread inside core.
*/
assert |= pager_dst == EC_OF_PT_OOM_OUTSIDE_OF_CORE;
/*
* This pager thread does solely reply to IPC calls - it should never
* cause OOM during the sending phase of a IPC.
*/
assert |= ((reason & (SELF | SEND)) == (SELF | SEND));
/*
* This pager thread should never send words (untyped items) - it just
* answers page faults by typed items (memory mappings).
*/
assert |= utcb->msg_words();
if (assert) {
PERR("unknown OOM case - stop core pager thread");
utcb->set_msg_word(0);
reply(myself->stack_top(), myself->tid().exc_pt_sel + Nova::SM_SEL_EC);
}
/* be strict in case of the -strict- STOP policy - stop causing thread */
if (policy == STOP) {
PERR("PD has insufficient kernel memory left - stop thread");
utcb->set_msg_word(0);
reply(myself->stack_top(), obj_dst->sel_sm_block());
}
char const * src_pd = "core";
char const * src_thread = "pager";
addr_t transfer_from = SRC_CORE_PD;
switch (pager_src) {
case SRC_PD_UNKNOWN:
/* should not happen on Genode - we create and know every PD in core */
PERR("Unknown PD has insufficient kernel memory left - stop thread");
utcb->set_msg_word(0);
reply(myself->stack_top(), myself->tid().exc_pt_sel + Nova::SM_SEL_EC);
case SRC_CORE_PD:
/* core PD -> other PD, which has insufficient kernel resources */
if (!(reason & SELF)) {
/* case that src thread != this thread in core */
src_thread = "unknown";
utcb->set_msg_word(0);
}
transfer_from = __core_pd_sel;
break;
default:
/* non core PD -> non core PD */
utcb->set_msg_word(0);
if (pager_src == pager_dst || policy == UPGRADE_CORE_TO_DST)
transfer_from = __core_pd_sel;
else {
/* delegation of items between different PDs */
src_pd = "unknown";
src_thread = reinterpret_cast<char *>(obj_src->badge());
transfer_from = obj_src->pd_sel();
}
}
uint8_t res = obj_dst->handle_oom(transfer_from, src_pd, src_thread,
policy);
if (res == Nova::NOVA_OK)
/* handling succeeded - continue with original IPC */
reply(myself->stack_top());
/* transfer nothing */
utcb->set_msg_word(0);
if (res != Nova::NOVA_PD_OOM)
PERR("Upgrading kernel memory failed, policy %u, error %u "
"- stop thread finally", policy, res);
/* else: caller will get blocked until RCU period is over */
/* block caller in semaphore */
reply(myself->stack_top(), obj_dst->sel_sm_block());
}
addr_t Pager_object::get_oom_portal()
{
addr_t const pt_oom = sel_oom_portal();
unsigned const use_cpu = location.xpos();
addr_t const ec_sel = pager_threads[use_cpu]->tid().ec_sel;
uint8_t res = create_portal(pt_oom, __core_pd_sel, ec_sel, Mtd(0),
reinterpret_cast<addr_t>(_oom_handler), this);
if (res == Nova::NOVA_OK)
return pt_oom;
PERR("creating portal for out of memory notification failed");
return 0;
}
/**********************
** Pager activation **
@ -615,6 +847,9 @@ Pager_activation_base::Pager_activation_base(const char *name, size_t stack_size
void Pager_activation_base::entry() { }
Pager_object * Pager_activation_base::pager_head() {
return _ep ? _ep->first() : nullptr; }
/**********************
** Pager entrypoint **
**********************/

View File

@ -184,7 +184,7 @@ static void init_core_page_fault_handler()
EXC_BASE = 0
};
addr_t ec_sel = cap_map()->insert();
addr_t ec_sel = cap_map()->insert(1);
uint8_t ret = create_ec(ec_sel, __core_pd_sel, boot_cpu(),
CORE_PAGER_UTCB_ADDR, core_pager_stack_top(),

View File

@ -62,24 +62,37 @@ int Platform_thread::start(void *ip, void *sp)
return -2;
}
addr_t const pt_oom = _pager->get_oom_portal();
if (!pt_oom || map_local((Utcb *)Thread_base::myself()->utcb(),
Obj_crd(pt_oom, 0), Obj_crd(_sel_pt_oom(), 0))) {
PERR("setup of out-of-memory notification portal - failed");
return -8;
}
if (!is_main_thread()) {
addr_t initial_sp = reinterpret_cast<addr_t>(sp);
addr_t utcb = is_vcpu() ? 0 : round_page(initial_sp);
addr_t const initial_sp = reinterpret_cast<addr_t>(sp);
addr_t const utcb = is_vcpu() ? 0 : round_page(initial_sp);
if (_sel_exc_base == Native_thread::INVALID_INDEX) {
PERR("exception base not specified");
return -3;
}
_pager->assign_pd(_pd->pd_sel());
/* ip == 0 means that caller will use the thread as worker */
bool thread_global = ip;
uint8_t res = create_ec(_sel_ec(), _pd->pd_sel(), _location.xpos(),
utcb, initial_sp, _sel_exc_base, thread_global);
if (res != Nova::NOVA_OK) {
PERR("creation of new thread failed %u", res);
return -4;
}
uint8_t res;
do {
res = create_ec(_sel_ec(), _pd->pd_sel(), _location.xpos(),
utcb, initial_sp, _sel_exc_base, thread_global);
if (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK != _pager->handle_oom()) {
_pd->assign_pd(Native_thread::INVALID_INDEX);
PERR("creation of new thread failed %u", res);
return -4;
}
} while (res != Nova::NOVA_OK);
if (!thread_global) {
_features |= WORKER;
@ -127,10 +140,15 @@ int Platform_thread::start(void *ip, void *sp)
Obj_crd::RIGHT_SM_UP | Obj_crd::RIGHT_SM_DOWN;
unsigned pts = is_vcpu() ? NUM_INITIAL_VCPU_PT_LOG2 : NUM_INITIAL_PT_LOG2;
enum { KEEP_FREE_PAGES_NOT_AVAILABLE_FOR_UPGRADE = 2, UPPER_LIMIT_PAGES = 32 };
Obj_crd initial_pts(_sel_exc_base, pts, rights);
uint8_t res = create_pd(pd_sel, pd_core_sel, initial_pts);
uint8_t res = create_pd(pd_sel, pd_core_sel, initial_pts,
KEEP_FREE_PAGES_NOT_AVAILABLE_FOR_UPGRADE, UPPER_LIMIT_PAGES);
if (res != NOVA_OK) {
PERR("create_pd returned %d", res);
_pager->dump_kernel_quota_usage();
goto cleanup_pd;
}
@ -151,10 +169,14 @@ int Platform_thread::start(void *ip, void *sp)
_pager->client_set_ec(_sel_ec());
_pager->initial_eip((addr_t)ip);
_pager->initial_esp((addr_t)sp);
_pager->assign_pd(pd_sel);
do {
/* let the thread run */
res = create_sc(_sel_sc(), pd_sel, _sel_ec(),
Qpd(Qpd::DEFAULT_QUANTUM, _priority));
} while (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK == _pager->handle_oom());
/* let the thread run */
res = create_sc(_sel_sc(), pd_sel, _sel_ec(),
Qpd(Qpd::DEFAULT_QUANTUM, _priority));
if (res != NOVA_OK) {
/*
* Reset pd cap since thread got not running and pd cap will
@ -164,6 +186,7 @@ int Platform_thread::start(void *ip, void *sp)
_pager->client_set_ec(Native_thread::INVALID_INDEX);
_pager->initial_eip(0);
_pager->initial_esp(0);
_pager->assign_pd(Native_thread::INVALID_INDEX);
PERR("create_sc returned %d", res);
goto cleanup_ec;
@ -208,8 +231,12 @@ void Platform_thread::resume()
using namespace Nova;
if (!is_worker()) {
uint8_t res = create_sc(_sel_sc(), _pd->pd_sel(), _sel_ec(),
Qpd(Qpd::DEFAULT_QUANTUM, _priority));
uint8_t res;
do {
res = create_sc(_sel_sc(), _pd->pd_sel(), _sel_ec(),
Qpd(Qpd::DEFAULT_QUANTUM, _priority));
} while (res == Nova::NOVA_PD_OOM && Nova::NOVA_OK == _pager->handle_oom());
if (res == NOVA_OK) return;
}
@ -312,7 +339,7 @@ unsigned long long Platform_thread::execution_time() const
Platform_thread::Platform_thread(const char *name, unsigned prio, int thread_id)
:
_pd(0), _pager(0), _id_base(cap_map()->insert(1)),
_pd(0), _pager(0), _id_base(cap_map()->insert(2)),
_sel_exc_base(Native_thread::INVALID_INDEX), _location(boot_cpu(), 0, 0, 0),
_features(0),
_priority(Cpu_session::scale_priority(Nova::Qpd::DEFAULT_PRIORITY, prio))
@ -332,6 +359,6 @@ Platform_thread::~Platform_thread()
using namespace Nova;
/* free ec and sc caps */
revoke(Obj_crd(_id_base, 1));
cap_map()->remove(_id_base, 1, false);
revoke(Obj_crd(_id_base, 2));
cap_map()->remove(_id_base, 2, false);
}

View File

@ -13,7 +13,7 @@ CC_WARN = -Wall -Wextra -Waggregate-return -Wcast-align -Wcast-qual \
-Wpointer-arith -Wredundant-decls -Wshadow -Wwrite-strings \
-Wabi -Wctor-dtor-privacy -Wno-non-virtual-dtor \
-Wold-style-cast -Woverloaded-virtual -Wsign-promo \
-Wframe-larger-than=64 -Wlogical-op -Wstrict-null-sentinel \
-Wframe-larger-than=112 -Wlogical-op -Wstrict-null-sentinel \
-Wstrict-overflow=5 -Wvolatile-register-var
CC_OPT += -pipe \
-fdata-sections -fomit-frame-pointer -freg-struct-return \

View File

@ -53,3 +53,4 @@ bomb
cpu_quota
stdcxx
nic_loopback
platform