use std::{io, os::fd::AsRawFd, ptr::NonNull};
use crate::DriverConfig;
use crate::driver::{FixedBufGuard, Ring};
pub(crate) struct Resources {
fixed_buf: FixedBufPool,
fixed_file: FixedFilePool,
}
pub(crate) struct ResourceToken {
pool: NonNull<Resources>,
idx: u16,
}
impl ResourceToken {
pub(crate) const fn fixed(pool: NonNull<Resources>, idx: u16) -> Self {
Self { pool, idx }
}
pub(crate) fn release(mut self) {
unsafe { self.pool.as_mut() }.release_fixed(self.idx);
}
}
fn capability_error(label: &'static str, err: io::Error) -> io::Error {
io::Error::other(format!(
"dope: required capability setup failed ({label}): {err}"
))
}
impl Resources {
pub(crate) fn new(ring: &Ring, cfg: &DriverConfig) -> io::Result<Self> {
Ok(Self {
fixed_file: FixedFilePool::new(ring, cfg.fixed_file_slots)
.map_err(|e| capability_error("fixed file registry", e))?,
fixed_buf: FixedBufPool::new(ring, cfg.fixed_buf_count, cfg.fixed_buf_len)
.map_err(|e| capability_error("fixed buffer registry", e))?,
})
}
pub(crate) fn release_fixed(&mut self, idx: u16) {
self.fixed_buf.release(idx);
}
pub(in crate::driver) fn fixed_guard(&mut self) -> Option<FixedBufGuard> {
let buf_index = self.fixed_buf.lease()?;
let (ptr, cap) = self.fixed_buf.ptr_len(buf_index);
let pool = NonNull::from(&mut *self);
Some(FixedBufGuard::new(
ResourceToken::fixed(pool, buf_index),
buf_index,
ptr,
cap,
))
}
pub(in crate::driver) fn register_fixed(
&mut self,
uring: &Ring,
io: crate::Fd,
) -> io::Result<Option<u32>> {
self.fixed_file.register(uring, io).map(Some)
}
pub(in crate::driver) fn unregister_fixed(&mut self, ring: &Ring, idx: u32) {
self.fixed_file.unregister(ring, idx);
}
}
struct FixedBufPool {
backing: Box<[u8]>,
buf_len: usize,
free: Vec<u16>,
}
impl FixedBufPool {
fn new(ring: &Ring, buf_count: usize, buf_len: usize) -> io::Result<Self> {
let total = buf_count.checked_mul(buf_len).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "fixed buf pool size overflow")
})?;
let mut backing = vec![0u8; total].into_boxed_slice();
let base = backing.as_mut_ptr();
let mut iovecs = Vec::with_capacity(buf_count);
for i in 0..buf_count {
let off = i * buf_len;
iovecs.push(libc::iovec {
iov_base: unsafe { base.add(off) } as *mut libc::c_void,
iov_len: buf_len,
});
}
if !iovecs.is_empty() {
unsafe {
ring.submitter().register_buffers(&iovecs)?;
}
}
let mut free = Vec::with_capacity(buf_count);
for i in (0..buf_count as u16).rev() {
free.push(i);
}
Ok(Self {
backing,
buf_len,
free,
})
}
fn lease(&mut self) -> Option<u16> {
self.free.pop()
}
fn release(&mut self, idx: u16) {
self.free.push(idx);
}
fn ptr_len(&self, idx: u16) -> (*mut u8, usize) {
let off = idx as usize * self.buf_len;
let ptr = unsafe { self.backing.as_ptr().add(off) } as *mut u8;
(ptr, self.buf_len)
}
}
struct FixedFilePool {
free: Vec<u32>,
}
impl FixedFilePool {
fn new(uring: &Ring, files: u32) -> io::Result<Self> {
uring.submitter().register_files_sparse(files)?;
let mut free = Vec::with_capacity(files as usize);
for i in 0..files {
free.push(i);
}
Ok(Self { free })
}
fn register(&mut self, uring: &Ring, fd: crate::Fd) -> io::Result<u32> {
let Some(idx) = self.free.pop() else {
return Err(io::Error::other("fixed file table exhausted"));
};
match uring
.submitter()
.register_files_update(idx, &[fd.as_raw_fd()])
{
Ok(1) => Ok(idx),
Ok(_) => {
self.free.push(idx);
Err(io::Error::other("register_files_update: unexpected count"))
}
Err(e) => {
self.free.push(idx);
Err(e)
}
}
}
fn unregister(&mut self, ring: &Ring, idx: u32) {
let _ = ring.submitter().register_files_update(idx, &[-1]);
self.free.push(idx);
}
}