use std::sync::Arc;
use arc_swap::ArcSwap;
use vm_memory::{GuestAddress, GuestMemoryMmap};
use crate::{AddressSpaceError, AddressSpaceLayout, AddressSpaceRegion, AddressSpaceRegionType};
#[derive(Clone)]
pub struct AddressSpaceBase {
regions: Vec<Arc<AddressSpaceRegion>>,
layout: AddressSpaceLayout,
}
impl AddressSpaceBase {
pub fn from_regions(
mut regions: Vec<Arc<AddressSpaceRegion>>,
layout: AddressSpaceLayout,
) -> Self {
regions.sort_unstable_by_key(|v| v.base);
for region in regions.iter() {
if !layout.is_region_valid(region) {
panic!(
"Invalid region {:?} for address space layout {:?}",
region, layout
);
}
}
for idx in 1..regions.len() {
if regions[idx].intersect_with(®ions[idx - 1]) {
panic!("address space regions intersect with each other");
}
}
AddressSpaceBase { regions, layout }
}
pub fn insert_region(
&mut self,
region: Arc<AddressSpaceRegion>,
) -> Result<(), AddressSpaceError> {
if !self.layout.is_region_valid(®ion) {
return Err(AddressSpaceError::InvalidAddressRange(
region.start_addr().0,
region.len(),
));
}
for idx in 0..self.regions.len() {
if self.regions[idx].intersect_with(®ion) {
return Err(AddressSpaceError::InvalidAddressRange(
region.start_addr().0,
region.len(),
));
}
}
self.regions.push(region);
Ok(())
}
pub fn walk_regions<F>(&self, mut cb: F) -> Result<(), AddressSpaceError>
where
F: FnMut(&Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError>,
{
for reg in self.regions.iter() {
cb(reg)?;
}
Ok(())
}
pub fn layout(&self) -> AddressSpaceLayout {
self.layout.clone()
}
pub fn last_addr(&self) -> GuestAddress {
let mut last_addr = GuestAddress(self.layout.mem_start);
for reg in self.regions.iter() {
if reg.ty != AddressSpaceRegionType::DAXMemory && reg.last_addr() > last_addr {
last_addr = reg.last_addr();
}
}
last_addr
}
pub fn is_dax_region(&self, guest_addr: GuestAddress) -> bool {
for reg in self.regions.iter() {
if reg.region_type() == AddressSpaceRegionType::DAXMemory
&& reg.start_addr() <= guest_addr
&& reg.start_addr().0 + reg.len() > guest_addr.0
{
return true;
}
}
false
}
pub fn prot_flags(&self, guest_addr: GuestAddress) -> Result<i32, AddressSpaceError> {
for reg in self.regions.iter() {
if reg.start_addr() <= guest_addr && reg.start_addr().0 + reg.len() > guest_addr.0 {
return Ok(reg.prot_flags());
}
}
Err(AddressSpaceError::InvalidRegionType)
}
pub fn numa_node_id(&self, gpa: u64) -> Option<u32> {
for reg in self.regions.iter() {
if gpa >= reg.base.0 && gpa < (reg.base.0 + reg.size) {
return reg.host_numa_node_id;
}
}
None
}
}
#[derive(Clone)]
pub struct AddressSpace {
state: Arc<ArcSwap<AddressSpaceBase>>,
}
impl AddressSpace {
pub fn convert_into_vm_as(
gm: GuestMemoryMmap,
) -> vm_memory::atomic::GuestMemoryAtomic<GuestMemoryMmap> {
vm_memory::atomic::GuestMemoryAtomic::from(Arc::new(gm))
}
pub fn from_regions(regions: Vec<Arc<AddressSpaceRegion>>, layout: AddressSpaceLayout) -> Self {
let base = AddressSpaceBase::from_regions(regions, layout);
AddressSpace {
state: Arc::new(ArcSwap::new(Arc::new(base))),
}
}
pub fn insert_region(
&mut self,
region: Arc<AddressSpaceRegion>,
) -> Result<(), AddressSpaceError> {
let curr = self.state.load().regions.clone();
let layout = self.state.load().layout.clone();
let mut base = AddressSpaceBase::from_regions(curr, layout);
base.insert_region(region)?;
let _old = self.state.swap(Arc::new(base));
Ok(())
}
pub fn walk_regions<F>(&self, cb: F) -> Result<(), AddressSpaceError>
where
F: FnMut(&Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError>,
{
self.state.load().walk_regions(cb)
}
pub fn layout(&self) -> AddressSpaceLayout {
self.state.load().layout()
}
pub fn last_addr(&self) -> GuestAddress {
self.state.load().last_addr()
}
pub fn is_dax_region(&self, guest_addr: GuestAddress) -> bool {
self.state.load().is_dax_region(guest_addr)
}
pub fn prot_flags(&self, guest_addr: GuestAddress) -> Result<i32, AddressSpaceError> {
self.state.load().prot_flags(guest_addr)
}
pub fn numa_node_id(&self, gpa: u64) -> Option<u32> {
self.state.load().numa_node_id(gpa)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use vm_memory::GuestUsize;
use vmm_sys_util::tempfile::TempFile;
const GUEST_PHYS_END: u64 = (1 << 46) - 1;
const GUEST_MEM_START: u64 = 0;
const GUEST_MEM_END: u64 = GUEST_PHYS_END >> 1;
const GUEST_DEVICE_START: u64 = GUEST_MEM_END + 1;
#[test]
fn test_address_space_base_from_regions() {
let mut file = TempFile::new().unwrap().into_file();
let sample_buf = &[1, 2, 3, 4, 5];
assert!(file.write_all(sample_buf).is_ok());
file.set_len(0x10000).unwrap();
let reg = Arc::new(
AddressSpaceRegion::create_device_region(GuestAddress(GUEST_DEVICE_START), 0x1000)
.unwrap(),
);
let regions = vec![reg];
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
let address_space = AddressSpaceBase::from_regions(regions, layout.clone());
assert_eq!(address_space.layout(), layout);
}
#[test]
#[should_panic(expected = "Invalid region")]
fn test_address_space_base_from_regions_when_region_invalid() {
let reg = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x1000,
None,
None,
0,
0,
false,
));
let regions = vec![reg];
let layout = AddressSpaceLayout::new(0x2000, 0x200, 0x1800);
let _address_space = AddressSpaceBase::from_regions(regions, layout);
}
#[test]
#[should_panic(expected = "address space regions intersect with each other")]
fn test_address_space_base_from_regions_when_region_intersected() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x200),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let _address_space = AddressSpaceBase::from_regions(regions, layout);
}
#[test]
fn test_address_space_base_insert_region() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1];
let layout = AddressSpaceLayout::new(0x2000, 0x100, 0x1800);
let mut address_space = AddressSpaceBase::from_regions(regions, layout);
address_space.insert_region(reg2).unwrap();
assert!(!address_space.regions[1].intersect_with(&address_space.regions[0]));
let invalid_reg = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x0),
0x100,
None,
None,
0,
0,
false,
));
assert_eq!(
format!(
"{:?}",
address_space.insert_region(invalid_reg).err().unwrap()
),
format!("InvalidAddressRange({:?}, {:?})", 0x0, 0x100)
);
let intersected_reg = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x400),
0x200,
None,
None,
0,
0,
false,
));
assert_eq!(
format!(
"{:?}",
address_space.insert_region(intersected_reg).err().unwrap()
),
format!("InvalidAddressRange({:?}, {:?})", 0x400, 0x200)
);
}
#[test]
fn test_address_space_base_walk_regions() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpaceBase::from_regions(regions, layout);
fn do_not_have_hotplug(region: &Arc<AddressSpaceRegion>) -> Result<(), AddressSpaceError> {
if region.is_hotplug() {
Err(AddressSpaceError::InvalidRegionType) } else {
Ok(())
}
}
assert!(matches!(
address_space.walk_regions(do_not_have_hotplug).unwrap(),
()
));
}
#[test]
fn test_address_space_base_last_addr() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpaceBase::from_regions(regions, layout);
assert_eq!(address_space.last_addr(), GuestAddress(0x500 - 1));
}
#[test]
fn test_address_space_base_is_dax_region() {
let page_size = 4096;
let address_space_region = vec![
Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(page_size),
page_size as GuestUsize,
)),
Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(page_size * 2),
page_size as GuestUsize,
)),
Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DAXMemory,
GuestAddress(GUEST_DEVICE_START),
page_size as GuestUsize,
)),
];
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
let address_space = AddressSpaceBase::from_regions(address_space_region, layout);
assert!(!address_space.is_dax_region(GuestAddress(page_size)));
assert!(!address_space.is_dax_region(GuestAddress(page_size * 2)));
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START)));
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + 1)));
assert!(!address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size)));
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size - 1)));
}
#[test]
fn test_address_space_base_prot_flags() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
Some(0),
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x300,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpaceBase::from_regions(regions, layout);
assert_eq!(address_space.prot_flags(GuestAddress(0x200)).unwrap(), 0);
assert_eq!(
address_space.prot_flags(GuestAddress(0x500)).unwrap(),
libc::PROT_READ | libc::PROT_WRITE
);
assert!(matches!(
address_space.prot_flags(GuestAddress(0x600)),
Err(AddressSpaceError::InvalidRegionType)
));
}
#[test]
fn test_address_space_base_numa_node_id() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
Some(0),
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x300,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpaceBase::from_regions(regions, layout);
assert_eq!(address_space.numa_node_id(0x200).unwrap(), 0);
assert_eq!(address_space.numa_node_id(0x400), None);
assert_eq!(address_space.numa_node_id(0x600), None);
}
#[test]
fn test_address_space_convert_into_vm_as() {
let gmm = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x0), 0x400)]).unwrap();
let _vm = AddressSpace::convert_into_vm_as(gmm);
}
#[test]
fn test_address_space_insert_region() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1];
let layout = AddressSpaceLayout::new(0x2000, 0x100, 0x1800);
let mut address_space = AddressSpace::from_regions(regions, layout);
assert!(matches!(address_space.insert_region(reg2).unwrap(), ()));
let invalid_reg = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x0),
0x100,
None,
None,
0,
0,
false,
));
assert_eq!(
format!(
"{:?}",
address_space.insert_region(invalid_reg).err().unwrap()
),
format!("InvalidAddressRange({:?}, {:?})", 0x0, 0x100)
);
let intersected_reg = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x400),
0x200,
None,
None,
0,
0,
false,
));
assert_eq!(
format!(
"{:?}",
address_space.insert_region(intersected_reg).err().unwrap()
),
format!("InvalidAddressRange({:?}, {:?})", 0x400, 0x200)
);
}
#[test]
fn test_address_space_walk_regions() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpace::from_regions(regions, layout);
fn access_all_hotplug_flag(
region: &Arc<AddressSpaceRegion>,
) -> Result<(), AddressSpaceError> {
region.is_hotplug();
Ok(())
}
assert!(matches!(
address_space.walk_regions(access_all_hotplug_flag).unwrap(),
()
));
}
#[test]
fn test_address_space_layout() {
let reg = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x1000,
None,
None,
0,
0,
false,
));
let regions = vec![reg];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpace::from_regions(regions, layout.clone());
assert_eq!(layout, address_space.layout());
}
#[test]
fn test_address_space_last_addr() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
None,
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x200,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpace::from_regions(regions, layout);
assert_eq!(address_space.last_addr(), GuestAddress(0x500 - 1));
}
#[test]
fn test_address_space_is_dax_region() {
let page_size = 4096;
let address_space_region = vec![
Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(page_size),
page_size as GuestUsize,
)),
Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(page_size * 2),
page_size as GuestUsize,
)),
Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DAXMemory,
GuestAddress(GUEST_DEVICE_START),
page_size as GuestUsize,
)),
];
let layout = AddressSpaceLayout::new(GUEST_PHYS_END, GUEST_MEM_START, GUEST_MEM_END);
let address_space = AddressSpace::from_regions(address_space_region, layout);
assert!(!address_space.is_dax_region(GuestAddress(page_size)));
assert!(!address_space.is_dax_region(GuestAddress(page_size * 2)));
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START)));
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + 1)));
assert!(!address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size)));
assert!(address_space.is_dax_region(GuestAddress(GUEST_DEVICE_START + page_size - 1)));
}
#[test]
fn test_address_space_prot_flags() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
Some(0),
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::new(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x300,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpace::from_regions(regions, layout);
assert_eq!(address_space.prot_flags(GuestAddress(0x200)).unwrap(), 0);
assert_eq!(
address_space.prot_flags(GuestAddress(0x500)).unwrap(),
libc::PROT_READ | libc::PROT_WRITE
);
assert!(matches!(
address_space.prot_flags(GuestAddress(0x600)),
Err(AddressSpaceError::InvalidRegionType)
));
}
#[test]
fn test_address_space_numa_node_id() {
let reg1 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x100),
0x200,
Some(0),
None,
0,
0,
false,
));
let reg2 = Arc::new(AddressSpaceRegion::build(
AddressSpaceRegionType::DefaultMemory,
GuestAddress(0x300),
0x300,
None,
None,
0,
0,
false,
));
let regions = vec![reg1, reg2];
let layout = AddressSpaceLayout::new(0x2000, 0x0, 0x1800);
let address_space = AddressSpace::from_regions(regions, layout);
assert_eq!(address_space.numa_node_id(0x200).unwrap(), 0);
assert_eq!(address_space.numa_node_id(0x400), None);
assert_eq!(address_space.numa_node_id(0x600), None);
}
}