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:
Ehmry - 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;
}

View File

@ -383,7 +383,7 @@ class Vfs::Rump_file_system : public File_system
{
for (Rump_watch_handle *h = _watchers.first(); h; h = h->next()) {
if (h->kqueue_check())
_env.watch_handler().handle_watch_response(h->context());
h->watch_response();
}
}

View File

@ -532,7 +532,7 @@ class Genode::File_content
};
class Genode::Watcher : Interface, Vfs::Vfs_watch_handle::Context
class Genode::Watcher
{
private:
@ -542,14 +542,17 @@ class Genode::Watcher : Interface, Vfs::Vfs_watch_handle::Context
Watcher(Watcher const &);
Watcher &operator = (Watcher const &);
Vfs::Vfs_watch_handle mutable *_handle = nullptr;
Vfs::Vfs_watch_handle mutable *_handle { nullptr };
void _watch(Vfs::File_system &fs, Allocator &alloc, Directory::Path const path)
void _watch(Vfs::File_system &fs, Allocator &alloc, Directory::Path const path,
Vfs::Watch_response_handler &handler)
{
Vfs::Directory_service::Watch_result res =
fs.watch(path.string(), &_handle, alloc);
if (res != Vfs::Directory_service::WATCH_OK)
if (res == Vfs::Directory_service::WATCH_OK)
_handle->handler(&handler);
else
error("failed to watch '", path, "'");
}
@ -560,21 +563,21 @@ class Genode::Watcher : Interface, Vfs::Vfs_watch_handle::Context
public:
Watcher(Directory const &dir, Directory::Path const &rel_path)
Watcher(Directory const &dir, Directory::Path const &rel_path,
Vfs::Watch_response_handler &handler)
{
_watch(_mutable(dir)._fs, _mutable(dir)._alloc,
Directory::join(dir._path, rel_path));
_handle->context(this);
Directory::join(dir._path, rel_path), handler);
}
~Watcher() { _handle->fs().close(_handle); }
virtual void handle_watch_notification() { }
};
template <typename T>
class Genode::Watch_handler : Watcher
class Genode::Watch_handler : public Vfs::Watch_response_handler,
private Watcher
{
private:
@ -586,10 +589,10 @@ class Genode::Watch_handler : Watcher
Watch_handler(Directory &dir, Directory::Path const &rel_path,
T &obj, void (T::*member)())
:
Watcher(dir, rel_path), _obj(obj), _member(member)
Watcher(dir, rel_path, *this), _obj(obj), _member(member)
{ }
void handle_watch_notification() override { (_obj.*_member)(); }
void watch_response() override { (_obj.*_member)(); }
};
#endif /* _INCLUDE__GEMS__VFS_H_ */

View File

@ -33,8 +33,9 @@ struct Fs_query::Watched_file
Watcher _watcher;
Watched_file(Directory const &dir, File_content::Path name)
: _name(name), _watcher(dir, name) { }
Watched_file(Directory const &dir, File_content::Path name,
Vfs::Watch_response_handler &handler)
: _name(name), _watcher(dir, name, handler) { }
virtual ~Watched_file() { }
@ -92,19 +93,20 @@ struct Fs_query::Watched_directory
Directory const _dir;
Registry<Registered<Watched_file> > _files { };
Watcher _watcher;
Watched_directory(Allocator &alloc, Directory &other, Directory::Path const &rel_path)
Registry<Registered<Watched_file> > _files { };
Watched_directory(Allocator &alloc, Directory &other, Directory::Path const &rel_path,
Vfs::Watch_response_handler &handler)
:
_alloc(alloc), _rel_path(rel_path),
_dir(other, rel_path), _watcher(other, rel_path)
_dir(other, rel_path), _watcher(other, rel_path, handler)
{
_dir.for_each_entry([&] (Directory::Entry const &entry) {
if (entry.type() == Vfs::Directory_service::DIRENT_TYPE_FILE) {
try {
new (_alloc) Registered<Watched_file>(_files, _dir, entry.name());
new (_alloc) Registered<Watched_file>(_files, _dir, entry.name(), handler);
} catch (...) { }
}
});
@ -142,7 +144,7 @@ struct Fs_query::Main : Vfs::Watch_response_handler
/**
* Vfs::Watch_response_handler interface
*/
void handle_watch_response(Vfs::Vfs_watch_handle::Context*) override
void watch_response() override
{
Signal_transmitter(_config_handler).submit();
}
@ -151,17 +153,11 @@ struct Fs_query::Main : Vfs::Watch_response_handler
{
Main &_main;
struct Io_response_dummy : Vfs::Io_response_handler {
void handle_io_response(Vfs::Vfs_handle::Context*) override { }
} _io_dummy { };
Vfs_env(Main &main) : _main(main) { }
Genode::Env &env() override { return _main._env; }
Allocator &alloc() override { return _main._heap; }
Vfs::File_system &root_dir() override { return _main._root_dir_fs; }
Vfs::Io_response_handler &io_handler() override { return _io_dummy; }
Vfs::Watch_response_handler &watch_handler() override { return _main; }
} _vfs_env { *this };
@ -201,7 +197,7 @@ struct Fs_query::Main : Vfs::Watch_response_handler
config.for_each_sub_node("query", [&] (Xml_node query) {
Directory::Path const path = query.attribute_value("path", Directory::Path());
new (_heap) Registered<Watched_directory>(_dirs, _heap, _root_dir, path);
new (_heap) Registered<Watched_directory>(_dirs, _heap, _root_dir, path, *this);
});
_reporter.generate([&] (Xml_generator &xml) {

View File

@ -38,21 +38,11 @@ struct Fs_tool::Main
{
Main &_main;
struct Io_response_dummy : Vfs::Io_response_handler {
void handle_io_response(Vfs::Vfs_handle::Context*) override { }
} _io_dummy { };
struct Watch_response_dummy: Vfs::Watch_response_handler {
void handle_watch_response(Vfs::Vfs_watch_handle::Context*) override { }
} _watch_dummy { };
Vfs_env(Main &main) : _main(main) { }
Genode::Env &env() override { return _main._env; }
Allocator &alloc() override { return _main._heap; }
Vfs::File_system &root_dir() override { return _main._root_dir_fs; }
Vfs::Io_response_handler &io_handler() override { return _io_dummy; }
Vfs::Watch_response_handler &watch_handler() override { return _watch_dummy; }
} _vfs_env { *this };

View File

@ -96,7 +96,7 @@ struct Menu_view::Main
Heap _heap { _env.ram(), _env.rm() };
struct Vfs_env : Vfs::Env, Vfs::Io_response_handler, Vfs::Watch_response_handler
struct Vfs_env : Vfs::Env
{
Genode::Env &_env;
Allocator &_alloc;
@ -105,14 +105,9 @@ struct Menu_view::Main
Vfs_env(Genode::Env &env, Allocator &alloc, Vfs::File_system &vfs)
: _env(env), _alloc(alloc), _vfs(vfs) { }
void handle_io_response (Vfs::Vfs_handle::Context *) override { }
void handle_watch_response(Vfs::Vfs_watch_handle::Context*) override { }
Genode::Env &env() override { return _env; }
Allocator &alloc() override { return _alloc; }
Vfs::File_system &root_dir() override { return _vfs; }
Io_response_handler &io_handler() override { return *this; }
Watch_response_handler &watch_handler() override { return *this; }
} _vfs_env;

View File

@ -86,8 +86,8 @@ class Vfs_audit::File_system : public Vfs::File_system
void sync_state()
{
audit->seek(Vfs_handle::seek());
audit->context(context());
if (audit)
audit->seek(Vfs_handle::seek());
}
Handle(Vfs_audit::File_system &fs,
@ -95,6 +95,12 @@ class Vfs_audit::File_system : public Vfs::File_system
int flags,
char const *path)
: Vfs_handle(fs, fs, alloc, flags), path(path) { };
void handler(Io_response_handler *rh) override
{
Vfs_handle::handler(rh);
if (audit) audit->handler(rh);
}
};
public:

View File

@ -32,11 +32,6 @@ namespace Vfs_ttf {
class Local_factory;
class File_system;
struct Dummy_io_response_handler : Vfs::Io_response_handler
{
void handle_io_response(Vfs::Vfs_handle::Context *) override { };
};
typedef Text_painter::Font Font;
}
@ -137,7 +132,6 @@ struct Vfs_ttf::Local_factory : File_system_factory
class Vfs_ttf::File_system : private Local_factory,
private Dummy_io_response_handler,
public Vfs::Dir_file_system
{
private:

View File

@ -338,7 +338,8 @@ static void suspended_callback();
* secondary stack for the application task. Context switching uses
* setjmp/longjmp.
*/
struct Libc::Kernel final : Genode::Entrypoint::Io_progress_handler
struct Libc::Kernel final : Vfs::Io_response_handler,
Genode::Entrypoint::Io_progress_handler
{
private:
@ -346,7 +347,7 @@ struct Libc::Kernel final : Genode::Entrypoint::Io_progress_handler
Genode::Allocator &_heap;
Env_implementation _libc_env { _env, _heap };
Vfs_plugin _vfs { _libc_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 };
@ -356,6 +357,9 @@ struct Libc::Kernel final : Genode::Entrypoint::Io_progress_handler
bool _valid_user_context = false;
bool _dispatch_pending_io_signals = false;
/* io_progress_handler marker */
bool _io_ready { false };
Genode::Thread &_myself { *Genode::Thread::myself() };
addr_t _kernel_stack = Thread::mystack().top;
@ -763,20 +767,42 @@ struct Libc::Kernel final : Genode::Entrypoint::Io_progress_handler
}
}
/**
* Entrypoint::Io_progress_handler interface
*/
/****************************************
** 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
{
/* some contexts may have been deblocked from select() */
if (libc_select_notify)
/*
* 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();
/*
* resume all as any VFS context may have
* been deblocked from blocking I/O
*/
Kernel::resume_all();
}
}
};

View File

