1 // Copyright © 2021 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 5 use std::fs::File; 6 use std::io::{Error, Seek, SeekFrom}; 7 use std::os::unix::io::{AsRawFd, RawFd}; 8 9 use io_uring::{opcode, types, IoUring}; 10 use vmm_sys_util::eventfd::EventFd; 11 12 use crate::async_io::{ 13 AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult, 14 }; 15 use crate::DiskTopology; 16 17 pub struct RawFileDisk { 18 file: File, 19 } 20 21 impl RawFileDisk { 22 pub fn new(file: File) -> Self { 23 RawFileDisk { file } 24 } 25 } 26 27 impl DiskFile for RawFileDisk { 28 fn size(&mut self) -> DiskFileResult<u64> { 29 self.file 30 .seek(SeekFrom::End(0)) 31 .map_err(DiskFileError::Size) 32 } 33 34 fn new_async_io(&self, ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> { 35 Ok(Box::new( 36 RawFileAsync::new(self.file.as_raw_fd(), ring_depth) 37 .map_err(DiskFileError::NewAsyncIo)?, 38 ) as Box<dyn AsyncIo>) 39 } 40 41 fn topology(&mut self) -> DiskTopology { 42 if let Ok(topology) = DiskTopology::probe(&self.file) { 43 topology 44 } else { 45 warn!("Unable to get device topology. Using default topology"); 46 DiskTopology::default() 47 } 48 } 49 } 50 51 pub struct RawFileAsync { 52 fd: RawFd, 53 io_uring: IoUring, 54 eventfd: EventFd, 55 } 56 57 impl RawFileAsync { 58 pub fn new(fd: RawFd, ring_depth: u32) -> std::io::Result<Self> { 59 let io_uring = IoUring::new(ring_depth)?; 60 let eventfd = EventFd::new(libc::EFD_NONBLOCK)?; 61 62 // Register the io_uring eventfd that will notify when something in 63 // the completion queue is ready. 64 io_uring.submitter().register_eventfd(eventfd.as_raw_fd())?; 65 66 Ok(RawFileAsync { 67 fd, 68 io_uring, 69 eventfd, 70 }) 71 } 72 } 73 74 impl AsyncIo for RawFileAsync { 75 fn notifier(&self) -> &EventFd { 76 &self.eventfd 77 } 78 79 fn read_vectored( 80 &mut self, 81 offset: libc::off_t, 82 iovecs: &[libc::iovec], 83 user_data: u64, 84 ) -> AsyncIoResult<()> { 85 let (submitter, mut sq, _) = self.io_uring.split(); 86 87 // SAFETY: we know the file descriptor is valid and we 88 // relied on vm-memory to provide the buffer address. 89 unsafe { 90 sq.push( 91 &opcode::Readv::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32) 92 .offset(offset.try_into().unwrap()) 93 .build() 94 .user_data(user_data), 95 ) 96 .map_err(|_| AsyncIoError::ReadVectored(Error::other("Submission queue is full")))? 97 }; 98 99 // Update the submission queue and submit new operations to the 100 // io_uring instance. 101 sq.sync(); 102 submitter.submit().map_err(AsyncIoError::ReadVectored)?; 103 104 Ok(()) 105 } 106 107 fn write_vectored( 108 &mut self, 109 offset: libc::off_t, 110 iovecs: &[libc::iovec], 111 user_data: u64, 112 ) -> AsyncIoResult<()> { 113 let (submitter, mut sq, _) = self.io_uring.split(); 114 115 // SAFETY: we know the file descriptor is valid and we 116 // relied on vm-memory to provide the buffer address. 117 unsafe { 118 sq.push( 119 &opcode::Writev::new(types::Fd(self.fd), iovecs.as_ptr(), iovecs.len() as u32) 120 .offset(offset.try_into().unwrap()) 121 .build() 122 .user_data(user_data), 123 ) 124 .map_err(|_| AsyncIoError::WriteVectored(Error::other("Submission queue is full")))? 125 }; 126 127 // Update the submission queue and submit new operations to the 128 // io_uring instance. 129 sq.sync(); 130 submitter.submit().map_err(AsyncIoError::WriteVectored)?; 131 132 Ok(()) 133 } 134 135 fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> { 136 if let Some(user_data) = user_data { 137 let (submitter, mut sq, _) = self.io_uring.split(); 138 139 // SAFETY: we know the file descriptor is valid. 140 unsafe { 141 sq.push( 142 &opcode::Fsync::new(types::Fd(self.fd)) 143 .build() 144 .user_data(user_data), 145 ) 146 .map_err(|_| AsyncIoError::Fsync(Error::other("Submission queue is full")))? 147 }; 148 149 // Update the submission queue and submit new operations to the 150 // io_uring instance. 151 sq.sync(); 152 submitter.submit().map_err(AsyncIoError::Fsync)?; 153 } else { 154 // SAFETY: FFI call with a valid fd 155 unsafe { libc::fsync(self.fd) }; 156 } 157 158 Ok(()) 159 } 160 161 fn next_completed_request(&mut self) -> Option<(u64, i32)> { 162 self.io_uring 163 .completion() 164 .next() 165 .map(|entry| (entry.user_data(), entry.result())) 166 } 167 } 168