1use std::io::{self, Error, ErrorKind};
2use std::mem::MaybeUninit;
3use std::os::fd::RawFd;
4use std::slice;
5
6use io_uring::types;
7use o3::marker::ThreadBound;
8
9use crate::driver::token::SHUTDOWN;
10use crate::driver::token::{Epoch, ROUTE_FRAMEWORK, SlotIndex, Token, kind};
11use crate::io::fd::{Fd, FdSlot};
12use io_uring::opcode::Accept;
13use io_uring::opcode::AsyncCancel;
14use io_uring::opcode::Bind;
15use io_uring::opcode::Close;
16use io_uring::opcode::Connect;
17use io_uring::opcode::Listen;
18use io_uring::opcode::OpenAt;
19use io_uring::opcode::PollAdd;
20use io_uring::opcode::Read;
21use io_uring::opcode::Recv;
22use io_uring::opcode::RecvMsgMulti;
23use io_uring::opcode::RecvMulti;
24use io_uring::opcode::Send;
25use io_uring::opcode::SendMsg;
26use io_uring::opcode::SetSockOpt;
27use io_uring::opcode::Shutdown;
28use io_uring::opcode::Socket;
29use io_uring::opcode::Splice;
30use io_uring::opcode::Statx;
31use io_uring::opcode::Timeout;
32use io_uring::opcode::Write;
33use io_uring::squeue::Entry;
34use io_uring::squeue::Flags;
35use io_uring::types::DestinationSlot;
36
37#[derive(Clone, Copy)]
38pub struct Create {
39 pub slot: FdSlot,
40 pub user_data: u64,
41}
42
43pub struct Sqe {
44 entry: Entry,
45 create: Option<Create>,
46 _thread: ThreadBound,
47}
48
49impl Sqe {
50 pub fn entry(&self) -> &Entry {
51 &self.entry
52 }
53
54 pub fn create_meta(&self) -> Option<Create> {
55 self.create
56 }
57
58 fn new(entry: Entry) -> Self {
59 Self {
60 entry,
61 create: None,
62 _thread: ThreadBound::NEW,
63 }
64 }
65
66 fn create(entry: Entry, slot: FdSlot, user_data: u64) -> Self {
67 let token = Token::new(ROUTE_FRAMEWORK, SlotIndex::new(slot.raw()), Epoch::ZERO)
68 .with_kind(kind::CREATE);
69 Self {
70 entry: entry.user_data(token.raw()),
71 create: Some(Create { slot, user_data }),
72 _thread: ThreadBound::NEW,
73 }
74 }
75
76 fn framework(slot: FdSlot, op_kind: u8) -> u64 {
77 Token::new(ROUTE_FRAMEWORK, SlotIndex::new(slot.raw()), Epoch::ZERO)
78 .with_kind(op_kind)
79 .raw()
80 }
81
82 pub fn from_entry(entry: Entry) -> Self {
83 Self::new(entry)
84 }
85
86 pub fn send(fd: &Fd, buf: &[u8], op: Token) -> Self {
87 Self::send_at(fd.slot(), buf, op)
88 }
89
90 pub fn send_at(slot: FdSlot, buf: &[u8], op: Token) -> Self {
91 Self::new(
92 Send::new(types::Fixed(slot.raw()), buf.as_ptr(), buf.len() as u32)
93 .flags(libc::MSG_NOSIGNAL)
94 .build()
95 .user_data(op.with_kind(kind::SEND).raw()),
96 )
97 }
98
99 pub unsafe fn write_fd(fd: RawFd, buf: &[u8], offset: u64, op: Token) -> Self {
102 Self::new(
103 Write::new(types::Fd(fd), buf.as_ptr(), buf.len() as u32)
104 .offset(offset)
105 .build()
106 .user_data(op.with_kind(kind::WRITE).raw()),
107 )
108 }
109
110 pub fn openat(dir: RawFd, path: *const libc::c_char, flags: i32, mode: u32, op: Token) -> Self {
111 Self::new(
112 OpenAt::new(types::Fd(dir), path)
113 .flags(flags)
114 .mode(mode as libc::mode_t)
115 .build()
116 .user_data(op.with_kind(kind::OPEN).raw()),
117 )
118 }
119
120 pub unsafe fn openat_fixed(
123 dir: RawFd,
124 path: *const libc::c_char,
125 flags: i32,
126 mode: u32,
127 slot: FdSlot,
128 op: Token,
129 ) -> io::Result<Self> {
130 let dest = DestinationSlot::try_from_slot_target(slot.raw())
131 .map_err(|_| Error::new(ErrorKind::InvalidInput, "dope: open slot out of range"))?;
132 Ok(Self::create(
133 OpenAt::new(types::Fd(dir), path)
134 .file_index(Some(dest))
135 .flags(flags)
136 .mode(mode as libc::mode_t)
137 .build(),
138 slot,
139 op.with_kind(kind::OPEN).raw(),
140 ))
141 }
142
143 pub unsafe fn read(fd: RawFd, buf: &mut [u8], offset: u64, op: Token) -> Self {
146 let buf = unsafe {
147 slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<MaybeUninit<u8>>(), buf.len())
148 };
149 unsafe { Self::read_uninit(fd, buf, offset, op.with_kind(kind::READ)) }
150 }
151
152 pub unsafe fn read_uninit(
155 fd: RawFd,
156 buf: &mut [MaybeUninit<u8>],
157 offset: u64,
158 op: Token,
159 ) -> Self {
160 Self::new(
161 Read::new(types::Fd(fd), buf.as_mut_ptr().cast(), buf.len() as u32)
162 .offset(offset)
163 .build()
164 .user_data(op.raw()),
165 )
166 }
167
168 pub fn read_fixed_file_uninit(
169 slot: FdSlot,
170 buf: &mut [MaybeUninit<u8>],
171 offset: u64,
172 op: Token,
173 ) -> Self {
174 Self::new(
175 Read::new(
176 types::Fixed(slot.raw()),
177 buf.as_mut_ptr().cast(),
178 buf.len() as u32,
179 )
180 .offset(offset)
181 .build()
182 .user_data(op.raw()),
183 )
184 }
185
186 pub fn stat_path(path: *const libc::c_char, stat: *mut libc::statx, op: Token) -> Self {
187 Self::new(
188 Statx::new(types::Fd(libc::AT_FDCWD), path, stat.cast::<types::statx>())
189 .mask(libc::STATX_TYPE | libc::STATX_SIZE | libc::STATX_MTIME)
190 .build()
191 .user_data(op.with_kind(kind::STAT).raw()),
192 )
193 }
194
195 pub fn stat_fd(fd: RawFd, stat: *mut libc::statx, op: Token) -> Self {
196 Self::new(
197 Statx::new(types::Fd(fd), c"".as_ptr(), stat.cast::<types::statx>())
198 .flags(libc::AT_EMPTY_PATH)
199 .mask(libc::STATX_TYPE | libc::STATX_SIZE | libc::STATX_MTIME)
200 .build()
201 .user_data(op.with_kind(kind::STAT).raw()),
202 )
203 }
204
205 pub unsafe fn splice_raw(
208 fd_in: RawFd,
209 off_in: i64,
210 fd_out: RawFd,
211 off_out: i64,
212 len: u32,
213 flags: u32,
214 op: Token,
215 ) -> Self {
216 Self::splice(fd_in, off_in, fd_out, off_out, len, flags, op)
217 }
218
219 fn splice(
220 fd_in: RawFd,
221 off_in: i64,
222 fd_out: RawFd,
223 off_out: i64,
224 len: u32,
225 flags: u32,
226 op: Token,
227 ) -> Self {
228 Self::new(
229 Splice::new(types::Fd(fd_in), off_in, types::Fd(fd_out), off_out, len)
230 .flags(flags)
231 .build()
232 .user_data(op.with_kind(kind::SPLICE).raw()),
233 )
234 }
235
236 pub fn splice_to_pipe(
237 fd_in: RawFd,
238 off_in: i64,
239 pipe_write_fd: RawFd,
240 len: u32,
241 op: Token,
242 ) -> Self {
243 Self::splice(fd_in, off_in, pipe_write_fd, -1, len, 0, op)
244 }
245
246 pub unsafe fn recv_multi(fd: &Fd, buf_group: u16, op: Token) -> Self {
249 Self::new(
250 RecvMulti::new(types::Fixed(fd.slot().raw()), buf_group)
251 .build()
252 .user_data(op.with_kind(kind::RECV).raw()),
253 )
254 }
255
256 pub const SUPPORTS_RECV_DISCARD: bool = true;
257
258 pub unsafe fn recv_discard(fd: &Fd, remaining: u64, op: Token) -> Self {
261 const DISCARD_CAP: u64 = 1 << 30;
262 static SCRATCH: u8 = 0;
263 let len = remaining.min(DISCARD_CAP) as u32;
264 Self::new(
265 Recv::new(
266 types::Fixed(fd.slot().raw()),
267 &SCRATCH as *const u8 as *mut u8,
268 len,
269 )
270 .flags(libc::MSG_TRUNC)
271 .build()
272 .user_data(op.with_kind(kind::RECV_DISCARD).raw()),
273 )
274 }
275
276 pub fn accept_oneshot(
277 listener: &Fd,
278 addr_ptr: *mut libc::sockaddr,
279 addrlen_ptr: *mut libc::socklen_t,
280 op: Token,
281 ) -> Self {
282 Self::new(
283 Accept::new(types::Fixed(listener.slot().raw()), addr_ptr, addrlen_ptr)
284 .file_index(Some(DestinationSlot::auto_target()))
285 .flags(0)
286 .build()
287 .user_data(op.with_kind(kind::ACCEPT).raw()),
288 )
289 }
290
291 pub fn recv_msg_multi(fd: &Fd, msghdr: &libc::msghdr, buf_group: u16, op: Token) -> Self {
292 Self::new(
293 RecvMsgMulti::new(types::Fixed(fd.slot().raw()), msghdr, buf_group)
294 .build()
295 .user_data(op.with_kind(kind::RECV).raw()),
296 )
297 }
298
299 pub fn send_msg(fd: &Fd, msg: &libc::msghdr, op: Token) -> Self {
300 Self::new(
301 SendMsg::new(types::Fixed(fd.slot().raw()), msg)
302 .flags(libc::MSG_NOSIGNAL as u32)
303 .build()
304 .user_data(op.with_kind(kind::SEND).raw()),
305 )
306 }
307
308 pub fn close_at(slot: FdSlot) -> Self {
309 Self::new(
310 Close::new(types::Fixed(slot.raw()))
311 .build()
312 .user_data(Self::framework(slot, kind::CLOSE)),
313 )
314 }
315
316 pub fn quickack(fd: &Fd) -> Self {
317 const TCP_QUICKACK: u32 = 12;
318 static QUICKACK_ON: libc::c_int = 1;
319 Self::new(
320 SetSockOpt::new(
321 types::Fixed(fd.slot().raw()),
322 libc::IPPROTO_TCP as u32,
323 TCP_QUICKACK,
324 &QUICKACK_ON as *const libc::c_int as *const libc::c_void,
325 size_of::<libc::c_int>() as u32,
326 )
327 .build()
328 .user_data(0),
329 )
330 }
331
332 pub fn shutdown(fd: &Fd, how: i32) -> Self {
333 Self::new(
334 Shutdown::new(types::Fixed(fd.slot().raw()), how)
335 .build()
336 .user_data(0),
337 )
338 }
339
340 pub fn shutdown_linked_at(slot: FdSlot, how: i32) -> Self {
341 Self::new(
342 Shutdown::new(types::Fixed(slot.raw()), how)
343 .build()
344 .flags(Flags::IO_HARDLINK)
345 .user_data(Self::framework(slot, kind::CLOSE_PREP)),
346 )
347 }
348
349 pub fn poll_shutdown(fd: RawFd) -> Self {
350 Self::new(
351 PollAdd::new(types::Fd(fd), libc::POLLIN as u32)
352 .build()
353 .user_data(SHUTDOWN.raw()),
354 )
355 }
356
357 pub fn cancel(target: Token, op_kind: u8) -> Self {
358 Self::new(
359 AsyncCancel::new(target.with_kind(op_kind).raw())
360 .build()
361 .user_data(0),
362 )
363 }
364
365 pub fn interval(timer: &'static types::Timespec, op: Token) -> Self {
371 Self::new(
372 Timeout::new(timer)
373 .count(0)
374 .flags(types::TimeoutFlags::MULTISHOT)
375 .build()
376 .user_data(op.with_kind(kind::TIMER).raw()),
377 )
378 }
379
380 pub fn cancel_create(slot: FdSlot) -> Self {
381 Self::new(
382 AsyncCancel::new(Self::framework(slot, kind::CREATE))
383 .build()
384 .user_data(0),
385 )
386 }
387
388 pub fn socket(
389 domain: i32,
390 socket_type: i32,
391 protocol: i32,
392 fd: &Fd,
393 op: Token,
394 ) -> io::Result<Self> {
395 Self::socket_at(domain, socket_type, protocol, fd.slot(), op)
396 }
397
398 pub fn socket_at(
399 domain: i32,
400 socket_type: i32,
401 protocol: i32,
402 slot: FdSlot,
403 op: Token,
404 ) -> io::Result<Self> {
405 let dest = DestinationSlot::try_from_slot_target(slot.raw())
406 .map_err(|_| Error::new(ErrorKind::InvalidInput, "dope: socket slot out of range"))?;
407 Ok(Self::create(
408 Socket::new(domain, socket_type, protocol)
409 .file_index(Some(dest))
410 .build(),
411 slot,
412 op.with_kind(kind::SOCKET).raw(),
413 ))
414 }
415
416 pub fn bind_at(
417 slot: FdSlot,
418 addr_ptr: *const libc::sockaddr,
419 addr_len: u32,
420 op: Token,
421 ) -> Self {
422 Self::new(
423 Bind::new(types::Fixed(slot.raw()), addr_ptr, addr_len)
424 .build()
425 .user_data(op.with_kind(kind::SOCKET).raw()),
426 )
427 }
428
429 pub fn listen_at(slot: FdSlot, backlog: i32, op: Token) -> Self {
430 Self::new(
431 Listen::new(types::Fixed(slot.raw()), backlog)
432 .build()
433 .user_data(op.with_kind(kind::SOCKET).raw()),
434 )
435 }
436
437 pub fn connect(fd: &Fd, addr_ptr: *const libc::sockaddr, addr_len: u32, op: Token) -> Self {
438 Self::new(
439 Connect::new(types::Fixed(fd.slot().raw()), addr_ptr, addr_len)
440 .build()
441 .user_data(op.with_kind(kind::CONNECT).raw()),
442 )
443 }
444}