@ -222,10 +222,12 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags,
/* FIXME error cleanup code leaks resources! */
if (!fd) {
handle->close();
errno = EMFILE;
return nullptr;
}
handle->handler(&_response_handler);
fd->flags = flags & O_ACCMODE;
return fd;
@ -301,13 +303,16 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags,
/* FIXME error cleanup code leaks resources! */
if (!fd) {
handle->close();
errno = EMFILE;
return nullptr;
}
handle->handler(&_response_handler);
fd->flags = flags & (O_ACCMODE|O_NONBLOCK|O_APPEND);
if ((flags & O_TRUNC) && (ftruncate(fd, 0) == -1)) {
handle->close();
errno = EINVAL; /* XXX which error code fits best ? */
return nullptr;
}
@ -319,6 +324,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags,
int Libc::Vfs_plugin::close(Libc::File_descriptor *fd)
{
Vfs::Vfs_handle *handle = vfs_handle(fd);
/* XXX: mark the handle as requiring sync or not */
_vfs_sync(handle);
VFS_THREAD_SAFE(handle->close());
Libc::file_descriptor_allocator()->free(fd);
@ -963,6 +969,8 @@ int Libc::Vfs_plugin::symlink(const char *oldpath, const char *newpath)
Vfs::file_size count = ::strlen(oldpath) + 1;
Vfs::file_size out_count = 0;
handle->handler(&_response_handler);
struct Check : Libc::Suspend_functor
{
bool retry { false };
@ -1031,6 +1039,8 @@ ssize_t Libc::Vfs_plugin::readlink(const char *path, char *buf, ::size_t buf_siz
return Errno(EACCES);
}
symlink_handle->handler(&_response_handler);
{
struct Check : Libc::Suspend_functor
{

View File

@ -7,7 +7,7 @@
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
* Copyright (C) 2014-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.
@ -39,9 +39,9 @@ class Libc::Vfs_plugin : public Libc::Plugin
{
private:
Genode::Allocator &_alloc;
Vfs::File_system &_root_dir;
Genode::Allocator &_alloc;
Vfs::File_system &_root_dir;
Vfs::Io_response_handler &_response_handler;
void _open_stdio(Genode::Xml_node const &node, char const *attr,
int libc_fd, unsigned flags)
@ -150,9 +150,11 @@ class Libc::Vfs_plugin : public Libc::Plugin
public:
Vfs_plugin(Libc::Env &env, Genode::Allocator &alloc)
Vfs_plugin(Libc::Env &env,
Genode::Allocator &alloc,
Vfs::Io_response_handler &handler)
:
_alloc(alloc), _root_dir(env.vfs())
_alloc(alloc), _root_dir(env.vfs()), _response_handler(handler)
{
using Genode::Xml_node;

View File

@ -238,7 +238,7 @@ class Fatfs::File_system : public Vfs::File_system
void _notify(File &file)
{
for (Fatfs_file_watch_handle *h = file.watchers.first(); h; h = h->next())
_vfs_env.watch_handler().handle_watch_response(h->context());
h->watch_response();
}
/**
@ -250,10 +250,9 @@ class Fatfs::File_system : public Vfs::File_system
Path parent(path);
parent.strip_last_element();
for (Fatfs_dir_watch_handle *h = _dir_watchers.first(); h; h = h->next()) {
for (Fatfs_dir_watch_handle *h = _dir_watchers.first(); h; h = h->next())
if (h->path == parent)
_vfs_env.watch_handler().handle_watch_response(h->context());
}
h->watch_response();
}
/**
@ -293,8 +292,7 @@ class Fatfs::File_system : public Vfs::File_system
{
handle->file = nullptr;
file.watchers.remove(handle);
if (auto *ctx = handle->context())
_vfs_env.watch_handler().handle_watch_response(ctx);
handle->watch_response();
}
_close(file);
}

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2016-2018 Genode Labs GmbH
* 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.
@ -239,12 +239,15 @@ struct Lwip::Lwip_file_handle final : Lwip_handle, private Lwip_handle_list::Ele
Socket_dir *socket;
typedef Genode::Fifo_element<Lwip_file_handle> Fifo_element;
typedef Genode::Fifo<Lwip_file_handle::Fifo_element> Fifo;
Fifo_element _read_ready_waiter { *this };
Fifo_element _io_progress_waiter { *this };
int in_transit = 0;
Kind kind;
bool notify = false;
void print(Genode::Output &output) const;
Lwip_file_handle(Vfs::File_system &fs, Allocator &alloc, int status_flags,
@ -257,6 +260,8 @@ struct Lwip::Lwip_file_handle final : Lwip_handle, private Lwip_handle_list::Ele
Write_result write(char const *src, file_size count,
file_size &out_count) override;
bool notify_read_ready();
};
@ -275,14 +280,15 @@ struct Lwip::Socket_dir : Lwip::Directory
Genode::Allocator &alloc;
Vfs::Io_response_handler &io_handler;
unsigned const _num;
Socket_name const _name { name_from_num(_num) };
/* lists of handles opened at this socket */
Lwip_handle_list handles { };
Lwip_file_handle::Fifo read_ready_queue { };
Lwip_file_handle::Fifo io_progress_queue { };
enum State {
NEW,
BOUND,
@ -293,8 +299,8 @@ struct Lwip::Socket_dir : Lwip::Directory
CLOSED
};
Socket_dir(unsigned num, Genode::Allocator &alloc, Vfs::Io_response_handler &io_handler)
: alloc(alloc), io_handler(io_handler), _num(num) { };
Socket_dir(unsigned num, Genode::Allocator &alloc)
: alloc(alloc), _num(num) { };
~Socket_dir()
@ -363,18 +369,25 @@ struct Lwip::Socket_dir : Lwip::Directory
virtual bool read_ready(Lwip_file_handle&) = 0;
void handle_io(int mask)
/**
* Notify handles waiting for this PCB / socket to be ready
*/
void process_read_ready()
{
for (Lwip::Lwip_file_handle *h = handles.first();
h; h = h->next())
{
if (h->kind & mask) {
io_handler.handle_io_response(h->context());
}
}
/* invoke all handles waiting for read_ready */
read_ready_queue.dequeue_all([] (Lwip_file_handle::Fifo_element &elem) {
elem.object().read_ready_response(); });
}
virtual Sync_result complete_sync() = 0;
/**
* Notify handles blocked by operations on this PCB / socket
*/
void process_io()
{
/* invoke all handles waiting for IO progress */
io_progress_queue.dequeue_all([] (Lwip_file_handle::Fifo_element &elem) {
elem.object().io_progress_response(); });
}
};
@ -388,16 +401,27 @@ Lwip::Lwip_file_handle::Lwip_file_handle(Vfs::File_system &fs, Allocator &alloc,
Lwip::Lwip_file_handle::~Lwip_file_handle()
{
if (socket)
if (socket) {
socket->handles.remove(this);
if (_read_ready_waiter.enqueued()) {
socket->read_ready_queue.remove(_read_ready_waiter);
}
if (_io_progress_waiter.enqueued()) {
socket->io_progress_queue.remove(_io_progress_waiter);
}
}
}
Lwip::Read_result Lwip::Lwip_file_handle::read(char *dst, file_size count,
file_size &out_count)
{
return (socket)
? socket->read(*this, dst, count, out_count)
: Read_result::READ_ERR_INVALID;
Lwip::Read_result result = Read_result::READ_ERR_INVALID;
if (socket) {
result = socket->read(*this, dst, count, out_count);
if (result == Read_result::READ_QUEUED && !_io_progress_waiter.enqueued())
socket->io_progress_queue.enqueue(_io_progress_waiter);
}
return result;
}
Lwip::Write_result Lwip::Lwip_file_handle::write(char const *src, file_size count,
@ -408,6 +432,16 @@ Lwip::Write_result Lwip::Lwip_file_handle::write(char const *src, file_size coun
: Write_result::WRITE_ERR_INVALID;
}
bool Lwip::Lwip_file_handle::notify_read_ready()
{
if (socket) {
if (!_read_ready_waiter.enqueued())
socket->read_ready_queue.enqueue(_read_ready_waiter);
return true;
}
return false;
}
void Lwip::Lwip_file_handle::print(Genode::Output &output) const
{
@ -457,9 +491,8 @@ class Lwip::Protocol_dir_impl final : public Protocol_dir
{
private:
Genode::Allocator &_alloc;
Vfs::Io_response_handler &_io_handler;
Genode::Entrypoint &_ep;
Genode::Allocator &_alloc;
Genode::Entrypoint &_ep;
Genode::List<SOCKET_DIR> _socket_dirs { };
@ -472,7 +505,7 @@ class Lwip::Protocol_dir_impl final : public Protocol_dir
friend class Udp_socket_dir;
Protocol_dir_impl(Vfs::Env &vfs_env)
: _alloc(vfs_env.alloc()), _io_handler(vfs_env.io_handler()), _ep(vfs_env.env().ep()) { }
: _alloc(vfs_env.alloc()), _ep(vfs_env.env().ep()) { }
SOCKET_DIR *lookup(char const *name)
{
@ -581,7 +614,7 @@ class Lwip::Protocol_dir_impl final : public Protocol_dir
}
SOCKET_DIR *new_socket = new (alloc)
SOCKET_DIR(id, *this, alloc, _io_handler, _ep, pcb);
SOCKET_DIR(id, *this, alloc, _ep, pcb);
_socket_dirs.insert(new_socket);
return *new_socket;
}
@ -638,7 +671,7 @@ class Lwip::Protocol_dir_impl final : public Protocol_dir
void notify()
{
for (SOCKET_DIR *sd = _socket_dirs.first(); sd; sd = sd->next()) {
sd->handle_io(~0U);
sd->process_io();
}
}
};
@ -736,10 +769,9 @@ class Lwip::Udp_socket_dir final :
Udp_socket_dir(unsigned num, Udp_proto_dir &proto_dir,
Genode::Allocator &alloc,
Vfs::Io_response_handler &io_handler,
Genode::Entrypoint &,
udp_pcb *pcb)
: Socket_dir(num, alloc, io_handler),
: Socket_dir(num, alloc),
_proto_dir(proto_dir), _pcb(pcb ? pcb : udp_new())
{
ip_addr_set_zero(&_to_addr);
@ -769,7 +801,8 @@ class Lwip::Udp_socket_dir final :
pbuf_free(buf);
}
handle_io(Lwip_file_handle::REMOTE|Lwip_file_handle::DATA_READY);
process_io();
process_read_ready();
}
@ -964,8 +997,6 @@ class Lwip::Udp_socket_dir final :
return Write_result::WRITE_ERR_INVALID;
}
Sync_result complete_sync() override { return Sync_result::SYNC_OK; };
};
@ -1027,10 +1058,9 @@ class Lwip::Tcp_socket_dir final :
Tcp_socket_dir(unsigned num, Tcp_proto_dir &proto_dir,
Genode::Allocator &alloc,
Vfs::Io_response_handler &io_handler,
Genode::Entrypoint &ep,
tcp_pcb *pcb)
: Socket_dir(num, alloc, io_handler), _proto_dir(proto_dir),
: Socket_dir(num, alloc), _proto_dir(proto_dir),
_ep(ep), _pcb(pcb ? pcb : tcp_new()), state(pcb ? READY : NEW)
{
/* 'this' will be the argument to LwIP callbacks */
@ -1073,7 +1103,8 @@ class Lwip::Tcp_socket_dir final :
tcp_arg(newpcb, elem);
tcp_recv(newpcb, tcp_delayed_recv_callback);
handle_io(Lwip_file_handle::ACCEPT|Lwip_file_handle::PENDING);
process_io();
process_read_ready();
return ERR_OK;
}
@ -1103,7 +1134,8 @@ class Lwip::Tcp_socket_dir final :
_pcb = NULL;
/* churn the application */
handle_io(~0U);
process_io();
process_read_ready();
}
/**
@ -1435,20 +1467,6 @@ class Lwip::Tcp_socket_dir final :
return Write_result::WRITE_ERR_INVALID;
}
Sync_result complete_sync() override
{
switch (state) {
case CONNECT:
/* sync will queue until the socket is connected and ready */
return Sync_result::SYNC_QUEUED;
case CLOSED:
/* assumed to be caused by error */
return Sync_result::SYNC_ERR_INVALID;
default:
return Sync_result::SYNC_OK;
}
}
};
@ -1483,9 +1501,8 @@ err_t tcp_connect_callback(void *arg, struct tcp_pcb *pcb, err_t)
Lwip::Tcp_socket_dir *socket_dir = static_cast<Lwip::Tcp_socket_dir *>(arg);
socket_dir->state = Lwip::Tcp_socket_dir::READY;
socket_dir->handle_io(
Lwip_file_handle::CONNECT |
Lwip_file_handle::DATA_READY);
socket_dir->process_io();
socket_dir->process_read_ready();
return ERR_OK;
}
@ -1518,7 +1535,9 @@ err_t tcp_recv_callback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t)
} else {
socket_dir->recv(p);
}
socket_dir->handle_io(Lwip_file_handle::DATA_READY);
socket_dir->process_io();
socket_dir->process_read_ready();
return ERR_OK;
}
@ -1558,7 +1577,8 @@ err_t tcp_sent_callback(void *arg, struct tcp_pcb*, u16_t len)
Lwip::Tcp_socket_dir *socket_dir = static_cast<Lwip::Tcp_socket_dir *>(arg);
socket_dir->pending_ack -= len;
socket_dir->handle_io(Lwip_file_handle::DATA);
socket_dir->process_io();
socket_dir->process_write_ready();
return ERR_OK;
}
*/
@ -1593,13 +1613,35 @@ class Lwip::File_system final : public Vfs::File_system
*/
struct Vfs_netif : Lwip::Nic_netif
{
Vfs::Io_response_handler &io_handler;
Tcp_proto_dir tcp_dir;
Udp_proto_dir udp_dir;
Nameserver_registry nameserver_handles { };
typedef Genode::Fifo_element<Vfs_handle> Handle_element;
typedef Genode::Fifo<Vfs_netif::Handle_element> Handle_queue;
Handle_queue blocked_handles { };
Vfs_netif(Vfs::Env &vfs_env,
Genode::Xml_node config)
: Lwip::Nic_netif(vfs_env.env(), vfs_env.alloc(), config),
tcp_dir(vfs_env), udp_dir(vfs_env)
{ }
~Vfs_netif()
{
/* free the allocated qeueue elements */
status_callback();
}
void enqueue(Vfs_handle &handle)
{
Handle_element *elem = new (handle.alloc())
Handle_element(handle);
blocked_handles.enqueue(*elem);
}
/**
* Wake the application when the interface changes.
*/
@ -1609,15 +1651,25 @@ class Lwip::File_system final : public Vfs::File_system
udp_dir.notify();
nameserver_handles.for_each([&] (Lwip_nameserver_handle &h) {
io_handler.handle_io_response(h.context()); });
h.io_progress_response(); });
blocked_handles.dequeue_all([] (Handle_element &elem) {
Vfs_handle &handle = elem.object();
destroy(elem.object().alloc(), &elem);
handle.io_progress_response();
});
}
void drop(Vfs_handle &handle)
{
blocked_handles.for_each([&] (Handle_element &elem) {
if (&elem.object() == &handle) {
blocked_handles.remove(elem);
destroy(elem.object().alloc(), &elem);
}
});
}
Vfs_netif(Vfs::Env &vfs_env,
Genode::Xml_node config,
Vfs::Io_response_handler &io)
: Lwip::Nic_netif(vfs_env.env(), vfs_env.alloc(), config),
io_handler(io), tcp_dir(vfs_env), udp_dir(vfs_env)
{ }
} _netif;
/**
@ -1641,7 +1693,7 @@ class Lwip::File_system final : public Vfs::File_system
public:
File_system(Vfs::Env &vfs_env, Genode::Xml_node config)
: _ep(vfs_env.env().ep()), _netif(vfs_env, config, vfs_env.io_handler())
: _ep(vfs_env.env().ep()), _netif(vfs_env, config)
{ }
/**
@ -1744,6 +1796,10 @@ class Lwip::File_system final : public Vfs::File_system
void close(Vfs_handle *vfs_handle) override
{
Socket_dir *socket = nullptr;
/* if the inteface is down this handle may be queued */
_netif.drop(*vfs_handle);
if (Lwip_handle *handle = dynamic_cast<Lwip_handle*>(vfs_handle)) {
if (Lwip_file_handle *file_handle = dynamic_cast<Lwip_file_handle*>(handle)) {
socket = file_handle->socket;
@ -1779,7 +1835,13 @@ class Lwip::File_system final : public Vfs::File_system
if (Lwip_handle *handle = dynamic_cast<Lwip_handle*>(vfs_handle)) {
while (true) {
res = handle->write(src, count, out_count);
if (res != WRITE_ERR_WOULD_BLOCK) break;
if (res != WRITE_ERR_WOULD_BLOCK || out_count) break;
/*
* XXX: block for signals until the write completes
* or fails, this is not how it should be done, but
* it's how lxip does it
*/
_ep.wait_and_dispatch_one_io_signal();
}
}
@ -1808,8 +1870,17 @@ class Lwip::File_system final : public Vfs::File_system
return Read_result::READ_ERR_INVALID;
}
bool queue_read(Vfs_handle *, file_size) override {
return _netif.ready(); }
/**
* All reads are unavailable while the network is down
*/
bool queue_read(Vfs_handle *vfs_handle, file_size) override
{
if (_netif.ready()) return true;
/* handle must be woken when the interface comes up */
_netif.enqueue(*vfs_handle);
return false;
}
bool read_ready(Vfs_handle *vfs_handle) override
{
@ -1827,11 +1898,8 @@ class Lwip::File_system final : public Vfs::File_system
bool notify_read_ready(Vfs_handle *vfs_handle) override
{
if (Lwip_file_handle *handle = dynamic_cast<Lwip_file_handle*>(vfs_handle)) {
if (handle->socket) {
return true;
}
}
if (Lwip_file_handle *handle = dynamic_cast<Lwip_file_handle*>(vfs_handle))
return handle->notify_read_ready();
return false;
}
@ -1843,15 +1911,8 @@ class Lwip::File_system final : public Vfs::File_system
Sync_result complete_sync(Vfs_handle *vfs_handle) override
{
Lwip_file_handle *h = dynamic_cast<Lwip_file_handle*>(vfs_handle);
if (h) {
if (h->socket) {
return h->socket->complete_sync();
} else {
return SYNC_QUEUED;
}
}
return SYNC_OK;
return (dynamic_cast<Lwip_file_handle*>(vfs_handle))
? SYNC_OK : SYNC_ERR_INVALID;
}
/***********************

View File

@ -7,7 +7,7 @@
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
* Copyright (C) 2011-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.
@ -125,12 +125,12 @@ class Vfs::Dir_file_system : public File_system
}
/**
* Propagate the handle context to each sub-handle
* Propagate the response handler to each sub-handle
*/
void context(Context *ctx) override
void handler(Watch_response_handler *h) override
{
handle_registry.for_each( [&] (Watch_handle_element &elem) {
elem.watch_handle.context(ctx); } );
elem.watch_handle.handler(h); } );
}
};
@ -313,8 +313,9 @@ class Vfs::Dir_file_system : public File_system
index = index - base;
vfs_handle.seek(index * sizeof(Dirent));
/* forward the handle context */
vfs_handle.context(dir_vfs_handle->context());
/* forward the response handler */
dir_vfs_handle->apply_handler([&] (Vfs::Io_response_handler &h) {
vfs_handle.handler(&h); });
result = vfs_handle.fs().queue_read(&vfs_handle, sizeof(Dirent));
}
@ -950,8 +951,9 @@ class Vfs::Dir_file_system : public File_system
static_cast<Dir_vfs_handle*>(vfs_handle);
auto f = [&result, dir_vfs_handle] (Dir_vfs_handle::Subdir_handle_element &e) {
/* forward the handle context */
e.vfs_handle.context(dir_vfs_handle->context());
/* forward the response handler */
dir_vfs_handle->apply_handler([&] (Io_response_handler &h) {
e.vfs_handle.handler(&h); });
e.synced = false;
if (!e.vfs_handle.fs().queue_sync(&e.vfs_handle)) {

View File

@ -5,7 +5,7 @@
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2018-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.
@ -34,10 +34,6 @@ struct Vfs::Env : Interface
* VFS root file-system
*/
virtual File_system &root_dir() = 0;
virtual Io_response_handler &io_handler() = 0;
virtual Watch_response_handler &watch_handler() = 0;
};
#endif /* _INCLUDE__VFS__ENV_H_ */

View File

@ -18,25 +18,9 @@
#include <vfs/vfs_handle.h>
namespace Vfs {
class Vfs_handle;
struct Io_response_handler;
struct Watch_response_handler;
struct File_io_service;
}
namespace Vfs { struct File_io_service; }
struct Vfs::Io_response_handler : Interface
{
virtual void handle_io_response(Vfs_handle::Context *context) = 0;
};
struct Vfs::Watch_response_handler : Interface
{
virtual void handle_watch_response(Vfs_watch_handle::Context*) = 0;
};
struct Vfs::File_io_service : Interface
{
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };

View File

@ -27,14 +27,6 @@ class Vfs::Simple_env : public Vfs::Env
Genode::Env &_env;
Genode::Allocator &_alloc;
struct Io_response_dummy : Vfs::Io_response_handler {
void handle_io_response(Vfs::Vfs_handle::Context*) override { }
} _io_dummy { };
struct Watch_response_dummy : Vfs::Watch_response_handler {
void handle_watch_response(Vfs::Vfs_watch_handle::Context*) override { }
} _watch_dummy { };
Vfs::Global_file_system_factory _fs_factory { _alloc };
Vfs::Dir_file_system _root_dir;
@ -56,9 +48,6 @@ class Vfs::Simple_env : public Vfs::Env
Genode::Env &env() override { return _env; }
Genode::Allocator &alloc() override { return _alloc; }
Vfs::File_system &root_dir() override { return _root_dir; }
Vfs::Io_response_handler &io_handler() override { return _io_dummy; }
Vfs::Watch_response_handler &watch_handler() override { return _watch_dummy; }
};
#endif /* _INCLUDE__VFS__SIMPLE_ENV_H_ */

