mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-21 23:41:15 +00:00
virtio-devices: mem: Adjust based on specification
Adjust the code to comply better with the virtio-mem specification by adding some validation for the virtio-mem configuration, but also by updating the virtio-mem configuration itself. Nowhere in the virtio-mem specification is stated the usable region size must be adjusted everytime the plugged size changes. For simplification reason, and without going against the specification, the usable region size is now kept static, setting its value to the size of the whole region. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
f24094392e
commit
ad8adcb955
@ -21,9 +21,9 @@ use super::{
|
|||||||
|
|
||||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||||
use crate::{VirtioInterrupt, VirtioInterruptType};
|
use crate::{VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use anyhow::anyhow;
|
||||||
use libc::EFD_NONBLOCK;
|
use libc::EFD_NONBLOCK;
|
||||||
use seccomp::{SeccompAction, SeccompFilter};
|
use seccomp::{SeccompAction, SeccompFilter};
|
||||||
use std::cmp;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
@ -44,10 +44,9 @@ const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
|
|||||||
|
|
||||||
// 128MiB is the standard memory block size in Linux. A virtio-mem region must
|
// 128MiB is the standard memory block size in Linux. A virtio-mem region must
|
||||||
// be aligned on this size, and the region size must be a multiple of it.
|
// be aligned on this size, and the region size must be a multiple of it.
|
||||||
pub const VIRTIO_MEM_ALIGN_SIZE: u64 = 128 * 1024 * 1024;
|
pub const VIRTIO_MEM_ALIGN_SIZE: u64 = 128 << 20;
|
||||||
// Use 2 MiB alignment so transparent hugepages can be used by KVM.
|
// Use 2 MiB alignment so transparent hugepages can be used by KVM.
|
||||||
const VIRTIO_MEM_DEFAULT_BLOCK_SIZE: u64 = 512 * 4096;
|
const VIRTIO_MEM_DEFAULT_BLOCK_SIZE: u64 = 2 << 20;
|
||||||
const VIRTIO_MEM_USABLE_EXTENT: u64 = 256 * 1024 * 1024;
|
|
||||||
|
|
||||||
// Request processed successfully, applicable for
|
// Request processed successfully, applicable for
|
||||||
// - VIRTIO_MEM_REQ_PLUG
|
// - VIRTIO_MEM_REQ_PLUG
|
||||||
@ -64,7 +63,8 @@ const VIRTIO_MEM_RESP_NACK: u16 = 1;
|
|||||||
// - VIRTIO_MEM_REQ_PLUG
|
// - VIRTIO_MEM_REQ_PLUG
|
||||||
// - VIRTIO_MEM_REQ_UNPLUG
|
// - VIRTIO_MEM_REQ_UNPLUG
|
||||||
// - VIRTIO_MEM_REQ_UNPLUG_ALL
|
// - VIRTIO_MEM_REQ_UNPLUG_ALL
|
||||||
// VIRTIO_MEM_RESP_BUSY: u16 = 2;
|
#[allow(unused)]
|
||||||
|
const VIRTIO_MEM_RESP_BUSY: u16 = 2;
|
||||||
|
|
||||||
// Error in request (e.g. addresses/alignment), applicable for
|
// Error in request (e.g. addresses/alignment), applicable for
|
||||||
// - VIRTIO_MEM_REQ_PLUG
|
// - VIRTIO_MEM_REQ_PLUG
|
||||||
@ -117,16 +117,13 @@ pub enum Error {
|
|||||||
// Failed to MpscRecv.
|
// Failed to MpscRecv.
|
||||||
MpscRecvFail(mpsc::RecvError),
|
MpscRecvFail(mpsc::RecvError),
|
||||||
// Resize invalid argument
|
// Resize invalid argument
|
||||||
ResizeInval(String),
|
ResizeError(anyhow::Error),
|
||||||
// Fail to resize trigger
|
// Fail to resize trigger
|
||||||
ResizeTriggerFail(DeviceError),
|
ResizeTriggerFail(DeviceError),
|
||||||
|
// Invalid configuration
|
||||||
|
ValidateError(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Got from qemu/include/standard-headers/linux/virtio_mem.h
|
|
||||||
// rust union doesn't support std::default::Default that
|
|
||||||
// need by mem.read_obj.
|
|
||||||
// Then move virtio_mem_req_plug, virtio_mem_req_unplug and
|
|
||||||
// virtio_mem_req_state to virtio_mem_req.
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
struct VirtioMemReq {
|
struct VirtioMemReq {
|
||||||
@ -134,31 +131,23 @@ struct VirtioMemReq {
|
|||||||
padding: [u16; 3],
|
padding: [u16; 3],
|
||||||
addr: u64,
|
addr: u64,
|
||||||
nb_blocks: u16,
|
nb_blocks: u16,
|
||||||
|
padding_1: [u16; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe because it only has data and has no implicit padding.
|
// Safe because it only has data and has no implicit padding.
|
||||||
unsafe impl ByteValued for VirtioMemReq {}
|
unsafe impl ByteValued for VirtioMemReq {}
|
||||||
|
|
||||||
// Got from qemu/include/standard-headers/linux/virtio_mem.h
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
struct VirtioMemRespState {
|
|
||||||
state: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
struct VirtioMemResp {
|
struct VirtioMemResp {
|
||||||
resp_type: u16,
|
resp_type: u16,
|
||||||
padding: [u16; 3],
|
padding: [u16; 3],
|
||||||
|
state: u16,
|
||||||
state: VirtioMemRespState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe because it only has data and has no implicit padding.
|
// Safe because it only has data and has no implicit padding.
|
||||||
unsafe impl ByteValued for VirtioMemResp {}
|
unsafe impl ByteValued for VirtioMemResp {}
|
||||||
|
|
||||||
// Got from qemu/include/standard-headers/linux/virtio_mem.h
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
struct VirtioMemConfig {
|
struct VirtioMemConfig {
|
||||||
@ -185,35 +174,72 @@ struct VirtioMemConfig {
|
|||||||
// Safe because it only has data and has no implicit padding.
|
// Safe because it only has data and has no implicit padding.
|
||||||
unsafe impl ByteValued for VirtioMemConfig {}
|
unsafe impl ByteValued for VirtioMemConfig {}
|
||||||
|
|
||||||
fn virtio_mem_config_resize(config: &mut VirtioMemConfig, size: u64) -> result::Result<(), Error> {
|
impl VirtioMemConfig {
|
||||||
if config.requested_size == size {
|
fn validate(&self) -> result::Result<(), Error> {
|
||||||
return Err(Error::ResizeInval(format!(
|
if self.addr % self.block_size != 0 {
|
||||||
"Virtio-mem resize {} is same with current config.requested_size",
|
return Err(Error::ValidateError(anyhow!(
|
||||||
size
|
"addr 0x{:x} is not aligned on block_size 0x{:x}",
|
||||||
|
self.addr,
|
||||||
|
self.block_size
|
||||||
)));
|
)));
|
||||||
} else if size > config.region_size {
|
}
|
||||||
let region_size = config.region_size;
|
if self.region_size % self.block_size != 0 {
|
||||||
return Err(Error::ResizeInval(format!(
|
return Err(Error::ValidateError(anyhow!(
|
||||||
"Virtio-mem resize {} is bigger than config.region_size {}",
|
"region_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||||
size, region_size
|
self.region_size,
|
||||||
|
self.block_size
|
||||||
)));
|
)));
|
||||||
} else if size % (config.block_size as u64) != 0 {
|
}
|
||||||
let block_size = config.block_size;
|
if self.usable_region_size % self.block_size != 0 {
|
||||||
return Err(Error::ResizeInval(format!(
|
return Err(Error::ValidateError(anyhow!(
|
||||||
"Virtio-mem resize {} is not aligned with config.block_size {}",
|
"usable_region_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||||
size, block_size
|
self.usable_region_size,
|
||||||
|
self.block_size
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if self.plugged_size % self.block_size != 0 {
|
||||||
|
return Err(Error::ValidateError(anyhow!(
|
||||||
|
"plugged_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||||
|
self.plugged_size,
|
||||||
|
self.block_size
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if self.requested_size % self.block_size != 0 {
|
||||||
|
return Err(Error::ValidateError(anyhow!(
|
||||||
|
"requested_size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||||
|
self.requested_size,
|
||||||
|
self.block_size
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
config.requested_size = size;
|
Ok(())
|
||||||
let tmp_size = cmp::min(
|
}
|
||||||
config.region_size,
|
|
||||||
config.requested_size + VIRTIO_MEM_USABLE_EXTENT,
|
fn resize(&mut self, size: u64) -> result::Result<(), Error> {
|
||||||
);
|
if self.requested_size == size {
|
||||||
config.usable_region_size = cmp::max(config.usable_region_size, tmp_size);
|
return Err(Error::ResizeError(anyhow!(
|
||||||
|
"new size 0x{:x} and requested_size are identical",
|
||||||
|
size
|
||||||
|
)));
|
||||||
|
} else if size > self.region_size {
|
||||||
|
return Err(Error::ResizeError(anyhow!(
|
||||||
|
"new size 0x{:x} is bigger than region_size 0x{:x}",
|
||||||
|
size,
|
||||||
|
self.region_size
|
||||||
|
)));
|
||||||
|
} else if size % (self.block_size as u64) != 0 {
|
||||||
|
return Err(Error::ResizeError(anyhow!(
|
||||||
|
"new size 0x{:x} is not aligned on block_size 0x{:x}",
|
||||||
|
size,
|
||||||
|
self.block_size
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.requested_size = size;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Request {
|
struct Request {
|
||||||
req: VirtioMemReq,
|
req: VirtioMemReq,
|
||||||
@ -510,7 +536,7 @@ impl MemEpollHandler {
|
|||||||
) -> u32 {
|
) -> u32 {
|
||||||
let resp = VirtioMemResp {
|
let resp = VirtioMemResp {
|
||||||
resp_type,
|
resp_type,
|
||||||
state: VirtioMemRespState { state },
|
state,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
match mem.write_obj(resp, status_addr) {
|
match mem.write_obj(resp, status_addr) {
|
||||||
@ -601,10 +627,6 @@ impl MemEpollHandler {
|
|||||||
);
|
);
|
||||||
if resp_type == VIRTIO_MEM_RESP_ACK {
|
if resp_type == VIRTIO_MEM_RESP_ACK {
|
||||||
config.plugged_size = 0;
|
config.plugged_size = 0;
|
||||||
config.usable_region_size = cmp::min(
|
|
||||||
config.region_size,
|
|
||||||
config.requested_size + VIRTIO_MEM_USABLE_EXTENT,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
MemEpollHandler::virtio_mem_send_response(
|
MemEpollHandler::virtio_mem_send_response(
|
||||||
&mem,
|
&mem,
|
||||||
@ -670,7 +692,7 @@ impl EpollHelperHandler for MemEpollHandler {
|
|||||||
let size = self.resize.size();
|
let size = self.resize.size();
|
||||||
let mut config = self.config.lock().unwrap();
|
let mut config = self.config.lock().unwrap();
|
||||||
let mut signal_error = false;
|
let mut signal_error = false;
|
||||||
let mut r = virtio_mem_config_resize(&mut config, size);
|
let mut r = config.resize(size);
|
||||||
r = match r {
|
r = match r {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
_ => match self.signal(&VirtioInterruptType::Config) {
|
_ => match self.signal(&VirtioInterruptType::Config) {
|
||||||
@ -745,20 +767,24 @@ impl Mem {
|
|||||||
|
|
||||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||||
|
|
||||||
let mut config = VirtioMemConfig::default();
|
let mut config = VirtioMemConfig {
|
||||||
config.block_size = VIRTIO_MEM_DEFAULT_BLOCK_SIZE;
|
block_size: VIRTIO_MEM_DEFAULT_BLOCK_SIZE,
|
||||||
config.addr = region.start_addr().raw_value();
|
addr: region.start_addr().raw_value(),
|
||||||
config.region_size = region.len();
|
region_size: region.len(),
|
||||||
config.usable_region_size = cmp::min(
|
usable_region_size: region.len(),
|
||||||
config.region_size,
|
plugged_size: 0,
|
||||||
config.requested_size + VIRTIO_MEM_USABLE_EXTENT,
|
requested_size: 0,
|
||||||
);
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
if initial_size != 0 {
|
if initial_size != 0 {
|
||||||
virtio_mem_config_resize(&mut config, initial_size).map_err(|e| {
|
config.resize(initial_size).map_err(|e| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("Virtio-mem resize {} got {:?}", initial_size, e),
|
format!(
|
||||||
|
"Failed to resize virtio-mem configuration to {}: {:?}",
|
||||||
|
initial_size, e
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -768,11 +794,18 @@ impl Mem {
|
|||||||
config.node_id = node_id;
|
config.node_id = node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
let host_fd = if let Some(f_offset) = region.file_offset() {
|
// Make sure the virtio-mem configuration complies with the
|
||||||
Some(f_offset.file().as_raw_fd())
|
// specification.
|
||||||
} else {
|
config.validate().map_err(|e| {
|
||||||
None
|
io::Error::new(
|
||||||
};
|
io::ErrorKind::Other,
|
||||||
|
format!("Invalid virtio-mem configuration: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let host_fd = region
|
||||||
|
.file_offset()
|
||||||
|
.map(|f_offset| f_offset.file().as_raw_fd());
|
||||||
|
|
||||||
Ok(Mem {
|
Ok(Mem {
|
||||||
common: VirtioCommon {
|
common: VirtioCommon {
|
||||||
|
Loading…
Reference in New Issue
Block a user