xref: /cloud-hypervisor/block/src/raw_async_aio.rs (revision eb0b14f70ed5ed44b76579145fd2a741c0100ae4)
1 // Copyright © 2023 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
4 //
5 // Copyright © 2023 Crusoe Energy Systems LLC
6 //
7 
8 use std::fs::File;
9 use std::io::{Seek, SeekFrom};
10 use std::os::unix::io::{AsRawFd, RawFd};
11 
12 use vmm_sys_util::aio;
13 use vmm_sys_util::eventfd::EventFd;
14 
15 use crate::async_io::{
16     AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
17 };
18 use crate::DiskTopology;
19 
20 pub struct RawFileDiskAio {
21     file: File,
22 }
23 
24 impl RawFileDiskAio {
25     pub fn new(file: File) -> Self {
26         RawFileDiskAio { file }
27     }
28 }
29 
30 impl DiskFile for RawFileDiskAio {
31     fn size(&mut self) -> DiskFileResult<u64> {
32         self.file
33             .seek(SeekFrom::End(0))
34             .map_err(DiskFileError::Size)
35     }
36 
37     fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
38         Ok(Box::new(
39             RawFileAsyncAio::new(self.file.as_raw_fd(), ring_depth)
40                 .map_err(DiskFileError::NewAsyncIo)?,
41         ) as Box<dyn AsyncIo>)
42     }
43 
44     fn topology(&mut self) -> DiskTopology {
45         if let Ok(topology) = DiskTopology::probe(&self.file) {
46             topology
47         } else {
48             warn!("Unable to get device topology. Using default topology");
49             DiskTopology::default()
50         }
51     }
52 
53     fn fd(&mut self) -> BorrowedDiskFd {
54         BorrowedDiskFd::new(self.file.as_raw_fd())
55     }
56 }
57 
58 pub struct RawFileAsyncAio {
59     fd: RawFd,
60     ctx: aio::IoContext,
61     eventfd: EventFd,
62 }
63 
64 impl RawFileAsyncAio {
65     pub fn new(fd: RawFd, queue_depth: u32) -> std::io::Result<Self> {
66         let eventfd = EventFd::new(libc::EFD_NONBLOCK)?;
67         let ctx = aio::IoContext::new(queue_depth)?;
68 
69         Ok(RawFileAsyncAio { fd, ctx, eventfd })
70     }
71 }
72 
73 impl AsyncIo for RawFileAsyncAio {
74     fn notifier(&self) -> &EventFd {
75         &self.eventfd
76     }
77 
78     fn read_vectored(
79         &mut self,
80         offset: libc::off_t,
81         iovecs: &[libc::iovec],
82         user_data: u64,
83     ) -> AsyncIoResult<()> {
84         let iocbs = [&mut aio::IoControlBlock {
85             aio_fildes: self.fd.as_raw_fd() as u32,
86             aio_lio_opcode: aio::IOCB_CMD_PREADV as u16,
87             aio_buf: iovecs.as_ptr() as u64,
88             aio_nbytes: iovecs.len() as u64,
89             aio_offset: offset,
90             aio_data: user_data,
91             aio_flags: aio::IOCB_FLAG_RESFD,
92             aio_resfd: self.eventfd.as_raw_fd() as u32,
93             ..Default::default()
94         }];
95         let _ = self
96             .ctx
97             .submit(&iocbs[..])
98             .map_err(AsyncIoError::ReadVectored)?;
99 
100         Ok(())
101     }
102 
103     fn write_vectored(
104         &mut self,
105         offset: libc::off_t,
106         iovecs: &[libc::iovec],
107         user_data: u64,
108     ) -> AsyncIoResult<()> {
109         let iocbs = [&mut aio::IoControlBlock {
110             aio_fildes: self.fd.as_raw_fd() as u32,
111             aio_lio_opcode: aio::IOCB_CMD_PWRITEV as u16,
112             aio_buf: iovecs.as_ptr() as u64,
113             aio_nbytes: iovecs.len() as u64,
114             aio_offset: offset,
115             aio_data: user_data,
116             aio_flags: aio::IOCB_FLAG_RESFD,
117             aio_resfd: self.eventfd.as_raw_fd() as u32,
118             ..Default::default()
119         }];
120         let _ = self
121             .ctx
122             .submit(&iocbs[..])
123             .map_err(AsyncIoError::WriteVectored)?;
124 
125         Ok(())
126     }
127 
128     fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
129         if let Some(user_data) = user_data {
130             let iocbs = [&mut aio::IoControlBlock {
131                 aio_fildes: self.fd.as_raw_fd() as u32,
132                 aio_lio_opcode: aio::IOCB_CMD_FSYNC as u16,
133                 aio_data: user_data,
134                 aio_flags: aio::IOCB_FLAG_RESFD,
135                 aio_resfd: self.eventfd.as_raw_fd() as u32,
136                 ..Default::default()
137             }];
138             let _ = self.ctx.submit(&iocbs[..]).map_err(AsyncIoError::Fsync)?;
139         } else {
140             // SAFETY: FFI call with a valid fd
141             unsafe { libc::fsync(self.fd) };
142         }
143 
144         Ok(())
145     }
146 
147     fn next_completed_request(&mut self) -> Option<(u64, i32)> {
148         let mut events: [aio::IoEvent; 1] = [aio::IoEvent::default()];
149         let rc = self.ctx.get_events(0, &mut events, None).unwrap();
150         if rc == 0 {
151             None
152         } else {
153             Some((events[0].data, events[0].res as i32))
154         }
155     }
156 }
157