View File

@ -17,30 +17,59 @@
#include <vfs/directory_service.h>
namespace Vfs{
struct Io_response_handler;
struct Watch_response_handler;
class Vfs_handle;
class Vfs_watch_handle;
class File_io_service;
class File_system;
class Vfs_watch_handle;
}
/**
* Object for encapsulating application-level
* response to VFS I/O
*
* These responses should be assumed to be called
* during I/O signal dispatch.
*/
struct Vfs::Io_response_handler : Genode::Interface
{
/**
* Respond to a resource becoming readable
*/
virtual void read_ready_response() = 0;
/**
* Respond to complete pending I/O
*/
virtual void io_progress_response() = 0;
};
/**
* Object for encapsulating application-level
* handlers of VFS responses.
*
* This response should be assumed to be called
* during I/O signal dispatch.
*/
struct Vfs::Watch_response_handler : Genode::Interface
{
virtual void watch_response() = 0;
};
class Vfs::Vfs_handle
{
public:
/**
* Opaque handle context
*/
struct Context { };
private:
Directory_service &_ds;
File_io_service &_fs;
Genode::Allocator &_alloc;
int _status_flags;
file_size _seek = 0;
Context *_context = nullptr;
Directory_service &_ds;
File_io_service &_fs;
Genode::Allocator &_alloc;
Io_response_handler *_handler = nullptr;
file_size _seek = 0;
int _status_flags;
/*
* Noncopyable
@ -90,11 +119,8 @@ class Vfs::Vfs_handle
Directory_service &ds() { return _ds; }
File_io_service &fs() { return _fs; }
Allocator &alloc() { return _alloc; }
void context(Context *context) { _context = context; }
Context *context() const { return _context; }
int status_flags() const { return _status_flags; }
void status_flags(int flags) { _status_flags = flags; }
/**
@ -112,6 +138,35 @@ class Vfs::Vfs_handle
*/
void advance_seek(file_size incr) { _seek += incr; }
/**
* Set response handler, unset with nullptr
*/
virtual void handler(Io_response_handler *handler)
{
_handler = handler;
}
/**
* Apply to response handler if present
*
* XXX: may not be necesarry if the method above is virtual.
*/
template <typename FUNC>
void apply_handler(FUNC const &func) const {
if (_handler) func(*_handler); }
/**
* Notify application through response handler
*/
void read_ready_response() {
if (_handler) _handler->read_ready_response(); }
/**
* Notify application through response handler
*/
void io_progress_response() {
if (_handler) _handler->io_progress_response(); }
/**
* Close handle at backing file-system.
*
@ -123,18 +178,11 @@ class Vfs::Vfs_handle
class Vfs::Vfs_watch_handle
{
public:
/**
* Opaque handle context
*/
struct Context { };
private:
Directory_service &_fs;
Genode::Allocator &_alloc;
Context *_context = nullptr;
Directory_service &_fs;
Genode::Allocator &_alloc;
Watch_response_handler *_handler = nullptr;
/*
* Noncopyable
@ -154,8 +202,21 @@ class Vfs::Vfs_watch_handle
Directory_service &fs() { return _fs; }
Allocator &alloc() { return _alloc; }
virtual void context(Context *context) { _context = context; }
Context *context() const { return _context; }
/**
* Set response handler, unset with nullptr
*/
virtual void handler(Watch_response_handler *handler) {
_handler = handler; }
/**
* Notify application through response handler
*/
void watch_response()
{
if (_handler)
_handler->watch_response();
}
/**
* Close handle at backing file-system.

View File

@ -21,7 +21,6 @@
#include <base/id_space.h>
#include <file_system_session/connection.h>
namespace Vfs { class Fs_file_system; }
@ -67,15 +66,18 @@ class Vfs::Fs_file_system : public File_system
::File_system::Packet_descriptor queued_sync_packet { };
};
struct Fs_vfs_handle;
typedef Genode::Fifo<Fs_vfs_handle> Fs_vfs_handle_queue;
struct Fs_vfs_handle : Vfs_handle,
private ::File_system::Node,
private Handle_space::Element,
private List<Fs_vfs_handle>::Element,
private Fs_vfs_handle_queue::Element,
private Handle_state
{
friend Genode::Id_space<::File_system::Node>;
friend Genode::List<Fs_vfs_handle>;
using Genode::List<Fs_vfs_handle>::Element::next;
friend Fs_vfs_handle_queue;
using Fs_vfs_handle_queue::Element::enqueued;
using Handle_state::queued_read_state;
using Handle_state::queued_read_packet;
@ -84,7 +86,6 @@ class Vfs::Fs_file_system : public File_system
using Handle_state::read_ready_state;
::File_system::Connection &_fs;
Io_response_handler &_io_handler;
bool _queue_read(file_size count, file_size const seek_offset)
{
@ -94,7 +95,8 @@ class Vfs::Fs_file_system : public File_system
::File_system::Session::Tx::Source &source = *_fs.tx();
/* if not ready to submit suggest retry */
if (!source.ready_to_submit()) return false;
if (!source.ready_to_submit())
return false;
file_size const max_packet_size = source.bulk_buffer_size() / 2;
file_size const clipped_count = min(max_packet_size, count);
@ -149,12 +151,11 @@ class Vfs::Fs_file_system : public File_system
Fs_vfs_handle(File_system &fs, Allocator &alloc,
int status_flags, Handle_space &space,
::File_system::Node_handle node_handle,
::File_system::Connection &fs_connection,
Io_response_handler &io_handler)
::File_system::Connection &fs_connection)
:
Vfs_handle(fs, fs, alloc, status_flags),
Handle_space::Element(*this, space, node_handle),
_fs(fs_connection), _io_handler(io_handler)
_fs(fs_connection)
{ }
::File_system::File_handle file_handle() const
@ -334,11 +335,10 @@ class Vfs::Fs_file_system : public File_system
::File_system::Session &fs_session,
::File_system::Node_handle fs_handle,
Handle_space &space,
::File_system::Connection &fs_connection,
Io_response_handler &io_handler)
::File_system::Connection &fs_connection)
:
Fs_vfs_handle(fs, *(Allocator*)nullptr, 0, space, fs_handle,
fs_connection, io_handler),
fs_connection),
_fs_session(fs_session)
{ }
@ -350,12 +350,9 @@ class Vfs::Fs_file_system : public File_system
struct Fs_vfs_watch_handle final : Vfs_watch_handle,
private ::File_system::Node,
private Handle_space::Element,
private List<Fs_vfs_watch_handle>::Element
private Handle_space::Element
{
friend Genode::Id_space<::File_system::Node>;
friend Genode::List<Fs_vfs_watch_handle>;
using Genode::List<Fs_vfs_watch_handle>::Element::next;
::File_system::Watch_handle const fs_handle;
@ -370,77 +367,7 @@ class Vfs::Fs_file_system : public File_system
{ }
};
struct Post_signal_hook : Genode::Entrypoint::Post_signal_hook
{
Genode::Entrypoint &_ep;
Io_response_handler &_io_handler;
Watch_response_handler &_watch_handler;
List<Fs_vfs_handle> _io_handle_list { };
Lock _list_lock { };
bool _notify_all { false };
Post_signal_hook(Vfs::Env &env)
:
_ep(env.env().ep()),
_io_handler(env.io_handler()),
_watch_handler(env.watch_handler())
{ }
void arm_io_event(Fs_vfs_handle *context)
{
if (!context) {
Lock::Guard list_guard(_list_lock);
_notify_all = true;
} else {
Lock::Guard list_guard(_list_lock);
for (Fs_vfs_handle *list_context = _io_handle_list.first();
list_context;
list_context = list_context->next())
{
if (list_context == context) {
/* already in list */
return;
}
}
_io_handle_list.insert(context);
}
_ep.schedule_post_signal_hook(this);
}
void function() override
{
Fs_vfs_handle *handle = nullptr;
do {
bool notify_all = false;
{
Lock::Guard list_guard(_list_lock);
handle = _io_handle_list.first();
_io_handle_list.remove(handle);
if (!handle && _notify_all) {
notify_all = true;
_notify_all = false;
}
}
if (handle) {
_io_handler.handle_io_response(handle->context());
} else if (notify_all) {
_io_handler.handle_io_response(nullptr);
}
/* done if no contexts and all notified */
} while (handle);
}
};
Post_signal_hook _post_signal_hook { _env };
Fs_vfs_handle_queue _congested_handles { };
file_size _read(Fs_vfs_handle &handle, void *buf,
file_size const count, file_size const seek_offset)
@ -465,9 +392,8 @@ class Vfs::Fs_file_system : public File_system
/* pass packet to server side */
source.submit_packet(packet_in);
while (handle.queued_read_state != Handle_state::Queued_state::ACK) {
while (handle.queued_read_state != Handle_state::Queued_state::ACK)
_env.env().ep().wait_and_dispatch_one_io_signal();
}
/* obtain result packet descriptor with updated status info */
Packet_descriptor const packet_out = handle.queued_read_packet;
@ -498,14 +424,25 @@ class Vfs::Fs_file_system : public File_system
file_size _write(Fs_vfs_handle &handle,
const char *buf, file_size count, file_size seek_offset)
{
/*
* TODO
* a sustained write loop will congest the packet buffer,
* perhaps acks should be processed before submission?
*
* _handle_ack();
*/
::File_system::Session::Tx::Source &source = *_fs.tx();
using ::File_system::Packet_descriptor;
file_size const max_packet_size = source.bulk_buffer_size() / 2;
count = min(max_packet_size, count);
if (!source.ready_to_submit())
if (!source.ready_to_submit()) {
if (!handle.enqueued())
_congested_handles.enqueue(handle);
throw Insufficient_buffer();
}
try {
Packet_descriptor packet_in(source.alloc_packet(count),
@ -519,6 +456,8 @@ class Vfs::Fs_file_system : public File_system
/* pass packet to server side */
source.submit_packet(packet_in);
} catch (::File_system::Session::Tx::Source::Packet_alloc_failed) {
if (!handle.enqueued())
_congested_handles.enqueue(handle);
throw Insufficient_buffer();
} catch (...) {
Genode::error("unhandled exception");
@ -529,8 +468,8 @@ class Vfs::Fs_file_system : public File_system
void _ready_to_submit()
{
/* notify anyone who might have failed on write() ready_to_submit */
_post_signal_hook.arm_io_event(nullptr);
_congested_handles.dequeue_all([] (Fs_vfs_handle &handle) {
handle.io_progress_response(); });
}
void _handle_ack()
@ -552,13 +491,13 @@ class Vfs::Fs_file_system : public File_system
switch (packet.operation()) {
case Packet_descriptor::READ_READY:
handle.read_ready_state = Handle_state::Read_ready_state::READY;
_post_signal_hook.arm_io_event(&handle);
handle.read_ready_response();
break;
case Packet_descriptor::READ:
handle.queued_read_packet = packet;
handle.queued_read_state = Handle_state::Queued_state::ACK;
_post_signal_hook.arm_io_event(&handle);
handle.io_progress_response();
break;
case Packet_descriptor::WRITE:
@ -566,13 +505,13 @@ class Vfs::Fs_file_system : public File_system
* Notify anyone who might have failed on
* 'alloc_packet()'
*/
_post_signal_hook.arm_io_event(nullptr);
handle.io_progress_response();
break;
case Packet_descriptor::SYNC:
handle.queued_sync_packet = packet;
handle.queued_sync_state = Handle_state::Queued_state::ACK;
_post_signal_hook.arm_io_event(&handle);
handle.io_progress_response();
break;
case Packet_descriptor::CONTENT_CHANGED:
@ -583,20 +522,14 @@ class Vfs::Fs_file_system : public File_system
try {
if (packet.operation() == Packet_descriptor::CONTENT_CHANGED) {
/*
* Trigger the watch response during signal dispatch.
* This is incompatible with the Libc I/O handling
* but the Libc does not open watch handles and shall
* not use them before Post_signal_hook is removed.
*/
_watch_handle_space.apply<Fs_vfs_watch_handle>(id, [&] (Fs_vfs_watch_handle &handle) {
_env.watch_handler().handle_watch_response(handle.context()); });
handle.watch_response(); });
} else {
_handle_space.apply<Fs_vfs_handle>(id, handle_read);
}
}
catch (Handle_space::Unknown_id) {
Genode::warning("ack for unknown VFS handle"); }
Genode::warning("ack for unknown File_system handle ", id); }
if (packet.operation() == Packet_descriptor::WRITE) {
Lock::Guard guard(_lock);
@ -605,8 +538,16 @@ class Vfs::Fs_file_system : public File_system
}
}
void _handle_ack_signal()
{
_handle_ack();
/* packet buffer space available */
_ready_to_submit();
}
Genode::Io_signal_handler<Fs_file_system> _ack_handler {
_env.env().ep(), *this, &Fs_file_system::_handle_ack };
_env.env().ep(), *this, &Fs_file_system::_handle_ack_signal };
Genode::Io_signal_handler<Fs_file_system> _ready_handler {
_env.env().ep(), *this, &Fs_file_system::_ready_to_submit };
@ -652,12 +593,17 @@ class Vfs::Fs_file_system : public File_system
try {
::File_system::Node_handle node = _fs.node(path);
Fs_handle_guard node_guard(*this, _fs, node, _handle_space,
_fs, _env.io_handler());
Fs_handle_guard node_guard(*this, _fs, node, _handle_space, _fs);
status = _fs.status(node);
}
catch (Genode::Out_of_ram) { return STAT_ERR_NO_PERM; }
catch (Genode::Out_of_caps) { return STAT_ERR_NO_PERM; }
catch (Genode::Out_of_ram) {
Genode::error("out-of-ram during stat");
return STAT_ERR_NO_PERM;
}
catch (Genode::Out_of_caps) {
Genode::error("out-of-caps during stat");
return STAT_ERR_NO_PERM;
}
catch (...) { return STAT_ERR_NO_ENTRY; }
out = Stat();
@ -688,8 +634,7 @@ class Vfs::Fs_file_system : public File_system
try {
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space, _fs,
_env.io_handler());
Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space, _fs);
_fs.unlink(dir, file_name.base() + 1);
}
@ -725,12 +670,12 @@ class Vfs::Fs_file_system : public File_system
_fs.dir(from_dir_path.base(), false);
Fs_handle_guard from_dir_guard(*this, _fs, from_dir,
_handle_space, _fs, _env.io_handler());
_handle_space, _fs);
::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(),
false);
Fs_handle_guard to_dir_guard(*this, _fs, to_dir, _handle_space,
_fs, _env.io_handler());
Fs_handle_guard to_dir_guard(
*this, _fs, to_dir, _handle_space, _fs);
_fs.move(from_dir, from_file_name.base() + 1,
to_dir, to_file_name.base() + 1);
@ -749,9 +694,10 @@ class Vfs::Fs_file_system : public File_system
try {
::File_system::Node_handle node = _fs.node(path);
Fs_handle_guard node_guard(*this, _fs, node,
_handle_space, _fs,
_env.io_handler());
_handle_space, _fs);
::File_system::Status status = _fs.status(node);
return status.size / sizeof(::File_system::Directory_entry);
}
catch (...) { }
@ -762,8 +708,7 @@ class Vfs::Fs_file_system : public File_system
{
try {
::File_system::Node_handle node = _fs.node(path);
Fs_handle_guard node_guard(*this, _fs, node, _handle_space,
_fs, _env.io_handler());
Fs_handle_guard node_guard(*this, _fs, node, _handle_space, _fs);
::File_system::Status status = _fs.status(node);
@ -809,16 +754,14 @@ class Vfs::Fs_file_system : public File_system
try {
::File_system::Dir_handle dir = _fs.dir(dir_path.base(), false);
Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space, _fs,
_env.io_handler());
Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space, _fs);
::File_system::File_handle file = _fs.file(dir,
file_name.base() + 1,
mode, create);
*out_handle = new (alloc)
Fs_vfs_file_handle(*this, alloc, vfs_mode, _handle_space,
file, _fs, _env.io_handler());
Fs_vfs_file_handle(*this, alloc, vfs_mode, _handle_space, file, _fs);
}
catch (::File_system::Lookup_failed) { return OPEN_ERR_UNACCESSIBLE; }
catch (::File_system::Permission_denied) { return OPEN_ERR_NO_PERM; }
@ -846,7 +789,7 @@ class Vfs::Fs_file_system : public File_system
*out_handle = new (alloc)
Fs_vfs_dir_handle(*this, alloc, ::File_system::READ_ONLY,
_handle_space, dir, _fs, _env.io_handler());
_handle_space, dir, _fs);
}
catch (::File_system::Lookup_failed) { return OPENDIR_ERR_LOOKUP_FAILED; }
catch (::File_system::Name_too_long) { return OPENDIR_ERR_NAME_TOO_LONG; }
@ -878,7 +821,7 @@ class Vfs::Fs_file_system : public File_system
false);
Fs_handle_guard from_dir_guard(*this, _fs, dir_handle,
_handle_space, _fs, _env.io_handler());
_handle_space, _fs);
::File_system::Symlink_handle symlink_handle =
_fs.symlink(dir_handle, symlink_name.base() + 1, create);
@ -886,8 +829,7 @@ class Vfs::Fs_file_system : public File_system
*out_handle = new (alloc)
Fs_vfs_symlink_handle(*this, alloc,
::File_system::READ_ONLY,
_handle_space, symlink_handle, _fs,
_env.io_handler());
_handle_space, symlink_handle, _fs);
return OPENLINK_OK;
}
@ -907,6 +849,8 @@ class Vfs::Fs_file_system : public File_system
Lock::Guard guard(_lock);
Fs_vfs_handle *fs_handle = static_cast<Fs_vfs_handle *>(vfs_handle);
if (fs_handle->enqueued())
_congested_handles.remove(*fs_handle);
_fs.close(fs_handle->file_handle());
destroy(fs_handle->alloc(), fs_handle);
@ -968,7 +912,6 @@ class Vfs::Fs_file_system : public File_system
Fs_vfs_handle &handle = static_cast<Fs_vfs_handle &>(*vfs_handle);
out_count = _write(handle, buf, buf_size, handle.seek());
return WRITE_OK;
}
@ -978,7 +921,10 @@ class Vfs::Fs_file_system : public File_system
Fs_vfs_handle *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
return handle->queue_read(count);
bool result = handle->queue_read(count);
if (!result && !handle->enqueued())
_congested_handles.enqueue(*handle);
return result;
}
Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count,
@ -990,7 +936,10 @@ class Vfs::Fs_file_system : public File_system
Fs_vfs_handle *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
return handle->complete_read(dst, count, out_count);
Read_result result = handle->complete_read(dst, count, out_count);
if (result == READ_QUEUED && !handle->enqueued())
_congested_handles.enqueue(*handle);
return result;
}
bool read_ready(Vfs_handle *vfs_handle) override
@ -1024,7 +973,7 @@ class Vfs::Fs_file_system : public File_system
/*
* When the packet is acknowledged the application is notified via
* Io_response_handler::handle_io_response().
* Response_handler::handle_response().
*/
return true;
}

