17#include "ublk/ublk_cmd.h"
24inline bool need_recovery(
const ublksrv_ctrl_dev_info *info)
noexcept {
25 return info->state == UBLK_S_DEV_QUIESCED ||
26 info->state == UBLK_S_DEV_FAIL_IO;
29struct daemon_setup_t {
30 template <
typename Sched,
typename Alloc>
31 ex::task<bool, TaskEnv<Sched, Alloc>> invoke(
int control_fd,
32 ublksrv_ctrl_dev_info *info) {
33 if (info->dev_id == -1) {
39 ex::upon_error([&](std::error_code ec) {
40 if (ec.value() != EEXIST) {
41 throw std::system_error(ec,
"add_dev");
45 auto r =
co_await std::move(s);
50 co_await control_get_dev_info_t{}.invoke<Sched, Alloc>(
51 control_fd, info->dev_id, info);
53 if (info->flags & UBLK_F_USER_RECOVERY && need_recovery(info)) {
54 co_await (control_start_user_recovery_t{}.invoke<Sched, Alloc>(
55 control_fd, info->dev_id) |
63struct daemon_configure_t {
64 template <
typename Sched,
typename Alloc>
65 ex::task<bool, TaskEnv<Sched, Alloc>>
66 invoke(
int control_fd, uint32_t dev_id, ublk_params *params) {
67 ublksrv_ctrl_dev_info info;
68 co_await cached_get_dev_info<Sched, Alloc>(control_fd, dev_id, &info);
70 if (info.state == UBLK_S_DEV_DEAD) {
71 co_await (control_set_params_t{}.invoke<Sched, Alloc>(
72 control_fd, dev_id, params) |
76 co_await (control_get_params_t{}.invoke<Sched, Alloc>(
77 control_fd, dev_id, params) |
84struct daemon_start_t {
85 template <
typename Sched,
typename Alloc>
86 ex::task<void, TaskEnv<Sched, Alloc>>
87 invoke(
int control_fd, uint32_t dev_id, int32_t daemon_pid) {
88 ublksrv_ctrl_dev_info info;
89 co_await cached_get_dev_info<Sched, Alloc>(control_fd, dev_id, &info);
91 if (!need_recovery(&info)) {
92 co_await (control_start_dev_t{}.invoke<Sched, Alloc>(
93 control_fd, dev_id, daemon_pid) |
95 }
else if (info.flags & UBLK_F_USER_RECOVERY) {
96 co_await (control_end_user_recovery_t{}.invoke<Sched, Alloc>(
97 control_fd, dev_id, daemon_pid) |
100 throw std::runtime_error(
101 "device can neither be started nor recovered");
107 template <
typename Sched,
typename Alloc, IoHandler Handler>
108 ex::task<void, TaskEnv<Sched, Alloc>>
109 invoke(
int control_fd, uint32_t dev_id, Handler *handler,
110 const RuntimeOptions *runtime_options,
size_t nr_files,
112 auto alloc =
co_await ex::read_env(ex::get_allocator);
114 ublksrv_ctrl_dev_info info;
115 co_await cached_get_dev_info<Sched, Alloc>(control_fd, dev_id, &info);
117 nr_files = std::max<size_t>(nr_files, 1);
118 if (info.flags & (UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_AUTO_BUF_REG)) {
119 nr_buffers = std::max<size_t>(nr_buffers, info.queue_depth);
121 auto options = runtime_options
122 ? runtime_options->build(info.queue_depth)
123 : RuntimeOptions().build(info.queue_depth);
125 AllocVector<std::unique_ptr<IoLoop>,
decltype(alloc)> loops(alloc);
126 loops.reserve(info.nr_hw_queues);
127 for (uint16_t q_id = 0; q_id < info.nr_hw_queues; q_id++) {
129 co_await (control_get_queue_affinity_t{}.invoke<Sched, Alloc>(
130 control_fd, dev_id, q_id, &cpuset) |
133 std::make_unique<IoLoop>(options, nr_files, nr_buffers, cpuset);
134 loops.push_back(std::move(loop));
135 options.enable_attach_wq(loops[0]->runtime());
138 std::string path = dev_path(dev_id);
139 int ublkc_fd =
co_await condy::async_open(path.c_str(), O_RDWR, 0);
140 auto d = defer([&]()
noexcept { close(ublkc_fd); });
142 ex::simple_counting_scope scope;
143 AllocVector<std::exception_ptr,
decltype(alloc)> errs(info.nr_hw_queues,
145 for (uint16_t q_id = 0; q_id < info.nr_hw_queues; q_id++) {
146 auto sched = loops[q_id]->get_scheduler();
147 auto env = ex::env{ex::prop{ex::get_allocator, alloc},
149 auto task = io_run_dev_t{}.invoke<
decltype(sched), Alloc>(
150 ublkc_fd, q_id, &info, handler) |
151 ex::write_env(std::move(env));
152 auto s = ex::starts_on(sched, std::move(task)) |
154 [&, q_id](
const std::exception_ptr &ep)
noexcept {
157 ex::spawn(std::move(s), scope.get_token());
159 co_await scope.join();
161 for (
auto &ep : errs) {
163 std::rethrow_exception(ep);
169struct daemon_shm_server_t {
170 template <
typename Sched,
typename Alloc, ShmHandler Handler>
171 auto invoke(
int control_fd, uint32_t dev_id, std::string_view path,
172 Handler *handler, uint32_t flags) {
173 return shm_server_run<Sched, Alloc>(
174 path, Session<Sched, Alloc, Handler>(control_fd, dev_id, flags,
Implementation of ublk control command interface.
Implementation of the ublk I/O loop for processing I/O requests on a dedicated scheduler.
The std::execution backend namespace used by ublk-cpp.
Handler concepts for ublk server.
Helpful condy runtime wrapper for running a dedicated I/O loop on a pinned thread.
auto add_dev(Fd fd, ublksrv_ctrl_dev_info *info) noexcept
Add a new ublk device.
The main namespace of the ublk-cpp library.
constexpr fetch_dev_info_t fetch_dev_info
Query object instance of fetch_dev_info_t.
Runtime options based on condy::RuntimeOptions.
Implementation of the shared-memory buffer registration service over a Unix domain socket.