Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement I/O safety traits #134

Merged
merged 3 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ members = [ "io-uring-test", "io-uring-bench" ]
unstable = []
overwrite = [ "bindgen" ]
direct-syscall = [ "sc" ]
io_safety = []

[dependencies]
bitflags = "1"
Expand Down
30 changes: 21 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,24 @@ mod submit;
mod sys;
pub mod types;

use std::convert::TryInto;
use std::mem::ManuallyDrop;
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::{cmp, io, mem};

#[cfg(feature = "io_safety")]
use std::os::unix::io::{AsFd, BorrowedFd};

pub use cqueue::CompletionQueue;
pub use register::Probe;
pub use squeue::SubmissionQueue;
pub use submit::Submitter;
use util::{Fd, Mmap};
use util::{Mmap, OwnedFd};

/// IoUring instance
pub struct IoUring {
sq: squeue::Inner,
cq: cqueue::Inner,
fd: Fd,
fd: OwnedFd,
params: Parameters,
memory: ManuallyDrop<MemoryMap>,
}
Expand Down Expand Up @@ -83,7 +85,7 @@ impl IoUring {
// I really hope that Rust can safely use self-reference types.
#[inline]
unsafe fn setup_queue(
fd: &Fd,
fd: &OwnedFd,
p: &sys::io_uring_params,
) -> io::Result<(MemoryMap, squeue::Inner, cqueue::Inner)> {
let sq_len = p.sq_off.array as usize + p.sq_entries as usize * mem::size_of::<u32>();
Expand Down Expand Up @@ -121,10 +123,13 @@ impl IoUring {
}
}

let fd: Fd = unsafe {
sys::io_uring_setup(entries, &mut p)
.try_into()
.map_err(|_| io::Error::last_os_error())?
let fd: OwnedFd = unsafe {
let fd = sys::io_uring_setup(entries, &mut p);
if fd >= 0 {
OwnedFd::from_raw_fd(fd)
} else {
return Err(io::Error::last_os_error());
}
};

let (mm, sq, cq) = unsafe { setup_queue(&fd, &p)? };
Expand Down Expand Up @@ -451,3 +456,10 @@ impl AsRawFd for IoUring {
self.fd.as_raw_fd()
}
}

#[cfg(feature = "io_safety")]
impl AsFd for IoUring {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
6 changes: 3 additions & 3 deletions src/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{io, ptr};

use crate::register::{execute, Probe};
use crate::sys;
use crate::util::{cast_ptr, Fd};
use crate::util::{cast_ptr, OwnedFd};
use crate::Parameters;

#[cfg(feature = "unstable")]
Expand All @@ -19,7 +19,7 @@ use crate::types;
/// io_uring supports both directly performing I/O on buffers and file descriptors and registering
/// them beforehand. Registering is slow, but it makes performing the actual I/O much faster.
pub struct Submitter<'a> {
fd: &'a Fd,
fd: &'a OwnedFd,
params: &'a Parameters,

sq_head: *const atomic::AtomicU32,
Expand All @@ -30,7 +30,7 @@ pub struct Submitter<'a> {
impl<'a> Submitter<'a> {
#[inline]
pub(crate) const fn new(
fd: &'a Fd,
fd: &'a OwnedFd,
params: &'a Parameters,
sq_head: *const atomic::AtomicU32,
sq_tail: *const atomic::AtomicU32,
Expand Down
75 changes: 37 additions & 38 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::convert::TryFrom;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::io::AsRawFd;
use std::sync::atomic;
use std::{io, mem, ptr};
use std::{io, ptr};

/// A region of memory mapped using `mmap(2)`.
pub struct Mmap {
Expand All @@ -11,7 +10,7 @@ pub struct Mmap {

impl Mmap {
/// Map `len` bytes starting from the offset `offset` in the file descriptor `fd` into memory.
pub fn new(fd: &Fd, offset: libc::off_t, len: usize) -> io::Result<Mmap> {
pub fn new(fd: &OwnedFd, offset: libc::off_t, len: usize) -> io::Result<Mmap> {
unsafe {
match libc::mmap(
ptr::null_mut(),
Expand Down Expand Up @@ -60,49 +59,49 @@ impl Drop for Mmap {
}
}

/// An owned file descriptor.
pub struct Fd(pub RawFd);
pub use fd::OwnedFd;

impl TryFrom<RawFd> for Fd {
type Error = ();

#[inline]
fn try_from(value: RawFd) -> Result<Fd, Self::Error> {
if value >= 0 {
Ok(Fd(value))
} else {
Err(())
}
}
#[cfg(feature = "io_safety")]
mod fd {
pub use std::os::unix::io::OwnedFd;
}

impl AsRawFd for Fd {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0
#[cfg(not(feature = "io_safety"))]
mod fd {
use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};

/// API-compatible with the `OwnedFd` type in the Rust stdlib.
pub struct OwnedFd(RawFd);

impl AsRawFd for OwnedFd {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
}

impl IntoRawFd for Fd {
#[inline]
fn into_raw_fd(self) -> RawFd {
let fd = self.0;
mem::forget(self);
fd
impl IntoRawFd for OwnedFd {
#[inline]
fn into_raw_fd(self) -> RawFd {
let fd = self.0;
mem::forget(self);
fd
}
}
}

impl FromRawFd for Fd {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> Fd {
Fd(fd)
impl FromRawFd for OwnedFd {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> OwnedFd {
OwnedFd(fd)
}
}
}

impl Drop for Fd {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
impl Drop for OwnedFd {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}
}
Expand Down