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