Skip to main content

moq_uring/
error.rs

1/// Why a worker or socket could not be set up or has failed.
2#[derive(thiserror::Error, Debug)]
3#[non_exhaustive]
4pub enum Error {
5	/// The kernel cannot run this worker at all; there is no fallback here.
6	/// The message names the missing feature and the running kernel. Callers
7	/// that want a fallback should construct a tokio-based stack instead.
8	#[error("unsupported kernel: {0}")]
9	Unsupported(String),
10
11	/// An io_uring or socket operation failed.
12	#[error("io error: {0}")]
13	Io(#[from] std::io::Error),
14}
15
16impl Error {
17	/// A failed ring setup or registration, naming the locked-memory limit when
18	/// that is what ran out.
19	///
20	/// The kernel charges every ring and provided-buffer ring to the user's
21	/// `RLIMIT_MEMLOCK`, shared with every other io_uring that user runs, so a
22	/// bare `ENOMEM` points at the wrong culprit.
23	pub(crate) fn ring(err: std::io::Error) -> Self {
24		if err.raw_os_error() != Some(libc::ENOMEM) {
25			return Self::Io(err);
26		}
27		let mut limit = libc::rlimit {
28			rlim_cur: 0,
29			rlim_max: 0,
30		};
31		// SAFETY: valid out-pointer.
32		let limit = match unsafe { libc::getrlimit(libc::RLIMIT_MEMLOCK, &mut limit) } {
33			0 => format!("{} KiB", limit.rlim_cur / 1024),
34			_ => "unknown".into(),
35		};
36		Self::Io(std::io::Error::new(
37			err.kind(),
38			format!(
39				"{err}: io_uring memory counts against RLIMIT_MEMLOCK ({limit}), shared by every process of this user; \
40				 raise it with `ulimit -l` or systemd's `LimitMEMLOCK=`"
41			),
42		))
43	}
44}
45
46#[cfg(test)]
47mod tests {
48	use super::*;
49
50	#[test]
51	fn a_ring_enomem_names_the_memlock_limit() {
52		let err = Error::ring(std::io::Error::from_raw_os_error(libc::ENOMEM));
53		assert!(err.to_string().contains("RLIMIT_MEMLOCK"), "{err}");
54
55		let err = Error::ring(std::io::Error::from_raw_os_error(libc::EBADF));
56		assert!(!err.to_string().contains("RLIMIT_MEMLOCK"), "{err}");
57	}
58}