Skip to content

Commit 74b8435

Browse files
authored
Test register buffers (#278)
* update docs * create register buffers test * clean up and add safety contracts * stop leaking memory * remove extra docs * remove man page pointers * add test back
1 parent fe2eade commit 74b8435

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

io-uring-test/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ fn test<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
9595
// register
9696
tests::register::test_register_files_sparse(&mut ring, &test)?;
9797
tests::register_buf_ring::test_register_buf_ring(&mut ring, &test)?;
98+
tests::register_buffers::test_register_buffers(&mut ring, &test)?;
9899
tests::register_sync_cancel::test_register_sync_cancel(&mut ring, &test)?;
99100
tests::register_sync_cancel::test_register_sync_cancel_unsubmitted(&mut ring, &test)?;
100101
tests::register_sync_cancel::test_register_sync_cancel_any(&mut ring, &test)?;

io-uring-test/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod poll;
66
pub mod queue;
77
pub mod register;
88
pub mod register_buf_ring;
9+
pub mod register_buffers;
910
pub mod register_sync_cancel;
1011
pub mod regression;
1112
pub mod timeout;
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
use crate::Test;
2+
use io_uring::{
3+
cqueue,
4+
opcode::{ReadFixed, WriteFixed},
5+
squeue,
6+
types::Fd,
7+
IoUring,
8+
};
9+
use libc::iovec;
10+
use std::{
11+
io::{self, IoSliceMut},
12+
ops::DerefMut,
13+
os::fd::AsRawFd,
14+
};
15+
16+
/// Creates shared buffers to be registered into the [`IoUring`] instance, and then tests that they
17+
/// can actually be used through the [`WriteFixed`] and [`ReadFixed`] opcodes.
18+
pub fn test_register_buffers<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
19+
ring: &mut IoUring<S, C>,
20+
test: &Test,
21+
) -> io::Result<()> {
22+
require!(
23+
test;
24+
test.probe.is_supported(WriteFixed::CODE);
25+
test.probe.is_supported(ReadFixed::CODE);
26+
);
27+
28+
println!("test register_buffers");
29+
30+
const BUF_SIZE: usize = 1 << 12; // Page size
31+
const BUFFERS: usize = 8; // The maximum number of squeue entries (in main.rs)
32+
33+
let file = tempfile::tempfile()?;
34+
let fd = Fd(file.as_raw_fd());
35+
36+
// Create the buffers
37+
let mut bufs = (0..BUFFERS)
38+
.map(|i| vec![b'A' + i as u8; BUF_SIZE])
39+
.collect::<Vec<_>>();
40+
41+
// Create the slices that point to the buffers
42+
let mut slices: Vec<IoSliceMut<'_>> = bufs
43+
.iter_mut()
44+
.map(|buf| IoSliceMut::new(buf.deref_mut()))
45+
.collect();
46+
47+
// Check that the data is correct
48+
assert!(slices
49+
.iter()
50+
.enumerate()
51+
.all(|(i, s)| s.iter().all(|&x| x == (b'A' + i as u8))));
52+
53+
// Now actually set up and register the buffers
54+
55+
// Safety: `IoSliceMut` is ABI compatible with the `iovec` type on Unix platforms, so it is safe
56+
// to cast these as `slices` is valid for this entire function
57+
let iovecs: &[iovec] =
58+
unsafe { std::slice::from_raw_parts(slices.as_ptr().cast(), slices.len()) };
59+
60+
let submitter = ring.submitter();
61+
// Safety: Since `iovecs` is derived from valid `IoSliceMut`s, this upholds the safety contract
62+
// of `register_buffers` that the buffers are valid until the buffers are unregistered
63+
unsafe { submitter.register_buffers(iovecs)? };
64+
65+
// Prepare writing the buffers out to the file
66+
(0..BUFFERS).for_each(|index| {
67+
let buf_ptr = slices[index].as_ptr();
68+
69+
let write_entry = WriteFixed::new(fd, buf_ptr.cast(), BUF_SIZE as u32, index as u16)
70+
.offset((index * BUF_SIZE) as u64)
71+
.build()
72+
.user_data(index as u64);
73+
74+
let mut submission_queue = ring.submission();
75+
// Safety: We have guaranteed that the buffers in `slices` are all valid for the entire
76+
// duration of this function
77+
unsafe {
78+
submission_queue.push(&write_entry.into()).unwrap();
79+
}
80+
});
81+
82+
// Submit and wait for the kernel to complete all of the operations
83+
assert_eq!(ring.submit_and_wait(BUFFERS)?, BUFFERS);
84+
85+
// The file should have saved all of the data now
86+
let cqes: Vec<cqueue::Entry> = ring.completion().map(Into::into).collect();
87+
cqes.iter().enumerate().for_each(|(index, ce)| {
88+
assert!(ce.user_data() < BUFFERS as u64);
89+
assert_eq!(
90+
ce.result(),
91+
BUF_SIZE as i32,
92+
"WriteFixed operation {} failed",
93+
index
94+
);
95+
});
96+
97+
// Zero out all buffers in memory
98+
(0..BUFFERS).for_each(|index| {
99+
slices[index].fill(0);
100+
assert!(slices[index].iter().all(|&x| x == 0));
101+
});
102+
103+
// Prepare reading the data back into the buffers from the file
104+
(0..BUFFERS).for_each(|index| {
105+
let buf_ptr = slices[index].as_mut_ptr();
106+
107+
let read_entry = ReadFixed::new(fd, buf_ptr.cast(), BUF_SIZE as u32, index as u16)
108+
.offset((index * BUF_SIZE) as u64)
109+
.build()
110+
.user_data(index as u64);
111+
112+
let mut submission_queue = ring.submission();
113+
// Safety: We have guaranteed that the buffers in `slices` are all valid for the entire
114+
// duration of this function
115+
unsafe {
116+
submission_queue.push(&read_entry.into()).unwrap();
117+
}
118+
});
119+
120+
// Submit and wait for the kernel to complete all of the operations
121+
assert_eq!(ring.submit_and_wait(BUFFERS)?, BUFFERS);
122+
123+
// The data should be back in the buffers
124+
let cqes: Vec<cqueue::Entry> = ring.completion().map(Into::into).collect();
125+
cqes.iter().enumerate().for_each(|(index, ce)| {
126+
assert!(ce.user_data() < BUFFERS as u64);
127+
assert_eq!(
128+
ce.result(),
129+
BUF_SIZE as i32,
130+
"ReadFixed operation {} failed",
131+
index
132+
);
133+
});
134+
135+
// Check that the data has been restored
136+
assert!((0..BUFFERS).all(|index| { slices[index].iter().all(|&x| x == (b'A' + index as u8)) }));
137+
138+
// Unregister the buffers
139+
let submitter = ring.submitter();
140+
submitter.unregister_buffers()?;
141+
142+
Ok(())
143+
}

0 commit comments

Comments
 (0)