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}