libc: pipe plugin

A libc plugin which implements pipes using a ring buffer.

Fixes #1947
This commit is contained in:
Christian Prochaska 2016-03-06 15:21:23 +01:00 committed by Christian Helmuth
parent f81867f208
commit ea726a1f48
34 changed files with 310 additions and 129 deletions

View File

@ -235,7 +235,7 @@ set boot_modules {
freetype.lib.so
gallium.lib.so
icu.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -87,7 +87,7 @@ install_config $config
set boot_modules {
core init timer
nic_drv
ld.lib.so libc.lib.so lwip.lib.so libc_lock_pipe.lib.so
ld.lib.so libc.lib.so lwip.lib.so libc_pipe.lib.so
tcp_terminal
test-terminal_echo
}

View File

@ -1,3 +1,3 @@
TARGET = tcp_terminal
SRC_CC = main.cc
LIBS = libc libc_lwip_nic_dhcp libc_lock_pipe
LIBS = libc libc_lwip_nic_dhcp libc_pipe

View File

@ -1,6 +1,6 @@
SRC_CC = plugin.cc
LIBS += libc
vpath %.cc $(REP_DIR)/src/lib/libc_lock_pipe
vpath %.cc $(REP_DIR)/src/lib/libc_pipe
SHARED_LIB = yes

View File

@ -35,4 +35,4 @@ INC_DIR += $(REP_DIR)/include/qt5/qtbase/QtCore/private \
$(QT5_CONTRIB_DIR)/qtbase/include/QtCore/$(QT_VERSION)/QtCore \
$(QT5_CONTRIB_DIR)/qtbase/include/QtCore/$(QT_VERSION)/QtCore/private
LIBS += qt5_host_tools launchpad zlib icu libc libm alarm libc_lock_pipe pthread
LIBS += qt5_host_tools launchpad zlib icu libc libm alarm libc_pipe pthread

View File

@ -0,0 +1,41 @@
build "core init test/libc_pipe"
create_boot_directory
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="test-libc_pipe">
<resource name="RAM" quantum="4M"/>
<config>
<libc stdout="/dev/log" stderr="/dev/log">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
</config>
</start>
</config>
}
build_boot_image {
core init test-libc_pipe
ld.lib.so libc.lib.so libc_pipe.lib.so pthread.lib.so
}
append qemu_args " -nographic -m 64 "
run_genode_until "child .* exited with exit value 0.*\n" 10

View File

@ -90,7 +90,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -80,7 +80,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -67,7 +67,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -66,7 +66,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -66,7 +66,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -78,7 +78,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libcrypto.lib.so
libm.lib.so
libpng.lib.so

View File

@ -70,7 +70,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libcrypto.lib.so
libm.lib.so
libpng.lib.so

View File

@ -70,7 +70,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libcrypto.lib.so
libm.lib.so
libpng.lib.so

View File

