lx 0.4.0

A no_std crate to use Linux system calls
Documentation
use core::{
    mem,
    ptr,
};

use super::abi::*;
use crate::{
    result_from_value,
    sigset_t,
    unit_result_from_value,
    AsRawFd,
    OwnedFd,
    RawFd,
};

// Submission queue entry flags.
pub const IOSQE_FIXED_FILE: u8 = 0x1;
pub const IOSQE_IO_DRAIN: u8 = 0x2;
pub const IOSQE_IO_LINK: u8 = 0x4;
pub const IOSQE_IO_HARDLINK: u8 = 0x8;
pub const IOSQE_ASYNC: u8 = 0x10;
pub const IOSQE_BUFFER_SELECT: u8 = 0x20;

// Opcodes.
pub const IORING_OP_NOP: u8 = 0;
pub const IORING_OP_READV: u8 = 1;
pub const IORING_OP_WRITEV: u8 = 2;
pub const IORING_OP_FSYNC: u8 = 3;
pub const IORING_OP_READ_FIXED: u8 = 4;
pub const IORING_OP_WRITE_FIXED: u8 = 5;
pub const IORING_OP_POLL_ADD: u8 = 6;
pub const IORING_OP_POLL_REMOVE: u8 = 7;
pub const IORING_OP_SYNC_FILE_RANGE: u8 = 8;
pub const IORING_OP_SENDMSG: u8 = 9;
pub const IORING_OP_RECVMSG: u8 = 10;
pub const IORING_OP_TIMEOUT: u8 = 11;
pub const IORING_OP_TIMEOUT_REMOVE: u8 = 12;
pub const IORING_OP_ACCEPT: u8 = 13;
pub const IORING_OP_ASYNC_CANCEL: u8 = 14;
pub const IORING_OP_LINK_TIMEOUT: u8 = 15;
pub const IORING_OP_CONNECT: u8 = 16;
pub const IORING_OP_FALLOCATE: u8 = 17;
pub const IORING_OP_OPENAT: u8 = 18;
pub const IORING_OP_CLOSE: u8 = 19;
pub const IORING_OP_FILES_UPDATE: u8 = 20;
pub const IORING_OP_STATX: u8 = 21;
pub const IORING_OP_READ: u8 = 22;
pub const IORING_OP_WRITE: u8 = 23;
pub const IORING_OP_FADVISE: u8 = 24;
pub const IORING_OP_MADVISE: u8 = 25;
pub const IORING_OP_SEND: u8 = 26;
pub const IORING_OP_RECV: u8 = 27;
pub const IORING_OP_OPENAT2: u8 = 28;
pub const IORING_OP_EPOLL_CTL: u8 = 29;
pub const IORING_OP_SPLICE: u8 = 30;
pub const IORING_OP_PROVIDE_BUFFERS: u8 = 31;
pub const IORING_OP_REMOVE_BUFFERS: u8 = 32;
pub const IORING_OP_TEE: u8 = 33;
pub const IORING_OP_SHUTDOWN: u8 = 34;
pub const IORING_OP_RENAMEAT: u8 = 35;
pub const IORING_OP_UNLINKAT: u8 = 36;
pub const IORING_OP_MKDIRAT: u8 = 37;
pub const IORING_OP_SYMLINKAT: u8 = 38;
pub const IORING_OP_LINKAT: u8 = 39;

// Fsync flags.
pub const IORING_FSYNC_DATASYNC: u32 = 0x1;

// Timeout flags.
pub const IORING_TIMEOUT_ABS: u32 = 0x1;
pub const IORING_TIMEOUT_UPDATE: u32 = 0x2;
pub const IORING_TIMEOUT_BOOTTIME: u32 = 0x4;
pub const IORING_TIMEOUT_REALTIME: u32 = 0x8;
pub const IORING_LINK_TIMEOUT_UPDATE: u32 = 0x10;
pub const IORING_TIMEOUT_CLOCK_MASK: u32 = IORING_TIMEOUT_BOOTTIME | IORING_TIMEOUT_REALTIME;
pub const IORING_TIMEOUT_UPDATE_MASK: u32 = IORING_TIMEOUT_UPDATE | IORING_LINK_TIMEOUT_UPDATE;

// Splice flags.
pub const SPLICE_F_FD_IN_FIXED: u32 = 0x80000000;

// Poll flags.
pub const IORING_POLL_ADD_MULTI: u32 = 0x1;
pub const IORING_POLL_UPDATE_EVENTS: u32 = 0x2;
pub const IORING_POLL_UPDATE_USER_DATA: u32 = 0x4;

// Completion entry flags.
pub const IORING_CQE_F_BUFFER: u32 = 0x1;
pub const IORING_CQE_F_MORE: u32 = 0x2;

