uring 0.1.0

Pure Rust library for io_uring
use std::io::{self, Error};
use std::mem;
use std::os::unix::io::RawFd;
use std::ptr::NonNull;
use sys_call::sys_call;

mod offsets;
use offsets::{CompletionOffsets, SubmissionOffsets};

mod queue;
use queue::Queue;

mod completion;
pub use completion::{CompletionQueue, CompletionQueueEntry};

mod submission;
pub use submission::{SubmissionQueue, SubmissionQueueEntry};

#[repr(C)]
#[derive(Debug)]
struct Params {
    sq_entries: u32,
    cq_entries: u32,
    flags: u32,
    sq_thread_cpu: u32,
    sq_thread_idle: u32,
    resv: [u32; 5],
    sq_off: SubmissionOffsets,
    cq_off: CompletionOffsets,
}

pub struct IoUring {
    pub sq_entries: u32,
    pub cq_entries: u32,
    pub flags: u32,
    pub sq_thread_cpu: u32,
    pub sq_thread_idle: u32,
}

impl Default for IoUring {
    fn default() -> Self {
        Self {
            sq_entries: 0,
            cq_entries: 0,
            flags: 0,
            sq_thread_cpu: 0,
            sq_thread_idle: 0,
        }
    }
}

impl IoUring {
    pub fn with_entries(sq_entries: u32) -> Self {
        Self {
            sq_entries,
            ..Default::default()
        }
    }
    pub fn setup(&self) -> io::Result<(SubmissionQueue, CompletionQueue)> {
        let mut params = Params {
            sq_entries: self.sq_entries,
            cq_entries: self.cq_entries,
            flags: self.flags,
            sq_thread_cpu: self.sq_thread_cpu,
            sq_thread_idle: self.sq_thread_idle,
            resv: [0; 5],
            sq_off: Default::default(),
            cq_off: Default::default(),
        };

        to_result(unsafe {
            const IO_URING_SETUP: isize = 425;
            sys_call!(
                IO_URING_SETUP,
                self.sq_entries as isize,
                &mut params as *mut _ as isize
            )
        })
        .and_then(|fd| {
            to_result(unsafe {
                const OFFSET_SQ_RING: isize = 0;
                mmap(
                    params.sq_off.array as isize
                        + (self.sq_entries as isize * mem::size_of::<u32>() as isize),
                    fd,
                    OFFSET_SQ_RING,
                )
            })
            .and_then(|sq_ptr| {
                to_result(unsafe {
                    const OFFSET_CQ_RING: isize = 0x8000000;
                    mmap(
                        params.cq_off.cqes as isize
                            + (self.cq_entries as isize
                                * mem::size_of::<CompletionQueueEntry>() as isize),
                        fd,
                        OFFSET_CQ_RING,
                    )
                })
                .and_then(|cq_ptr| {
                    to_result(unsafe {
                        const OFFSET_SQES: isize = 0x10000000;
                        mmap(
                            self.sq_entries as isize
                                * mem::size_of::<SubmissionQueueEntry>() as isize,
                            fd,
                            OFFSET_SQES,
                        )
                    })
                    .map(|sqes| {
                        let sqes = sqes as *mut SubmissionQueueEntry;
                        let cqes = (cq_ptr + params.cq_off.cqes as isize) as *mut _;
                        unsafe {
                            (
                                SubmissionQueue {
                                    queue: Queue {
                                        head: field(sq_ptr, params.sq_off.head),
                                        tail: field(sq_ptr, params.sq_off.tail),
                                        mask: field(sq_ptr, params.sq_off.mask),
                                        len: self.sq_entries,
                                        entries: NonNull::new_unchecked(sqes),
                                    },
                                    array: field(sq_ptr, params.sq_off.array),
                                },
                                CompletionQueue {
                                    fd: fd as RawFd,
                                    queue: Queue {
                                        head: field(cq_ptr, params.cq_off.head),
                                        tail: field(cq_ptr, params.cq_off.tail),
                                        mask: field(cq_ptr, params.cq_off.mask),
                                        len: self.cq_entries,
                                        entries: NonNull::new_unchecked(cqes),
                                    },
                                },
                            )
                        }
                    })
                })
            })
        })
    }
}

fn to_result(ret: isize) -> io::Result<isize> {
    if ret > 0 {
        Ok(ret)
    } else {
        Err(Error::from_raw_os_error(ret as i32 * -1))
    }
}

unsafe fn mmap(size: isize, fd: isize, offset: isize) -> isize {
    sys_call!(9, 0, size, 1 | 2, 0x0001 | 0x08000, fd, offset)
}

unsafe fn field(ptr: isize, field: u32) -> NonNull<u32> {
    NonNull::new_unchecked((ptr + field as isize) as *mut u32)
}