@@ -8,24 +8,70 @@ use io_uring::{
8
8
} ;
9
9
use libc:: iovec;
10
10
use std:: {
11
+ fs:: File ,
11
12
io:: { self , IoSliceMut } ,
13
+ io:: { Error , Write } ,
12
14
ops:: DerefMut ,
13
15
os:: fd:: AsRawFd ,
16
+ os:: fd:: FromRawFd ,
14
17
} ;
15
18
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.
19
+ use io_uring:: { opcode, types} ;
20
+ use libc:: EFAULT ;
21
+
18
22
pub fn test_register_buffers < S : squeue:: EntryMarker , C : cqueue:: EntryMarker > (
19
23
ring : & mut IoUring < S , C > ,
20
24
test : & Test ,
21
- ) -> io:: Result < ( ) > {
25
+ ) -> anyhow:: Result < ( ) > {
26
+ _test_register_buffers (
27
+ ring,
28
+ test,
29
+ "register_buffers" ,
30
+ None ,
31
+ |ring, iovecs, _| unsafe { ring. submitter ( ) . register_buffers ( & iovecs) } ,
32
+ ) ?;
33
+ _test_register_buffers (
34
+ ring,
35
+ test,
36
+ "register_buffers2" ,
37
+ ring. params ( ) . is_feature_resource_tagging ( ) ,
38
+ |ring, iovecs, tags| unsafe { ring. submitter ( ) . register_buffers2 ( iovecs, tags) } ,
39
+ ) ?;
40
+ _test_register_buffers (
41
+ ring,
42
+ test,
43
+ "register_buffers_sparse" ,
44
+ ring. params ( ) . is_feature_resource_tagging ( ) ,
45
+ |ring, iovecs, _| {
46
+ let submitter = ring. submitter ( ) ;
47
+ submitter. register_buffers_sparse ( iovecs. len ( ) as _ ) ?;
48
+ unsafe { submitter. register_buffers_update ( 0 , iovecs, None ) }
49
+ } ,
50
+ ) ?;
51
+
52
+ return Ok ( ( ) ) ;
53
+ }
54
+
55
+ fn _test_register_buffers <
56
+ S : squeue:: EntryMarker ,
57
+ C : cqueue:: EntryMarker ,
58
+ F : FnMut ( & mut IoUring < S , C > , & [ iovec ] , & [ u64 ] ) -> io:: Result < ( ) > ,
59
+ P : Into < Option < bool > > ,
60
+ > (
61
+ ring : & mut IoUring < S , C > ,
62
+ test : & Test ,
63
+ name : & str ,
64
+ probe : P ,
65
+ mut register : F ,
66
+ ) -> anyhow:: Result < ( ) > {
22
67
require ! (
23
68
test;
24
69
test. probe. is_supported( WriteFixed :: CODE ) ;
25
70
test. probe. is_supported( ReadFixed :: CODE ) ;
71
+ probe. into( ) . unwrap_or( true ) ;
26
72
) ;
27
73
28
- println ! ( "test register_buffers " ) ;
74
+ println ! ( "test {name} " ) ;
29
75
30
76
const BUF_SIZE : usize = 1 << 12 ; // Page size
31
77
const BUFFERS : usize = 8 ; // The maximum number of squeue entries (in main.rs)
@@ -56,11 +102,11 @@ pub fn test_register_buffers<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
56
102
// to cast these as `slices` is valid for this entire function
57
103
let iovecs: & [ iovec ] =
58
104
unsafe { std:: slice:: from_raw_parts ( slices. as_ptr ( ) . cast ( ) , slices. len ( ) ) } ;
105
+ let tags = vec ! [ 0 ; slices. len( ) ] ;
59
106
60
- let submitter = ring. submitter ( ) ;
61
107
// Safety: Since `iovecs` is derived from valid `IoSliceMut`s, this upholds the safety contract
62
108
// of `register_buffers` that the buffers are valid until the buffers are unregistered
63
- unsafe { submitter . register_buffers ( iovecs ) ? } ;
109
+ register ( ring , iovecs , & tags ) . unwrap ( ) ;
64
110
65
111
// Prepare writing the buffers out to the file
66
112
( 0 ..BUFFERS ) . for_each ( |index| {
@@ -141,3 +187,165 @@ pub fn test_register_buffers<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
141
187
142
188
Ok ( ( ) )
143
189
}
190
+
191
+ const BUFFER_TAG : u64 = 0xbadcafe ;
192
+ const TIMEOUT_TAG : u64 = 0xbadf00d ;
193
+
194
+ pub fn test_register_buffers_update < S : squeue:: EntryMarker , C : cqueue:: EntryMarker > (
195
+ ring : & mut IoUring < S , C > ,
196
+ test : & Test ,
197
+ ) -> anyhow:: Result < ( ) > {
198
+ require ! (
199
+ test;
200
+ test. probe. is_supported( opcode:: ReadFixed :: CODE ) ;
201
+ ring. params( ) . is_feature_resource_tagging( ) ;
202
+ ) ;
203
+
204
+ println ! ( "test register_buffers_update" ) ;
205
+
206
+ let ( read, mut write) = create_pipe ( ) ?;
207
+ let mut buf = [ 0xde , 0xed , 0xbe , 0xef ] ;
208
+ let mut tags = [ BUFFER_TAG ] ;
209
+ let iovecs = [ libc:: iovec {
210
+ iov_len : buf. len ( ) ,
211
+ iov_base : buf. as_mut_ptr ( ) as _ ,
212
+ } ] ;
213
+ let timeout = types:: Timespec :: new ( ) . nsec ( 50 * 1_000_000 ) ;
214
+ let timeout = opcode:: Timeout :: new ( & timeout as _ )
215
+ . build ( )
216
+ . user_data ( TIMEOUT_TAG )
217
+ . into ( ) ;
218
+ let read_sqe = opcode:: ReadFixed :: new (
219
+ types:: Fd ( read. as_raw_fd ( ) ) ,
220
+ buf. as_mut_ptr ( ) ,
221
+ buf. len ( ) as _ ,
222
+ 5 ,
223
+ )
224
+ . build ( )
225
+ . user_data ( 42 )
226
+ . into ( ) ;
227
+
228
+ // Register a buffer table and then immediately unregister it
229
+ ring. submitter ( ) . register_buffers_sparse ( 1 ) ?;
230
+ ring. submitter ( ) . unregister_buffers ( ) ?;
231
+
232
+ // Push a timeout of 50ms
233
+ unsafe { ring. submission ( ) . push ( & timeout) . unwrap ( ) } ;
234
+
235
+ // We should not receive any other entries than the timeout
236
+ check_only_timeout ( ring) ?;
237
+
238
+ // Register a sparse buffer table of 10 elements
239
+ ring. submitter ( ) . register_buffers_sparse ( 10 ) ?;
240
+
241
+ // Try read the pipe using a sparse buffer
242
+ let cqe = {
243
+ write. write ( "yo" . as_bytes ( ) ) ?;
244
+
245
+ unsafe { ring. submission ( ) . push ( & read_sqe) . unwrap ( ) } ;
246
+
247
+ ring. submit_and_wait ( 1 ) ?;
248
+
249
+ ring. completion ( ) . next ( ) . unwrap ( ) . into ( )
250
+ } ;
251
+
252
+ // We should get the correct user_data
253
+ if cqe. user_data ( ) != 42 {
254
+ return Err ( anyhow:: anyhow!( "unexpected completion queue entry" ) ) ;
255
+ }
256
+
257
+ // EFAULT is to be expected with incorrect fixed buffers
258
+ if cqe. result ( ) != -EFAULT {
259
+ return Err ( anyhow:: anyhow!( "unexpected read result: {}" , cqe. result( ) ) ) ;
260
+ }
261
+
262
+ // Register a buffer at the index 5
263
+ unsafe {
264
+ ring. submitter ( )
265
+ . register_buffers_update ( 5 , & iovecs, Some ( & tags) ) ?;
266
+
267
+ // Push a timeout of 50ms
268
+ ring. submission ( ) . push ( & timeout) . unwrap ( ) ;
269
+ }
270
+
271
+ // We should not receive any other entries than the timeout
272
+ check_only_timeout ( ring) ?;
273
+
274
+ // Register a buffer at the same index 5, but this time with an empty tag.
275
+ let cqe = {
276
+ tags[ 0 ] = 0 ;
277
+
278
+ unsafe {
279
+ ring. submitter ( )
280
+ . register_buffers_update ( 5 , & iovecs, Some ( & tags) ) ?;
281
+ }
282
+ ring. submit_and_wait ( 1 ) ?;
283
+
284
+ ring. completion ( ) . next ( ) . unwrap ( ) . into ( )
285
+ } ;
286
+
287
+ // We should get a cqe with the first tag because we registered a
288
+ // new buffer at an index where a buffer was already registered.
289
+ if cqe. user_data ( ) != BUFFER_TAG {
290
+ return Err ( anyhow:: anyhow!(
291
+ "expected completion queue to contain a buffer unregistered event"
292
+ ) ) ;
293
+ }
294
+
295
+ // Try reading now that the buffer is registered at index 5
296
+ let cqe = {
297
+ unsafe {
298
+ ring. submission ( ) . push ( & read_sqe) . unwrap ( ) ;
299
+ }
300
+
301
+ ring. submit_and_wait ( 1 ) ?;
302
+
303
+ ring. completion ( ) . next ( ) . unwrap ( ) . into ( )
304
+ } ;
305
+
306
+ // We should get the correct user_data
307
+ if cqe. user_data ( ) != 42 {
308
+ return Err ( anyhow:: anyhow!( "unexpected completion queue entry" ) ) ;
309
+ }
310
+
311
+ // We should read exactly two bytes
312
+ if cqe. result ( ) != 2 {
313
+ return Err ( anyhow:: anyhow!( "unexpected read result: {}" , cqe. result( ) ) ) ;
314
+ }
315
+
316
+ // The first two bytes of `buf` should be "yo"
317
+ if & buf[ 0 ..2 ] != "yo" . as_bytes ( ) {
318
+ return Err ( anyhow:: anyhow!( "unexpected read buffer data: {:x?}" , & buf) ) ;
319
+ }
320
+
321
+ return Ok ( ( ) ) ;
322
+ }
323
+
324
+ /// Create a pipe and return both ends as RAII `File` handles
325
+ fn create_pipe ( ) -> io:: Result < ( File , File ) > {
326
+ let mut fds = [ -1 , -1 ] ;
327
+
328
+ unsafe {
329
+ if libc:: pipe ( fds. as_mut_ptr ( ) ) == -1 {
330
+ Err ( Error :: last_os_error ( ) )
331
+ } else {
332
+ Ok ( ( File :: from_raw_fd ( fds[ 0 ] ) , File :: from_raw_fd ( fds[ 1 ] ) ) )
333
+ }
334
+ }
335
+ }
336
+
337
+ /// Submit sqes and asserts the only cqe is a timeout entry
338
+ fn check_only_timeout < S : squeue:: EntryMarker , C : cqueue:: EntryMarker > (
339
+ ring : & mut IoUring < S , C > ,
340
+ ) -> Result < ( ) , anyhow:: Error > {
341
+ ring. submit_and_wait ( 1 ) ?;
342
+
343
+ if Into :: < cqueue:: Entry > :: into ( ring. completion ( ) . next ( ) . unwrap ( ) ) . user_data ( ) == TIMEOUT_TAG {
344
+ // There should not be any more entries in the queue
345
+ if ring. completion ( ) . next ( ) . is_none ( ) {
346
+ return Ok ( ( ) ) ;
347
+ }
348
+ }
349
+
350
+ Err ( anyhow:: anyhow!( "unexpected completion queue entry" ) )
351
+ }
0 commit comments