Skip to main content

dope_core/backend/uring/
sqe.rs

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    /// # Safety
100    /// `fd` must stay open and `buf` stable and unchanged until completion.
101    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    /// # Safety
121    /// `dir` and a NUL-terminated `path` must stay valid, and `slot` reserved, until completion.
122    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    /// # Safety
144    /// `fd` must stay open and `buf` stable and unaliased until completion.
145    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    /// # Safety
153    /// `fd` must stay open and `buf` stable and unaliased until completion.
154    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    /// # Safety
206    /// Both descriptors must stay open until completion.
207    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    /// # Safety
247    /// `fd` must belong to the receiving driver and stay live until completion.
248    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    /// # Safety
259    /// `fd` must belong to the receiving driver and stay live until completion.
260    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    /// Arms a kernel-owned recurring timer.
366    ///
367    /// The timer specification is referenced by the multishot operation and
368    /// must therefore remain live until the driver is torn down or the timer
369    /// is cancelled.
370    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}