View File

@ -147,13 +147,10 @@ class Vfs_ram::Node : private Genode::Avl_node<Node>, private Genode::Lock
void close(Io_handle &handle) { _io_handles.remove(&handle); }
void close(Watch_handle &handle) { _watch_handles.remove(&handle); }
void notify(Watch_response_handler &handler)
void notify()
{
for (Watch_handle *h = _watch_handles.first(); h; h = h->next()) {
if (auto *ctx = h->context()) {
handler.handle_watch_response(ctx);
}
}
for (Watch_handle *h = _watch_handles.first(); h; h = h->next())
h->watch_response();
}
void unlink() { inode = 0; }
@ -606,7 +603,7 @@ class Vfs::Ram_file_system : public Vfs::File_system
try { file = new (_env.alloc()) File(name, _env.alloc()); }
catch (Out_of_memory) { return OPEN_ERR_NO_SPACE; }
parent->adopt(file);
parent->notify(_env.watch_handler());
parent->notify();
} else {
Node *node = lookup(path);
if (!node) return OPEN_ERR_UNACCESSIBLE;
@ -661,7 +658,7 @@ class Vfs::Ram_file_system : public Vfs::File_system
catch (Out_of_memory) { return OPENDIR_ERR_NO_SPACE; }
parent->adopt(dir);
parent->notify(_env.watch_handler());
parent->notify();
} else {
Node *node = lookup(path);
@ -719,7 +716,7 @@ class Vfs::Ram_file_system : public Vfs::File_system
link->lock();
parent->adopt(link);
link->unlock();
parent->notify(_env.watch_handler());
parent->notify();
} else {
if (!node) return OPENLINK_ERR_LOOKUP_FAILED;
@ -762,7 +759,7 @@ class Vfs::Ram_file_system : public Vfs::File_system
if (ram_handle->node.unlinked() && !ram_handle->node.opened()) {
destroy(_env.alloc(), &ram_handle->node);
} else if (node_modified) {
node.notify(_env.watch_handler());
node.notify();
}
}
@ -840,7 +837,7 @@ class Vfs::Ram_file_system : public Vfs::File_system
to_dir->release(to_node);
/* notify the node being replaced */
to_node->notify(_env.watch_handler());
to_node->notify();
/* free the node that is replaced */
remove(to_node);
@ -850,8 +847,8 @@ class Vfs::Ram_file_system : public Vfs::File_system
from_node->name(new_name);
to_dir->adopt(from_node);
from_dir->notify(_env.watch_handler());
to_dir->notify(_env.watch_handler());
from_dir->notify();
to_dir->notify();
return RENAME_OK;
}
@ -869,8 +866,8 @@ class Vfs::Ram_file_system : public Vfs::File_system
node->lock();
parent->release(node);
node->notify(_env.watch_handler());
parent->notify(_env.watch_handler());
node->notify();
parent->notify();
remove(node);
return UNLINK_OK;
}
@ -1001,7 +998,7 @@ class Vfs::Ram_file_system : public Vfs::File_system
if (handle->modifying) {
handle->modifying = false;
handle->node.close(*handle);
handle->node.notify(_env.watch_handler());
handle->node.notify();
handle->node.open(*handle);
}
return SYNC_OK;

