Skip to content

Commit 3d67475

Browse files
committed
fs: add io_uring open operation
1 parent a2032b9 commit 3d67475

File tree

11 files changed

+413
-19
lines changed

11 files changed

+413
-19
lines changed

tokio/src/fs/open_options.rs

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
use crate::fs::{asyncify, File};
1+
use crate::fs::File;
22

33
use std::io;
44
use std::path::Path;
55

6+
cfg_tokio_unstable_uring! {
7+
mod uring_open_options;
8+
use uring_open_options::UringOpenOptions;
9+
use crate::runtime::driver::op::Op;
10+
}
11+
612
#[cfg(test)]
713
mod mock_open_options;
814
#[cfg(test)]
915
use mock_open_options::MockOpenOptions as StdOpenOptions;
1016
#[cfg(not(test))]
1117
use std::fs::OpenOptions as StdOpenOptions;
1218

13-
#[cfg(unix)]
14-
use std::os::unix::fs::OpenOptionsExt;
19+
cfg_not_tokio_unstable_uring! {
20+
#[cfg(unix)]
21+
use std::os::unix::fs::OpenOptionsExt;
22+
use crate::fs::asyncify;
23+
}
24+
1525
#[cfg(windows)]
1626
use std::os::windows::fs::OpenOptionsExt;
1727

@@ -79,7 +89,22 @@ use std::os::windows::fs::OpenOptionsExt;
7989
/// }
8090
/// ```
8191
#[derive(Clone, Debug)]
82-
pub struct OpenOptions(StdOpenOptions);
92+
pub struct OpenOptions(
93+
#[cfg(not(all(
94+
tokio_unstable_uring,
95+
feature = "rt",
96+
feature = "fs",
97+
target_os = "linux",
98+
)))]
99+
StdOpenOptions,
100+
#[cfg(all(
101+
tokio_unstable_uring,
102+
feature = "rt",
103+
feature = "fs",
104+
target_os = "linux",
105+
))]
106+
pub(crate) UringOpenOptions,
107+
);
83108

84109
impl OpenOptions {
85110
/// Creates a blank new set of options ready for configuration.
@@ -99,7 +124,22 @@ impl OpenOptions {
99124
/// let future = options.read(true).open("foo.txt");
100125
/// ```
101126
pub fn new() -> OpenOptions {
102-
OpenOptions(StdOpenOptions::new())
127+
OpenOptions(
128+
#[cfg(not(all(
129+
tokio_unstable_uring,
130+
feature = "rt",
131+
feature = "fs",
132+
target_os = "linux",
133+
)))]
134+
StdOpenOptions::new(),
135+
#[cfg(all(
136+
tokio_unstable_uring,
137+
feature = "rt",
138+
feature = "fs",
139+
target_os = "linux",
140+
))]
141+
UringOpenOptions::new(),
142+
)
103143
}
104144

105145
/// Sets the option for read access.
@@ -386,17 +426,35 @@ impl OpenOptions {
386426
/// [`Other`]: std::io::ErrorKind::Other
387427
/// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied
388428
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
389-
let path = path.as_ref().to_owned();
390-
let opts = self.0.clone();
429+
self.open_inner(path).await
430+
}
391431

392-
let std = asyncify(move || opts.open(path)).await?;
393-
Ok(File::from_std(std))
432+
cfg_not_tokio_unstable_uring! {
433+
async fn open_inner(&self, path: impl AsRef<Path>) -> io::Result<File> {
434+
let path = path.as_ref().to_owned();
435+
let opts = self.0.clone();
436+
437+
let std = asyncify(move || opts.open(path)).await?;
438+
Ok(File::from_std(std))
439+
}
440+
441+
/// Returns a mutable reference to the underlying `std::fs::OpenOptions`
442+
#[cfg(any(windows, unix))]
443+
pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
444+
&mut self.0
445+
}
394446
}
395447

396-
/// Returns a mutable reference to the underlying `std::fs::OpenOptions`
397-
#[cfg(any(windows, unix))]
398-
pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
399-
&mut self.0
448+
cfg_tokio_unstable_uring! {
449+
async fn open_inner(&self, path: impl AsRef<Path>) -> io::Result<File> {
450+
Op::open(path.as_ref(), self)?.await
451+
}
452+
453+
/// Returns a mutable reference to the underlying `std::fs::OpenOptions`
454+
#[cfg(any(windows, unix))]
455+
pub(super) fn as_inner_mut(&mut self) -> &mut UringOpenOptions {
456+
&mut self.0
457+
}
400458
}
401459
}
402460

