Skip to content

7901: fs: add unstable io_uring uring_cmd support for File#65

Open
martin-augment wants to merge 1 commit intomasterfrom
pr-7901-2026-02-16-11-06-43
Open

7901: fs: add unstable io_uring uring_cmd support for File#65
martin-augment wants to merge 1 commit intomasterfrom
pr-7901-2026-02-16-11-06-43

Conversation

@martin-augment
Copy link
Owner

7901: To review by AI

@gemini-code-assist
Copy link

Summary of Changes

Hello @martin-augment, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces unstable support for the IORING_OP_URING_CMD operation within tokio::fs::File, leveraging io_uring on Linux. This enhancement allows users to asynchronously submit 16-byte device-specific command payloads, providing a more direct and potentially more performant way to interact with underlying file descriptors for specialized operations.

Highlights

  • New uring_cmd method for File: An async fn uring_cmd has been added to tokio::fs::File, enabling the execution of IORING_OP_URING_CMD operations for device-specific commands. This feature is unstable and requires specific tokio_unstable and io-uring feature flags.
  • Dedicated UringCmd module: A new module, tokio::io::uring::cmd, was introduced to encapsulate the logic for the UringCmd operation, including its Completable and Cancellable implementations.
  • Runtime integration and cancellation support: The UringCmd operation has been integrated into the tokio runtime's CancelData enum, ensuring proper cancellation handling for uring_cmd operations.
  • New test case: A new test, uring_cmd_is_available_via_file, was added to verify the basic functionality and availability of the uring_cmd method.
Changelog
  • tokio/src/fs/file.rs
    • Added the uring_cmd asynchronous method to the File struct, enabling IORING_OP_URING_CMD operations.
  • tokio/src/io/uring/cmd.rs
    • Created a new module defining the UringCmd struct and its Completable, Cancellable implementations, and the uring_cmd16 constructor for Op.
  • tokio/src/io/uring/mod.rs
    • Exported the newly created cmd module.
  • tokio/src/runtime/driver/op.rs
    • Imported the UringCmd struct.
    • Extended the CancelData enum to include UringCmd for cancellation support.
  • tokio/tests/fs_uring.rs
    • Imported File from tokio::fs.
    • Added a new #[tokio::test] named uring_cmd_is_available_via_file to test the basic usage of the uring_cmd method.
Activity
  • The pull request was created and is awaiting review.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Walkthrough

This pull request adds io_uring command support to Tokio's File API on Linux. A new uring_cmd async method is introduced on tokio::fs::File that accepts a command operation, 16-byte command data, and optional buffer index. The implementation includes a new UringCmd struct with Completable and Cancellable trait implementations, an Op::uring_cmd16 associated function to construct io_uring submission queue entries, and corresponding updates to the CancelData enum and module structure. The feature is guarded behind tokio_unstable, io-uring, rt, fs feature flags, and Linux-only compilation. A test verifies the method's availability on File instances.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pr-7901-2026-02-16-11-06-43

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link

claude bot commented Feb 16, 2026

Pull Request Review: Add unstable io_uring uring_cmd support for File

Summary

This PR adds support for IORING_OP_URING_CMD operations to tokio's File API, enabling device/file-specific commands through io_uring. The implementation follows established patterns in the codebase and is properly gated behind unstable features.

Code Quality ✅

Structure and Organization

  • The new cmd.rs module follows the existing pattern used by read.rs and write.rs
  • Proper trait implementations (Completable, Cancellable) match the codebase conventions
  • Integration into the driver's CancelData enum is correct

Documentation

  • Good documentation explaining what uring_cmd does
  • Properly documents the feature gates required
  • Documents error conditions

Potential Issues ⚠️

1. Missing Documentation Examples (Minor)

The uring_cmd method lacks a usage example. Other public methods in file.rs (like sync_all, sync_data, metadata) include # Examples sections with code snippets. Consider adding a usage example showing how to call this method.