View File

@ -7,7 +7,7 @@
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
* Copyright (C) 2012-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.
@ -18,7 +18,6 @@
#include <terminal_session/connection.h>
#include <vfs/single_file_system.h>
#include <base/signal.h>
#include <base/registry.h>
@ -33,52 +32,50 @@ class Vfs::Terminal_file_system : public Single_file_system
Label _label;
Genode::Env &_env;
Io_response_handler &_io_handler;
Terminal::Connection _terminal { _env, _label.string() };
typedef Genode::Registered<Vfs_handle> Registered_handle;
typedef Genode::Registry<Registered_handle> Handle_registry;
struct Post_signal_hook : Genode::Entrypoint::Post_signal_hook
struct Terminal_vfs_handle : Single_vfs_handle
{
Genode::Entrypoint &_ep;
Io_response_handler &_io_handler;
Vfs_handle *_handle = nullptr;
Terminal::Connection &terminal;
bool notifying { false };
bool blocked { false };
Post_signal_hook(Genode::Entrypoint &ep,
Io_response_handler &io_handler)
: _ep(ep), _io_handler(io_handler) { }
Terminal_vfs_handle(Terminal::Connection &term,
Directory_service &ds,
File_io_service &fs,
Genode::Allocator &alloc,
int flags)
:
Single_vfs_handle(ds, fs, alloc, flags),
terminal(term)
{ }
void arm(Vfs_handle &handle)
bool read_ready() override {
return terminal.avail(); }
Read_result read(char *dst, file_size count,
file_size &out_count) override
{
_handle = &handle;
_ep.schedule_post_signal_hook(this);
if (!terminal.avail()) {
blocked = true;
return READ_QUEUED;
}
out_count = terminal.read(dst, count);
return READ_OK;
}
void function() override
Write_result write(char const *src, file_size count,
file_size &out_count) override
{
/*
* XXX The current implementation executes the post signal hook
* for the last armed context only. When changing this,
* beware that the called handle_io_response() may change
* this object in a signal handler.
*/
_io_handler.handle_io_response(_handle ? _handle->context() : nullptr);
_handle = nullptr;
out_count = terminal.write(src, count);
return WRITE_OK;
}
private:
/*
* Noncopyable
*/
Post_signal_hook(Post_signal_hook const &);
Post_signal_hook &operator = (Post_signal_hook const &);
};
Post_signal_hook _post_signal_hook { _env.ep(), _io_handler };
typedef Genode::Registered<Terminal_vfs_handle> Registered_handle;
typedef Genode::Registry<Registered_handle> Handle_registry;
Handle_registry _handle_registry { };
@ -87,20 +84,17 @@ class Vfs::Terminal_file_system : public Single_file_system
void _handle_read_avail()
{
_handle_registry.for_each([this] (Registered_handle &h) {
_post_signal_hook.arm(h);
});
}
_handle_registry.for_each([this] (Registered_handle &handle) {
if (handle.blocked) {
handle.blocked = false;
handle.io_progress_response();
}
Read_result _read(Vfs_handle *, char *dst, file_size count,
file_size &out_count)
{
if (_terminal.avail()) {
out_count = _terminal.read(dst, count);
return READ_OK;
} else {
return READ_QUEUED;
}
if (handle.notifying) {
handle.notifying = false;
handle.read_ready_response();
}
});
}
public:
@ -109,7 +103,7 @@ class Vfs::Terminal_file_system : public Single_file_system
:
Single_file_system(NODE_TYPE_CHAR_DEVICE, name(), config),
_label(config.attribute_value("label", Label())),
_env(env.env()), _io_handler(env.io_handler())
_env(env.env())
{
/* register for read-avail notification */
_terminal.read_avail_sigh(_read_avail_handler);
@ -118,7 +112,7 @@ class Vfs::Terminal_file_system : public Single_file_system
static const char *name() { return "terminal"; }
char const *type() override { return "terminal"; }
Open_result open(char const *path, unsigned,
Open_result open(char const *path, unsigned flags,
Vfs_handle **out_handle,
Allocator &alloc) override
{
@ -127,33 +121,27 @@ class Vfs::Terminal_file_system : public Single_file_system
try {
*out_handle = new (alloc)
Registered_handle(_handle_registry, *this, *this, alloc, 0);
Registered_handle(_handle_registry, _terminal, *this, *this, alloc, flags);
return OPEN_OK;
}
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
}
/********************************
** File I/O service interface **
********************************/
Write_result write(Vfs_handle *, char const *buf, file_size buf_size,
file_size &out_count) override
bool notify_read_ready(Vfs_handle *vfs_handle) override
{
out_count = _terminal.write(buf, buf_size);
return WRITE_OK;
}
Terminal_vfs_handle *handle =
static_cast<Terminal_vfs_handle*>(vfs_handle);
if (!handle)
return false;
Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count,
file_size &out_count) override
{
return _read(vfs_handle, dst, count, out_count);
}
bool read_ready(Vfs_handle *) override
{
return _terminal.avail();
handle->notifying = true;
return true;
}
Ftruncate_result ftruncate(Vfs_handle *, file_size) override
@ -163,19 +151,7 @@ class Vfs::Terminal_file_system : public Single_file_system
bool check_unblock(Vfs_handle *, bool rd, bool wr, bool) override
{
if (rd && (_terminal.avail() > 0))
return true;
if (wr)
return true;
return false;
}
void register_read_ready_sigh(Vfs_handle *,
Signal_context_capability sigh) override
{
_terminal.read_avail_sigh(sigh);
return ((rd && _terminal.avail()) || wr);
}
};

