VFS: Replace global response handlers with local handlers

Replace the I/O response handler that is passed to the VFS at
construction with an object that is dynamically attached to handles.
This object shall also accept read-ready notifications, and plugins are
encouraged to keep handles awaiting ready-ready notifications separate
from handles that await I/O progress.

Replace the use of handle lists in plugins with handle queues, this
makes the code easier to understand and the ordering of notifications to
the application more explicit.

These changes replace the use of the Post_signal_hook from all VFS
plugins, applications must assume that read-ready and I/O notifications
occur during I/O signal dispatch and use an Io_progress_handler at its
entrypoints to defer response until after signal dispatching.

Fix #3257
This commit is contained in:
Emery Hemingway
2019-03-25 15:41:43 +01:00
committed by Christian Helmuth
parent e2ff776b35
commit a635873568
27 changed files with 1397 additions and 1341 deletions

View File

@ -179,7 +179,7 @@ struct Vfs::File : Vfs::Node
/**
* Check for data to read or write
*/
virtual bool poll(bool trigger_io_response, Vfs::Vfs_handle::Context *context) = 0;
virtual bool poll() { return true; }
virtual Lxip::ssize_t write(Lxip_vfs_file_handle &,
char const *src, Genode::size_t len,
@ -213,7 +213,6 @@ struct Vfs::Directory : Vfs::Node
typedef Vfs::Directory_service::Open_result Open_result;
virtual Open_result open(Vfs::File_system &fs,
Vfs::Io_response_handler &io_handler,
Genode::Allocator &alloc,
char const*, unsigned, Vfs::Vfs_handle**) = 0;
@ -250,7 +249,6 @@ struct Lxip::Socket_dir : Vfs::Directory
virtual sockaddr_storage &remote_addr() = 0;
virtual void close() = 0;
virtual bool closed() const = 0;
virtual void trigger_io_response(Vfs::Vfs_handle::Context *) = 0;
Socket_dir(char const *name) : Vfs::Directory(name) { }
};
@ -265,6 +263,9 @@ struct Vfs::Lxip_vfs_handle : Vfs::Vfs_handle
Lxip_vfs_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags)
: Vfs::Vfs_handle(fs, fs, alloc, status_flags) { }
/**
* Check if the file attached to this handle is ready to read
*/
virtual bool read_ready() = 0;
virtual Read_result read(char *dst,
@ -284,8 +285,15 @@ struct Vfs::Lxip_vfs_file_handle final : Vfs::Lxip_vfs_handle
Vfs::File *file;
List_element<Lxip_vfs_file_handle> file_le { this };
List_element<Lxip_vfs_file_handle> polling_le { this };
/* file association element */
List_element<Lxip_vfs_file_handle> file_le { this };
/* notification elements */
typedef Genode::Fifo_element<Lxip_vfs_file_handle> Fifo_element;
typedef Genode::Fifo<Fifo_element> Fifo;
Fifo_element read_ready_elem { *this };
Fifo_element io_progress_elem { *this };
char content_buffer[Lxip::MAX_DATA_LEN];
@ -304,7 +312,7 @@ struct Vfs::Lxip_vfs_file_handle final : Vfs::Lxip_vfs_handle
}
bool read_ready() override {
return (file) ? file->poll(false, nullptr) : false; }
return (file) ? file->poll() : false; }
Read_result read(char *dst, file_size count, file_size &out_count) override
{
@ -337,6 +345,12 @@ struct Vfs::Lxip_vfs_file_handle final : Vfs::Lxip_vfs_handle
virtual Sync_result sync() override {
return (file) ? file->sync() : Sync_result::SYNC_ERR_INVALID; }
void io_enqueue(Fifo &fifo)
{
if (!io_progress_elem.enqueued())
fifo.enqueue(io_progress_elem);
}
};
@ -365,23 +379,38 @@ struct Vfs::Lxip_vfs_dir_handle final : Vfs::Lxip_vfs_handle
/**
* List of open handles to potentially poll
*
* Could be a dynamic queue, but this works for now.
* Queues of open handles to poll
*/
static Vfs::Lxip_vfs_file_handles _polling_handles;
static Vfs::Lxip_vfs_file_handle::Fifo _io_progress_waiters;
static Vfs::Lxip_vfs_file_handle::Fifo _read_ready_waiters;
static void poll_all()
{
using namespace Linux;
_io_progress_waiters.for_each(
[&] (Vfs::Lxip_vfs_file_handle::Fifo_element &elem) {
Vfs::Lxip_vfs_file_handle &handle = elem.object();
if (handle.file) {
if (handle.file->poll()) {
/* do not notify again until some I/O queues */
_io_progress_waiters.remove(elem);
for (Genode::List_element<Vfs::Lxip_vfs_file_handle> *le = _polling_handles.first();
le; le = le->next())
{
Vfs::Lxip_vfs_file_handle *handle = le->object();
if (handle->file)
handle->file->poll(true, handle->context());
}
handle.io_progress_response();
}
}
});
_read_ready_waiters.for_each(
[&] (Vfs::Lxip_vfs_file_handle::Fifo_element &elem) {
Vfs::Lxip_vfs_file_handle &handle = elem.object();
if (handle.file) {
if (handle.file->poll()) {
/* do not notify again until notify_read_ready */
_read_ready_waiters.remove(elem);
handle.read_ready_response();
}
}
});
}
@ -415,7 +444,6 @@ class Vfs::Lxip_file : public Vfs::File
Genode::List_element<Vfs::Lxip_vfs_file_handle> *le = handles.first();
while (le) {
Vfs::Lxip_vfs_file_handle *h = le->object();
_polling_handles.remove(&h->polling_le);
handles.remove(&h->file_le);
h->file = nullptr;
le = handles.first();
@ -431,7 +459,7 @@ class Vfs::Lxip_file : public Vfs::File
};
class Vfs::Lxip_data_file : public Vfs::Lxip_file
class Vfs::Lxip_data_file final : public Vfs::Lxip_file
{
public:
@ -442,19 +470,13 @@ class Vfs::Lxip_data_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool trigger_io_response,
Vfs::Vfs_handle::Context *context) override
bool poll() override
{
using namespace Linux;
file f;
f.f_flags = 0;
if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET)) {
if (trigger_io_response)
_parent.trigger_io_response(context);
return true;
}
return false;
return (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET));
}
Lxip::ssize_t write(Lxip_vfs_file_handle &,
@ -477,7 +499,7 @@ class Vfs::Lxip_data_file : public Vfs::Lxip_file
return res;
}
Lxip::ssize_t read(Lxip_vfs_file_handle &,
Lxip::ssize_t read(Lxip_vfs_file_handle &handle,
char *dst, Genode::size_t len,
file_size /* ignored */) override
{
@ -490,14 +512,16 @@ class Vfs::Lxip_data_file : public Vfs::Lxip_file
msghdr msg = create_msghdr(nullptr, 0, len, &iov);
Lxip::ssize_t ret = _sock.ops->recvmsg(&_sock, &msg, len, MSG_DONTWAIT);
if (ret == -EAGAIN)
if (ret == -EAGAIN) {
handle.io_enqueue(_io_progress_waiters);
throw Would_block();
}
return ret;
}
};
class Vfs::Lxip_bind_file : public Vfs::Lxip_file
class Vfs::Lxip_bind_file final : public Vfs::Lxip_file
{
private:
@ -514,8 +538,6 @@ class Vfs::Lxip_bind_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool, Vfs::Vfs_handle::Context *) { return true; }
Lxip::ssize_t write(Lxip_vfs_file_handle &handle,
char const *src, Genode::size_t len,
file_size /* ignored */) override
@ -567,7 +589,7 @@ class Vfs::Lxip_bind_file : public Vfs::Lxip_file
};
class Vfs::Lxip_listen_file : public Vfs::Lxip_file
class Vfs::Lxip_listen_file final : public Vfs::Lxip_file
{
private:
@ -582,8 +604,6 @@ class Vfs::Lxip_listen_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool, Vfs::Vfs_handle::Context *) { return true; }
Lxip::ssize_t write(Lxip_vfs_file_handle &handle,
char const *src, Genode::size_t len,
file_size /* ignored */) override
@ -620,7 +640,7 @@ class Vfs::Lxip_listen_file : public Vfs::Lxip_file
};
class Vfs::Lxip_connect_file : public Vfs::Lxip_file
class Vfs::Lxip_connect_file final : public Vfs::Lxip_file
{
private:
@ -636,7 +656,7 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool trigger_io_response, Vfs::Vfs_handle::Context *context)
bool poll() override
{
/*
* The connect file is considered readable when the socket is
@ -647,12 +667,7 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
file f;
f.f_flags = 0;
if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLOUT_SET)) {
if (trigger_io_response)
_parent.trigger_io_response(context);
return true;
}
return false;
return (_sock.ops->poll(&f, &_sock, nullptr) & (POLLOUT_SET));
}
Lxip::ssize_t write(Lxip_vfs_file_handle &handle,
@ -736,7 +751,7 @@ class Vfs::Lxip_connect_file : public Vfs::Lxip_file
};
class Vfs::Lxip_local_file : public Vfs::Lxip_file
class Vfs::Lxip_local_file final : public Vfs::Lxip_file
{
public:
@ -747,8 +762,6 @@ class Vfs::Lxip_local_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool, Vfs::Vfs_handle::Context *) { return true; }
Lxip::ssize_t read(Lxip_vfs_file_handle &handle,
char *dst, Genode::size_t len,
file_size /* ignored */) override
@ -777,7 +790,7 @@ class Vfs::Lxip_local_file : public Vfs::Lxip_file
};
class Vfs::Lxip_remote_file : public Vfs::Lxip_file
class Vfs::Lxip_remote_file final : public Vfs::Lxip_file
{
public:
@ -788,8 +801,7 @@ class Vfs::Lxip_remote_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool trigger_io_response,
Vfs::Vfs_handle::Context *context) override
bool poll() override
{
using namespace Linux;
@ -798,16 +810,9 @@ class Vfs::Lxip_remote_file : public Vfs::Lxip_file
switch (_parent.parent().type()) {
case Lxip::Protocol_dir::TYPE_DGRAM:
if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET)) {
if (trigger_io_response)
_parent.trigger_io_response(context);
return true;
}
return false;
return (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN_SET));
case Lxip::Protocol_dir::TYPE_STREAM:
if (trigger_io_response)
_parent.trigger_io_response(context);
return true;
}
@ -837,7 +842,10 @@ class Vfs::Lxip_remote_file : public Vfs::Lxip_file
sizeof(handle.content_buffer), &iov);
int const res = _sock.ops->recvmsg(&_sock, &msg, 0, MSG_DONTWAIT|MSG_PEEK);
if (res == -EAGAIN) throw Would_block();
if (res == -EAGAIN) {
handle.io_enqueue(_io_progress_waiters);
throw Would_block();
}
if (res < 0) return -1;
}
break;
@ -879,7 +887,7 @@ class Vfs::Lxip_remote_file : public Vfs::Lxip_file
};
class Vfs::Lxip_accept_file : public Vfs::Lxip_file
class Vfs::Lxip_accept_file final : public Vfs::Lxip_file
{
public:
@ -890,20 +898,14 @@ class Vfs::Lxip_accept_file : public Vfs::Lxip_file
** File interface **
********************/
bool poll(bool trigger_io_response,
Vfs::Vfs_handle::Context *context) override
bool poll() override
{
using namespace Linux;
file f;
f.f_flags = 0;
if (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN)) {
if (trigger_io_response)
_parent.trigger_io_response(context);
return true;
}
return false;
return (_sock.ops->poll(&f, &_sock, nullptr) & (POLLIN));
}
Lxip::ssize_t read(Lxip_vfs_file_handle &handle,
@ -921,6 +923,8 @@ class Vfs::Lxip_accept_file : public Vfs::Lxip_file
Genode::strncpy(dst, "1\n", len);
return Genode::strlen(dst);
}
handle.io_enqueue(_io_progress_waiters);
throw Would_block();
}
};
@ -941,7 +945,6 @@ class Vfs::Lxip_socket_dir final : public Lxip::Socket_dir
Genode::Allocator &_alloc;
Lxip::Protocol_dir &_parent;
Vfs::Io_response_handler &_io_response_handler;
Linux::socket &_sock;
Vfs::File *_files[MAX_FILES];
@ -967,7 +970,6 @@ class Vfs::Lxip_socket_dir final : public Lxip::Socket_dir
{
Accept_socket_file() : Vfs::File("accept_socket") { }
bool poll(bool, Vfs::Vfs_handle::Context *) override { return true; }
} _accept_socket_file { };
char _name[Lxip::MAX_SOCKET_NAME_LEN];
@ -983,11 +985,10 @@ class Vfs::Lxip_socket_dir final : public Lxip::Socket_dir
Lxip_socket_dir(Genode::Allocator &alloc,
Lxip::Protocol_dir &parent,
Vfs::Io_response_handler &io_response_handler,
Linux::socket &sock)
:
Lxip::Socket_dir(_name),
_alloc(alloc), _parent(parent), _io_response_handler(io_response_handler),
_alloc(alloc), _parent(parent),
_sock(sock), id(parent.adopt_socket(*this))
{
Genode::snprintf(_name, sizeof(_name), "%u", id);
@ -1034,7 +1035,6 @@ class Vfs::Lxip_socket_dir final : public Lxip::Socket_dir
Open_result
open(Vfs::File_system &fs,
Vfs::Io_response_handler &,
Genode::Allocator &alloc,
char const *path, unsigned mode, Vfs::Vfs_handle**out_handle) override
{
@ -1081,10 +1081,6 @@ class Vfs::Lxip_socket_dir final : public Lxip::Socket_dir
void close() override { _closed = true; }
bool closed() const override { return _closed; }
void trigger_io_response(Vfs::Vfs_handle::Context *context) override
{
_io_response_handler.handle_io_response(context);
}
/*************************
** Directory interface **
@ -1143,13 +1139,12 @@ struct Vfs::Lxip_socket_handle final : Vfs::Lxip_vfs_handle
Lxip_socket_dir socket_dir;
Lxip_socket_handle(Vfs::File_system &fs,
Vfs::Io_response_handler &io_handler,
Genode::Allocator &alloc,
Lxip::Protocol_dir &parent,
Linux::socket &sock)
:
Lxip_vfs_handle(fs, alloc, 0),
socket_dir(alloc, parent, io_handler, sock)
socket_dir(alloc, parent, sock)
{ }
bool read_ready() override { return true; }
@ -1190,8 +1185,7 @@ Vfs::Lxip_socket_dir::_accept_new_socket(Vfs::File_system &fs,
try {
Vfs::Lxip_socket_handle *handle = new (alloc)
Vfs::Lxip_socket_handle(fs, _io_response_handler, alloc,
_parent, *new_sock);
Vfs::Lxip_socket_handle(fs, alloc, _parent, *new_sock);
*out_handle = handle;
return Vfs::Directory_service::Open_result::OPEN_OK;
}
@ -1211,13 +1205,10 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
Genode::Allocator &_alloc;
Vfs::File_system &_parent;
Vfs::Io_response_handler &_io_response_handler;
struct New_socket_file : Vfs::File
{
New_socket_file() : Vfs::File("new_socket") { }
bool poll(bool, Vfs::Vfs_handle::Context *) override { return true; }
} _new_socket_file { };
Type const _type;
@ -1260,7 +1251,6 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
Vfs::Directory_service::Open_result
_open_new_socket(Vfs::File_system &fs,
Vfs::Io_response_handler &io_handler,
Genode::Allocator &alloc,
Vfs::Vfs_handle **out_handle)
{
@ -1289,8 +1279,7 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
try {
Vfs::Lxip_socket_handle *handle = new (alloc)
Vfs::Lxip_socket_handle(fs, io_handler, alloc,
*this, *sock);
Vfs::Lxip_socket_handle(fs, alloc, *this, *sock);
*out_handle = handle;
return Vfs::Directory_service::Open_result::OPEN_OK;
}
@ -1309,13 +1298,11 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
Protocol_dir_impl(Genode::Allocator &alloc,
Vfs::File_system &parent,
Vfs::Io_response_handler &io_response_handler,
char const *name,
Lxip::Protocol_dir::Type type)
:
Protocol_dir(name),
_alloc(alloc), _parent(parent), _io_response_handler(io_response_handler),
_type(type)
_alloc(alloc), _parent(parent), _type(type)
{
for (Genode::size_t i = 0; i < MAX_NODES; i++) {
_nodes[i] = nullptr;
@ -1377,14 +1364,13 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
Type type() { return _type; }
Open_result open(Vfs::File_system &fs,
Vfs::Io_response_handler &io_handler,
Genode::Allocator &alloc,
char const *path, unsigned mode,
Vfs::Vfs_handle **out_handle) override
{
if (strcmp(path, "/new_socket") == 0) {
if (mode != 0) return Open_result::OPEN_ERR_NO_PERM;
return _open_new_socket(fs, io_handler, alloc, out_handle);
return _open_new_socket(fs, alloc, out_handle);
}
path++;
@ -1397,7 +1383,7 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
Vfs::Directory *dir = dynamic_cast<Directory *>(_nodes[i]);
if (dir) {
path += (p - path);
return dir->open(fs, io_handler, alloc, path, mode, out_handle);
return dir->open(fs, alloc, path, mode, out_handle);
}
}
}
@ -1482,7 +1468,7 @@ class Lxip::Protocol_dir_impl : public Protocol_dir
};
class Vfs::Lxip_address_file : public Vfs::File
class Vfs::Lxip_address_file final : public Vfs::File
{
private:
@ -1493,8 +1479,6 @@ class Vfs::Lxip_address_file : public Vfs::File
Lxip_address_file(char const *name, unsigned int &numeric_address)
: Vfs::File(name), _numeric_address(numeric_address) { }
bool poll(bool, Vfs::Vfs_handle::Context *) { return true; }
Lxip::ssize_t read(Lxip_vfs_file_handle &handle,
char *dst, Genode::size_t len,
file_size /* ignored */) override
@ -1517,7 +1501,7 @@ class Vfs::Lxip_address_file : public Vfs::File
};
class Vfs::Lxip_link_state_file : public Vfs::File
class Vfs::Lxip_link_state_file final : public Vfs::File
{
private:
@ -1528,8 +1512,6 @@ class Vfs::Lxip_link_state_file : public Vfs::File
Lxip_link_state_file(char const *name, bool &numeric_link_state)
: Vfs::File(name), _numeric_link_state(numeric_link_state) { }
bool poll(bool, Vfs::Vfs_handle::Context *) { return true; }
Lxip::ssize_t read(Lxip_vfs_file_handle &handle,
char *dst, Genode::size_t len,
file_size /* ignored */) override
@ -1571,12 +1553,11 @@ class Vfs::Lxip_file_system : public Vfs::File_system,
Genode::Entrypoint &_ep;
Genode::Allocator &_alloc;
Vfs::Io_response_handler &_io_response_handler;
Lxip::Protocol_dir_impl _tcp_dir {
_alloc, *this, _io_response_handler, "tcp", Lxip::Protocol_dir::TYPE_STREAM };
_alloc, *this, "tcp", Lxip::Protocol_dir::TYPE_STREAM };
Lxip::Protocol_dir_impl _udp_dir {
_alloc, *this, _io_response_handler, "udp", Lxip::Protocol_dir::TYPE_DGRAM };
_alloc, *this, "udp", Lxip::Protocol_dir::TYPE_DGRAM };
Lxip_address_file _address { "address", ic_myaddr };
Lxip_address_file _netmask { "netmask", ic_netmask };
@ -1638,8 +1619,7 @@ class Vfs::Lxip_file_system : public Vfs::File_system,
Lxip_file_system(Vfs::Env &env, Genode::Xml_node config)
:
Directory(""),
_ep(env.env().ep()), _alloc(env.alloc()),
_io_response_handler(env.io_handler())
_ep(env.env().ep()), _alloc(env.alloc())
{
apply_config(config);
}
@ -1702,7 +1682,6 @@ class Vfs::Lxip_file_system : public Vfs::File_system,
Vfs::Directory::Open_result
open(Vfs::File_system &fs,
Vfs::Io_response_handler &io_handler,
Genode::Allocator &alloc,
char const*, unsigned, Vfs::Vfs_handle**) override {
return Vfs::Directory::Open_result::OPEN_ERR_UNACCESSIBLE; }
@ -1825,10 +1804,10 @@ class Vfs::Lxip_file_system : public Vfs::File_system,
try {
if (Genode::strcmp(path, "/tcp", 4) == 0)
return _tcp_dir.open(*this, _io_response_handler, alloc,
return _tcp_dir.open(*this, alloc,
&path[4], mode, out_handle);
if (Genode::strcmp(path, "/udp", 4) == 0)
return _udp_dir.open(*this, _io_response_handler, alloc,
return _udp_dir.open(*this, alloc,
&path[4], mode, out_handle);
Vfs::Node *node = _lookup(path);
@ -1875,8 +1854,10 @@ class Vfs::Lxip_file_system : public Vfs::File_system,
Lxip_vfs_file_handle *file_handle =
dynamic_cast<Vfs::Lxip_vfs_file_handle*>(handle);
if (file_handle)
_polling_handles.remove(&file_handle->polling_le);
if (file_handle) {
_io_progress_waiters.remove(file_handle->io_progress_elem);
_read_ready_waiters.remove(file_handle->read_ready_elem);
}
Genode::destroy(handle->alloc(), handle);
}
@ -1930,9 +1911,9 @@ class Vfs::Lxip_file_system : public Vfs::File_system,
Lxip_vfs_file_handle *handle =
dynamic_cast<Vfs::Lxip_vfs_file_handle *>(vfs_handle);
if (handle && dynamic_cast<Lxip_file*>(handle->file)) {
_polling_handles.remove(&handle->polling_le);
_polling_handles.insert(&handle->polling_le);
if (handle) {
if (!handle->read_ready_elem.enqueued())
_read_ready_waiters.enqueue(handle->read_ready_elem);
return true;
}