1 // vim: tw=80
2
3 // Annoyingly, Cargo is unable to conditionally build an entire test binary. So
4 // we must disable the test here rather than in Cargo.toml
5 #![cfg(target_os = "freebsd")]
6
7 use nix::errno::*;
8 use nix::libc::off_t;
9 use nix::sys::aio::*;
10 use nix::sys::signal::SigevNotify;
11 use nix::unistd::{SysconfVar, sysconf};
12 use std::os::unix::io::AsRawFd;
13 use std::{thread, time};
14 use sysctl::CtlValue;
15 use tempfile::tempfile;
16
17 const BYTES_PER_OP: usize = 512;
18
19 /// Attempt to collect final status for all of `liocb`'s operations, freeing
20 /// system resources
finish_liocb(liocb: &mut LioCb)21 fn finish_liocb(liocb: &mut LioCb) {
22 for j in 0..liocb.len() {
23 loop {
24 let e = liocb.error(j);
25 match e {
26 Ok(()) => break,
27 Err(Errno::EINPROGRESS) =>
28 thread::sleep(time::Duration::from_millis(10)),
29 Err(x) => panic!("aio_error({:?})", x)
30 }
31 }
32 assert_eq!(liocb.aio_return(j).unwrap(), BYTES_PER_OP as isize);
33 }
34 }
35
36 // Deliberately exceed system resource limits, causing lio_listio to return EIO.
37 // This test must run in its own process since it deliberately uses all AIO
38 // resources. ATM it is only enabled on FreeBSD, because I don't know how to
39 // check system AIO limits on other operating systems.
40 #[test]
test_lio_listio_resubmit()41 fn test_lio_listio_resubmit() {
42 let mut resubmit_count = 0;
43
44 // Lookup system resource limits
45 let alm = sysconf(SysconfVar::AIO_LISTIO_MAX)
46 .expect("sysconf").unwrap() as usize;
47 let maqpp = if let CtlValue::Int(x) = sysctl::value(
48 "vfs.aio.max_aio_queue_per_proc").unwrap(){
49 x as usize
50 } else {
51 panic!("unknown sysctl");
52 };
53
54 // Find lio_listio sizes that satisfy the AIO_LISTIO_MAX constraint and also
55 // result in a final lio_listio call that can only partially be queued
56 let target_ops = maqpp + alm / 2;
57 let num_listios = (target_ops + alm - 3) / (alm - 2);
58 let ops_per_listio = (target_ops + num_listios - 1) / num_listios;
59 assert!((num_listios - 1) * ops_per_listio < maqpp,
60 "the last lio_listio won't make any progress; fix the algorithm");
61 println!("Using {:?} LioCbs of {:?} operations apiece", num_listios,
62 ops_per_listio);
63
64 let f = tempfile().unwrap();
65 let buffer_set = (0..num_listios).map(|_| {
66 (0..ops_per_listio).map(|_| {
67 vec![0u8; BYTES_PER_OP]
68 }).collect::<Vec<_>>()
69 }).collect::<Vec<_>>();
70
71 let mut liocbs = (0..num_listios).map(|i| {
72 let mut builder = LioCbBuilder::with_capacity(ops_per_listio);
73 for j in 0..ops_per_listio {
74 let offset = (BYTES_PER_OP * (i * ops_per_listio + j)) as off_t;
75 builder = builder.emplace_slice(f.as_raw_fd(),
76 offset,
77 &buffer_set[i][j][..],
78 0, //priority
79 SigevNotify::SigevNone,
80 LioOpcode::LIO_WRITE);
81 }
82 let mut liocb = builder.finish();
83 let mut err = liocb.listio(LioMode::LIO_NOWAIT, SigevNotify::SigevNone);
84 while err == Err(Errno::EIO) ||
85 err == Err(Errno::EAGAIN) ||
86 err == Err(Errno::EINTR) {
87 //
88 thread::sleep(time::Duration::from_millis(10));
89 resubmit_count += 1;
90 err = liocb.listio_resubmit(LioMode::LIO_NOWAIT,
91 SigevNotify::SigevNone);
92 }
93 liocb
94 }).collect::<Vec<_>>();
95
96 // Ensure that every AioCb completed
97 for liocb in liocbs.iter_mut() {
98 finish_liocb(liocb);
99 }
100
101 if resubmit_count > 0 {
102 println!("Resubmitted {:?} times, test passed", resubmit_count);
103 } else {
104 println!("Never resubmitted. Test ambiguous");
105 }
106 }
107