]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
RIO: Add poll builder to support immediate-mode polling API
authorHugo Landau <hlandau@openssl.org>
Mon, 13 May 2024 19:20:23 +0000 (20:20 +0100)
committerNeil Horman <nhorman@openssl.org>
Mon, 17 Feb 2025 16:27:33 +0000 (11:27 -0500)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25416)

ssl/rio/build.info
ssl/rio/poll_builder.c [new file with mode: 0644]
ssl/rio/poll_builder.h [new file with mode: 0644]
ssl/rio/poll_method.h [new file with mode: 0644]

index 15f93fb07d5e935b84ad0cfe030d436768f85695..58b0f8bc93585d75b44b7d4072dc4c78e4d4e565 100644 (file)
@@ -4,3 +4,4 @@ SOURCE[$LIBSSL]=poll_immediate.c
 IF[{- !$disabled{quic} -}]
   SOURCE[$LIBSSL]=rio_notifier.c
 ENDIF
+SOURCE[$LIBSSL]=poll_builder.c
diff --git a/ssl/rio/poll_builder.c b/ssl/rio/poll_builder.c
new file mode 100644 (file)
index 0000000..bd0979a
--- /dev/null
@@ -0,0 +1,170 @@
+#include "poll_builder.h"
+#include "internal/safe_math.h"
+#include <assert.h>
+
+OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
+
+int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb)
+{
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+    FD_ZERO(&rpb->rfd);
+    FD_ZERO(&rpb->wfd);
+    FD_ZERO(&rpb->efd);
+    rpb->hwm_fd     = -1;
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+    rpb->pfd_heap   = NULL;
+    rpb->pfd_num    = 0;
+    rpb->pfd_alloc  = OSSL_NELEM(rpb->pfds);
+#endif
+    return 1;
+}
+
+void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb)
+{
+    if (rpb == NULL)
+        return;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+    OPENSSL_free(rpb->pfd_heap);
+#endif
+}
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+static int rpb_ensure_alloc(RIO_POLL_BUILDER *rpb, size_t alloc)
+{
+    struct pollfd *pfd_heap_new;
+    size_t total_size;
+    int error = 0;
+
+    if (alloc <= rpb->pfd_alloc)
+        return 1;
+
+    total_size = safe_mul_size_t(alloc, sizeof(struct pollfd), &error);
+    if (error)
+        return 0;
+
+    pfd_heap_new = OPENSSL_realloc(rpb->pfd_heap, total_size);
+    if (pfd_heap_new == NULL)
+        return 0;
+
+    rpb->pfd_heap   = pfd_heap_new;
+    rpb->pfd_alloc  = alloc;
+    return 1;
+}
+#endif
+
+int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd,
+                                 int want_read, int want_write)
+{
+    if (fd < 0)
+        return 0;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+
+# ifndef OPENSSL_SYS_WINDOWS
+    /*
+     * On Windows there is no relevant limit to the magnitude of a fd value (see
+     * above). On *NIX the fd_set uses a bitmap and we must check the limit.
+     */
+    if (fd >= FD_SETSIZE)
+        return 0;
+# endif
+
+    if (want_read)
+        openssl_fdset(fd, &rpb->rfd);
+
+    if (want_write)
+        openssl_fdset(fd, &rpb->wfd);
+
+    openssl_fdset(fd, &rpb->efd);
+    if (fd > rpb->hwm_fd)
+        rpb->hwm_fd = fd;
+
+    return 1;
+
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+    size_t num_loop;
+
+    for (num_loop = 0;; ++num_loop) {
+        size_t i;
+        struct pollfd *pfds = (rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds);
+        struct pollfd *pfd;
+
+        for (i = 0; i < rpb->pfd_num; ++i) {
+            pfd = &pfds[i];
+
+            if (pfd->fd == -1 || pfd->fd == fd)
+                break;
+        }
+
+        if (i >= rpb->pfd_alloc) {
+            /* Was not able to find room - enlarge and try again. */
+            if (num_loop > 0)
+                /* Should not happen twice. */
+                return 0;
+
+            if (!rpb_ensure_alloc(rpb, rpb->pfd_alloc * 2))
+                return 0;
+
+            continue;
+        }
+
+        assert(i <= rpb->pfd_num && rpb->pfd_num <= rpb->pfd_alloc);
+        pfds[i].fd      = fd;
+        pfds[i].events  = 0;
+
+        if (want_read)
+            pfds[i].events |= POLLIN;
+        if (want_write)
+            pfds[i].events |= POLLOUT;
+
+        if (i == rpb->pfd_num)
+            ++rpb->pfd_num;
+
+        return 1;
+    }
+#endif
+}
+
+int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline)
+{
+    int rc;
+
+#if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+    do {
+        struct timeval timeout, *p_timeout = &timeout;
+
+        /*
+         * select expects a timeout, not a deadline, so do the conversion.
+         * Update for each call to ensure the correct value is used if we repeat
+         * due to EINTR.
+         */
+        if (ossl_time_is_infinite(deadline))
+            p_timeout = NULL;
+        else
+            /*
+             * ossl_time_subtract saturates to zero so we don't need to check if
+             * now > deadline.
+             */
+            timeout = ossl_time_to_timeval(ossl_time_subtract(deadline,
+                                                              ossl_time_now()));
+
+        rc = select(rpb->hwm_fd + 1, &rpb->rfd, &rpb->wfd, &rpb->efd, p_timeout);
+    } while (rc == -1 && get_last_socket_error_is_eintr());
+#elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+    do {
+        int timeout_ms;
+
+        if (ossl_time_is_infinite(deadline))
+            timeout_ms = -1;
+        else
+            timeout_ms = ossl_time2ms(ossl_time_subtract(deadline,
+                                                         ossl_time_now()));
+
+        rc = poll(rpb->pfd_heap != NULL ? rpb->pfd_heap : rpb->pfds,
+                  rpb->pfd_num, timeout_ms);
+    } while (rc == -1 && get_last_socket_error_is_eintr());
+#endif
+
+    return rc < 0 ? 0 : 1;
+}
diff --git a/ssl/rio/poll_builder.h b/ssl/rio/poll_builder.h
new file mode 100644 (file)
index 0000000..456e404
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#ifndef OSSL_POLL_BUILDER_H
+# define OSSL_POLL_BUILDER_H
+
+# include "poll_method.h"
+# include "internal/time.h"
+
+/*
+ * RIO_POLL_BUILDER
+ * ================
+ *
+ * RIO_POLL_BUILDER provides support for immediate-mode polling architectures.
+ * It abstracts OS-specific immediate-mode polling APIs such as select(2) and
+ * poll(2) and allows an arbitrarily large number of FDs to be polled for while
+ * providing minimal overhead (over the OS APIs themselves) for small numbers of
+ * FDs.
+ */
+typedef struct rio_poll_builder_st {
+# if RIO_POLL_METHOD == RIO_POLL_METHOD_SELECT
+    fd_set          rfd, wfd, efd;
+    int             hwm_fd;
+# elif RIO_POLL_METHOD == RIO_POLL_METHOD_POLL
+#  define RIO_NUM_STACK_PFDS  32
+    struct pollfd   *pfd_heap;
+    struct pollfd   pfds[RIO_NUM_STACK_PFDS];
+    size_t          pfd_num, pfd_alloc;
+# else
+#  error Unknown RIO poll method
+# endif
+} RIO_POLL_BUILDER;
+
+/*
+ * Initialises a new poll builder.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
+int ossl_rio_poll_builder_init(RIO_POLL_BUILDER *rpb);
+
+/*
+ * Tears down a poll builder, freeing any heap allocations (if any) which may
+ * have been made internally.
+ */
+void ossl_rio_poll_builder_cleanup(RIO_POLL_BUILDER *rpb);
+
+/*
+ * Adds a file descriptor to a poll builder. If want_read is 1, listens for
+ * readability events (POLLIN). If want_write is 1, listens for writability
+ * events (POLLOUT).
+ *
+ * If this is called with the same fd twice, the result equivalent to calling
+ * it one time with logically OR'd values of want_read and want_write.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
+int ossl_rio_poll_builder_add_fd(RIO_POLL_BUILDER *rpb, int fd,
+                                 int want_read, int want_write);
+
+/*
+ * Polls the set of file descriptors added to a poll builder. deadline is a
+ * deadline time based on the ossl_time_now() clock or ossl_time_infinite() for
+ * no timeout. Returns 1 on success or 0 on failure.
+ */
+int ossl_rio_poll_builder_poll(RIO_POLL_BUILDER *rpb, OSSL_TIME deadline);
+
+/*
+ * TODO(RIO): No support currently for readout of what was readable/writeable as
+ * it is currently not needed.
+ */
+
+#endif
diff --git a/ssl/rio/poll_method.h b/ssl/rio/poll_method.h
new file mode 100644 (file)
index 0000000..917bbb7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#ifndef OSSL_POLL_METHOD_H
+# define OSSL_POLL_METHOD_H
+
+# include "internal/common.h"
+# include "internal/sockets.h"
+
+# define RIO_POLL_METHOD_SELECT         1
+# define RIO_POLL_METHOD_POLL           2
+
+# ifndef RIO_POLL_METHOD
+#  if !defined(OPENSSL_SYS_WINDOWS) && defined(POLLIN)
+#   define RIO_POLL_METHOD              RIO_POLL_METHOD_POLL
+#  else
+#   define RIO_POLL_METHOD              RIO_POLL_METHOD_SELECT
+#  endif
+# endif
+
+#endif