New VFS plugin for emulating POSIX pipes

Add a new plugin for creating pipes between pairs of VFS handles. It is
intended to replace the libc_pipe plugin, one of the last remaining libc
plugins.

In contrast to the libc_pipe plugin, this plugin defers cross-handle
notification until I/O signal handling rather than block and unblock
readers using a semaphore. This is a performance regression in the case
of multiple threads blocking on a pipe, but shall be an intermediate
mechanism pending renovations within the libc VFS and threading layers.
As a side effect, threads blocked on a pipe might not be resumed until
the main thread suspends and dispatches I/O signals.

The "test-libc_pipe" test has been adjusted to use the VFS pipe plugin
and tests both local pipes and pipes hosted remotely in the VFS server.

Merge adaptations (such as EOF handling, adjustment to VFS/libc
interface changes) by Norman Feske.

Fix #2303
This commit is contained in:
Emery Hemingway
2019-07-09 14:16:46 +02:00
committed by Christian Helmuth
parent 9b7915facb
commit c51b4b5742
15 changed files with 780 additions and 22 deletions

View File

@ -2,4 +2,6 @@ _/src/init
_/src/test-libc_pipe
_/src/libc
_/src/vfs
_/src/vfs_pipe
_/src/posix
_/src/sequence

View File

@ -2,40 +2,71 @@
<events>
<timeout meaning="failed" sec="30" />
<log meaning="succeeded">child "test-libc_pipe" exited with exit value 0</log>
<log meaning="succeeded">child "sequence" exited with exit value 0</log>
<log meaning="failed">Error: </log>
</events>
<content>
<rom label="ld.lib.so"/>
<rom label="libc.lib.so"/>
<rom label="vfs.lib.so"/>
<rom label="libm.lib.so"/>
<rom label="libc_pipe.lib.so"/>
<rom label="posix.lib.so"/>
<rom label="sequence"/>
<rom label="test-libc_pipe"/>
<rom label="vfs"/>
<rom label="vfs.lib.so"/>
<rom label="vfs_pipe.lib.so"/>
</content>
<config>
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="test-libc_pipe">
<default caps="256"/>
<start name="pipes_fs">
<binary name="vfs"/>
<provides> <service name="File_system"/> </provides>
<resource name="RAM" quantum="4M"/>
<config>
<vfs> <dir name="dev"> <log/> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
<vfs> <pipe/> </vfs>
<default-policy root="/" writeable="yes"/>
</config>
</start>
<start name="sequence">
<resource name="RAM" quantum="4M"/>
<config>
<start name="libc_pipe_local">
<binary name="test-libc_pipe"/>
<config>
<vfs>
<dir name="dev">
<dir name="pipe"> <pipe/> </dir>
<log/>
</dir>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log" pipe="/dev/pipe"/>
</config>
</start>
<start name="libc_pipe_remote">
<binary name="test-libc_pipe"/>
<config>
<vfs>
<dir name="dev">
<dir name="pipe"> <fs/> </dir>
<log/>
</dir>
</vfs>
<libc stdout="/dev/log" stderr="/dev/log" pipe="/dev/pipe"/>
</config>
</start>
</config>
</start>
</config>

View File

@ -1,11 +1,2 @@
SRC_DIR = src/test/libc_pipe
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
MIRROR_FROM_REP_DIR := include/libc-plugin \
lib/mk/libc_pipe.mk \
src/lib/libc_pipe
content: $(MIRROR_FROM_REP_DIR)
$(MIRROR_FROM_REP_DIR):
$(mirror_from_rep_dir)

View File

