dde_rump: support blocking I/O operations from non-ep threads

Issue #4433
This commit is contained in:
Christian Prochaska 2022-02-24 20:47:32 +01:00 committed by Christian Helmuth
parent 87c5f91a74
commit 88dec4cc94

View File

@ -13,6 +13,7 @@
#include "sched.h" #include "sched.h"
#include <base/allocator_avl.h> #include <base/allocator_avl.h>
#include <base/registry.h>
#include <block_session/connection.h> #include <block_session/connection.h>
#include <rump/env.h> #include <rump/env.h>
#include <rump_fs/fs.h> #include <rump_fs/fs.h>
@ -21,6 +22,60 @@ static const bool verbose = false;
enum { GENODE_FD = 64 }; enum { GENODE_FD = 64 };
class Io_signal_blockade : public Genode::Io_signal_handler<Io_signal_blockade>
{
private:
Genode::Entrypoint &_ep;
Genode::Thread const *_ep_thread_ptr;
bool _signal_handler_called { false };
typedef Genode::Registered_no_delete<Genode::Blockade> Registered_blockade;
Genode::Registry<Registered_blockade> _blockades;
void _handle_io_signal()
{
/* unblock the ep thread */
_signal_handler_called = true;
/* unblock non-ep threads */
_blockades.for_each([] (Genode::Blockade &blockade) {
blockade.wakeup();
});
}
public:
Io_signal_blockade(Entrypoint &ep)
: Io_signal_handler(ep, *this,
&Io_signal_blockade::_handle_io_signal),
_ep(ep)
{
_ep_thread_ptr = Genode::Thread::myself();
}
void block_for_io()
{
if (Genode::Thread::myself() == _ep_thread_ptr) {
while (!_signal_handler_called)
_ep.wait_and_dispatch_one_io_signal();
_signal_handler_called = false;
} else {
Registered_blockade _blockade { _blockades };
_blockade.block();
}
}
};
/** /**
* Block session connection * Block session connection
*/ */
@ -46,14 +101,14 @@ class Backend
Block::Connection<Job> _session { Rump::env().env(), &_alloc }; Block::Connection<Job> _session { Rump::env().env(), &_alloc };
Block::Session::Info _info { _session.info() }; Block::Session::Info _info { _session.info() };
Genode::Mutex _session_mutex; Genode::Mutex _session_mutex;
Io_signal_blockade _io_signal_blockade { _ep };
bool _blocked_for_synchronous_io = false; int _blocked_for_synchronous_io = 0;
Genode::Io_signal_handler<Backend> _signal_handler {
_ep, *this, &Backend::_update_jobs };
void _update_jobs() void _update_jobs()
{ {
Genode::Mutex::Guard guard(_session_mutex);
struct Update_jobs_policy struct Update_jobs_policy
{ {
void produce_write_content(Job &job, Block::seek_off_t offset, void produce_write_content(Job &job, Block::seek_off_t offset,
@ -78,6 +133,17 @@ class Backend
_session.update_jobs(policy); _session.update_jobs(policy);
} }
/*
* This function can be called by multiple threads
* (ep and 'pdaemon' have been observed so far in practice).
* A non-ep thread cannot dispatch signals and needs to block
* until the ep has processed the signal. Therefore it is
* important that the ep has the chance to process the signal
* even in the case that it calls this function while a non-ep
* thread is already blocking here. For this reason the
* '_session_mutex' cannot be held over the scope of the whole
* function.
*/
bool _synchronous_io(void *ptr, Block::Operation const &operation) bool _synchronous_io(void *ptr, Block::Operation const &operation)
{ {
Genode::Constructible<Job> job { }; Genode::Constructible<Job> job { };
@ -85,20 +151,22 @@ class Backend
{ {
Genode::Mutex::Guard guard(_session_mutex); Genode::Mutex::Guard guard(_session_mutex);
job.construct(_session, ptr, operation); job.construct(_session, ptr, operation);
_blocked_for_synchronous_io = true; _blocked_for_synchronous_io++;
} }
_update_jobs(); _update_jobs();
while (!job->completed()) while (!job->completed()) {
_ep.wait_and_dispatch_one_io_signal(); _io_signal_blockade.block_for_io();
_update_jobs();
}
bool const success = job->success; bool const success = job->success;
{ {
Genode::Mutex::Guard guard(_session_mutex); Genode::Mutex::Guard guard(_session_mutex);
job.destruct(); job.destruct();
_blocked_for_synchronous_io = false; _blocked_for_synchronous_io--;
} }
return success; return success;
@ -108,7 +176,7 @@ class Backend
Backend() Backend()
{ {
_session.sigh(_signal_handler); _session.sigh(_io_signal_blockade);
} }
uint64_t block_count() const { return _info.block_count; } uint64_t block_count() const { return _info.block_count; }
@ -142,7 +210,7 @@ class Backend
return success; return success;
} }
bool blocked_for_io() const { return _blocked_for_synchronous_io; } bool blocked_for_io() const { return _blocked_for_synchronous_io > 0; }
}; };