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