Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# vm-allocator

`vm-allocator` is a crate designed to to provide allocation and release strategies
`vm-allocator` is a crate designed to provide allocation and release strategies
that are needed by the VMM during the lifetime of a virtual machine. Possible
resource types that a VMM could allocate using vm-allocator are MMIO addresses,
PIO addresses, GSI numbers, device ids.
PIO addresses, GSI numbers, device IDs.

We have decided to have two allocator implementations, one for resources that can
be abstracted to an integer and another allocator for addresses. We chose
to have a separate allocator for addresses to add more semantic meaning to this
resources (i.e. it needs informations like alignment which for resources like
resource (i.e. it needs information like alignment which for resources like
interrupt numbers are not needed). The main components are:

- `IDAllocator` - which should be used for all resources that can be reduced to
Expand All @@ -27,9 +27,9 @@ characteristics of such a resource are represented by the `IdAllocator` struct.

The struct that defines the IdAllocator contains the ends of the interval that is
managed, a field that points at the next available ID and a BTreeSet that is used
to store the released IDs. We choosed to use a BTreeSet because the average
complexity for deletion and insertion is O(logN) compared to Vec for example,
another benefit is that the entries are sorted so we will always use the first
to store the released IDs. We choose to use a BTreeSet because the average
complexity for deletion and insertion is O(log N) compared to Vec for example,
another benefit is that the entries are sorted so, we will always use the first
available ID.

#### Allocation policy
Expand Down
Binary file added images/after_free.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/first_node.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/interval_tree_allocation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions src/allocation_engine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Design

## Allocation engine

This implementation uses an interval tree that is specialized for allocation of
memory-mapped I/O and port I/O address space. The fields of the structures
defined will have semantic meaning for this context (e.g. node state to indicate
if a node in the tree is assigned or not to a device).

We offer three options for placing a memory slot in the managed address space:

1. `LastMatch` -> When using this allocation policy the allocator will try to
insert the range described by the constraint at the first available position
starting from the end of the managed address space.
2. `FirstMatch` -> When using this allocation policy the allocator will try to
insert the range described by the constraint at the first available position
starting from the beginning of the managed address space.
3. `ExactMatch(u64)` -> When using this allocation policy the allocator will try
to insert the range at the exact position described by the constraint, otherwise
it will return an error.

Struct `Constraint` is used to describe the overall information of the resource
needed to be allocated. This structure is also used by IntervalTree to know where
and how to allocate the resource. The fields that are mandatory for allocating
a new memory slot are size of the slot, alignment of the slot and allocation policy.
Optionally the user can specify a range where the allocator will place the allocated
memory slot.

## Interval tree

An interval tree is a tree data structure used for storing information about intervals.
Specifically, it allows one to efficiently identify intervals that are overlapping
with a given point, or another interval. We considered that this characteristic
makes this data structure appropriate to be used as an allocation engine for
memory slots inside an address space. The time complexity of an interval tree,
namely O(log ⁡n+m) for queries, O(log n) for creation and O(log n) for insertion
and deletion of nodes. The key of each node of the tree is represented using a
struct named `Range` that contains the bounds of the address space. Each node in
the tree can have two states, either `Free` or `Allocated`. Beside the information
presented above the representation of a node also contains references two the
children node of the current node.

## Usage

To use the `IntervalTree` implementation as an address allocator one should first
create an interval tree object and give an address space as a root node. After,
the user should create a constraint with the size for the resource. Optionally
the constraint could also contain the maximum, minimum and alignment for the
constraint.

## State transition

At the beginning, the interval tree will contain just one node that will represent
the whole address space, the state of this node will be `NodeState::Free`.

![InteralTree creation example](/images/first_node.png)

When we allocate a memory slot, one of the nodes that have the state `NodeState::Free`
will be split accordingly. A new node that has as the key a range representing the
allocated memory slot will be inserted in the tree.

![Node Allocation example](/images/interval_tree_allocation.png)

When one of the allocated nodes is freed its state will be changed from `NodeState::Allocated`
to `NodeState::Free` if there are two adjacent nodes that are not allocated then
they will be merged in a single node.

![Node Freeing example](/images/after_free.png)

## License

**!!!NOTICE**: The BSD-3-Clause license is not included in this template.
The license needs to be manually added because the text of the license file
also includes the copyright. The copyright can be different for different
crates. If the crate contains code from CrosVM, the crate must add the
CrosVM copyright which can be found
[here](https://chromium.googlesource.com/chromiumos/platform/crosvm/+/master/LICENSE).
For crates developed from scratch, the copyright is different and depends on
the contributors.
76 changes: 76 additions & 0 deletions src/allocation_engine/interval_tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2

/// Policy for resource allocation.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum AllocPolicy {
/// Allocate the first matched entry.
FirstMatch,
/// Allocate first matched entry from the end of the range.
LastMatch,
/// Allocate a memory slot starting with the specified address
/// if it is available.
ExactMatch(u64),
}

impl Default for AllocPolicy {
fn default() -> Self {
AllocPolicy::FirstMatch
}
}

/// Struct to describe resource allocation constraints.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Constraint {
/// Size to allocate.
pub size: u64,
/// Range where the allocated resource will be placed.
pub desired_range: Range,
/// Alignment for the allocated resource.
pub align: u64,
/// Resource allocation policy.
pub policy: AllocPolicy,
}

/// A closed interval range [min, max] used to describe a
/// memory slot that will be assigned to a device by the VMM.
/// This structure represents the key of the Node object in
/// the interval tree implementation.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Hash, Ord)]
pub struct Range {
pub min: u64,
pub max: u64,
}

/// Node state for interval tree nodes.
///
/// Valid state transition:
/// - None -> Free: IntervalTree::insert()
/// - Free -> Allocated: IntervalTree::allocate()
/// - Allocated -> Free: IntervalTree::free()
/// - * -> None: IntervalTree::delete()
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum NodeState {
/// Node is free.
Free,
/// Node is allocated.
Allocated,
}

/// Internal tree node to implement interval tree.
#[derive(Debug, PartialEq)]
struct InnerNode {
/// Interval handled by this node.
key: Range,
/// NodeState, can be Free or Allocated.
node_state: NodeState,
/// Optional left child of current node.
left: Option<Box<InnerNode>>,
/// Optional right child of current node.
right: Option<Box<InnerNode>>,
/// Cached height of the node.
height: u64,
/// Cached maximum valued covered by this node.
max_key: u64,
}
7 changes: 7 additions & 0 deletions src/allocation_engine/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2

mod interval_tree;

pub use interval_tree::{AllocPolicy, NodeState, Range};