Rollup merge of #147077 - joboet:mov_kernel_copy_sys, r=ibraheemdev

std: move `kernel_copy` to `sys`

Part of rust-lang/rust#117276.

The current organisation of the `kernel_copy` mechanism used to specialise `io::copy` on Linux necessitated circular links between the `io::copy` module and the implementation in `sys::pal::unix::kernel_copy`, as well as presenting an exception to the tidy PAL rule that forbids OS-based `#[cfg]`s outside of `sys` and `os`.

This PR fixes this by moving `kernel_copy` to `sys` (as per rust-lang/rust#117276) and returning a `CopyState` from that function specifying whether `io::copy` should use its fallback. The `kernel_copy` function on other platforms just unconditionally returns `CopyState::Fallback`.
This commit is contained in:
Matthias Krüger 2025-11-17 21:47:58 +01:00 committed by GitHub
commit c3af6292ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 54 additions and 31 deletions

View file

@ -4,6 +4,7 @@ use crate::cmp;
use crate::collections::VecDeque;
use crate::io::IoSlice;
use crate::mem::MaybeUninit;
use crate::sys::io::{CopyState, kernel_copy};
#[cfg(test)]
mod tests;
@ -63,19 +64,17 @@ where
R: Read,
W: Write,
{
cfg_select! {
any(target_os = "linux", target_os = "android") => {
crate::sys::kernel_copy::copy_spec(reader, writer)
}
_ => {
generic_copy(reader, writer)
match kernel_copy(reader, writer)? {
CopyState::Ended(copied) => Ok(copied),
CopyState::Fallback(copied) => {
generic_copy(reader, writer).map(|additional| copied + additional)
}
}
}
/// The userspace read-write-loop implementation of `io::copy` that is used when
/// OS-specific specializations for copy offloading are not available or not applicable.
pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<u64>
where
R: Read,
W: Write,
@ -269,7 +268,7 @@ impl BufferedWriterSpec for Vec<u8> {
}
}
pub fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
reader: &mut R,
writer: &mut W,
) -> Result<u64> {

View file

@ -14,7 +14,7 @@ cfg_select! {
pub use unix::chroot;
pub(crate) use unix::debug_assert_fd_is_open;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) use unix::CachedFileMetadata;
pub(super) use unix::CachedFileMetadata;
use crate::sys::common::small_c_string::run_path_with_cstr as with_native_path;
}
target_os = "windows" => {

View file

@ -2302,7 +2302,7 @@ mod cfm {
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) use cfm::CachedFileMetadata;
pub(in crate::sys) use cfm::CachedFileMetadata;
#[cfg(not(target_vendor = "apple"))]
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {

View file

@ -48,9 +48,9 @@ use libc::sendfile as sendfile64;
use libc::sendfile64;
use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
use super::CopyState;
use crate::cmp::min;
use crate::fs::{File, Metadata};
use crate::io::copy::generic_copy;
use crate::io::{
BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock,
StdinLock, StdoutLock, Take, Write,
@ -70,10 +70,10 @@ use crate::sys::weak::syscall;
#[cfg(test)]
mod tests;
pub(crate) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
pub fn kernel_copy<R: Read + ?Sized, W: Write + ?Sized>(
read: &mut R,
write: &mut W,
) -> Result<u64> {
) -> Result<CopyState> {
let copier = Copier { read, write };
SpecCopy::copy(copier)
}
@ -176,17 +176,17 @@ struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
}
trait SpecCopy {
fn copy(self) -> Result<u64>;
fn copy(self) -> Result<CopyState>;
}
impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
default fn copy(self) -> Result<u64> {
generic_copy(self.read, self.write)
default fn copy(self) -> Result<CopyState> {
Ok(CopyState::Fallback(0))
}
}
impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
fn copy(self) -> Result<u64> {
fn copy(self) -> Result<CopyState> {
let (reader, writer) = (self.read, self.write);
let r_cfg = reader.properties();
let w_cfg = writer.properties();
@ -214,7 +214,9 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
result.update_take(reader);
match result {
CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
CopyResult::Ended(bytes_copied) => {
return Ok(CopyState::Ended(bytes_copied + written));
}
CopyResult::Error(e, _) => return Err(e),
CopyResult::Fallback(bytes) => written += bytes,
}
@ -231,7 +233,9 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
result.update_take(reader);
match result {
CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
CopyResult::Ended(bytes_copied) => {
return Ok(CopyState::Ended(bytes_copied + written));
}
CopyResult::Error(e, _) => return Err(e),
CopyResult::Fallback(bytes) => written += bytes,
}
@ -244,7 +248,9 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
result.update_take(reader);
match result {
CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written),
CopyResult::Ended(bytes_copied) => {
return Ok(CopyState::Ended(bytes_copied + written));
}
CopyResult::Error(e, _) => return Err(e),
CopyResult::Fallback(0) => { /* use the fallback below */ }
CopyResult::Fallback(_) => {
@ -255,10 +261,7 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
}
// fallback if none of the more specialized syscalls wants to work with these file descriptors
match generic_copy(reader, writer) {
Ok(bytes) => Ok(bytes + written),
err => err,
}
Ok(CopyState::Fallback(written))
}
}
@ -558,7 +561,7 @@ fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
}
}
pub(super) enum CopyResult {
enum CopyResult {
Ended(u64),
Error(Error, u64),
Fallback(u64),
@ -587,7 +590,7 @@ const INVALID_FD: RawFd = -1;
/// Callers must handle fallback to a generic copy loop.
/// `Fallback` may indicate non-zero number of bytes already written
/// if one of the files' cursor +`max_len` would exceed u64::MAX (`EOVERFLOW`).
pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
use crate::cmp;
const NOT_PROBED: u8 = 0;

View file

@ -0,0 +1,23 @@
pub enum CopyState {
#[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))]
Ended(u64),
Fallback(u64),
}
cfg_select! {
any(target_os = "linux", target_os = "android") => {
mod linux;
pub use linux::kernel_copy;
}
_ => {
use crate::io::{Result, Read, Write};
pub fn kernel_copy<R: ?Sized, W: ?Sized>(_reader: &mut R, _writer: &mut W) -> Result<CopyState>
where
R: Read,
W: Write,
{
Ok(CopyState::Fallback(0))
}
}
}

View file

@ -50,8 +50,11 @@ mod is_terminal {
}
}
mod kernel_copy;
pub use io_slice::{IoSlice, IoSliceMut};
pub use is_terminal::is_terminal;
pub use kernel_copy::{CopyState, kernel_copy};
// Bare metal platforms usually have very small amounts of RAM
// (in the order of hundreds of KB)

View file

@ -5,8 +5,6 @@ use crate::io::ErrorKind;
#[cfg(target_os = "fuchsia")]
pub mod fuchsia;
pub mod futex;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod kernel_copy;
#[cfg(target_os = "linux")]
pub mod linux;
pub mod os;

View file

@ -59,12 +59,9 @@ const EXCEPTION_PATHS: &[&str] = &[
"library/std/src/os", // Platform-specific public interfaces
// Temporary `std` exceptions
// FIXME: platform-specific code should be moved to `sys`
"library/std/src/io/copy.rs",
"library/std/src/io/stdio.rs",
"library/std/src/lib.rs", // for miniz_oxide leaking docs, which itself workaround
"library/std/src/path.rs",
"library/std/src/sys_common", // Should only contain abstractions over platforms
"library/std/src/net/test.rs", // Utility helpers for tests
"library/std/src/io/error.rs", // Repr unpacked needed for UEFI
];