2. Parameter Documentation (Moderate)

The public API parameters cmd_op, cmd, and buf_index lack documentation explaining:

  • What cmd_op represents (device-specific operation code)
  • What the 16-byte cmd array contains (command payload format)
  • What buf_index is used for (registered buffer index)

Users will need to understand these parameters to use the API effectively. Consider adding a Parameters section to the docstring.

3. Test Coverage (Moderate)

The test in fs_uring.rs:148-154 only verifies that the method is callable but doesn't verify the result. The current test (let _ = file.uring_cmd(0, [0; 16], None).await;) will likely fail or return an error on most files since they don't support uring_cmd. Consider asserting the expected error type for regular files.

4. Safety Comment Precision (Minor)

In cmd.rs:42, the safety comment states "Parameters are valid for the entire duration of the operation" but doesn't explain why. The fd is owned and the cmd is copied by value into the SQE, so this is correct, but could be more explicit like in read.rs:58-59.

Performance Considerations ✅

  • The use of OwnedFd ensures proper lifetime management
  • No unnecessary allocations
  • Direct integration with io_uring driver

Security Concerns ⚠️

Privileged Operation (Important)

The uring_cmd operation can execute arbitrary device-specific commands. While this is inherently a low-level API:

  1. Consider adding a safety note in the documentation that this API can perform privileged operations depending on the device and command
  2. The API is appropriately gated behind tokio_unstable, which is good
  3. Users should be aware that improper use could affect system stability

Suggested addition: A "Safety Considerations" section noting that this is a low-level API that passes commands directly to device drivers, and incorrect command payloads may cause undefined behavior or system instability depending on the device.

Best Practices ✅

  • Proper use of complete_inflight() before issuing new operations (file.rs:479-481)
  • Correct lock management (acquire, use, drop)
  • Consistent error handling with io::ErrorKind::Unsupported
  • Feature gating is comprehensive and correct

Recommendations

  1. Add parameter documentation explaining cmd_op, cmd, and buf_index
  2. Add usage example to the docstring
  3. Improve test coverage to verify expected behavior
  4. Add safety note about privileged operations and device-specific risks
  5. Consider adding a reference to relevant io_uring documentation or kernel docs

Conclusion

This is a solid implementation that follows tokio's conventions well. The main gaps are in documentation and test coverage rather than code quality. The implementation itself is sound and matches established patterns in the codebase.

Recommendation: Approve with suggestions - The code is functionally correct, but would benefit from improved documentation before merging.


🤖 Generated with Claude Code

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces io_uring command support for File operations in Tokio, specifically IORING_OP_URING_CMD, leveraging low-level kernel features on Linux. While the implementation appears well-structured, a critical security vulnerability has been identified. The current implementation exposes this powerful functionality through a safe API, which violates Rust's memory safety guarantees. Since the command payload can contain arbitrary pointers that the kernel will dereference, the uring_cmd method must be marked as unsafe to ensure callers are aware of the responsibility to provide valid memory addresses.

Comment on lines +470 to +475
pub async fn uring_cmd(
&self,
cmd_op: u32,
cmd: [u8; 16],
buf_index: Option<u16>,
) -> io::Result<u32> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The uring_cmd method is a public, safe API that allows submitting arbitrary 16-byte command payloads to the kernel via io_uring's IORING_OP_URING_CMD. Many implementations of this operation (such as the NVMe driver) interpret the 16-byte payload as containing pointers to memory buffers. Because uring_cmd is marked as a safe function, it allows a user to provide a payload that contains arbitrary memory addresses, which the kernel will then attempt to access. This can lead to memory safety violations (e.g., use-after-free or data corruption) if the addresses point to invalid or out-of-scope memory. In Rust, any function that can lead to memory unsafety depending on its input parameters must be marked as unsafe.

