Skip to main content

ntex_net/uring/
driver.rs

1use std::cell::{Cell, UnsafeCell};
2use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
3use std::{cmp, collections::VecDeque, fmt, io, mem, net, ptr, rc::Rc, sync::Arc};
4
5#[cfg(unix)]
6use std::os::unix::net::UnixStream as OsUnixStream;
7
8use ntex_io::Io;
9use ntex_io_uring::cqueue::{self, Entry as CEntry, more};
10use ntex_io_uring::opcode::{AsyncCancel, PollAdd};
11use ntex_io_uring::squeue::{Entry as SEntry, SubmissionQueue};
12use ntex_io_uring::{IoUring, Probe, types::CancelBuilder, types::Fd};
13use ntex_rt::{DriverType, Notify, PollResult, Runtime, syscall};
14use ntex_service::cfg::SharedCfg;
15use socket2::{Protocol, SockAddr, Socket, Type};
16
17use super::{TcpStream, UnixStream, stream::StreamOps};
18use crate::channel::Receiver;
19
20pub trait Handler {
21    /// Operation is completed.
22    fn completed(&mut self, id: usize, flags: u32, result: io::Result<usize>);
23
24    /// Operation is canceled.
25    fn canceled(&mut self, id: usize);
26
27    /// The driver's turn has completed.
28    fn tick(&mut self);
29
30    /// Clean up the handle before dropping the driver.
31    fn cleanup(&mut self);
32}
33
34pub struct DriverApi {
35    batch: u64,
36    inner: Rc<DriverInner>,
37}
38
39impl DriverApi {
40    #[inline]
41    /// Check if kernel ver 6.1 or greater
42    pub fn is_new(&self) -> bool {
43        self.inner.flags.get().contains(Flags::NEW)
44    }
45
46    fn submit_inner<F>(&self, f: F)
47    where
48        F: FnOnce(&mut SEntry),
49    {
50        unsafe {
51            let changes = &mut *self.inner.changes.get();
52            let sq = self.inner.ring.submission();
53            if !changes.is_empty() || sq.is_full() {
54                changes.push_back(mem::MaybeUninit::uninit());
55                let entry = changes.back_mut().unwrap();
56                ptr::write_bytes(entry.as_mut_ptr(), 0, 1);
57                f(entry.assume_init_mut());
58            } else {
59                sq.push_inline(f).expect("Queue size is checked");
60            }
61        }
62    }
63
64    #[inline]
65    /// Submit request to the driver.
66    pub fn submit(&self, id: u32, entry: SEntry) {
67        self.submit_inner(|en| {
68            *en = entry;
69            en.set_user_data(u64::from(id) | self.batch);
70        });
71    }
72
73    #[inline]
74    /// Submit request to the driver.
75    pub fn submit_inline<F>(&self, id: u32, f: F)
76    where
77        F: FnOnce(&mut SEntry),
78    {
79        self.submit_inner(|en| {
80            f(en);
81            en.set_user_data(u64::from(id) | self.batch);
82        });
83    }
84
85    #[inline]
86    /// Attempt to cancel an already issued request.
87    pub fn cancel(&self, id: u32) {
88        self.submit_inner(|en| {
89            *en = AsyncCancel::new(u64::from(id) | self.batch)
90                .build()
91                .user_data(Driver::CANCEL);
92        });
93    }
94
95    #[inline]
96    /// Attempt to sync cancel all requests.
97    pub fn cancel_all_sync(&self, fd: Fd) -> io::Result<()> {
98        self.inner
99            .ring
100            .submitter()
101            .register_sync_cancel(None, CancelBuilder::fd(fd).all())
102    }
103
104    /// Get whether a specific io-uring opcode is supported.
105    pub fn is_supported(&self, opcode: u8) -> bool {
106        self.inner.probe.is_supported(opcode)
107    }
108}
109
110/// Low-level driver of io-uring.
111pub struct Driver {
112    fd: RawFd,
113    hid: Cell<u64>,
114    notifier: Notifier,
115    #[allow(clippy::box_collection)]
116    handlers: Cell<Option<Box<Vec<HandlerItem>>>>,
117    inner: Rc<DriverInner>,
118}
119
120struct HandlerItem {
121    hnd: Box<dyn Handler>,
122    modified: bool,
123}
124
125impl HandlerItem {
126    fn tick(&mut self) {
127        if self.modified {
128            self.modified = false;
129            self.hnd.tick();
130        }
131    }
132}
133
134bitflags::bitflags! {
135    #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
136    struct Flags: u8 {
137        const NEW      = 0b0000_0001;
138        const NOTIFIER = 0b0000_0010;
139    }
140}
141
142struct DriverInner {
143    probe: Probe,
144    flags: Cell<Flags>,
145    ring: IoUring<SEntry, CEntry>,
146    changes: UnsafeCell<VecDeque<mem::MaybeUninit<SEntry>>>,
147}
148
149impl Driver {
150    const NOTIFY: u64 = u64::MAX;
151    const CANCEL: u64 = u64::MAX - 1;
152    const BATCH: u64 = 48;
153    const BATCH_MASK: u64 = 0xFFFF_0000_0000_0000;
154    const DATA_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
155
156    /// Create io-uring driver
157    pub fn new(capacity: u32) -> io::Result<Self> {
158        // Create ring
159        let (new, ring) = if let Ok(ring) = IoUring::builder()
160            .setup_coop_taskrun()
161            .setup_single_issuer()
162            .setup_defer_taskrun()
163            .build(capacity)
164        {
165            log::info!(
166                "New io-uring driver with single-issuer, coop-taskrun, defer-taskrun"
167            );
168            (true, ring)
169        } else if let Ok(ring) = IoUring::builder().setup_single_issuer().build(capacity) {
170            log::info!("New io-uring driver with single-issuer");
171            (true, ring)
172        } else {
173            let ring = IoUring::builder().build(capacity)?;
174            log::info!("New io-uring driver");
175            (false, ring)
176        };
177
178        let mut probe = Probe::new();
179        ring.submitter().register_probe(&mut probe)?;
180
181        // Remote notifier
182        let notifier = Notifier::new()?;
183        unsafe {
184            let sq = ring.submission();
185            sq.push(
186                &PollAdd::new(Fd(notifier.as_raw_fd()), libc::POLLIN as _)
187                    .multi(true)
188                    .build()
189                    .user_data(Self::NOTIFY),
190            )
191            .expect("the squeue sould not be full");
192            sq.sync();
193        }
194
195        let fd = ring.as_raw_fd();
196        let inner = Rc::new(DriverInner {
197            ring,
198            probe,
199            flags: Cell::new(if new { Flags::NEW } else { Flags::empty() }),
200            changes: UnsafeCell::new(VecDeque::with_capacity(32)),
201        });
202
203        Ok(Self {
204            fd,
205            inner,
206            notifier,
207            hid: Cell::new(0),
208            handlers: Cell::new(Some(Box::new(Vec::new()))),
209        })
210    }
211
212    /// Driver type
213    pub const fn tp(&self) -> DriverType {
214        DriverType::IoUring
215    }
216
217    /// Register updates handler
218    pub fn register<F>(&self, f: F)
219    where
220        F: FnOnce(DriverApi) -> Box<dyn Handler>,
221    {
222        let id = self.hid.get();
223        let mut handlers = self.handlers.take().unwrap_or_default();
224        handlers.push(HandlerItem {
225            hnd: f(DriverApi {
226                batch: id << Self::BATCH,
227                inner: self.inner.clone(),
228            }),
229            modified: false,
230        });
231        self.handlers.set(Some(handlers));
232        self.hid.set(id + 1);
233    }
234
235    fn apply_changes(&self, sq: SubmissionQueue<'_, SEntry>) -> bool {
236        unsafe {
237            let changes = &mut *self.inner.changes.get();
238            if changes.is_empty() {
239                false
240            } else {
241                let num = cmp::min(changes.len(), sq.capacity() - sq.len());
242                let (s1, s2) = changes.as_slices();
243                let s1_num = cmp::min(s1.len(), num);
244                if s1_num > 0 {
245                    // safety: "changes" contains only initialized entries
246                    sq.push_multiple(
247                        ((&raw const s1[0..s1_num]) as *const [SEntry])
248                            .as_ref()
249                            .unwrap(),
250                    )
251                    .unwrap();
252                } else if !s2.is_empty() {
253                    let s2_num = cmp::min(s2.len(), num - s1_num);
254                    if s2_num > 0 {
255                        sq.push_multiple(
256                            ((&raw const s2[0..s2_num]) as *const [SEntry])
257                                .as_ref()
258                                .unwrap(),
259                        )
260                        .unwrap();
261                    }
262                }
263                changes.drain(0..num);
264
265                !changes.is_empty()
266            }
267        }
268    }
269
270    /// Handle ring completions, forward changes to specific handler
271    fn poll_completions(
272        &self,
273        cq: &mut cqueue::CompletionQueue<'_, CEntry>,
274        sq: SubmissionQueue<'_, SEntry>,
275    ) {
276        cq.sync();
277
278        if !cqueue::CompletionQueue::<'_, _>::is_empty(cq) {
279            let mut handlers = self.handlers.take().unwrap();
280            for entry in cq {
281                let user_data = entry.user_data();
282                match user_data {
283                    Self::CANCEL => {}
284                    Self::NOTIFY => {
285                        let flags = entry.flags();
286                        self.notifier.clear().expect("cannot clear notifier");
287
288                        // re-submit notifier fd
289                        if !more(flags) {
290                            unsafe {
291                                sq.push(
292                                    &PollAdd::new(
293                                        Fd(self.notifier.as_raw_fd()),
294                                        libc::POLLIN as _,
295                                    )
296                                    .multi(true)
297                                    .build()
298                                    .user_data(Self::NOTIFY),
299                                )
300                            }
301                            .expect("the squeue sould not be full");
302                        }
303                    }
304                    _ => {
305                        let batch =
306                            ((user_data & Self::BATCH_MASK) >> Self::BATCH) as usize;
307                        let user_data = (user_data & Self::DATA_MASK) as usize;
308
309                        let result = entry.result();
310                        if result == -libc::ECANCELED {
311                            handlers[batch].modified = true;
312                            handlers[batch].hnd.canceled(user_data);
313                        } else {
314                            let result = if result < 0 {
315                                Err(io::Error::from_raw_os_error(-result))
316                            } else {
317                                #[allow(clippy::cast_sign_loss)]
318                                Ok(result as _)
319                            };
320                            handlers[batch].modified = true;
321                            handlers[batch]
322                                .hnd
323                                .completed(user_data, entry.flags(), result);
324                        }
325                    }
326                }
327            }
328            for h in handlers.iter_mut() {
329                h.tick();
330            }
331            self.handlers.set(Some(handlers));
332        }
333    }
334}
335
336impl AsRawFd for Driver {
337    fn as_raw_fd(&self) -> RawFd {
338        self.fd
339    }
340}
341
342impl crate::Reactor for Driver {
343    fn tcp_connect(&self, addr: net::SocketAddr, cfg: SharedCfg) -> Receiver<Io> {
344        let addr = SockAddr::from(addr);
345        let result = Socket::new(addr.domain(), Type::STREAM, Some(Protocol::TCP))
346            .and_then(crate::helpers::prep_socket)
347            .map(move |sock| (addr, sock));
348
349        match result {
350            Err(err) => Receiver::new(Err(err)),
351            Ok((addr, sock)) => {
352                super::connect::ConnectOps::get(self).connect(sock, addr, cfg)
353            }
354        }
355    }
356
357    fn unix_connect(&self, addr: std::path::PathBuf, cfg: SharedCfg) -> Receiver<Io> {
358        let result = SockAddr::unix(addr).and_then(|addr| {
359            Socket::new(addr.domain(), Type::STREAM, None)
360                .and_then(crate::helpers::prep_socket)
361                .map(move |sock| (addr, sock))
362        });
363
364        match result {
365            Err(err) => Receiver::new(Err(err)),
366            Ok((addr, sock)) => {
367                super::connect::ConnectOps::get(self).connect(sock, addr, cfg)
368            }
369        }
370    }
371
372    fn from_tcp_stream(&self, stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io> {
373        stream.set_nodelay(true)?;
374
375        Ok(Io::new(
376            TcpStream(
377                crate::helpers::prep_socket(Socket::from(stream))?,
378                StreamOps::get(self),
379            ),
380            cfg,
381        ))
382    }
383
384    #[cfg(unix)]
385    fn from_unix_stream(&self, stream: OsUnixStream, cfg: SharedCfg) -> io::Result<Io> {
386        Ok(Io::new(
387            UnixStream(
388                crate::helpers::prep_socket(Socket::from(stream))?,
389                StreamOps::get(self),
390            ),
391            cfg,
392        ))
393    }
394}
395
396impl ntex_rt::Driver for Driver {
397    /// Poll the driver and handle completed operations.
398    fn run(&self, rt: &Runtime) -> io::Result<()> {
399        let ring = &self.inner.ring;
400        let sq = ring.submission();
401        let mut cq = unsafe { ring.completion_shared() };
402        let submitter = ring.submitter();
403        let result = loop {
404            self.poll_completions(&mut cq, sq);
405
406            let more_tasks = match rt.poll() {
407                PollResult::Pending => false,
408                PollResult::PollAgain => true,
409                PollResult::Ready => break Ok(()),
410            };
411            let more_changes = self.apply_changes(sq);
412
413            // squeue has to sync after we apply all changes
414            // otherwise ring won't see any change in submit call
415            sq.sync();
416
417            let result = if more_changes || more_tasks {
418                submitter.submit()
419            } else {
420                submitter.submit_and_wait(1)
421            };
422
423            if let Err(e) = result {
424                match e.raw_os_error() {
425                    Some(libc::ETIME | libc::EBUSY | libc::EAGAIN | libc::EINTR) => {
426                        log::info!("Ring submit interrupted, {e:?}");
427                    }
428                    _ => break Err(e),
429                }
430            }
431        };
432
433        // cleanup handlers
434        if result.is_ok() {
435            for mut h in self.handlers.take().unwrap().into_iter() {
436                h.hnd.cleanup();
437            }
438        }
439
440        result
441    }
442
443    /// Get notification handle
444    fn handle(&self) -> Box<dyn Notify> {
445        Box::new(self.notifier.handle())
446    }
447}
448
449#[derive(Debug)]
450pub(crate) struct Notifier {
451    fd: Arc<OwnedFd>,
452}
453
454impl Notifier {
455    /// Create a new notifier.
456    pub(crate) fn new() -> io::Result<Self> {
457        let fd = syscall!(libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK))?;
458        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
459        Ok(Self { fd: Arc::new(fd) })
460    }
461
462    pub(crate) fn clear(&self) -> io::Result<()> {
463        loop {
464            let mut buffer = [0u64];
465            let res = syscall!(libc::read(
466                self.fd.as_raw_fd(),
467                buffer.as_mut_ptr().cast(),
468                mem::size_of::<u64>()
469            ));
470            #[allow(clippy::cast_possible_wrap)]
471            match res {
472                Ok(len) => {
473                    debug_assert_eq!(len, mem::size_of::<u64>() as isize);
474                    break Ok(());
475                }
476                // Clear the next time
477                Err(e) if e.kind() == io::ErrorKind::WouldBlock => break Ok(()),
478                // Just like read_exact
479                Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
480                Err(e) => break Err(e),
481            }
482        }
483    }
484
485    pub(crate) fn handle(&self) -> NotifyHandle {
486        NotifyHandle::new(self.fd.clone())
487    }
488}
489
490impl AsRawFd for Notifier {
491    fn as_raw_fd(&self) -> RawFd {
492        self.fd.as_raw_fd()
493    }
494}
495
496#[derive(Clone, Debug)]
497/// A notify handle to the driver.
498pub(crate) struct NotifyHandle {
499    fd: Arc<OwnedFd>,
500}
501
502impl NotifyHandle {
503    pub(crate) fn new(fd: Arc<OwnedFd>) -> Self {
504        Self { fd }
505    }
506}
507
508impl Notify for NotifyHandle {
509    /// Notify the driver.
510    fn notify(&self) -> io::Result<()> {
511        let data = 1u64;
512        syscall!(libc::write(
513            self.fd.as_raw_fd(),
514            (&raw const data).cast(),
515            std::mem::size_of::<u64>(),
516        ))?;
517        Ok(())
518    }
519}
520
521impl fmt::Debug for Driver {
522    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523        f.debug_struct("Driver")
524            .field("fd", &self.fd)
525            .field("hid", &self.hid)
526            .field("nodifier", &self.notifier)
527            .finish()
528    }
529}
530
531impl fmt::Debug for DriverApi {
532    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533        f.debug_struct("DriverApi")
534            .field("batch", &self.batch)
535            .finish()
536    }
537}