blob: df5a02633042ebe8f228c119f7c7438bba9ee22e [file] [log] [blame]
Imre Kis703482d2023-11-30 15:51:26 +01001// SPDX-FileCopyrightText: Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Region pool
5//!
6//! The region pool component handles regions allocations from memory areas in a generic way.
7
8use core::ops::Range;
9
10use alloc::vec::Vec;
11
12/// Region interface
13///
14/// This trait provides the necessary information about a region to the `RegionPool`.
15pub trait Region: Sized {
16 type Resource;
17
18 /// Get base address
19 fn base(&self) -> usize;
20
21 // Get length
22 fn length(&self) -> usize;
23
24 /// Check if the region is used
25 fn used(&self) -> bool;
26
27 /// Check if an area defined by its base address and length is inside the region
28 fn contains(&self, base: usize, length: usize) -> bool;
29
30 /// Try append the parameter region. Return true on success.
31 fn try_append(&mut self, other: &Self) -> bool;
32
33 /// Split region into multiple regions by the area passed as a parameter. It returns the region
34 /// of the area as the first member of the return tuple and all the new sliced regions as the
35 /// second member of the tuple.
36 /// The function has to handle four cases:
37 /// * The area matches the region exactly -> return one region
38 /// * The area is at the beginning of the region -> return two areas
39 /// * The area is at the end of the region -> return two areas
40 /// * The area is in the middle of the region -> return three areas
41 fn create_split(
42 &self,
43 base: usize,
44 length: usize,
45 resource: Option<Self::Resource>,
46 ) -> (Self, Vec<Self>);
47}
48
49/// Region pool error type
50#[derive(Debug, PartialEq)]
51pub enum RegionPoolError {
52 NoSpace,
53 AlreadyUsed,
54 NotFound,
55}
56
57/// Region pool
58pub struct RegionPool<T: Region> {
59 regions: Vec<T>,
60}
61
62impl<T: Region> RegionPool<T> {
63 /// Create empty region pool
64 pub fn new() -> Self {
65 Self {
66 regions: Vec::new(),
67 }
68 }
69
70 /// Add a region to the pool
71 pub fn add(&mut self, region: T) -> Result<(), RegionPoolError> {
72 if !self
73 .regions
74 .iter()
75 .any(|r| r.contains(region.base(), region.length()))
76 {
77 self.regions.push(region);
78 self.regions.sort_by_key(|a| a.base());
79
80 Ok(())
81 } else {
82 Err(RegionPoolError::AlreadyUsed)
83 }
84 }
85
86 /// Allocate a region from the pool of a given length. It will select an area in which the
87 /// region fits and has the minimal size. Then it splits this area to get the allocated region
88 /// with the exact size.
89 pub fn allocate(&mut self, length: usize, resource: T::Resource) -> Result<T, RegionPoolError> {
90 let region_to_allocate_from = self
91 .regions
92 .iter()
93 .enumerate()
94 .filter(|(_index, region)| region.length() >= length && !region.used())
95 .min_by_key(|(_index_a, region)| region.length());
96
97 if let Some((index, region)) = region_to_allocate_from {
98 let (new_region, split_regions) =
99 region.create_split(region.base(), length, Some(resource));
100 self.replace(index, split_regions);
101 Ok(new_region)
102 } else {
103 Err(RegionPoolError::NoSpace)
104 }
105 }
106
107 /// Acquire a region with the given base address and length.
108 pub fn acquire(
109 &mut self,
110 base: usize,
111 length: usize,
112 resource: T::Resource,
113 ) -> Result<T, RegionPoolError> {
114 let region_to_acquire_from = self
115 .regions
116 .iter()
117 .enumerate()
118 .find(|(_, region)| region.contains(base, length) && !region.used());
119
120 if let Some((index, region)) = region_to_acquire_from {
121 let (new_region, split_regions) = region.create_split(base, length, Some(resource));
122 self.replace(index, split_regions);
123 Ok(new_region)
124 } else {
125 Err(RegionPoolError::AlreadyUsed)
126 }
127 }
128
129 /// Release region
130 pub fn release(&mut self, region_to_release: T) -> Result<(), RegionPoolError> {
131 assert!(region_to_release.used());
132
133 let region_to_release_from = self.regions.iter().enumerate().find(|(_, r)| {
134 r.contains(region_to_release.base(), region_to_release.length()) && r.used()
135 });
136
137 if let Some((index, region)) = region_to_release_from {
138 self.replace(
139 index,
140 region
141 .create_split(region_to_release.base(), region_to_release.length(), None)
142 .1,
143 );
144 self.regions.dedup_by(|a, b| b.try_append(a));
145
146 Ok(())
147 } else {
148 Err(RegionPoolError::NotFound)
149 }
150 }
151
152 /// Find the region which contains the given area
153 pub fn find_containing_region(
154 &self,
155 base: usize,
156 length: usize,
157 ) -> Result<&T, RegionPoolError> {
158 let region_index = match self.regions.binary_search_by(|r| r.base().cmp(&base)) {
159 Ok(exact_index) => Ok(exact_index),
160 Err(insert_index) => {
161 if insert_index > 0 {
162 Ok(insert_index - 1)
163 } else {
164 Err(RegionPoolError::NotFound)
165 }
166 }
167 }?;
168
169 if self.regions[region_index].contains(base, length) {
170 Ok(&self.regions[region_index])
171 } else {
172 Err(RegionPoolError::NotFound)
173 }
174 }
175
176 fn replace(&mut self, index: usize, replacements: Vec<T>) {
177 self.regions.splice(
178 Range {
179 start: index,
180 end: index + 1,
181 },
182 replacements,
183 );
184 }
185}
186
187#[cfg(test)]
Imre Kis42935a22024-10-17 11:30:16 +0200188mod tests {
189 use super::*;
Imre Kis703482d2023-11-30 15:51:26 +0100190
Imre Kis42935a22024-10-17 11:30:16 +0200191 #[derive(Debug, PartialEq, Eq)]
192 struct RegionExample {
Imre Kis703482d2023-11-30 15:51:26 +0100193 base: usize,
194 length: usize,
Imre Kis42935a22024-10-17 11:30:16 +0200195 used: bool,
Imre Kis703482d2023-11-30 15:51:26 +0100196 }
Imre Kis703482d2023-11-30 15:51:26 +0100197
Imre Kis42935a22024-10-17 11:30:16 +0200198 impl RegionExample {
199 fn new(base: usize, length: usize, used: bool) -> Self {
200 Self { base, length, used }
201 }
202 }
Imre Kis703482d2023-11-30 15:51:26 +0100203
Imre Kis42935a22024-10-17 11:30:16 +0200204 impl Region for RegionExample {
205 type Resource = ();
Imre Kis703482d2023-11-30 15:51:26 +0100206
Imre Kis42935a22024-10-17 11:30:16 +0200207 fn base(&self) -> usize {
208 self.base
209 }
Imre Kis703482d2023-11-30 15:51:26 +0100210
Imre Kis42935a22024-10-17 11:30:16 +0200211 fn length(&self) -> usize {
212 self.length
213 }
Imre Kis703482d2023-11-30 15:51:26 +0100214
Imre Kis42935a22024-10-17 11:30:16 +0200215 fn used(&self) -> bool {
216 self.used
217 }
Imre Kis703482d2023-11-30 15:51:26 +0100218
Imre Kis42935a22024-10-17 11:30:16 +0200219 fn contains(&self, base: usize, length: usize) -> bool {
220 self.base <= base && base + length <= self.base + self.length
221 }
Imre Kis703482d2023-11-30 15:51:26 +0100222
Imre Kis42935a22024-10-17 11:30:16 +0200223 fn try_append(&mut self, other: &Self) -> bool {
224 if self.used == other.used && self.base + self.length == other.base {
225 self.length += other.length;
226 true
227 } else {
228 false
229 }
230 }
Imre Kis703482d2023-11-30 15:51:26 +0100231
Imre Kis42935a22024-10-17 11:30:16 +0200232 fn create_split(
233 &self,
234 base: usize,
235 length: usize,
236 resource: Option<Self::Resource>,
237 ) -> (Self, Vec<Self>) {
238 let mut res = Vec::new();
239 if self.base != base {
240 res.push(RegionExample::new(self.base, base - self.base, self.used));
241 }
242 res.push(RegionExample::new(base, length, resource.is_some()));
243 if self.base + self.length != base + length {
244 res.push(RegionExample::new(
245 base + length,
246 (self.base + self.length) - (base + length),
247 self.used,
248 ));
249 }
Imre Kis703482d2023-11-30 15:51:26 +0100250
Imre Kis42935a22024-10-17 11:30:16 +0200251 (RegionExample::new(base, length, resource.is_some()), res)
252 }
253 }
Imre Kis703482d2023-11-30 15:51:26 +0100254
Imre Kis42935a22024-10-17 11:30:16 +0200255 #[test]
256 fn test_region_contains() {
257 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
Imre Kis703482d2023-11-30 15:51:26 +0100258
Imre Kis42935a22024-10-17 11:30:16 +0200259 assert!(region.contains(0x4000_0000, 0x1000_0000));
260 assert!(!region.contains(0x4000_0000 - 1, 0x1000_0000 + 1));
261 assert!(!region.contains(0x4000_0000, 0x1000_0000 + 1));
262 assert!(region.contains(0x4000_0000 + 1, 0x1000_0000 - 1));
263 }
Imre Kis703482d2023-11-30 15:51:26 +0100264
Imre Kis42935a22024-10-17 11:30:16 +0200265 #[test]
266 fn test_region_try_append() {
267 // Normal append
268 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
269 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, false);
Imre Kis703482d2023-11-30 15:51:26 +0100270
Imre Kis42935a22024-10-17 11:30:16 +0200271 assert!(region.try_append(&appending));
272 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_1000, false), region);
Imre Kis703482d2023-11-30 15:51:26 +0100273
Imre Kis42935a22024-10-17 11:30:16 +0200274 // Different used flags
275 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
276 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, true);
Imre Kis703482d2023-11-30 15:51:26 +0100277
Imre Kis42935a22024-10-17 11:30:16 +0200278 assert!(!region.try_append(&appending));
279 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
280
281 // Not continious
282 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
283 let appending = RegionExample::new(0x5000_1000, 0x0000_1000, false);
284
285 assert!(!region.try_append(&appending));
286 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
287 }
288
289 #[test]
290 fn test_region_create_split() {
291 // Cut first part
292 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
293
294 let res = region.create_split(0x4000_0000, 0x0000_1000, Some(())).1;
295 assert_eq!(RegionExample::new(0x4000_0000, 0x0000_1000, true), res[0]);
296 assert_eq!(RegionExample::new(0x4000_1000, 0x0fff_f000, false), res[1]);
297
298 // Cut last part
299 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
300
301 let res = region.create_split(0x4fff_f000, 0x0000_1000, Some(())).1;
302 assert_eq!(RegionExample::new(0x4000_0000, 0x0fff_f000, false), res[0]);
303 assert_eq!(RegionExample::new(0x4fff_f000, 0x0000_1000, true), res[1]);
304
305 // Cut middle part
306 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
307
308 let res = region.create_split(0x4020_0000, 0x0000_1000, Some(())).1;
309 assert_eq!(RegionExample::new(0x4000_0000, 0x0020_0000, false), res[0]);
310 assert_eq!(RegionExample::new(0x4020_0000, 0x0000_1000, true), res[1]);
311 assert_eq!(RegionExample::new(0x4020_1000, 0x0fdf_f000, false), res[2]);
312 }
313
314 #[test]
315 fn test_region_pool_add() {
316 let mut pool = RegionPool::new();
317 assert_eq!(
318 Ok(()),
319 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
320 );
321 assert_eq!(
322 Ok(()),
323 pool.add(RegionExample::new(0x5000_0000, 0x1000_0000, false))
324 );
325 assert_eq!(
326 Err(RegionPoolError::AlreadyUsed),
327 pool.add(RegionExample::new(0x4000_1000, 0x1000, false))
328 );
329 }
330
331 #[test]
332 fn test_pool_allocate() {
333 let mut pool = RegionPool::new();
Imre Kis703482d2023-11-30 15:51:26 +0100334 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
Imre Kis42935a22024-10-17 11:30:16 +0200335 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100336
Imre Kis42935a22024-10-17 11:30:16 +0200337 let res = pool.allocate(0x1000, ());
338 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
339 let res = pool.allocate(0x1_0000, ());
340 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1_0000, true)), res);
341 let res = pool.allocate(0x2000_0000, ());
342 assert_eq!(Err(RegionPoolError::NoSpace), res);
343 }
Imre Kis703482d2023-11-30 15:51:26 +0100344
Imre Kis42935a22024-10-17 11:30:16 +0200345 #[test]
346 fn test_region_pool_acquire() {
347 let mut pool = RegionPool::new();
348 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
349 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100350
Imre Kis42935a22024-10-17 11:30:16 +0200351 let res = pool.acquire(0x4000_1000, 0x1000, ());
352 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1000, true)), res);
353 let res = pool.allocate(0x1_0000, ());
354 assert_eq!(Ok(RegionExample::new(0x4000_2000, 0x10000, true)), res);
355 let res = pool.acquire(0x4000_1000, 0x1000, ());
356 assert_eq!(Err(RegionPoolError::AlreadyUsed), res);
357 }
Imre Kis703482d2023-11-30 15:51:26 +0100358
Imre Kis42935a22024-10-17 11:30:16 +0200359 #[test]
360 fn test_region_pool_release() {
361 let mut pool = RegionPool::new();
362 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
363 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100364
Imre Kis42935a22024-10-17 11:30:16 +0200365 let res = pool.allocate(0x1000, ());
366 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
367 assert_eq!(Ok(()), pool.release(res.unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100368
Imre Kis42935a22024-10-17 11:30:16 +0200369 let res = pool.allocate(0x1_0000, ());
370 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x10000, true)), res);
371 assert_eq!(Ok(()), pool.release(res.unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100372
Imre Kis42935a22024-10-17 11:30:16 +0200373 assert_eq!(
374 Err(RegionPoolError::NotFound),
375 pool.release(RegionExample::new(0x4000_0000, 0x1000, true))
376 );
377 }
Imre Kis703482d2023-11-30 15:51:26 +0100378
Imre Kis42935a22024-10-17 11:30:16 +0200379 #[test]
380 #[should_panic]
381 fn test_region_pool_release_not_used() {
382 let mut pool = RegionPool::new();
383 pool.release(RegionExample::new(0x4000_0000, 0x1000, false))
384 .unwrap();
385 }
Imre Kis703482d2023-11-30 15:51:26 +0100386
Imre Kis42935a22024-10-17 11:30:16 +0200387 #[test]
388 fn test_region_pool_find_containing_region() {
389 let mut pool = RegionPool::<RegionExample>::new();
390 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
391 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100392
Imre Kis42935a22024-10-17 11:30:16 +0200393 assert_eq!(
394 Err(RegionPoolError::NotFound),
395 pool.find_containing_region(0x0000_0000, 0x1000)
396 );
Imre Kis703482d2023-11-30 15:51:26 +0100397
Imre Kis42935a22024-10-17 11:30:16 +0200398 assert_eq!(
399 Err(RegionPoolError::NotFound),
400 pool.find_containing_region(0x5000_0000, 0x1000)
401 );
Imre Kis703482d2023-11-30 15:51:26 +0100402
Imre Kis42935a22024-10-17 11:30:16 +0200403 assert_eq!(
404 Err(RegionPoolError::NotFound),
405 pool.find_containing_region(0x4000_0000, 0x2000_0000)
406 );
Imre Kis703482d2023-11-30 15:51:26 +0100407
Imre Kis42935a22024-10-17 11:30:16 +0200408 assert_eq!(
409 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
410 pool.find_containing_region(0x4000_0000, 0x1000_0000)
411 );
Imre Kis703482d2023-11-30 15:51:26 +0100412
Imre Kis42935a22024-10-17 11:30:16 +0200413 assert_eq!(
414 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
415 pool.find_containing_region(0x4000_1000, 0x1000)
416 );
417 }
Imre Kis703482d2023-11-30 15:51:26 +0100418}