Skip to content

Commit

Permalink
wip: something that compiles
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean Banko authored and seanbanko committed Mar 2, 2024
1 parent cdafe53 commit 3f35273
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 8 deletions.
3 changes: 3 additions & 0 deletions block/src/async_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ pub enum AsyncIoError {
/// Failed synchronizing file.
#[error("Failed synchronizing file: {0}")]
Fsync(#[source] std::io::Error),
/// Failed allocating a temporary buffer.
#[error("Failed allocating a temporary buffer: {0}")]
TemporaryBufferAllocation(std::io::Error),
}

pub type AsyncIoResult<T> = std::result::Result<T, AsyncIoError>;
Expand Down
176 changes: 171 additions & 5 deletions block/src/raw_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,28 @@ use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, DiskFile, DiskFileError, DiskFileResult,
};
use crate::DiskTopology;
use core::slice;
use libc::{getppid, AT_STATX_SYNC_AS_STAT};
use smallvec::SmallVec;
use std::alloc::{alloc_zeroed, Layout};
use std::collections::VecDeque;
use std::fs::File;
use std::io::{Seek, SeekFrom};
use std::os::unix::io::{AsRawFd, RawFd};
use std::{clone, io};
use vmm_sys_util::eventfd::EventFd;

pub struct RawFileDiskSync {
file: File,
logical_block_size: Option<u64>,
}

