mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-19 23:53:55 +00:00
base: redesign object pool using lambda interface
Instead of returning pointers to locked objects via a lookup function, the new object pool implementation restricts object access to functors resp. lambda expressions that are applied to the objects within the pool itself. Fix #884 Fix #1658
This commit is contained in:
committed by
Christian Helmuth
parent
555835c95b
commit
458b4d6fc4
@ -15,35 +15,34 @@ using namespace Genode;
|
||||
|
||||
void Cpu_session_component::thread_id(Thread_capability thread_cap, int pid, int tid)
|
||||
{
|
||||
Object_pool<Cpu_thread_component>::Guard
|
||||
thread(_thread_ep->lookup_and_lock(thread_cap));
|
||||
if (!thread) return;
|
||||
|
||||
thread->platform_thread()->thread_id(pid, tid);
|
||||
_thread_ep->apply(thread_cap, [&] (Cpu_thread_component *thread) {
|
||||
if (thread) thread->platform_thread()->thread_id(pid, tid); });
|
||||
}
|
||||
|
||||
|
||||
Untyped_capability Cpu_session_component::server_sd(Thread_capability thread_cap)
|
||||
{
|
||||
Object_pool<Cpu_thread_component>::Guard
|
||||
thread(_thread_ep->lookup_and_lock(thread_cap));
|
||||
if (!thread) return Untyped_capability();
|
||||
auto lambda = [] (Cpu_thread_component *thread) {
|
||||
if (!thread) return Untyped_capability();
|
||||
|
||||
enum { DUMMY_LOCAL_NAME = 0 };
|
||||
typedef Native_capability::Dst Dst;
|
||||
return Untyped_capability(Dst(thread->platform_thread()->server_sd()),
|
||||
DUMMY_LOCAL_NAME);
|
||||
enum { DUMMY_LOCAL_NAME = 0 };
|
||||
typedef Native_capability::Dst Dst;
|
||||
return Untyped_capability(Dst(thread->platform_thread()->server_sd()),
|
||||
DUMMY_LOCAL_NAME);
|
||||
};
|
||||
return _thread_ep->apply(thread_cap, lambda);
|
||||
}
|
||||
|
||||
|
||||
Untyped_capability Cpu_session_component::client_sd(Thread_capability thread_cap)
|
||||
{
|
||||
Object_pool<Cpu_thread_component>::Guard
|
||||
thread(_thread_ep->lookup_and_lock(thread_cap));
|
||||
if (!thread) return Untyped_capability();
|
||||
auto lambda = [] (Cpu_thread_component *thread) {
|
||||
if (!thread) return Untyped_capability();
|
||||
|
||||
enum { DUMMY_LOCAL_NAME = 0 };
|
||||
typedef Native_capability::Dst Dst;
|
||||
return Untyped_capability(Dst(thread->platform_thread()->client_sd()),
|
||||
DUMMY_LOCAL_NAME);
|
||||
enum { DUMMY_LOCAL_NAME = 0 };
|
||||
typedef Native_capability::Dst Dst;
|
||||
return Untyped_capability(Dst(thread->platform_thread()->client_sd()),
|
||||
DUMMY_LOCAL_NAME);
|
||||
};
|
||||
return _thread_ep->apply(thread_cap, lambda);
|
||||
}
|
||||
|
@ -40,16 +40,15 @@ namespace Genode {
|
||||
*/
|
||||
Thread_capability thread_cap() { return _thread_cap; } const
|
||||
void thread_cap(Thread_capability cap) { _thread_cap = cap; }
|
||||
|
||||
/* required by lookup_and_lock, provided by Object_pool::Entry normally */
|
||||
void release() { }
|
||||
};
|
||||
|
||||
struct Pager_entrypoint
|
||||
{
|
||||
Pager_entrypoint(Cap_session *) { }
|
||||
|
||||
Pager_object *lookup_and_lock(Pager_capability) { return 0; }
|
||||
template <typename FUNC>
|
||||
auto apply(Pager_capability, FUNC f) -> decltype(f(nullptr)) {
|
||||
return f(nullptr); }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
namespace Genode {
|
||||
|
||||
class Dataspace_component;
|
||||
class Pd_session_component : public Rpc_object<Linux_pd_session, Pd_session_component>
|
||||
{
|
||||
private:
|
||||
@ -39,6 +40,8 @@ namespace Genode {
|
||||
Parent_capability _parent;
|
||||
Rpc_entrypoint *_ds_ep;
|
||||
|
||||
void _start(Dataspace_component *ds);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
|
@ -305,6 +305,98 @@ static const char *get_env(const char *key)
|
||||
** PD session interface **
|
||||
**************************/
|
||||
|
||||
void Pd_session_component::_start(Dataspace_component *ds)
|
||||
{
|
||||
const char *tmp_filename = "temporary_executable_elf_dataspace_file_for_execve";
|
||||
|
||||
if (!ds) {
|
||||
PERR("could not lookup binary, aborted PD startup");
|
||||
return; /* XXX reflect error to client */
|
||||
}
|
||||
|
||||
/* we need 's' on stack to make it an lvalue with an lvalue member we use the pointer to */
|
||||
Linux_dataspace::Filename s = ds->fname();
|
||||
const char *filename = s.buf;
|
||||
|
||||
/*
|
||||
* In order to be executable via 'execve', a program must be represented as
|
||||
* a file on the Linux file system. However, this is not the case for a
|
||||
* plain RAM dataspace that contains an ELF image. In this case, we copy
|
||||
* the dataspace content into a temporary file whose path is passed to
|
||||
* 'execve()'.
|
||||
*/
|
||||
if (strcmp(filename, "") == 0) {
|
||||
|
||||
filename = tmp_filename;
|
||||
|
||||
int tmp_binary_fd = lx_open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);
|
||||
if (tmp_binary_fd < 0) {
|
||||
PERR("Could not create file '%s'", filename);
|
||||
return; /* XXX reflect error to client */
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
int num_bytes = 0;
|
||||
while ((num_bytes = lx_read(ds->fd().dst().socket, buf, sizeof(buf))) != 0)
|
||||
lx_write(tmp_binary_fd, buf, num_bytes);
|
||||
|
||||
lx_close(tmp_binary_fd);
|
||||
}
|
||||
|
||||
/* pass parent capability as environment variable to the child */
|
||||
enum { ENV_STR_LEN = 256 };
|
||||
static char envbuf[5][ENV_STR_LEN];
|
||||
Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu",
|
||||
_parent.local_name());
|
||||
Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s",
|
||||
get_env("DISPLAY"));
|
||||
Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s",
|
||||
get_env("HOME"));
|
||||
Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s",
|
||||
get_env("LD_LIBRARY_PATH"));
|
||||
|
||||
char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0],
|
||||
&envbuf[3][0], &envbuf[4][0], 0 };
|
||||
|
||||
/* prefix name of Linux program (helps killing some zombies) */
|
||||
char const *prefix = "[Genode] ";
|
||||
char pname_buf[sizeof(_label) + sizeof(prefix)];
|
||||
snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _label);
|
||||
char *argv_buf[2];
|
||||
argv_buf[0] = pname_buf;
|
||||
argv_buf[1] = 0;
|
||||
|
||||
/*
|
||||
* We cannot create the new process via 'fork()' because all our used
|
||||
* memory including stack memory is backed by dataspaces, which had been
|
||||
* mapped with the 'MAP_SHARED' flag. Therefore, after being created, the
|
||||
* new process starts using the stack with the same physical memory pages
|
||||
* as used by parent process. This would ultimately lead to stack
|
||||
* corruption. To prevent both processes from concurrently accessing the
|
||||
* same stack, we pause the execution of the parent until the child calls
|
||||
* 'execve'. From then on, the child has its private memory layout. The
|
||||
* desired behaviour is normally provided by 'vfork' but we use the more
|
||||
* modern 'clone' call for this purpose.
|
||||
*/
|
||||
enum { STACK_SIZE = 4096 };
|
||||
static char stack[STACK_SIZE]; /* initial stack used by the child until
|
||||
calling 'execve' */
|
||||
|
||||
/*
|
||||
* Argument frame as passed to 'clone'. Because, we can only pass a single
|
||||
* pointer, all arguments are embedded within the 'execve_args' struct.
|
||||
*/
|
||||
Execve_args arg(filename, _root, argv_buf, env, _uid, _gid,
|
||||
_parent.dst().socket);
|
||||
|
||||
_pid = lx_create_process((int (*)(void *))_exec_child,
|
||||
stack + STACK_SIZE - sizeof(umword_t), &arg);
|
||||
|
||||
if (strcmp(filename, tmp_filename) == 0)
|
||||
lx_unlink(filename);
|
||||
}
|
||||
|
||||
|
||||
Pd_session_component::Pd_session_component(Rpc_entrypoint * ep,
|
||||
Allocator * md_alloc,
|
||||
const char * args)
|
||||
@ -366,94 +458,7 @@ int Pd_session_component::assign_parent(Parent_capability parent)
|
||||
|
||||
void Pd_session_component::start(Capability<Dataspace> binary)
|
||||
{
|
||||
const char *tmp_filename = "temporary_executable_elf_dataspace_file_for_execve";
|
||||
|
||||
/* lookup binary dataspace */
|
||||
Object_pool<Dataspace_component>::Guard ds(_ds_ep->lookup_and_lock(binary));
|
||||
|
||||
if (!ds) {
|
||||
PERR("could not lookup binary, aborted PD startup");
|
||||
return; /* XXX reflect error to client */
|
||||
}
|
||||
|
||||
/* we need 's' on stack to make it an lvalue with an lvalue member we use the pointer to */
|
||||
Linux_dataspace::Filename s = ds->fname();
|
||||
const char *filename = s.buf;
|
||||
|
||||
/*
|
||||
* In order to be executable via 'execve', a program must be represented as
|
||||
* a file on the Linux file system. However, this is not the case for a
|
||||
* plain RAM dataspace that contains an ELF image. In this case, we copy
|
||||
* the dataspace content into a temporary file whose path is passed to
|
||||
* 'execve()'.
|
||||
*/
|
||||
if (strcmp(filename, "") == 0) {
|
||||
|
||||
filename = tmp_filename;
|
||||
|
||||
int tmp_binary_fd = lx_open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);
|
||||
if (tmp_binary_fd < 0) {
|
||||
PERR("Could not create file '%s'", filename);
|
||||
return; /* XXX reflect error to client */
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
int num_bytes = 0;
|
||||
while ((num_bytes = lx_read(ds->fd().dst().socket, buf, sizeof(buf))) != 0)
|
||||
lx_write(tmp_binary_fd, buf, num_bytes);
|
||||
|
||||
lx_close(tmp_binary_fd);
|
||||
}
|
||||
|
||||
/* pass parent capability as environment variable to the child */
|
||||
enum { ENV_STR_LEN = 256 };
|
||||
static char envbuf[5][ENV_STR_LEN];
|
||||
Genode::snprintf(envbuf[1], ENV_STR_LEN, "parent_local_name=%lu",
|
||||
_parent.local_name());
|
||||
Genode::snprintf(envbuf[2], ENV_STR_LEN, "DISPLAY=%s",
|
||||
get_env("DISPLAY"));
|
||||
Genode::snprintf(envbuf[3], ENV_STR_LEN, "HOME=%s",
|
||||
get_env("HOME"));
|
||||
Genode::snprintf(envbuf[4], ENV_STR_LEN, "LD_LIBRARY_PATH=%s",
|
||||
get_env("LD_LIBRARY_PATH"));
|
||||
|
||||
char *env[] = { &envbuf[0][0], &envbuf[1][0], &envbuf[2][0],
|
||||
&envbuf[3][0], &envbuf[4][0], 0 };
|
||||
|
||||
/* prefix name of Linux program (helps killing some zombies) */
|
||||
char const *prefix = "[Genode] ";
|
||||
char pname_buf[sizeof(_label) + sizeof(prefix)];
|
||||
snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _label);
|
||||
char *argv_buf[2];
|
||||
argv_buf[0] = pname_buf;
|
||||
argv_buf[1] = 0;
|
||||
|
||||
/*
|
||||
* We cannot create the new process via 'fork()' because all our used
|
||||
* memory including stack memory is backed by dataspaces, which had been
|
||||
* mapped with the 'MAP_SHARED' flag. Therefore, after being created, the
|
||||
* new process starts using the stack with the same physical memory pages
|
||||
* as used by parent process. This would ultimately lead to stack
|
||||
* corruption. To prevent both processes from concurrently accessing the
|
||||
* same stack, we pause the execution of the parent until the child calls
|
||||
* 'execve'. From then on, the child has its private memory layout. The
|
||||
* desired behaviour is normally provided by 'vfork' but we use the more
|
||||
* modern 'clone' call for this purpose.
|
||||
*/
|
||||
enum { STACK_SIZE = 4096 };
|
||||
static char stack[STACK_SIZE]; /* initial stack used by the child until
|
||||
calling 'execve' */
|
||||
|
||||
/*
|
||||
* Argument frame as passed to 'clone'. Because, we can only pass a single
|
||||
* pointer, all arguments are embedded within the 'execve_args' struct.
|
||||
*/
|
||||
Execve_args arg(filename, _root, argv_buf, env, _uid, _gid,
|
||||
_parent.dst().socket);
|
||||
|
||||
_pid = lx_create_process((int (*)(void *))_exec_child,
|
||||
stack + STACK_SIZE - sizeof(umword_t), &arg);
|
||||
|
||||
if (strcmp(filename, tmp_filename) == 0)
|
||||
lx_unlink(filename);
|
||||
_ds_ep->apply(binary, [&] (Dataspace_component *ds) {
|
||||
_start(ds); });
|
||||
};
|
||||
|
@ -193,10 +193,8 @@ Platform_env_base::Rm_session_mmap::_dataspace_size(Capability<Dataspace> ds_cap
|
||||
}
|
||||
|
||||
/* use local function call if called from the entrypoint */
|
||||
Object_pool<Rpc_object_base>::Guard
|
||||
ds_rpc(core_env()->entrypoint()->lookup_and_lock(ds_cap));
|
||||
Dataspace * ds = dynamic_cast<Dataspace *>(&*ds_rpc);
|
||||
return ds ? ds->size() : 0;
|
||||
return core_env()->entrypoint()->apply(ds_cap, [] (Dataspace *ds) {
|
||||
return ds ? ds->size() : 0; });
|
||||
}
|
||||
|
||||
|
||||
@ -212,10 +210,6 @@ int Platform_env_base::Rm_session_mmap::_dataspace_fd(Capability<Dataspace> ds_c
|
||||
|
||||
Capability<Linux_dataspace> lx_ds_cap = static_cap_cast<Linux_dataspace>(ds_cap);
|
||||
|
||||
Object_pool<Rpc_object_base>::Guard
|
||||
ds_rpc(core_env()->entrypoint()->lookup_and_lock(lx_ds_cap));
|
||||
Linux_dataspace * ds = dynamic_cast<Linux_dataspace *>(&*ds_rpc);
|
||||
|
||||
/*
|
||||
* Return a duplicate of the dataspace file descriptor, which will be freed
|
||||
* immediately after mmap'ing the file (see 'Rm_session_mmap').
|
||||
@ -225,7 +219,8 @@ int Platform_env_base::Rm_session_mmap::_dataspace_fd(Capability<Dataspace> ds_c
|
||||
* socket descriptor during the RPC handling). When later destroying the
|
||||
* dataspace, the descriptor would unexpectedly be closed again.
|
||||
*/
|
||||
return ds ? lx_dup(ds->fd().dst().socket) : -1;
|
||||
return core_env()->entrypoint()->apply(lx_ds_cap, [] (Linux_dataspace *ds) {
|
||||
return ds ? lx_dup(ds->fd().dst().socket) : -1; });
|
||||
}
|
||||
|
||||
|
||||
@ -239,9 +234,6 @@ bool Platform_env_base::Rm_session_mmap::_dataspace_writable(Dataspace_capabilit
|
||||
return writable;
|
||||
}
|
||||
|
||||
Object_pool<Rpc_object_base>::Guard
|
||||
ds_rpc(core_env()->entrypoint()->lookup_and_lock(ds_cap));
|
||||
Dataspace * ds = dynamic_cast<Dataspace *>(&*ds_rpc);
|
||||
|
||||
return ds ? ds->writable() : false;
|
||||
return core_env()->entrypoint()->apply(ds_cap, [] (Dataspace *ds) {
|
||||
return ds ? ds->writable() : false; });
|
||||
}
|
||||
|
Reference in New Issue
Block a user