-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fs: add io_uring open operation
#7321
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
Conversation
|
I ran a benchmark in commit d0dc6b2. settings$ uname -a
Linux mox692-ThinkPad-P51 6.8.0-59-generic 61-Ubuntu SMP PREEMPT_DYNAMIC Fri Apr 11 23:16:11 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
# On commit `d0dc6b2`,
# uring bench
$ RUSTFLAGS="--cfg tokio_unstable_uring" cargo bench open_many_files
# thread pool bench
$ cargo bench open_many_filesSingle-threaded runtime
Multi-threaded runtime
While performance improves in a current thread runtime, the results actually regress as the number of threads increases. Currently there is only one global ring in the runtime, so increasing threads may be causing lock contention. I'll look into whether I can reduce the lock contention. |
|
The main feedback I have here is that although it's ok to use |
|
Update: Sync with master and now the base branch is 7357. |
|
This is ready for review. |
Darksonn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks reasonable to me. Have we verified whether CI runs a recent enough Linux to actually run the io_uring code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/tokio-rs/tokio/actions/runs/15971607056/job/45043945544?pr=7321
SLOW [>21480.000s] tokio::fs_uring shutdown_runtime_while_performing_io_uring_ops
This job ran for six hours and was canceled due to timeout. Were we just unlucky? Or was something else slows down the test?
|
This doesn't seem like a random occurrence, I'll take a look into it. |
|
To fix #7321 (review), I opened another PR #7436 since it's somewhat unrelated to |
Darksonn
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks reasonable to me.
tokio/src/runtime/driver/op.rs
Outdated
| #[allow(dead_code)] | ||
| #[derive(Debug)] | ||
| pub(crate) enum CancelData {} | ||
| pub(crate) enum CancelData { | ||
| Open(Open), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the dead code annotation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed some #[allow(dead_code)] annotations, but it seems that some of them still require the annotation. So I also added comments to explain that.
tokio/tests/fs_uring.rs
Outdated
| // Avoid busy looping. | ||
| tokio::time::sleep(std::time::Duration::from_millis(10)).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use yield_now() here? Sleeping is best avoided in tests if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using yield_now should work, but now I'm seeing a test failure after this change (6dd50a3) ... Let me figure out what's going on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out there was a flaw in the drop implementation of UringContext, so I pushed a commit (76d1537) to fix that.
Previous implementation used a wrong key when we remove the slab entry:
// commit: 1405ee87b11c6bbb558d2f411002fd587524cd88
fn drop(&mut self) {
...
let mut cancel_ops = Slab::new();
let mut keys_to_move = Vec::new();
...
for key in keys_to_move {
let lifecycle = self.remove_op(key);
// Here, cancel_ops generates a separate index from `self.ops`.
cancel_ops.insert(lifecycle);
}
while !cancel_ops.is_empty() {
...
for cqe in self.ring_mut().completion() {
let idx = cqe.user_data() as usize;
// Bug: We should not use `idx` gained from `cqe.user_data()` here,
// as it will not correspond to the index in `cancel_ops`
cancel_ops.remove(idx);
}
}
}|
I'll be back soon and address the comments. |
|
I'm reading this branch locally, would you mind fixing the clippy reports? RUSTFLAGS="--cfg tokio_uring" cargo +1.88 clippy --all --tests --all-features |
|
Does the clippy job need to be modified? |
I think we need to improve the clippy job to cover different combination of feature flags of Currently, it covers only one combination. tokio/.github/workflows/ci.yml Line 845 in 9f42305
|
|
I probably would only consider two combinations for clippy:
It's not critical that every combination is clippy-free. |
mox692
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clippy stuff is resolved, and I slightly fixed the test.
| // If io_uring is enabled (and not falling back to the thread pool), | ||
| // the first poll should return Pending. | ||
| let _pending = Box::pin(fut).poll_unpin(cx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the da0175b, I removed an assertion assert!(res.is_pending(), "Expected the open to be pending"); since it could be ready at first poll if it's using fallback logic. (For example, old kernel ci check could fail here)
ADD-SP
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me.
Overview
This PR implement io_uring version
opensystem call.Since common file operations, like
fs::readandfs::write, useopenunder the hood, I think suppporting open sysacall first is a good starting point.Also, since this is the first operation we actually ship, I've added:
enable_uring)Implementation
OpenOptions, the implementation is chosen based on the presence of cfg flags.open()method, which uses io_uring or thread_pool.open()onUringOpenOptions, if the machine does not supportio_uring, it will fall back to using the standardOpenOptions.Much of the code in this PR is ported from the
tokio-uringcrate.Why do we need our own
UringOpenOptions?The standard library’s
OpenOptionsdoes not expose accessors for its internal flags. However, when usingio_uringto perform theopensystem call, it's necessary to access these internal flags. Since this is not possible with the standard implementation, we define our ownUringOpenOptionstype.This limitation is tracked in the std (rust-lang/rust#76801), but it has not yet been resolved.