Suggested change
pub async fn uring_cmd(
&self,
cmd_op: u32,
cmd: [u8; 16],
buf_index: Option<u16>,
) -> io::Result<u32> {
pub async unsafe fn uring_cmd(
&self,
cmd_op: u32,
cmd: [u8; 16],
buf_index: Option<u16>,
) -> io::Result<u32> {

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:useful; category:documentation; feedback: The Gemini AI reviewer is correct! It seems the new method would be quite powerful and it will be possible to execute privileged operations thru it. Marking it as unsafe would signal to its users that they should make sure that it does what they need.

));
}

let fd: OwnedFd = self.std.try_clone()?.into();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The fd variable is created as an OwnedFd but then immediately converted to types::Fd (which is RawFd) in Op::uring_cmd16. The OwnedFd is then moved into the UringCmd struct. This seems redundant. It would be more efficient to pass the OwnedFd directly to Op::uring_cmd16 and let UringCmd own it from the start, avoiding the try_clone() and into() calls if Op::uring_cmd16 can accept OwnedFd directly or handle the conversion internally without an extra clone.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:annoying; category:documentation; feedback: The Gemini AI reviewer is not correct! The proposed improvement is exactly the same as it is implemented.

}

let fd: OwnedFd = self.std.try_clone()?.into();
let (res, _fd) = Op::uring_cmd16(fd, cmd_op, cmd, buf_index).await;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The _fd variable is unused. While it's common to prefix unused variables with an underscore, it's good practice to remove them if they serve no purpose to avoid confusion and potential resource leaks if the OwnedFd was not properly handled by the Op.

Suggested change
let (res, _fd) = Op::uring_cmd16(fd, cmd_op, cmd, buf_index).await;
let res = Op::uring_cmd16(fd, cmd_op, cmd, buf_index).await.0;

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:valid-but-wont-fix; category:documentation; feedback: The Gemini AI reviewer is not correct! The method returns a tuple and the used syntax is explicit that the second item is not needed and it will be ignored. Both solutions work the same way. It is matter of taste.

type Output = (io::Result<u32>, OwnedFd);

