genode/repos/libports/src/lib/libc/task.cc

985 lines
23 KiB
C++
Raw Normal View History

/*
2016-12-13 17:09:35 +01:00
* \brief Libc kernel for main and pthreads user contexts
* \author Christian Helmuth
2016-12-13 17:09:35 +01:00
* \author Emery Hemingway
* \date 2016-01-22
*/
/*
* Copyright (C) 2016-2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/component.h>
2016-12-13 17:09:35 +01:00
#include <base/log.h>
#include <base/thread.h>
#include <base/rpc_server.h>
#include <base/rpc_client.h>
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
#include <vfs/simple_env.h>
2016-12-13 17:09:35 +01:00
#include <timer_session/connection.h>
/* libc includes */
#include <libc/component.h>
#include <libc/select.h>
#include <libc-plugin/plugin_registry.h>
/* libc-internal includes */
#include <internal/call_func.h>
#include <base/internal/unmanaged_singleton.h>
#include "vfs_plugin.h"
#include "libc_init.h"
2016-12-13 17:09:35 +01:00
#include "task.h"
extern char **environ;
namespace Libc {
class Env_implementation;
2016-12-13 17:09:35 +01:00
class Kernel;
class Pthreads;
class Timer;
class Timer_accessor;
class Timeout;
class Timeout_handler;
using Genode::Microseconds;
}
class Libc::Env_implementation : public Libc::Env
{
private:
Genode::Env &_env;
Genode::Attached_rom_dataspace _config { _env, "config" };
Genode::Xml_node _vfs_config()
{
try { return _config.xml().sub_node("vfs"); }
catch (Genode::Xml_node::Nonexistent_sub_node) { }
try {
Genode::Xml_node node =
_config.xml().sub_node("libc").sub_node("vfs");
Genode::warning("'<config> <libc> <vfs/>' is deprecated, "
"please move to '<config> <vfs/>'");
return node;
}
catch (Genode::Xml_node::Nonexistent_sub_node) { }
return Genode::Xml_node("<vfs/>");
}
Genode::Xml_node _libc_config()
{
try { return _config.xml().sub_node("libc"); }
catch (Genode::Xml_node::Nonexistent_sub_node) { }
return Genode::Xml_node("<libc/>");
}
Vfs::Simple_env _vfs_env;
Genode::Xml_node _config_xml() const override {
return _config.xml(); };
public:
Env_implementation(Genode::Env &env, Genode::Allocator &alloc)
: _env(env), _vfs_env(_env, alloc, _vfs_config()) { }
/*************************
** Libc::Env interface **
*************************/
Vfs::File_system &vfs() override {
return _vfs_env.root_dir(); }
Genode::Xml_node libc_config() override {
return _libc_config(); }
/***************************
** Genode::Env interface **
***************************/
Parent &parent() override { return _env.parent(); }
Cpu_session &cpu() override { return _env.cpu(); }
Region_map &rm() override { return _env.rm(); }
Pd_session &pd() override { return _env.pd(); }
Entrypoint &ep() override { return _env.ep(); }
Cpu_session_capability cpu_session_cap() override {
return _env.cpu_session_cap(); }
Pd_session_capability pd_session_cap() override {
return _env.pd_session_cap(); }
Id_space<Parent::Client> &id_space() override {
return _env.id_space(); }
Session_capability session(Parent::Service_name const &name,
Parent::Client::Id id,
Parent::Session_args const &args,
Affinity const &aff) override {
return _env.session(name, id, args, aff); }
void upgrade(Parent::Client::Id id,
Parent::Upgrade_args const &args) override {
return _env.upgrade(id, args); }
void close(Parent::Client::Id id) override {
return _env.close(id); }
/* already done by the libc */
void exec_static_constructors() override { }
void reinit(Native_capability::Raw raw) override {
_env.reinit(raw); }
void reinit_main_thread(Capability<Region_map> &stack_area_rm) override {
_env.reinit_main_thread(stack_area_rm); }
};
2016-12-13 17:09:35 +01:00
struct Libc::Timer
{
os/timer: interpolate time via timestamps Previously, the Genode::Timer::curr_time always used the Timer_session::elapsed_ms RPC as back end. Now, Genode::Timer reads this remote time only in a periodic fashion independently from the calls to Genode::Timer::curr_time. If now one calls Genode::Timer::curr_time, the function takes the last read remote time value and adapts it using the timestamp difference since the remote-time read. The conversion factor from timestamps to time is estimated on every remote-time read using the last read remote-time value and the timestamp difference since the last remote time read. This commit also re-works the timeout test. The test now has two stages. In the first stage, it tests fast polling of the Genode::Timer::curr_time. This stage checks the error between locally interpolated and timer-driver time as well as wether the locally interpolated time is monotone and sufficiently homogeneous. In the second stage several periodic and one-shot timeouts are scheduled at once. This stage checks if the timeouts trigger sufficiently precise. This commit adds the new Kernel::time syscall to base-hw. The syscall is solely used by the Genode::Timer on base-hw as substitute for the timestamp. This is because on ARM, the timestamp function uses the ARM performance counter that stops counting when the WFI (wait for interrupt) instruction is active. This instruction, however is used by the base-hw idle contexts that get active when no user thread needs to be scheduled. Thus, the ARM performance counter is not a good choice for time interpolation and we use the kernel internal time instead. With this commit, the timeout library becomes a basic library. That means that it is linked against the LDSO which then provides it to the program it serves. Furthermore, you can't use the timeout library anymore without the LDSO because through the kernel-dependent LDSO make-files we can achieve a kernel-dependent timeout implementation. This commit introduces a structured Duration type that shall successively replace the use of Microseconds, Milliseconds, and integer types for duration values. Open issues: * The timeout test fails on Raspberry PI because of precision errors in the first stage. However, this does not render the framework unusable in general on the RPI but merely is an issue when speaking of microseconds precision. * If we run on ARM with another Kernel than HW the timestamp speed may continuously vary from almost 0 up to CPU speed. The Timer, however, only uses interpolation if the timestamp speed remained stable (12.5% tolerance) for at least 3 observation periods. Currently, one period is 100ms, so its 300ms. As long as this is not the case, Timer_session::elapsed_ms is called instead. Anyway, it might happen that the CPU load was stable for some time so interpolation becomes active and now the timestamp speed drops. In the worst case, we would now have 100ms of slowed down time. The bad thing about it would be, that this also affects the timeout of the period. Thus, it might "freeze" the local time for more than 100ms. On the other hand, if the timestamp speed suddenly raises after some stable time, interpolated time can get too fast. This would shorten the period but nonetheless may result in drifting away into the far future. Now we would have the problem that we can't deliver the real time anymore until it has caught up because the output of Timer::curr_time shall be monotone. So, effectively local time might "freeze" again for more than 100ms. It would be a solution to not use the Trace::timestamp on ARM w/o HW but a function whose return value causes the Timer to never use interpolation because of its stability policy. Fixes #2400
2017-04-22 00:52:23 +02:00
::Timer::Connection _timer;
2016-12-13 17:09:35 +01:00
os/timer: interpolate time via timestamps Previously, the Genode::Timer::curr_time always used the Timer_session::elapsed_ms RPC as back end. Now, Genode::Timer reads this remote time only in a periodic fashion independently from the calls to Genode::Timer::curr_time. If now one calls Genode::Timer::curr_time, the function takes the last read remote time value and adapts it using the timestamp difference since the remote-time read. The conversion factor from timestamps to time is estimated on every remote-time read using the last read remote-time value and the timestamp difference since the last remote time read. This commit also re-works the timeout test. The test now has two stages. In the first stage, it tests fast polling of the Genode::Timer::curr_time. This stage checks the error between locally interpolated and timer-driver time as well as wether the locally interpolated time is monotone and sufficiently homogeneous. In the second stage several periodic and one-shot timeouts are scheduled at once. This stage checks if the timeouts trigger sufficiently precise. This commit adds the new Kernel::time syscall to base-hw. The syscall is solely used by the Genode::Timer on base-hw as substitute for the timestamp. This is because on ARM, the timestamp function uses the ARM performance counter that stops counting when the WFI (wait for interrupt) instruction is active. This instruction, however is used by the base-hw idle contexts that get active when no user thread needs to be scheduled. Thus, the ARM performance counter is not a good choice for time interpolation and we use the kernel internal time instead. With this commit, the timeout library becomes a basic library. That means that it is linked against the LDSO which then provides it to the program it serves. Furthermore, you can't use the timeout library anymore without the LDSO because through the kernel-dependent LDSO make-files we can achieve a kernel-dependent timeout implementation. This commit introduces a structured Duration type that shall successively replace the use of Microseconds, Milliseconds, and integer types for duration values. Open issues: * The timeout test fails on Raspberry PI because of precision errors in the first stage. However, this does not render the framework unusable in general on the RPI but merely is an issue when speaking of microseconds precision. * If we run on ARM with another Kernel than HW the timestamp speed may continuously vary from almost 0 up to CPU speed. The Timer, however, only uses interpolation if the timestamp speed remained stable (12.5% tolerance) for at least 3 observation periods. Currently, one period is 100ms, so its 300ms. As long as this is not the case, Timer_session::elapsed_ms is called instead. Anyway, it might happen that the CPU load was stable for some time so interpolation becomes active and now the timestamp speed drops. In the worst case, we would now have 100ms of slowed down time. The bad thing about it would be, that this also affects the timeout of the period. Thus, it might "freeze" the local time for more than 100ms. On the other hand, if the timestamp speed suddenly raises after some stable time, interpolated time can get too fast. This would shorten the period but nonetheless may result in drifting away into the far future. Now we would have the problem that we can't deliver the real time anymore until it has caught up because the output of Timer::curr_time shall be monotone. So, effectively local time might "freeze" again for more than 100ms. It would be a solution to not use the Trace::timestamp on ARM w/o HW but a function whose return value causes the Timer to never use interpolation because of its stability policy. Fixes #2400
2017-04-22 00:52:23 +02:00
Timer(Genode::Env &env) : _timer(env) { }
2016-12-13 17:09:35 +01:00
Genode::Duration curr_time()
2016-12-13 17:09:35 +01:00
{
return _timer.curr_time();
2016-12-13 17:09:35 +01:00
}
static Microseconds microseconds(Genode::uint64_t timeout_ms)
2016-12-13 17:09:35 +01:00
{
return Microseconds(1000*timeout_ms);
}
static Genode::uint64_t max_timeout()
2016-12-13 17:09:35 +01:00
{
return ~0UL/1000;
2016-12-13 17:09:35 +01:00
}
};
/**
2016-12-13 17:09:35 +01:00
* Interface for obtaining the libc-global timer instance
*
2016-12-13 17:09:35 +01:00
* The 'Timer' is instantiated on demand whenever the 'Timer_accessor::timer'
* method is first called. This way, libc-using components do not depend of a
* timer connection unless they actually use time-related functionality.
*/
struct Libc::Timer_accessor
{
virtual Timer &timer() = 0;
};
struct Libc::Timeout_handler
{
virtual void handle_timeout() = 0;
};
/*
* TODO curr_time wrapping
*/
struct Libc::Timeout
{
os/timer: interpolate time via timestamps Previously, the Genode::Timer::curr_time always used the Timer_session::elapsed_ms RPC as back end. Now, Genode::Timer reads this remote time only in a periodic fashion independently from the calls to Genode::Timer::curr_time. If now one calls Genode::Timer::curr_time, the function takes the last read remote time value and adapts it using the timestamp difference since the remote-time read. The conversion factor from timestamps to time is estimated on every remote-time read using the last read remote-time value and the timestamp difference since the last remote time read. This commit also re-works the timeout test. The test now has two stages. In the first stage, it tests fast polling of the Genode::Timer::curr_time. This stage checks the error between locally interpolated and timer-driver time as well as wether the locally interpolated time is monotone and sufficiently homogeneous. In the second stage several periodic and one-shot timeouts are scheduled at once. This stage checks if the timeouts trigger sufficiently precise. This commit adds the new Kernel::time syscall to base-hw. The syscall is solely used by the Genode::Timer on base-hw as substitute for the timestamp. This is because on ARM, the timestamp function uses the ARM performance counter that stops counting when the WFI (wait for interrupt) instruction is active. This instruction, however is used by the base-hw idle contexts that get active when no user thread needs to be scheduled. Thus, the ARM performance counter is not a good choice for time interpolation and we use the kernel internal time instead. With this commit, the timeout library becomes a basic library. That means that it is linked against the LDSO which then provides it to the program it serves. Furthermore, you can't use the timeout library anymore without the LDSO because through the kernel-dependent LDSO make-files we can achieve a kernel-dependent timeout implementation. This commit introduces a structured Duration type that shall successively replace the use of Microseconds, Milliseconds, and integer types for duration values. Open issues: * The timeout test fails on Raspberry PI because of precision errors in the first stage. However, this does not render the framework unusable in general on the RPI but merely is an issue when speaking of microseconds precision. * If we run on ARM with another Kernel than HW the timestamp speed may continuously vary from almost 0 up to CPU speed. The Timer, however, only uses interpolation if the timestamp speed remained stable (12.5% tolerance) for at least 3 observation periods. Currently, one period is 100ms, so its 300ms. As long as this is not the case, Timer_session::elapsed_ms is called instead. Anyway, it might happen that the CPU load was stable for some time so interpolation becomes active and now the timestamp speed drops. In the worst case, we would now have 100ms of slowed down time. The bad thing about it would be, that this also affects the timeout of the period. Thus, it might "freeze" the local time for more than 100ms. On the other hand, if the timestamp speed suddenly raises after some stable time, interpolated time can get too fast. This would shorten the period but nonetheless may result in drifting away into the far future. Now we would have the problem that we can't deliver the real time anymore until it has caught up because the output of Timer::curr_time shall be monotone. So, effectively local time might "freeze" again for more than 100ms. It would be a solution to not use the Trace::timestamp on ARM w/o HW but a function whose return value causes the Timer to never use interpolation because of its stability policy. Fixes #2400
2017-04-22 00:52:23 +02:00
Libc::Timer_accessor &_timer_accessor;
Timeout_handler &_handler;
::Timer::One_shot_timeout<Timeout> _timeout;
2016-12-13 17:09:35 +01:00
bool _expired = true;
Genode::uint64_t _absolute_timeout_ms = 0;
2016-12-13 17:09:35 +01:00
os/timer: interpolate time via timestamps Previously, the Genode::Timer::curr_time always used the Timer_session::elapsed_ms RPC as back end. Now, Genode::Timer reads this remote time only in a periodic fashion independently from the calls to Genode::Timer::curr_time. If now one calls Genode::Timer::curr_time, the function takes the last read remote time value and adapts it using the timestamp difference since the remote-time read. The conversion factor from timestamps to time is estimated on every remote-time read using the last read remote-time value and the timestamp difference since the last remote time read. This commit also re-works the timeout test. The test now has two stages. In the first stage, it tests fast polling of the Genode::Timer::curr_time. This stage checks the error between locally interpolated and timer-driver time as well as wether the locally interpolated time is monotone and sufficiently homogeneous. In the second stage several periodic and one-shot timeouts are scheduled at once. This stage checks if the timeouts trigger sufficiently precise. This commit adds the new Kernel::time syscall to base-hw. The syscall is solely used by the Genode::Timer on base-hw as substitute for the timestamp. This is because on ARM, the timestamp function uses the ARM performance counter that stops counting when the WFI (wait for interrupt) instruction is active. This instruction, however is used by the base-hw idle contexts that get active when no user thread needs to be scheduled. Thus, the ARM performance counter is not a good choice for time interpolation and we use the kernel internal time instead. With this commit, the timeout library becomes a basic library. That means that it is linked against the LDSO which then provides it to the program it serves. Furthermore, you can't use the timeout library anymore without the LDSO because through the kernel-dependent LDSO make-files we can achieve a kernel-dependent timeout implementation. This commit introduces a structured Duration type that shall successively replace the use of Microseconds, Milliseconds, and integer types for duration values. Open issues: * The timeout test fails on Raspberry PI because of precision errors in the first stage. However, this does not render the framework unusable in general on the RPI but merely is an issue when speaking of microseconds precision. * If we run on ARM with another Kernel than HW the timestamp speed may continuously vary from almost 0 up to CPU speed. The Timer, however, only uses interpolation if the timestamp speed remained stable (12.5% tolerance) for at least 3 observation periods. Currently, one period is 100ms, so its 300ms. As long as this is not the case, Timer_session::elapsed_ms is called instead. Anyway, it might happen that the CPU load was stable for some time so interpolation becomes active and now the timestamp speed drops. In the worst case, we would now have 100ms of slowed down time. The bad thing about it would be, that this also affects the timeout of the period. Thus, it might "freeze" the local time for more than 100ms. On the other hand, if the timestamp speed suddenly raises after some stable time, interpolated time can get too fast. This would shorten the period but nonetheless may result in drifting away into the far future. Now we would have the problem that we can't deliver the real time anymore until it has caught up because the output of Timer::curr_time shall be monotone. So, effectively local time might "freeze" again for more than 100ms. It would be a solution to not use the Trace::timestamp on ARM w/o HW but a function whose return value causes the Timer to never use interpolation because of its stability policy. Fixes #2400
2017-04-22 00:52:23 +02:00
void _handle(Duration now)
2016-12-13 17:09:35 +01:00
{
_expired = true;
_absolute_timeout_ms = 0;
2016-12-13 17:09:35 +01:00
_handler.handle_timeout();
}
Timeout(Timer_accessor &timer_accessor, Timeout_handler &handler)
:
_timer_accessor(timer_accessor),
_handler(handler),
_timeout(_timer_accessor.timer()._timer, *this, &Timeout::_handle)
{ }
void start(Genode::uint64_t timeout_ms)
2016-12-13 17:09:35 +01:00
{
Milliseconds const now = _timer_accessor.timer().curr_time().trunc_to_plain_ms();
2016-12-13 17:09:35 +01:00
_expired = false;
_absolute_timeout_ms = now.value + timeout_ms;
_timeout.schedule(_timer_accessor.timer().microseconds(timeout_ms));
2016-12-13 17:09:35 +01:00
}
Genode::uint64_t duration_left() const
2016-12-13 17:09:35 +01:00
{
Milliseconds const now = _timer_accessor.timer().curr_time().trunc_to_plain_ms();
2016-12-13 17:09:35 +01:00
if (_expired || _absolute_timeout_ms < now.value)
return 0;
2017-05-22 15:55:39 +02:00
return _absolute_timeout_ms - now.value;
2016-12-13 17:09:35 +01:00
}
};
struct Libc::Pthreads
{
struct Pthread : Timeout_handler
{
Genode::Lock lock { Genode::Lock::LOCKED };
Pthread *next { nullptr };
Timer_accessor &_timer_accessor;
Constructible<Timeout> _timeout;
void _construct_timeout_once()
{
if (!_timeout.constructed())
_timeout.construct(_timer_accessor, *this);
}
2016-12-13 17:09:35 +01:00
Pthread(Timer_accessor &timer_accessor, Genode::uint64_t timeout_ms)
: _timer_accessor(timer_accessor)
{
if (timeout_ms > 0) {
_construct_timeout_once();
_timeout->start(timeout_ms);
}
}
Genode::uint64_t duration_left()
2016-12-13 17:09:35 +01:00
{
_construct_timeout_once();
return _timeout->duration_left();
2016-12-13 17:09:35 +01:00
}
void handle_timeout() override
2016-12-13 17:09:35 +01:00
{
lock.unlock();
}
};
Genode::Lock mutex;
Pthread *pthreads = nullptr;
Timer_accessor &timer_accessor;
Pthreads(Timer_accessor &timer_accessor)
: timer_accessor(timer_accessor) { }
void resume_all()
{
Genode::Lock::Guard g(mutex);
for (Pthread *p = pthreads; p; p = p->next)
p->lock.unlock();
}
Genode::uint64_t suspend_myself(Suspend_functor & check, Genode::uint64_t timeout_ms)
2016-12-13 17:09:35 +01:00
{
Pthread myself { timer_accessor, timeout_ms };
2016-12-13 17:09:35 +01:00
{
Genode::Lock::Guard g(mutex);
myself.next = pthreads;
pthreads = &myself;
}
if (check.suspend())
myself.lock.lock();
2016-12-13 17:09:35 +01:00
{
Genode::Lock::Guard g(mutex);
/* address of pointer to next pthread allows to change the head */
for (Pthread **next = &pthreads; *next; next = &(*next)->next) {
if (*next == &myself) {
*next = myself.next;
break;
}
}
}
return timeout_ms > 0 ? myself.duration_left() : 0;
2016-12-13 17:09:35 +01:00
}
};
extern void (*libc_select_notify)();
2016-12-13 17:09:35 +01:00
/* internal utility */
static void resumed_callback();
static void suspended_callback();
/**
* Libc "kernel"
*
* This class represents the "kernel" of the libc-based application
* Blocking and deblocking happens here on libc functions like read() or
* select(). This combines blocking of the VFS backend and other signal sources
* (e.g., timers). The libc task runs on the component thread and allocates a
* secondary stack for the application task. Context switching uses
* setjmp/longjmp.
*/
struct Libc::Kernel final : Vfs::Io_response_handler,
Genode::Entrypoint::Io_progress_handler
{
private:
Genode::Env &_env;
Genode::Allocator &_heap;
Env_implementation _libc_env { _env, _heap };
Vfs_plugin _vfs { _libc_env, _heap, *this };
Genode::Reconstructible<Genode::Io_signal_handler<Kernel>> _resume_main_handler {
_env.ep(), *this, &Kernel::_resume_main };
2016-12-13 17:09:35 +01:00
jmp_buf _kernel_context;
jmp_buf _user_context;
bool _valid_user_context = false;
bool _dispatch_pending_io_signals = false;
/* io_progress_handler marker */
bool _io_ready { false };
2016-12-13 17:09:35 +01:00
Genode::Thread &_myself { *Genode::Thread::myself() };
addr_t _kernel_stack = Thread::mystack().top;
2016-12-13 17:09:35 +01:00
void *_user_stack = {
_myself.alloc_secondary_stack(_myself.name().string(),
Component::stack_size()) };
2016-12-13 17:09:35 +01:00
void (*_original_suspended_callback)() = nullptr;
enum State { KERNEL, USER };
State _state = KERNEL;
Application_code *_nested_app_code = nullptr;
Application_code *_app_code = nullptr;
bool _app_returned = false;
2017-02-21 17:35:51 +01:00
bool _resume_main_once = false;
bool _suspend_scheduled = false;
Select_handler_base *_scheduled_select_handler = nullptr;
void _resume_main() { _resume_main_once = true; }
2016-12-13 17:09:35 +01:00
struct Timer_accessor : Libc::Timer_accessor
{
Genode::Env &_env;
/*
* The '_timer' is constructed by whatever thread (main thread
* of pthread) that uses a time-related function first. Hence,
* the construction must be protected by a lock.
*/
Genode::Lock _lock;
Genode::Constructible<Timer> _timer;
Timer_accessor(Genode::Env &env) : _env(env) { }
Timer &timer() override
{
Lock::Guard guard(_lock);
if (!_timer.constructed())
_timer.construct(_env);
return *_timer;
}
};
Timer_accessor _timer_accessor { _env };
struct Main_timeout : Timeout_handler
{
Timer_accessor &_timer_accessor;
Constructible<Timeout> _timeout;
Kernel &_kernel;
2016-12-13 17:09:35 +01:00
void _construct_timeout_once()
{
if (!_timeout.constructed())
_timeout.construct(_timer_accessor, *this);
}
Main_timeout(Timer_accessor &timer_accessor, Kernel &kernel)
: _timer_accessor(timer_accessor), _kernel(kernel)
2016-12-13 17:09:35 +01:00
{ }
void timeout(Genode::uint64_t timeout_ms)
2016-12-13 17:09:35 +01:00
{
_construct_timeout_once();
_timeout->start(timeout_ms);
2016-12-13 17:09:35 +01:00
}
Genode::uint64_t duration_left()
2016-12-13 17:09:35 +01:00
{
_construct_timeout_once();
return _timeout->duration_left();
}
void handle_timeout() override
2016-12-13 17:09:35 +01:00
{
_kernel._resume_main();
2016-12-13 17:09:35 +01:00
}
};
Main_timeout _main_timeout { _timer_accessor, *this };
2016-12-13 17:09:35 +01:00
Pthreads _pthreads { _timer_accessor };
2017-02-21 17:35:51 +01:00
struct Resumer
{
GENODE_RPC(Rpc_resume, void, resume);
GENODE_RPC_INTERFACE(Rpc_resume);
};
struct Resumer_component : Rpc_object<Resumer, Resumer_component>
{
Kernel &_kernel;
Resumer_component(Kernel &kernel) : _kernel(kernel) { }
void resume() { _kernel.run_after_resume(); }
};
/**
2016-12-13 17:09:35 +01:00
* Trampoline to application (user) code
*
* This function is called by the main thread.
*/
2016-12-13 17:09:35 +01:00
static void _user_entry(Libc::Kernel *kernel)
{
struct Check : Suspend_functor {
bool suspend() override { return true; }
} check;
kernel->_app_code->execute();
kernel->_app_returned = true;
kernel->_suspend_main(check, 0);
2016-12-13 17:09:35 +01:00
}
bool _main_context() const { return &_myself == Genode::Thread::myself(); }
/**
2016-12-13 17:09:35 +01:00
* Utility to switch main context to kernel
*
* User context must be saved explicitly before this function is called
* to enable _switch_to_user() later.
*/
2016-12-13 17:09:35 +01:00
void _switch_to_kernel()
{
_state = KERNEL;
_longjmp(_kernel_context, 1);
}
/**
* Utility to switch main context to user
*
* Kernel context must be saved explicitly before this function is called
* to enable _switch_to_kernel() later.
*/
void _switch_to_user()
{
if (!_valid_user_context)
Genode::error("switching to invalid user context");
_resume_main_once = false;
2016-12-13 17:09:35 +01:00
_state = USER;
_longjmp(_user_context, 1);
}
Genode::uint64_t _suspend_main(Suspend_functor &check,
Genode::uint64_t timeout_ms)
2016-12-13 17:09:35 +01:00
{
/* check that we're not running on libc kernel context */
if (Thread::mystack().top == _kernel_stack) {
error("libc suspend() called from non-user context (",
__builtin_return_address(0), ") - aborting");
exit(1);
}
if (!check.suspend())
return 0;
if (timeout_ms > 0)
_main_timeout.timeout(timeout_ms);
if (!_setjmp(_user_context)) {
_valid_user_context = true;
2016-12-13 17:09:35 +01:00
_switch_to_kernel();
} else {
_valid_user_context = false;
}
2016-12-13 17:09:35 +01:00
/*
* During the suspension of the application code a nested
* Libc::with_libc() call took place, which will be executed
* before returning to the first Libc::with_libc() call.
*/
if (_nested_app_code) {
/*
* We have to explicitly set the user context back to true
* because we are borrowing it to execute our nested application
* code.
*/
_valid_user_context = true;
_nested_app_code->execute();
_nested_app_code = nullptr;
_longjmp(_kernel_context, 1);
}
return timeout_ms > 0 ? _main_timeout.duration_left() : 0;
2016-12-13 17:09:35 +01:00
}
public:
Kernel(Genode::Env &env, Genode::Allocator &heap)
: _env(env), _heap(heap)
{
_env.ep().register_io_progress_handler(*this);
}
2016-12-13 17:09:35 +01:00
~Kernel() { Genode::error(__PRETTY_FUNCTION__, " should not be executed!"); }
Libc::Env & libc_env() { return _libc_env; }
2016-12-13 17:09:35 +01:00
/**
* Setup kernel context and run libc application main context
*
* This function is called by the component thread on with_libc().
2016-12-13 17:09:35 +01:00
*/
void run(Libc::Application_code &app_code)
{
2016-12-13 17:09:35 +01:00
if (!_main_context() || _state != KERNEL) {
Genode::error(__PRETTY_FUNCTION__, " called from non-kernel context");
return;
}
_resume_main_once = false;
_app_returned = false;
_app_code = &app_code;
2016-12-13 17:09:35 +01:00
/* save continuation of libc kernel (incl. current stack) */
if (!_setjmp(_kernel_context)) {
/* _setjmp() returned directly -> switch to user stack and call application code */
2016-12-13 17:09:35 +01:00
_state = USER;
call_func(_user_stack, (void *)_user_entry, (void *)this);
/* never reached */
}
/* _setjmp() returned after _longjmp() - user context suspended */
2017-02-21 17:35:51 +01:00
while ((!_app_returned) && (!_suspend_scheduled)) {
if (_dispatch_pending_io_signals) {
/* dispatch pending signals but don't block */
while (_env.ep().dispatch_pending_io_signal()) ;
} else {
/* block for signals */
_env.ep().wait_and_dispatch_one_io_signal();
}
if (_resume_main_once && !_setjmp(_kernel_context))
_switch_to_user();
}
2017-02-21 17:35:51 +01:00
_suspend_scheduled = false;
}
/*
* Run libc application main context after suspend and resume
*/
void run_after_resume()
{
if (!_setjmp(_kernel_context))
_switch_to_user();
while ((!_app_returned) && (!_suspend_scheduled)) {
_env.ep().wait_and_dispatch_one_io_signal();
2017-02-21 17:35:51 +01:00
if (_resume_main_once && !_setjmp(_kernel_context))
_switch_to_user();
}
_suspend_scheduled = false;
}
/**
2016-12-13 17:09:35 +01:00
* Resume all contexts (main and pthreads)
*/
void resume_all()
{
if (_app_returned) {
if (_scheduled_select_handler)
_scheduled_select_handler->dispatch_select();
} else {
if (_main_context())
_resume_main();
else
Genode::Signal_transmitter(*_resume_main_handler).submit();
}
2016-12-13 17:09:35 +01:00
_pthreads.resume_all();
}
/**
* Suspend this context (main or pthread)
*/
Genode::uint64_t suspend(Suspend_functor &check, Genode::uint64_t timeout_ms)
{
if (timeout_ms > 0
&& timeout_ms > _timer_accessor.timer().max_timeout()) {
2016-12-13 17:09:35 +01:00
Genode::warning("libc: limiting exceeding timeout of ",
timeout_ms, " ms to maximum of ",
_timer_accessor.timer().max_timeout(), " ms");
2016-12-13 17:09:35 +01:00
timeout_ms = min(timeout_ms, _timer_accessor.timer().max_timeout());
}
2016-12-13 17:09:35 +01:00
return _main_context() ? _suspend_main(check, timeout_ms)
: _pthreads.suspend_myself(check, timeout_ms);
}
void dispatch_pending_io_signals()
{
if (!_main_context()) return;
if (!_setjmp(_user_context)) {
_valid_user_context = true;
_dispatch_pending_io_signals = true;
_resume_main_once = true; /* afterwards resume main */
_switch_to_kernel();
} else {
_valid_user_context = false;
_dispatch_pending_io_signals = false;
}
}
Genode::Duration current_time()
{
return _timer_accessor.timer().curr_time();
}
/**
2016-12-13 17:09:35 +01:00
* Called from the main context (by fork)
*/
2016-12-13 17:09:35 +01:00
void schedule_suspend(void(*original_suspended_callback) ())
{
2016-12-13 17:09:35 +01:00
if (_state != USER) {
Genode::error(__PRETTY_FUNCTION__, " called from non-user context");
return;
2016-12-13 17:09:35 +01:00
}
2016-12-13 17:09:35 +01:00
/*
* We hook into suspend-resume callback chain to destruct and
* reconstruct parts of the kernel from the context of the initial
* thread, i.e., without holding any object locks.
*/
_original_suspended_callback = original_suspended_callback;
_env.ep().schedule_suspend(suspended_callback, resumed_callback);
if (!_setjmp(_user_context)) {
_valid_user_context = true;
2017-02-21 17:35:51 +01:00
_suspend_scheduled = true;
2016-12-13 17:09:35 +01:00
_switch_to_kernel();
} else {
_valid_user_context = false;
}
}
void schedule_select(Select_handler_base *h)
{
_scheduled_select_handler = h;
}
/**
2016-12-13 17:09:35 +01:00
* Called from the context of the initial thread (on fork)
*/
2016-12-13 17:09:35 +01:00
void entrypoint_suspended()
{
_resume_main_handler.destruct();
2016-12-13 17:09:35 +01:00
_original_suspended_callback();
}
2016-12-13 17:09:35 +01:00
/**
* Called from the context of the initial thread (after fork)
*/
void entrypoint_resumed()
{
_resume_main_handler.construct(_env.ep(), *this, &Kernel::_resume_main);
2017-02-21 17:35:51 +01:00
Resumer_component resumer { *this };
Capability<Resumer> resumer_cap =
_env.ep().rpc_ep().manage(&resumer);
resumer_cap.call<Resumer::Rpc_resume>();
_env.ep().rpc_ep().dissolve(&resumer);
2016-12-13 17:09:35 +01:00
}
/**
* Return if main is currently suspended
*/
bool main_suspended() { return _state == KERNEL; }
/**
* Public alias for _main_context()
*/
bool main_context() const { return _main_context(); }
/**
* Execute application code while already executing in run()
*/
void nested_execution(Libc::Application_code &app_code)
{
_nested_app_code = &app_code;
if (!_setjmp(_kernel_context)) {
_switch_to_user();
}
}
/****************************************
** Vfs::Io_response_handler interface **
****************************************/
void read_ready_response() override {
_io_ready = true; }
void io_progress_response() override {
_io_ready = true; }
/**********************************************
* Entrypoint::Io_progress_handler interface **
**********************************************/
void handle_io_progress() override
{
/*
* TODO: make VFS I/O completion checks during
* kernel time to avoid flapping between stacks
*/
if (_io_ready) {
_io_ready = false;
/* some contexts may have been deblocked from select() */
if (libc_select_notify)
libc_select_notify();
/*
* resume all as any VFS context may have
* been deblocked from blocking I/O
*/
Kernel::resume_all();
}
}
2016-12-13 17:09:35 +01:00
};
2016-12-13 17:09:35 +01:00
/**
* Libc kernel singleton
*
* The singleton is implemented with the unmanaged-singleton utility
* in Component::construct() to ensure it is never destructed
* like normal static global objects. Otherwise, the task object may be
* destructed in a RPC to Rpc_resume, which would result in a deadlock.
*/
static Libc::Kernel *kernel;
2016-12-13 17:09:35 +01:00
/**
* Main context execution was suspended (on fork)
*
* This function is executed in the context of the initial thread.
*/
static void suspended_callback() { kernel->entrypoint_suspended(); }
/**
2016-12-13 17:09:35 +01:00
* Resume main context execution (after fork)
*
2016-12-13 17:09:35 +01:00
* This function is executed in the context of the initial thread.
*/
2016-12-13 17:09:35 +01:00
static void resumed_callback() { kernel->entrypoint_resumed(); }
2016-12-13 17:09:35 +01:00
/*******************
** Libc task API **
*******************/
2016-12-13 17:09:35 +01:00
void Libc::resume_all() { kernel->resume_all(); }
Genode::uint64_t Libc::suspend(Suspend_functor &s, Genode::uint64_t timeout_ms)
2016-12-13 17:09:35 +01:00
{
if (!kernel) {
error("libc kernel not initialized, needed for suspend()");
exit(1);
}
return kernel->suspend(s, timeout_ms);
2016-12-13 17:09:35 +01:00
}
void Libc::dispatch_pending_io_signals()
{
kernel->dispatch_pending_io_signals();
}
Genode::Duration Libc::current_time()
{
return kernel->current_time();
}
2016-12-13 17:09:35 +01:00
void Libc::schedule_suspend(void (*suspended) ())
{
if (!kernel) {
error("libc kernel not initialized, needed for fork()");
exit(1);
}
2016-12-13 17:09:35 +01:00
kernel->schedule_suspend(suspended);
}
void Libc::schedule_select(Libc::Select_handler_base *h)
{
if (!kernel) {
error("libc kernel not initialized, needed for select()");
exit(1);
}
kernel->schedule_select(h);
}
void Libc::execute_in_application_context(Libc::Application_code &app_code)
{
if (!kernel) {
error("libc kernel not initialized, needed for with_libc()");
exit(1);
}
/*
* The libc execution model builds on the main entrypoint, which handles
* all relevant signals (e.g., timing and VFS). Additional component
* entrypoints or pthreads should never call with_libc() but we catch this
* here and just execute the application code directly.
*/
if (!kernel->main_context()) {
app_code.execute();
return;
}
static bool nested = false;
if (nested) {
if (kernel->main_suspended()) {
kernel->nested_execution(app_code);
} else {
app_code.execute();
}
return;
}
nested = true;
kernel->run(app_code);
nested = false;
}
Genode::Xml_node Libc::libc_config()
{
return kernel->libc_env().libc_config();
}
/***************************
** Component entry point **
***************************/
Genode::size_t Component::stack_size() { return Libc::Component::stack_size(); }
void Component::construct(Genode::Env &env)
{
/* initialize the global pointer to environment variables */
static char *null_env = nullptr;
if (!environ) environ = &null_env;
Genode::Allocator &heap =
*unmanaged_singleton<Genode::Heap>(env.ram(), env.rm());
/* pass Genode::Env to libc subsystems that depend on it */
Libc::init_malloc(heap);
Libc::init_mem_alloc(env);
Libc::init_dl(env);
Libc::sysctl_init(env);
Libc::init_pthread_support(env);
kernel = unmanaged_singleton<Libc::Kernel>(env, heap);
Libc::libc_config_init(kernel->libc_env().libc_config());
/*
* XXX The following two steps leave us with the dilemma that we don't know
* which linked library may depend on the successfull initialization of a
* plugin. For example, some high-level library may try to open a network
* connection in its constructor before the network-stack library is
* initialized. But, we can't initialize plugins before calling static
* constructors as those are needed to know about the libc plugin. The only
* solution is to remove all libc plugins beside the VFS implementation,
* which is our final goal anyway.
*/
/* finish static construction of component and libraries */
Libc::with_libc([&] () { env.exec_static_constructors(); });
/* initialize plugins that require Genode::Env */
auto init_plugin = [&] (Libc::Plugin &plugin) {
plugin.init(env);
};
Libc::plugin_registry()->for_each_plugin(init_plugin);
/* construct libc component on kernel stack */
Libc::Component::construct(kernel->libc_env());
}
/**
* Default stack size for libc-using components
*/
Genode::size_t Libc::Component::stack_size() __attribute__((weak));
Genode::size_t Libc::Component::stack_size() { return 32UL*1024*sizeof(long); }