wireshift_fallback/ops/
read.rs1use std::collections::HashMap;
2use std::io::Read;
3use std::os::unix::fs::FileExt;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6
7use wireshift_core::buffer::{Buffer, Submitted};
8use wireshift_core::op::CompletionPayload;
9use wireshift_core::{Error, Result};
10
11use crate::ops::retry_eintr;
12
13pub fn execute_read(
15 mut file: std::fs::File,
16 offset: u64,
17 mut buffer: Buffer<Submitted>,
18 canceled: Arc<AtomicBool>,
19 len: Option<usize>,
20) -> Result<CompletionPayload> {
21 if canceled.load(Ordering::Relaxed) {
22 return Err(Error::canceled(
23 "read canceled before data transfer started",
24 "avoid canceling the request before the backend starts transferring data",
25 ));
26 }
27 let buf = buffer.backend_mut();
28 let read_len = len.unwrap_or(buf.len());
29 let target_buf = if read_len < buf.len() {
30 &mut buf[..read_len]
31 } else {
32 buf
33 };
34 apply_madv_sequential(target_buf)?;
35
36 let bytes = retry_eintr!(file.read_at(target_buf, offset))
37 .or_else(|error| {
38 let is_espipe = error.raw_os_error() == Some(rustix::io::Errno::SPIPE.raw_os_error());
39 if is_espipe && offset == 0 {
40 retry_eintr!(file.read(target_buf))
41 } else {
42 Err(error)
43 }
44 })
45 .map_err(|error| {
46 Error::io(
47 "read failed",
48 error,
49 "ensure the file descriptor is readable",
50 )
51 })?;
52 let completed = buffer.into_completed(bytes)?;
53 Ok(CompletionPayload::Read {
54 buffer: completed,
55 bytes,
56 })
57}
58
59pub fn execute_read_vectored(
61 mut file: std::fs::File,
62 offset: u64,
63 buffers: Vec<Buffer<Submitted>>,
64) -> Result<CompletionPayload> {
65 let mut total = 0_usize;
66 let mut current_offset = offset;
67 let mut completed_buffers = Vec::with_capacity(buffers.len());
68 for mut buffer in buffers {
69 let (bytes, is_short) = {
70 let chunk = buffer.backend_mut();
71 apply_madv_sequential(chunk)?;
72 let bytes = retry_eintr!(file.read_at(chunk, current_offset))
73 .or_else(|error| {
74 let is_espipe =
75 error.raw_os_error() == Some(rustix::io::Errno::SPIPE.raw_os_error());
76 if is_espipe && offset == 0 {
77 retry_eintr!(file.read(chunk))
78 } else {
79 Err(error)
80 }
81 })
82 .map_err(|error| {
83 Error::io(
84 "readv segment read failed",
85 error,
86 "ensure the file descriptor remains readable for all readv segments",
87 )
88 })?;
89 (bytes, bytes < chunk.len())
90 };
91 total += bytes;
92 current_offset += bytes as u64;
93 completed_buffers.push(buffer.into_completed(bytes)?);
94 if is_short {
95 break;
96 }
97 }
98 Ok(CompletionPayload::ReadVectored {
99 buffers: completed_buffers,
100 bytes: total,
101 })
102}
103
104pub fn execute_read_gpu(
106 file: std::fs::File,
107 offset: u64,
108 mut buffer: wireshift_core::ops::AlignedBuffer,
109 canceled: Arc<AtomicBool>,
110) -> Result<CompletionPayload> {
111 if canceled.load(Ordering::Relaxed) {
112 return Err(Error::canceled(
113 "gpu staged read canceled before data transfer started",
114 "avoid canceling the request before the backend starts transferring data",
115 ));
116 }
117 let slice = buffer.as_mut_slice();
118 apply_madv_sequential(slice)?;
119 let bytes = retry_eintr!(file.read_at(slice, offset)).map_err(|error| {
120 Error::io(
121 "gpu staged read failed",
122 error,
123 "ensure the file descriptor is readable and supports positional reads",
124 )
125 })?;
126 Ok(CompletionPayload::GpuRead { buffer, bytes })
127}
128
129pub fn execute_read_fixed(
131 slot: u32,
132 offset: u64,
133 mut buffer: Buffer<Submitted>,
134 canceled: Arc<AtomicBool>,
135 fixed_files: &mut HashMap<u32, std::fs::File>,
136) -> Result<CompletionPayload> {
137 if canceled.load(Ordering::Relaxed) {
138 return Err(Error::canceled(
139 "read canceled before data transfer started",
140 "avoid canceling the request before the backend starts transferring data",
141 ));
142 }
143 let file = fixed_files.get(&slot).ok_or_else(|| {
144 Error::completion(
145 format!("direct descriptor slot {slot} was read before open"),
146 "ensure linked reads open the direct descriptor before issuing a fixed read",
147 )
148 })?;
149 let slice = buffer.backend_mut();
150 apply_madv_sequential(slice)?;
151 let bytes = retry_eintr!(file.read_at(slice, offset)).map_err(|error| {
152 Error::io(
153 "read failed",
154 error,
155 "ensure the file descriptor is readable",
156 )
157 })?;
158 let completed = buffer.into_completed(bytes)?;
159 Ok(CompletionPayload::Read {
160 buffer: completed,
161 bytes,
162 })
163}
164
165pub fn apply_madv_sequential(buffer: &mut [u8]) -> Result<()> {
169 if buffer.is_empty() {
170 return Ok(());
171 }
172
173 let page_size = rustix::param::page_size();
174
175 let start = buffer.as_mut_ptr() as usize;
176 let end = start.checked_add(buffer.len()).ok_or_else(|| {
177 Error::validation(
178 "buffer address overflowed while preparing MADV_SEQUENTIAL",
179 "reduce the read buffer length so address calculations stay in range",
180 )
181 })?;
182 let aligned_start = start / page_size * page_size;
183 let aligned_end = end
184 .checked_add(page_size - 1)
185 .map(|value| value / page_size * page_size)
186 .ok_or_else(|| {
187 Error::validation(
188 "buffer range overflowed while page-aligning MADV_SEQUENTIAL",
189 "reduce the read buffer length so page alignment stays in range",
190 )
191 })?;
192 let aligned_len = aligned_end.checked_sub(aligned_start).ok_or_else(|| {
193 Error::validation(
194 "aligned MADV_SEQUENTIAL range underflowed",
195 "reduce the read buffer length so alignment calculations stay valid",
196 )
197 })?;
198
199 #[cfg(target_os = "linux")]
200 {
201 use rustix::mm::{madvise, Advice};
202 let ptr = aligned_start as *mut std::ffi::c_void;
206 unsafe {
207 madvise(ptr, aligned_len, Advice::Sequential).map_err(|error| {
208 Error::io(
209 "madvise(MADV_SEQUENTIAL) failed",
210 std::io::Error::from(error),
211 "ensure the runtime allows calling madvise on the target memory range",
212 )
213 })?;
214 }
215 }
216
217 Ok(())
219}
220
221
222#[cfg(test)]
223mod tests {
224 use super::apply_madv_sequential;
225
226 #[test]
227 fn verify_apply_madv_sequential() {
228 let mut buf = vec![0u8; 16 * 1024];
229 assert!(apply_madv_sequential(&mut buf).is_ok());
230 }
231}