ublk-cpp v0.1
A ublk library for std::execution.
Loading...
Searching...
No Matches
shm.hpp
Go to the documentation of this file.
1
6
7#pragma once
8
11#include "ublk/detail/task.hpp"
12#include "ublk/handler.hpp"
13#include <condy.hpp>
14#include <exception>
15#include <stdexcept>
16#include <sys/socket.h>
17#include <sys/un.h>
18#include <utility>
19
20namespace ublk {
21namespace detail {
22
23template <typename Sched, typename Alloc, typename Session>
24inline ex::task<void, TaskEnv<Sched, Alloc>>
25shm_session(int client_fd, Session session, ex::counting_scope &scope) {
26 auto sched = co_await ex::read_env(ex::get_start_scheduler);
27
28 auto d = defer([&]() noexcept { close(client_fd); });
29
30 char data;
31 char control[CMSG_SPACE(sizeof(int))];
32 iovec iov = {.iov_base = &data, .iov_len = sizeof(data)};
33 msghdr msg = {};
34 msg.msg_iov = &iov;
35 msg.msg_iovlen = 1;
36 msg.msg_control = control;
37 msg.msg_controllen = sizeof(control);
38
39 co_await condy::async_recvmsg(client_fd, &msg, 0);
40
41 int received_fd = -1;
42 for (cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
43 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
44 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
45 std::memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(received_fd));
46 }
47 }
48
49 if (received_fd == -1) {
50 throw std::runtime_error("client did not pass a file descriptor");
51 }
52 auto d2 = defer([&]() noexcept { close(received_fd); });
53
54 auto s = session.handle(received_fd, scope) |
55 ex::stopped_as_error(
56 std::error_code(ECANCELED, std::generic_category())) |
57 ex::upon_error([](const auto &err) noexcept {
58 return normalize_error(err, EIO);
59 }) |
60 ex::then([&](auto r) noexcept {
61 using R = decltype(r);
62 if constexpr (std::same_as<R, std::error_code>) {
63 return -r.value();
64 } else {
65 return r;
66 }
67 });
68 int32_t res = co_await std::move(s);
69
70 auto d3 = defer([&]() noexcept {
71 if (res >= 0) {
72 session.cleanup(sched, scope);
73 }
74 });
75
76 co_await condy::async_send(client_fd, condy::buffer(&res, sizeof(res)), 0);
77
78 if (res < 0) {
79 throw std::system_error(-res, std::generic_category());
80 }
81
82 auto r = co_await condy::async_recv(client_fd,
83 condy::buffer(&data, sizeof(data)), 0);
84 if (r > 0) {
85 throw std::runtime_error("client sent data after result");
86 }
87}
88
89template <typename Sched, typename Alloc, typename Session>
90inline ex::task<void, TaskEnv<Sched, Alloc>>
91shm_server(int server_fd, Session session, ex::counting_scope &scope) {
92 auto sched = co_await ex::read_env(ex::get_start_scheduler);
93 while (true) {
94 sockaddr_un client_addr;
95 socklen_t client_len = sizeof(client_addr);
96 int client_fd = co_await condy::async_accept(
97 server_fd, (struct sockaddr *)&client_addr, &client_len, 0);
98
99 auto task = shm_session<Sched, Alloc>(client_fd, session, scope);
100 auto s = ex::starts_on(sched, std::move(task)) |
101 ex::upon_error([](const std::exception_ptr &) noexcept {});
102 ex::spawn(std::move(s), scope.get_token());
103 }
104}
105
106template <typename Sched, typename Alloc, typename Session>
107inline ex::task<void, TaskEnv<Sched, Alloc>>
108shm_server_run(std::string_view path, Session session) {
109 int server_fd = co_await condy::async_socket(AF_UNIX, SOCK_STREAM, 0, 0);
110 auto d = defer([&]() noexcept { close(server_fd); });
111
112 sockaddr_un addr = {};
113 addr.sun_family = AF_UNIX;
114 if (path.size() >= sizeof(addr.sun_path)) {
115 throw std::length_error("unix path too long");
116 }
117 std::memcpy(addr.sun_path, path.data(), path.size());
118 addr.sun_path[path.size()] = '\0';
119
120 co_await (condy::async_unlink(addr.sun_path, 0) |
121 ex::upon_error([](std::error_code ec) {
122 if (ec.value() != ENOENT) {
123 throw std::system_error(ec, "unlink");
124 }
125 return -ec.value();
126 }));
127
128 co_await condy::async_bind(server_fd, reinterpret_cast<sockaddr *>(&addr),
129 sizeof(addr));
130 auto d2 = defer([&]() noexcept { unlink(addr.sun_path); });
131 co_await condy::async_listen(server_fd, 128);
132
133 ex::counting_scope scope;
134 auto stop_request = [&]() noexcept { scope.request_stop(); };
135 ex::inplace_stop_callback<decltype(stop_request)> cb{
136 co_await ex::read_env(ex::get_stop_token), std::move(stop_request)};
137
138 std::exception_ptr err;
139 auto sched = co_await ex::read_env(ex::get_start_scheduler);
140 auto s = ex::starts_on(
141 sched, shm_server<Sched, Alloc>(server_fd, session, scope)) |
142 ex::upon_error([&](std::exception_ptr eptr) noexcept {
143 scope.request_stop();
144 err = std::move(eptr);
145 });
146 ex::spawn(std::move(s), scope.get_token());
147 co_await scope.join();
148 if (err) {
149 std::rethrow_exception(err);
150 }
151}
152
153template <typename Sched, typename Alloc, ShmHandler Handler> class Session {
154public:
155 Session(int control_fd, uint32_t dev_id, uint32_t flags, Handler &handler)
156 : control_fd_(control_fd), dev_id_(dev_id), flags_(flags),
157 handler_(handler) {}
158
159 ex::task<int32_t, TaskEnv<Sched, Alloc>>
160 handle(int memfd, ex::counting_scope &scope) noexcept {
161 auto sched = co_await ex::read_env(ex::get_start_scheduler);
162 bool ok = false;
163 auto d = defer([&]() noexcept {
164 if (!ok) {
165 cleanup(sched, scope);
166 }
167 });
168
169 struct statx stx;
170 co_await condy::async_statx(memfd, "", AT_EMPTY_PATH, STATX_SIZE, &stx);
171 size_ = stx.stx_size;
172
173 int map_prot = PROT_READ | PROT_WRITE;
174 if (flags_ & UBLK_SHMEM_BUF_READ_ONLY) {
175 map_prot = PROT_READ;
176 }
177 void *addr =
178 mmap(nullptr, size_, map_prot, MAP_SHARED | MAP_POPULATE, memfd, 0);
179 if (addr == MAP_FAILED) {
180 throw std::system_error(errno, std::generic_category(),
181 "memfd mmap");
182 }
183 base_ = addr;
184
185 ublk_shmem_buf_reg buf_reg = {};
186 buf_reg.addr = reinterpret_cast<uint64_t>(base_);
187 buf_reg.len = size_;
188 buf_reg.flags = flags_;
189 index_ = co_await control_register_shm_buf_t{}.invoke<Sched, Alloc>(
190 control_fd_, dev_id_, &buf_reg);
191
192 co_await handler_.handle_reg_shm(index_, base_, size_);
193
194 ok = true;
195 co_return 0;
196 }
197
198 void cleanup(Sched sched, ex::counting_scope &scope) noexcept {
199 Session self = *this;
200 auto s =
201 ex::just() |
202 ex::let_value([self = std::move(self)]()
203 -> ex::task<void, TaskEnv<Sched, Alloc>> {
204 if (self.index_ != -1) {
205 co_await ex::unstoppable(
206 control_unregister_shm_buf_t{}.invoke<Sched, Alloc>(
207 self.control_fd_, self.dev_id_, self.index_));
208 self.handler_.handle_unreg_shm(self.index_);
209 }
210 if (self.base_) {
211 munmap(self.base_, self.size_);
212 }
213 });
214 ex::spawn(
215 ex::starts_on(sched, std::move(s)) |
216 ex::upon_error([](const std::exception_ptr &) noexcept {}),
217 scope.get_token());
218 }
219
220private:
221 int control_fd_;
222 uint32_t dev_id_;
223 uint32_t flags_;
224 Handler &handler_;
225 size_t size_ = 0;
226 void *base_ = nullptr;
227 int32_t index_ = -1;
228};
229
230} // namespace detail
231} // namespace ublk
Implementation of ublk control command interface.
The std::execution backend namespace used by ublk-cpp.
Handler concepts for ublk server.
The main namespace of the ublk-cpp library.
Definition ublk.hpp:21
Task environment utils.