resources: Add address allocation helper

Add the AddressAllocator module that will be used by both architectures
to manage distributing address ranges to devices. This will make the
addition of PCI devices easier as now both MMIO and PCI will need to
share address space. Add this to a new resources crate.

Change-Id: I6a971dd795f2118bd6cfec7dc34a65b0d4a32f9b
Signed-off-by: Dylan Reid <dgreid@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1072570
Reviewed-by: Sonny Rao <sonnyrao@chromium.org>
This commit is contained in:
Dylan Reid 2018-05-23 12:59:11 -07:00 committed by chrome-bot
parent d678784ae1
commit dea77cef92
5 changed files with 136 additions and 0 deletions

8
Cargo.lock generated
View file

@ -71,6 +71,7 @@ dependencies = [
"qcow 0.1.0",
"qcow_utils 0.1.0",
"rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"resources 0.1.0",
"sys_util 0.1.0",
"vhost 0.1.0",
"vm_control 0.1.0",
@ -298,6 +299,13 @@ dependencies = [
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "resources"
version = "0.1.0"
dependencies = [
"data_model 0.1.0",
]
[[package]]
name = "syn"
version = "0.12.15"

View file

@ -36,6 +36,7 @@ plugin_proto = { path = "plugin_proto", optional = true }
crosvm_plugin = { path = "crosvm_plugin", optional = true }
protobuf = { version = "=1.4.3", optional = true }
qcow_utils = { path = "qcow_utils" }
resources = { path = "resources" }
p9 = { path = "p9" }
[target.'cfg(target_arch = "x86_64")'.dependencies]

7
resources/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "resources"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
[dependencies]
data_model = { path = "../data_model" }

View file

@ -0,0 +1,111 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Manages allocating address ranges.
/// Use `AddressAllocator` whenever an address range needs to be allocated to different users.
///
/// # Examples
///
/// ```
/// # use resources::AddressAllocator;
/// AddressAllocator::new(0x1000, 0x10000, Some(0x100)).map(|mut pool| {
/// assert_eq!(pool.allocate(0x110), Some(0x1000));
/// assert_eq!(pool.allocate(0x100), Some(0x1200));
/// });
/// ```
#[derive(Debug, Eq, PartialEq)]
pub struct AddressAllocator {
pool_base: u64,
pool_end: u64,
alignment: u64,
next_addr: u64,
}
impl AddressAllocator {
/// Creates a new `AddressAllocator` for managing a range of addresses.
/// Can return `None` if `pool_base` + `pool_size` overflows a u64 or if alignment isn't a power
/// of two.
///
/// * `pool_base` - The starting address of the range to manage.
/// * `pool_size` - The size of the address range in bytes.
/// * `align_size` - The minimum size of an address region to align to, defaults to four.
pub fn new(pool_base: u64, pool_size: u64, align_size: Option<u64>) -> Option<Self> {
if pool_size == 0 {
return None;
}
let pool_end = pool_base.checked_add(pool_size - 1)?;
let alignment = align_size.unwrap_or(4);
if !alignment.is_power_of_two() || alignment == 0 {
return None;
}
Some(AddressAllocator {
pool_base,
pool_end,
alignment,
next_addr: pool_base,
})
}
/// Allocates a range of addresses from the managed region. Returns `Some(allocated_address)`
/// when successful, or `None` if an area of `size` can't be allocated.
pub fn allocate(&mut self, size: u64) -> Option<u64> {
if size == 0 {
return None;
}
let align_adjust = if self.next_addr % self.alignment != 0 {
self.alignment - (self.next_addr % self.alignment)
} else {
0
};
let addr = self.next_addr.checked_add(align_adjust)?;
let end_addr = addr.checked_add(size - 1)?;
if end_addr > self.pool_end {
return None;
}
// TODO(dgreid): Use a smarter allocation strategy. The current strategy is just
// bumping this pointer, meaning it will eventually exhaust available addresses.
self.next_addr = end_addr.saturating_add(1);
Some(addr)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_fails_overflow() {
assert_eq!(AddressAllocator::new(u64::max_value(), 0x100, None), None);
}
#[test]
fn new_fails_size_zero() {
assert_eq!(AddressAllocator::new(0x1000, 0, None), None);
}
#[test]
fn new_fails_alignment_zero() {
assert_eq!(AddressAllocator::new(0x1000, 0x10000, Some(0)), None);
}
#[test]
fn new_fails_alignment_non_power_of_two() {
assert_eq!(AddressAllocator::new(0x1000, 0x10000, Some(200)), None);
}
#[test]
fn allocate_fails_not_enough_space() {
let mut pool = AddressAllocator::new(0x1000, 0x1000, Some(0x100)).unwrap();
assert_eq!(pool.allocate(0x800), Some(0x1000));
assert_eq!(pool.allocate(0x900), None);
assert_eq!(pool.allocate(0x800), Some(0x1800));
}
#[test]
fn allocate_alignment() {
let mut pool = AddressAllocator::new(0x1000, 0x10000, Some(0x100)).unwrap();
assert_eq!(pool.allocate(0x110), Some(0x1000));
assert_eq!(pool.allocate(0x100), Some(0x1200));
}
}

9
resources/src/lib.rs Normal file
View file

@ -0,0 +1,9 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Manages system resources that can be allocated to VMs and their devices.
mod address_allocator;
pub use address_allocator::AddressAllocator;