Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/ci-latest-kernel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ jobs:
-DUBLKCPP_BUILD_TESTS=ON \
-DUBLKCPP_BUILD_UBLKCTL=ON \
-DUBLKCPP_TESTS_STATIC_LINK=ON \
-DCONDY_LINK_STDEXEC=ON \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_BUILD_TYPE=Release
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
-DUBLKCPP_BUILD_TESTS=ON \
-DUBLKCPP_BUILD_UBLKCTL=ON \
-DUBLKCPP_BUILD_EXAMPLES=ON \
-DUBLKCPP_EXECUTION_BACKEND=stdexec \
-DCONDY_LINK_STDEXEC=ON \
$SANITIZER_FLAG \
-DCMAKE_C_COMPILER=${{matrix.compiler.cc}} \
-DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci-static-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
cmake -B ${{github.workspace}}/build \
-DUBLKCPP_BUILD_TESTS=ON \
-DUBLKCPP_BUILD_UBLKCTL=ON \
-DCONDY_LINK_STDEXEC=ON \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_BUILD_TYPE=Debug \
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci-toolchain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
backend: [ "stdexec", "beman" ]
backend: [ STDEXEC, BEMAN ]
compiler:
- { cc: gcc, cxx: g++ }
- { cc: clang, cxx: clang++ }
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
-DUBLKCPP_BUILD_TESTS=ON \
-DUBLKCPP_BUILD_UBLKCTL=ON \
-DUBLKCPP_BUILD_EXAMPLES=ON \
-DUBLKCPP_EXECUTION_BACKEND=${{ matrix.backend }} \
-DCONDY_LINK_${{ matrix.backend }}=ON \
-DCMAKE_C_COMPILER=${{matrix.compiler.cc}} \
-DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} \
-DCMAKE_BUILD_TYPE=Debug
Expand Down
18 changes: 2 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.24)

