server/vfs: use Id_space to manage open nodes

Replacing the node lookup table with an Id_space removes the
limit on open handles per session and allows mutal associativity
between File_system handles and local VFS handles.

Fix #2221
This commit is contained in:
Emery Hemingway 2017-01-04 15:27:42 +01:00 committed by Norman Feske
parent 531e35ec42
commit ded2f7e2d4
4 changed files with 180 additions and 243 deletions

View File

@ -79,12 +79,12 @@ namespace File_system {
struct File_system::Node_handle
{
int value;
unsigned long value;
Node_handle() : value(-1) { }
Node_handle() : value(~0UL) { }
Node_handle(int v) : value(v) { }
bool valid() const { return value != -1; }
bool valid() const { return value != ~0UL; }
bool operator == (Node_handle const &other) const { return other.value == value; }
bool operator != (Node_handle const &other) const { return other.value != value; }
@ -95,21 +95,21 @@ struct File_system::Node_handle
struct File_system::File_handle : Node_handle
{
File_handle() { }
File_handle(int v) : Node_handle(v) { }
File_handle(unsigned long v) : Node_handle(v) { }
};
struct File_system::Dir_handle : Node_handle
{
Dir_handle() { }
Dir_handle(int v) : Node_handle(v) { }
Dir_handle(unsigned long v) : Node_handle(v) { }
};
struct File_system::Symlink_handle : Node_handle
{
Symlink_handle() { }
Symlink_handle(int v) : Node_handle(v) { }
Symlink_handle(unsigned long v) : Node_handle(v) { }
};

View File

@ -19,7 +19,6 @@
#include <vfs/dir_file_system.h>
#include <os/session_policy.h>
#include <vfs/file_system_factory.h>
#include <os/config.h>
#include <base/sleep.h>
#include <base/component.h>
@ -52,27 +51,23 @@ class Vfs_server::Session_component :
{
private:
/* maximum number of open nodes per session */
enum { MAX_NODE_HANDLES = 128U };
Node *_nodes[MAX_NODE_HANDLES];
/**
* Each open node handle can act as a listener to be informed about
* node changes.
*/
Listener _listeners[MAX_NODE_HANDLES];
Node_space _node_space;
Genode::String<160> _label;
Genode::Ram_connection _ram = { _label.string() };
Genode::Heap _alloc =
{ &_ram, Genode::env()->rm_session() };
Genode::Ram_connection _ram;
Genode::Heap _alloc;
Genode::Signal_handler<Session_component> _process_packet_handler;
Genode::Signal_handler<Session_component>
_process_packet_dispatcher;
Vfs::Dir_file_system &_vfs;
Directory _root;
/*
* The root node needs be allocated with the session struct
* but removeable from the id space at session destruction.
*/
Genode::Constructible<Directory> _root;
bool _writable;
@ -80,50 +75,37 @@ class Vfs_server::Session_component :
** Handle to node mapping **
****************************/
bool _in_range(int handle) const {
return ((handle >= 0) && (handle < MAX_NODE_HANDLES));
}
int _next_slot()
{
for (int i = 1; i < MAX_NODE_HANDLES; ++i)
if (_nodes[i] == nullptr)
return i;
throw Out_of_metadata();
}
/**
* Lookup node using its handle as key
*/
Node *_lookup_node(Node_handle handle) {
return _in_range(handle.value) ? _nodes[handle.value] : 0; }
/**
* Lookup typed node using its handle as key
* Apply functor to node
*
* \throw Invalid_handle
*/
template <typename HANDLE_TYPE>
typename Node_type<HANDLE_TYPE>::Type &_lookup(HANDLE_TYPE handle)
template <typename FUNC>
void _apply(Node_handle handle, FUNC const &fn)
{
if (!_in_range(handle.value))
throw Invalid_handle();
Node_space::Id id { handle.value };
typedef typename Node_type<HANDLE_TYPE>::Type Node;
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node)
throw Invalid_handle();
return *node;
try { _node_space.apply<Node>(id, fn); }
catch (Node_space::Unknown_id) { throw Invalid_handle(); }
}
bool _refer_to_same_node(Node_handle h1, Node_handle h2) const
/**
* Apply functor to typed node
*
* \throw Invalid_handle
*/
template <typename HANDLE_TYPE, typename FUNC>
void _apply(HANDLE_TYPE handle, FUNC const &fn)
{
if (!(_in_range(h1.value) && _in_range(h2.value)))
throw Invalid_handle();
Node_space::Id id { handle.value };
return _nodes[h1.value] == _nodes[h2.value];
try { _node_space.apply<Node>(id, [&] (Node &node) {
typedef typename Node_type<HANDLE_TYPE>::Type Typed_node;
Typed_node *n = dynamic_cast<Typed_node *>(&node);
if (!n)
throw Invalid_handle();
fn(*n);
}); } catch (Node_space::Unknown_id) { throw Invalid_handle(); }
}
@ -152,24 +134,20 @@ class Vfs_server::Session_component :
switch (packet.operation()) {
case Packet_descriptor::READ: {
Node *node = _lookup_node(packet.handle());
if (!(node && (node->mode&READ_ONLY)))
return;
res_length = node->read(_vfs, (char *)content, length, seek);
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 (...) { }
break;
}
case Packet_descriptor::WRITE: {
Node *node = _lookup_node(packet.handle());
if (!(node && (node->mode&WRITE_ONLY)))
return;
res_length = node->write(_vfs, (char const *)content, length, seek);
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;
}
}
packet.length(res_length);
packet.succeeded(!!res_length);
@ -179,12 +157,12 @@ class Vfs_server::Session_component :
{
Packet_descriptor packet = tx_sink()->get_packet();
_process_packet_op(packet);
/*
* 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);
}
@ -232,6 +210,18 @@ class Vfs_server::Session_component :
throw Invalid_name();
}
void _close(Node &node)
{
if (File *file = dynamic_cast<File*>(&node))
destroy(_alloc, file);
else if (Directory *dir = dynamic_cast<Directory*>(&node))
destroy(_alloc, dir);
else if (Symlink *link = dynamic_cast<Symlink*>(&node))
destroy(_alloc, link);
else
destroy(_alloc, &node);
}
public:
/**
@ -252,29 +242,23 @@ class Vfs_server::Session_component :
bool writable)
:
Session_rpc_object(env.ram().alloc(tx_buf_size), env.rm(), env.ep().rpc_ep()),
_label(label),
_process_packet_dispatcher(env.ep(), *this, &Session_component::_process_packets),
_label(label), _ram(env), _alloc(_ram, env.rm()),
_process_packet_handler(env.ep(), *this, &Session_component::_process_packets),
_vfs(vfs),
_root(vfs, root_path, false),
_root(),
_writable(writable)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
_tx.sigh_packet_avail(_process_packet_dispatcher);
_tx.sigh_ready_to_ack(_process_packet_dispatcher);
_tx.sigh_packet_avail(_process_packet_handler);
_tx.sigh_ready_to_ack(_process_packet_handler);
/*
* the '/' node is not dynamically allocated, so it is
* permanently bound to Dir_handle(0);
*/
_nodes[0] = &_root;
for (unsigned i = 1; i < MAX_NODE_HANDLES; ++i)
_nodes[i] = nullptr;
_ram.ref_account(env.ram_session_cap());
env.ram().transfer_quota(_ram.cap(), ram_quota);
_ram.ref_account(Genode::env()->ram_session_cap());
Genode::env()->ram_session()->transfer_quota(_ram.cap(), ram_quota);
_root.construct(_node_space, vfs, root_path, false);
}
/**
@ -282,8 +266,11 @@ class Vfs_server::Session_component :
*/
~Session_component()
{
Dataspace_capability ds = tx_sink()->dataspace();
env()->ram_session()->free(static_cap_cast<Genode::Ram_dataspace>(ds));
/* remove the root from _node_space via destructor */
_root.destruct();
while (_node_space.apply_any<Node>([&] (Node &node) {
_close(node); })) { }
}
void upgrade(char const *args)
@ -311,22 +298,18 @@ class Vfs_server::Session_component :
}
_assert_valid_path(path_str);
Vfs_server::Path fullpath(_root.path());
Vfs_server::Path fullpath(_root->path());
fullpath.append(path_str);
path_str = fullpath.base();
/* make sure a handle is free before allocating */
auto slot = _next_slot();
if (!create && !_vfs.directory(path_str))
throw Lookup_failed();
Directory *dir;
try { dir = new (_alloc) Directory(_vfs, path_str, create); }
try { dir = new (_alloc) Directory(_node_space, _vfs, path_str, create); }
catch (Out_of_memory) { throw Out_of_metadata(); }
_nodes[slot] = dir;
return Dir_handle(slot);
return Dir_handle(dir->id().value);
}
File_handle file(Dir_handle dir_handle, Name const &name,
@ -335,37 +318,33 @@ class Vfs_server::Session_component :
if ((create || (fs_mode & WRITE_ONLY)) && (!_writable))
throw Permission_denied();
Directory &dir = _lookup(dir_handle);
File_handle new_handle;
char const *name_str = name.string();
_assert_valid_name(name_str);
_apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
/* make sure a handle is free before allocating */
auto slot = _next_slot();
File *file = dir.file(_vfs, _alloc, name_str, fs_mode, create);
_nodes[slot] = file;
return File_handle(slot);
new_handle = dir.file(
_node_space, _vfs, _alloc, name_str, fs_mode, create).value;
});
return new_handle;
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create) override
{
if (create && !_writable) throw Permission_denied();
Directory &dir = _lookup(dir_handle);
Symlink_handle new_handle;
char const *name_str = name.string();
_assert_valid_name(name_str);
_apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
/* make sure a handle is free before allocating */
auto slot = _next_slot();
Symlink *link = dir.symlink(_vfs, _alloc, name_str,
_writable ? READ_WRITE : READ_ONLY, create);
_nodes[slot] = link;
return Symlink_handle(slot);
new_handle = dir.symlink(
_node_space, _vfs, _alloc, name_str,
_writable ? READ_WRITE : READ_ONLY, create).value;
});
return new_handle;
}
Node_handle node(File_system::Path const &path) override
@ -378,90 +357,61 @@ class Vfs_server::Session_component :
_assert_valid_path(path_str);
/* re-root the path */
Path sub_path(path_str+1, _root.path());
Path sub_path(path_str+1, _root->path());
path_str = sub_path.base();
if (!_vfs.leaf_path(path_str))
throw Lookup_failed();
auto slot = _next_slot();
Node *node;
try { node = new (_alloc) Node(path_str, STAT_ONLY); }
try { node = new (_alloc) Node(_node_space, path_str, STAT_ONLY); }
catch (Out_of_memory) { throw Out_of_metadata(); }
_nodes[slot] = node;
return Node_handle(slot);
return Node_handle(node->id().value);
}
void close(Node_handle handle) override
{
/* handle '0' cannot be freed */
if (!handle.value) {
_root.notify_listeners();
return;
}
if (!_in_range(handle.value))
return;
Node *node = _nodes[handle.value];
if (!node) { return; }
node->notify_listeners();
/*
* De-allocate handle
*/
Listener &listener = _listeners[handle.value];
if (listener.valid())
node->remove_listener(&listener);
if (File *file = dynamic_cast<File*>(node))
destroy(_alloc, file);
else if (Directory *dir = dynamic_cast<Directory*>(node))
destroy(_alloc, dir);
else if (Symlink *link = dynamic_cast<Symlink*>(node))
destroy(_alloc, link);
else
destroy(_alloc, node);
_nodes[handle.value] = 0;
listener = Listener();
_apply(handle, [&] (Node &node) {
/* root directory should not be freed */
if (!(node.id() == _root->id()))
_close(node);
});
}
Status status(Node_handle node_handle) override
{
Directory_service::Stat vfs_stat;
File_system::Status fs_stat;
Node &node = _lookup(node_handle);
_apply(node_handle, [&] (Node &node) {
Directory_service::Stat vfs_stat;
if (_vfs.stat(node.path(), vfs_stat) != Directory_service::STAT_OK)
return fs_stat;
if (_vfs.stat(node.path(), vfs_stat) != Directory_service::STAT_OK)
return;
fs_stat.inode = vfs_stat.inode;
fs_stat.inode = vfs_stat.inode;
switch (vfs_stat.mode & (
Directory_service::STAT_MODE_DIRECTORY |
Directory_service::STAT_MODE_SYMLINK |
File_system::Status::MODE_FILE)) {
switch (vfs_stat.mode & (
Directory_service::STAT_MODE_DIRECTORY |
Directory_service::STAT_MODE_SYMLINK |
File_system::Status::MODE_FILE)) {
case Directory_service::STAT_MODE_DIRECTORY:
fs_stat.mode = File_system::Status::MODE_DIRECTORY;
fs_stat.size = _vfs.num_dirent(node.path()) * sizeof(Directory_entry);
return fs_stat;
case Directory_service::STAT_MODE_DIRECTORY:
fs_stat.mode = File_system::Status::MODE_DIRECTORY;
fs_stat.size = _vfs.num_dirent(node.path()) * sizeof(Directory_entry);
return;
case Directory_service::STAT_MODE_SYMLINK:
fs_stat.mode = File_system::Status::MODE_SYMLINK;
break;
case Directory_service::STAT_MODE_SYMLINK:
fs_stat.mode = File_system::Status::MODE_SYMLINK;
break;
default: /* Directory_service::STAT_MODE_FILE */
fs_stat.mode = File_system::Status::MODE_FILE;
break;
}
default: /* Directory_service::STAT_MODE_FILE */
fs_stat.mode = File_system::Status::MODE_FILE;
break;
}
fs_stat.size = vfs_stat.size;
fs_stat.size = vfs_stat.size;
});
return fs_stat;
}
@ -469,19 +419,20 @@ class Vfs_server::Session_component :
{
if (!_writable) throw Permission_denied();
Directory &dir = _lookup(dir_handle);
_apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
char const *name_str = name.string();
_assert_valid_name(name_str);
Path path(name_str, dir.path());
Path path(name_str, dir.path());
assert_unlink(_vfs.unlink(path.base()));
dir.mark_as_updated();
assert_unlink(_vfs.unlink(path.base()));
dir.mark_as_updated();
});
}
void truncate(File_handle file_handle, file_size_t size) override {
_lookup(file_handle).truncate(size); }
_apply(file_handle, [&] (File &file) {
file.truncate(size); }); }
void move(Dir_handle from_dir_handle, Name const &from_name,
Dir_handle to_dir_handle, Name const &to_name) override
@ -495,53 +446,29 @@ class Vfs_server::Session_component :
_assert_valid_name(from_str);
_assert_valid_name( to_str);
Directory &from_dir = _lookup(from_dir_handle);
Directory &to_dir = _lookup( to_dir_handle);
_apply(from_dir_handle, [&] (Directory &from_dir) {
_apply(to_dir_handle, [&] (Directory &to_dir) {
Path from_path(from_str, from_dir.path());
Path to_path( to_str, to_dir.path());
Path from_path(from_str, from_dir.path());
Path to_path( to_str, to_dir.path());
assert_rename(_vfs.rename(from_path.base(), to_path.base()));
assert_rename(_vfs.rename(from_path.base(), to_path.base()));
from_dir.mark_as_updated();
to_dir.mark_as_updated();
from_dir.mark_as_updated();
to_dir.mark_as_updated();
});
});
}
void sigh(Node_handle handle, Signal_context_capability sigh) override
{
if (!_in_range(handle.value))
throw Invalid_handle();
Node *node = dynamic_cast<Node *>(_nodes[handle.value]);
if (!node)
throw Invalid_handle();
Listener &listener = _listeners[handle.value];
/*
* If there was already a handler registered for the node,
* remove the old handler.
*/
if (listener.valid())
node->remove_listener(&listener);
/*
* Register new handler
*/
listener = Listener(sigh);
node->add_listener(&listener);
}
void sigh(Node_handle handle, Signal_context_capability sigh) override { }
/**
* Sync the VFS and send any pending signals on the node.
*/
void sync(Node_handle handle) override
{
try {
Node &node = _lookup(handle);
_apply(handle, [&] (Node &node) {
_vfs.sync(node.path());
node.notify_listeners();
} catch (Invalid_handle) { }
});
}
void control(Node_handle, Control) override { }