fn complete(self, cqe: CqeResult) -> Self::Output {
(cqe.result, self.fd)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The cqe.result is directly returned as io::Result<u32>. It's important to ensure that cqe.result correctly represents the return value of the IORING_OP_URING_CMD operation. The io_uring documentation states that the result field contains the return value of the system call, which for URING_CMD is typically an integer. This seems correct, but it's worth double-checking the expected return type and potential error mapping for IORING_OP_URING_CMD to ensure it aligns with u32.

let sqe = uring_cmd.build();

// SAFETY: Parameters are valid for the entire duration of the operation
unsafe { Op::new(sqe, UringCmd { fd }) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Op::new function is marked as unsafe. The comment states "Parameters are valid for the entire duration of the operation." This is a critical safety invariant. It's important to explicitly document how UringCmd ensures the fd (which is OwnedFd) remains valid and is not dropped prematurely or used after being moved, especially since OwnedFd is moved into UringCmd and then UringCmd is moved into Op. The OwnedFd is correctly returned in complete and complete_with_error, ensuring its lifetime is managed. This looks correct, but a more detailed comment on how the OwnedFd's lifetime is managed within the Op and UringCmd would be beneficial for future maintainers.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:good-to-have; category:documentation; feedback: The Gemini AI reviewer is correct! More information why the parameters are valid thru the entire duration of the operation would be helpful for the maintainers and the users of the API.

let (tmp_file, _path): (Vec<NamedTempFile>, Vec<PathBuf>) = create_tmp_files(1);
let file = File::open(tmp_file[0].path()).await.unwrap();

let _ = file.uring_cmd(0, [0; 16], None).await;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The test uring_cmd_is_available_via_file calls uring_cmd but discards the result with let _ = .... While this test primarily checks for availability and successful execution without panicking, it would be more robust to assert something about the result, even if it's just is_ok(). This would confirm that the operation completed successfully from the kernel's perspective, rather than just not crashing.

    let res = file.uring_cmd(0, [0; 16], None).await;
    assert!(res.is_ok(), "uring_cmd operation failed: {:?}", res);

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:good-to-have; category:bug; feedback: The Gemini AI reviewer is correct! Currently the test ignores the result. It would be good to assert that it is successful or not, at least.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tokio/tests/fs_uring.rs (1)

148-154: Test only verifies compilation, not behavior.

The result is silently discarded (let _ = ...), so this test passes even if the operation returns an error. Consider asserting at least that the result is an Err (since a regular file doesn't support uring_cmd), to guard against silent regressions:

-    let _ = file.uring_cmd(0, [0; 16], None).await;
+    // Regular files do not support uring_cmd; expect Unsupported or a kernel error.
+    let res = file.uring_cmd(0, [0; 16], None).await;
+    assert!(res.is_err(), "uring_cmd on a plain file should fail");

This would also catch the scenario where check_and_init incorrectly returns true for an unsupported opcode.

@augmentcode
Copy link

augmentcode bot commented Feb 16, 2026

🤖 Augment PR Summary

Summary: Adds unstable support for submitting io_uring IORING_OP_URING_CMD operations via tokio::fs::File on Linux.

Changes:

  • Introduced File::uring_cmd gated behind tokio_unstable + io-uring + Linux.
  • Added a new io_uring operation type (io::uring::cmd::UringCmd) and Op::uring_cmd16 builder.
  • Extended runtime cancellation plumbing (CancelData::UringCmd) to retain the owned FD while in-flight/cancelled.
  • Registered the new module in tokio::io::uring.
  • Added a basic test ensuring the API is callable from a File instance.

Technical Notes: The implementation probes runtime support via check_and_init(UringCmd16::CODE) and returns ErrorKind::Unsupported when unavailable.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.


let mut inner = self.inner.lock().await;
inner.complete_inflight().await;
drop(inner);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropping the inner lock before awaiting the io_uring op means other File ops can start while the uring command is still in-flight, so the earlier complete_inflight() doesn’t actually ensure exclusivity. Is it intended that uring_cmd can overlap with subsequent reads/writes/seeks on the same File?

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:useful; category:bug; feedback: The Augment AI reviewer is correct! The lock is dropped early and this allows other ops to be executed in the same time. This moves the responsibility to the application developer to make sure the operations are properly synchronized when needed.

drop(inner);

let handle = crate::runtime::Handle::current();
let driver_handle = handle.inner.driver().io();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle.inner.driver().io() will panic if the runtime was built without the I/O driver enabled, but the docstring only describes returning ErrorKind::Unsupported when io_uring/opcode isn’t available. Consider clarifying that uring_cmd also requires the runtime I/O driver to be enabled (or otherwise ensuring it fails with an io::Error rather than panicking).

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value:useful; category:bug; feedback: The Augment AI reviewer is correct! The docstring of the method lists most of the prerequisites but it misses the Builder::enable_io() call. Without it the IO driver won't be usable.

@martin-augment
Copy link
Owner Author

148-154: Test only verifies compilation, not behavior.

The result is silently discarded (let _ = ...), so this test passes even if the operation returns an error. Consider asserting at least that the result is an Err (since a regular file doesn't support uring_cmd), to guard against silent regressions:

-    let _ = file.uring_cmd(0, [0; 16], None).await;
+    // Regular files do not support uring_cmd; expect Unsupported or a kernel error.
+    let res = file.uring_cmd(0, [0; 16], None).await;
+    assert!(res.is_err(), "uring_cmd on a plain file should fail");

This would also catch the scenario where check_and_init incorrectly returns true for an unsupported opcode.

value:good-to-have; category:bug; feedback: The CodeRabbit AI reviewer is correct! Currently the test ignores the result. It would be good to assert that it is successful or not, at least.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants