vmm_sys_util/linux/
aio.rs

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
11use std::io::{Error, Result};
12use std::os::raw::{c_int, c_long, c_uint, c_ulong};
13use std::ptr::null_mut;
14
15type __s16 = ::std::os::raw::c_short;
16type __u16 = ::std::os::raw::c_ushort;
17type __u32 = ::std::os::raw::c_uint;
18type __s64 = ::std::os::raw::c_longlong;
19type __u64 = ::std::os::raw::c_ulonglong;
20
21/// Read from a file descriptor at a given offset.
22pub const IOCB_CMD_PREAD: u32 = 0;
23/// Write to a file descriptor at a given offset.
24pub const IOCB_CMD_PWRITE: u32 = 1;
25/// Synchronize a file's in-core metadata and data to storage device.
26pub const IOCB_CMD_FSYNC: u32 = 2;
27/// Synchronize a file's in-core data to storage device.
28pub const IOCB_CMD_FDSYNC: u32 = 3;
29/// Noop, this defined by never used by linux kernel.
30pub const IOCB_CMD_NOOP: u32 = 6;
31/// Read from a file descriptor at a given offset into multiple buffers.
32pub const IOCB_CMD_PREADV: u32 = 7;
33/// Write to a file descriptor at a given offset from multiple buffers.
34pub 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.
38pub const IOCB_FLAG_RESFD: u32 = 1;
39
40/// Maximum number of concurrent requests.
41pub 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)]
47pub 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)]
66pub 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)]
76pub struct IoContext(::std::os::raw::c_ulong);
77
78impl 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)]
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    /// ```
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.
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    /// ```
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() as *mut libc::timespec,
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
248impl Drop for IoContext {
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)]
258mod test {
259    use super::*;
260    use std::fs::File;
261    use std::os::unix::io::AsRawFd;
262
263    #[test]
264    fn test_new_context() {
265        let _ = IoContext::new(0).unwrap_err();
266    }
267
268    #[test]
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]
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]
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]
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}