# Version is defined once in the version header.
set(UBLKCPP_VERSION_HEADER include/ublk/version.hpp)
Expand All @@ -20,8 +20,6 @@ option(UBLKCPP_USE_URING_CMD128 "Use IORING_OP_URING_CMD128 for control commands
option(UBLKCPP_TESTS_STATIC_LINK "Use static linking for tests" OFF)
option(UBLKCPP_TESTS_ASAN "Enable address and undefined behavior sanitizers for tests" OFF)
option(UBLKCPP_TESTS_TSAN "Enable thread sanitizer for tests" OFF)
set(UBLKCPP_EXECUTION_BACKEND "stdexec" CACHE STRING
"std::execution implementation used via Condy: stdexec (default) or beman")

include(FetchContent)

Expand All @@ -47,23 +45,11 @@ FetchContent_Declare(

add_library(ublkcpp INTERFACE)
target_include_directories(ublkcpp INTERFACE include)
target_compile_features(ublkcpp INTERFACE cxx_std_23) # TODO: cxx_std_26
target_compile_features(ublkcpp INTERFACE cxx_std_20)
if(UBLKCPP_USE_URING_CMD128)
target_compile_definitions(ublkcpp INTERFACE UBLKCPP_USE_URING_CMD128)
endif()

if(UBLKCPP_EXECUTION_BACKEND STREQUAL "beman")
set(CONDY_ENABLE_STDEXEC OFF)
set(CONDY_ENABLE_BEMAN ON)
elseif(UBLKCPP_EXECUTION_BACKEND STREQUAL "stdexec")
set(CONDY_ENABLE_STDEXEC ON)
set(CONDY_ENABLE_BEMAN OFF)
else()
message(FATAL_ERROR
"UBLKCPP_EXECUTION_BACKEND must be 'stdexec' or 'beman', got '"
"${UBLKCPP_EXECUTION_BACKEND}'")
endif()
message(STATUS "ublkcpp: execution backend: ${UBLKCPP_EXECUTION_BACKEND}")
add_subdirectory(third_party/condy)
target_link_libraries(ublkcpp INTERFACE condy)

Expand Down
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ublk-cpp

![C++](https://img.shields.io/badge/C++-26-blue)
![C++](https://img.shields.io/badge/C++-20-blue)
![License](https://img.shields.io/github/license/condy-cpp/ublk-cpp)
![Release](https://img.shields.io/github/v/release/condy-cpp/ublk-cpp)
![Stars](https://img.shields.io/github/stars/condy-cpp/ublk-cpp?style=social)
Expand All @@ -10,21 +10,17 @@
![CI (Static Check)](https://github.com/condy-cpp/ublk-cpp/actions/workflows/ci-static-check.yml/badge.svg?branch=master)
![Deploy Docs](https://github.com/condy-cpp/ublk-cpp/actions/workflows/deploy-docs.yml/badge.svg?branch=master)

ublk-cpp is a C++ library for writing [ublk servers](https://docs.kernel.org/block/ublk.html), targeting the C++26 `std::execution` sender model:
ublk-cpp is a C++ library for writing [ublk servers](https://docs.kernel.org/block/ublk.html), built on the `std::execution` sender model:

- **Comprehensive ublk Support**
Full coverage of the ublk userspace interface — device lifecycle management, per-queue I/O loops, and advanced features such as user recovery and shm buffer registration.

- **Full io_uring Ecosystem**
Built on top of [Condy](https://github.com/condy-cpp/condy), all io_uring operations can be used directly inside your ublk server.

- **C++26 Sender Model**
- **Sender Model**
The API is built on `std::execution` senders — composable with standard algorithms and interoperable with any asynchronous driver.

> [!NOTE]
> This repository is experimental and will not reach a stable state until
> `std::execution` (C++26) is finalized.

## Documentation

- **[Online Docs (GitHub Pages)](https://condy-cpp.github.io/ublk-cpp/)**
Expand Down
45 changes: 25 additions & 20 deletions bin/ublkctl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@
#include <cstdlib>
#include <fcntl.h>
#include <filesystem>
#include <format>
#include <iostream>
#include <print>
#include <sched.h>
#include <stdexcept>
#include <string>
#include <ublk.hpp>
#include <unistd.h>
#include <utility>
#include <variant>

namespace ex = condy::detail::ex;
namespace ex = ublk::detail::ex;

struct AddCmd {
uint32_t dev_id = -1;
Expand Down Expand Up @@ -127,18 +128,20 @@ std::string flags_str(uint64_t flags) noexcept {

void dump_dev_info(uint32_t dev_id, const ublksrv_ctrl_dev_info &info,
const ublk_params &params) {
std::println("dev id {}: nr_hw_queues {} queue_depth {} block size {} "
"dev_capacity {}",
dev_id, info.nr_hw_queues, info.queue_depth,
1U << params.basic.logical_bs_shift, params.basic.dev_sectors);
std::println("\tmax rq size {} daemon pid {} state {}",
info.max_io_buf_bytes, info.ublksrv_pid,
state_str(info.state));
std::println("\tflags 0x{:x} [{}]", info.flags, flags_str(info.flags));
std::println("\tublkc: {}:{} ublkb: {}:{} owner: {}:{}",
params.devt.char_major, params.devt.char_minor,
params.devt.disk_major, params.devt.disk_minor, info.owner_uid,
info.owner_gid);
std::cout << std::format("dev id {}: nr_hw_queues {} queue_depth {} block "
"size {} dev_capacity {}\n",
dev_id, info.nr_hw_queues, info.queue_depth,
1U << params.basic.logical_bs_shift,
params.basic.dev_sectors);
std::cout << std::format("\tmax rq size {} daemon pid {} state {}\n",
info.max_io_buf_bytes, info.ublksrv_pid,
state_str(info.state));
std::cout << std::format("\tflags 0x{:x} [{}]\n", info.flags,
flags_str(info.flags));
std::cout << std::format("\tublkc: {}:{} ublkb: {}:{} owner: {}:{}\n",
params.devt.char_major, params.devt.char_minor,
params.devt.disk_major, params.devt.disk_minor,
info.owner_uid, info.owner_gid);
}

std::string cpu_str(const cpu_set_t &cpuset) noexcept {
Expand Down Expand Up @@ -171,7 +174,8 @@ ex::task<void> dump_queue_affinity(int ctrl_fd,
for (uint16_t q_id = 0; q_id < info.nr_hw_queues; q_id++) {
cpu_set_t cpuset;
co_await ublk::get_queue_affinity(ctrl_fd, info.dev_id, q_id, &cpuset);
std::println("\tqueue {}: affinity({})", q_id, cpu_str(cpuset));
std::cout << std::format("\tqueue {}: affinity({})\n", q_id,
cpu_str(cpuset));
}
}

Expand Down Expand Up @@ -253,10 +257,11 @@ ex::task<void> run_cmd(int ctrl_fd, const FeaturesCmd &) {
uint64_t features = 0;
co_await ublk::get_features(ctrl_fd, &features);

std::println("ublk_drv features: 0x{:x}", features);
std::cout << std::format("ublk_drv features: 0x{:x}\n", features);
for (const auto &e : FLAG_TABLE) {
if (features & e.flag)
std::println("\t{:<20s}: 0x{:x}", e.short_name, e.flag);
std::cout << std::format("\t{:<20s}: 0x{:x}\n", e.short_name,
e.flag);
}
}

Expand Down Expand Up @@ -367,7 +372,7 @@ Cmd parse_args(int argc, char *argv[]) {
} else if (app.got_subcommand("quiesce")) {
return quiesce_opts;
} else {
std::unreachable();
throw std::runtime_error("unhandled subcommand");
}
}

Expand All @@ -391,10 +396,10 @@ int main(int argc, char *argv[]) noexcept(false) {
std::visit([&](const auto &c) { return run_cmd(ctrl_fd, c); },
cmd)));
} catch (const std::system_error &e) {
std::println(std::cerr, "ublkctl: {}", e.what());
std::cerr << std::format("ublkctl: {}\n", e.what());
return e.code().value();
} catch (const std::exception &e) {
std::println(std::cerr, "ublkctl: {}", e.what());
std::cerr << std::format("ublkctl: {}\n", e.what());
return 1;
}
return 0;
Expand Down
2 changes: 1 addition & 1 deletion docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ PROJECT_NUMBER = @UBLKCPP_VERSION_STRING@
# for a project that appears at the top of each page and should give viewers a
# quick idea about the purpose of the project. Keep the description short.

PROJECT_BRIEF = A ublk library targeting C++26 std::execution.
PROJECT_BRIEF = A ublk library for std::execution.

# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
Expand Down
8 changes: 3 additions & 5 deletions docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
@brief How to build and integrate ublk-cpp in your project.

> [!NOTE]
> ublk-cpp currently depends on condy's **experimental execution integration**, which can be backed by either **stdexec** (default) or **beman/execution**. Once `std::execution` is finalized in C++26, it will migrate to the standard library implementation.
> ublk-cpp requires condy with `std::execution` support. Condy uses the standard library implementation when available, or a fetched backend ([stdexec](https://github.com/NVIDIA/stdexec) or [beman/execution](https://github.com/bemanproject/execution)) enabled through condy's `CONDY_LINK_STDEXEC` / `CONDY_LINK_BEMAN` options.

## Using ublk-cpp as a Submodule

Expand Down Expand Up @@ -42,7 +42,7 @@ target_link_libraries(my_app PRIVATE ublkcpp)

Condy fetches and **statically links liburing** by default (`CONDY_LINK_LIBURING=ON`). To use the liburing installed on your system instead, configure with `CONDY_LINK_LIBURING=OFF`.

ublk-cpp currently also fetches and depends on **[stdexec](https://github.com/NVIDIA/stdexec)**, or **[beman/execution](https://github.com/bemanproject/execution)** when enabled via the `UBLKCPP_EXECUTION_BACKEND` option. Once `std::execution` is finalized in C++26, this dependency is expected to be replaced by the standard library implementation.
ublk-cpp requires condy with `std::execution` support. When the standard library provides it, condy detects it automatically. Otherwise enable a fetched backend with `CONDY_LINK_STDEXEC=ON` ([stdexec](https://github.com/NVIDIA/stdexec)) or `CONDY_LINK_BEMAN=ON` ([beman/execution](https://github.com/bemanproject/execution)).

## Building

Expand All @@ -55,15 +55,13 @@ ublk-cpp provides CMake options to build tests, the `ublkctl` tool, examples, an
| `UBLKCPP_BUILD_EXAMPLES` | Build examples | OFF |
| `UBLKCPP_BUILD_DOCS` | Build Doxygen documentation | OFF |
| `UBLKCPP_USE_URING_CMD128` | Use `IORING_OP_URING_CMD128` for control commands | ON |
| `UBLKCPP_TESTS_STATIC_LINK` | Use static linking for tests | OFF |
| `UBLKCPP_TESTS_ASAN` | Enable ASan/UBSan for tests | OFF |
| `UBLKCPP_EXECUTION_BACKEND` | `std::execution` implementation used via Condy: `stdexec` or `beman` | `stdexec` |

```bash
cmake -B build -S . \
-DUBLKCPP_BUILD_TESTS=ON \
-DUBLKCPP_BUILD_UBLKCTL=ON \
-DUBLKCPP_BUILD_EXAMPLES=ON \
-DCONDY_LINK_STDEXEC=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
```
Expand Down
10 changes: 6 additions & 4 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ublk is a kernel framework for implementing block device drivers in userspace. I
The runtime is represented by `condy::Runtime`, and a scheduler object can be obtained via `condy::get_scheduler()`. All interfaces provided by ublk-cpp are required to run on that scheduler.

```cpp
namespace ex = std::execution; // or stdexec / beman::execution
// sqe128 is required for ublk control cmd
condy::Runtime runtime(condy::RuntimeOptions().enable_sqe128());
std::jthread loop([&]() { runtime.run(); });
Expand Down Expand Up @@ -180,15 +181,16 @@ Then run `ublk::daemon::run()` and `ublk::daemon::start()` concurrently. After `
```cpp
ex::task<void> wait_signal(int ctrl_fd, int signal_fd, uint32_t dev_id,
ex::inplace_stop_source &source) {
std::println("ublk-nop: ublk device {} is running...", dev_id);
std::cout << std::format("ublk-nop: ublk device {} is running...\n",
dev_id);
auto parent_token = co_await ex::read_env(ex::get_stop_token);
ex::inplace_stop_callback cb{parent_token,
[&] noexcept { source.request_stop(); }};
[&]() noexcept { source.request_stop(); }};
signalfd_siginfo si;
co_await (condy::async_read(signal_fd, condy::buffer(&si, sizeof(si)), 0) |
ex::write_env(ex::prop{ex::get_stop_token, source.get_token()}));
std::println("ublk-nop: received signal {}, shutting down...",
si.ssi_signo);
std::cout << std::format(
"ublk-nop: received signal {}, shutting down...\n", si.ssi_signo);
co_await ublk::stop_dev(ctrl_fd, dev_id);
}
// ...
Expand Down
Loading
Loading