@ -78,6 +78,7 @@ class Libc::Vfs_plugin : public Plugin
Constructible<Genode::Directory> _root_dir { };
Vfs::Io_response_handler &_response_handler;
Update_mtime const _update_mtime;
bool const _pipe_configured;
/**
* Sync a handle and propagate errors
@ -102,6 +103,14 @@ class Libc::Vfs_plugin : public Plugin
template <typename FN>
void _with_info(File_descriptor &fd, FN const &fn);
static bool _init_pipe_configured(Xml_node config)
{
bool result = false;
config.with_sub_node("libc", [&] (Xml_node libc_node) {
result = libc_node.has_attribute("pipe"); });
return result;
}
public:
Vfs_plugin(Libc::Env &env,
@ -114,7 +123,8 @@ class Libc::Vfs_plugin : public Plugin
_alloc(alloc),
_root_fs(env.vfs()),
_response_handler(handler),
_update_mtime(update_mtime)
_update_mtime(update_mtime),
_pipe_configured(_init_pipe_configured(config))
{
if (config.has_sub_node("libc"))
_root_dir.construct(vfs_env);
@ -134,6 +144,7 @@ class Libc::Vfs_plugin : public Plugin
bool supports_access(const char *, int) override { return true; }
bool supports_mkdir(const char *, mode_t) override { return true; }
bool supports_open(const char *, int) override { return true; }
bool supports_pipe() override { return _pipe_configured; }
bool supports_poll() override { return true; }
bool supports_readlink(const char *, char *, ::size_t) override { return true; }
bool supports_rename(const char *, const char *) override { return true; }
@ -167,6 +178,7 @@ class Libc::Vfs_plugin : public Plugin
int ioctl(File_descriptor *, int , char *) override;
::off_t lseek(File_descriptor *fd, ::off_t offset, int whence) override;
int mkdir(const char *, mode_t) override;
int pipe(File_descriptor *pipefdo[2]) override;
bool poll(File_descriptor &fdo, struct pollfd &pfd) override;
ssize_t read(File_descriptor *, void *, ::size_t) override;
ssize_t readlink(const char *, char *, ::size_t) override;

View File

@ -166,6 +166,13 @@ namespace Libc {
char const *string() const { return _value.string(); }
};
char const *config_pipe() __attribute__((weak));
char const *config_pipe()
{
static Config_attr attr("pipe", "");
return attr.string();
}
char const *config_rng() __attribute__((weak));
char const *config_rng()
{
@ -1599,6 +1606,65 @@ int Libc::Vfs_plugin::munmap(void *addr, ::size_t)
}
int Libc::Vfs_plugin::pipe(Libc::File_descriptor *pipefdo[2])
{
Absolute_path base_path(Libc::config_pipe());
if (base_path == "") {
error(__func__, ": pipe fs not mounted");
return Errno(EACCES);
}
Libc::File_descriptor *meta_fd { nullptr };
{
Absolute_path new_path = base_path;
new_path.append("/new");
meta_fd = open(new_path.base(), O_RDONLY, Libc::ANY_FD);
if (!meta_fd) {
Genode::error("failed to create pipe at ", new_path);
return Errno(EACCES);
}
meta_fd->path(new_path.string());
char buf[32] { };
int const n = read(meta_fd, buf, sizeof(buf)-1);
if (n < 1) {
error("failed to read pipe at ", new_path);
close(meta_fd);
return Errno(EACCES);
}
buf[n] = '\0';
base_path.append("/");
base_path.append(buf);
}
auto open_pipe_fd = [&] (auto path_suffix, auto flags)
{
Absolute_path path = base_path;
path.append(path_suffix);
File_descriptor *fd = open(path.base(), flags, Libc::ANY_FD);
if (!fd)
error("failed to open pipe end at ", path);
else
fd->path(path.string());
return fd;
};
pipefdo[0] = open_pipe_fd("/out", O_RDONLY);
pipefdo[1] = open_pipe_fd("/in", O_WRONLY);
close(meta_fd);
if (!pipefdo[0] || !pipefdo[1])
return Errno(EACCES);
return 0;
}
bool Libc::Vfs_plugin::poll(File_descriptor &fd, struct pollfd &pfd)
{
if (fd.plugin != this) return false;

View File

@ -76,7 +76,8 @@ int main(int argc, char *argv[])
ssize_t bytes_written = write(pipefd[1], buf, BUF_SIZE);
if (bytes_written != BUF_SIZE) {
fprintf(stderr, "Error writing to pipe\n");
fprintf(stderr, "Error writing to pipe (bytes_written=%zd, BUF_SIZE=%zd)\n",
bytes_written, (size_t)BUF_SIZE);
exit(1);
}

View File

@ -1,5 +1,5 @@
TARGET = test-libc_pipe
LIBS = base posix libc_pipe
LIBS = base posix
SRC_CC = main.cc
CC_CXX_WARN_STRICT =