15#include <sys/socket.h>
22namespace ex = condy::detail::ex;
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);
29 auto d = defer([&]()
noexcept { close(client_fd); });
32 char control[CMSG_SPACE(
sizeof(
int))];
33 iovec iov = {.iov_base = &data, .iov_len =
sizeof(data)};
37 msg.msg_control = control;
38 msg.msg_controllen =
sizeof(control);
40 co_await condy::async_recvmsg(client_fd, &msg, 0);
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));
50 if (received_fd == -1) {
51 throw std::runtime_error(
"client did not pass a file descriptor");
53 auto d2 = defer([&]()
noexcept { close(received_fd); });
55 auto s = session.handle(received_fd, scope) |
57 std::error_code(ECANCELED, std::generic_category())) |
58 ex::upon_error([](
const auto &err)
noexcept {
59 return normalize_error(err, EIO);
61 ex::then([&](
auto r)
noexcept {
62 using R =
decltype(r);
63 if constexpr (std::same_as<R, std::error_code>) {
69 int32_t res =
co_await std::move(s);
71 auto d3 = defer([&]()
noexcept {
73 session.cleanup(sched, scope);
77 co_await condy::async_send(client_fd, condy::buffer(&res,
sizeof(res)), 0);
80 throw std::system_error(-res, std::generic_category());
83 auto r =
co_await condy::async_recv(client_fd,
84 condy::buffer(&data,
sizeof(data)), 0);
86 throw std::runtime_error(
"client sent data after result");
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);
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);
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());
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); });
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");
118 std::memcpy(addr.sun_path, path.data(), path.size());
119 addr.sun_path[path.size()] =
'\0';
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");
129 co_await condy::async_bind(server_fd,
reinterpret_cast<sockaddr *
>(&addr),
131 auto d2 = defer([&]
noexcept { unlink(addr.sun_path); });
132 co_await condy::async_listen(server_fd, 128);
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)};
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);
147 ex::spawn(std::move(s), scope.get_token());
148 co_await scope.join();
150 std::rethrow_exception(err);
154template <
typename Sched,
typename Alloc, ShmHandler Handler>
class Session {
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),
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);
164 auto d = defer([&]()
noexcept {
166 cleanup(sched, scope);
171 co_await condy::async_statx(memfd,
"", AT_EMPTY_PATH, STATX_SIZE, &stx);
172 size_ = stx.stx_size;
174 int map_prot = PROT_READ | PROT_WRITE;
175 if (flags_ & UBLK_SHMEM_BUF_READ_ONLY) {
176 map_prot = PROT_READ;
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(),
186 ublk_shmem_buf_reg buf_reg = {};
187 buf_reg.addr =
reinterpret_cast<uint64_t
>(base_);
189 buf_reg.flags = flags_;
190 index_ =
co_await control_register_shm_buf_t{}.invoke<Sched, Alloc>(
191 control_fd_, dev_id_, &buf_reg);
193 co_await handler_.handle_reg_shm(index_, base_, size_);
199 void cleanup(Sched sched, ex::counting_scope &scope)
noexcept {
200 Session self = *
this;
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_);
212 munmap(self.base_, self.size_);
216 ex::starts_on(sched, std::move(s)) |
217 ex::upon_error([](
const std::exception_ptr &)
noexcept {}),
227 void *base_ =
nullptr;
Implementation of ublk control command interface.
Handler concepts for ublk server.
The main namespace of the ublk-cpp library.