1use std::io;
6use std::os::fd::AsFd;
7use std::os::fd::AsRawFd;
8use std::sync::atomic::AtomicBool;
9use std::sync::atomic::Ordering;
10use std::sync::Arc;
11
12use base::handle_eintr_errno;
13use base::linux::preadv2;
14use base::linux::pwritev2;
15use base::sys::fallocate;
16use base::sys::FallocateMode;
17use base::AsRawDescriptor;
18use base::IoBufMut;
19use base::VolatileSlice;
20use remain::sorted;
21use thiserror::Error as ThisError;
22
23use super::fd_executor;
24use super::fd_executor::EpollReactor;
25use super::fd_executor::RegisteredSource;
26use crate::common_executor::RawExecutor;
27use crate::mem::BackingMemory;
28use crate::AsyncError;
29use crate::AsyncResult;
30use crate::IoOptions;
31use crate::MemRegion;
32
33#[sorted]
34#[derive(ThisError, Debug)]
35pub enum Error {
36 #[error("An error occurred attempting to register a waker with the executor: {0}.")]
38 AddingWaker(fd_executor::Error),
39 #[error("Failed to discard a block: {0}")]
41 Discard(base::Error),
42 #[error("An executor error occurred: {0}")]
44 Executor(fd_executor::Error),
45 #[error("An error occurred when executing fallocate synchronously: {0}")]
47 Fallocate(base::Error),
48 #[error("An error occurred when executing fdatasync synchronously: {0}")]
50 Fdatasync(base::Error),
51 #[error("An error occurred when executing fsync synchronously: {0}")]
53 Fsync(base::Error),
54 #[error("An error occurred when reading the FD: {0}.")]
56 Read(base::Error),
57 #[error("An error occurred when seeking the FD: {0}.")]
59 Seeking(base::Error),
60 #[error("An error occurred when writing the FD: {0}.")]
62 Write(base::Error),
63}
64pub type Result<T> = std::result::Result<T, Error>;
65
66impl From<Error> for io::Error {
67 fn from(e: Error) -> Self {
68 use Error::*;
69 match e {
70 AddingWaker(e) => e.into(),
71 Executor(e) => e.into(),
72 Discard(e) => e.into(),
73 Fallocate(e) => e.into(),
74 Fdatasync(e) => e.into(),
75 Fsync(e) => e.into(),
76 Read(e) => e.into(),
77 Seeking(e) => e.into(),
78 Write(e) => e.into(),
79 }
80 }
81}
82
83impl From<Error> for AsyncError {
84 fn from(e: Error) -> AsyncError {
85 AsyncError::SysVariants(e.into())
86 }
87}
88
89static KERNEL_SUPPORTS_DONTCACHE: AtomicBool = AtomicBool::new(true);
90
91pub struct PollSource<F> {
93 registered_source: RegisteredSource<F>,
94}
95
96impl<F: AsRawDescriptor> PollSource<F> {
97 pub fn new(f: F, ex: &Arc<RawExecutor<EpollReactor>>) -> Result<Self> {
99 RegisteredSource::new(ex, f)
100 .map({
101 |f| PollSource {
102 registered_source: f,
103 }
104 })
105 .map_err(Error::Executor)
106 }
107}
108
109impl<F: AsRawDescriptor> PollSource<F> {
110 pub async fn read_to_vec(
112 &self,
113 file_offset: Option<u64>,
114 mut vec: Vec<u8>,
115 options: IoOptions,
116 ) -> AsyncResult<(usize, Vec<u8>)> {
117 let mut use_dontcache =
118 options.dontcache && KERNEL_SUPPORTS_DONTCACHE.load(Ordering::Relaxed);
119 loop {
120 let res = handle_eintr_errno!(if use_dontcache {
121 let offset = file_offset.map(|o| o as libc::off_t).unwrap_or(-1);
122 let mut iovecs = [IoBufMut::new(&mut vec)];
123 preadv2(
124 self.registered_source.duped_fd.as_fd(),
125 &mut iovecs,
126 offset,
127 libc::RWF_DONTCACHE,
128 )
129 } else if let Some(offset) = file_offset {
130 unsafe {
134 libc::pread64(
135 self.registered_source.duped_fd.as_raw_fd(),
136 vec.as_mut_ptr() as *mut libc::c_void,
137 vec.len(),
138 offset as libc::off64_t,
139 )
140 }
141 } else {
142 unsafe {
146 libc::read(
147 self.registered_source.duped_fd.as_raw_fd(),
148 vec.as_mut_ptr() as *mut libc::c_void,
149 vec.len(),
150 )
151 }
152 });
153
154 if res >= 0 {
155 return Ok((res as usize, vec));
156 }
157
158 let err = base::Error::last();
159 match err.errno() {
160 libc::ENOSYS | libc::EINVAL if use_dontcache => {
161 base::warn!(
164 "crosvm: preadv2 or RWF_DONTCACHE read not supported, falling back to v1!"
165 );
166 KERNEL_SUPPORTS_DONTCACHE.store(false, Ordering::Relaxed);
167 use_dontcache = false;
168 continue;
169 }
170 libc::EWOULDBLOCK => {
171 let op = self
172 .registered_source
173 .wait_readable()
174 .map_err(Error::AddingWaker)?;
175 op.await.map_err(Error::Executor)?;
176 }
177 _ => return Err(Error::Read(err).into()),
178 }
179 }
180 }
181
182 pub async fn read_to_mem(
184 &self,
185 file_offset: Option<u64>,
186 mem: Arc<dyn BackingMemory + Send + Sync>,
187 mem_offsets: impl IntoIterator<Item = MemRegion>,
188 options: IoOptions,
189 ) -> AsyncResult<usize> {
190 let mut iovecs = mem_offsets
191 .into_iter()
192 .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok())
193 .collect::<Vec<VolatileSlice>>();
194 let mut use_dontcache =
195 options.dontcache && KERNEL_SUPPORTS_DONTCACHE.load(Ordering::Relaxed);
196 loop {
197 let res = handle_eintr_errno!(if use_dontcache {
198 let offset = file_offset.map(|o| o as libc::off_t).unwrap_or(-1);
199 preadv2(
200 self.registered_source.duped_fd.as_fd(),
201 VolatileSlice::as_iobufs_mut(&mut iovecs),
202 offset,
203 libc::RWF_DONTCACHE,
204 )
205 } else if let Some(offset) = file_offset {
206 unsafe {
210 libc::preadv64(
211 self.registered_source.duped_fd.as_raw_fd(),
212 iovecs.as_mut_ptr() as *mut _,
213 iovecs.len() as i32,
214 offset as libc::off64_t,
215 )
216 }
217 } else {
218 unsafe {
222 libc::readv(
223 self.registered_source.duped_fd.as_raw_fd(),
224 iovecs.as_mut_ptr() as *mut _,
225 iovecs.len() as i32,
226 )
227 }
228 });
229
230 if res >= 0 {
231 return Ok(res as usize);
232 }
233
234 let err = base::Error::last();
235 match err.errno() {
236 libc::ENOSYS | libc::EINVAL if use_dontcache => {
237 base::warn!(
240 "crosvm: preadv2 or RWF_DONTCACHE read not supported, falling back to v1!"
241 );
242 KERNEL_SUPPORTS_DONTCACHE.store(false, Ordering::Relaxed);
243 use_dontcache = false;
244 continue;
245 }
246 libc::EWOULDBLOCK => {
247 let op = self
248 .registered_source
249 .wait_readable()
250 .map_err(Error::AddingWaker)?;
251 op.await.map_err(Error::Executor)?;
252 }
253 _ => return Err(Error::Read(err).into()),
254 }
255 }
256 }
257
258 pub async fn wait_readable(&self) -> AsyncResult<()> {
260 let op = self
261 .registered_source
262 .wait_readable()
263 .map_err(Error::AddingWaker)?;
264 op.await.map_err(Error::Executor)?;
265 Ok(())
266 }
267
268 pub async fn write_from_vec(
270 &self,
271 file_offset: Option<u64>,
272 mut vec: Vec<u8>,
273 options: IoOptions,
274 ) -> AsyncResult<(usize, Vec<u8>)> {
275 let mut use_dontcache =
276 options.dontcache && KERNEL_SUPPORTS_DONTCACHE.load(Ordering::Relaxed);
277 loop {
278 let res = handle_eintr_errno!(if use_dontcache {
279 let offset = file_offset.map(|o| o as libc::off_t).unwrap_or(-1);
280 let iovecs = [IoBufMut::new(&mut vec)];
281 pwritev2(
282 self.registered_source.duped_fd.as_fd(),
283 IoBufMut::as_iobufs(&iovecs),
284 offset,
285 libc::RWF_DONTCACHE,
286 )
287 } else if let Some(offset) = file_offset {
288 unsafe {
292 libc::pwrite64(
293 self.registered_source.duped_fd.as_raw_fd(),
294 vec.as_ptr() as *const libc::c_void,
295 vec.len(),
296 offset as libc::off64_t,
297 )
298 }
299 } else {
300 unsafe {
304 libc::write(
305 self.registered_source.duped_fd.as_raw_fd(),
306 vec.as_ptr() as *const libc::c_void,
307 vec.len(),
308 )
309 }
310 });
311
312 if res >= 0 {
313 return Ok((res as usize, vec));
314 }
315
316 let err = base::Error::last();
317 match err.errno() {
318 libc::ENOSYS | libc::EINVAL if use_dontcache => {
319 base::warn!(
322 "crosvm: pwritev2 or RWF_DONTCACHE write not supported, falling back to v1!"
323 );
324 KERNEL_SUPPORTS_DONTCACHE.store(false, Ordering::Relaxed);
325 use_dontcache = false;
326 continue;
327 }
328 libc::EWOULDBLOCK => {
329 let op = self
330 .registered_source
331 .wait_writable()
332 .map_err(Error::AddingWaker)?;
333 op.await.map_err(Error::Executor)?;
334 }
335 _ => return Err(Error::Write(err).into()),
336 }
337 }
338 }
339
340 pub async fn write_from_mem(
342 &self,
343 file_offset: Option<u64>,
344 mem: Arc<dyn BackingMemory + Send + Sync>,
345 mem_offsets: impl IntoIterator<Item = MemRegion>,
346 options: IoOptions,
347 ) -> AsyncResult<usize> {
348 let iovecs = mem_offsets
349 .into_iter()
350 .map(|mem_range| mem.get_volatile_slice(mem_range))
351 .filter_map(|r| r.ok())
352 .collect::<Vec<VolatileSlice>>();
353 let mut use_dontcache =
354 options.dontcache && KERNEL_SUPPORTS_DONTCACHE.load(Ordering::Relaxed);
355 loop {
356 let res = handle_eintr_errno!(if use_dontcache {
357 let offset = file_offset.map(|o| o as libc::off_t).unwrap_or(-1);
358 pwritev2(
359 self.registered_source.duped_fd.as_fd(),
360 IoBufMut::as_iobufs(VolatileSlice::as_iobufs(&iovecs)),
361 offset,
362 libc::RWF_DONTCACHE,
363 )
364 } else if let Some(offset) = file_offset {
365 unsafe {
369 libc::pwritev64(
370 self.registered_source.duped_fd.as_raw_fd(),
371 iovecs.as_ptr() as *mut _,
372 iovecs.len() as i32,
373 offset as libc::off64_t,
374 )
375 }
376 } else {
377 unsafe {
381 libc::writev(
382 self.registered_source.duped_fd.as_raw_fd(),
383 iovecs.as_ptr() as *mut _,
384 iovecs.len() as i32,
385 )
386 }
387 });
388
389 if res >= 0 {
390 return Ok(res as usize);
391 }
392
393 let err = base::Error::last();
394 match err.errno() {
395 libc::ENOSYS | libc::EINVAL if use_dontcache => {
396 base::warn!(
399 "crosvm: pwritev2 or RWF_DONTCACHE write not supported, falling back to v1!"
400 );
401 KERNEL_SUPPORTS_DONTCACHE.store(false, Ordering::Relaxed);
402 use_dontcache = false;
403 continue;
404 }
405 libc::EWOULDBLOCK => {
406 let op = self
407 .registered_source
408 .wait_writable()
409 .map_err(Error::AddingWaker)?;
410 op.await.map_err(Error::Executor)?;
411 }
412 _ => return Err(Error::Write(err).into()),
413 }
414 }
415 }
416
417 pub async fn fsync(&self) -> AsyncResult<()> {
421 let ret = handle_eintr_errno!(unsafe {
423 libc::fsync(self.registered_source.duped_fd.as_raw_fd())
424 });
425 if ret == 0 {
426 Ok(())
427 } else {
428 Err(Error::Fsync(base::Error::last()).into())
429 }
430 }
431
432 pub async fn punch_hole(&self, file_offset: u64, len: u64) -> AsyncResult<()> {
434 Ok(fallocate(
435 &self.registered_source.duped_fd,
436 FallocateMode::PunchHole,
437 file_offset,
438 len,
439 )
440 .map_err(Error::Fallocate)?)
441 }
442
443 pub async fn write_zeroes_at(&self, file_offset: u64, len: u64) -> AsyncResult<()> {
445 Ok(fallocate(
446 &self.registered_source.duped_fd,
447 FallocateMode::ZeroRange,
448 file_offset,
449 len,
450 )
451 .map_err(Error::Fallocate)?)
452 }
453
454 pub async fn fdatasync(&self) -> AsyncResult<()> {
457 let ret = handle_eintr_errno!(unsafe {
459 libc::fdatasync(self.registered_source.duped_fd.as_raw_fd())
460 });
461 if ret == 0 {
462 Ok(())
463 } else {
464 Err(Error::Fdatasync(base::Error::last()).into())
465 }
466 }
467
468 pub fn into_source(self) -> F {
470 self.registered_source.source
471 }
472
473 pub fn as_source_mut(&mut self) -> &mut F {
475 &mut self.registered_source.source
476 }
477
478 pub fn as_source(&self) -> &F {
480 &self.registered_source.source
481 }
482}
483
484#[cfg(test)]
486mod tests {
487 use std::fs::File;
488
489 use super::*;
490 use crate::ExecutorTrait;
491
492 #[test]
493 fn memory_leak() {
494 async fn owns_poll_source(source: PollSource<File>) {
497 let _ = source.wait_readable().await;
498 }
499
500 let (rx, _tx) = base::pipe().unwrap();
501 let ex = RawExecutor::<EpollReactor>::new().unwrap();
502 let source = PollSource::new(rx, &ex).unwrap();
503 ex.spawn_local(owns_poll_source(source)).detach();
504
505 }
510}