Skip to content

Commit 0c9432d

Browse files
vmalloc: use a red-black tree instead of linked-list
Signed-off-by: Andy-Python-Programmer <[email protected]>
1 parent 3124a81 commit 0c9432d

File tree

1 file changed

+51
-15
lines changed

1 file changed

+51
-15
lines changed

src/aero_kernel/src/mem/vmalloc.rs

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@
1919

2020
//! Due to internal-fragmentation in the buddy frame allocator, we cannot allocate large
2121
//! amount of contiguous physical memory. We instead use [`vmalloc`] to allocate virtually
22-
//! contiguous memory.
22+
//! contiguous memory. The allocator uses a red-black tree to keep track of the free memory
23+
//! so we can allocate and free memory efficiently.
2324
//!
24-
//! An area is reserved for [`vmalloc`] in the kernel address space, starting at [`VMALLOC_VIRT_START`] and
25-
//! ending at [`VMALLOC_VIRT_END`].
25+
//! An area is reserved for [`vmalloc`] in the kernel address space, starting
26+
//! at [`VMALLOC_VIRT_START`] and ending at [`VMALLOC_VIRT_END`].
2627
27-
use alloc::collections::LinkedList;
28+
use alloc::boxed::Box;
29+
use intrusive_collections::*;
2830
use spin::Once;
2931

3032
use crate::utils::sync::{Mutex, MutexGuard};
3133

32-
use super::{paging::*, AddressSpace};
34+
use super::paging::*;
35+
use super::AddressSpace;
3336

3437
pub(super) const VMALLOC_MAX_SIZE: usize = 128 * 1024 * 1024; // 128 GiB
3538
pub(super) const VMALLOC_START: VirtAddr = VirtAddr::new(0xfffff80000000000);
@@ -38,29 +41,59 @@ pub(super) const VMALLOC_END: VirtAddr =
3841

3942
static VMALLOC: Once<Mutex<Vmalloc>> = Once::new();
4043

41-
struct VmallocArea {
44+
struct VmallocAreaProtected {
4245
addr: VirtAddr,
4346
size: usize,
4447
}
4548

46-
impl VmallocArea {
49+
impl VmallocAreaProtected {
4750
fn new(addr: VirtAddr, size: usize) -> Self {
4851
Self { addr, size }
4952
}
5053
}
5154

55+
struct VmallocArea {
56+
// NOTE: Since there are equal amount of read and write operations we are going to
57+
// protect the data using a [`Mutex`].
58+
protected: Mutex<VmallocAreaProtected>,
59+
link: RBTreeLink,
60+
}
61+
62+
impl VmallocArea {
63+
fn new(addr: VirtAddr, size: usize) -> Self {
64+
Self {
65+
protected: Mutex::new(VmallocAreaProtected::new(addr, size)),
66+
link: Default::default(),
67+
}
68+
}
69+
}
70+
71+
impl<'a> KeyAdapter<'a> for VmallocAreaAdaptor {
72+
type Key = usize;
73+
74+
fn get_key(&self, this: &'a VmallocArea) -> Self::Key {
75+
// NOTE: We use the size of the vmalloc area as the key for the red-black tree
76+
// so when we are allocating or deallocating memory we can find a large enough, free
77+
// vmalloc area efficiently.
78+
this.protected.lock().size
79+
}
80+
}
81+
82+
intrusive_collections::intrusive_adapter!(VmallocAreaAdaptor = Box<VmallocArea>: VmallocArea { link: RBTreeLink });
83+
5284
pub(super) struct Vmalloc {
53-
free_list: LinkedList<VmallocArea>,
85+
free_list: RBTree<VmallocAreaAdaptor>,
5486
}
5587

5688
impl Vmalloc {
5789
fn new() -> Self {
5890
let mut this = Self {
59-
free_list: LinkedList::new(),
91+
free_list: RBTree::new(Default::default()),
6092
};
6193

6294
this.free_list
63-
.push_front(VmallocArea::new(VMALLOC_START, VMALLOC_MAX_SIZE));
95+
.insert(box VmallocArea::new(VMALLOC_START, VMALLOC_MAX_SIZE));
96+
6497
this
6598
}
6699

@@ -72,9 +105,10 @@ impl Vmalloc {
72105

73106
let area = self
74107
.free_list
75-
.iter_mut()
76-
.find(|area| area.size >= size_bytes)?;
108+
.iter()
109+
.find(|area| area.protected.lock().size >= size_bytes)?;
77110

111+
let mut area = area.protected.lock();
78112
let address = area.addr.clone();
79113

80114
if area.size > size_bytes {
@@ -126,15 +160,17 @@ impl Vmalloc {
126160
// check if this block can be merged into another block.
127161
let merge = self
128162
.free_list
129-
.iter_mut()
130-
.find(|area| addr + size == area.addr);
163+
.iter()
164+
.find(|area| addr + size == area.protected.lock().addr);
131165

132166
if let Some(merge) = merge {
167+
let mut merge = merge.protected.lock();
168+
133169
merge.addr = addr;
134170
merge.size += size;
135171
} else {
136172
// the block cannot be merged, so add it to the free list.
137-
self.free_list.push_back(VmallocArea::new(addr, size));
173+
self.free_list.insert(box VmallocArea::new(addr, size));
138174
}
139175

140176
let mut address_space = AddressSpace::this();

0 commit comments

Comments
 (0)