diff --git a/block/src/qcow_async.rs b/block/src/qcow_async.rs index c26fdaea2..4501502a9 100644 --- a/block/src/qcow_async.rs +++ b/block/src/qcow_async.rs @@ -582,6 +582,7 @@ mod unit_tests { use super::*; use crate::disk_file::AsyncDiskFile; use crate::qcow::{QcowFile, RawFile}; + use crate::{BatchRequest, RequestType}; fn create_disk_with_data( file_size: u64, @@ -742,4 +743,100 @@ mod unit_tests { "second half should come from cluster 1" ); } + + #[test] + fn test_qcow_async_batch_mixed_requests() { + let file_size = 100 * 1024 * 1024; + let temp_file = TempFile::new().unwrap(); + { + let raw_file = RawFile::new(temp_file.as_file().try_clone().unwrap(), false); + QcowFile::new(raw_file, 3, file_size, true).unwrap(); + } + let disk = QcowDiskAsync::new(temp_file.as_file().try_clone().unwrap(), false, false, true) + .unwrap(); + + let mut async_io = disk.new_async_io(8).unwrap(); + + // Prepare write data for two regions. + let write_a = vec![0xAA; 4096]; + let write_b = vec![0xBB; 4096]; + let offset_a: u64 = 0; + let offset_b: u64 = 65536; + + let iov_a = libc::iovec { + iov_base: write_a.as_ptr() as *mut libc::c_void, + iov_len: write_a.len(), + }; + let iov_b = libc::iovec { + iov_base: write_b.as_ptr() as *mut libc::c_void, + iov_len: write_b.len(), + }; + + let batch = vec![ + BatchRequest { + offset: offset_a as libc::off_t, + iovecs: smallvec::smallvec![iov_a], + user_data: 10, + request_type: RequestType::Out, + }, + BatchRequest { + offset: offset_b as libc::off_t, + iovecs: smallvec::smallvec![iov_b], + user_data: 20, + request_type: RequestType::Out, + }, + ]; + + async_io.submit_batch_requests(&batch).unwrap(); + + let mut completions = [ + wait_for_completion(async_io.as_mut()), + wait_for_completion(async_io.as_mut()), + ]; + completions.sort_by_key(|c| c.0); + assert_eq!(completions[0], (10, 4096)); + assert_eq!(completions[1], (20, 4096)); + drop(async_io); + + // Batch read both regions back. + let mut read_a = vec![0u8; 4096]; + let mut read_b = vec![0u8; 4096]; + let riov_a = libc::iovec { + iov_base: read_a.as_mut_ptr() as *mut libc::c_void, + iov_len: read_a.len(), + }; + let riov_b = libc::iovec { + iov_base: read_b.as_mut_ptr() as *mut libc::c_void, + iov_len: read_b.len(), + }; + + let mut async_io = disk.new_async_io(8).unwrap(); + let read_batch = vec![ + BatchRequest { + offset: offset_a as libc::off_t, + iovecs: smallvec::smallvec![riov_a], + user_data: 30, + request_type: RequestType::In, + }, + BatchRequest { + offset: offset_b as libc::off_t, + iovecs: smallvec::smallvec![riov_b], + user_data: 40, + request_type: RequestType::In, + }, + ]; + + async_io.submit_batch_requests(&read_batch).unwrap(); + + let mut completions = [ + wait_for_completion(async_io.as_mut()), + wait_for_completion(async_io.as_mut()), + ]; + completions.sort_by_key(|c| c.0); + assert_eq!(completions[0], (30, 4096)); + assert_eq!(completions[1], (40, 4096)); + + assert_eq!(read_a, write_a, "batch read A should match written data"); + assert_eq!(read_b, write_b, "batch read B should match written data"); + } }