@@ -649,9 +707,23 @@ cfg_windows! {
649707
}
650708
}
651709

652-
impl From<StdOpenOptions> for OpenOptions {
653-
fn from(options: StdOpenOptions) -> OpenOptions {
654-
OpenOptions(options)
710+
cfg_not_tokio_unstable_uring! {
711+
impl From<StdOpenOptions> for OpenOptions {
712+
fn from(options: StdOpenOptions) -> OpenOptions {
713+
OpenOptions(options)
714+
}
715+
}
716+
}
717+
718+
cfg_tokio_unstable_uring! {
719+
impl From<StdOpenOptions> for OpenOptions {
720+
fn from(_options: StdOpenOptions) -> OpenOptions {
721+
// It's not straitforward to convert from std's OpenOptions to io_uring's one.
722+
// * https://github.com/rust-lang/rust/issues/74943
723+
// * https://github.com/rust-lang/rust/issues/76801
724+
725+
panic!("Conversion from std's OpenOptions to io_uring's one is not supported")
726+
}
655727
}
656728
}
657729

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use std::io;
2+
3+
#[derive(Debug, Clone)]
4+
pub(crate) struct UringOpenOptions {
5+
pub(crate) read: bool,
6+
pub(crate) write: bool,
7+
pub(crate) append: bool,
8+
pub(crate) truncate: bool,
9+
pub(crate) create: bool,
10+
pub(crate) create_new: bool,
11+
pub(crate) mode: libc::mode_t,
12+
pub(crate) custom_flags: libc::c_int,
13+
}
14+
15+
impl UringOpenOptions {
16+
pub(crate) fn new() -> Self {
17+
Self {
18+
read: false,
19+
write: false,
20+
append: false,
21+
truncate: false,
22+
create: false,
23+
create_new: false,
24+
mode: 0o666,
25+
custom_flags: 0,
26+
}
27+
}
28+
29+
pub(crate) fn append(&mut self, append: bool) -> &mut Self {
30+
self.append = append;
31+
self
32+
}
33+
34+
pub(crate) fn create(&mut self, create: bool) -> &mut Self {
35+
self.create = create;
36+
self
37+
}
38+
39+
pub(crate) fn create_new(&mut self, create_new: bool) -> &mut Self {
40+
self.create_new = create_new;
41+
self
42+
}
43+
44+
pub(crate) fn read(&mut self, read: bool) -> &mut Self {
45+
self.read = read;
46+
self
47+
}
48+
49+
pub(crate) fn write(&mut self, write: bool) -> &mut Self {
50+
self.write = write;
51+
self
52+
}
53+
54+
pub(crate) fn truncate(&mut self, truncate: bool) -> &mut Self {
55+
self.truncate = truncate;
56+
self
57+
}
58+
59+
pub(crate) fn mode(&mut self, mode: u32) -> &mut Self {
60+
self.mode = mode as libc::mode_t;
61+
self
62+
}
63+
64+
pub(crate) fn custom_flags(&mut self, flags: i32) -> &mut Self {
65+
self.custom_flags = flags;
66+
self
67+
}
68+
69+
pub(crate) fn access_mode(&self) -> io::Result<libc::c_int> {
70+
match (self.read, self.write, self.append) {
71+
(true, false, false) => Ok(libc::O_RDONLY),
72+
(false, true, false) => Ok(libc::O_WRONLY),
73+
(true, true, false) => Ok(libc::O_RDWR),
74+
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
75+
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
76+
(false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
77+
}
78+
}
79+
80+
pub(crate) fn creation_mode(&self) -> io::Result<libc::c_int> {
81+
match (self.write, self.append) {
82+
(true, false) => {}
83+
(false, false) => {
84+
if self.truncate || self.create || self.create_new {
85+
return Err(io::Error::from_raw_os_error(libc::EINVAL));
86+
}
87+
}
88+
(_, true) => {
89+
if self.truncate && !self.create_new {
90+
return Err(io::Error::from_raw_os_error(libc::EINVAL));
91+
}
92+
}
93+
}
94+
95+
Ok(match (self.create, self.truncate, self.create_new) {
96+
(false, false, false) => 0,
97+
(true, false, false) => libc::O_CREAT,
98+
(false, true, false) => libc::O_TRUNC,
99+
(true, true, false) => libc::O_CREAT | libc::O_TRUNC,
100+
(_, _, true) => libc::O_CREAT | libc::O_EXCL,
101+
})
102+
}
103+
}

tokio/src/io/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,7 @@ cfg_io_blocking! {
293293
pub(crate) use crate::blocking::JoinHandle as Blocking;
294294
}
295295
}
296+
297+
cfg_tokio_unstable_uring! {
298+
pub(crate) mod uring;
299+
}

tokio/src/io/uring/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub(crate) mod open;
2+
pub(crate) mod utils;

tokio/src/io/uring/open.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use super::utils::cstr;
2+
use crate::{
3+
fs::OpenOptions,
4+
runtime::driver::op::{Completable, CqeResult, Op},
5+
};
6+
use io_uring::{opcode, types};
7+
use std::{ffi::CString, io, os::fd::FromRawFd, path::Path};
8+
9+
pub(crate) struct Open {
10+
#[allow(dead_code)]
11+
path: CString,
12+
}
13+
14+
impl Completable for Open {
15+
type Output = crate::fs::File;
16+
fn complete(self, cqe: CqeResult) -> io::Result<Self::Output> {
17+
let fd = cqe.result? as i32;
18+
let file = unsafe { crate::fs::File::from_raw_fd(fd) };
19+
Ok(file)
20+
}
21+
}
22+
23+
impl Op<Open> {
24+
/// Submit a request to open a file.
25+
pub(crate) fn open(path: &Path, options: &OpenOptions) -> io::Result<Op<Open>> {
26+
let inner_opt = options;
27+
let path = cstr(path)?;
28+
29+
let custom_flags = inner_opt.0.custom_flags;
30+
let flags = libc::O_CLOEXEC
31+
| options.0.access_mode()?
32+
| options.0.creation_mode()?
33+
| (custom_flags & !libc::O_ACCMODE);
34+
35+
let open_op = opcode::OpenAt::new(types::Fd(libc::AT_FDCWD), path.as_ptr())
36+
.flags(flags)
37+
.mode(inner_opt.0.mode)
38+
.build();
39+
40+
// SAFETY: Parameters are valid for the entire duration of the operation
41+
let op = unsafe { Op::new(open_op, Open { path }) };
42+
Ok(op)
43+
}
44+
}

tokio/src/io/uring/utils.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use std::{ffi::CString, io, path::Path};
2+
3+
pub(crate) fn cstr(p: &Path) -> io::Result<CString> {
4+
use std::os::unix::ffi::OsStrExt;
5+
Ok(CString::new(p.as_os_str().as_bytes())?)
6+
}

tokio/src/macros/cfg.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,3 +727,17 @@ macro_rules! cfg_tokio_unstable_uring {
727727
)*
728728
};
729729
}
730+
731+
macro_rules! cfg_not_tokio_unstable_uring {
732+
($($item:item)*) => {
733+
$(
734+
#[cfg(not(all(
735+
tokio_unstable_uring,
736+
feature = "rt",
737+
feature = "fs",
738+
target_os = "linux",
739+
)))]
740+
$item
741+
)*
742+
};
743+
}