View File

@ -18,6 +18,7 @@
#include <file_system/node.h>
#include <vfs/file_system.h>
#include <os/path.h>
#include <base/id_space.h>
/* Local includes */
#include "assert.h"
@ -32,6 +33,8 @@ namespace Vfs_server {
struct File;
struct Symlink;
typedef Genode::Id_space<Node> Node_space;
/* Vfs::MAX_PATH is shorter than File_system::MAX_PATH */
enum { MAX_PATH_LEN = Vfs::MAX_PATH_LEN };
@ -67,13 +70,16 @@ namespace Vfs_server {
}
struct Vfs_server::Node : File_system::Node_base
struct Vfs_server::Node : File_system::Node_base, Node_space::Element
{
Path const _path;
Mode const mode;
Node(char const *node_path, Mode node_mode)
: _path(node_path), mode(node_mode) { }
Node(Node_space &space, char const *node_path, Mode node_mode)
:
Node_space::Element(*this, space),
_path(node_path), mode(node_mode)
{ }
virtual ~Node() { }
@ -86,11 +92,12 @@ struct Vfs_server::Node : File_system::Node_base
struct Vfs_server::Symlink : Node
{
Symlink(Vfs::File_system &vfs,
Symlink(Node_space &space,
Vfs::File_system &vfs,
char const *link_path,
Mode mode,
bool create)
: Node(link_path, mode)
: Node(space, link_path, mode)
{
if (create)
assert_symlink(vfs.symlink("", link_path));
@ -132,12 +139,13 @@ class Vfs_server::File : public Node
public:
File(Vfs::File_system &vfs,
File(Node_space &space,
Vfs::File_system &vfs,
Genode::Allocator &alloc,
char const *file_path,
Mode fs_mode,
bool create)
: Node(file_path, fs_mode)
: Node(space, file_path, fs_mode)
{
unsigned vfs_mode =
(fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0);
@ -201,35 +209,37 @@ class Vfs_server::File : public Node
struct Vfs_server::Directory : Node
{
Directory(Vfs::File_system &vfs, char const *dir_path, bool create)
: Node(dir_path, READ_ONLY)
Directory(Node_space &space, Vfs::File_system &vfs, char const *dir_path, bool create)
: Node(space, dir_path, READ_ONLY)
{
if (create)
assert_mkdir(vfs.mkdir(dir_path, 0));
}
File *file(Vfs::File_system &vfs,
Genode::Allocator &alloc,
char const *file_path,
Mode mode,
bool create)
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(vfs, alloc, path_str, mode, create); }
try { file = new (alloc) File(space, vfs, alloc, path_str, mode, create); }
catch (Out_of_memory) { throw Out_of_metadata(); }
if (create)
mark_as_updated();
return file;
return file->id();
}
Symlink *symlink(Vfs::File_system &vfs,
Genode::Allocator &alloc,
char const *link_path,
Mode mode,
bool create)
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();
@ -240,11 +250,11 @@ struct Vfs_server::Directory : Node
}
Symlink *link;
try { link = new (alloc) Symlink(vfs, path_str, mode, create); }
try { link = new (alloc) Symlink(space, vfs, path_str, mode, create); }
catch (Out_of_memory) { throw Out_of_metadata(); }
if (create)
mark_as_updated();
return link;
return link->id();
}

View File

@ -1,4 +1,4 @@
TARGET = vfs
SRC_CC = main.cc
LIBS = base config vfs
LIBS = base vfs
INC_DIR += $(PRG_DIR)