Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 81 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
uses: actions/checkout@v6

- name: Configure
run: cmake --preset dev
run: cmake --preset dev -DPV_ENABLE_IO_URING=OFF

- name: Build
run: cmake --build --preset dev --parallel
Expand All @@ -113,6 +113,38 @@ jobs:
path: build/dev/Testing
if-no-files-found: ignore

unit-tests-io-uring:
name: Unit tests (io_uring, Ubuntu 26.04, Clang)
runs-on: ubuntu-24.04
timeout-minutes: 60
container:
image: ci.trafficserver.apache.org/proxy-verifier/ubuntu:26.04
options: --security-opt seccomp=unconfined
env:
CC: clang
CXX: clang++
PV_TEST_REQUIRE_IO_URING: "1"
steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Configure
run: cmake --preset dev -DPV_ENABLE_IO_URING=ON

- name: Build
run: cmake --build --preset dev --parallel

- name: Run unit tests
run: ctest --preset dev

- name: Upload unit-test logs
if: failure()
uses: actions/upload-artifact@v6
with:
name: unit-test-io-uring-logs
path: build/dev/Testing
if-no-files-found: ignore

urtests:
name: Uranium tests (Ubuntu 26.04, GCC)
runs-on: ubuntu-24.04
Expand All @@ -127,7 +159,10 @@ jobs:
uses: actions/checkout@v6

- name: Configure
run: cmake --preset dev -DURTEST_SANDBOX=/tmp/proxy-verifier-urtest
run: >-
cmake --preset dev
-DPV_ENABLE_IO_URING=OFF
-DURTEST_SANDBOX=/tmp/proxy-verifier-urtest

- name: Build
run: cmake --build --preset dev --parallel
Expand All @@ -152,3 +187,47 @@ jobs:
name: urtest-sandbox
path: artifacts/urtest-sandbox.tar.gz
if-no-files-found: ignore

urtests-io-uring:
name: Uranium tests (io_uring, Ubuntu 26.04, GCC)
runs-on: ubuntu-24.04
timeout-minutes: 60
container:
image: ci.trafficserver.apache.org/proxy-verifier/ubuntu:26.04
options: --security-opt seccomp=unconfined
env:
CC: gcc
CXX: g++
steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Configure
run: >-
cmake --preset dev
-DPV_ENABLE_IO_URING=ON
-DURTEST_SANDBOX=/tmp/proxy-verifier-urtest-io-uring

- name: Build
run: cmake --build --preset dev --parallel

- name: Run Uranium tests
run: ./build/dev/urtest.sh -v -n "$(nproc)" --verifier-io-uring required

- name: Collect Uranium sandbox
if: failure()
run: |
mkdir -p artifacts
if [[ -d /tmp/proxy-verifier-urtest-io-uring ]]; then
tar --create --gzip \
--file="${GITHUB_WORKSPACE}/artifacts/urtest-io-uring-sandbox.tar.gz" \
--directory=/tmp proxy-verifier-urtest-io-uring
fi