View File

@ -6,7 +6,7 @@
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
* Copyright (C) 2015-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.
@ -22,8 +22,8 @@
#include <root/component.h>
#include <os/session_policy.h>
#include <base/allocator_guard.h>
#include <vfs/dir_file_system.h>
#include <vfs/file_system_factory.h>
#include <util/fifo.h>
#include <vfs/simple_env.h>
/* Local includes */
#include "assert.h"
@ -36,13 +36,10 @@ namespace Vfs_server {
class Session_resources;
class Session_component;
class Io_response_handler;
class Watch_response_handler;
class Vfs_env;
class Root;
typedef Genode::Registered<Session_component> Registered_session;
typedef Genode::Registry<Registered_session> Session_registry;
typedef Genode::Fifo<Session_component> Session_queue;
/**
* Convenience utities for parsing quotas
@ -85,15 +82,29 @@ class Vfs_server::Session_resources
class Vfs_server::Session_component : private Session_resources,
public ::File_system::Session_rpc_object,
public Session_io_handler
private Session_queue::Element
{
friend Session_queue;
private:
Vfs::File_system &_vfs;
Genode::Entrypoint &_ep;
Packet_stream &_stream { *tx_sink() };
/* global queue of nodes to process after an I/O signal */
Node_queue &_pending_nodes;
/* global queue of sessions for which packets await progress */
Session_queue &_pending_sessions;
/* collection of open nodes local to this session */
Node_space _node_space { };
Genode::Signal_handler<Session_component> _process_packet_handler;
Vfs::File_system &_vfs;
Genode::Signal_handler<Session_component> _process_packet_handler {
_ep, *this, &Session_component::_process_packets };
/*
* The root node needs be allocated with the session struct
@ -105,11 +116,6 @@ class Vfs_server::Session_component : private Session_resources,
bool const _writable;
/*
* XXX Currently, we have only one packet in backlog, which must finish
* processing before new packets can be processed.
*/
Packet_descriptor _backlog_packet { };
/****************************
** Handle to node mapping **
@ -154,190 +160,58 @@ class Vfs_server::Session_component : private Session_resources,
** Packet-stream processing **
******************************/
struct Not_ready { };
struct Dont_ack { };
/**
* Perform packet operation
* Attempt to process the head of the packet queue
*
* \throw Not_ready
* \throw Dont_ack
* Return true if the packet can be popped from the
* queue or false if the the packet cannot be processed
* or further queued.
*/
void _process_packet_op(Packet_descriptor &packet)
bool _process_packet()
{
/* leave the packet queued so that it cannot leak */
Packet_descriptor packet = _stream.peek_packet();
/* assume failure by default */
packet.succeeded(false);
size_t const length = packet.length();
seek_off_t const seek = packet.position();
if ((packet.length() > packet.size()))
return;
/* resulting length */
size_t res_length = 0;
bool succeeded = false;
switch (packet.operation()) {
case Packet_descriptor::READ:
try {
_apply(packet.handle(), [&] (Io_node &node) {
if (!node.read_ready()) {
node.notify_read_ready(true);
throw Not_ready();
}
if (node.mode() & READ_ONLY) {
res_length = node.read(
(char *)tx_sink()->packet_content(packet), length, seek);
/* no way to distinguish EOF from unsuccessful
reads, both have res_length == 0 */
succeeded = true;
}
});
}
catch (Not_ready) { throw; }
catch (Operation_incomplete) { throw Not_ready(); }
catch (...) { }
break;
case Packet_descriptor::WRITE:
try {
_apply(packet.handle(), [&] (Io_node &node) {
if (node.mode() & WRITE_ONLY) {
res_length = node.write(
(char const *)tx_sink()->packet_content(packet), length, seek);
/* File system session can't handle partial writes */
if (res_length != length) {
Genode::error("partial write detected ",
res_length, " vs ", length);
/* don't acknowledge */
throw Dont_ack();
}
succeeded = true;
}
});
} catch (Operation_incomplete) {
throw Not_ready();
} catch (...) { }
break;
case Packet_descriptor::READ_READY:
try {
_apply(static_cast<File_handle>(packet.handle().value), [] (File &node) {
if (!node.read_ready()) {
node.notify_read_ready(true);
throw Dont_ack();
}
});
succeeded = true;
}
catch (Dont_ack) { throw; }
catch (...) { }
break;
case Packet_descriptor::CONTENT_CHANGED:
Genode::warning("ignoring CONTENT_CHANGED packet from client");
throw Dont_ack();
case Packet_descriptor::SYNC:
/**
* Sync the VFS and send any pending signals on the node.
*/
try {
_apply(packet.handle(), [&] (Io_node &node) {
succeeded = node.sync();
});
} catch (Operation_incomplete) {
throw Not_ready();
} catch (...) { Genode::error("SYNC: unhandled exception"); }
break;
if ((packet.length() > packet.size())) {
/* not a valid packet */
_stream.acknowledge_packet(packet);
return true;
}
packet.length(res_length);
packet.succeeded(succeeded);
}
bool handle_invalid = true;
bool result = true;
bool _try_process_packet_op(Packet_descriptor &packet)
{
try {
_process_packet_op(packet);
return true;
} catch (Not_ready) {
_backlog_packet = packet;
_apply(packet.handle(), [&] (Io_node &node) {
handle_invalid = false;
result = node.process_packet(packet);
});
}
catch (File_system::Invalid_handle) { }
return false;
/* send the packet back if the handle is missing */
if (handle_invalid)
_stream.acknowledge_packet(packet);
return (handle_invalid || result);
}
bool _process_backlog()
{
/* indicate success if there's no backlog */
if (!_backlog_packet.size() &&
(_backlog_packet.operation() != Packet_descriptor::SYNC)) {
return true;
}
protected:
/* only start processing if acknowledgement is possible */
if (!tx_sink()->ready_to_ack())
return false;
if (!_try_process_packet_op(_backlog_packet))
return false;
/*
* The 'acknowledge_packet' function cannot block because we
* checked for 'ready_to_ack' in '_process_packets'.
*/
tx_sink()->acknowledge_packet(_backlog_packet);
/* invalidate backlog packet */
_backlog_packet = Packet_descriptor();
return true;
}
bool _process_packet()
{
Packet_descriptor packet = tx_sink()->get_packet();
if (!_try_process_packet_op(packet))
return false;
/*
* The 'acknowledge_packet' function cannot block because we
* checked for 'ready_to_ack' in '_process_packets'.
*/
tx_sink()->acknowledge_packet(packet);
return true;
}
friend Vfs_server::Root;
using Session_queue::Element::enqueued;
/**
* Called by signal dispatcher, executed in the context of the main
* thread (not serialized with the RPC functions)
* Called by the global Io_progress_handler as
* well as the local signal handler
*
* Return true if the packet queue was emptied
*/
void _process_packets()
bool process_packets()
{
using namespace Genode;
/*
* XXX Process client backlog before looking at new requests. This
* limits the number of simultaneously addressed handles (which
* was also the case before adding the backlog in case of
* blocking operations).
*/
if (!_process_backlog())
/* backlog not cleared - block for next condition change */
return;
/**
* Process packets in batches, otherwise a client that
* submits packets as fast as they are processed will
@ -345,32 +219,44 @@ class Vfs_server::Session_component : private Session_resources,
*/
int quantum = TX_QUEUE_SIZE;
while (tx_sink()->packet_avail()) {
if (--quantum == 0) {
/* come back to this later */
Signal_transmitter(_process_packet_handler).submit();
break;
while (_stream.packet_avail()) {
if (_process_packet()) {
/*
* the packet was rejected or associated with
* a handle, pop it from the packet queue
*/
_stream.get_packet();
} else {
/* no progress */
return false;
}
/*
* Make sure that the '_process_packet' function does not
* block.
*
* If the acknowledgement queue is full, we defer packet
* processing until the client processed pending
* acknowledgements and thereby emitted a ready-to-ack
* signal. Otherwise, the call of 'acknowledge_packet()'
* in '_process_packet' would infinitely block the context
* of the main thread. The main thread is however needed
* for receiving any subsequent 'ready-to-ack' signals.
*/
if (!tx_sink()->ready_to_ack())
return;
if (--quantum == 0) {
/* come back to this later */
Genode::Signal_transmitter(_process_packet_handler).submit();
return false;
}
}
try {
if (!_process_packet())
return;
} catch (Dont_ack) { }
return true;
}
private:
/**
* Called by signal handler
*/
void _process_packets()
{
bool done = process_packets();
if (done && enqueued()) {
/* this session is idle */
_pending_sessions.remove(*this);
} else
if (!done && !enqueued()) {
/* this session needs unblocking */
_pending_sessions.enqueue(*this);
}
}
@ -391,6 +277,9 @@ class Vfs_server::Session_component : private Session_resources,
throw Invalid_name();
}
/**
* Destroy an open node
*/
void _close(Node &node)
{
if (File *file = dynamic_cast<File*>(&node))
@ -409,33 +298,31 @@ class Vfs_server::Session_component : private Session_resources,
/**
* Constructor
* \param ep thead entrypoint for session
* \param cache node cache
* \param tx_buf_size shared transmission buffer size
* \param root_path path root of the session
* \param writable whether the session can modify files
*/
Session_component(Genode::Env &env,
char const *label,
Genode::Ram_quota ram_quota,
Genode::Cap_quota cap_quota,
size_t tx_buf_size,
Vfs::File_system &vfs,
Node_queue &pending_nodes,
Session_queue &pending_sessions,
char const *root_path,
bool writable)
:
Session_resources(env.pd(), env.rm(), ram_quota, cap_quota, tx_buf_size),
Session_rpc_object(_packet_ds.cap(), env.rm(), env.ep().rpc_ep()),
_process_packet_handler(env.ep(), *this, &Session_component::_process_packets),
_vfs(vfs),
_ep(env.ep()),
_pending_nodes(pending_nodes),
_pending_sessions(pending_sessions),
_root_path(root_path),
_label(label),
_writable(writable)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
* Register an I/O signal handler for
* packet-avail and ready-to-ack signals.
*/
_tx.sigh_packet_avail(_process_packet_handler);
_tx.sigh_ready_to_ack(_process_packet_handler);
@ -446,8 +333,12 @@ class Vfs_server::Session_component : private Session_resources,
*/
~Session_component()
{
/* flush and close the open handles */
while (_node_space.apply_any<Node>([&] (Node &node) {
_close(node); })) { }
if (enqueued())
_pending_sessions.remove(*this);
}
/**
@ -458,53 +349,6 @@ class Vfs_server::Session_component : private Session_resources,
void upgrade(Genode::Cap_quota caps) {
_cap_guard.upgrade(caps); }
/*
* Called by the IO response handler for events which are not
* node-specific, for example after 'release_packet()' to signal
* that a previously failed 'alloc_packet()' may succeed now.
*/
void handle_general_io() {
_process_packets(); }
/********************************
** Node_io_handler interface **
********************************/
void handle_node_io(Io_node &node) override
{
_process_backlog();
if (!tx_sink()->ready_to_ack()) {
Genode::error(
"dropping I/O notfication, congested packet buffer to '", _label, "'");
}
if (node.notify_read_ready() && node.read_ready()
&& tx_sink()->ready_to_ack()) {
Packet_descriptor packet(Packet_descriptor(),
Node_handle { node.id().value },
Packet_descriptor::READ_READY,
0, 0);
packet.succeeded(true);
tx_sink()->acknowledge_packet(packet);
node.notify_read_ready(false);
}
}
void handle_node_watch(Watch_node &node) override
{
if (!tx_sink()->ready_to_ack()) {
Genode::error(
"dropping watch notfication, congested packet buffer to '", _label, "'");
} else {
Packet_descriptor packet(Packet_descriptor(),
Node_handle { node.id().value },
Packet_descriptor::CONTENT_CHANGED,
0, 0);
tx_sink()->acknowledge_packet(packet);
}
}
/***************************
** File_system interface **
@ -531,7 +375,8 @@ class Vfs_server::Session_component : private Session_resources,
Directory *dir;
try { dir = new (_alloc) Directory(_node_space, _vfs, _alloc,
*this, path_str, create); }
_pending_nodes, _stream,
path_str, create); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Dir_handle(dir->id().value);
@ -582,7 +427,8 @@ class Vfs_server::Session_component : private Session_resources,
Node *node;
try { node = new (_alloc) Node(_node_space, path_str, *this); }
try { node = new (_alloc) Node(_node_space, path_str,
_pending_nodes, _stream); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Node_handle { node->id().value };
@ -614,7 +460,8 @@ class Vfs_server::Session_component : private Session_resources,
Node *node;
try { node = new (_alloc)
Watch_node(_node_space, path_str, *vfs_handle, *this); }
Watch_node(_node_space, path_str, *vfs_handle,
_pending_nodes, _stream); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Watch_handle { node->id().value };
@ -622,9 +469,15 @@ class Vfs_server::Session_component : private Session_resources,
void close(Node_handle handle) override
{
/*
* churn the packet queue so that any pending
* packets on this handle are processed
*/
process_packets();
try { _apply_node(handle, [&] (Node &node) {
_close(node);
}); } catch (::File_system::Invalid_handle) { }
_close(node); }); }
catch (::File_system::Invalid_handle) { }
}
Status status(Node_handle node_handle) override
@ -713,95 +566,6 @@ class Vfs_server::Session_component : private Session_resources,
};
/**
* Global VFS event handler
*/
struct Vfs_server::Io_response_handler : Vfs::Io_response_handler
{
Session_registry &_session_registry;
bool _in_progress { false };
bool _handle_general_io { false };
Io_response_handler(Session_registry &session_registry)
: _session_registry(session_registry) { }
void handle_io_response(Vfs::Vfs_handle::Context *context) override
{
if (_in_progress) {
/* called recursively, context is nullptr in this case */
_handle_general_io = true;
return;
}
_in_progress = true;
if (context)
Io_node::node_by_context(*context).handle_io_response();
else
_handle_general_io = true;
while (_handle_general_io) {
_handle_general_io = false;
_session_registry.for_each([ ] (Registered_session &r) {
r.handle_general_io();
});
}
_in_progress = false;
}
};
/**
* Global VFS watch handler
*/
struct Vfs_server::Watch_response_handler : Vfs::Watch_response_handler
{
void handle_watch_response(Vfs::Vfs_watch_handle::Context *context) override
{
if (context)
Watch_node::node_by_context(*context).handle_watch_response();
}
};
class Vfs_server::Vfs_env final : Vfs::Env
{
private:
Genode::Env &_env;
Genode::Heap _heap { &_env.ram(), &_env.rm() };
Io_response_handler _io_handler;
Watch_response_handler _watch_handler { };
Vfs::Global_file_system_factory _global_file_system_factory { _heap };
Vfs::Dir_file_system _root_dir;
public:
Vfs_env(Genode::Env &env, Genode::Xml_node config,
Session_registry &sessions)
: _env(env), _io_handler(sessions),
_root_dir(*this, config, _global_file_system_factory)
{ }
Genode::Env &env() override { return _env; }
Genode::Allocator &alloc() override { return _heap; }
Vfs::File_system &root_dir() override { return _root_dir; }
Io_response_handler &io_handler() override {
return _io_handler; }
Watch_response_handler &watch_handler() override {
return _watch_handler; }
};
class Vfs_server::Root : public Genode::Root_component<Session_component>
{
private:
@ -820,38 +584,6 @@ class Vfs_server::Root : public Genode::Root_component<Session_component>
}
}
Session_registry _session_registry { };
Vfs_env _vfs_env { _env, vfs_config(), _session_registry };
/**
* Global I/O event handler
*
* This is a safe and slow intermediate implementation
* to be replaced with one that only processes handles
* and sessions that await progress.
*/
struct Io_progress_handler : Genode::Entrypoint::Io_progress_handler
{
Session_registry &_session_registry;
Io_progress_handler(Genode::Entrypoint &ep,
Session_registry &session_registry)
: _session_registry(session_registry)
{
ep.register_io_progress_handler(*this);
}
/**
* Entrypoint::Io_progress_handler interface
*/
void handle_io_progress() override
{
_session_registry.for_each([ ] (Registered_session &r) {
r.handle_general_io(); });
}
} _io_progress_handler { _env.ep(), _session_registry };
Genode::Signal_handler<Root> _config_handler {
_env.ep(), *this, &Root::_config_update };
@ -861,6 +593,81 @@ class Vfs_server::Root : public Genode::Root_component<Session_component>
_vfs_env.root_dir().apply_config(vfs_config());
}
/**
* The VFS uses an internal heap that
* subtracts from the component quota
*/
Genode::Heap _vfs_heap { &_env.ram(), &_env.rm() };
Vfs::Simple_env _vfs_env { _env, _vfs_heap, vfs_config() };
/**
* Object for post-I/O-signal processing
*
* This allows packet and VFS backend signals to
* be dispatched quickly followed by a processing
* of sessions that might be unblocked.
*/
struct Io_progress_handler : Genode::Entrypoint::Io_progress_handler
{
/* All nodes with a packet operation awaiting an I/O signal */
Node_queue pending_nodes { };
/* All sessions with packet queues that await processing */
Session_queue pending_sessions { };
/**
* Post-signal hook invoked by entrypoint
*/
void handle_io_progress() override
{
bool handle_progress = false;
/* process handles awaiting progress */
{
/* nodes to process later */
Node_queue retry { };
/* empty the pending nodes and process */
pending_nodes.dequeue_all([&] (Node &node) {
if (node.process_io()) {
handle_progress = true;
} else {
if (!node.enqueued()) {
retry.enqueue(node);
}
}
});
/* requeue the unprocessed nodes in order */
retry.dequeue_all([&] (Node &node) {
pending_nodes.enqueue(node); });
}
/*
* if any pending handles were processed then
* process session packet queues awaiting progress
*/
if (handle_progress) {
/* sessions to process later */
Session_queue retry { };
/* empty the pending nodes and process */
pending_sessions.dequeue_all([&] (Session_component &session) {
if (!session.process_packets()) {
/* requeue the session if there are packets remaining */
if (!session.enqueued()) {
retry.enqueue(session);
}
}
});
/* requeue the unprocessed sessions in order */
retry.dequeue_all([&] (Session_component &session) {
pending_sessions.enqueue(session); });
}
}
} _progress_handler { };
protected:
Session_component *_create_session(const char *args) override
@ -943,11 +750,13 @@ class Vfs_server::Root : public Genode::Root_component<Session_component>
}
Session_component *session = new (md_alloc())
Registered_session(_session_registry, _env, label.string(),
Genode::Ram_quota{ram_quota},
Genode::Cap_quota{cap_quota},
tx_buf_size, _vfs_env.root_dir(),
session_root.base(), writeable);
Session_component(_env, label.string(),
Genode::Ram_quota{ram_quota},
Genode::Cap_quota{cap_quota},
tx_buf_size, _vfs_env.root_dir(),
_progress_handler.pending_nodes,
_progress_handler.pending_sessions,
session_root.base(), writeable);
auto ram_used = _env.pd().used_ram().value - initial_ram_usage;
auto cap_used = _env.pd().used_caps().value - initial_cap_usage;
@ -967,6 +776,11 @@ class Vfs_server::Root : public Genode::Root_component<Session_component>
return session;
}
/**
* Session upgrades are important for the VFS server,
* this allows sessions to open arbitrarily large amounts
* of handles without starving other sessions.
*/
void _upgrade_session(Session_component *session,
char const *args) override
{
@ -986,6 +800,7 @@ class Vfs_server::Root : public Genode::Root_component<Session_component>
Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env)
{
_env.ep().register_io_progress_handler(_progress_handler);
_config_rom.sigh(_config_handler);
env.parent().announce(env.ep().manage(*this));
}

