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