impl RawFileDiskSync {
pub fn new(file: File) -> Self {
RawFileDiskSync { file }
pub fn new(file: File, logical_block_size: Option<u64>) -> Self {
RawFileDiskSync {
file,
logical_block_size,
}
}
}

Expand All @@ -30,15 +39,26 @@ impl DiskFile for RawFileDiskSync {
}

fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult<Box<dyn AsyncIo>> {
Ok(Box::new(RawFileSync::new(self.file.as_raw_fd())) as Box<dyn AsyncIo>)
Ok(Box::new(RawFileSync::new(
self.file.as_raw_fd(),
self.logical_block_size,
)) as Box<dyn AsyncIo>)
}

fn topology(&mut self) -> DiskTopology {
if let Ok(topology) = DiskTopology::probe(&self.file) {
let topology = if let Ok(topology) = DiskTopology::probe(&self.file) {
topology
} else {
warn!("Unable to get device topology. Using default topology");
DiskTopology::default()
};
if let Some(logical_block_size) = self.logical_block_size {
DiskTopology {
logical_block_size,
..topology
}
} else {
topology
}
}
}
Expand All @@ -47,18 +67,38 @@ pub struct RawFileSync {
fd: RawFd,
eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>,
logical_block_size: Option<u64>,
}

impl RawFileSync {
pub fn new(fd: RawFd) -> Self {
pub fn new(fd: RawFd, logical_block_size: Option<u64>) -> Self {
RawFileSync {
fd,
eventfd: EventFd::new(libc::EFD_NONBLOCK).expect("Failed creating EventFd for RawFile"),
completion_list: VecDeque::new(),
logical_block_size,
}
}
}

fn new_aligned_iovec(
length: usize,
alignment: usize,
) -> std::result::Result<libc::iovec, AsyncIoError> {
let layout = Layout::from_size_align(length as usize, alignment).unwrap();
// SAFETY: layout has non-zero size
let aligned_ptr = unsafe { alloc_zeroed(layout) };
if aligned_ptr.is_null() {
return Err(AsyncIoError::TemporaryBufferAllocation(
io::Error::last_os_error(),
));
}
Ok(libc::iovec {
iov_base: aligned_ptr as *mut libc::c_void,
iov_len: length as libc::size_t,
})
}

impl AsyncIo for RawFileSync {
fn notifier(&self) -> &EventFd {
&self.eventfd
Expand All @@ -70,6 +110,50 @@ impl AsyncIo for RawFileSync {
iovecs: &[libc::iovec],
user_data: u64,
) -> AsyncIoResult<()> {
if let Some(logical_block_size) = self.logical_block_size {
let logical_block_size = logical_block_size as usize;

let offset = offset as usize;
let offset_aligned_down = (offset / logical_block_size) * logical_block_size;
let offset_aligned_up =
(offset + logical_block_size + 1 / logical_block_size) * logical_block_size;

let total_length = iovecs.iter().fold(0, |acc, e| acc + e.iov_len);

let end = offset + total_length;
let end_aligned_up =
((end + logical_block_size - 1) / logical_block_size) * logical_block_size;

// Pad iovecs with dummy header and footer
let mut iovecs: Vec<libc::iovec> = iovecs.iter().cloned().collect();
let header = new_aligned_iovec(offset - offset_aligned_down, logical_block_size)?;
iovecs.insert(0, header);
let footer =
new_aligned_iovec(end_aligned_up - offset + total_length, logical_block_size)?;
iovecs.push(footer);

// SAFETY: FFI call with valid arguments
let result = unsafe {
libc::preadv(
self.fd as libc::c_int,
iovecs.as_ptr(),
iovecs.len() as libc::c_int,
offset_aligned_down as _,
)
};
if result < 0 {
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
}

self.completion_list.push_back((
user_data,
(result as usize - (header.iov_len + footer.iov_len)) as i32,
));
self.eventfd.write(1).unwrap();

return Ok(());
}

// SAFETY: FFI call with valid arguments
let result = unsafe {
libc::preadv(
Expand All @@ -95,6 +179,88 @@ impl AsyncIo for RawFileSync {
iovecs: &[libc::iovec],
user_data: u64,
) -> AsyncIoResult<()> {
if let Some(logical_block_size) = self.logical_block_size {
let logical_block_size = logical_block_size as usize;

let offset = offset as usize;
let offset_aligned_down = (offset / logical_block_size) * logical_block_size;
let offset_aligned_up =
(offset + logical_block_size + 1 / logical_block_size) * logical_block_size;

let total_length = iovecs.iter().fold(0, |acc, e| acc + e.iov_len);

let end = offset + total_length;
let end_aligned_down = (end / logical_block_size) * logical_block_size;
let end_aligned_up =
((end + logical_block_size - 1) / logical_block_size) * logical_block_size;

// Read headers
let mut tmp_iovecs: Vec<libc::iovec> = iovecs.iter().cloned().collect();
let header1 = new_aligned_iovec(offset - offset_aligned_down, logical_block_size)?;
let header2 = new_aligned_iovec(offset_aligned_up - offset, logical_block_size)?;
let mut header_iovecs: Vec<libc::iovec> = Vec::new();
header_iovecs.push(header1);
header_iovecs.push(header1);
// SAFETY: FFI call with valid arguments
let header_read_result = unsafe {
libc::preadv(
self.fd as libc::c_int,
header_iovecs.as_ptr(),
header_iovecs.len() as libc::c_int,
offset_aligned_down as _,
)
};
if header_read_result < 0 {
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
}

// Read footers
let mut footer_iovecs: Vec<libc::iovec> = iovecs.iter().cloned().collect();
let footer1 = new_aligned_iovec(end - end_aligned_down, logical_block_size)?;
let footer2 = new_aligned_iovec(end_aligned_up - end, logical_block_size)?;
let mut footer_iovecs: Vec<libc::iovec> = Vec::new();
footer_iovecs.push(footer1);
footer_iovecs.push(footer2);
// SAFETY: FFI call with valid arguments
let footer_read_result = unsafe {
libc::preadv(
self.fd as libc::c_int,
footer_iovecs.as_ptr(),
footer_iovecs.len() as libc::c_int,
offset_aligned_down as _,
)
};
if footer_read_result < 0 {
return Err(AsyncIoError::ReadVectored(std::io::Error::last_os_error()));
}

// Pad iovecs with header1 and footer2
let mut iovecs: Vec<libc::iovec> = iovecs.iter().cloned().collect();
iovecs.insert(0, header1);
iovecs.push(footer2);

let mut iovecs: Vec<libc::iovec> = iovecs.iter().cloned().collect();
// SAFETY: FFI call with valid arguments
let result = unsafe {
libc::pwritev(
self.fd as libc::c_int,
iovecs.as_ptr(),
iovecs.len() as libc::c_int,
offset_aligned_down as _,
)
};
if result < 0 {
return Err(AsyncIoError::WriteVectored(std::io::Error::last_os_error()));
}

self.completion_list.push_back((
user_data,
(result as usize - (header1.iov_len + footer2.iov_len)) as i32,
));
self.eventfd.write(1).unwrap();

return Ok(());
}
// SAFETY: FFI call with valid arguments
let result = unsafe {
libc::pwritev(
Expand Down
12 changes: 10 additions & 2 deletions vmm/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,8 @@ impl DiskConfig {
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,\
id=<device_id>,pci_segment=<segment_id>,rate_limit_group=<group_id>,\
queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>";
queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>,\
logical_block_size=<bytes>";

pub fn parse(disk: &str) -> Result<Self> {
let mut parser = OptionParser::new();
Expand All @@ -1038,7 +1039,8 @@ impl DiskConfig {
.add("pci_segment")
.add("serial")
.add("rate_limit_group")
.add("queue_affinity");
.add("queue_affinity")
.add("logical_block_size");
parser.parse(disk).map_err(Error::ParseDisk)?;

let path = parser.get("path").map(PathBuf::from);
Expand Down Expand Up @@ -1123,6 +1125,10 @@ impl DiskConfig {
})
.collect()
});
let logical_block_size = parser
.convert::<u64>("logical_block_size")
.map_err(Error::ParseDisk)
.unwrap_or_default();
let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 {
Some(TokenBucketConfig {
size: bw_size,
Expand Down Expand Up @@ -1167,6 +1173,7 @@ impl DiskConfig {
pci_segment,
serial,
queue_affinity,
logical_block_size,
})
}

Expand Down Expand Up @@ -2968,6 +2975,7 @@ mod tests {
pci_segment: 0,
serial: None,
queue_affinity: None,
logical_block_size: Some(512),
}
}

Expand Down
3 changes: 2 additions & 1 deletion vmm/src/device_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2461,7 +2461,8 @@ impl DeviceManager {
Box::new(RawFileDiskAio::new(file)) as Box<dyn DiskFile>
} else {
info!("Using synchronous RAW disk file");
Box::new(RawFileDiskSync::new(file)) as Box<dyn DiskFile>
Box::new(RawFileDiskSync::new(file, disk_cfg.logical_block_size))
as Box<dyn DiskFile>
}
}
ImageType::Qcow2 => {
Expand Down
2 changes: 2 additions & 0 deletions vmm/src/vm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ pub struct DiskConfig {
pub serial: Option<String>,
#[serde(default)]
pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
#[serde(default)]
pub logical_block_size: Option<u64>,
}

pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
Expand Down

0 comments on commit 3f35273

Please sign in to comment.