Skip to content

Commit

Permalink
Use preadv/pwritev to efficiently handle multiple mappings at once.
Browse files Browse the repository at this point in the history
  • Loading branch information
luqmana committed Sep 2, 2021
1 parent faebb4f commit 5e33d91
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 37 deletions.
3 changes: 2 additions & 1 deletion propolis/Cargo.toml
Expand Up @@ -8,7 +8,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libc = "0.2"
#libc = "0.2"
libc = { path = "../../libc" }
bitflags = "1.2"
byteorder = "1"
lazy_static = "1.4"
Expand Down
41 changes: 6 additions & 35 deletions propolis/src/block.rs
Expand Up @@ -11,7 +11,7 @@ use std::sync::{Arc, Mutex, Weak};

use crate::common::*;
use crate::dispatch::{DispCtx, Dispatcher};
use crate::vmm::{MemCtx, SubMapping};
use crate::vmm::{MappingExt, MemCtx, SubMapping};

/// Type of operations which may be issued to a virtual block device.
#[derive(Copy, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -165,40 +165,11 @@ impl<R: BlockReq> FileBdev<R> {

let total_size: usize = mappings.iter().map(|x| x.len()).sum();

let nbytes = {
let mut nbytes = 0;

for mapping in mappings {
let inner_nbytes = if is_read {
mapping.pread(
&self.fp,
mapping.len(),
(offset + nbytes) as i64,
)?
} else {
mapping.pwrite(
&self.fp,
mapping.len(),
(offset + nbytes) as i64,
)?
};

if inner_nbytes != mapping.len() {
println!(
"{} at offset {} of size {} incomplete! only {} bytes",
if is_read { "read" } else { "write" },
offset + nbytes,
mapping.len(),
inner_nbytes,
);
return Ok(BlockResult::Failure);
}

nbytes += inner_nbytes;
}

nbytes
};
let nbytes = if is_read {
mappings.preadv(&self.fp, offset as i64)
} else {
mappings.pwritev(&self.fp, offset as i64)
}?;

assert_eq!(nbytes as usize, total_size);
Ok(BlockResult::Success)
Expand Down
86 changes: 85 additions & 1 deletion propolis/src/vmm/mapping.rs
@@ -1,5 +1,7 @@
//! Module for managing guest memory mappings.

use libc::iovec;

use crate::common::PAGE_SIZE;
use crate::util::aspace::ASpace;
use crate::vmm::VmmFile;
Expand Down Expand Up @@ -370,7 +372,7 @@ impl<'a> SubMapping<'a> {
if !self.prot.contains(Prot::WRITE) {
return Err(Error::new(
ErrorKind::PermissionDenied,
"No read access",
"No write access",
));
}

Expand Down Expand Up @@ -517,6 +519,88 @@ impl<'a> SubMapping<'a> {
}
}

pub trait MappingExt {
/// preadv from `file` into multiple mappings
fn preadv(&self, file: &File, offset: i64) -> Result<usize>;

/// pwritev from multiple mappings to `file`
fn pwritev(&self, file: &File, offset: i64) -> Result<usize>;
}

impl<'a, T: AsRef<[SubMapping<'a>]>> MappingExt for T {
fn preadv(&self, file: &File, offset: i64) -> Result<usize> {
if !self
.as_ref()
.iter()
.all(|mapping| mapping.prot.contains(Prot::WRITE))
{
return Err(Error::new(
ErrorKind::PermissionDenied,
"No write access",
));
}

let iov = self
.as_ref()
.iter()
.map(|mapping| iovec {
iov_base: mapping.ptr.as_ptr() as *mut libc::c_void,
iov_len: mapping.len,
})
.collect::<Vec<_>>();

let read = unsafe {
libc::preadv(
file.as_raw_fd(),
iov.as_ptr(),
iov.len() as libc::c_int,
offset,
)
};
if read == -1 {
return Err(Error::last_os_error());
}

Ok(read as usize)
}

fn pwritev(&self, file: &File, offset: i64) -> Result<usize> {
if !self
.as_ref()
.iter()
.all(|mapping| mapping.prot.contains(Prot::READ))
{
return Err(Error::new(
ErrorKind::PermissionDenied,
"No read access",
));
}

let iov = self
.as_ref()
.iter()
.map(|mapping| iovec {
iov_base: mapping.ptr.as_ptr() as *mut libc::c_void,
iov_len: mapping.len,
})
.collect::<Vec<_>>();

let written = unsafe {
libc::pwritev(
file.as_raw_fd(),
iov.as_ptr(),
iov.len() as libc::c_int,
offset,
)
};
if written == -1 {
return Err(Error::last_os_error());
}

Ok(written as usize)
}
}

#[cfg(test)]
pub mod tests {
use super::*;
Expand Down

0 comments on commit 5e33d91

Please sign in to comment.