File diff suppressed because it is too large Load Diff

View File

@ -213,50 +213,25 @@ struct Noux::Main
/* initialize virtual file system */
Vfs::Global_file_system_factory _global_file_system_factory { _heap };
struct Io_response_handler : Vfs::Io_response_handler
struct Io_progress_handler : Genode::Entrypoint::Io_progress_handler
{
Vfs_io_waiter_registry io_waiter_registry { };
void handle_io_response(Vfs::Vfs_handle::Context *context) override
Io_progress_handler(Genode::Entrypoint &ep)
{
if (context) {
Vfs_handle_context *vfs_handle_context = static_cast<Vfs_handle_context*>(context);
vfs_handle_context->vfs_io_waiter.wakeup();
return;
}
ep.register_io_progress_handler(*this);
}
void handle_io_progress() override
{
io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
r.wakeup();
});
}
} _io_response_handler { };
} _io_response_handler { _env.ep() };
struct Vfs_env : Vfs::Env, Vfs::Watch_response_handler
{
Main &_main;
Vfs::Global_file_system_factory _fs_factory { _main._heap };
Vfs::Dir_file_system _root_dir;
Vfs_env(Main &main, Xml_node config)
: _main(main), _root_dir(*this, config, _fs_factory) { }
/**
* Vfs::Watch_response_handler interface
*/
void handle_watch_response(Vfs::Vfs_watch_handle::Context*) override { }
/**
* Vfs::Env interface
*/
Genode::Env &env() override { return _main._env; }
Allocator &alloc() override { return _main._heap; }
Vfs::File_system &root_dir() override { return _root_dir; }
Vfs::Io_response_handler &io_handler() override { return _main._io_response_handler; }
Vfs::Watch_response_handler &watch_handler() override { return *this; }
} _vfs_env { *this, _config.xml().sub_node("fstab") };
Vfs::Simple_env _vfs_env { _env, _heap, _config.xml().sub_node("fstab") };
Vfs::File_system &_root_dir = _vfs_env.root_dir();

