Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use preadv/pwritev to efficiently handle multiple mappings at once. #40

Merged
merged 3 commits into from Sep 3, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 = { git = "https://github.com/rust-lang/libc.git" }
luqmana marked this conversation as resolved.
Show resolved Hide resolved
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