pub const IORING_CQE_BUFFER_SHIFT: u8 = 16;

pub const IORING_OFF_SQ_RING: usize = 0x0;
pub const IORING_OFF_CQ_RING: usize = 0x8000000;
pub const IORING_OFF_SQES: usize = 0x10000000;

pub const IORING_SQ_NEED_WAKEUP: u32 = 0x1;
pub const IORING_SQ_CQ_OVERFLOW: u32 = 0x2;

pub const IORING_CQ_EVENTFD_DISABLED: u32 = 0x2;

pub const IORING_ENTER_GETEVENTS: u32 = 0x1;
pub const IORING_ENTER_SQ_WAKEUP: u32 = 0x2;
pub const IORING_ENTER_SQ_WAIT: u32 = 0x4;
pub const IORING_ENTER_EXT_ARG: u32 = 0x8;

// Setup flags.
pub const IORING_SETUP_IOPOLL: u32 = 0x1;
pub const IORING_SETUP_SQPOLL: u32 = 0x2;
pub const IORING_SETUP_SQ_AFF: u32 = 0x4;
pub const IORING_SETUP_CQSIZE: u32 = 0x8;
pub const IORING_SETUP_CLAMP: u32 = 0x10;
pub const IORING_SETUP_ATTACH_WQ: u32 = 0x20;
pub const IORING_SETUP_R_DISABLED: u32 = 0x40;
pub const IORING_SETUP_SUBMIT_ALL: u32 = 0x80;
pub const IORING_SETUP_COOP_TASKRUN: u32 = 0x100;
pub const IORING_SETUP_TASKRUN_FLAG: u32 = 0x200;
pub const IORING_SETUP_SQE128: u32 = 0x400;
pub const IORING_SETUP_CQE32: u32 = 0x800;
pub const IORING_SETUP_SINGLE_ISSUER: u32 = 0x1000;
pub const IORING_SETUP_DEFER_TASKRUN: u32 = 0x2000;
pub const IORING_SETUP_NO_MMAP: u32 = 0x4000;
pub const IORING_SETUP_REGISTERED_FD_ONLY: u32 = 0x8000;

// Features supported by the kernel.
pub const IORING_FEAT_SINGLE_MMAP: u32 = 0x1;
pub const IORING_FEAT_NODROP: u32 = 0x2;
pub const IORING_FEAT_SUBMIT_STABLE: u32 = 0x4;
pub const IORING_FEAT_RW_CUR_POS: u32 = 0x8;
pub const IORING_FEAT_CUR_PERSONALITY: u32 = 0x10;
pub const IORING_FEAT_FAST_POLL: u32 = 0x20;
pub const IORING_FEAT_POLL_32BITS: u32 = 0x40;
pub const IORING_FEAT_SQPOLL_NONFIXED: u32 = 0x80;
pub const IORING_FEAT_EXT_ARG: u32 = 0x100;
pub const IORING_FEAT_NATIVE_WORKERS: u32 = 0x200;
pub const IORING_FEAT_RSRC_TAGS: u32 = 0x400;

// Resource register codes.
pub const IORING_REGISTER_BUFFERS: u8 = 0;
pub const IORING_UNREGISTER_BUFFERS: u8 = 1;
pub const IORING_REGISTER_FILES: u8 = 2;
pub const IORING_UNREGISTER_FILES: u8 = 3;
pub const IORING_REGISTER_EVENTFD: u8 = 4;
pub const IORING_UNREGISTER_EVENTFD: u8 = 5;
pub const IORING_REGISTER_FILES_UPDATE: u8 = 6;
pub const IORING_REGISTER_EVENTFD_ASYNC: u8 = 7;
pub const IORING_REGISTER_PROBE: u8 = 8;
pub const IORING_REGISTER_PERSONALITY: u8 = 9;
pub const IORING_UNREGISTER_PERSONALITY: u8 = 10;
pub const IORING_REGISTER_RESTRICTIONS: u8 = 11;
pub const IORING_REGISTER_ENABLE_RINGS: u8 = 12;
pub const IORING_REGISTER_FILES2: u8 = 13;
pub const IORING_REGISTER_FILES_UPDATE2: u8 = 14;
pub const IORING_REGISTER_BUFFERS2: u8 = 15;
pub const IORING_REGISTER_BUFFERS_UPDATE: u8 = 16;
pub const IORING_REGISTER_IOWQ_AFF: u8 = 17;
pub const IORING_UNREGISTER_IOWQ_AFF: u8 = 18;
pub const IORING_REGISTER_IOWQ_MAX_WORKERS: u8 = 19;

pub const IO_URING_OP_SUPPORTED: u16 = 0x1;

