From cd6a0890904a47e82857886fe2a8df458571ea31 Mon Sep 17 00:00:00 2001 From: wokron Date: Tue, 18 Aug 2026 16:13:24 +0800 Subject: [PATCH 1/3] add scsi bsg support --- include/condy/cqe_handler.hpp | 41 +++++++++++++++++++++++++++++++++++ module/condy.cppm | 2 ++ 2 files changed, 43 insertions(+) diff --git a/include/condy/cqe_handler.hpp b/include/condy/cqe_handler.hpp index 4f7ae694..8679c69d 100644 --- a/include/condy/cqe_handler.hpp +++ b/include/condy/cqe_handler.hpp @@ -65,6 +65,47 @@ struct NVMePassthruCQEHandler { } }; +/** + * @brief Result for SCSI BSG passthrough commands. + * @details Contains the raw res2 from the CQE. Use the member functions + * to extract individual fields. + */ +struct SCSIBsgResult { + /** @brief Raw res2 from the CQE. */ + uint64_t res2; + + /** @brief Extract SCSI device status byte. */ + uint8_t device_status() noexcept { return res2 & 0xff; } + + /** @brief Extract driver status. */ + uint8_t driver_status() noexcept { return (res2 >> 8) & 0xff; } + + /** @brief Extract host status. */ + uint8_t host_status() noexcept { return (res2 >> 16) & 0xff; } + + /** @brief Extract sense data length. */ + uint8_t sense_len() noexcept { return (res2 >> 24) & 0xff; } + + /** @brief Extract residual transfer length. */ + uint32_t resid_len() noexcept { return res2 >> 32; } +}; + +/** + * @brief A CQE handler for SCSI BSG passthrough commands that extracts the + * SCSI status and result from the CQE. + * @return std::pair A pair containing the result of + * the operation (the value of `cqe->res`) and the raw SCSI status. + */ +struct SCSIBsgPassthruCQEHandler { + std::pair operator()(io_uring_cqe *cqe) noexcept { + assert( + detail::Context::current().runtime()->ring_internal().check_cqe32( + cqe) && + "Expected big CQE for SCSI BSG passthrough"); + return {cqe->res, SCSIBsgResult{cqe->big_cqe[0]}}; + } +}; + #if CONDY_URING_VERSION_GE(2, 12) // >= 2.12 /** * @brief Result for TX timestamp operations, containing timestamp information diff --git a/module/condy.cppm b/module/condy.cppm index cb167d00..cbc90cc4 100644 --- a/module/condy.cppm +++ b/module/condy.cppm @@ -63,6 +63,8 @@ using condy::ZeroCopyRxDMABufArea; // cqe_handler.hpp using condy::NVMePassthruCQEHandler; +using condy::SCSIBsgPassthruCQEHandler; +using condy::SCSIBsgResult; using condy::SelectBufferCQEHandler; using condy::SimpleCQEHandler; #if CONDY_URING_VERSION_GE(2, 12) // >= 2.12 From e209832427f8f3d19e33ef1ea330e3860d102de2 Mon Sep 17 00:00:00 2001 From: wokron Date: Tue, 18 Aug 2026 16:13:30 +0800 Subject: [PATCH 2/3] add tests --- tests/helpers.hpp | 26 +++++++ tests/test_async_operations.4.cpp | 109 ++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/tests/helpers.hpp b/tests/helpers.hpp index 6cf4977b..69922da6 100644 --- a/tests/helpers.hpp +++ b/tests/helpers.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -231,4 +232,29 @@ inline auto my_cmd_nvme_write(Fd fd, const void *buf, size_t buf_size, #endif } +template +inline auto my_cmd_scsi_test_unit_ready(Fd fd) { + static const uint8_t cdb[6] = {0}; // TEST UNIT READY (opcode 0x00) + auto cmd_func = [=](io_uring_sqe *sqe) { + struct bsg_uring_cmd *cmd = (struct bsg_uring_cmd *)sqe->cmd; + memset(cmd, 0, sizeof(struct bsg_uring_cmd)); + cmd->protocol = BSG_PROTOCOL_SCSI; + cmd->subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + cmd->request = (__u64)(uintptr_t)cdb; + cmd->request_len = 6; + }; +#if CONDY_URING_VERSION_GE(2, 13) // >= 2.13 + if constexpr (SQE128) { + return condy::async_uring_cmd128( + 0, fd, cmd_func); + } else { + return condy::async_uring_cmd( + 0, fd, cmd_func); + } +#else + return condy::async_uring_cmd( + 0, fd, cmd_func); +#endif +} + } // namespace diff --git a/tests/test_async_operations.4.cpp b/tests/test_async_operations.4.cpp index 5d0cb001..1c368831 100644 --- a/tests/test_async_operations.4.cpp +++ b/tests/test_async_operations.4.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -390,6 +391,60 @@ TEST_CASE( close(fd); } +TEST_CASE("test async_operations - test uring_cmd - scsi bsg - basic") { + const char *bsg_device_path = std::getenv("CONDY_TEST_BSG_DEVICE_PATH"); + if (bsg_device_path == nullptr) { + MESSAGE("CONDY_TEST_BSG_DEVICE_PATH not set, skipping"); + return; + } + + int fd = open(bsg_device_path, O_RDWR); + REQUIRE(fd >= 0); + + condy::Runtime runtime( + condy::RuntimeOptions().enable_sqe128().enable_cqe32()); + + auto func = [&]() -> condy::Coro { + auto [status, result] = co_await my_cmd_scsi_test_unit_ready(fd); + REQUIRE(status == 0); + REQUIRE(result.device_status() == 0); + REQUIRE(result.host_status() == 0); + }; + condy::sync_wait(runtime, func()); + + close(fd); +} + +TEST_CASE("test async_operations - test uring_cmd - scsi bsg - fixed fd") { + const char *bsg_device_path = std::getenv("CONDY_TEST_BSG_DEVICE_PATH"); + if (bsg_device_path == nullptr) { + MESSAGE("CONDY_TEST_BSG_DEVICE_PATH not set, skipping"); + return; + } + + int fd = open(bsg_device_path, O_RDWR); + REQUIRE(fd >= 0); + + condy::Runtime runtime( + condy::RuntimeOptions().enable_sqe128().enable_cqe32()); + + auto func = [&]() -> condy::Coro { + auto &fd_table = condy::current_runtime().fd_table(); + fd_table.init(1); + int r = co_await condy::async_files_update(&fd, 1, 0); + REQUIRE(r == 1); + + auto [status, result] = + co_await my_cmd_scsi_test_unit_ready(condy::fixed(0)); + REQUIRE(status == 0); + REQUIRE(result.device_status() == 0); + REQUIRE(result.host_status() == 0); + }; + condy::sync_wait(runtime, func()); + + close(fd); +} + #if CONDY_URING_VERSION_GE(2, 12) // >= 2.12 TEST_CASE("test async_operations - test uring_cmd_multishot - tx timestamp") { int r; @@ -577,6 +632,60 @@ TEST_CASE( close(fd); } + +TEST_CASE("test async_operations - test uring_cmd128 - scsi bsg - basic") { + const char *bsg_device_path = std::getenv("CONDY_TEST_BSG_DEVICE_PATH"); + if (bsg_device_path == nullptr) { + MESSAGE("CONDY_TEST_BSG_DEVICE_PATH not set, skipping"); + return; + } + + int fd = open(bsg_device_path, O_RDWR); + REQUIRE(fd >= 0); + + condy::Runtime runtime( + condy::RuntimeOptions().enable_sqe_mixed().enable_cqe_mixed()); + + auto func = [&]() -> condy::Coro { + auto [status, result] = co_await my_cmd_scsi_test_unit_ready(fd); + REQUIRE(status == 0); + REQUIRE(result.device_status() == 0); + REQUIRE(result.host_status() == 0); + }; + condy::sync_wait(runtime, func()); + + close(fd); +} + +TEST_CASE("test async_operations - test uring_cmd128 - scsi bsg - fixed fd") { + const char *bsg_device_path = std::getenv("CONDY_TEST_BSG_DEVICE_PATH"); + if (bsg_device_path == nullptr) { + MESSAGE("CONDY_TEST_BSG_DEVICE_PATH not set, skipping"); + return; + } + + int fd = open(bsg_device_path, O_RDWR); + REQUIRE(fd >= 0); + + condy::Runtime runtime( + condy::RuntimeOptions().enable_sqe_mixed().enable_cqe_mixed()); + + auto func = [&]() -> condy::Coro { + auto &fd_table = condy::current_runtime().fd_table(); + fd_table.init(1); + int r = co_await condy::async_files_update(&fd, 1, 0); + REQUIRE(r == 1); + + auto [status, result] = + co_await my_cmd_scsi_test_unit_ready(condy::fixed(0)); + REQUIRE(status == 0); + REQUIRE(result.device_status() == 0); + REQUIRE(result.host_status() == 0); + }; + condy::sync_wait(runtime, func()); + + close(fd); +} #endif #if CONDY_URING_VERSION_GE(2, 5) // >= 2.5 From cf04676b48b1a02258d53503690d256b801844a8 Mon Sep 17 00:00:00 2001 From: wokron Date: Tue, 18 Aug 2026 17:26:30 +0800 Subject: [PATCH 3/3] update ci to test scsi --- .github/workflows/ci-latest-kernel.yml | 2 +- scripts/light-kernel.sh | 7 +++++++ scripts/vm-run.sh | 11 ++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-latest-kernel.yml b/.github/workflows/ci-latest-kernel.yml index 9b19d2e7..f1f44022 100644 --- a/.github/workflows/ci-latest-kernel.yml +++ b/.github/workflows/ci-latest-kernel.yml @@ -82,7 +82,7 @@ jobs: timeout-minutes: 15 run: | bash ./scripts/vm-run.sh \ - -c "CONDY_TEST_NVME_DEVICE_PATH=/dev/nvme0n1 CONDY_TEST_NVME_NG_DEVICE_PATH=/dev/ng0n1 ./tests" \ + -c "CONDY_TEST_NVME_DEVICE_PATH=/dev/nvme0n1 CONDY_TEST_NVME_NG_DEVICE_PATH=/dev/ng0n1 CONDY_TEST_BSG_DEVICE_PATH=/dev/bsg/0:0:0:0 ./tests" \ ./.cache/bzImage ./build/tests/tests \ 2>&1 | tee vm-run.log if grep -q "SUCCESS!" vm-run.log; then diff --git a/scripts/light-kernel.sh b/scripts/light-kernel.sh index da67ce7b..141b10a8 100644 --- a/scripts/light-kernel.sh +++ b/scripts/light-kernel.sh @@ -60,6 +60,13 @@ make kvm_guest.config ./scripts/config --enable CONFIG_NVME_CORE ./scripts/config --enable CONFIG_BLK_DEV_NVME ./scripts/config --enable CONFIG_NVME_MULTIPATH +# Configure SCSI and BSG support +./scripts/config --enable CONFIG_SCSI +./scripts/config --enable CONFIG_BLK_DEV_SD +./scripts/config --enable CONFIG_SCSI_LOWLEVEL +./scripts/config --enable CONFIG_SCSI_VIRTIO +./scripts/config --enable CONFIG_VIRTIO_PCI +./scripts/config --enable CONFIG_BLK_DEV_BSG # Use olddefconfig to set new options to default values make olddefconfig diff --git a/scripts/vm-run.sh b/scripts/vm-run.sh index 2a68fe6e..400d4672 100644 --- a/scripts/vm-run.sh +++ b/scripts/vm-run.sh @@ -88,6 +88,13 @@ trap "rm -f '$SSD_IMG'; rm -rf $TEMP_DIR" EXIT SSD_DRIVE="-drive file=$SSD_IMG,if=none,id=ssd0,format=raw,cache=none,aio=io_uring" SSD_DEVICE="-device nvme,drive=ssd0,serial=ssd0" +# Simulate SCSI disk for BSG testing +SCSI_IMG="/dev/shm/vm-scsi.img.$$" +truncate -s 16M "$SCSI_IMG" +trap "rm -f '$SSD_IMG' '$SCSI_IMG'; rm -rf $TEMP_DIR" EXIT +SCSI_DRIVE="-drive file=$SCSI_IMG,if=none,id=scsi0,format=raw" +SCSI_DEVICE="-device virtio-scsi-pci,id=scsi0 -device scsi-hd,drive=scsi0,bus=scsi0.0" + KVM_FLAG="" if [ "$KVM_ENABLED" = true ]; then KVM_FLAG="-enable-kvm" @@ -115,4 +122,6 @@ qemu-system-x86_64 \ -append "$KERNEL_ARGS" \ -nographic \ $SSD_DRIVE \ - $SSD_DEVICE + $SSD_DEVICE \ + $SCSI_DRIVE \ + $SCSI_DEVICE