tokio/src/runtime/builder.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,15 @@ impl Builder {
338338
all(unix, feature = "signal")
339339
))]
340340
self.enable_io();
341+
342+
#[cfg(all(
343+
tokio_unstable_uring,
344+
feature = "rt",
345+
feature = "fs",
346+
target_os = "linux",
347+
))]
348+
self.enable_uring();
349+
341350
#[cfg(feature = "time")]
342351
self.enable_time();
343352

@@ -1575,6 +1584,30 @@ cfg_time! {
15751584
}
15761585
}
15771586

1587+
cfg_tokio_unstable_uring! {
1588+
impl Builder {
1589+
/// Enables the `tokio-uring` driver.
1590+
///
1591+
/// Doing this enables using io_uring operations on the runtime.
1592+
///
1593+
/// # Examples
1594+
///
1595+
/// ```
1596+
/// use tokio::runtime;
1597+
///
1598+
/// let rt = runtime::Builder::new_multi_thread()
1599+
/// .enable_uring()
1600+
/// .build()
1601+
/// .unwrap();
1602+
/// ```
1603+
pub fn enable_uring(&mut self) -> &mut Self {
1604+
// Currently, the uring flag is represented as `enable_io`.
1605+
self.enable_io = true;
1606+
self
1607+
}
1608+
}
1609+
}
1610+
15781611
cfg_test_util! {
15791612
impl Builder {
15801613
/// Controls if the runtime's clock starts paused or advancing.

tokio/src/runtime/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ pub(crate) mod context;
323323

324324
pub(crate) mod park;
325325

326-
mod driver;
326+
pub(crate) mod driver;
327327

328328
pub(crate) mod scheduler;
329329

0 commit comments

Comments
 (0)