View File

@ -79,7 +79,7 @@ struct Noux::Vfs_dataspace
Vfs_handle_context read_context;
Vfs::Vfs_handle::Guard guard(file);
file->context(&read_context);
file->handler(&read_context);
ds = ram.alloc(stat_out.size);

View File

@ -648,7 +648,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Vfs_handle_context read_context;
Vfs::Vfs_handle::Guard guard(symlink_handle);
symlink_handle->context(&read_context);
symlink_handle->handler(&read_context);
Vfs::file_size out_count = 0;
Vfs::File_io_service::Read_result read_result;
@ -782,7 +782,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Vfs_handle_context sync_context;
Vfs::Vfs_handle::Guard guard(symlink_handle);
symlink_handle->context(&sync_context);
symlink_handle->handler(&sync_context);
while (symlink_handle->fs().complete_sync(symlink_handle) ==
Vfs::File_io_service::SYNC_QUEUED)
@ -913,7 +913,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
Vfs_handle_context sync_context;
Vfs::Vfs_handle::Guard guard(sync_handle);
sync_handle->context(&sync_context);
sync_handle->handler(&sync_context);
while (sync_handle->fs().complete_sync(sync_handle) ==
Vfs::File_io_service::SYNC_QUEUED)

View File

@ -40,9 +40,15 @@ class Noux::Vfs_io_waiter
void wakeup() { _sem.up(); }
};
struct Noux::Vfs_handle_context : Vfs::Vfs_handle::Context
struct Noux::Vfs_handle_context : Vfs::Io_response_handler
{
Vfs_io_waiter vfs_io_waiter { };
void read_ready_response() override {
vfs_io_waiter.wakeup(); }
void io_progress_response() override {
vfs_io_waiter.wakeup(); }
};
struct Noux::Vfs_io_channel : Io_channel