From 61b6dccf13e904811be58e896244a9a5cc17e343 Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Wed, 1 Feb 2017 21:07:14 +0100 Subject: [PATCH] Queued read/write/read_ready in VFS and servers --- repos/libports/src/lib/libc/task.cc | 9 +- repos/libports/src/lib/libc/vfs_plugin.cc | 3 +- repos/os/include/vfs/dir_file_system.h | 14 +- repos/os/include/vfs/file_io_service.h | 14 +- repos/os/include/vfs/vfs_handle.h | 13 +- repos/os/src/app/cli_monitor/main.cc | 2 +- repos/os/src/lib/vfs/fs_file_system.h | 442 ++++++++++++++------ repos/os/src/lib/vfs/terminal_file_system.h | 58 ++- repos/os/src/server/vfs/main.cc | 196 ++++++--- repos/os/src/server/vfs/node.h | 53 ++- repos/os/src/test/vfs_stress/main.cc | 5 +- repos/ports/src/noux/main.cc | 2 +- 12 files changed, 610 insertions(+), 201 deletions(-) diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index 4acf8516f7..4a109cfea7 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -304,10 +304,17 @@ struct Libc::Pthreads }; +extern void (*libc_select_notify)(); + struct Libc::Io_response_handler : Vfs::Io_response_handler { - void handle_io_response() override + void handle_io_response(Vfs::Vfs_handle::Context *) override { + /* some contexts may have been deblocked from select() */ + if (libc_select_notify) + libc_select_notify(); + + /* resume all as any context may have been deblocked from blocking I/O */ Libc::resume_all(); } }; diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index 3e3ab7ec41..b46fb6a04b 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -781,7 +781,8 @@ int Libc::Vfs_plugin::select(int nfds, FD_SET(fd, readfds); ++nready; } else { -// handle->fs().notify_read_ready(handle); + while (!handle->fs().notify_read_ready(handle)) + Libc::suspend(); } } diff --git a/repos/os/include/vfs/dir_file_system.h b/repos/os/include/vfs/dir_file_system.h index cd7377653e..b7a12379c5 100644 --- a/repos/os/include/vfs/dir_file_system.h +++ b/repos/os/include/vfs/dir_file_system.h @@ -424,9 +424,9 @@ class Vfs::Dir_file_system : public File_system } Open_result open(char const *path, - unsigned mode, - Vfs_handle **out_handle, - Allocator &alloc) override + unsigned mode, + Vfs_handle **out_handle, + Allocator &alloc) override { /* * If 'path' is a directory, we create a 'Vfs_handle' @@ -631,6 +631,14 @@ class Vfs::Dir_file_system : public File_system return handle->fs().read_ready(handle); } + + bool notify_read_ready(Vfs_handle *handle) override + { + if (&handle->fs() == this) + return true; + + return handle->fs().notify_read_ready(handle); + } }; #endif /* _INCLUDE__VFS__DIR_FILE_SYSTEM_H_ */ diff --git a/repos/os/include/vfs/file_io_service.h b/repos/os/include/vfs/file_io_service.h index 5ed8fc4958..2539b2f5a9 100644 --- a/repos/os/include/vfs/file_io_service.h +++ b/repos/os/include/vfs/file_io_service.h @@ -16,6 +16,8 @@ #ifndef _INCLUDE__VFS__FILE_IO_SERVICE_H_ #define _INCLUDE__VFS__FILE_IO_SERVICE_H_ +#include + namespace Vfs { class Vfs_handle; struct Io_response_handler; @@ -25,7 +27,7 @@ namespace Vfs { struct Vfs::Io_response_handler { - virtual void handle_io_response() = 0; + virtual void handle_io_response(Vfs::Vfs_handle::Context *context) = 0; }; @@ -81,6 +83,16 @@ struct Vfs::File_io_service */ virtual bool read_ready(Vfs_handle *) = 0; + /** + * Explicitly indicate interest in read-ready for a handle + * + * For example, the file-system-session plugin can then send READ_READY + * packets to the server. + * + * \return false if notification setup failed + */ + virtual bool notify_read_ready(Vfs_handle *) { return true; } + /*************** ** Ftruncate ** diff --git a/repos/os/include/vfs/vfs_handle.h b/repos/os/include/vfs/vfs_handle.h index e893d6a904..f13093fc02 100644 --- a/repos/os/include/vfs/vfs_handle.h +++ b/repos/os/include/vfs/vfs_handle.h @@ -14,10 +14,12 @@ #ifndef _INCLUDE__VFS__VFS_HANDLE_H_ #define _INCLUDE__VFS__VFS_HANDLE_H_ -#include #include -namespace Vfs { class Vfs_handle; } +namespace Vfs{ + class Vfs_handle; + class File_io_service; +} class Vfs::Vfs_handle @@ -32,6 +34,13 @@ class Vfs::Vfs_handle public: + /** + * Opaque handle context + */ + struct Context { }; + + Context *context = nullptr; + struct Guard { Vfs_handle *handle; diff --git a/repos/os/src/app/cli_monitor/main.cc b/repos/os/src/app/cli_monitor/main.cc index 06f796b760..6bd2375f80 100644 --- a/repos/os/src/app/cli_monitor/main.cc +++ b/repos/os/src/app/cli_monitor/main.cc @@ -151,7 +151,7 @@ struct Cli_monitor::Main struct Io_response_handler : Vfs::Io_response_handler { - void handle_io_response() { } + void handle_io_response(Vfs::Vfs_handle::Context *) override { } } io_response_handler; /* initialize virtual 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 fb015bc230..7da4a52320 100644 --- a/repos/os/src/lib/vfs/fs_file_system.h +++ b/repos/os/src/lib/vfs/fs_file_system.h @@ -1,11 +1,13 @@ /* * \brief Adapter from Genode 'File_system' session to VFS * \author Norman Feske + * \author Emery Hemingway + * \author Christian Helmuth * \date 2011-02-17 */ /* - * Copyright (C) 2012-2016 Genode Labs GmbH + * Copyright (C) 2012-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. @@ -16,8 +18,10 @@ /* Genode includes */ #include +#include #include + namespace Vfs { class Fs_file_system; } @@ -37,6 +41,7 @@ class Vfs::Fs_file_system : public File_system Genode::Env &_env; Genode::Allocator_avl _fs_packet_alloc; + Io_response_handler &_io_handler; typedef Genode::String<64> Label_string; Label_string _label; @@ -46,126 +51,244 @@ class Vfs::Fs_file_system : public File_system ::File_system::Connection _fs; - class Fs_vfs_handle : public Vfs_handle + struct Fs_vfs_handle; + + struct Handle_space : Genode::Id_space { - private: + struct Id : Genode::Id_space::Id + { + Id(unsigned long v) { value = v; } + Id(::File_system::Node_handle h) { value = h.value; } + }; + }; - ::File_system::File_handle const _handle; + Handle_space _handle_space; - public: + struct Handle_state + { + enum class Read_ready_state { IDLE, PENDING, READY }; + Read_ready_state read_ready_state = Read_ready_state::IDLE; - Fs_vfs_handle(File_system &fs, Allocator &alloc, - int status_flags, ::File_system::File_handle handle) - : Vfs_handle(fs, fs, alloc, status_flags), _handle(handle) - { } + enum class Queued_state { IDLE, QUEUED, ACK }; + Queued_state queued_read_state = Queued_state::IDLE; + Queued_state queued_write_state = Queued_state::IDLE; - ::File_system::File_handle file_handle() const { return _handle; } + ::File_system::Packet_descriptor queued_read_packet; + ::File_system::Packet_descriptor queued_write_packet; + }; + + struct Fs_vfs_handle : Vfs_handle, Handle_space::Element, Handle_state + { + Handle_space::Id const id; + + Fs_vfs_handle(File_system &fs, Allocator &alloc, int status_flags, + Handle_space &space, Handle_space::Id id) + : + Vfs_handle(fs, fs, alloc, status_flags), + Handle_space::Element(*this, space, id), + id(id) + { } + + ::File_system::File_handle file_handle() const { return id.value; } }; /** * Helper for managing the lifetime of temporary open node handles */ - struct Fs_handle_guard + struct Fs_handle_guard : Fs_vfs_handle { - ::File_system::Session &_fs; - ::File_system::Node_handle _handle; + ::File_system::Session &_fs_session; + ::File_system::Node_handle _fs_handle; - Fs_handle_guard(::File_system::Session &fs, - ::File_system::Node_handle handle) - : _fs(fs), _handle(handle) { } + Fs_handle_guard(File_system &fs, + ::File_system::Session &fs_session, + ::File_system::Node_handle fs_handle, + Handle_space &space) + : + Fs_vfs_handle(fs, *(Allocator*)nullptr, 0, space, Handle_space::Id(fs_handle)), + _fs_session(fs_session), _fs_handle(fs_handle) + { } - ~Fs_handle_guard() { _fs.close(_handle); } + ~Fs_handle_guard() { _fs_session.close(_fs_handle); } }; - file_size _read(::File_system::Node_handle node_handle, void *buf, + struct Post_signal_hook : Genode::Entrypoint::Post_signal_hook + { + Genode::Entrypoint &_ep; + Io_response_handler &_io_handler; + Vfs_handle::Context *_context = nullptr; + + Post_signal_hook(Genode::Entrypoint &ep, + Io_response_handler &io_handler) + : _ep(ep), _io_handler(io_handler) { } + + void arm(Vfs_handle::Context *context) + { + _context = context; + _ep.schedule_post_signal_hook(this); + } + + void function() 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(_context); + _context = nullptr; + } + }; + + Post_signal_hook _post_signal_hook { _env.ep(), _io_handler }; + + file_size _read(Fs_vfs_handle &handle, void *buf, file_size const count, file_size const seek_offset) { ::File_system::Session::Tx::Source &source = *_fs.tx(); + using ::File_system::Packet_descriptor; file_size const max_packet_size = source.bulk_buffer_size() / 2; file_size const clipped_count = min(max_packet_size, count); - ::File_system::Packet_descriptor const - packet_in(source.alloc_packet(clipped_count), - node_handle, - ::File_system::Packet_descriptor::READ, - clipped_count, - seek_offset); + /* XXX check if alloc_packet() and submit_packet() will succeed! */ + + Packet_descriptor const packet_in(source.alloc_packet(clipped_count), + handle.file_handle(), + Packet_descriptor::READ, + clipped_count, + seek_offset); /* pass packet to server side */ source.submit_packet(packet_in); + /* wait until packet was acknowledged */ + handle.queued_read_state = Handle_state::Queued_state::QUEUED; + do { + _env.ep().wait_and_dispatch_one_signal(); + } while (handle.queued_read_state != Handle_state::Queued_state::ACK); + /* obtain result packet descriptor with updated status info */ - ::File_system::Packet_descriptor const - packet_out = source.get_acked_packet(); + Packet_descriptor const packet_out = handle.queued_read_packet; + + handle.queued_read_state = Handle_state::Queued_state::IDLE; + handle.queued_read_packet = Packet_descriptor(); + + if (!packet_out.succeeded()) { + /* could be EOF or a real error */ + ::File_system::Status status = _fs.status(handle.file_handle()); + if (seek_offset < status.size) + Genode::warning("unexpected failure on file-system read"); + } file_size const read_num_bytes = min(packet_out.length(), count); memcpy(buf, source.packet_content(packet_out), read_num_bytes); - /* - * XXX check if acked packet belongs to request, - * needed for thread safety - */ - source.release_packet(packet_out); return read_num_bytes; } - file_size _write(::File_system::Node_handle node_handle, + file_size _write(Fs_vfs_handle &handle, const char *buf, file_size count, file_size seek_offset) { ::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); - ::File_system::Packet_descriptor - packet(source.alloc_packet(count), - node_handle, - ::File_system::Packet_descriptor::WRITE, - count, - seek_offset); + /* XXX check if alloc_packet() and submit_packet() will succeed! */ - memcpy(source.packet_content(packet), buf, count); + Packet_descriptor packet_in(source.alloc_packet(count), + handle.file_handle(), + Packet_descriptor::WRITE, + count, + seek_offset); + + memcpy(source.packet_content(packet_in), buf, count); /* pass packet to server side */ - source.submit_packet(packet); + source.submit_packet(packet_in); + + /* wait until packet was acknowledged */ + handle.queued_write_state = Handle_state::Queued_state::QUEUED; + do { + _env.ep().wait_and_dispatch_one_signal(); + } while (handle.queued_write_state != Handle_state::Queued_state::ACK); /* obtain result packet descriptor with updated status info */ - ::File_system::Packet_descriptor const - packet_out = source.get_acked_packet(); + Packet_descriptor const packet_out = handle.queued_write_packet; - /* - * XXX check if acked packet belongs to request, - * needed for thread safety - */ + handle.queued_write_state = Handle_state::Queued_state::IDLE; + handle.queued_write_packet = Packet_descriptor(); file_size const write_num_bytes = min(packet_out.length(), count); - source.release_packet(packet); + source.release_packet(packet_out); return write_num_bytes; } + void _handle_ack() + { + ::File_system::Session::Tx::Source &source = *_fs.tx(); + using ::File_system::Packet_descriptor; + + while (source.ack_avail()) { + + Packet_descriptor const packet = source.get_acked_packet(); + + Handle_space::Id const id(packet.handle()); + + _handle_space.apply(id, [&] (Fs_vfs_handle &handle) + { + switch (packet.operation()) { + case Packet_descriptor::READ_READY: + handle.read_ready_state = Handle_state::Read_ready_state::READY; + break; + + case Packet_descriptor::READ: + handle.queued_read_packet = packet; + handle.queued_read_state = Handle_state::Queued_state::ACK; + break; + + case Packet_descriptor::WRITE: + handle.queued_write_packet = packet; + handle.queued_write_state = Handle_state::Queued_state::ACK; + break; + } + + _post_signal_hook.arm(handle.context); + }); + } + } + + Genode::Signal_handler _ack_handler { + _env.ep(), *this, &Fs_file_system::_handle_ack }; + public: - Fs_file_system(Genode::Env &env, - Genode::Allocator &alloc, - Genode::Xml_node config, - Io_response_handler &) + Fs_file_system(Genode::Env &env, + Genode::Allocator &alloc, + Genode::Xml_node config, + Io_response_handler &io_handler) : _env(env), _fs_packet_alloc(&alloc), + _io_handler(io_handler), _label(config.attribute_value("label", Label_string())), _root( config.attribute_value("root", Root_string())), _fs(env, _fs_packet_alloc, _label.string(), _root.string(), config.attribute_value("writeable", true), ::File_system::DEFAULT_TX_BUF_SIZE) - { } - + { + _fs.sigh_ack_avail(_ack_handler); + } /********************************* ** Directory-service interface ** @@ -187,12 +310,12 @@ 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(_fs, dir); + Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space); ::File_system::File_handle file = _fs.file(dir, file_name.base() + 1, ::File_system::READ_ONLY, false); - Fs_handle_guard file_guard(_fs, file); + Fs_handle_guard file_guard(*this, _fs, file, _handle_space); ::File_system::Status status = _fs.status(file); @@ -210,25 +333,7 @@ class Vfs::Fs_file_system : public File_system file_size const count = min(max_packet_size, status.size - seek_offset); - ::File_system::Packet_descriptor - packet(source.alloc_packet(count), - file, - ::File_system::Packet_descriptor::READ, - count, - seek_offset); - - /* pass packet to server side */ - source.submit_packet(packet); - source.get_acked_packet(); - - memcpy(local_addr + seek_offset, source.packet_content(packet), count); - - /* - * XXX check if acked packet belongs to request, - * needed for thread safety - */ - - source.release_packet(packet); + _read(file_guard, local_addr + seek_offset, count, seek_offset); } _env.rm().detach(local_addr); @@ -253,7 +358,7 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Node_handle node = _fs.node(path); - Fs_handle_guard node_guard(_fs, node); + Fs_handle_guard node_guard(*this, _fs, node, _handle_space); status = _fs.status(node); } catch (::File_system::Lookup_failed) { return STAT_ERR_NO_ENTRY; } @@ -281,7 +386,7 @@ class Vfs::Fs_file_system : public File_system { Lock::Guard guard(_lock); - ::File_system::Session::Tx::Source &source = *_fs.tx(); + using ::File_system::Directory_entry; if (strcmp(path, "") == 0) path = "/"; @@ -291,31 +396,13 @@ class Vfs::Fs_file_system : public File_system catch (::File_system::Lookup_failed) { return DIRENT_ERR_INVALID_PATH; } catch (::File_system::Name_too_long) { return DIRENT_ERR_INVALID_PATH; } catch (...) { return DIRENT_ERR_NO_PERM; } - Fs_handle_guard dir_guard(_fs, dir_handle); - enum { DIRENT_SIZE = sizeof(::File_system::Directory_entry) }; + Fs_handle_guard dir_guard(*this, _fs, dir_handle, _handle_space); + Directory_entry entry; - ::File_system::Packet_descriptor - packet(source.alloc_packet(DIRENT_SIZE), - dir_handle, - ::File_system::Packet_descriptor::READ, - DIRENT_SIZE, - index*DIRENT_SIZE); + enum { DIRENT_SIZE = sizeof(Directory_entry) }; - /* pass packet to server side */ - source.submit_packet(packet); - source.get_acked_packet(); - - /* - * XXX check if acked packet belongs to request, - * needed for thread safety - */ - - typedef ::File_system::Directory_entry Directory_entry; - - /* copy-out payload into destination buffer */ - Directory_entry const *entry = - (Directory_entry *)source.packet_content(packet); + _read(dir_guard, &entry, DIRENT_SIZE, index*DIRENT_SIZE); /* * The default value has no meaning because the switch below @@ -324,17 +411,16 @@ class Vfs::Fs_file_system : public File_system */ Dirent_type type = DIRENT_TYPE_END; - switch (entry->type) { + /* copy-out payload into destination buffer */ + switch (entry.type) { case Directory_entry::TYPE_DIRECTORY: type = DIRENT_TYPE_DIRECTORY; break; case Directory_entry::TYPE_FILE: type = DIRENT_TYPE_FILE; break; case Directory_entry::TYPE_SYMLINK: type = DIRENT_TYPE_SYMLINK; break; } - out.fileno = entry->inode; + out.fileno = entry.inode; out.type = type; - strncpy(out.name, entry->name, sizeof(out.name)); - - source.release_packet(packet); + strncpy(out.name, entry.name, sizeof(out.name)); return DIRENT_OK; } @@ -349,7 +435,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(_fs, dir); + Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space); _fs.unlink(dir, file_name.base() + 1); } @@ -376,13 +462,13 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false); - Fs_handle_guard from_dir_guard(_fs, dir_handle); + Fs_handle_guard from_dir_guard(*this, _fs, dir_handle, _handle_space); ::File_system::Symlink_handle symlink_handle = _fs.symlink(dir_handle, symlink_name.base() + 1, false); - Fs_handle_guard symlink_guard(_fs, symlink_handle); + Fs_handle_guard symlink_guard(*this, _fs, symlink_handle, _handle_space); - out_len = _read(symlink_handle, buf, buf_size, 0); + out_len = _read(symlink_guard, buf, buf_size, 0); return READLINK_OK; } @@ -410,9 +496,9 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Dir_handle from_dir = _fs.dir(from_dir_path.base(), false); - Fs_handle_guard from_dir_guard(_fs, from_dir); + Fs_handle_guard from_dir_guard(*this, _fs, from_dir, _handle_space); ::File_system::Dir_handle to_dir = _fs.dir(to_dir_path.base(), false); - Fs_handle_guard to_dir_guard(_fs, to_dir); + Fs_handle_guard to_dir_guard(*this, _fs, to_dir, _handle_space); _fs.move(from_dir, from_file_name.base() + 1, to_dir, to_file_name.base() + 1); @@ -462,13 +548,13 @@ class Vfs::Fs_file_system : public File_system try { ::File_system::Dir_handle dir_handle = _fs.dir(abs_path.base(), false); - Fs_handle_guard from_dir_guard(_fs, dir_handle); + Fs_handle_guard from_dir_guard(*this, _fs, dir_handle, _handle_space); ::File_system::Symlink_handle symlink_handle = _fs.symlink(dir_handle, symlink_name.base() + 1, true); - Fs_handle_guard symlink_guard(_fs, symlink_handle); + Fs_handle_guard symlink_guard(*this, _fs, symlink_handle, _handle_space); - _write(symlink_handle, from, strlen(from) + 1, 0); + _write(symlink_guard, from, strlen(from) + 1, 0); } catch (::File_system::Invalid_handle) { return SYMLINK_ERR_NO_ENTRY; } catch (::File_system::Node_already_exists) { return SYMLINK_ERR_EXISTS; } @@ -488,7 +574,7 @@ class Vfs::Fs_file_system : public File_system ::File_system::Node_handle node; try { node = _fs.node(path); } catch (...) { return 0; } - Fs_handle_guard node_guard(_fs, node); + Fs_handle_guard node_guard(*this, _fs, node, _handle_space); ::File_system::Status status = _fs.status(node); @@ -499,7 +585,7 @@ class Vfs::Fs_file_system : public File_system { try { ::File_system::Node_handle node = _fs.node(path); - Fs_handle_guard node_guard(_fs, node); + Fs_handle_guard node_guard(*this, _fs, node, _handle_space); ::File_system::Status status = _fs.status(node); @@ -543,12 +629,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(_fs, dir); + Fs_handle_guard dir_guard(*this, _fs, dir, _handle_space); ::File_system::File_handle file = _fs.file(dir, file_name.base() + 1, mode, create); - *out_handle = new (alloc) Fs_vfs_handle(*this, alloc, vfs_mode, file); + Handle_space::Id id { file }; + *out_handle = new (alloc) + Fs_vfs_handle(*this, alloc, vfs_mode, _handle_space, id); } catch (::File_system::Lookup_failed) { return OPEN_ERR_UNACCESSIBLE; } catch (::File_system::Permission_denied) { return OPEN_ERR_NO_PERM; } @@ -603,9 +691,9 @@ class Vfs::Fs_file_system : public File_system { Lock::Guard guard(_lock); - Fs_vfs_handle const *handle = static_cast(vfs_handle); + Fs_vfs_handle &handle = static_cast(*vfs_handle); - out_count = _write(handle->file_handle(), buf, buf_size, handle->seek()); + out_count = _write(handle, buf, buf_size, handle.seek()); return WRITE_OK; } @@ -615,22 +703,120 @@ class Vfs::Fs_file_system : public File_system { Lock::Guard guard(_lock); - Fs_vfs_handle const *handle = static_cast(vfs_handle); + Fs_vfs_handle &handle = static_cast(*vfs_handle); - ::File_system::Status status = _fs.status(handle->file_handle()); - file_size const size_of_file = status.size; + /* reset the ready_ready state */ + handle.read_ready_state = Handle_state::Read_ready_state::IDLE; - file_size const file_bytes_left = size_of_file >= handle->seek() - ? size_of_file - handle->seek() : 0; - - count = min(count, file_bytes_left); - - out_count = _read(handle->file_handle(), dst, count, handle->seek()); + out_count = _read(handle, dst, count, handle.seek()); return READ_OK; } - bool read_ready(Vfs_handle *) override { return true; } + bool queue_read(Vfs_handle *vfs_handle, char *dst, file_size count, + Read_result &out_result, file_size &out_count) override + { + Lock::Guard guard(_lock); + + Fs_vfs_handle *handle = static_cast(vfs_handle); + + if (handle->queued_read_state != Handle_state::Queued_state::IDLE) + return false; + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + /* if not ready to submit suggest retry */ + 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); + + ::File_system::Packet_descriptor p; + try { + p = source.alloc_packet(clipped_count); + } catch (::File_system::Session::Tx::Source::Packet_alloc_failed) { + return false; + } + + ::File_system::Packet_descriptor const + packet(p, handle->file_handle(), + ::File_system::Packet_descriptor::READ, + clipped_count, handle->seek()); + + /* pass packet to server side */ + source.submit_packet(packet); + + handle->read_ready_state = Handle_state::Read_ready_state::IDLE; + handle->queued_read_state = Handle_state::Queued_state::QUEUED; + + out_result = READ_QUEUED; + + return true; + } + + Read_result complete_read(Vfs_handle *vfs_handle, char *dst, file_size count, + file_size &out_count) override + { + Lock::Guard guard(_lock); + + Fs_vfs_handle *handle = static_cast(vfs_handle); + + if (handle->queued_read_state != Handle_state::Queued_state::ACK) + return READ_QUEUED; + + /* obtain result packet descriptor with updated status info */ + ::File_system::Packet_descriptor const + packet = handle->queued_read_packet; + + file_size const read_num_bytes = min(packet.length(), count); + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + memcpy(dst, source.packet_content(packet), read_num_bytes); + + source.release_packet(packet); + handle->queued_read_state = Handle_state::Queued_state::IDLE; + handle->queued_read_packet = ::File_system::Packet_descriptor(); + + out_count = read_num_bytes; + + return READ_OK; + } + + bool read_ready(Vfs_handle *vfs_handle) override + { + Fs_vfs_handle *handle = static_cast(vfs_handle); + + return handle->read_ready_state == Handle_state::Read_ready_state::READY; + } + + bool notify_read_ready(Vfs_handle *vfs_handle) override + { + Fs_vfs_handle *handle = static_cast(vfs_handle); + if (handle->read_ready_state != Handle_state::Read_ready_state::IDLE) + return true; + + ::File_system::Session::Tx::Source &source = *_fs.tx(); + + /* if not ready to submit suggest retry */ + if (!source.ready_to_submit()) return false; + + using ::File_system::Packet_descriptor; + + Packet_descriptor packet(Packet_descriptor(), + handle->file_handle(), + Packet_descriptor::READ_READY, + 0, 0); + + source.submit_packet(packet); + handle->read_ready_state = Handle_state::Read_ready_state::PENDING; + + /* + * When the packet is acknowledged the application is notified via + * Io_response_handler::handle_io_response(). + */ + return true; + } Ftruncate_result ftruncate(Vfs_handle *vfs_handle, file_size len) override { diff --git a/repos/os/src/lib/vfs/terminal_file_system.h b/repos/os/src/lib/vfs/terminal_file_system.h index 5af22d44f8..eab71592d2 100644 --- a/repos/os/src/lib/vfs/terminal_file_system.h +++ b/repos/os/src/lib/vfs/terminal_file_system.h @@ -19,6 +19,7 @@ #include #include #include +#include namespace Vfs { class Terminal_file_system; } @@ -36,10 +37,52 @@ class Vfs::Terminal_file_system : public Single_file_system 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 + { + Genode::Entrypoint &_ep; + Io_response_handler &_io_handler; + Vfs_handle::Context *_context = nullptr; + + Post_signal_hook(Genode::Entrypoint &ep, + Io_response_handler &io_handler) + : _ep(ep), _io_handler(io_handler) { } + + void arm(Vfs_handle::Context *context) + { + _context = context; + _ep.schedule_post_signal_hook(this); + } + + void function() 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(_context); + _context = nullptr; + } + }; + + Post_signal_hook _post_signal_hook { _env.ep(), _io_handler }; + + Handle_registry _handle_registry; + Genode::Signal_handler _read_avail_handler { _env.ep(), *this, &Terminal_file_system::_handle_read_avail }; - void _handle_read_avail() { _io_handler.handle_io_response(); } + void _handle_read_avail() + { + _handle_registry.for_each([this] (Registered_handle &h) { + _post_signal_hook.arm(h.context); + }); + } Read_result _read(Vfs_handle *vfs_handle, char *dst, file_size count, file_size &out_count) @@ -70,6 +113,19 @@ 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 mode, + Vfs_handle **out_handle, + Allocator &alloc) override + { + if (!_single_file(path)) + return OPEN_ERR_UNACCESSIBLE; + + *out_handle = new (alloc) + Registered_handle(_handle_registry, *this, *this, alloc, 0); + + return OPEN_OK; + } + /******************************** ** File I/O service interface ** ********************************/ diff --git a/repos/os/src/server/vfs/main.cc b/repos/os/src/server/vfs/main.cc index e4c88cb66a..c02b6c5863 100644 --- a/repos/os/src/server/vfs/main.cc +++ b/repos/os/src/server/vfs/main.cc @@ -13,36 +13,37 @@ */ /* Genode includes */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Local includes */ #include "assert.h" #include "node.h" -namespace Vfs_server { +namespace Vfs_server { using namespace File_system; using namespace Vfs; - class Session_component; - class Root; - class Io_response_handler; + class Session_component; + class Root; + class Io_response_handler; + typedef Genode::Registered Registered_session; + typedef Genode::Registry Session_registry; }; -class Vfs_server::Session_component : - public File_system::Session_rpc_object +class Vfs_server::Session_component : public File_system::Session_rpc_object, + public File_io_handler { private: @@ -53,7 +54,7 @@ class Vfs_server::Session_component : Genode::Signal_handler _process_packet_handler; - Vfs::Dir_file_system &_vfs; + Vfs::Dir_file_system &_vfs; /* * The root node needs be allocated with the session struct @@ -61,8 +62,13 @@ class Vfs_server::Session_component : */ Genode::Constructible _root; - bool _writable; + bool _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 ** @@ -106,8 +112,14 @@ class Vfs_server::Session_component : ** Packet-stream processing ** ******************************/ + struct Not_read_ready { }; + struct Dont_ack { }; + /** * Perform packet operation + * + * \throw Not_read_ready + * \throw Dont_ack */ void _process_packet_op(Packet_descriptor &packet) { @@ -118,49 +130,100 @@ class Vfs_server::Session_component : /* assume failure by default */ packet.succeeded(false); - if ((!(content && length)) || (packet.length() > packet.size())) { + if ((packet.length() > packet.size())) return; - } /* resulting length */ size_t res_length = 0; switch (packet.operation()) { - case Packet_descriptor::READ: try { - _apply(packet.handle(), [&] (Node &node) { - if (node.mode&READ_ONLY) - res_length = node.read(_vfs, (char *)content, length, seek); - }); } catch (...) { } + case Packet_descriptor::READ: + try { + _apply(packet.handle(), [&] (Node &node) { + if (!node.read_ready()) + throw Not_read_ready(); + if (node.mode&READ_ONLY) + res_length = node.read(_vfs, (char *)content, length, seek); + }); + } catch (Not_read_ready) { throw; + } catch (...) { } break; - case Packet_descriptor::WRITE: try { - _apply(packet.handle(), [&] (Node &node) { - if (node.mode&WRITE_ONLY) - res_length = node.write(_vfs, (char const *)content, length, seek); - }); } catch (...) { } + case Packet_descriptor::WRITE: + try { + _apply(packet.handle(), [&] (Node &node) { + if (node.mode&WRITE_ONLY) + res_length = node.write(_vfs, (char const *)content, length, seek); + }); + } catch (...) { } break; case Packet_descriptor::READ_READY: - /* not supported */ - break; + try { + _apply(static_cast(packet.handle().value), [] (File &node) { + node.notify_read_ready = true; + }); + } catch (...) { } + + throw Dont_ack(); } packet.length(res_length); packet.succeeded(!!res_length); } - void _process_packet() + bool _try_process_packet_op(Packet_descriptor &packet) { - Packet_descriptor packet = tx_sink()->get_packet(); + try { + _process_packet_op(packet); + return true; + } catch (Not_read_ready) { + _backlog_packet = packet; + } + return false; + } + + bool _process_backlog() + { + /* indicate success if there's no backlog */ + if (!_backlog_packet.size()) + return true; + + /* 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'. */ - _process_packet_op(packet); tx_sink()->acknowledge_packet(packet); + + return true; } /** @@ -169,6 +232,16 @@ class Vfs_server::Session_component : */ void _process_packets() { + /* + * 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; + while (tx_sink()->packet_avail()) { /* @@ -186,7 +259,10 @@ class Vfs_server::Session_component : if (!tx_sink()->ready_to_ack()) return; - _process_packet(); + try { + if (!_process_packet()) + return; + } catch (Dont_ack) { } } } @@ -243,7 +319,6 @@ class Vfs_server::Session_component : _alloc(_ram, env.rm()), _process_packet_handler(env.ep(), *this, &Session_component::_process_packets), _vfs(vfs), - _root(), _writable(writable) { /* @@ -275,6 +350,19 @@ class Vfs_server::Session_component : _ram.upgrade(new_quota); } + /* File_io_handler interface */ + void handle_file_io(File &file) override + { + if (file.notify_read_ready && tx_sink()->ready_to_ack()) { + Packet_descriptor packet(Packet_descriptor(), + Node_handle(file.id().value), + Packet_descriptor::READ_READY, + 0, 0); + tx_sink()->acknowledge_packet(packet); + file.notify_read_ready = false; + } + _process_packets(); + } /*************************** ** File_system interface ** @@ -320,7 +408,7 @@ class Vfs_server::Session_component : _assert_valid_name(name_str); new_handle = dir.file( - _node_space, _vfs, _alloc, name_str, fs_mode, create).value; + _node_space, _vfs, _alloc, *this, name_str, fs_mode, create).value; }); return new_handle; } @@ -425,9 +513,11 @@ class Vfs_server::Session_component : }); } - void truncate(File_handle file_handle, file_size_t size) override { + void truncate(File_handle file_handle, file_size_t size) override + { _apply(file_handle, [&] (File &file) { - file.truncate(size); }); } + file.truncate(size); }); + } void move(Dir_handle from_dir_handle, Name const &from_name, Dir_handle to_dir_handle, Name const &to_name) override @@ -472,9 +562,15 @@ class Vfs_server::Session_component : struct Vfs_server::Io_response_handler : Vfs::Io_response_handler { - void handle_io_response() override + Session_registry &_session_registry; + + Io_response_handler(Session_registry &session_registry) + : _session_registry(session_registry) { } + + void handle_io_response(Vfs::Vfs_handle::Context *context) override { - Genode::log(__func__, " called"); + if (Vfs_server::Node *node = static_cast(context)) + node->handle_io_response(); } }; @@ -499,7 +595,9 @@ class Vfs_server::Root : } } - Io_response_handler _io_response_handler; + Session_registry _session_registry; + + Io_response_handler _io_response_handler { _session_registry }; Vfs::Dir_file_system _vfs { _env, _heap, vfs_config(), _io_response_handler, @@ -593,14 +691,10 @@ class Vfs_server::Root : throw Root::Unavailable(); } - Session_component *session = new(md_alloc()) - Session_component(_env, - label.string(), - ram_quota, - tx_buf_size, - _vfs, - session_root.base(), - writeable); + Session_component *session = new (md_alloc()) + Registered_session(_session_registry, _env, label.string(), + ram_quota, tx_buf_size, _vfs, + session_root.base(), writeable); Genode::log("session opened for '", label, "' at '", session_root, "'"); return session; diff --git a/repos/os/src/server/vfs/node.h b/repos/os/src/server/vfs/node.h index d64fad685f..6e5974770b 100644 --- a/repos/os/src/server/vfs/node.h +++ b/repos/os/src/server/vfs/node.h @@ -1,11 +1,12 @@ /* * \brief Internal nodes of VFS server * \author Emery Hemingway + * \author Christian Helmuth * \date 2016-03-29 */ /* - * Copyright (C) 2016 Genode Labs GmbH + * Copyright (C) 2016-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. @@ -35,6 +36,11 @@ namespace Vfs_server { typedef Genode::Id_space Node_space; + struct File_io_handler + { + virtual void handle_file_io(File &file) = 0; + }; + /* Vfs::MAX_PATH is shorter than File_system::MAX_PATH */ enum { MAX_PATH_LEN = Vfs::MAX_PATH_LEN }; @@ -70,7 +76,8 @@ namespace Vfs_server { } -struct Vfs_server::Node : File_system::Node_base, Node_space::Element +struct Vfs_server::Node : File_system::Node_base, Node_space::Element, + Vfs::Vfs_handle::Context { Path const _path; Mode const mode; @@ -87,7 +94,8 @@ struct Vfs_server::Node : File_system::Node_base, Node_space::Element virtual size_t read(Vfs::File_system&, char*, size_t, seek_off_t) { return 0; } virtual size_t write(Vfs::File_system&, char const*, size_t, seek_off_t) { return 0; } - + virtual bool read_ready() { return false; } + virtual void handle_io_response() { } }; struct Vfs_server::Symlink : Node @@ -127,6 +135,8 @@ struct Vfs_server::Symlink : Node notify_listeners(); return target.length(); } + + bool read_ready() override { return true; } }; @@ -134,24 +144,32 @@ class Vfs_server::File : public Node { private: + File_io_handler &_file_io_handler; + Vfs::Vfs_handle *_handle; char const *_leaf_path; /* offset pointer to Node::_path */ public: - File(Node_space &space, + bool notify_read_ready = false; + + File(Node_space &space, Vfs::File_system &vfs, Genode::Allocator &alloc, + File_io_handler &file_io_handler, char const *file_path, Mode fs_mode, bool create) - : Node(space, file_path, fs_mode) + : + Node(space, file_path, fs_mode), + _file_io_handler(file_io_handler) { 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()); + _leaf_path = vfs.leaf_path(path()); + _handle->context = this; } ~File() { _handle->ds().close(_handle); } @@ -167,7 +185,8 @@ class Vfs_server::File : public Node ** Node interface ** ********************/ - size_t read(Vfs::File_system&, char *dst, size_t len, seek_off_t seek_offset) + size_t read(Vfs::File_system&, char *dst, size_t len, + seek_off_t seek_offset) override { Vfs::file_size res = 0; @@ -185,7 +204,8 @@ class Vfs_server::File : public Node return res; } - size_t write(Vfs::File_system&, char const *src, size_t len, seek_off_t seek_offset) + size_t write(Vfs::File_system&, char const *src, size_t len, + seek_off_t seek_offset) override { Vfs::file_size res = 0; @@ -204,6 +224,13 @@ class Vfs_server::File : public Node mark_as_updated(); return res; } + + bool read_ready() override { return _handle->fs().read_ready(_handle); } + + void handle_io_response() override + { + _file_io_handler.handle_file_io(*this); + } }; @@ -219,6 +246,7 @@ struct Vfs_server::Directory : Node Node_space::Id file(Node_space &space, Vfs::File_system &vfs, Genode::Allocator &alloc, + File_io_handler &file_io_handler, char const *file_path, Mode mode, bool create) @@ -227,8 +255,11 @@ struct Vfs_server::Directory : Node char const *path_str = subpath.base(); File *file; - try { file = new (alloc) File(space, vfs, alloc, path_str, mode, create); } - catch (Out_of_memory) { throw Out_of_metadata(); } + try { + file = new (alloc) + File(space, vfs, alloc, file_io_handler, path_str, mode, create); + } catch (Out_of_memory) { throw Out_of_metadata(); } + if (create) mark_as_updated(); return file->id(); @@ -297,6 +328,8 @@ struct Vfs_server::Directory : Node } return len - remains; } + + bool read_ready() override { return true; } }; #endif /* _VFS__NODE_H_ */ diff --git a/repos/os/src/test/vfs_stress/main.cc b/repos/os/src/test/vfs_stress/main.cc index 2159db5a9c..adc5e821ca 100644 --- a/repos/os/src/test/vfs_stress/main.cc +++ b/repos/os/src/test/vfs_stress/main.cc @@ -474,7 +474,10 @@ void Component::construct(Genode::Env &env) struct Io_response_handler : Vfs::Io_response_handler { - void handle_io_response() override { Genode::log(__func__, " called"); } + void handle_io_response(Vfs::Vfs_handle::Context *) override + { + Genode::log(__func__, " called"); + } } io_response_handler; Vfs::Dir_file_system vfs_root(env, heap, config_xml.sub_node("vfs"), diff --git a/repos/ports/src/noux/main.cc b/repos/ports/src/noux/main.cc index f8f557aff8..1aa6f317ac 100644 --- a/repos/ports/src/noux/main.cc +++ b/repos/ports/src/noux/main.cc @@ -214,7 +214,7 @@ struct Noux::Main struct Io_response_handler : Vfs::Io_response_handler { - void handle_io_response() override { Genode::log(__func__, " called"); } + void handle_io_response(Vfs::Vfs_handle::Context *) override { } } _io_response_handler; Vfs::Dir_file_system _root_dir { _env, _heap, _config.xml().sub_node("fstab"),