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