@ -66,7 +66,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -129,7 +129,7 @@ append boot_modules {
icu.lib.so
ld.lib.so
libc.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
libm.lib.so
libpng.lib.so
jpeg.lib.so

View File

@ -1,22 +1,25 @@
/*
* \brief Pipe plugin implementation
* \author Christian Prochaska
* \date 2010-03-17
* \date 2014-07-11
*/
/*
* Copyright (C) 2010-2013 Genode Labs GmbH
* Copyright (C) 2014-2016 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.
*/
/* Genode includes */
#include <base/env.h>
#include <base/printf.h>
#include <os/ring_buffer.h>
#include <util/misc_math.h>
/* libc includes */
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
@ -30,22 +33,27 @@
extern void (*libc_select_notify)();
namespace {
namespace Libc_pipe {
using namespace Genode;
enum Type { READ_END, WRITE_END };
enum { PIPE_BUF_SIZE = 4096 };
typedef Ring_buffer<unsigned char, PIPE_BUF_SIZE+1> Pipe_buffer;
class Plugin_context : public Libc::Plugin_context
{
private:
Type _type;
char *_buffer;
Pipe_buffer *_buffer;
Libc::File_descriptor *_partner;
Genode::Lock *_lock;
Genode::Cancelable_lock::State *_lock_state;
Genode::Semaphore *_write_avail_sem;
bool _nonblock = false;
public:
@ -61,21 +69,14 @@ namespace {
~Plugin_context();
Type type() const { return _type; }
char *buffer() const { return _buffer; }
Libc::File_descriptor *partner() const { return _partner; }
Genode::Lock *lock() const { return _lock; }
Genode::Cancelable_lock::State *lock_state() { return _lock_state; }
Type type() const { return _type; }
Pipe_buffer *buffer() const { return _buffer; }
Libc::File_descriptor *partner() const { return _partner; }
Genode::Semaphore *write_avail_sem() const { return _write_avail_sem; }
bool nonblock() const { return _nonblock; }
void set_partner(Libc::File_descriptor *partner)
{
_partner = partner;
}
void set_lock_state(Genode::Cancelable_lock::State lock_state)
{
*_lock_state = lock_state;
}
void set_partner(Libc::File_descriptor *partner) { _partner = partner; }
void set_nonblock(bool nonblock) { _nonblock = nonblock; }
};
@ -88,20 +89,22 @@ namespace {
*/
Plugin();
bool supports_pipe();
bool supports_pipe() override;
bool supports_select(int nfds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
struct timeval *timeout) override;
int close(Libc::File_descriptor *pipefdo);
int fcntl(Libc::File_descriptor *pipefdo, int cmd, long arg);
int pipe(Libc::File_descriptor *pipefdo[2]);
ssize_t read(Libc::File_descriptor *pipefdo, void *buf, ::size_t count);
int close(Libc::File_descriptor *pipefdo) override;
int fcntl(Libc::File_descriptor *pipefdo, int cmd, long arg) override;
int pipe(Libc::File_descriptor *pipefdo[2]) override;
ssize_t read(Libc::File_descriptor *pipefdo, void *buf,
::size_t count) override;
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
ssize_t write(Libc::File_descriptor *pipefdo, const void *buf, ::size_t count);
fd_set *exceptfds, struct timeval *timeout) override;
ssize_t write(Libc::File_descriptor *pipefdo, const void *buf,
::size_t count) override;
};
@ -138,30 +141,15 @@ namespace {
/* allocate shared resources */
_buffer = (char*)malloc(PIPE_BUF_SIZE);
if (!_buffer) {
PERR("pipe buffer allocation failed");
}
Genode::memset(_buffer, 0, PIPE_BUF_SIZE);
_buffer = new (Genode::env()->heap()) Pipe_buffer;
_write_avail_sem = new (Genode::env()->heap()) Genode::Semaphore(PIPE_BUF_SIZE);
_lock_state = new (Genode::env()->heap())
Genode::Cancelable_lock::State(Genode::Lock::LOCKED);
if (!_lock_state) {
PERR("pipe lock_state allocation failed");
}
_lock = new (Genode::env()->heap()) Genode::Lock(*_lock_state);
if (!_lock) {
PERR("pipe lock allocation failed");
}
} else {
/* get shared resource pointers from partner */
_buffer = context(_partner)->buffer();
_lock_state = context(_partner)->lock_state();
_lock = context(_partner)->lock();
_write_avail_sem = context(_partner)->write_avail_sem();
}
}
@ -169,13 +157,15 @@ namespace {
Plugin_context::~Plugin_context()
{
if (_partner) {
/* remove the fd this context belongs to from the partner's context */
context(_partner)->set_partner(0);
} else {
/* partner fd is already destroyed -> free shared resources */
free(_buffer);
destroy(Genode::env()->heap(), _lock);
destroy(Genode::env()->heap(), _lock_state);
destroy(Genode::env()->heap(), _buffer);
destroy(Genode::env()->heap(), _write_avail_sem);
}
}
@ -207,13 +197,16 @@ namespace {
* the sets belongs to this plugin
*/
for (int libc_fd = 0; libc_fd < nfds; libc_fd++) {
if (FD_ISSET(libc_fd, readfds) || FD_ISSET(libc_fd, writefds)
|| FD_ISSET(libc_fd, exceptfds)) {
if (FD_ISSET(libc_fd, readfds) ||
FD_ISSET(libc_fd, writefds) ||
FD_ISSET(libc_fd, exceptfds)) {
Libc::File_descriptor *fdo =
Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
if (fdo && (fdo->plugin == this)) {
if (fdo && (fdo->plugin == this))
return true;
}
}
}
return false;
@ -232,26 +225,49 @@ namespace {
int Plugin::fcntl(Libc::File_descriptor *pipefdo, int cmd, long arg)
{
switch (cmd) {
case F_GETFL:
if (is_write_end(pipefdo))
return O_WRONLY;
else
return O_RDONLY;
case F_SETFD:
{
const long supported_flags = FD_CLOEXEC;
/* if unsupported flags are used, fall through with error */
if (!(arg & ~supported_flags)) {
/* close fd if exec is called - no exec support -> ignore */
if (arg & FD_CLOEXEC)
return 0;
case F_SETFD:
{
const long supported_flags = FD_CLOEXEC;
/* if unsupported flags are used, fall through with error */
if (!(arg & ~supported_flags)) {
/* close fd if exec is called - no exec support -> ignore */
if (arg & FD_CLOEXEC)
return 0;
}
}
}
default:
PERR("fcntl(): command %d arg %ld not supported - pipe",
cmd, arg);
return -1;
case F_GETFL:
if (is_write_end(pipefdo))
return O_WRONLY;
else
return O_RDONLY;
case F_SETFL:
{
constexpr long supported_flags = O_NONBLOCK;
context(pipefdo)->set_nonblock(arg & O_NONBLOCK);
if ((arg & ~supported_flags) == 0)
break;
/* unsupported flags present */
PERR("%s: command F_SETFL arg %ld not fully supported",
__PRETTY_FUNCTION__, arg);
return -1;
}
default:
PERR("%s: command %d arg %ld not supported", __PRETTY_FUNCTION__, cmd, arg);
return -1;
}
return -1;
}
@ -271,21 +287,40 @@ namespace {
{
if (!is_read_end(fdo)) {
PERR("Cannot read from write end of pipe.");
errno = EBADF;
return -1;
}
context(fdo)->set_lock_state(Genode::Lock::LOCKED);
context(fdo)->lock()->lock();
if (!context(fdo)->partner())
return 0;
if ((count > 0) && buf)
Genode::memcpy(buf, context(fdo)->buffer(),
Genode::min(count, (::size_t)PIPE_BUF_SIZE));
if (context(fdo)->nonblock() && context(fdo)->buffer()->empty()) {
errno = EAGAIN;
return -1;
}
return 0;
/* blocking mode, read at least one byte */
ssize_t num_bytes_read = 0;
do {
((unsigned char*)buf)[num_bytes_read] =
context(fdo)->buffer()->get();
num_bytes_read++;
context(fdo)->write_avail_sem()->up();
} while ((num_bytes_read < (ssize_t)count) &&
!context(fdo)->buffer()->empty());
return num_bytes_read;
}
/* no support for execptfds right now */
int Plugin::select(int nfds,
fd_set *readfds,
fd_set *writefds,
@ -303,6 +338,7 @@ namespace {
FD_ZERO(exceptfds);
for (int libc_fd = 0; libc_fd < nfds; libc_fd++) {
fdo = Libc::file_descriptor_allocator()->find_by_libc_fd(libc_fd);
/* handle only libc_fds that belong to this plugin */
@ -310,14 +346,15 @@ namespace {
continue;
if (FD_ISSET(libc_fd, &in_readfds) &&
(is_read_end(fdo)) &&
(*context(fdo)->lock_state() == Genode::Lock::UNLOCKED)) {
is_read_end(fdo) &&
!context(fdo)->buffer()->empty()) {
FD_SET(libc_fd, readfds);
nready++;
}
/* currently the write end is always ready for writing */
if (FD_ISSET(libc_fd, &in_writefds)) {
if (FD_ISSET(libc_fd, &in_writefds) &&
is_write_end(fdo) &&
(context(fdo)->buffer()->avail_capacity() > 0)) {
FD_SET(libc_fd, writefds);
nready++;
}
@ -331,33 +368,43 @@ namespace {
{
if (!is_write_end(fdo)) {
PERR("Cannot write into read end of pipe.");
errno = EBADF;
return -1;
}
/*
* Currently each 'write()' overwrites the previous
* content of the pipe buffer.
*/
if ((count > 0) && buf)
Genode::memcpy(context(fdo)->buffer(), buf,
Genode::min(count, (::size_t)PIPE_BUF_SIZE));
if (context(fdo)->nonblock() &&
(context(fdo)->buffer()->avail_capacity() == 0)) {
errno = EAGAIN;
return -1;
}
context(fdo)->set_lock_state(Genode::Lock::UNLOCKED);
context(fdo)->lock()->unlock();
::size_t num_bytes_written = 0;
while (num_bytes_written < count) {
if (context(fdo)->buffer()->avail_capacity() == 0) {
if (context(fdo)->nonblock())
return num_bytes_written;
if (libc_select_notify)
libc_select_notify();
}
context(fdo)->write_avail_sem()->down();
context(fdo)->buffer()->add(((unsigned char*)buf)[num_bytes_written]);
num_bytes_written++;
}
if (libc_select_notify)
libc_select_notify();
return 0;
return num_bytes_written;
}
} /* unnamed namespace */
void __attribute__((constructor)) init_libc_lock_pipe()
{
PDBG("init_libc_lock_pipe()\n");
static Plugin plugin;
}
void __attribute__((constructor)) init_libc_pipe()
{
static Libc_pipe::Plugin plugin;
}

View File

@ -0,0 +1,89 @@
/*
* \brief libc_pipe test
* \author Christian Prochaska
* \date 2016-04-24
*/
/*
* Copyright (C) 2016 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.
*/
/* libc includes */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
enum { BUF_SIZE = 16*1024 };
static char buf[BUF_SIZE];
static int pipefd[2];
static volatile bool reader_finished = false;
void *read_pipe(void *arg)
{
static char read_buf[BUF_SIZE];
ssize_t num_bytes_read = 0;
while (num_bytes_read < BUF_SIZE) {
ssize_t res = read(pipefd[0],
&read_buf[num_bytes_read],
BUF_SIZE - num_bytes_read);
if (res < 0) {
fprintf(stderr, "Error reading from pipe\n");
exit(1);
}
num_bytes_read += res;
}
if ((read_buf[0] != buf[0]) ||
(read_buf[BUF_SIZE - 1] != buf[BUF_SIZE - 1])) {
fprintf(stderr, "Error: data mismatch\n");
exit(1);
}
reader_finished = true;
return 0;
}
int main(int argc, char *argv[])
{
/* test values */
buf[0] = 1;
buf[BUF_SIZE - 1] = 255;
int res = pipe(pipefd);
if (res != 0) {
fprintf(stderr, "Error creating pipe\n");
exit(1);
}
pthread_t tid;
pthread_create(&tid, 0, read_pipe, 0);
ssize_t bytes_written = write(pipefd[1], buf, BUF_SIZE);
if (bytes_written != BUF_SIZE) {
fprintf(stderr, "Error writing to pipe");
exit(1);
}
/* pthread_join() is not implemented at this time */
while (!reader_finished) { }
printf("--- test finished ---\n");
return 0;
}

View File

@ -0,0 +1,3 @@
TARGET = test-libc_pipe
LIBS = libc libc_pipe pthread
SRC_CC = main.cc

View File

@ -228,7 +228,7 @@ in the form of a separate shared library. Please make sure to integrate those
shared libraries along with the dynamic linker (ld.lib.so) in your boot image.
They are 'libc.lib.so' (the libc), 'libc_log.lib.so' (for printing the LOG
output of GDB monitor to core's LOG service), 'libc_terminal.lib.so' (to
connect with GDB), and 'libc_lock_pipe.lib.so' (used for synchronizing threads
connect with GDB), and 'libc_pipe.lib.so' (used for synchronizing threads
via 'select' and 'pipe'). For using the TCP terminal, 'lwip.lib.so' (TCP/IP
stack) is needed as well.

View File

@ -107,7 +107,7 @@ append boot_modules {
libc.lib.so libm.lib.so lwip.lib.so
zlib.lib.so libpng.lib.so jpeg.lib.so libssl.lib.so libcrypto.lib.so
freetype.lib.so
libc_lock_pipe.lib.so
libc_pipe.lib.so
stdcxx.lib.so
nitpicker_plugin.tar
}

View File

@ -115,7 +115,7 @@ install_config $config
# generic modules
set boot_modules {
core init timer
ld.lib.so libc.lib.so libc_lock_pipe.lib.so libc_terminal.lib.so
ld.lib.so libc.lib.so libc_pipe.lib.so libc_terminal.lib.so
uart_drv
gdb_monitor
nitpicker pointer scout

View File

@ -88,7 +88,7 @@ install_config $config
# generic modules
set boot_modules {
core init timer
ld.lib.so libc.lib.so libc_lock_pipe.lib.so libc_terminal.lib.so
ld.lib.so libc.lib.so libc_pipe.lib.so libc_terminal.lib.so
uart_drv
gdb_monitor test-gdb_monitor
}

View File

@ -77,7 +77,7 @@ install_config $config
# generic modules
set boot_modules {
core init timer
ld.lib.so libc.lib.so libc_lock_pipe.lib.so libc_terminal.lib.so
ld.lib.so libc.lib.so libc_pipe.lib.so libc_terminal.lib.so
uart_drv
gdb_monitor test-gdb_monitor
}

View File

@ -71,7 +71,7 @@ install_config $config
# generic modules
set boot_modules {
core init timer
ld.lib.so libc.lib.so libc_log.lib.so libc_lock_pipe.lib.so libc_terminal.lib.so
ld.lib.so libc.lib.so libc_log.lib.so libc_pipe.lib.so libc_terminal.lib.so
uart_drv
gdb_monitor test-gdb_monitor_target_config
}

View File

@ -206,7 +206,7 @@ install_config $config
set boot_modules {
core init timer ld.lib.so noux terminal terminal_crosslink
libc.lib.so libm.lib.so libc_noux.lib.so ncurses.lib.so expat.lib.so
libc_lock_pipe.lib.so libc_terminal.lib.so
libc_pipe.lib.so libc_terminal.lib.so
gdb_monitor test-gdb_monitor
gdb.tar test-gdb_monitor.tar test-gdb_monitor-src.tar
}

View File

@ -140,7 +140,7 @@ exec cp ${genode_dir}/repos/os/src/app/cli_monitor/gdb_command_config bin
set boot_modules {
core init timer ld.lib.so noux terminal_mux terminal_crosslink
libc.lib.so libm.lib.so libc_noux.lib.so ncurses.lib.so expat.lib.so
libc_lock_pipe.lib.so libc_log.lib.so libc_terminal.lib.so
libc_pipe.lib.so libc_log.lib.so libc_terminal.lib.so
cli_monitor gdb_monitor terminal_log gdb.tar
gdb_command_config
gdb_target.tar

View File

@ -159,7 +159,7 @@ install_config $config
# generic modules
set boot_modules {
core init timer tcp_terminal nic_bridge
ld.lib.so noux_net libc.lib.so libm.lib.so libc_lock_pipe.lib.so
ld.lib.so noux_net libc.lib.so libm.lib.so libc_pipe.lib.so
libc_resolv.lib.so libc_noux.lib.so lwip.lib.so noux_netcat.tar
}

View File

@ -138,7 +138,7 @@ lappend_if [have_spec linux] boot_modules fb_sdl
lappend_if [have_spec x86] boot_modules rtc_drv
append boot_modules {
ld.lib.so libc.lib.so libm.lib.so pthread.lib.so libc_lock_pipe.lib.so
ld.lib.so libc.lib.so libm.lib.so pthread.lib.so libc_pipe.lib.so
libc_terminal.lib.so libiconv.lib.so stdcxx.lib.so
qemu-usb.lib.so
}

View File

@ -229,7 +229,7 @@ append boot_modules {
core init timer
part_blk ahci_drv fs_rom
ld.lib.so libc.lib.so libm.lib.so pthread.lib.so
libc_lock_pipe.lib.so libc_terminal.lib.so
libc_pipe.lib.so libc_terminal.lib.so
libiconv.lib.so stdcxx.lib.so
rump.lib.so rump_fs.lib.so rump_fs
virtualbox qemu-usb.lib.so

View File

@ -10,7 +10,7 @@ INC_DIR += $(GDB_CONTRIB_DIR)/include \
$(PRG_DIR)/gdbserver \
$(PRG_DIR)
LIBS = libc libc_terminal libc_lock_pipe \
LIBS = libc libc_terminal libc_pipe \
gdbserver_platform gdbserver_libc_support
SRC_C = event-loop.c \

View File

@ -1,6 +1,6 @@
TARGET = openvpn
LIBS += libc libc_lock_pipe libc_lwip_nic_dhcp \
LIBS += libc libc_pipe libc_lwip_nic_dhcp \
libcrypto libssl config_args server
OPENVPN_PORT_DIR := $(call select_from_ports,openvpn)

View File

@ -23,7 +23,7 @@ LIBS += virtualbox-bios virtualbox-recompiler virtualbox-runtime \
virtualbox-storage virtualbox-zlib virtualbox-liblzf \
virtualbox-hwaccl virtualbox-xml virtualbox-main
LIBS += pthread libc_terminal libc_lock_pipe libiconv
LIBS += pthread libc_terminal libc_pipe libiconv
LIBS += qemu-usb

View File

@ -8,6 +8,7 @@ tar_rom
noux
noux_net_netcat
libc_ffat
libc_pipe
libc_vfs
libc_vfs_ram
libc_vfs_fs