From 62b3f24d1cc216c5136c532cdb9f98d57549ab8e Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Fri, 1 Mar 2024 11:04:42 +0100 Subject: [PATCH] libc: select() based on poll() Fixes #5131 --- repos/libports/include/libc/select.h | 11 +- repos/libports/src/lib/libc/internal/init.h | 9 +- repos/libports/src/lib/libc/internal/plugin.h | 22 +- repos/libports/src/lib/libc/internal/select.h | 3 +- .../src/lib/libc/internal/vfs_plugin.h | 9 +- repos/libports/src/lib/libc/kernel.cc | 8 +- repos/libports/src/lib/libc/plugin.cc | 12 +- repos/libports/src/lib/libc/poll.cc | 190 +++++---- repos/libports/src/lib/libc/select.cc | 366 +++++------------- .../libports/src/lib/libc/socket_fs_plugin.cc | 107 +---- repos/libports/src/lib/libc/vfs_plugin.cc | 75 +--- 11 files changed, 290 insertions(+), 522 deletions(-) diff --git a/repos/libports/include/libc/select.h b/repos/libports/include/libc/select.h index b5f87e9473..2cc9ff15b5 100644 --- a/repos/libports/include/libc/select.h +++ b/repos/libports/include/libc/select.h @@ -23,15 +23,12 @@ namespace Libc { - struct Select_cb; - typedef Genode::Constructible Select_handler_cb; - struct Select_handler_base { - Select_handler_cb *_select_cb; - - Select_handler_base(); - ~Select_handler_base(); + int _nfds { }; + fd_set _readfds { }; + fd_set _writefds { }; + fd_set _exceptfds { }; /** * Traditional select() diff --git a/repos/libports/src/lib/libc/internal/init.h b/repos/libports/src/lib/libc/internal/init.h index eaf0ffbcd3..8b8645638f 100644 --- a/repos/libports/src/lib/libc/internal/init.h +++ b/repos/libports/src/lib/libc/internal/init.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2016-2020 Genode Labs GmbH + * Copyright (C) 2016-2024 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. @@ -69,10 +69,15 @@ namespace Libc { void init_vfs_plugin(Monitor &, Genode::Region_map &); void init_file_operations(Cwd &, Config_accessor const &); + /** + * Poll support + */ + void init_poll(Signal &, Monitor &); + /** * Select support */ - void init_select(Select &, Signal &, Monitor &); + void init_select(Select &); /** * Support for querying available RAM quota in sysctl functions diff --git a/repos/libports/src/lib/libc/internal/plugin.h b/repos/libports/src/lib/libc/internal/plugin.h index d2b47a9cec..d26513d190 100644 --- a/repos/libports/src/lib/libc/internal/plugin.h +++ b/repos/libports/src/lib/libc/internal/plugin.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2010-2024 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. @@ -52,6 +52,17 @@ namespace Libc { public: + struct Pollfd + { + File_descriptor *fdo; + short events; + /* + * This pointer points to 'revents' of the original + * 'struct pollfd' array. + */ + short *revents; + }; + Plugin(int priority = 0); virtual ~Plugin(); @@ -65,11 +76,6 @@ namespace Libc { virtual bool supports_readlink(const char *path, char *buf, ::size_t bufsiz); virtual bool supports_rename(const char *oldpath, const char *newpath); virtual bool supports_rmdir(const char *path); - virtual bool supports_select(int nfds, - fd_set *readfds, - fd_set *writefds, - fd_set *exceptfds, - struct timeval *timeout); virtual bool supports_socket(int domain, int type, int protocol); virtual bool supports_stat(const char *path); virtual bool supports_symlink(const char *oldpath, const char *newpath); @@ -120,7 +126,7 @@ namespace Libc { virtual int msync(void *addr, ::size_t len, int flags); virtual File_descriptor *open(const char *pathname, int flags); virtual int pipe(File_descriptor *pipefd[2]); - virtual bool poll(File_descriptor&, struct pollfd &pfd); + virtual int poll(Pollfd fds[], int nfds); virtual ssize_t read(File_descriptor *, void *buf, ::size_t count); virtual ssize_t readlink(const char *path, char *buf, ::size_t bufsiz); virtual ssize_t recv(File_descriptor *, void *buf, ::size_t len, int flags); @@ -129,8 +135,6 @@ namespace Libc { virtual ssize_t recvmsg(File_descriptor *, struct msghdr *msg, int flags); virtual int rename(const char *oldpath, const char *newpath); virtual int rmdir(const char *pathname); - virtual int select(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, struct timeval *timeout); virtual ssize_t send(File_descriptor *, const void *buf, ::size_t len, int flags); virtual ssize_t sendto(File_descriptor *, const void *buf, ::size_t len, int flags, diff --git a/repos/libports/src/lib/libc/internal/select.h b/repos/libports/src/lib/libc/internal/select.h index 0e91bc044d..d3faa38b28 100644 --- a/repos/libports/src/lib/libc/internal/select.h +++ b/repos/libports/src/lib/libc/internal/select.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2019 Genode Labs GmbH + * Copyright (C) 2019-2024 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. @@ -31,7 +31,6 @@ namespace Libc { virtual void deschedule_select() = 0; }; - void select_notify_from_kernel(); } #endif /* _LIBC__INTERNAL__SELECT_H_ */ diff --git a/repos/libports/src/lib/libc/internal/vfs_plugin.h b/repos/libports/src/lib/libc/internal/vfs_plugin.h index 5e6ad2d245..17f314b29b 100644 --- a/repos/libports/src/lib/libc/internal/vfs_plugin.h +++ b/repos/libports/src/lib/libc/internal/vfs_plugin.h @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2014-2019 Genode Labs GmbH + * Copyright (C) 2014-2024 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. @@ -223,10 +223,6 @@ class Libc::Vfs_plugin final : public Plugin bool supports_unlink(const char *) override { return true; } bool supports_mmap() override { return true; } - bool supports_select(int nfds, - fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) override; - /* kernel-specific API without monitor */ File_descriptor *open_from_kernel(const char *, int, int libc_fd); int close_from_kernel(File_descriptor *); @@ -248,7 +244,7 @@ class Libc::Vfs_plugin final : public Plugin 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; + int poll(Pollfd fds[], int nfds) override; ssize_t read(File_descriptor *, void *, ::size_t) override; ssize_t readlink(const char *, char *, ::size_t) override; int rename(const char *, const char *) override; @@ -259,7 +255,6 @@ class Libc::Vfs_plugin final : public Plugin ssize_t write(File_descriptor *, const void *, ::size_t ) override; void *mmap(void *, ::size_t, int, int, File_descriptor *, ::off_t) override; int munmap(void *, ::size_t) override; - int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) override; }; #endif /* _LIBC__INTERNAL__VFS_PLUGIN_H_ */ diff --git a/repos/libports/src/lib/libc/kernel.cc b/repos/libports/src/lib/libc/kernel.cc index 07761f3bf1..0f16d90ccc 100644 --- a/repos/libports/src/lib/libc/kernel.cc +++ b/repos/libports/src/lib/libc/kernel.cc @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2016-2020 Genode Labs GmbH + * Copyright (C) 2016-2024 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. @@ -408,9 +408,6 @@ void Libc::Kernel::_clone_state_from_parent() } -extern void (*libc_select_notify_from_kernel)(); - - void Libc::Kernel::handle_io_progress() { if (_io_progressed) { @@ -502,7 +499,8 @@ Libc::Kernel::Kernel(Genode::Env &env, Genode::Allocator &heap) init_vfs_plugin(*this, _env.rm()); init_file_operations(*this, _libc_env); init_time(*this, *this); - init_select(*this, _signal, *this); + init_poll(_signal, *this); + init_select(*this); init_socket_fs(*this, *this); init_passwd(_passwd_config()); init_signal(_signal); diff --git a/repos/libports/src/lib/libc/plugin.cc b/repos/libports/src/lib/libc/plugin.cc index f027eaf67b..8e26fb741c 100644 --- a/repos/libports/src/lib/libc/plugin.cc +++ b/repos/libports/src/lib/libc/plugin.cc @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2010-2024 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. @@ -110,13 +110,6 @@ bool Plugin::supports_rmdir(const char*) } -bool Plugin::supports_select(int, fd_set *, fd_set *, - fd_set *, struct timeval *) -{ - return false; -} - - bool Plugin::supports_socket(int, int, int) { return false; @@ -206,11 +199,10 @@ DUMMY(void *, (void *)(-1), mmap, (void *addr, ::size_t length, int prot, int fl DUMMY(int, -1, munmap, (void *, ::size_t)); DUMMY(int, -1, msync, (void *addr, ::size_t len, int flags)); DUMMY(int, -1, pipe, (File_descriptor*[2])); -DUMMY(bool, 0, poll, (File_descriptor &, struct pollfd &)); +DUMMY(int, -1, poll, (Pollfd[], int)); DUMMY(ssize_t, -1, readlink, (const char *, char *, ::size_t)); DUMMY(int, -1, rename, (const char *, const char *)); DUMMY(int, -1, rmdir, (const char*)); -DUMMY(int, -1, select, (int, fd_set *, fd_set *, fd_set *, struct timeval *)); DUMMY(int, -1, stat, (const char*, struct stat*)); DUMMY(int, -1, symlink, (const char*, const char*)); DUMMY(int, -1, unlink, (const char*)); diff --git a/repos/libports/src/lib/libc/poll.cc b/repos/libports/src/lib/libc/poll.cc index cc1593407b..1f8a4c85e6 100644 --- a/repos/libports/src/lib/libc/poll.cc +++ b/repos/libports/src/lib/libc/poll.cc @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2010-2019 Genode Labs GmbH + * Copyright (C) 2010-2024 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. @@ -22,91 +22,141 @@ #include #include #include +#include +#include using namespace Libc; -/** - * The poll function was taken from OpenSSH portable (bsd-poll.c) and adepted - * to better fit within Genode's libc. - * - * Copyright (c) 2004, 2005, 2007 Darren Tucker (dtucker at zip com au). - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ +static Monitor *_monitor_ptr; +static Libc::Signal *_signal_ptr; + +void Libc::init_poll(Signal &signal, Monitor &monitor) +{ + _signal_ptr = &signal; + _monitor_ptr = &monitor; +} + + +static int poll_plugins(Plugin::Pollfd pollfds[], int nfds) +{ + int nready = 0; + + for (Plugin *plugin = plugin_registry()->first(); + plugin; + plugin = plugin->next()) { + + if (!plugin->supports_poll()) + continue; + + /* count the number of pollfds for this plugin */ + + int plugin_nfds = 0; + + for (int pollfd_index = 0; pollfd_index < nfds; pollfd_index++) + if (pollfds[pollfd_index].fdo->plugin == plugin) + plugin_nfds++; + + if (plugin_nfds == 0) + continue; + + /* + * Copy the pollfds belonging to this plugin to a plugin-specific + * array. 'revents' still points into the original structure. + */ + + Plugin::Pollfd plugin_pollfds[plugin_nfds]; + + for (int pollfd_index = 0, plugin_pollfd_index = 0; + pollfd_index < nfds; + pollfd_index++) { + + if (pollfds[pollfd_index].fdo->plugin == plugin) { + plugin_pollfds[plugin_pollfd_index] = pollfds[pollfd_index]; + plugin_pollfd_index++; + } + } + + int plugin_nready = plugin->poll(plugin_pollfds, plugin_nfds); + + if (plugin_nready < 0) + return plugin_nready; + + nready += plugin_nready; + } + + return nready; +} + + extern "C" int __attribute__((weak)) -poll(struct pollfd fds[], nfds_t nfds, int timeout) +poll(struct pollfd pollfds[], nfds_t nfds, int timeout_ms) { - nfds_t i; - int ret, fd, maxfd = 0; - fd_set readfds, writefds, exceptfds; - struct timeval tv, *tvp = NULL; + /* + * Look up the file descriptor objects early-on to reduce repeated + * overhead. + */ - for (i = 0; i < nfds; i++) { - fd = fds[i].fd; - if (fd >= (int)FD_SETSIZE) { - return Libc::Errno(EINVAL); - } - maxfd = MAX(maxfd, fd); + Plugin::Pollfd plugins_pollfds[nfds]; + + for (nfds_t pollfd_index = 0; pollfd_index < nfds; pollfd_index++) { + pollfds[pollfd_index].revents = 0; + plugins_pollfds[pollfd_index].fdo = + file_descriptor_allocator()->find_by_libc_fd(pollfds[pollfd_index].fd); + plugins_pollfds[pollfd_index].events = pollfds[pollfd_index].events; + plugins_pollfds[pollfd_index].revents = &pollfds[pollfd_index].revents; } - /* populate event bit vectors for the events we're interested in */ + int nready = poll_plugins(plugins_pollfds, nfds); - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&exceptfds); + /* return if any descriptor is ready or an error occurred */ + if (nready != 0) + return nready; - for (i = 0; i < nfds; i++) { - fd = fds[i].fd; - if (fd == -1) - continue; - if (fds[i].events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { - FD_SET(fd, &readfds); - FD_SET(fd, &exceptfds); - } - if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { - FD_SET(fd, &writefds); - FD_SET(fd, &exceptfds); - } + /* return on zero-timeout */ + if (timeout_ms == 0) + return 0; + + /* convert infinite timeout to monitor interface */ + if (timeout_ms < 0) + timeout_ms = 0; + + if (!_monitor_ptr || !_signal_ptr) { + struct Missing_call_of_init_poll : Exception { }; + throw Missing_call_of_init_poll(); } - /* poll timeout is msec, select is timeval (sec + usec) */ - if (timeout >= 0) { - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - tvp = &tv; - } - ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, tvp); + unsigned const orig_signal_count = _signal_ptr->count(); - /* scan through select results and set poll() flags */ - for (i = 0; i < nfds; i++) { - fd = fds[i].fd; - fds[i].revents = 0; - if (fd == -1) - continue; - if ((fds[i].events & POLLIN) && FD_ISSET(fd, &readfds)) { - fds[i].revents |= POLLIN; - } - if ((fds[i].events & POLLOUT) && FD_ISSET(fd, &writefds)) { - fds[i].revents |= POLLOUT; - } - if (FD_ISSET(fd, &exceptfds)) { - fds[i].revents |= POLLERR; - } - } + auto signal_occurred_during_poll = [&] () + { + return (_signal_ptr->count() != orig_signal_count); + }; - return ret; + auto monitor_fn = [&] () + { + nready = poll_plugins(plugins_pollfds, nfds); + + if (nready != 0) + return Monitor::Function_result::COMPLETE; + + if (signal_occurred_during_poll()) + return Monitor::Function_result::COMPLETE; + + return Monitor::Function_result::INCOMPLETE; + }; + + Monitor::Result const monitor_result = + _monitor_ptr->monitor(monitor_fn, timeout_ms); + + if (monitor_result == Monitor::Result::TIMEOUT) + return 0; + + if (signal_occurred_during_poll()) + return Errno(EINTR); + + return nready; } diff --git a/repos/libports/src/lib/libc/select.cc b/repos/libports/src/lib/libc/select.cc index a9149c2109..0d1e1770cc 100644 --- a/repos/libports/src/lib/libc/select.cc +++ b/repos/libports/src/lib/libc/select.cc @@ -6,16 +6,13 @@ * \author Norman Feske * \date 2010-01-21 * - * the 'select()' implementation is partially based on the lwip version as - * implemented in 'src/api/sockets.c' - * * Note what POSIX states about select(): File descriptors associated with * regular files always select true for ready to read, ready to write, and * error conditions. */ /* - * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2010-2024 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. @@ -42,182 +39,67 @@ #include #include -namespace Libc { - struct Select_cb; - struct Select_cb_list; -} - using namespace Libc; static Select *_select_ptr; -static Libc::Signal *_signal_ptr; -static Monitor *_monitor_ptr; -void Libc::init_select(Select &select, Signal &signal, Monitor &monitor) +void Libc::init_select(Select &select) { _select_ptr = &select; - _signal_ptr = &signal; - _monitor_ptr = &monitor; } -/** Description for a task waiting in select */ -struct Libc::Select_cb +static int poll_nfds_from_select_nfds(int select_nfds, + fd_set &readfds, + fd_set &writefds, + fd_set &exceptfds) { - Select_cb *next = nullptr; + int poll_nfds = 0; - int const nfds; - int nready = 0; - fd_set readfds; - fd_set writefds; - fd_set exceptfds; + for (int fd = 0; fd < select_nfds; fd++) { - Select_cb(int nfds, fd_set const &readfds, fd_set const &writefds, fd_set const &exceptfds) - : - nfds(nfds), readfds(readfds), writefds(writefds), exceptfds(exceptfds) - { } -}; + bool fd_in_readfds = FD_ISSET(fd, &readfds); + bool fd_in_writefds = FD_ISSET(fd, &writefds); + bool fd_in_exceptfds = FD_ISSET(fd, &exceptfds); - -struct Libc::Select_cb_list -{ - Mutex _mutex; - Select_cb *_first = nullptr; - - struct Guard : Mutex::Guard - { - Select_cb_list *l; - - Guard(Select_cb_list &list) : Mutex::Guard(list._mutex), l(&list) { } - }; - - void unsynchronized_insert(Select_cb *scb) - { - scb->next = _first; - _first = scb; + if (fd_in_readfds || fd_in_writefds || fd_in_exceptfds) + poll_nfds++; } - void insert(Select_cb *scb) - { - Guard guard(*this); - unsynchronized_insert(scb); - } - - void remove(Select_cb *scb) - { - Guard guard(*this); - - /* address of pointer to next allows to change the head */ - for (Select_cb **next = &_first; *next; next = &(*next)->next) { - if (*next == scb) { - *next = scb->next; - break; - } - } - } - - template - void for_each(FUNC const &func) - { - Guard guard(*this); - - for (Select_cb *scb = _first; scb; scb = scb->next) - func(*scb); - } -}; - -/** The global list of tasks waiting for select */ -static Libc::Select_cb_list &select_cb_list() -{ - static Select_cb_list inst; - return inst; + return poll_nfds; } -/** - * Poll plugin select() functions - * - * We iterate over all file descriptors in each list and count the number of - * ready descriptors. Output file-descriptor sets are cleared by this function - * (according to POSIX). - */ -static int selscan(int nfds, - fd_set *in_readfds, fd_set *in_writefds, fd_set *in_exceptfds, - fd_set *out_readfds, fd_set *out_writefds, fd_set *out_exceptfds) +void populate_pollfds(struct pollfd pollfds[], int poll_nfds, + int select_nfds, + fd_set &readfds, fd_set &writefds, fd_set &exceptfds) { - int nready = 0; + for (int fd = 0, pollfd_index = 0; fd < select_nfds; fd++) { - /* zero timeout for polling of the plugins' select() functions */ - struct timeval tv_0 = { 0, 0 }; + bool fd_in_readfds = FD_ISSET(fd, &readfds); + bool fd_in_writefds = FD_ISSET(fd, &writefds); + bool fd_in_exceptfds = FD_ISSET(fd, &exceptfds); - /* temporary fd sets that are passed to the plugins */ - int plugin_nready; - fd_set plugin_readfds; - fd_set plugin_writefds; - fd_set plugin_exceptfds; + if (!fd_in_readfds && !fd_in_writefds && !fd_in_exceptfds) + continue; - /* clear fd sets */ - if (out_readfds) FD_ZERO(out_readfds); - if (out_writefds) FD_ZERO(out_writefds); - if (out_exceptfds) FD_ZERO(out_exceptfds); + pollfds[pollfd_index].fd = fd; - if (nfds > FD_SETSIZE) - return Libc::Errno(EINVAL); + pollfds[pollfd_index].events = 0; - for (Plugin *plugin = plugin_registry()->first(); - plugin; - plugin = plugin->next()) { - if (plugin->supports_select(nfds, in_readfds, in_writefds, in_exceptfds, &tv_0)) { + if (fd_in_readfds) + pollfds[pollfd_index].events |= POLLIN; - plugin_readfds = *in_readfds; - plugin_writefds = *in_writefds; - plugin_exceptfds = *in_exceptfds; + if (fd_in_writefds) + pollfds[pollfd_index].events |= POLLOUT; - plugin_nready = plugin->select(nfds, &plugin_readfds, &plugin_writefds, &plugin_exceptfds, &tv_0); + if (fd_in_exceptfds) + pollfds[pollfd_index].events |= POLLERR; - if (plugin_nready > 0) { - for (int libc_fd = 0; libc_fd < nfds; libc_fd++) { - if (out_readfds && FD_ISSET(libc_fd, &plugin_readfds)) { - FD_SET(libc_fd, out_readfds); - } - if (out_writefds && FD_ISSET(libc_fd, &plugin_writefds)) { - FD_SET(libc_fd, out_writefds); - } - if (out_exceptfds && FD_ISSET(libc_fd, &plugin_exceptfds)) { - FD_SET(libc_fd, out_exceptfds); - } - } - nready += plugin_nready; - } else if (plugin_nready < 0) { - error("plugin->select() returned error value ", plugin_nready); - } - } + pollfd_index++; } - - return nready; -} - - -/* this function gets called by plugin backends when file descripors become ready */ -void Libc::select_notify_from_kernel() -{ - fd_set tmp_readfds, tmp_writefds, tmp_exceptfds; - - /* check for each waiting select() function if one of its fds is ready now - * and if so, wake all up */ - - select_cb_list().for_each([&] (Select_cb &scb) { - scb.nready = selscan(scb.nfds, - &scb.readfds, &scb.writefds, &scb.exceptfds, - &tmp_readfds, &tmp_writefds, &tmp_exceptfds); - if (scb.nready > 0) { - scb.readfds = tmp_readfds; - scb.writefds = tmp_writefds; - scb.exceptfds = tmp_exceptfds; - } - }); } @@ -225,86 +107,63 @@ extern "C" __attribute__((weak)) int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *tv) { - fd_set in_readfds, in_writefds, in_exceptfds; + /* + * Create copies of the input sets or new empty input sets to avoid + * repeated nullpointer checks later on. + */ - Constructible select_cb; + fd_set in_readfds, in_writefds, in_exceptfds; 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); - /* - * Insert callback first to avoid race after 'selscan()' - */ + int poll_nfds = poll_nfds_from_select_nfds(nfds, in_readfds, in_writefds, in_exceptfds); - select_cb.construct(nfds, in_readfds, in_writefds, in_exceptfds); - select_cb_list().insert(&(*select_cb)); + struct pollfd pollfds[poll_nfds]; - int const nready = selscan(nfds, - &in_readfds, &in_writefds, &in_exceptfds, - readfds, writefds, exceptfds); + populate_pollfds(pollfds, poll_nfds, + nfds, in_readfds, in_writefds, in_exceptfds); - /* return if any descripor is ready */ - if (nready) { - select_cb_list().remove(&(*select_cb)); - return nready; + int const timeout_ms = (tv != nullptr) + ? tv->tv_sec*1000 + tv->tv_usec/1000 + : -1; + + int poll_nready = poll(pollfds, poll_nfds, timeout_ms); + + if (poll_nready < 0) + return poll_nready; + + if (readfds) FD_ZERO(readfds); + if (writefds) FD_ZERO(writefds); + if (exceptfds) FD_ZERO(exceptfds); + + int nready = 0; + + if (poll_nready > 0) { + + for (int pollfd_index = 0; + pollfd_index < poll_nfds; + pollfd_index++) { + + if (readfds && (pollfds[pollfd_index].revents & POLLIN)) { + FD_SET(pollfds[pollfd_index].fd, readfds); + nready++; + } + + if (writefds && (pollfds[pollfd_index].revents & POLLOUT)) { + FD_SET(pollfds[pollfd_index].fd, writefds); + nready++; + } + + if (exceptfds && (pollfds[pollfd_index].revents & POLLERR)) { + FD_SET(pollfds[pollfd_index].fd, exceptfds); + nready++; + } + } } - /* return on zero-timeout */ - if (tv && (tv->tv_sec) == 0 && (tv->tv_usec == 0)) { - select_cb_list().remove(&(*select_cb)); - return 0; - } - - using Genode::uint64_t; - - uint64_t const timeout_ms = (tv != nullptr) - ? (uint64_t)tv->tv_sec*1000 + tv->tv_usec/1000 - : 0UL; - { - struct Missing_call_of_init_select : Exception { }; - if (!_monitor_ptr || !_signal_ptr) - throw Missing_call_of_init_select(); - } - - unsigned const orig_signal_count = _signal_ptr->count(); - - auto signal_occurred_during_select = [&] () - { - return (_signal_ptr->count() != orig_signal_count); - }; - - auto monitor_fn = [&] () - { - select_notify_from_kernel(); - - if (select_cb->nready != 0) - return Monitor::Function_result::COMPLETE; - - if (signal_occurred_during_select()) - return Monitor::Function_result::COMPLETE; - - return Monitor::Function_result::INCOMPLETE; - }; - - Monitor::Result const monitor_result = - _monitor_ptr->monitor(monitor_fn, timeout_ms); - - select_cb_list().remove(&(*select_cb)); - - if (monitor_result == Monitor::Result::TIMEOUT) - return 0; - - if (signal_occurred_during_select()) - return Errno(EINTR); - - /* 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; - if (exceptfds) *exceptfds = select_cb->exceptfds; - - return select_cb->nready; + return nready; } @@ -355,37 +214,22 @@ int __sys_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds int Libc::Select_handler_base::select(int nfds, fd_set &readfds, fd_set &writefds, fd_set &exceptfds) { - fd_set in_readfds, in_writefds, in_exceptfds; + /* + * Save the input data before calling '::select()' which zeroes out + * the fd sets if nothing is ready. + */ + _nfds = nfds; + _readfds = readfds; + _writefds = writefds; + _exceptfds = exceptfds; - in_readfds = readfds; - in_writefds = writefds; - in_exceptfds = exceptfds; + struct timeval tv { 0, 0 }; - /* remove potentially enqueued callback from list */ - if (_select_cb->constructed()) - select_cb_list().remove(&(**_select_cb)); + int const nready = ::select(nfds, &readfds, &writefds, &exceptfds, &tv); - { - /* - * 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()); - - int const nready = selscan(nfds, - &in_readfds, &in_writefds, &in_exceptfds, - &readfds, &writefds, &exceptfds); - - /* return if any descripor is ready */ - if (nready) - return nready; - - /* 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 or an error occurred */ + if (nready != 0) + return nready; struct Missing_call_of_init_select : Exception { }; if (!_select_ptr) @@ -399,27 +243,23 @@ int Libc::Select_handler_base::select(int nfds, fd_set &readfds, void Libc::Select_handler_base::dispatch_select() { - Libc::select_notify_from_kernel(); + /* '::select()' zeroes out the fd sets if nothing is ready */ + fd_set tmp_readfds = _readfds; + fd_set tmp_writefds = _writefds; + fd_set tmp_exceptfds = _exceptfds; - Select_handler_cb &select_cb = *_select_cb; + struct timeval tv { 0, 0 }; - if (select_cb->nready == 0) return; + int const nready = ::select(_nfds, + &tmp_readfds, + &tmp_writefds, + &tmp_exceptfds, + &tv); - select_cb_list().remove(&(*select_cb)); + if (nready == 0) return; if (_select_ptr) _select_ptr->deschedule_select(); - select_ready(select_cb->nready, select_cb->readfds, - select_cb->writefds, select_cb->exceptfds); + select_ready(nready, tmp_readfds, tmp_writefds, tmp_exceptfds); } - - -Libc::Select_handler_base::Select_handler_base() -: - _select_cb((Select_handler_cb*)malloc(sizeof(*_select_cb))) -{ } - - -Libc::Select_handler_base::~Select_handler_base() -{ } diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.cc b/repos/libports/src/lib/libc/socket_fs_plugin.cc index f8e9a1d27a..264292470e 100644 --- a/repos/libports/src/lib/libc/socket_fs_plugin.cc +++ b/repos/libports/src/lib/libc/socket_fs_plugin.cc @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2015-2019 Genode Labs GmbH + * Copyright (C) 2015-2024 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. @@ -355,14 +355,12 @@ struct Libc::Socket_fs::Local_functor : Sockaddr_functor struct Libc::Socket_fs::Plugin : Libc::Plugin { bool supports_poll() override { return true; } - bool supports_select(int, fd_set *, fd_set *, fd_set *, timeval *) override; ssize_t read(File_descriptor *, void *, ::size_t) override; ssize_t write(File_descriptor *, const void *, ::size_t) override; int fcntl(File_descriptor *, int, long) override; int close(File_descriptor *) override; - bool poll(File_descriptor &fd, struct pollfd &pfd) override; - int select(int, fd_set *, fd_set *, fd_set *, timeval *) override; + int poll(Pollfd fds[], int nfds) override; int ioctl(File_descriptor *, unsigned long, char *) override; }; @@ -1316,111 +1314,46 @@ ssize_t Socket_fs::Plugin::write(File_descriptor *fd, const void *buf, ::size_t } -bool Socket_fs::Plugin::poll(File_descriptor &fdo, struct pollfd &pfd) -{ - if (fdo.plugin != this) return false; - Socket_fs::Context *context { nullptr }; +int Socket_fs::Plugin::poll(Pollfd fds[], int nfds) - try { - context = dynamic_cast(fdo.context); - } catch (Socket_fs::Context::Inaccessible) { - pfd.revents |= POLLNVAL; - return true; - } - - enum { - POLLIN_MASK = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI, - POLLOUT_MASK = POLLOUT | POLLWRNORM | POLLWRBAND, - }; - - bool res { false }; - - if ((pfd.events & POLLIN_MASK) && context->read_ready()) { - pfd.revents |= pfd.events & POLLIN_MASK; - res = true; - } - - if ((pfd.events & POLLOUT_MASK) && context->write_ready()) { - pfd.revents |= pfd.events & POLLOUT_MASK; - res = true; - } - - return res; -} - - -bool Socket_fs::Plugin::supports_select(int nfds, - fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) -{ - /* return true if any file descriptor (which is set) belongs to the VFS */ - for (int fd = 0; fd < nfds; ++fd) { - - if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) { - File_descriptor *fdo = file_descriptor_allocator()->find_by_libc_fd(fd); - - if (fdo && (fdo->plugin == this)) - return true; - } - } - - return false; -} - - -int Socket_fs::Plugin::select(int nfds, - fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - timeval *timeout) { int nready = 0; - fd_set const in_readfds = *readfds; - fd_set const in_writefds = *writefds; - /* XXX exceptfds not supported */ - - /* clear fd sets */ - FD_ZERO(readfds); - FD_ZERO(writefds); - FD_ZERO(exceptfds); - auto fn = [&] { - for (int fd = 0; fd < nfds; ++fd) { + for (int pollfd_index = 0; pollfd_index < nfds; pollfd_index++) { - bool fd_in_readfds = FD_ISSET(fd, &in_readfds); - bool fd_in_writefds = FD_ISSET(fd, &in_writefds); + bool fd_ready = false; - if (!fd_in_readfds && !fd_in_writefds) - continue; + if (fds[pollfd_index].events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { - File_descriptor *fdo = file_descriptor_allocator()->find_by_libc_fd(fd); - - /* handle only fds that belong to this plugin */ - if (!fdo || (fdo->plugin != this)) - continue; - - if (fd_in_readfds) { try { - Socket_fs::Context *context = dynamic_cast(fdo->context); + Socket_fs::Context *context = + static_cast(fds[pollfd_index].fdo->context); if (context->read_ready()) { - FD_SET(fd, readfds); - ++nready; + *fds[pollfd_index].revents |= POLLIN; + fd_ready = true; } } catch (Socket_fs::Context::Inaccessible) { } } - if (fd_in_writefds) { + if (fds[pollfd_index].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { try { - Socket_fs::Context *context = dynamic_cast(fdo->context); + Socket_fs::Context *context = + static_cast(fds[pollfd_index].fdo->context); + if (context->write_ready()) { - FD_SET(fd, writefds); - ++nready; + *fds[pollfd_index].revents |= POLLOUT; + fd_ready = true; } } catch (Socket_fs::Context::Inaccessible) { } } - /* XXX exceptfds not supported */ + /* XXX POLLERR not supported */ + + if (fd_ready) + nready++; } return Fn::COMPLETE; }; diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index 2dd1ed0f22..f7d44fe254 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -2513,84 +2513,39 @@ int Libc::Vfs_plugin::pipe(Libc::File_descriptor *pipefdo[2]) } -bool Libc::Vfs_plugin::poll(File_descriptor &fd, struct pollfd &pfd) -{ - error("Plugin::poll() is deprecated"); - return false; -} - - -bool Libc::Vfs_plugin::supports_select(int nfds, - fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) -{ - /* return true if any file descriptor (which is set) belongs to the VFS */ - for (int fd = 0; fd < nfds; ++fd) { - - if (FD_ISSET(fd, readfds) || FD_ISSET(fd, writefds) || FD_ISSET(fd, exceptfds)) { - - File_descriptor *fdo = - file_descriptor_allocator()->find_by_libc_fd(fd); - - if (fdo && (fdo->plugin == this)) - return true; - } - } - return false; -} - - -int Libc::Vfs_plugin::select(int nfds, - fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) +int Libc::Vfs_plugin::poll(Pollfd fds[], int nfds) { int nready = 0; - fd_set const in_readfds = *readfds; - fd_set const in_writefds = *writefds; - /* XXX exceptfds not supported */ - - /* clear fd sets */ - FD_ZERO(readfds); - FD_ZERO(writefds); - FD_ZERO(exceptfds); - auto fn = [&] { - for (int fd = 0; fd < nfds; ++fd) { - bool fd_in_readfds = FD_ISSET(fd, &in_readfds); - bool fd_in_writefds = FD_ISSET(fd, &in_writefds); + for (int pollfd_index = 0; pollfd_index < nfds; pollfd_index++) { - if (!fd_in_readfds && !fd_in_writefds) - continue; - - File_descriptor *fdo = - file_descriptor_allocator()->find_by_libc_fd(fd); - - /* handle only fds that belong to this plugin */ - if (!fdo || (fdo->plugin != this)) - continue; - - Vfs::Vfs_handle *handle = vfs_handle(fdo); + Vfs::Vfs_handle *handle = vfs_handle(fds[pollfd_index].fdo); if (!handle) continue; - if (fd_in_readfds) { + bool fd_ready = false; + + if (fds[pollfd_index].events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { if (handle->fs().read_ready(*handle)) { - FD_SET(fd, readfds); - ++nready; + *fds[pollfd_index].revents |= POLLIN; + fd_ready = true; } else { handle->fs().notify_read_ready(handle); } } - if (fd_in_writefds) { + if (fds[pollfd_index].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { if (handle->fs().write_ready(*handle)) { - FD_SET(fd, writefds); - ++nready; + *fds[pollfd_index].revents |= POLLOUT; + fd_ready = true; } } - /* XXX exceptfds not supported */ + /* XXX POLLERR not supported */ + + if (fd_ready) + nready++; } return Fn::COMPLETE; };