1 // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2 // SPDX-License-Identifier: BSD-3-Clause
3 
4 //! Safe wrapper over [`Linux AIO`](http://man7.org/linux/man-pages/man7/aio.7.html).
5 
6 #![allow(non_camel_case_types)]
7 
8 /* automatically generated by rust-bindgen from file linux/include/uapi/linux/aio_abi.h
9  * of commit 69973b8 and then manually edited */
10 
11 use std::io::{Error, Result};
12 use std::os::raw::{c_int, c_long, c_uint, c_ulong};
13 use std::ptr::null_mut;
14 
15 type __s16 = ::std::os::raw::c_short;
16 type __u16 = ::std::os::raw::c_ushort;
17 type __u32 = ::std::os::raw::c_uint;
18 type __s64 = ::std::os::raw::c_longlong;
19 type __u64 = ::std::os::raw::c_ulonglong;
20 
21 /// Read from a file descriptor at a given offset.
22 pub const IOCB_CMD_PREAD: u32 = 0;
23 /// Write to a file descriptor at a given offset.
24 pub const IOCB_CMD_PWRITE: u32 = 1;
25 /// Synchronize a file's in-core metadata and data to storage device.
26 pub const IOCB_CMD_FSYNC: u32 = 2;
27 /// Synchronize a file's in-core data to storage device.
28 pub const IOCB_CMD_FDSYNC: u32 = 3;
29 /// Noop, this defined by never used by linux kernel.
30 pub const IOCB_CMD_NOOP: u32 = 6;
31 /// Read from a file descriptor at a given offset into multiple buffers.
32 pub const IOCB_CMD_PREADV: u32 = 7;
33 /// Write to a file descriptor at a given offset from multiple buffers.
34 pub const IOCB_CMD_PWRITEV: u32 = 8;
35 
36 /// Valid flags for the "aio_flags" member of the "struct iocb".
37 /// Set if the "aio_resfd" member of the "struct iocb" is valid.
38 pub const IOCB_FLAG_RESFD: u32 = 1;
39 
40 /// Maximum number of concurrent requests.
41 pub const MAX_REQUESTS: usize = 0x10000;
42 
43 /// Wrapper over the [`iocb`](https://elixir.bootlin.com/linux/v4.9/source/include/uapi/linux/aio_abi.h#L79) structure.
44 #[allow(missing_docs)]
45 #[repr(C)]
46 #[derive(Debug, Default, Copy, Clone)]
47 pub struct IoControlBlock {
48     pub aio_data: __u64,
49     pub aio_key: __u32,
50     pub aio_reserved1: __u32,
51     pub aio_lio_opcode: __u16,
52     pub aio_reqprio: __s16,
53     pub aio_fildes: __u32,
54     pub aio_buf: __u64,
55     pub aio_nbytes: __u64,
56     pub aio_offset: __s64,
57     pub aio_reserved2: __u64,
58     pub aio_flags: __u32,
59     pub aio_resfd: __u32,
60 }
61 
62 /// Wrapper over the [`io_event`](https://elixir.bootlin.com/linux/v4.9/source/include/uapi/linux/aio_abi.h#L58) structure.
63 #[allow(missing_docs)]
64 #[repr(C)]
65 #[derive(Debug, Default, Copy, Clone)]
66 pub struct IoEvent {
67     pub data: __u64,
68     pub obj: __u64,
69     pub res: __s64,
70     pub res2: __s64,
71 }
72 
73 /// Newtype for [`aio_context_t`](https://elixir.bootlin.com/linux/v4.9/source/include/uapi/linux/aio_abi.h#L33).
74 #[repr(transparent)]
75 #[derive(Debug)]
76 pub struct IoContext(::std::os::raw::c_ulong);
77 
78 impl IoContext {
79     /// Create a new aio context instance.
80     ///
81     /// Refer to Linux [`io_setup`](http://man7.org/linux/man-pages/man2/io_setup.2.html).
82     ///
83     /// # Arguments
84     /// * `nr_events`: maximum number of concurrently processing IO operations.
85     #[allow(clippy::new_ret_no_self)]
new(nr_events: c_uint) -> Result<Self>86     pub fn new(nr_events: c_uint) -> Result<Self> {
87         if nr_events as usize > MAX_REQUESTS {
88             return Err(Error::from_raw_os_error(libc::EINVAL));
89         }
90 
91         let mut ctx = IoContext(0);
92         let rc =
93             // SAFETY: Safe because we use valid parameters and check the result.
94             unsafe { libc::syscall(libc::SYS_io_setup, nr_events, &mut ctx as *mut Self) as c_int };
95         if rc < 0 {
96             Err(Error::last_os_error())
97         } else {
98             Ok(ctx)
99         }
100     }
101 
102     /// Submit asynchronous I/O blocks for processing.
103     ///
104     /// Refer to Linux [`io_submit`](http://man7.org/linux/man-pages/man2/io_submit.2.html).
105     ///
106     /// # Arguments
107     /// * `iocbs`: array of AIO control blocks, which will be submitted to the context.
108     ///
109     /// # Examples
110     /// ```
111     /// extern crate vmm_sys_util;
112     /// use vmm_sys_util::aio::*;
113     /// # use std::fs::File;
114     /// # use std::os::unix::io::AsRawFd;
115     ///
116     /// let file = File::open("/dev/zero").unwrap();
117     /// let ctx = IoContext::new(128).unwrap();
118     /// let mut buf: [u8; 4096] = unsafe { std::mem::uninitialized() };
119     /// let iocbs = [&mut IoControlBlock {
120     ///     aio_fildes: file.as_raw_fd() as u32,
121     ///     aio_lio_opcode: IOCB_CMD_PREAD as u16,
122     ///     aio_buf: buf.as_mut_ptr() as u64,
123     ///     aio_nbytes: buf.len() as u64,
124     ///     ..Default::default()
125     /// }];
126     /// assert_eq!(ctx.submit(&iocbs[..]).unwrap(), 1);
127     /// ```
submit(&self, iocbs: &[&mut IoControlBlock]) -> Result<usize>128     pub fn submit(&self, iocbs: &[&mut IoControlBlock]) -> Result<usize> {
129         // SAFETY: It's safe because parameters are valid and we have checked the result.
130         let rc = unsafe {
131             libc::syscall(
132                 libc::SYS_io_submit,
133                 self.0,
134                 iocbs.len() as c_ulong,
135                 iocbs.as_ptr(),
136             ) as c_int
137         };
138         if rc < 0 {
139             Err(Error::last_os_error())
140         } else {
141             Ok(rc as usize)
142         }
143     }
144 
145     /// Cancel an outstanding asynchronous I/O operation.
146     ///
147     /// Refer to Linux [`io_cancel`](http://man7.org/linux/man-pages/man2/io_cancel.2.html).
148     /// Note: according to current Linux kernel implementation(v4.19), libc::SYS_io_cancel always
149     /// return failure, thus rendering it useless.
150     ///
151     /// # Arguments
152     /// * `iocb`: The iocb for the operation to be canceled.
153     /// * `result`: If the operation is successfully canceled, the event will be copied into the
154     ///             memory pointed to by result without being placed into the completion queue.
cancel(&self, iocb: &IoControlBlock, result: &mut IoEvent) -> Result<()>155     pub fn cancel(&self, iocb: &IoControlBlock, result: &mut IoEvent) -> Result<()> {
156         // SAFETY: It's safe because parameters are valid and we have checked the result.
157         let rc = unsafe {
158             libc::syscall(
159                 libc::SYS_io_cancel,
160                 self.0,
161                 iocb as *const IoControlBlock,
162                 result as *mut IoEvent,
163             ) as c_int
164         };
165         if rc < 0 {
166             Err(Error::last_os_error())
167         } else {
168             Ok(())
169         }
170     }
171 
172     /// Read asynchronous I/O events from the completion queue.
173     ///
174     /// Refer to Linux [`io_getevents`](http://man7.org/linux/man-pages/man2/io_getevents.2.html).
175     ///
176     /// # Arguments
177     /// * `min_nr`: read at least min_nr events.
178     /// * `events`: array to receive the io operation results.
179     /// * `timeout`: optional amount of time to wait for events.
180     ///
181     /// # Examples
182     ///
183     /// ```
184     /// extern crate vmm_sys_util;
185     /// use vmm_sys_util::aio::*;
186     /// # use std::fs::File;
187     /// # use std::os::unix::io::AsRawFd;
188     ///
189     /// let file = File::open("/dev/zero").unwrap();
190     /// let ctx = IoContext::new(128).unwrap();
191     /// let mut buf: [u8; 4096] = unsafe { std::mem::uninitialized() };
192     /// let iocbs = [
193     ///     &mut IoControlBlock {
194     ///         aio_fildes: file.as_raw_fd() as u32,
195     ///         aio_lio_opcode: IOCB_CMD_PREAD as u16,
196     ///         aio_buf: buf.as_mut_ptr() as u64,
197     ///         aio_nbytes: buf.len() as u64,
198     ///         ..Default::default()
199     ///     },
200     ///     &mut IoControlBlock {
201     ///         aio_fildes: file.as_raw_fd() as u32,
202     ///         aio_lio_opcode: IOCB_CMD_PREAD as u16,
203     ///         aio_buf: buf.as_mut_ptr() as u64,
204     ///         aio_nbytes: buf.len() as u64,
205     ///         ..Default::default()
206     ///     },
207     /// ];
208     ///
209     /// let mut rc = ctx.submit(&iocbs[..]).unwrap();
210     /// let mut events = [unsafe { std::mem::uninitialized::<IoEvent>() }];
211     /// rc = ctx.get_events(1, &mut events, None).unwrap();
212     /// assert_eq!(rc, 1);
213     /// assert!(events[0].res > 0);
214     /// rc = ctx.get_events(1, &mut events, None).unwrap();
215     /// assert_eq!(rc, 1);
216     /// assert!(events[0].res > 0);
217     /// ```
get_events( &self, min_nr: c_long, events: &mut [IoEvent], timeout: Option<&mut libc::timespec>, ) -> Result<usize>218     pub fn get_events(
219         &self,
220         min_nr: c_long,
221         events: &mut [IoEvent],
222         timeout: Option<&mut libc::timespec>,
223     ) -> Result<usize> {
224         let to = match timeout {
225             Some(val) => val as *mut libc::timespec,
226             None => null_mut(),
227         };
228 
229         // SAFETY: It's safe because parameters are valid and we have checked the result.
230         let rc = unsafe {
231             libc::syscall(
232                 libc::SYS_io_getevents,
233                 self.0,
234                 min_nr,
235                 events.len() as c_long,
236                 events.as_mut_ptr(),
237                 to,
238             ) as c_int
239         };
240         if rc < 0 {
241             Err(Error::last_os_error())
242         } else {
243             Ok(rc as usize)
244         }
245     }
246 }
247 
248 impl Drop for IoContext {
drop(&mut self)249     fn drop(&mut self) {
250         if self.0 != 0 {
251             // SAFETY: It's safe because the context is created by us.
252             let _ = unsafe { libc::syscall(libc::SYS_io_destroy, self.0) as c_int };
253         }
254     }
255 }
256 
257 #[cfg(test)]
258 mod test {
259     use super::*;
260     use std::fs::File;
261     use std::os::unix::io::AsRawFd;
262 
263     #[test]
test_new_context()264     fn test_new_context() {
265         let _ = IoContext::new(0).unwrap_err();
266     }
267 
268     #[test]
test_cancel_request()269     fn test_cancel_request() {
270         let file = File::open("/dev/zero").unwrap();
271 
272         let ctx = IoContext::new(128).unwrap();
273         let mut buf: [u8; 16384] = [0u8; 16384];
274         let iocbs = [&mut IoControlBlock {
275             aio_fildes: file.as_raw_fd() as u32,
276             aio_lio_opcode: IOCB_CMD_PREAD as u16,
277             aio_buf: buf.as_mut_ptr() as u64,
278             aio_nbytes: buf.len() as u64,
279             ..Default::default()
280         }];
281 
282         let mut rc = ctx.submit(&iocbs).unwrap();
283         assert_eq!(rc, 1);
284 
285         let mut result = Default::default();
286         let err = ctx
287             .cancel(iocbs[0], &mut result)
288             .unwrap_err()
289             .raw_os_error()
290             .unwrap();
291         assert_eq!(err, libc::EINVAL);
292 
293         let mut events = [IoEvent::default()];
294         rc = ctx.get_events(1, &mut events, None).unwrap();
295         assert_eq!(rc, 1);
296         assert!(events[0].res > 0);
297     }
298 
299     #[test]
test_read_zero()300     fn test_read_zero() {
301         let file = File::open("/dev/zero").unwrap();
302 
303         let ctx = IoContext::new(128).unwrap();
304         let mut buf: [u8; 4096] = [0u8; 4096];
305         let iocbs = [
306             &mut IoControlBlock {
307                 aio_fildes: file.as_raw_fd() as u32,
308                 aio_lio_opcode: IOCB_CMD_PREAD as u16,
309                 aio_buf: buf.as_mut_ptr() as u64,
310                 aio_nbytes: buf.len() as u64,
311                 ..Default::default()
312             },
313             &mut IoControlBlock {
314                 aio_fildes: file.as_raw_fd() as u32,
315                 aio_lio_opcode: IOCB_CMD_PREAD as u16,
316                 aio_buf: buf.as_mut_ptr() as u64,
317                 aio_nbytes: buf.len() as u64,
318                 ..Default::default()
319             },
320         ];
321 
322         let mut rc = ctx.submit(&iocbs[..]).unwrap();
323         assert_eq!(rc, 2);
324 
325         let mut events = [IoEvent::default()];
326         rc = ctx.get_events(1, &mut events, None).unwrap();
327         assert_eq!(rc, 1);
328         assert!(events[0].res > 0);
329 
330         rc = ctx.get_events(1, &mut events, None).unwrap();
331         assert_eq!(rc, 1);
332         assert!(events[0].res > 0);
333     }
334 
335     #[test]
bindgen_test_layout_io_event()336     fn bindgen_test_layout_io_event() {
337         assert_eq!(
338             ::std::mem::size_of::<IoEvent>(),
339             32usize,
340             concat!("Size of: ", stringify!(IoEvent))
341         );
342         assert_eq!(
343             ::std::mem::align_of::<IoEvent>(),
344             8usize,
345             concat!("Alignment of", stringify!(IoEvent))
346         );
347     }
348 
349     #[test]
bindgen_test_layout_iocb()350     fn bindgen_test_layout_iocb() {
351         assert_eq!(
352             ::std::mem::size_of::<IoControlBlock>(),
353             64usize,
354             concat!("Size of:", stringify!(IoControlBlock))
355         );
356         assert_eq!(
357             ::std::mem::align_of::<IoControlBlock>(),
358             8usize,
359             concat!("Alignment of", stringify!(IoControlBlock))
360         );
361     }
362 }
363