mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 06:33:31 +00:00
parent
eafe5e81e3
commit
df662cc2f3
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -1 +1 @@
|
||||
50a7e018fac5bd0bbb7c43ccb9681beae1c93db8
|
||||
9f2aef990ef7fcf148a53e63ba9bebdb0e3fff10
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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; }
|
||||
|
@ -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 **
|
||||
**********************/
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -53,3 +53,4 @@ bomb
|
||||
cpu_quota
|
||||
stdcxx
|
||||
nic_loopback
|
||||
platform
|
||||
|
Loading…
Reference in New Issue
Block a user