19
19
20
20
//! Due to internal-fragmentation in the buddy frame allocator, we cannot allocate large
21
21
//! 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.
23
24
//!
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`].
26
27
27
- use alloc:: collections:: LinkedList ;
28
+ use alloc:: boxed:: Box ;
29
+ use intrusive_collections:: * ;
28
30
use spin:: Once ;
29
31
30
32
use crate :: utils:: sync:: { Mutex , MutexGuard } ;
31
33
32
- use super :: { paging:: * , AddressSpace } ;
34
+ use super :: paging:: * ;
35
+ use super :: AddressSpace ;
33
36
34
37
pub ( super ) const VMALLOC_MAX_SIZE : usize = 128 * 1024 * 1024 ; // 128 GiB
35
38
pub ( super ) const VMALLOC_START : VirtAddr = VirtAddr :: new ( 0xfffff80000000000 ) ;
@@ -38,29 +41,59 @@ pub(super) const VMALLOC_END: VirtAddr =
38
41
39
42
static VMALLOC : Once < Mutex < Vmalloc > > = Once :: new ( ) ;
40
43
41
- struct VmallocArea {
44
+ struct VmallocAreaProtected {
42
45
addr : VirtAddr ,
43
46
size : usize ,
44
47
}
45
48
46
- impl VmallocArea {
49
+ impl VmallocAreaProtected {
47
50
fn new ( addr : VirtAddr , size : usize ) -> Self {
48
51
Self { addr, size }
49
52
}
50
53
}
51
54
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
+
52
84
pub ( super ) struct Vmalloc {
53
- free_list : LinkedList < VmallocArea > ,
85
+ free_list : RBTree < VmallocAreaAdaptor > ,
54
86
}
55
87
56
88
impl Vmalloc {
57
89
fn new ( ) -> Self {
58
90
let mut this = Self {
59
- free_list : LinkedList :: new ( ) ,
91
+ free_list : RBTree :: new ( Default :: default ( ) ) ,
60
92
} ;
61
93
62
94
this. free_list
63
- . push_front ( VmallocArea :: new ( VMALLOC_START , VMALLOC_MAX_SIZE ) ) ;
95
+ . insert ( box VmallocArea :: new ( VMALLOC_START , VMALLOC_MAX_SIZE ) ) ;
96
+
64
97
this
65
98
}
66
99
@@ -72,9 +105,10 @@ impl Vmalloc {
72
105
73
106
let area = self
74
107
. free_list
75
- . iter_mut ( )
76
- . find ( |area| area. size >= size_bytes) ?;
108
+ . iter ( )
109
+ . find ( |area| area. protected . lock ( ) . size >= size_bytes) ?;
77
110
111
+ let mut area = area. protected . lock ( ) ;
78
112
let address = area. addr . clone ( ) ;
79
113
80
114
if area. size > size_bytes {
@@ -126,15 +160,17 @@ impl Vmalloc {
126
160
// check if this block can be merged into another block.
127
161
let merge = self
128
162
. free_list
129
- . iter_mut ( )
130
- . find ( |area| addr + size == area. addr ) ;
163
+ . iter ( )
164
+ . find ( |area| addr + size == area. protected . lock ( ) . addr ) ;
131
165
132
166
if let Some ( merge) = merge {
167
+ let mut merge = merge. protected . lock ( ) ;
168
+
133
169
merge. addr = addr;
134
170
merge. size += size;
135
171
} else {
136
172
// 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) ) ;
138
174
}
139
175
140
176
let mut address_space = AddressSpace :: this ( ) ;
0 commit comments