libc: process VFS operations in monitor

Fixes #2635
This commit is contained in:
Christian Helmuth 2020-07-13 08:59:08 +02:00 committed by Norman Feske
parent a89d61acf2
commit 36b6ebc030
8 changed files with 932 additions and 759 deletions

View File

@ -64,7 +64,7 @@ namespace Libc {
/**
* Virtual file system
*/
void init_vfs_plugin(Suspend &);
void init_vfs_plugin(Monitor &);
void init_file_operations(Cwd &);
/**

View File

@ -282,8 +282,7 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
void _monitors_handler()
{
_execute_monitors_pending = false;
_monitors.execute_monitors();
/* used to leave I/O-signal dispatcher only - handled afterwards */
}
Constructible<Clone_connection> _clone_connection { };
@ -452,13 +451,21 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
_switch_to_user();
}
if (_dispatch_pending_io_signals) {
/* dispatch pending signals but don't block */
while (_env.ep().dispatch_pending_io_signal()) ;
if (_execute_monitors_pending) {
_execute_monitors_pending = false;
_monitors.execute_monitors();
} else {
/* block for signals */
_env.ep().wait_and_dispatch_one_io_signal();
handle_io_progress();
if (_dispatch_pending_io_signals) {
/* dispatch pending signals but don't block */
while (_env.ep().dispatch_pending_io_signal()) ;
} else {
/* block for signals */
_env.ep().wait_and_dispatch_one_io_signal();
handle_io_progress();
}
}
if (!_kernel_routine && _resume_main_once && !_setjmp(_kernel_context))
@ -562,7 +569,8 @@ struct Libc::Kernel final : Vfs::Io_response_handler,
{
if (!_execute_monitors_pending) {
_execute_monitors_pending = true;
Signal_transmitter(*_execute_monitors).submit();
if (!_main_context())
Signal_transmitter(*_execute_monitors).submit();
}
}

View File

@ -30,6 +30,8 @@ namespace Libc {
virtual void deschedule_select() = 0;
};
void select_notify_from_kernel();
}
#endif /* _LIBC__INTERNAL__SELECT_H_ */

View File

@ -81,9 +81,9 @@ class Libc::Vfs_plugin : public Plugin
bool const _pipe_configured;
/**
* Sync a handle and propagate errors
* Sync a handle
*/
int _vfs_sync(Vfs::Vfs_handle&);
void _vfs_sync(Vfs::Vfs_handle&);
/**
* Update modification time
@ -158,12 +158,11 @@ class Libc::Vfs_plugin : public Plugin
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout) override;
File_descriptor *open(const char *, int, int libc_fd);
File_descriptor *open(const char *path, int flags) override
{
return open(path, flags, ANY_FD);
}
/* kernel-specific API without monitor */
File_descriptor *open_from_kernel(const char *, int, int libc_fd);
int close_from_kernel(File_descriptor *);
::off_t lseek_from_kernel(File_descriptor *fd, ::off_t offset);
int stat_from_kernel(const char *, struct stat *);
int access(char const *, int) override;
int close(File_descriptor *) override;
@ -178,6 +177,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;
File_descriptor *open(const char *path, int flags) 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;

View File

