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