diff --git a/repos/dde_linux/src/lib/vfs/lxip/vfs.cc b/repos/dde_linux/src/lib/vfs/lxip/vfs.cc index 43192ace82..3ac47e938b 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 6b6b157715..7b3269dfc1 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 ea43d3cb6d..2aac9cce16 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 4deba310ea..8591051104 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 093c0b52ae..54ba93c976 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 40c2c5604a..778f824668 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 11b69482df..51c637737d 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 435ba83c0a..e1ec7a3903 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 9495bab24d..827772faf2 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 165b3b704e..99379e09d1 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 848cffc005..2573588d20 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 6ac1f1bbd0..2c856ac9eb 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 1ed9abe980..a69c3d4448 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 8601b33d7a..4da5fd47a3 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 4a8fbd7571..655c9a87dc 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 ab90e4be6e..178a74b90e 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 5cadd40972..5eee5ebc04 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 9696ee4699..396185a83f 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 95e9605576..6c1fb8bf88 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 9096958e96..67260238dd 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 6d4a3cdd4e..d47fc9eac2 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 15df90aa6e..9449399bff 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 cc0d5cbbe5..db9425c926 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 7097da1485..ca4f77c578 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 6f19998aa0..a46e0aa5b7 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 013b1a89a4..0dd1603202 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 de16f42647..8290088a70 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