@ -116,27 +116,137 @@ void Libc::Kernel::reset_malloc_heap()
void Libc::Kernel::_init_file_descriptors()
{
/**
* path element token
*/
struct Scanner_policy_path_element
{
static bool identifier_char(char c, unsigned /* i */)
{
return (c != '/') && (c != 0);
}
};
typedef Genode::Token<Scanner_policy_path_element> Path_element_token;
auto resolve_symlinks = [&] (Absolute_path next_iteration_working_path, Absolute_path &resolved_path)
{
char path_element[PATH_MAX];
char symlink_target[PATH_MAX];
Absolute_path current_iteration_working_path;
enum { FOLLOW_LIMIT = 10 };
int follow_count = 0;
bool symlink_resolved_in_this_iteration;
do {
if (follow_count++ == FOLLOW_LIMIT) {
errno = ELOOP;
throw Symlink_resolve_error();
}
current_iteration_working_path = next_iteration_working_path;
next_iteration_working_path.import("");
symlink_resolved_in_this_iteration = false;
Path_element_token t(current_iteration_working_path.base());
while (t) {
if (t.type() != Path_element_token::IDENT) {
t = t.next();
continue;
}
t.string(path_element, sizeof(path_element));
try {
next_iteration_working_path.append_element(path_element);
} catch (Path_base::Path_too_long) {
errno = ENAMETOOLONG;
throw Symlink_resolve_error();
}
/*
* If a symlink has been resolved in this iteration, the remaining
* path elements get added and a new iteration starts.
*/
if (!symlink_resolved_in_this_iteration) {
struct stat stat_buf;
int res = _vfs.stat_from_kernel(next_iteration_working_path.base(), &stat_buf);
if (res == -1) {
throw Symlink_resolve_error();
}
if (S_ISLNK(stat_buf.st_mode)) {
res = readlink(next_iteration_working_path.base(),
symlink_target, sizeof(symlink_target) - 1);
if (res < 1)
throw Symlink_resolve_error();
/* zero terminate target */
symlink_target[res] = 0;
if (symlink_target[0] == '/')
/* absolute target */
next_iteration_working_path.import(symlink_target, _cwd.base());
else {
/* relative target */
next_iteration_working_path.strip_last_element();
try {
next_iteration_working_path.append_element(symlink_target);
} catch (Path_base::Path_too_long) {
errno = ENAMETOOLONG;
throw Symlink_resolve_error();
}
}
symlink_resolved_in_this_iteration = true;
}
}
t = t.next();
}
} while (symlink_resolved_in_this_iteration);
resolved_path = next_iteration_working_path;
resolved_path.remove_trailing('/');
};
typedef String<Vfs::MAX_PATH_LEN> Path;
auto resolve_absolute_path = [&] (Path const &path)
{
Absolute_path abs_path { };
Absolute_path abs_dir(path.string(), _cwd.base()); abs_dir.strip_last_element();
Absolute_path dir_entry(path.string(), _cwd.base()); dir_entry.keep_only_last_element();
try {
resolve_symlinks(abs_dir, abs_path);
abs_path.append_element(dir_entry.string());
return abs_path;
} catch (Path_base::Path_too_long) { return Absolute_path(); }
};
auto init_fd = [&] (Xml_node const &node, char const *attr,
int libc_fd, unsigned flags)
{
if (!node.has_attribute(attr))
return;
typedef String<Vfs::MAX_PATH_LEN> Path;
Path const path = node.attribute_value(attr, Path());
Absolute_path const path {
resolve_absolute_path(node.attribute_value(attr, Path())) };
struct stat out_stat { };
if (stat(path.string(), &out_stat) != 0)
if (_vfs.stat_from_kernel(path.string(), &out_stat) != 0)
return;
File_descriptor *fd = _vfs.open(path.string(), flags, libc_fd);
File_descriptor *fd = _vfs.open_from_kernel(path.string(), flags, libc_fd);
if (!fd)
return;
if (fd->libc_fd != libc_fd) {
error("could not allocate fd ",libc_fd," for ",path,", "
"got fd ",fd->libc_fd);
_vfs.close(fd);
_vfs.close_from_kernel(fd);
return;
}
@ -151,14 +261,14 @@ void Libc::Kernel::_init_file_descriptors()
warning("may leak former FD path memory");
{
char *dst = (char *)_heap.alloc(path.length());
copy_cstring(dst, path.string(), path.length());
char *dst = (char *)_heap.alloc(path.max_len());
copy_cstring(dst, path.string(), path.max_len());
fd->fd_path = dst;
}
::off_t const seek = node.attribute_value("seek", 0ULL);
if (seek)
_vfs.lseek(fd, seek, SEEK_SET);
_vfs.lseek_from_kernel(fd, seek);
};
if (_vfs.root_dir_has_dirents()) {
@ -323,11 +433,16 @@ void Libc::Kernel::_clone_state_from_parent()
}
extern void (*libc_select_notify)();
extern void (*libc_select_notify_from_kernel)();
void Libc::Kernel::handle_io_progress()
{
if (_execute_monitors_pending) {
_execute_monitors_pending = false;
_monitors.execute_monitors();
}
/*
* TODO: make VFS I/O completion checks during
* kernel time to avoid flapping between stacks
@ -337,14 +452,14 @@ void Libc::Kernel::handle_io_progress()
_io_ready = false;
/* some contexts may have been deblocked from select() */
if (libc_select_notify)
libc_select_notify();
select_notify_from_kernel();
/*
* resume all as any VFS context may have
* been deblocked from blocking I/O
*/
Kernel::resume_all();
_monitors.execute_monitors();
}
}

