ublk-cpp v0.1
A ublk library for std::execution.
Loading...
Searching...
No Matches
nop.cpp
Go to the documentation of this file.
1
7
8#include <cerrno>
9#include <condy.hpp>
10#include <csignal>
11#include <cstdint>
12#include <exception>
13#include <fcntl.h>
14#include <format>
15#include <getopt.h>
16#include <iostream>
17#include <sys/signalfd.h>
18#include <system_error>
19#include <thread>
20#include <ublk.hpp>
21#include <unistd.h>
22
23namespace ex = ublk::detail::ex;
24
25void prep_dev_info(ublksrv_ctrl_dev_info &info, uint32_t dev_id,
26 uint64_t flags) noexcept {
27 info = {};
28 info.dev_id = dev_id;
29 info.nr_hw_queues = 1;
30 info.queue_depth = 128;
31 info.max_io_buf_bytes = 512 << 10;
32 info.flags = flags;
33}
34
35void prep_params(ublk_params &params,
36 const ublksrv_ctrl_dev_info &info) noexcept {
37 params = {};
38 params.len = sizeof(params);
39 params.types = UBLK_PARAM_TYPE_BASIC;
40 params.basic.logical_bs_shift = 9;
41 params.basic.physical_bs_shift = 12;
42 params.basic.io_opt_shift = 12;
43 params.basic.io_min_shift = 9;
44 params.basic.max_sectors = info.max_io_buf_bytes >> 9;
45 params.basic.dev_sectors = (250ull * 1024 * 1024 * 1024) >> 9;
46}
47
48void print_usage(const char *prog) {
49 std::cout << std::format("Usage: {} -n <dev_id>\n", prog);
50 std::cout << "Run a no-op ublk block device.\n";
51 std::cout << '\n';
52 std::cout << "Options:\n";
53 std::cout << " -n <dev_id> ublk device id to use\n";
54 std::cout << " -h show this help and exit\n";
55}
56
57ex::task<void> wait_signal(int ctrl_fd, int signal_fd, uint32_t dev_id,
58 ex::inplace_stop_source &source) {
59 std::cout << std::format("ublk-nop: ublk device {} is running...\n",
60 dev_id);
61 auto parent_token = co_await ex::read_env(ex::get_stop_token);
62 auto stop_request = [&]() noexcept { source.request_stop(); };
63 ex::inplace_stop_callback<decltype(stop_request)> cb{
64 parent_token, std::move(stop_request)};
65 signalfd_siginfo si;
66 co_await (condy::async_read(signal_fd, condy::buffer(&si, sizeof(si)), 0) |
67 ex::write_env(ex::prop{ex::get_stop_token, source.get_token()}));
68 std::cout << std::format("ublk-nop: received signal {}, shutting down...\n",
69 si.ssi_signo);
70 co_await ublk::stop_dev(ctrl_fd, dev_id);
71}
72
73// NOLINTNEXTLINE(bugprone-unsafe-to-allow-exceptions)
74int main(int argc, char *argv[]) noexcept(false) {
75 uint32_t dev_id = -1;
76 int opt;
77 while ((opt = getopt(argc, argv, "n:h")) != -1) {
78 switch (opt) {
79 case 'n':
80 dev_id = std::stoul(optarg);
81 break;
82 case 'h':
83 default:
84 print_usage(argv[0]);
85 return opt == 'h' ? 0 : 1;
86 }
87 }
88
89 try {
90 sigset_t mask;
91 sigemptyset(&mask);
92 sigaddset(&mask, SIGINT);
93 sigaddset(&mask, SIGTERM);
94 sigprocmask(SIG_BLOCK, &mask, nullptr);
95
96 int signal_fd = signalfd(-1, &mask, SFD_NONBLOCK);
97 if (signal_fd < 0) {
98 throw std::system_error(errno, std::generic_category(), "signalfd");
99 }
100 auto d = ublk::detail::defer([&]() noexcept { close(signal_fd); });
101
102 int ctrl_fd = open("/dev/ublk-control", O_RDWR | O_CLOEXEC);
103 if (ctrl_fd < 0) {
104 throw std::system_error(errno, std::generic_category(),
105 "open /dev/ublk-control");
106 }
107 auto d2 = ublk::detail::defer([&]() noexcept { close(ctrl_fd); });
108
109 condy::RuntimeOptions options;
110 options.enable_sqe128();
111 condy::Runtime runtime(options);
112 std::jthread loop([&]() { runtime.run(); });
113 auto d3 = ublk::detail::defer([&]() noexcept { runtime.allow_exit(); });
114 ex::scheduler auto sched = condy::get_scheduler(runtime);
115
116 uint64_t features;
117 ublksrv_ctrl_dev_info info;
118 ublk_params params;
119 struct {
120 auto handle_io(const ublk::IoData &data) noexcept {
121 return ex::just(data.iod->nr_sectors * 512);
122 }
123 } handler;
124 static_assert(ublk::IoHandler<decltype(handler)>);
125 ex::inplace_stop_source stop_source;
126
127 ex::sender auto s =
128 ublk::get_features(ctrl_fd, &features) |
129 ex::let_value([&]() noexcept {
130 uint64_t flags = 0;
131 if (features & UBLK_F_USER_RECOVERY) {
132 flags |= UBLK_F_USER_RECOVERY;
133 }
134 prep_dev_info(info, dev_id, flags);
135 return ublk::daemon::setup(ctrl_fd, &info);
136 }) |
137 ex::let_value([&](bool) noexcept {
138 prep_params(params, info);
139 return ublk::daemon::configure(ctrl_fd, info.dev_id, &params);
140 }) |
141 ex::let_value([&](bool) noexcept {
142 ex::sender auto daemon =
143 ublk::daemon::run(ctrl_fd, info.dev_id, handler, {}) |
144 ex::then([&]() noexcept { stop_source.request_stop(); });
145 ex::sender auto start =
146 ublk::daemon::start(ctrl_fd, info.dev_id, getpid()) |
147 ex::let_value([&]() {
148 return wait_signal(ctrl_fd, signal_fd, info.dev_id,
149 stop_source);
150 });
151 return ex::when_all(daemon, start);
152 }) |
153 ex::let_value(
154 [&]() noexcept { return ublk::del_dev(ctrl_fd, info.dev_id); });
155
156 ex::sync_wait(ex::starts_on(sched, s));
157 } catch (const std::system_error &e) {
158 std::cerr << std::format("ublk-nop: {}\n", e.what());
159 return e.code().value();
160 } catch (const std::exception &e) {
161 std::cerr << std::format("ublk-nop: {}\n", e.what());
162 return 1;
163 }
164
165 return 0;
166}
Concept of an async handler with handle_io(const IoData&).
Definition handler.hpp:33
auto setup(int control_fd, ublksrv_ctrl_dev_info *info) noexcept
Ensure the device described by info has been created and return its current information.
Definition daemon.hpp:27
auto start(int control_fd, uint32_t dev_id, int32_t daemon_pid) noexcept
Start the device, served by this daemon, if possible.
Definition daemon.hpp:55
auto configure(int control_fd, uint32_t dev_id, ublk_params *params) noexcept
Ensure the device has been configured and return its current configuration in params.
Definition daemon.hpp:42
auto run(int control_fd, uint32_t dev_id, Handler &handler, Options options) noexcept
Run the daemon serving a device until it stops.
Definition daemon.hpp:82
auto stop_dev(int control_fd, uint32_t dev_id) noexcept
Stop a ublk device.
Definition control.hpp:81
auto get_features(int control_fd, uint64_t *features) noexcept
Get the feature flags supported by the ublk driver.
Definition control.hpp:142
auto del_dev(int control_fd, uint32_t dev_id) noexcept
Delete a ublk device.
Definition control.hpp:58
Main include file for the ublk-cpp library.