xref: /cloud-hypervisor/block/src/raw_async.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
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