View File

@ -35,6 +35,7 @@
#include <signal.h>
/* libc-internal includes */
#include <internal/kernel.h>
#include <internal/init.h>
#include <internal/signal.h>
#include <internal/suspend.h>
@ -207,7 +208,7 @@ static int selscan(int nfds,
/* this function gets called by plugin backends when file descripors become ready */
static void select_notify()
void Libc::select_notify_from_kernel()
{
bool resume_all = false;
fd_set tmp_readfds, tmp_writefds, tmp_exceptfds;
@ -260,40 +261,35 @@ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
Constructible<Select_cb> select_cb;
/* initialize the select notification function pointer */
if (!libc_select_notify)
libc_select_notify = select_notify;
if (readfds) in_readfds = *readfds; else FD_ZERO(&in_readfds);
if (writefds) in_writefds = *writefds; else FD_ZERO(&in_writefds);
if (exceptfds) in_exceptfds = *exceptfds; else FD_ZERO(&in_exceptfds);
{
/*
* We use the guard directly to atomically check if any descripor is
* ready, but insert into select-callback list otherwise.
*/
Select_cb_list::Guard guard(select_cb_list());
/*
* Insert callback first to avoid race after 'selscan()'
*/
int const nready = selscan(nfds,
&in_readfds, &in_writefds, &in_exceptfds,
readfds, writefds, exceptfds);
select_cb.construct(nfds, in_readfds, in_writefds, in_exceptfds);
select_cb_list().insert(&(*select_cb));
/* return if any descripor is ready */
if (nready)
return nready;
int const nready = selscan(nfds,
&in_readfds, &in_writefds, &in_exceptfds,
readfds, writefds, exceptfds);
/* return on zero-timeout */
if (tv && (tv->tv_sec) == 0 && (tv->tv_usec == 0))
return 0;
/* suspend as we don't have any immediate events */
select_cb.construct(nfds, in_readfds, in_writefds, in_exceptfds);
select_cb_list().unsynchronized_insert(&(*select_cb));
/* return if any descripor is ready */
if (nready) {
select_cb_list().remove(&(*select_cb));
return nready;
}
/* return on zero-timeout */
if (tv && (tv->tv_sec) == 0 && (tv->tv_usec == 0)) {
select_cb_list().remove(&(*select_cb));
return 0;
}
/* suspend as we don't have any immediate events */
struct Timeout
{
timeval const *_tv;
@ -352,7 +348,7 @@ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
if (signal_occurred_during_select())
return Errno(EINTR);
/* not timed out -> results have been stored in select_cb by select_notify() */
/* not timed out -> results have been stored in select_cb by select_notify_from_kernel() */
if (readfds) *readfds = select_cb->readfds;
if (writefds) *writefds = select_cb->writefds;
@ -410,10 +406,6 @@ int Libc::Select_handler_base::select(int nfds, fd_set &readfds,
{
fd_set in_readfds, in_writefds, in_exceptfds;
/* initialize the select notification function pointer */
if (!libc_select_notify)
libc_select_notify = select_notify;
in_readfds = readfds;
in_writefds = writefds;
in_exceptfds = exceptfds;
@ -424,7 +416,7 @@ int Libc::Select_handler_base::select(int nfds, fd_set &readfds,
{
/*
* We use the guard directly to atomically check is any descripor is
* We use the guard directly to atomically check if any descripor is
* ready, and insert into select-callback list otherwise.
*/
Select_cb_list::Guard guard(select_cb_list());

View File

@ -46,7 +46,7 @@
namespace Libc {
extern char const *config_socket();
bool read_ready(File_descriptor *);
bool read_ready_from_kernel(File_descriptor *);
}
@ -180,7 +180,7 @@ struct Libc::Socket_fs::Context : Plugin_context
bool _fd_read_ready(Fd type)
{
if (_fd[type].file)
return Libc::read_ready(_fd[type].file);
return Libc::read_ready_from_kernel(_fd[type].file);
else
return false;
}

File diff suppressed because it is too large Load Diff