From a635873568e2c7292f95dd3087baa09be8515a8a Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Mon, 25 Mar 2019 15:41:43 +0100 Subject: [PATCH] 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 --- repos/dde_linux/src/lib/vfs/lxip/vfs.cc | 205 ++-- repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc | 2 +- repos/gems/include/gems/vfs.h | 27 +- repos/gems/src/app/fs_query/main.cc | 26 +- repos/gems/src/app/fs_tool/main.cc | 10 - repos/gems/src/app/menu_view/main.cc | 7 +- repos/gems/src/lib/vfs/audit/vfs_audit.cc | 10 +- repos/gems/src/lib/vfs/ttf/vfs.cc | 6 - repos/libports/src/lib/libc/task.cc | 50 +- repos/libports/src/lib/libc/vfs_plugin.cc | 10 + repos/libports/src/lib/libc/vfs_plugin.h | 14 +- repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc | 10 +- repos/libports/src/lib/vfs/lwip/vfs.cc | 223 +++-- repos/os/include/vfs/dir_file_system.h | 18 +- repos/os/include/vfs/env.h | 6 +- repos/os/include/vfs/file_io_service.h | 18 +- repos/os/include/vfs/simple_env.h | 11 - repos/os/include/vfs/vfs_handle.h | 119 ++- repos/os/src/lib/vfs/fs_file_system.h | 209 ++-- repos/os/src/lib/vfs/ram_file_system.h | 29 +- repos/os/src/lib/vfs/terminal_file_system.h | 132 +-- repos/os/src/server/vfs/main.cc | 595 ++++------- repos/os/src/server/vfs/node.h | 944 +++++++++++------- repos/ports/src/noux/main.cc | 41 +- repos/ports/src/noux/rom_session_component.h | 2 +- repos/ports/src/noux/syscall.cc | 6 +- repos/ports/src/noux/vfs_io_channel.h | 8 +- 27 files changed, 1397 insertions(+), 1341 deletions(-) diff --git a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc index 43192ace8..3ac47e938 100644 --- a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc +++ b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc @@ -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 file_le { this }; - List_element polling_le { this }; + /* file association element */ + List_element file_le { this }; + + /* notification elements */ + typedef Genode::Fifo_element Fifo_element; + typedef Genode::Fifo 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 *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 *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(_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(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_handle); - if (handle && dynamic_cast(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; } diff --git a/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc b/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc index 6b6b15771..7b3269dfc 100644 --- a/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc +++ b/repos/dde_rump/src/lib/vfs/rump/vfs_rump.cc @@ -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(); } } diff --git a/repos/gems/include/gems/vfs.h b/repos/gems/include/gems/vfs.h index ea43d3cb6..2aac9cce1 100644 --- a/repos/gems/include/gems/vfs.h +++ b/repos/gems/include/gems/vfs.h @@ -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 -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_ */ diff --git a/repos/gems/src/app/fs_query/main.cc b/repos/gems/src/app/fs_query/main.cc index 4deba310e..859105110 100644 --- a/repos/gems/src/app/fs_query/main.cc +++ b/repos/gems/src/app/fs_query/main.cc @@ -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 > _files { }; - Watcher _watcher; - Watched_directory(Allocator &alloc, Directory &other, Directory::Path const &rel_path) + Registry > _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(_files, _dir, entry.name()); + new (_alloc) Registered(_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(_dirs, _heap, _root_dir, path); + new (_heap) Registered(_dirs, _heap, _root_dir, path, *this); }); _reporter.generate([&] (Xml_generator &xml) { diff --git a/repos/gems/src/app/fs_tool/main.cc b/repos/gems/src/app/fs_tool/main.cc index 093c0b52a..54ba93c97 100644 --- a/repos/gems/src/app/fs_tool/main.cc +++ b/repos/gems/src/app/fs_tool/main.cc @@ -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 }; diff --git a/repos/gems/src/app/menu_view/main.cc b/repos/gems/src/app/menu_view/main.cc index 40c2c5604..778f82466 100644 --- a/repos/gems/src/app/menu_view/main.cc +++ b/repos/gems/src/app/menu_view/main.cc @@ -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; diff --git a/repos/gems/src/lib/vfs/audit/vfs_audit.cc b/repos/gems/src/lib/vfs/audit/vfs_audit.cc index 11b69482d..51c637737 100644 --- a/repos/gems/src/lib/vfs/audit/vfs_audit.cc +++ b/repos/gems/src/lib/vfs/audit/vfs_audit.cc @@ -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: diff --git a/repos/gems/src/lib/vfs/ttf/vfs.cc b/repos/gems/src/lib/vfs/ttf/vfs.cc index 435ba83c0..e1ec7a390 100644 --- a/repos/gems/src/lib/vfs/ttf/vfs.cc +++ b/repos/gems/src/lib/vfs/ttf/vfs.cc @@ -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: diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index 9495bab24..827772faf 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -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> _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(); + } } }; diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index 165b3b704..99379e09d 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -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 { diff --git a/repos/libports/src/lib/libc/vfs_plugin.h b/repos/libports/src/lib/libc/vfs_plugin.h index 848cffc00..2573588d2 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.h +++ b/repos/libports/src/lib/libc/vfs_plugin.h @@ -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; diff --git a/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc b/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc index 6ac1f1bbd..2c856ac9e 100644 --- a/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc +++ b/repos/libports/src/lib/vfs/fatfs/vfs_fatfs.cc @@ -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); } diff --git a/repos/libports/src/lib/vfs/lwip/vfs.cc b/repos/libports/src/lib/vfs/lwip/vfs.cc index 1ed9abe98..a69c3d444 100644 --- a/repos/libports/src/lib/vfs/lwip/vfs.cc +++ b/repos/libports/src/lib/vfs/lwip/vfs.cc @@ -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 Fifo_element; + typedef Genode::Fifo 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_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(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(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 Handle_element; + typedef Genode::Fifo 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(vfs_handle)) { if (Lwip_file_handle *file_handle = dynamic_cast(handle)) { socket = file_handle->socket; @@ -1779,7 +1835,13 @@ class Lwip::File_system final : public Vfs::File_system if (Lwip_handle *handle = dynamic_cast(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(vfs_handle)) { - if (handle->socket) { - return true; - } - } + if (Lwip_file_handle *handle = dynamic_cast(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(vfs_handle); - if (h) { - if (h->socket) { - return h->socket->complete_sync(); - } else { - return SYNC_QUEUED; - } - } - return SYNC_OK; + return (dynamic_cast(vfs_handle)) + ? SYNC_OK : SYNC_ERR_INVALID; } /*********************** diff --git a/repos/os/include/vfs/dir_file_system.h b/repos/os/include/vfs/dir_file_system.h index 8601b33d7..4da5fd47a 100644 --- a/repos/os/include/vfs/dir_file_system.h +++ b/repos/os/include/vfs/dir_file_system.h @@ -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(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)) { diff --git a/repos/os/include/vfs/env.h b/repos/os/include/vfs/env.h index 4a8fbd757..655c9a87d 100644 --- a/repos/os/include/vfs/env.h +++ b/repos/os/include/vfs/env.h @@ -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_ */ diff --git a/repos/os/include/vfs/file_io_service.h b/repos/os/include/vfs/file_io_service.h index ab90e4be6..178a74b90 100644 --- a/repos/os/include/vfs/file_io_service.h +++ b/repos/os/include/vfs/file_io_service.h @@ -18,25 +18,9 @@ #include -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 }; diff --git a/repos/os/include/vfs/simple_env.h b/repos/os/include/vfs/simple_env.h index 5cadd4097..5eee5ebc0 100644 --- a/repos/os/include/vfs/simple_env.h +++ b/repos/os/include/vfs/simple_env.h @@ -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_ */ diff --git a/repos/os/include/vfs/vfs_handle.h b/repos/os/include/vfs/vfs_handle.h index 9696ee469..396185a83 100644 --- a/repos/os/include/vfs/vfs_handle.h +++ b/repos/os/include/vfs/vfs_handle.h @@ -17,30 +17,59 @@ #include 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 + 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. diff --git a/repos/os/src/lib/vfs/fs_file_system.h b/repos/os/src/lib/vfs/fs_file_system.h index 95e960557..6c1fb8bf8 100644 --- a/repos/os/src/lib/vfs/fs_file_system.h +++ b/repos/os/src/lib/vfs/fs_file_system.h @@ -21,7 +21,6 @@ #include #include - 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_queue; + struct Fs_vfs_handle : Vfs_handle, private ::File_system::Node, private Handle_space::Element, - private List::Element, + private Fs_vfs_handle_queue::Element, private Handle_state { friend Genode::Id_space<::File_system::Node>; - friend Genode::List; - using Genode::List::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::Element + private Handle_space::Element { friend Genode::Id_space<::File_system::Node>; - friend Genode::List; - using Genode::List::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 _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(id, [&] (Fs_vfs_watch_handle &handle) { - _env.watch_handler().handle_watch_response(handle.context()); }); + handle.watch_response(); }); } else { _handle_space.apply(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 _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 _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(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(*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(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(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; } diff --git a/repos/os/src/lib/vfs/ram_file_system.h b/repos/os/src/lib/vfs/ram_file_system.h index 9096958e9..67260238d 100644 --- a/repos/os/src/lib/vfs/ram_file_system.h +++ b/repos/os/src/lib/vfs/ram_file_system.h @@ -147,13 +147,10 @@ class Vfs_ram::Node : private Genode::Avl_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; diff --git a/repos/os/src/lib/vfs/terminal_file_system.h b/repos/os/src/lib/vfs/terminal_file_system.h index 6d4a3cdd4..d47fc9eac 100644 --- a/repos/os/src/lib/vfs/terminal_file_system.h +++ b/repos/os/src/lib/vfs/terminal_file_system.h @@ -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 #include -#include #include @@ -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 Registered_handle; - typedef Genode::Registry 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 Registered_handle; + typedef Genode::Registry 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(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); } }; diff --git a/repos/os/src/server/vfs/main.cc b/repos/os/src/server/vfs/main.cc index 15df90aa6..9449399bf 100644 --- a/repos/os/src/server/vfs/main.cc +++ b/repos/os/src/server/vfs/main.cc @@ -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 #include #include -#include -#include +#include +#include /* 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 Registered_session; - typedef Genode::Registry Session_registry; + typedef Genode::Fifo 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 _process_packet_handler; - - Vfs::File_system &_vfs; + Genode::Signal_handler _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(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(&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) { _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 { private: @@ -820,38 +584,6 @@ class Vfs_server::Root : public Genode::Root_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 _config_handler { _env.ep(), *this, &Root::_config_update }; @@ -861,6 +593,81 @@ class Vfs_server::Root : public Genode::Root_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 = 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 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 Root_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)); } diff --git a/repos/os/src/server/vfs/node.h b/repos/os/src/server/vfs/node.h index cc0d5cbbe..db9425c92 100644 --- a/repos/os/src/server/vfs/node.h +++ b/repos/os/src/server/vfs/node.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2016-2017 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. @@ -29,6 +29,12 @@ namespace Vfs_server { using namespace File_system; using namespace Vfs; + typedef Vfs::File_io_service::Write_result Write_result; + typedef Vfs::File_io_service::Read_result Read_result; + typedef Vfs::File_io_service::Sync_result Sync_result; + + typedef ::File_system::Session::Tx::Sink Packet_stream; + class Node; class Io_node; class Watch_node; @@ -37,19 +43,7 @@ namespace Vfs_server { class Symlink; typedef Genode::Id_space Node_space; - - struct Session_io_handler : Interface - { - virtual void handle_node_io(Io_node &node) = 0; - virtual void handle_node_watch(Watch_node &node) = 0; - }; - - /** - * Read/write operation incomplete exception - * - * The operation can be retried later. - */ - struct Operation_incomplete { }; + typedef Genode::Fifo Node_queue; /* Vfs::MAX_PATH is shorter than File_system::MAX_PATH */ enum { MAX_PATH_LEN = Vfs::MAX_PATH_LEN }; @@ -81,18 +75,17 @@ namespace Vfs_server { /* * Note that the file objects are created at the * VFS in the local node constructors, this is to - * ensure that Out_of_ram is thrown before - * the VFS is modified. + * ensure that in the case of file creating that the + * Out_of_ram exception is thrown before the VFS is + * modified. */ } + class Vfs_server::Node : public ::File_system::Node_base, - private Node_space::Element + private Node_space::Element, + private Node_queue::Element { - public: - - enum Op_state { IDLE, READ_QUEUED, SYNC_QUEUED }; - private: /* @@ -105,27 +98,51 @@ class Vfs_server::Node : public ::File_system::Node_base, protected: - /** - * I/O handler for session context + /* + * Global queue of nodes that await + * some response from the VFS libray + * + * A global collection is perhaps dangerous + * but ensures fairness across sessions */ - Session_io_handler &_session_io_handler; + Node_queue &_response_queue; + + /* stream used for reply packets */ + Packet_stream &_stream; public: - Node(Node_space &space, char const *node_path, - Session_io_handler &io_handler) - : - Node_space::Element(*this, space), - _path(node_path), - _session_io_handler(io_handler) + friend Node_queue; + using Node_queue::Element::enqueued; + + Node(Node_space &space, + char const *node_path, + Node_queue &response_queue, + Packet_stream &stream) + : Node_space::Element(*this, space), + _path(node_path), + _response_queue(response_queue), + _stream(stream) { } - virtual ~Node() { } + virtual ~Node() + { + if (enqueued()) + _response_queue.remove(*this); + } using Node_space::Element::id; char const *path() const { return _path.base(); } + /** + * Process pending activity, called by post-signal hook + * + * Default implementation is to return true so that the + * node is removed from the pending handle queue. + */ + virtual bool process_io() { return true; } + /** * Print for debugging */ @@ -133,9 +150,12 @@ class Vfs_server::Node : public ::File_system::Node_base, out.out_string(_path.base()); } }; -class Vfs_server::Io_node : public Vfs_server::Node, - private Vfs::Vfs_handle::Context -{ + +/** + * Super-class for nodes that process read/write packets + */ +class Vfs_server::Io_node : public Vfs_server::Node, + public Vfs::Io_response_handler{ public: enum Op_state { IDLE, READ_QUEUED, SYNC_QUEUED }; @@ -150,165 +170,285 @@ class Vfs_server::Io_node : public Vfs_server::Node, Mode const _mode; - bool _notify_read_ready = false; + bool _packet_queued = false; + bool _packet_op_pending = false; protected: - Vfs::Vfs_handle::Context &context() { return *this; } + Vfs::Vfs_handle &_handle; - Vfs::Vfs_handle *_handle { nullptr }; - Op_state op_state { Op_state::IDLE }; + /** + * Packets that have been removed from the + * packet stream are transfered here + */ + Packet_descriptor _packet { }; - size_t _read(char *dst, size_t len, seek_off_t seek_offset) + /** + * Abstract read implementation + * + * Returns true if the pending packet + * shall be returned to client + */ + bool _vfs_read(char *dst, file_size count, + file_offset seek_offset, file_size &out_count) { - _handle->seek(seek_offset); + if (!(_mode & READ_ONLY)) return true; - typedef Vfs::File_io_service::Read_result Result; + _handle.seek(seek_offset); - Vfs::file_size out_count = 0; - Result out_result = Result::READ_OK; - - switch (op_state) { - case Op_state::IDLE: - - if (!_handle->fs().queue_read(_handle, len)) - throw Operation_incomplete(); - - /* fall through */ - - case Op_state::READ_QUEUED: - out_result = _handle->fs().complete_read(_handle, dst, len, - out_count); - switch (out_result) { - case Result::READ_OK: - op_state = Op_state::IDLE; - return out_count; - - case Result::READ_ERR_WOULD_BLOCK: - case Result::READ_ERR_AGAIN: - case Result::READ_ERR_INTERRUPT: - op_state = Op_state::IDLE; - throw Operation_incomplete(); - - case Result::READ_ERR_IO: - case Result::READ_ERR_INVALID: - op_state = Op_state::IDLE; - /* FIXME revise error handling */ - return 0; - - case Result::READ_QUEUED: - op_state = Op_state::READ_QUEUED; - throw Operation_incomplete(); + if (!_packet_op_pending) { + /* if the read cannot be queued with the VFS then stop here */ + if (!_handle.fs().queue_read(&_handle, count)) { + return false; } + _packet_op_pending = true; + } + + Read_result result = _handle.fs().complete_read( + &_handle, dst, count, out_count); + + switch (result) { + case Read_result::READ_OK: + _packet.succeeded(true); break; - case Op_state::SYNC_QUEUED: - throw Operation_incomplete(); + case Read_result::READ_ERR_IO: + case Read_result::READ_ERR_INVALID: + _packet.length(out_count); + break; + + case Read_result::READ_ERR_WOULD_BLOCK: + case Read_result::READ_ERR_AGAIN: + case Read_result::READ_ERR_INTERRUPT: + case Read_result::READ_QUEUED: + /* packet is still pending */ + return false; } - return 0; + /* packet is processed */ + _packet_op_pending = false; + return true; } - size_t _write(char const *src, size_t len, - seek_off_t seek_offset) + /** + * Abstract write implementation + * + * Returns true if the pending packet + * shall be returned to client + */ + bool _vfs_write(char const *src, file_size count, + file_offset seek_offset, file_size &out_count) { - Vfs::file_size res = 0; + if (!(_mode & WRITE_ONLY)) + return true; - _handle->seek(seek_offset); + _handle.seek(seek_offset); try { - _handle->fs().write(_handle, src, len, res); - } catch (Vfs::File_io_service::Insufficient_buffer) { - throw Operation_incomplete(); + Write_result result = _handle.fs().write( + &_handle, src, count, out_count); + + if (result == Write_result::WRITE_OK) { + mark_as_updated(); + _packet.succeeded(true); + } + } + catch (Vfs::File_io_service::Insufficient_buffer) + { + /* packet is pending */ + return false; } - if (res) - mark_as_updated(); + /* packet is processed */ + return true; - return res; + /* No further error handling! */ } + inline + void _drop_packet() + { + _packet = Packet_descriptor(); + _packet_queued = false; + } + + inline + void _ack_packet(size_t count) + { + _packet.length(count); + _stream.acknowledge_packet(_packet); + _packet = Packet_descriptor(); + _packet_queued = false; + } + + /** + * Abstract sync implementation + */ + bool _sync() + { + if (!_packet_op_pending) { + /* if the sync cannot be queued with the VFS then stop here */ + if (!_handle.fs().queue_sync(&_handle)) { + return false; + } + _packet_op_pending = true; + } + + Sync_result result = _handle.fs().complete_sync(&_handle); + + switch (result) { + case Sync_result::SYNC_OK: + _packet.succeeded(true); + break; + + case Sync_result::SYNC_ERR_INVALID: + break; + + case Sync_result::SYNC_QUEUED: + /* packet still pending */ + return false; + } + + /* packet processed */ + _ack_packet(0); + _packet_op_pending = false; + return true; + } + + /** + * Virtual methods for specialized node-type I/O + */ + virtual bool _read() = 0; + virtual bool _write() = 0; + public: Io_node(Node_space &space, char const *node_path, Mode node_mode, - Session_io_handler &io_handler) - : Node(space, node_path, io_handler), _mode(node_mode) { } + Node_queue &response_queue, Packet_stream &stream, + Vfs_handle &handle) + : Node(space, node_path, response_queue, stream), + _mode(node_mode), _handle(handle) + { + _handle.handler(this); + } - virtual ~Io_node() { } + virtual ~Io_node() + { + _handle.handler(nullptr); + _handle.close(); + } using Node_space::Element::id; - static Io_node &node_by_context(Vfs::Vfs_handle::Context &context) + /** + * Process the packet that is queued at this handle + * + * Return true if the node was processed and is now idle. + */ + bool process_io() override { - return static_cast(context); + if (!_packet_queued) return true; + if (!_stream.ready_to_ack()) + return false; + + bool result = true; + + switch (_packet.operation()) { + case Packet_descriptor::READ: result = _read(); break; + case Packet_descriptor::WRITE: result = _write(); break; + case Packet_descriptor::SYNC: result = _sync(); break; + + case Packet_descriptor::READ_READY: + /* + * the read-ready pending state is managed + * by the VFS, this packet can be discarded + */ + _drop_packet(); + + if (_handle.fs().read_ready(&_handle)) { + /* if the handle is ready, send a packet back immediately */ + read_ready_response(); + } else { + /* register to send READ_READY later */ + _handle.fs().notify_read_ready(&_handle); + } + + break; + + case Packet_descriptor::CONTENT_CHANGED: + /* discard this packet */ + _drop_packet(); + break; + } + + return result; + } + + /** + * Process a packet by queuing it locally or sending + * an immediate response. Return false if no progress + * can be made. + * + * Called by packet stream signal handler + */ + bool process_packet(Packet_descriptor const &packet) + { + /* attempt to clear any pending packet */ + if (!process_io()) + return false; + + /* otherwise store the packet locally and process */ + _packet = packet; + _packet_queued = true; + process_io(); + return true; } Mode mode() const { return _mode; } - virtual size_t read(char * /* dst */, size_t /* len */, seek_off_t) - { return 0; } - virtual size_t write(char const * /* src */, size_t /* len */, - seek_off_t) { return 0; } - - bool read_ready() { return _handle->fs().read_ready(_handle); } + /**************************************** + ** Vfs::Io_response_handler interface ** + ****************************************/ /** - * The global handler has drawn an association from an I/O - * context and this open node, now process the event at the - * session for this node. + * Called by the VFS plugin of this handle */ - void handle_io_response() { - _session_io_handler.handle_node_io(*this); } - - void notify_read_ready(bool requested) + void read_ready_response() override { - if (requested) - _handle->fs().notify_read_ready(_handle); - _notify_read_ready = requested; + if (!_stream.ready_to_ack()) { + /* log a message to catch loops */ + Genode::warning("deferring READ_READY response"); + _handle.fs().notify_read_ready(&_handle); + return; + } + + /* Send packet immediately, though this could be queued */ + Packet_descriptor packet(Packet_descriptor(), + Node_handle { id().value }, + Packet_descriptor::READ_READY, + 0, 0); + packet.succeeded(true); + _stream.acknowledge_packet(packet); } - bool notify_read_ready() const { return _notify_read_ready; } - - bool sync() + /** + * Called by the VFS plugin of this handle + */ + void io_progress_response() override { - typedef Vfs::File_io_service::Sync_result Result; - Result out_result = Result::SYNC_OK; - - switch (op_state) { - case Op_state::IDLE: - - if (!_handle->fs().queue_sync(_handle)) - throw Operation_incomplete(); - - /* fall through */ - - case Op_state::SYNC_QUEUED: - out_result = _handle->fs().complete_sync(_handle); - switch (out_result) { - case Result::SYNC_OK: - op_state = Op_state::IDLE; - return true; - - case Result::SYNC_ERR_INVALID: - return false; - - case Result::SYNC_QUEUED: - op_state = Op_state::SYNC_QUEUED; - throw Operation_incomplete(); - } - break; - - case Op_state::READ_QUEUED: - throw Operation_incomplete(); - } - return false; + /* + * do not process packet immediately, + * queue to maintain ordering (priorities?) + */ + if (!enqueued()) + _response_queue.enqueue(*this); } }; -class Vfs_server::Watch_node final : public Vfs_server::Node, - private Vfs::Vfs_watch_handle::Context +class Vfs_server::Watch_node final : public Vfs_server::Node, + public Vfs::Watch_response_handler { private: @@ -324,96 +464,136 @@ class Vfs_server::Watch_node final : public Vfs_server::Node, Watch_node(Node_space &space, char const *path, Vfs::Vfs_watch_handle &handle, - Session_io_handler &io_handler) - : - Node(space, path, io_handler), - _watch_handle(handle) + Node_queue &response_queue, + Packet_stream &stream) + : Node(space, path, response_queue, stream), + _watch_handle(handle) { - /* - * set the context so this Watch object - * is passed back thru the Io_handler - */ - _watch_handle.context(this); + _watch_handle.handler(this); } - ~Watch_node() + ~Watch_node() { + _watch_handle.close(); } + + + /******************************************* + ** Vfs::Watch_response_handler interface ** + *******************************************/ + + void watch_response() override { - _watch_handle.context((Vfs::Vfs_watch_handle::Context*)~0ULL); - _watch_handle.close(); + /* send a packet immediately otherwise defer */ + if (!process_io() && !enqueued()) + _response_queue.enqueue(*this); } - static Watch_node &node_by_context(Vfs::Vfs_watch_handle::Context &context) - { - return static_cast(context); - } + + /******************************** + ** Vfs_server::Node interface ** + ********************************/ /** - * The global handler has drawn an association from a watch - * context and this open node, now process the event at the - * session for this node. + * Called by global I/O progress handler */ - void handle_watch_response() { - _session_io_handler.handle_node_watch(*this); } + bool process_io() override + { + if (!_stream.ready_to_ack()) return false; + + Packet_descriptor packet(Packet_descriptor(), + Node_handle { id().value }, + Packet_descriptor::CONTENT_CHANGED, + 0, 0); + packet.succeeded(true); + _stream.acknowledge_packet(packet); + return true; + } }; struct Vfs_server::Symlink : Io_node { - Symlink(Node_space &space, - Vfs::File_system &vfs, - Genode::Allocator &alloc, - Session_io_handler &node_io_handler, - char const *link_path, - Mode mode, - bool create) - : Io_node(space, link_path, mode, node_io_handler) - { - assert_openlink(vfs.openlink(link_path, create, &_handle, alloc)); - _handle->context(&context()); - } + protected: - ~Symlink() { _handle->close(); } + /******************** + ** Node interface ** + ********************/ - /******************** - ** Node interface ** - ********************/ + bool _read() override + { + if (_packet.position() != 0) { + /* partial read is not supported */ + _ack_packet(0); + return true; + } - size_t read(char *dst, size_t len, seek_off_t seek_offset) override - { - if (seek_offset != 0) { - /* partial read is not supported */ - return 0; + file_size out_count = 0; + bool result = _vfs_read(_stream.packet_content(_packet), + _packet.length(), 0, out_count); + if (result) + _ack_packet(out_count); + return result; } - return _read(dst, len, 0); - } + bool _write() override + { + if (_packet.position() != 0) { + /* partial write is not supported */ + _ack_packet(0); + return true; + } - size_t write(char const *src, size_t const len, seek_off_t) override - { - /* - * if the symlink target is too long return a short result - * because a competent File_system client will error on a - * length mismatch - */ + file_size count = _packet.length(); - if (len > MAX_PATH_LEN) { - return len >> 1; + /* + * if the symlink target is too long return a short result + * because a competent File_system client will error on a + * length mismatch + */ + if (count > MAX_PATH_LEN) { + _ack_packet(1); + return true; + } + + /* ensure symlink gets something null-terminated */ + Genode::String target(Genode::Cstring( + _stream.packet_content(_packet), count)); + size_t const target_len = target.length()-1; + + file_size out_count = 0; + bool result = _vfs_write(target.string(), target_len, 0, out_count); + + if (result) { + _ack_packet(out_count); + if (out_count > 0) { + mark_as_updated(); + notify_listeners(); + } + } + return result; } - /* ensure symlink gets something null-terminated */ - Genode::String target(Genode::Cstring(src, len)); - size_t const target_len = target.length()-1; + static + Vfs_handle &_open(Vfs::File_system &vfs, Genode::Allocator &alloc, + char const *link_path, bool create) + { + Vfs_handle *h = nullptr; + assert_openlink(vfs.openlink(link_path, create, &h, alloc)); + return *h; + } - file_size out_count; + public: - if (_handle->fs().write(_handle, target.string(), target_len, out_count) != - File_io_service::WRITE_OK) - return 0; - - mark_as_updated(); - notify_listeners(); - return len; - } + Symlink(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Node_queue &response_queue, + Packet_stream &stream, + char const *link_path, + Mode mode, + bool create) + : Io_node(space, link_path, mode, response_queue, stream, + _open(vfs, alloc, link_path, create)) + { } }; @@ -429,60 +609,89 @@ class Vfs_server::File : public Io_node char const *_leaf_path = nullptr; /* offset pointer to Node::_path */ + inline + seek_off_t seek_tail(file_size count) + { + typedef Directory_service::Stat_result Result; + Vfs::Directory_service::Stat st; + + /* if stat fails, try and see if the VFS will seek to the end */ + return (_handle.ds().stat(_leaf_path, st) == Result::STAT_OK) + ? ((count < st.size) ? (st.size - count) : 0) + : (seek_off_t)SEEK_TAIL; + } + + protected: + + bool _read() override + { + file_size out_count = 0; + file_size count = _packet.length(); + seek_off_t seek_offset = _packet.position(); + + if (seek_offset == (seek_off_t)SEEK_TAIL) + seek_offset = seek_tail(count); + + bool result = _vfs_read(_stream.packet_content(_packet), + count, seek_offset, out_count); + if (result) + _ack_packet(out_count); + return result; + } + + bool _write() override + { + file_size out_count = 0; + file_size count = _packet.length(); + seek_off_t seek_offset = _packet.position(); + + if (seek_offset == (seek_off_t)SEEK_TAIL) + seek_offset = seek_tail(count); + + bool result = _vfs_write(_stream.packet_content(_packet), + count, seek_offset, out_count); + if (result) { + _ack_packet(out_count); + if (out_count > 0) { + mark_as_updated(); + notify_listeners(); + } + } + return result; + } + + static + Vfs_handle &_open(Vfs::File_system &vfs, Genode::Allocator &alloc, + char const *file_path, Mode fs_mode, bool create) + { + Vfs_handle *h = nullptr; + unsigned vfs_mode = (fs_mode-1) | + (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0); + + assert_open(vfs.open(file_path, vfs_mode, &h, alloc)); + return *h; + } + public: - File(Node_space &space, - Vfs::File_system &vfs, - Genode::Allocator &alloc, - Session_io_handler &node_io_handler, - char const *file_path, - Mode fs_mode, - bool create) + File(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Node_queue &response_queue, + Packet_stream &stream, + char const *file_path, + Mode fs_mode, + bool create) : - Io_node(space, file_path, fs_mode, node_io_handler) + Io_node(space, file_path, fs_mode, response_queue, stream, + _open(vfs, alloc, file_path, fs_mode, create)) { - unsigned vfs_mode = - (fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0); - - assert_open(vfs.open(file_path, vfs_mode, &_handle, alloc)); - _leaf_path = vfs.leaf_path(path()); - _handle->context(&context()); - } - - ~File() { _handle->close(); } - - size_t read(char *dst, size_t len, seek_off_t seek_offset) override - { - if (seek_offset == (seek_off_t)SEEK_TAIL) { - typedef Directory_service::Stat_result Result; - Vfs::Directory_service::Stat st; - - /* if stat fails, try and see if the VFS will seek to the end */ - seek_offset = (_handle->ds().stat(_leaf_path, st) == Result::STAT_OK) ? - ((len < st.size) ? (st.size - len) : 0) : (seek_off_t)SEEK_TAIL; - } - - return _read(dst, len, seek_offset); - } - - size_t write(char const *src, size_t len, - seek_off_t seek_offset) override - { - if (seek_offset == (seek_off_t)SEEK_TAIL) { - typedef Directory_service::Stat_result Result; - Vfs::Directory_service::Stat st; - - /* if stat fails, try and see if the VFS will seek to the end */ - seek_offset = (_handle->ds().stat(_leaf_path, st) == Result::STAT_OK) ? - st.size : (seek_off_t)SEEK_TAIL; - } - - return _write(src, len, seek_offset); + _leaf_path = vfs.leaf_path(path()); } void truncate(file_size_t size) { - assert_truncate(_handle->fs().ftruncate(_handle, size)); + assert_truncate(_handle.fs().ftruncate(&_handle, size)); mark_as_updated(); } }; @@ -490,106 +699,141 @@ class Vfs_server::File : public Io_node struct Vfs_server::Directory : Io_node { - Directory(Node_space &space, - Vfs::File_system &vfs, - Genode::Allocator &alloc, - Session_io_handler &node_io_handler, - char const *dir_path, - bool create) - : Io_node(space, dir_path, READ_ONLY, node_io_handler) - { - assert_opendir(vfs.opendir(dir_path, create, &_handle, alloc)); - _handle->context(&context()); - } + protected: - ~Directory() { _handle->close(); } + /******************** + ** Node interface ** + ********************/ - Node_space::Id file(Node_space &space, - Vfs::File_system &vfs, - Genode::Allocator &alloc, - char const *file_path, - Mode mode, - bool create) - { - Path subpath(file_path, path()); - char const *path_str = subpath.base(); - - File *file; - try { - file = new (alloc) File(space, vfs, alloc, - _session_io_handler, - path_str, mode, create); - } catch (Out_of_memory) { throw Out_of_ram(); } - - if (create) - mark_as_updated(); - return file->id(); - } - - Node_space::Id symlink(Node_space &space, - Vfs::File_system &vfs, - Genode::Allocator &alloc, - char const *link_path, - Mode mode, - bool create) - { - Path subpath(link_path, path()); - char const *path_str = subpath.base(); - - Symlink *link; - try { link = new (alloc) Symlink(space, vfs, alloc, - _session_io_handler, - path_str, mode, create); } - catch (Out_of_memory) { throw Out_of_ram(); } - if (create) - mark_as_updated(); - return link->id(); - } - - - /******************** - ** Node interface ** - ********************/ - - size_t read(char *dst, size_t len, seek_off_t seek_offset) override - { - Directory_service::Dirent vfs_dirent; - size_t blocksize = sizeof(::File_system::Directory_entry); - - unsigned index = (seek_offset / blocksize); - - size_t remains = len; - - while (remains >= blocksize) { - - if ((_read((char*)&vfs_dirent, sizeof(vfs_dirent), - index * sizeof(vfs_dirent)) < sizeof(vfs_dirent)) || - (vfs_dirent.type == Vfs::Directory_service::DIRENT_TYPE_END)) - return len - remains; - - ::File_system::Directory_entry *fs_dirent = (Directory_entry *)dst; - fs_dirent->inode = vfs_dirent.fileno; - switch (vfs_dirent.type) { - case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY: - fs_dirent->type = ::File_system::Directory_entry::TYPE_DIRECTORY; - break; - case Vfs::Directory_service::DIRENT_TYPE_SYMLINK: - fs_dirent->type = ::File_system::Directory_entry::TYPE_SYMLINK; - break; - case Vfs::Directory_service::DIRENT_TYPE_FILE: - default: - fs_dirent->type = ::File_system::Directory_entry::TYPE_FILE; - break; + bool _read() override + { + if (_packet.length() < sizeof(Directory_entry)) { + _ack_packet(0); + return true; } - strncpy(fs_dirent->name, vfs_dirent.name, MAX_NAME_LEN); - remains -= blocksize; - dst += blocksize; + seek_off_t seek_offset = _packet.position(); + + Directory_service::Dirent vfs_dirent; + size_t blocksize = sizeof(::File_system::Directory_entry); + + unsigned index = (seek_offset / blocksize); + + file_size out_count = 0; + bool result = _vfs_read( + (char*)&vfs_dirent, sizeof(vfs_dirent), + index * sizeof(vfs_dirent), out_count); + + if (result) { + if (out_count != sizeof(vfs_dirent)) { + _ack_packet(0); + return true; + } + + ::File_system::Directory_entry *fs_dirent = + (Directory_entry *)_stream.packet_content(_packet); + fs_dirent->inode = vfs_dirent.fileno; + + switch (vfs_dirent.type) { + case Vfs::Directory_service::DIRENT_TYPE_END: + _ack_packet(0); + return true; + + case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY: + fs_dirent->type = ::File_system::Directory_entry::TYPE_DIRECTORY; + break; + case Vfs::Directory_service::DIRENT_TYPE_SYMLINK: + fs_dirent->type = ::File_system::Directory_entry::TYPE_SYMLINK; + break; + case Vfs::Directory_service::DIRENT_TYPE_FILE: + default: + fs_dirent->type = ::File_system::Directory_entry::TYPE_FILE; + break; + } + + strncpy(fs_dirent->name, vfs_dirent.name, MAX_NAME_LEN); + + _ack_packet(sizeof(Directory_entry)); + return true; + } + return false; } - return len - remains; - } - size_t write(char const *, size_t, seek_off_t) override { return 0; } + bool _write() override + { + _ack_packet(0); + return true; + } + + static + Vfs_handle &_open(Vfs::File_system &vfs, Genode::Allocator &alloc, + char const *dir_path, bool create) + { + Vfs_handle *h = nullptr; + assert_opendir(vfs.opendir(dir_path, create, &h, alloc)); + return *h; + } + + public: + + Directory(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + Node_queue &response_queue, + Packet_stream &stream, + char const *dir_path, + bool create) + : Io_node(space, dir_path, READ_ONLY, response_queue, stream, + _open(vfs, alloc, dir_path, create)) + { } + + /** + * Open a file handle at this directory + */ + Node_space::Id file(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + char const *file_path, + Mode mode, + bool create) + { + Path subpath(file_path, path()); + char const *path_str = subpath.base(); + + File *file; + try { + file = new (alloc) File(space, vfs, alloc, + _response_queue, _stream, + path_str, mode, create); + } catch (Out_of_memory) { throw Out_of_ram(); } + + if (create) + mark_as_updated(); + return file->id(); + } + + /** + * Open a symlink handle at this directory + */ + Node_space::Id symlink(Node_space &space, + Vfs::File_system &vfs, + Genode::Allocator &alloc, + char const *link_path, + Mode mode, + bool create) + { + Path subpath(link_path, path()); + char const *path_str = subpath.base(); + + Symlink *link; + try { link = new (alloc) Symlink(space, vfs, alloc, + _response_queue, _stream, + path_str, mode, create); } + catch (Out_of_memory) { throw Out_of_ram(); } + if (create) + mark_as_updated(); + return link->id(); + } }; #endif /* _VFS__NODE_H_ */ diff --git a/repos/ports/src/noux/main.cc b/repos/ports/src/noux/main.cc index 7097da148..ca4f77c57 100644 --- a/repos/ports/src/noux/main.cc +++ b/repos/ports/src/noux/main.cc @@ -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(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(); diff --git a/repos/ports/src/noux/rom_session_component.h b/repos/ports/src/noux/rom_session_component.h index 6f19998aa..a46e0aa5b 100644 --- a/repos/ports/src/noux/rom_session_component.h +++ b/repos/ports/src/noux/rom_session_component.h @@ -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); diff --git a/repos/ports/src/noux/syscall.cc b/repos/ports/src/noux/syscall.cc index 013b1a89a..0dd160320 100644 --- a/repos/ports/src/noux/syscall.cc +++ b/repos/ports/src/noux/syscall.cc @@ -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) diff --git a/repos/ports/src/noux/vfs_io_channel.h b/repos/ports/src/noux/vfs_io_channel.h index de16f4264..8290088a7 100644 --- a/repos/ports/src/noux/vfs_io_channel.h +++ b/repos/ports/src/noux/vfs_io_channel.h @@ -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