Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-latest-kernel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions include/condy/cqe_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int32_t, SCSIBsgResult> A pair containing the result of
* the operation (the value of `cqe->res`) and the raw SCSI status.
*/
struct SCSIBsgPassthruCQEHandler {
std::pair<int32_t, SCSIBsgResult> 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
Expand Down
2 changes: 2 additions & 0 deletions module/condy.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions scripts/light-kernel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 10 additions & 1 deletion scripts/vm-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -115,4 +122,6 @@ qemu-system-x86_64 \
-append "$KERNEL_ARGS" \
-nographic \
$SSD_DRIVE \
$SSD_DEVICE
$SSD_DEVICE \
$SCSI_DRIVE \
$SCSI_DEVICE
26 changes: 26 additions & 0 deletions tests/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cstring>
#include <doctest.h>
#include <fcntl.h>
#include <linux/bsg.h>
#include <linux/futex.h>
#include <linux/nvme_ioctl.h>
#include <netinet/in.h>
Expand Down Expand Up @@ -231,4 +232,29 @@ inline auto my_cmd_nvme_write(Fd fd, const void *buf, size_t buf_size,
#endif
}

template <bool SQE128 = false, condy::FdLike Fd>
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<condy::SCSIBsgPassthruCQEHandler>(
0, fd, cmd_func);
} else {
return condy::async_uring_cmd<condy::SCSIBsgPassthruCQEHandler>(
0, fd, cmd_func);
}
#else
return condy::async_uring_cmd<condy::SCSIBsgPassthruCQEHandler>(
0, fd, cmd_func);
#endif
}

} // namespace
109 changes: 109 additions & 0 deletions tests/test_async_operations.4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cstring>
#include <doctest.h>
#include <fcntl.h>
#include <linux/bsg.h>
#include <linux/errqueue.h>
#include <linux/futex.h>
#include <linux/net_tstamp.h>
Expand Down Expand Up @@ -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<void> {
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<void> {
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;
Expand Down Expand Up @@ -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<void> {
auto [status, result] = co_await my_cmd_scsi_test_unit_ready<true>(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<void> {
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<true>(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
Expand Down
Loading