- name: Upload Uranium sandbox
if: failure()
uses: actions/upload-artifact@v6
with:
name: urtest-io-uring-sandbox
path: artifacts/urtest-io-uring-sandbox.tar.gz
if-no-files-found: ignore
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ option(PV_ENABLE_ASAN
"Build the verifier binaries with AddressSanitizer instrumentation." OFF)
option(PV_RELEASE_LAYOUT
"Install release artifacts into a platform-specific bindir." OFF)
option(PV_ENABLE_IO_URING
"Use io_uring for socket readiness when liburing is available." ON)
option(
BUILD_PORTABLE
"Enable portable build settings such as conservative Linux amd64 ISA flags."
Expand Down Expand Up @@ -153,6 +155,9 @@ pv_fetch_dependencies()
include(HttpDependencies)
pv_find_http_dependencies(PV_HTTP_DEPENDENCY_TARGETS)

include(IoUring)
pv_find_io_uring(PV_IO_URING_TARGET)

add_subdirectory(src)
string(SHA256 _pv_source_hash "${CMAKE_SOURCE_DIR}")
string(SUBSTRING "${_pv_source_hash}" 0 8 _pv_source_hash)
Expand Down
40 changes: 36 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Table of Contents
* [--tls-secrets-log-file <secrets_log_file_name>](#--tls-secrets-log-file-secrets_log_file_name)
* [--send-buffer-size <size>](#--send-buffer-size-size)
* [--poll-timeout <timeout_ms>](#--poll-timeout-timeout_ms)
* [--io-uring <mode>](#--io-uring-mode)
* [Tools](#tools)
* [Replay Gen <a href="tools/replay-gen.py">replay-gen.py</a>](tools/replay-gen.py)
* [-n,--number &lt;NUMBER&gt;](#-n--number-number)
Expand Down Expand Up @@ -1832,12 +1833,18 @@ presets. The build requires:
* OpenSSL 3.5 or newer development files
* nghttp2 1.60 or newer development files
* nghttp3 0.8 or newer development files
* liburing 2.0 or newer development files on Linux (optional)

CMake fetches pinned `libswoc` and `yaml-cpp` sources during configure. HTTP/3
uses OpenSSL 3.5 or newer for the native QUIC transport and nghttp3 for HTTP/3
framing and QPACK. HTTP/2 uses nghttp2. Install these libraries through the
system package manager before configuring Proxy Verifier. ngtcp2 is not
required because OpenSSL provides the QUIC transport.
required because OpenSSL provides the QUIC transport. On Linux, CMake enables
io_uring socket readiness support when liburing is installed. Builds without
liburing, non-Linux builds, and kernels that cannot initialize io_uring retain
the existing `poll(2)` behavior. Dynamic Linux builds that enable io_uring
support require the liburing shared library at runtime; portable Linux release
binaries link it statically.

For Mac builds, the following brew command can be helpful:

Expand Down Expand Up @@ -1868,9 +1875,9 @@ This places the build-tree binaries under `build/dev/bin`.

The Dockerfiles under `docker/alpine_3.24`, `docker/fedora_44`, and
`docker/ubuntu_26.04` are build and development environments, not deployment
images. Each installs OpenSSL, nghttp2, and nghttp3 from its distribution along
with the Proxy Verifier build, formatting, license-audit, and Uranium/pytest
toolchains.
images. Each installs OpenSSL, nghttp2, nghttp3, and liburing from its
distribution along with the Proxy Verifier build, formatting, license-audit,
and Uranium/pytest toolchains.
GitHub Actions uses only the Ubuntu 26.04 image. Alpine 3.24 is used to build
the portable Linux release binaries, while Fedora 44 provides an additional
current-distribution development environment. Users who prefer Alpine or
Expand Down Expand Up @@ -2355,6 +2362,31 @@ non-blocking sockets with a timeout. By default, this timeout is set to 5
seconds (5,000 milliseconds). This optional argument provides a way to specify a
different timeout in milliseconds for these operations.

#### --io-uring \<mode\>

On Linux builds with liburing, Proxy Verifier uses one process-wide io_uring
to multiplex socket readiness waits. This avoids creating an io_uring for each
connection thread. The HTTP, TLS, and replay state machines remain unchanged,
and Proxy Verifier transparently uses `poll(2)` when io_uring is unavailable.

The mode can be one of:

* `auto` uses io_uring when both the build and running kernel support it, then
falls back to `poll(2)` if initialization is unavailable. This is the
default.
* `off` always uses `poll(2)`. This is useful as the control side of a
production performance comparison using the same binary.
* `required` exits with an error unless io_uring initializes successfully and
does not fall back if the backend later fails. Use this for an io_uring
production canary so kernel security policy or a container seccomp profile
cannot silently turn the test into a `poll(2)` run.

Regardless of the selected mode, zero or negative timeouts use a direct
`poll(2)` call because they do not benefit from asynchronous dispatch.

Set the CMake option `PV_ENABLE_IO_URING=OFF` to omit io_uring support even if
liburing is installed.

## Tools
This section describes how to use some of the scripts under the [tools](tools) directory.

Expand Down
48 changes: 48 additions & 0 deletions cmake/IoUring.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# @file
#
# Copyright 2026, Verizon Media SPDX-License-Identifier: Apache-2.0
#

include_guard(GLOBAL)

function(pv_find_io_uring out_var)
set(_pv_io_uring_target "")
if(PV_ENABLE_IO_URING AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(PV_STATIC_BUILD)
set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(PV_LIBURING QUIET IMPORTED_TARGET GLOBAL "liburing>=2.0")
if(PV_LIBURING_FOUND)
Comment thread
bneradt marked this conversation as resolved.
if(PV_STATIC_BUILD)
if(NOT PV_LIBURING_LINK_LIBRARIES)
message(FATAL_ERROR "Static builds require a static liburing archive")
endif()
foreach(_pv_io_uring_library IN LISTS PV_LIBURING_LINK_LIBRARIES)
if(NOT _pv_io_uring_library MATCHES
"\\${CMAKE_STATIC_LIBRARY_SUFFIX}$")
message(
FATAL_ERROR
"Static builds require a static liburing archive, but pkg-config resolved "
"${_pv_io_uring_library}")
endif()
endforeach()
endif()
set(_pv_io_uring_target PkgConfig::PV_LIBURING)
message(STATUS "Building with io_uring socket readiness support")
else()
message(
STATUS "liburing was not found; using the poll socket readiness backend"
)
endif()
elseif(PV_ENABLE_IO_URING)
message(
STATUS "io_uring is not available on ${CMAKE_SYSTEM_NAME}; using poll")
else()
message(STATUS "io_uring support is disabled; using poll")
endif()

set(${out_var}
"${_pv_io_uring_target}"
PARENT_SCOPE)
endfunction()
1 change: 1 addition & 0 deletions docker/alpine_3.24/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ RUN apk add --no-cache \
gcompat \
git \
libffi-dev \
liburing-dev \
linux-headers \
nghttp2-dev \
nghttp2-static \
Expand Down
1 change: 1 addition & 0 deletions docker/fedora_44/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dnf install -y \
libffi-devel \
libnghttp2-devel \
libnghttp3-devel \
liburing-devel \
openssl-devel \
pkgconf-pkg-config \
procps-ng \
Expand Down
1 change: 1 addition & 0 deletions docker/ubuntu_26.04/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ apt-get install -y --no-install-recommends \
libnghttp2-dev \
libnghttp3-dev \
libssl-dev \
liburing-dev \
libzstd-dev \
lbzip2 \
pkg-config \
Expand Down
7 changes: 6 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(PV_CORE_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/core/Localizer.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/core/ProxyVerifier.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/core/proxy_protocol_util.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/core/socket_io.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/core/verification.cc"
"${CMAKE_CURRENT_SOURCE_DIR}/core/YamlParser.cc")

Expand All @@ -29,7 +30,11 @@ target_compile_definitions(verifier-core
target_include_directories(verifier-core PUBLIC "${PROJECT_SOURCE_DIR}/src")
target_link_libraries(
verifier-core PUBLIC Threads::Threads libswoc-static yaml-cpp::yaml-cpp
${PV_HTTP_DEPENDENCY_TARGETS})
${PV_HTTP_DEPENDENCY_TARGETS} ${PV_IO_URING_TARGET})

if(PV_IO_URING_TARGET)
target_compile_definitions(verifier-core PRIVATE PV_HAVE_LIBURING=1)
endif()

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(verifier-core PUBLIC stdc++fs)
Expand Down
19 changes: 19 additions & 0 deletions src/client/verifier-client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "core/https.h"
#include "core/ProxyVerifier.h"
#include "core/YamlParser.h"
#include "core/socket_io.h"

#include <atomic>
#include <chrono>
Expand Down Expand Up @@ -747,6 +748,16 @@ Engine::parse_args()
Errata errata;
auto args{arguments.get("run")};

auto const io_uring_arg = arguments.get("io-uring");
auto &&[socket_io_backend, socket_io_errata] =
configure_socket_io(io_uring_arg ? io_uring_arg[0] : "auto");
static_cast<void>(socket_io_backend);
errata.note(std::move(socket_io_errata));
if (!errata.is_ok()) {
process_exit_code = 1;
return false;
Comment thread
bneradt marked this conversation as resolved.
}

if (args.size() < 1) {
errata.note(S_ERROR, R"("run" command requires a directory path as an argument.)");
process_exit_code = 1;
Expand Down Expand Up @@ -1396,6 +1407,14 @@ main(int /* argc */, char const *argv[])
"",
1,
"")
.add_option(
"--io-uring",
"",
"Select the socket readiness backend: auto, off, or required. "
"Default is auto.",
"",
1,
"auto")
.add_option(
"--keys",
"-k",
Expand Down
5 changes: 2 additions & 3 deletions src/core/http.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#include "core/http.h"
#include "core/socket_io.h"
#include "core/verification.h"
#include "core/ProxyVerifier.h"

Expand Down Expand Up @@ -1394,9 +1395,7 @@ Session::poll_for_data_on_socket(chrono::milliseconds timeout, short events)
if (is_closed()) {
return {-1, Errata(S_DIAG, "Poll called on a closed connection.")};
}
auto const timeout_ms = std::max<chrono::milliseconds::rep>(timeout.count(), 0);
struct pollfd pfd = {.fd = _fd, .events = events, .revents = 0};
return ::poll(&pfd, 1, timeout_ms);
return poll_for_socket_io(_fd, timeout, events);
}

swoc::Rv<int>
Expand Down
5 changes: 3 additions & 2 deletions src/core/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -883,8 +883,9 @@ struct Ssn
};

/** A session reader.
* This is essentially a wrapper around a socket to support use of @c poll on
* the socket. The goal is to enable a read operation that waits for data but
* This is essentially a wrapper around a socket with timed readiness waits.
* Linux builds use io_uring when it is available and otherwise fall back to
* @c poll. The goal is to enable a read operation that waits for data but
* returns as soon as any data is available.
*/
class Session
Expand Down
Loading