pub const IORING_RESTRICTION_REGISTER_OP: u8 = 0;
pub const IORING_RESTRICTION_SQE_OP: u8 = 1;
pub const IORING_RESTRICTION_SQE_FLAGS_ALLOWED: u8 = 2;
pub const IORING_RESTRICTION_SQE_FLAGS_REQUIRED: u8 = 3;

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_sqe {
    pub opcode: u8,
    pub flags: u8,
    pub ioprio: u16,
    pub fd: RawFd,
    pub off: u64,
    pub addr: u64,
    pub len: u32,
    pub op_flags: u32,
    pub user_data: u64,
    pub buf_index_or_group: u16,
    pub personality: u16,
    pub splice_fd_in_or_file_index: u32,
    zero: [u64; 2],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_cqe {
    pub user_data: u64,
    pub ret: i32,
    pub flags: u32,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_sqring_offsets {
    pub head: u32,
    pub tail: u32,
    pub ring_mask: u32,
    pub ring_entries: u32,
    pub flags: u32,
    pub dropped: u32,
    pub array: u32,
    zero: [u32; 3],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_cqring_offsets {
    pub head: u32,
    pub tail: u32,
    pub ring_mask: u32,
    pub ring_entries: u32,
    pub overflow: u32,
    pub cqes: u32,
    pub flags: u32,
    zero: [u32; 3],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_rsrc_register {
    pub nr: u32,
    zero: u32,
    zero2: u64,
    pub data: u64,
    pub tags: u64,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_rsrc_update {
    pub offset: u32,
    zero: u32,
    pub data: u64,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_rsrc_update2 {
    pub offset: u32,
    zero: u32,
    pub data: u64,
    pub tags: u64,
    pub nr: u32,
    zero2: u32,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_params {
    pub sq_entries: u32,
    pub cq_entries: u32,
    pub flags: u32,
    pub sq_thread_cpu: u32,
    pub sq_thread_idle: u32,
    pub features: u32,
    pub wq_fd: u32,
    zero: [u32; 3],
    pub sq_off: io_sqring_offsets,
    pub cq_off: io_cqring_offsets,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_probe_op {
    pub op: u8,
    zero: u8,
    pub flags: u16,
    zero2: u32,
}

#[allow(non_camel_case_types)]
#[repr(C)]
pub struct io_uring_probe {
    pub last_op: u8,
    pub ops_len: u8,
    zero: u16,
    zero2: [u32; 3],
    pub ops: [io_uring_probe_op],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_restriction {
    pub opcode: u16,
    pub op_or_flags: u8,
    zero: u8,
    zero2: [u32; 3],
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Clone, Copy)]
pub struct io_uring_getevents_arg {
    pub sigmask: u64,
    pub sigmask_sz: u32,
    zero: u32,
    pub ts: u64,
}

/// Registers a resource on the io_uring instance so that it can be used in submition entries.
///
/// # Safety
///
/// `args` must contain valid data. What this means depends on the `opcode`.
/// io_uring documentation or implementation.
#[inline]
pub unsafe fn io_uring_register(
    fd: &impl AsRawFd,
    opcode: u32,
    args: &[*const u8],
) -> crate::Result<()> {
    let ret = syscall_4(
        427,
        fd.as_raw_fd() as usize,
        opcode as usize,
        args.as_ptr() as usize,
        args.len(),
    ) as i32;
    unit_result_from_value(ret)
}

#[inline]
pub fn io_uring_setup(entries: u32, params: &mut io_uring_params) -> crate::Result<OwnedFd> {
    let ret = unsafe {
        syscall_2(
            425,
            entries as usize,
            params as *mut io_uring_params as usize,
        ) as i32
    };
    result_from_value(ret).map(OwnedFd::new)
}

/// Tell the kernel about new pending submission entries, and/or wait for new completion entries.
///
/// # Safety
///
/// If `arg` is not `ptr::null()`, then `arg_size` must contain the size of the value it is
/// pointing to, and the value must be valid.
#[inline]
pub unsafe fn io_uring_enter(
    fd_or_idx: u32,
    to_submit: u32,
    min_complete: u32,
    flags: u32,
    arg: *const u8,
    arg_size: usize,
) -> crate::Result<i32> {
    let ret = syscall_6(
        426,
        fd_or_idx as usize,
        to_submit as usize,
        min_complete as usize,
        flags as usize,
        arg as usize,
        arg_size,
    ) as i32;
    result_from_value(ret)
}

#[inline]
pub fn io_uring_enter_getevents(
    fd_or_idx: u32,
    to_submit: u32,
    min_complete: u32,
) -> crate::Result<i32> {
    unsafe {
        io_uring_enter(
            fd_or_idx,
            to_submit,
            min_complete,
            IORING_ENTER_GETEVENTS,
            ptr::null(),
            mem::size_of::<sigset_t>(),
        )
    }
}