lx_kit: decouple schedule execution

Prior to this commit, whenever an external event occurred, for example
timer or interrupt, the corresponding I/O signal handler was triggered.
This handler unblocked the task waiting for the event and initiated the
immediate execution of all unblocked tasks. Since these tasks may hit
serialization points, e.g. synchronously waiting for packet stream
operations, that require handling of other I/O signals this leads to
nested execution. This, however, is not supported and mixes application
and I/O level signal handling.

The flagging of the scheduling intent is now decoupled from its
execution by using an application level signal handler that is run in
the context of the components main entrypoint. The I/O signal handler
now triggers the scheduling execution by sending a local signal to
the EP.

Since it might be necessary to execute a pending schedule from the EP
directly the scheduler is extended with the 'execute' member function
that performs the check that the scheduler is called from within the
EP and triggers the execution afterwards.

Issue #4927.
This commit is contained in:
Josef Söntgen 2023-06-15 12:57:03 +02:00 committed by Norman Feske
parent 1f1fafb0cf
commit caac994da8
8 changed files with 49 additions and 16 deletions

View File

@ -59,7 +59,7 @@ class Wireguard::Main : private Entrypoint::Io_progress_handler,
void _handle_signal()
{
lx_user_handle_io();
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
}
void _handle_config() { _config_rom.update(); }

View File

@ -14,6 +14,7 @@
* version 2.
*/
#include <base/entrypoint.h>
#include <util/list.h>
#include <lx_kit/task.h>
@ -29,14 +30,21 @@ class Lx_kit::Scheduler
{
private:
Scheduler(Scheduler const &) = delete;
Scheduler& operator=(const Scheduler&) = delete;
List<Task> _present_list { };
Task * _current { nullptr };
Task * _idle { nullptr };
Genode::Entrypoint &ep;
Genode::Entrypoint &_ep;
void _idle_pre_post_process();
void _schedule();
Signal_handler<Scheduler> _execute_schedule;
public:
Task & current();
@ -48,7 +56,10 @@ class Lx_kit::Scheduler
void add(Task & task);
void remove(Task & task);
void schedule();
void schedule() { _execute_schedule.local_submit(); }
void execute();
void unblock_irq_handler();
void unblock_time_handler();
@ -57,7 +68,12 @@ class Lx_kit::Scheduler
template <typename FN>
void for_each_task(FN const & fn);
Scheduler(Genode::Entrypoint &ep) : ep(ep) { }
Scheduler(Genode::Entrypoint &ep)
:
_ep { ep },
_execute_schedule { _ep, *this,
&Scheduler::_schedule }
{ }
};

View File

@ -65,9 +65,16 @@ extern "C" void lx_emul_start_kernel(void * dtb)
extern "C" void lx_emul_execute_kernel_until(int (*condition)(void*), void * args)
{
Lx_kit::env().scheduler.schedule();
do {
/*
* Assume we have to execute all scheduled tasks once before
* it makes sense to check the condition.
*/
Lx_kit::env().scheduler.execute();
if (condition(args))
break;
while (!condition(args)) {
Lx_kit::env().env.ep().wait_and_dispatch_one_io_signal();
}
} while (true);
}

View File

@ -101,11 +101,11 @@ Task & Scheduler::task(void * lx_task)
}
void Scheduler::schedule()
void Scheduler::execute()
{
/* sanity check that right thread & stack is in use */
auto const thread = Genode::Thread::myself();
if (!ep.rpc_ep().myself(addr_t(&thread))) {
if (!_ep.rpc_ep().myself(addr_t(&thread))) {
Genode::error("Lx_kit::Scheduler called by invalid thread/stack ",
thread->name(), " ",
Genode::Hex(thread->mystack().base), "-",
@ -114,6 +114,16 @@ void Scheduler::schedule()
Genode::sleep_forever();
}
_schedule();
}
/*
* This signal handler function must only be called from within an EP
* context, see check in 'execute()'.
*/
void Scheduler::_schedule()
{
_idle_pre_post_process();
/*

View File

@ -415,7 +415,7 @@ class Lx::Socket
void _handle()
{
lx_emul_task_unblock(socketcall_task_struct_ptr);
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
}
void _handle_blockade()
@ -511,7 +511,7 @@ void wifi_kick_socketcall()
if (!_socket) { return; }
lx_emul_task_unblock(socketcall_task_struct_ptr);
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
}

View File

@ -79,7 +79,7 @@ void Wifi::set_rfkill(bool blocked)
lx_emul_rfkill_switch_all(blocked);
lx_emul_task_unblock(rfkill_task_struct_ptr);
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
/*
* We have to open the device again after unblocking
@ -88,7 +88,7 @@ void Wifi::set_rfkill(bool blocked)
* unconditionally and that will bring the netdevice UP again.
*/
lx_emul_task_unblock(uplink_task_struct_ptr);
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
if (rfkill_helper.constructed())
rfkill_helper->submit_notification();
@ -120,7 +120,7 @@ struct Firmware_helper
if (_calling_task)
lx_emul_task_unblock((struct task_struct*)_calling_task);
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
}
Wifi::Firmware_request_handler &_request_handler;

View File

@ -177,7 +177,7 @@ void Framebuffer::Driver::config_update()
update_in_progress = true;
lx_emul_task_unblock(lx_user_task);
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
}

View File

@ -61,7 +61,7 @@ struct Main : private Entrypoint::Io_progress_handler
void handle_signal()
{
lx_user_handle_io();
Lx_kit::env().scheduler.schedule();
Lx_kit::env().scheduler.execute();
}
Main(Env & env) : env(env)