genode/repos/ports/src/noux/rom_session_component.h
Emery Hemingway a635873568 VFS: Replace global response handlers with local handlers
Replace the I/O response handler that is passed to the VFS at
construction with an object that is dynamically attached to handles.
This object shall also accept read-ready notifications, and plugins are
encouraged to keep handles awaiting ready-ready notifications separate
from handles that await I/O progress.

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

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

Fix #3257
2019-04-09 12:30:34 +02:00

262 lines
6.4 KiB
C++

/*
* \brief ROM session implementation used by Noux processes
* \author Norman Feske
* \date 2013-07-18
*/
/*
* Copyright (C) 2013-2017 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.
*/
#ifndef _NOUX__ROM_SESSION_COMPONENT_H_
#define _NOUX__ROM_SESSION_COMPONENT_H_
/* Genode includes */
#include <rom_session/connection.h>
#include <base/rpc_server.h>
/* Noux includes */
#include "vfs_io_channel.h"
namespace Noux {
struct Vfs_dataspace;
struct Rom_dataspace_info;
class Rom_session_component;
}
/**
* Dataspace obtained from the VFS
*/
struct Noux::Vfs_dataspace
{
typedef Child_policy::Name Name;
Name const name;
Vfs::File_system &root_dir;
Vfs_io_waiter_registry &vfs_io_waiter_registry;
Genode::Ram_allocator &ram;
Genode::Region_map &rm;
Genode::Allocator &alloc;
Dataspace_capability ds { };
bool got_ds_from_vfs { true };
Vfs_dataspace(Vfs::File_system &root_dir,
Vfs_io_waiter_registry &vfs_io_waiter_registry,
Name const &name,
Genode::Ram_allocator &ram, Genode::Region_map &rm,
Genode::Allocator &alloc)
:
name(name),
root_dir(root_dir), vfs_io_waiter_registry(vfs_io_waiter_registry),
ram(ram), rm(rm), alloc(alloc)
{
ds = root_dir.dataspace(name.string());
if (!ds.valid()) {
got_ds_from_vfs = false;
Vfs::Directory_service::Stat stat_out;
if (root_dir.stat(name.string(), stat_out) != Vfs::Directory_service::STAT_OK)
return;
if (stat_out.size == 0)
return;
Vfs::Vfs_handle *file;
if (root_dir.open(name.string(),
Vfs::Directory_service::OPEN_MODE_RDONLY,
&file,
alloc) != Vfs::Directory_service::OPEN_OK)
return;
Vfs_handle_context read_context;
Vfs::Vfs_handle::Guard guard(file);
file->handler(&read_context);
ds = ram.alloc(stat_out.size);
char *addr = rm.attach(static_cap_cast<Genode::Ram_dataspace>(ds));
for (Vfs::file_size bytes_read = 0; bytes_read < stat_out.size; ) {
Registered_no_delete<Vfs_io_waiter>
vfs_io_waiter(vfs_io_waiter_registry);
while (!file->fs().queue_read(file, stat_out.size - bytes_read))
vfs_io_waiter.wait_for_io();
Vfs::File_io_service::Read_result read_result;
Vfs::file_size out_count;
for (;;) {
read_result = file->fs().complete_read(file, addr + bytes_read,
stat_out.size,
out_count);
if (read_result != Vfs::File_io_service::READ_QUEUED)
break;
read_context.vfs_io_waiter.wait_for_io();
}
/* wake up threads blocking for 'queue_*()' or 'write()' */
vfs_io_waiter_registry.for_each([] (Vfs_io_waiter &r) {
r.wakeup();
});
if (read_result != Vfs::File_io_service::READ_OK) {
Genode::error("Error reading dataspace from VFS");
rm.detach(addr);
ram.free(static_cap_cast<Genode::Ram_dataspace>(ds));
root_dir.close(file);
return;
}
bytes_read += out_count;
file->advance_seek(out_count);
}
rm.detach(addr);
}
}
~Vfs_dataspace()
{
if (got_ds_from_vfs)
root_dir.release(name.string(), ds);
else
ram.free(static_cap_cast<Genode::Ram_dataspace>(ds));
}
};
struct Noux::Rom_dataspace_info : Dataspace_info
{
Rom_dataspace_info(Dataspace_capability ds) : Dataspace_info(ds) { }
~Rom_dataspace_info() { }
Dataspace_capability fork(Ram_allocator &,
Region_map &,
Allocator &alloc,
Dataspace_registry &ds_registry,
Rpc_entrypoint &) override
{
ds_registry.insert(new (alloc) Rom_dataspace_info(ds_cap()));
return ds_cap();
}
void poke(Region_map &, addr_t, char const *, size_t) override
{
error("attempt to poke onto a ROM dataspace");
}
};
/**
* Local ROM service
*
* Depending on the ROM name, the data is provided by the VFS (if the name
* starts with a '/' or the parent's ROM service. If the name empty, an
* invalid dataspace capability is returned (this is used for the binary
* ROM session of a forked process).
*/
class Noux::Rom_session_component : public Rpc_object<Rom_session>
{
public:
typedef Child_policy::Name Name;
private:
Allocator &_alloc;
Rpc_entrypoint &_ep;
Vfs::File_system &_root_dir;
Vfs_io_waiter_registry &_vfs_io_waiter_registry;
Dataspace_registry &_ds_registry;
Constructible<Vfs_dataspace> _rom_from_vfs { };
/**
* Wrapped ROM session at core
*/
Constructible<Rom_connection> _rom_from_parent { };
Dataspace_capability _init_ds_cap(Env &env, Name const &name)
{
if (name.string()[0] == '/') {
_rom_from_vfs.construct(_root_dir, _vfs_io_waiter_registry,
name, env.ram(), env.rm(), _alloc);
return _rom_from_vfs->ds;
}
_rom_from_parent.construct(env, name.string());
Dataspace_capability ds = _rom_from_parent->dataspace();
return ds;
}
Dataspace_capability const _ds_cap;
public:
Rom_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep,
Vfs::File_system &root_dir,
Vfs_io_waiter_registry &vfs_io_waiter_registry,
Dataspace_registry &ds_registry, Name const &name)
:
_alloc(alloc), _ep(ep), _root_dir(root_dir),
_vfs_io_waiter_registry(vfs_io_waiter_registry),
_ds_registry(ds_registry), _ds_cap(_init_ds_cap(env, name))
{
_ep.manage(this);
_ds_registry.insert(new (alloc) Rom_dataspace_info(_ds_cap));
}
~Rom_session_component()
{
Rom_dataspace_info *ds_info = nullptr;
/*
* Lookup and lock ds info instead of directly accessing
* the '_ds_info' member.
*/
_ds_registry.apply(_ds_cap, [&] (Rom_dataspace_info *info) {
if (!info) {
error("~Rom_session_component: unexpected !info");
return;
}
_ds_registry.remove(info);
info->dissolve_users();
ds_info = info;
});
destroy(_alloc, ds_info);
_ep.dissolve(this);
}
/***************************
** ROM session interface **
***************************/
Rom_dataspace_capability dataspace() override
{
return static_cap_cast<Rom_dataspace>(_ds_cap);
}
void sigh(Signal_context_capability) override { }
};
#endif /* _NOUX__ROM_SESSION_COMPONENT_H_ */