From 01ea8f22eaf670ad0cfa5a1facd554ab364bfdcd Mon Sep 17 00:00:00 2001 From: Motoyuki Kimura Date: Thu, 31 Jul 2025 20:52:28 +0900 Subject: [PATCH] ci: add kernel-version-test workflow for io_uring tests (#7486) --- .github/workflows/ci.yml | 25 +++++ .../workflows/uring-kernel-version-test.yml | 103 ++++++++++++++++++ tokio/tests/fs_try_exists.rs | 2 +- 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/uring-kernel-version-test.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d895a3c0f..71b3ff84e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1231,3 +1231,28 @@ jobs: echo 'Please remove trailing whitespace from these lines.' exit 1 fi + + get-latest-kernel-version: + runs-on: ubuntu-latest + outputs: + kernel_version: ${{ steps.fetch.outputs.kernel_version }} + steps: + - name: Fetch latest stable kernel + id: fetch + run: | + KERNEL_VERSION=$(curl -s https://www.kernel.org/releases.json | jq -r '.latest_stable.version') + echo "kernel_version=$KERNEL_VERSION" >> $GITHUB_OUTPUT + + test-io-uring-on-specific-kernel-versions: + name: Test io_uring on Linux ${{ matrix.kernel_version }} + needs: [get-latest-kernel-version, basics] + strategy: + matrix: + kernel_version: + # A latest stable kernel version + - ${{ needs.get-latest-kernel-version.outputs.kernel_version }} + # A kernel version that doesn't support io_uring + - '4.19.325' + uses: ./.github/workflows/uring-kernel-version-test.yml + with: + kernel_version: ${{ matrix.kernel_version }} diff --git a/.github/workflows/uring-kernel-version-test.yml b/.github/workflows/uring-kernel-version-test.yml new file mode 100644 index 000000000..a0dfd89bf --- /dev/null +++ b/.github/workflows/uring-kernel-version-test.yml @@ -0,0 +1,103 @@ +name: Uring Kernel Version Test + +on: + workflow_call: + inputs: + kernel_version: + description: 'Version of the Linux kernel to build' + required: true + type: string + +jobs: + build: + runs-on: ubuntu-latest + env: + KERNEL_VERSION: ${{ inputs.kernel_version }} + steps: + - uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential bison flex libssl-dev libelf-dev \ + qemu-system-x86 busybox-static cpio xz-utils wget + + - name: Cache Linux source + id: cache-kernel + uses: actions/cache@v4 + with: + path: linux-${{ env.KERNEL_VERSION }} + key: kernel-${{ env.KERNEL_VERSION }} + + - name: Download & build Linux kernel + if: steps.cache-kernel.outputs.cache-hit != 'true' + run: | + MAJOR=${KERNEL_VERSION%%.*} + wget https://cdn.kernel.org/pub/linux/kernel/v${MAJOR}.x/linux-${KERNEL_VERSION}.tar.xz + tar xf linux-${KERNEL_VERSION}.tar.xz + cd linux-${KERNEL_VERSION} + make defconfig + make -j$(nproc) + + - name: Generate test binaries with io_uring enabled + run: | + # Build both integration (tokio/tests/) and unit (e.g., tokio/src/fs/file/tests.rs) tests with io_uring enabled + rustup target add x86_64-unknown-linux-musl + RUSTFLAGS="--cfg tokio_uring" \ + cargo test -p tokio --features full \ + --target x86_64-unknown-linux-musl --test 'fs*' --lib --no-run + + - name: Prepare initramfs + tests binaries + run: | + set -e + rm -rf initramfs + mkdir -p initramfs/{bin,bin/tests,sbin,proc,sys,tmp} + + # Copy test binaries into initramfs + for bin in target/x86_64-unknown-linux-musl/debug/deps/{fs_*,tokio-*}; do + if [ -f "$bin" ] && [ -x "$bin" ]; then + cp "$bin" initramfs/bin/tests + fi + done + + # Add BusyBox & symlinks + cp /usr/bin/busybox initramfs/bin/ + for cmd in sh mount uname true sleep; do ln -sf busybox initramfs/bin/$cmd; done + ln -sf ../bin/busybox initramfs/sbin/poweroff + + # Generate init script + cat > initramfs/init << 'EOF' + #!/bin/sh + set -e + mkdir -p /dev + # create device nodes, as some tests require them + mknod /dev/null c 1 3 + mknod /dev/zero c 1 5 + mknod /dev/tty c 5 0 + mount -t proc proc /proc + mount -t sysfs sysfs /sys + mkdir -p /tmp && mount -t tmpfs -o mode=1777 tmpfs /tmp + for f in /bin/tests/*; do RUST_BACKTRACE=1 "$f" ; done + EOF + chmod +x initramfs/init + + # Pack into a CPIO archive + (cd initramfs && find . -print0 \ + | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz) + + - name: Run tests in QEMU + run: | + qemu-system-x86_64 \ + -kernel linux-${{ env.KERNEL_VERSION }}/arch/x86/boot/bzImage \ + -initrd initramfs.cpio.gz \ + -append "console=ttyS0 rootfstype=ramfs panic=1" \ + -nographic -no-reboot -m 1024 -action panic=exit-failure 2>&1 | tee qemu-output.log + + # qemu always exits with 0, so we check if the tests passed by using grep. + if grep -q "test result: FAILED" qemu-output.log; then + echo "tests failed (QEMU exited abnormally)" + exit 1 + else + echo "all tests passed" + fi diff --git a/tokio/tests/fs_try_exists.rs b/tokio/tests/fs_try_exists.rs index 5c8553417..75cd223fa 100644 --- a/tokio/tests/fs_try_exists.rs +++ b/tokio/tests/fs_try_exists.rs @@ -16,7 +16,7 @@ async fn try_exists() { assert!(fs::try_exists(existing_path).await.unwrap()); assert!(!fs::try_exists(nonexisting_path).await.unwrap()); // FreeBSD root user always has permission to stat. - #[cfg(all(unix, not(target_os = "freebsd")))] + #[cfg(all(unix, not(any(target_os = "freebsd", tokio_uring))))] { use std::os::unix::prelude::PermissionsExt; let permission_denied_directory_path = dir.path().join("baz");