blob: 116e523cdd575f0acef0652fdc1147a86b113dbf [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;
Imre Kis589aa052024-09-10 20:19:47 +020017 type Base: Ord + Copy;
18 type Length: Ord + Copy;
Imre Kisf0370e82024-11-18 16:24:55 +010019 type Alignment: Copy;
Imre Kis703482d2023-11-30 15:51:26 +010020
21 /// Get base address
Imre Kis589aa052024-09-10 20:19:47 +020022 fn base(&self) -> Self::Base;
Imre Kis703482d2023-11-30 15:51:26 +010023
24 // Get length
Imre Kis589aa052024-09-10 20:19:47 +020025 fn length(&self) -> Self::Length;
Imre Kis703482d2023-11-30 15:51:26 +010026
27 /// Check if the region is used
28 fn used(&self) -> bool;
29
30 /// Check if an area defined by its base address and length is inside the region
Imre Kis589aa052024-09-10 20:19:47 +020031 fn contains(&self, base: Self::Base, length: Self::Length) -> bool;
Imre Kis703482d2023-11-30 15:51:26 +010032
Imre Kisf0370e82024-11-18 16:24:55 +010033 /// Try to allocate a base address from the region. If there's no alignment specified, the
34 /// function will check if the requested length fits into the buffer and if yes, it will
35 /// return the base of the region.
36 /// If an alignment is specified, it will call try_alloc_aligned which has to be implemeted
37 /// by the trait users.
38 /// This function is part of the trait so the trait users can override it if they want to
39 /// achieve a better implementation specific behavior.
40 fn try_alloc(
41 &self,
42 length: Self::Length,
43 alignment: Option<Self::Alignment>,
44 ) -> Option<Self::Base> {
45 if let Some(alignment) = alignment {
46 self.try_alloc_aligned(length, alignment)
47 } else if length <= self.length() {
48 Some(self.base())
49 } else {
50 None
51 }
52 }
53
54 /// Try to allocate aligned address from the region.
55 fn try_alloc_aligned(
56 &self,
57 length: Self::Length,
58 alignment: Self::Alignment,
59 ) -> Option<Self::Base>;
60
Imre Kis703482d2023-11-30 15:51:26 +010061 /// Try append the parameter region. Return true on success.
62 fn try_append(&mut self, other: &Self) -> bool;
63
64 /// Split region into multiple regions by the area passed as a parameter. It returns the region
65 /// of the area as the first member of the return tuple and all the new sliced regions as the
66 /// second member of the tuple.
67 /// The function has to handle four cases:
68 /// * The area matches the region exactly -> return one region
69 /// * The area is at the beginning of the region -> return two areas
70 /// * The area is at the end of the region -> return two areas
71 /// * The area is in the middle of the region -> return three areas
72 fn create_split(
73 &self,
Imre Kis589aa052024-09-10 20:19:47 +020074 base: Self::Base,
75 length: Self::Length,
Imre Kis703482d2023-11-30 15:51:26 +010076 resource: Option<Self::Resource>,
77 ) -> (Self, Vec<Self>);
78}
79
80/// Region pool error type
81#[derive(Debug, PartialEq)]
82pub enum RegionPoolError {
83 NoSpace,
84 AlreadyUsed,
85 NotFound,
86}
87
88/// Region pool
89pub struct RegionPool<T: Region> {
90 regions: Vec<T>,
91}
92
93impl<T: Region> RegionPool<T> {
94 /// Create empty region pool
95 pub fn new() -> Self {
96 Self {
97 regions: Vec::new(),
98 }
99 }
100
101 /// Add a region to the pool
102 pub fn add(&mut self, region: T) -> Result<(), RegionPoolError> {
103 if !self
104 .regions
105 .iter()
106 .any(|r| r.contains(region.base(), region.length()))
107 {
108 self.regions.push(region);
109 self.regions.sort_by_key(|a| a.base());
110
111 Ok(())
112 } else {
113 Err(RegionPoolError::AlreadyUsed)
114 }
115 }
116
117 /// Allocate a region from the pool of a given length. It will select an area in which the
118 /// region fits and has the minimal size. Then it splits this area to get the allocated region
119 /// with the exact size.
Imre Kis589aa052024-09-10 20:19:47 +0200120 pub fn allocate(
121 &mut self,
122 length: T::Length,
123 resource: T::Resource,
Imre Kisf0370e82024-11-18 16:24:55 +0100124 alignment: Option<T::Alignment>,
Imre Kis589aa052024-09-10 20:19:47 +0200125 ) -> Result<T, RegionPoolError> {
Imre Kis703482d2023-11-30 15:51:26 +0100126 let region_to_allocate_from = self
127 .regions
128 .iter()
129 .enumerate()
Imre Kisf0370e82024-11-18 16:24:55 +0100130 .filter(|(_index, region)| {
131 !region.used() && region.try_alloc(length, alignment).is_some()
132 })
Imre Kis703482d2023-11-30 15:51:26 +0100133 .min_by_key(|(_index_a, region)| region.length());
134
135 if let Some((index, region)) = region_to_allocate_from {
Imre Kisf0370e82024-11-18 16:24:55 +0100136 // The previous call to try_alloc ensures that the following unwrap will not fail.
137 let base = region.try_alloc(length, alignment).unwrap();
138 let (new_region, split_regions) = region.create_split(base, length, Some(resource));
Imre Kis703482d2023-11-30 15:51:26 +0100139 self.replace(index, split_regions);
140 Ok(new_region)
141 } else {
142 Err(RegionPoolError::NoSpace)
143 }
144 }
145
146 /// Acquire a region with the given base address and length.
147 pub fn acquire(
148 &mut self,
Imre Kis589aa052024-09-10 20:19:47 +0200149 base: T::Base,
150 length: T::Length,
Imre Kis703482d2023-11-30 15:51:26 +0100151 resource: T::Resource,
152 ) -> Result<T, RegionPoolError> {
153 let region_to_acquire_from = self
154 .regions
155 .iter()
156 .enumerate()
157 .find(|(_, region)| region.contains(base, length) && !region.used());
158
159 if let Some((index, region)) = region_to_acquire_from {
160 let (new_region, split_regions) = region.create_split(base, length, Some(resource));
161 self.replace(index, split_regions);
162 Ok(new_region)
163 } else {
164 Err(RegionPoolError::AlreadyUsed)
165 }
166 }
167
168 /// Release region
169 pub fn release(&mut self, region_to_release: T) -> Result<(), RegionPoolError> {
170 assert!(region_to_release.used());
171
172 let region_to_release_from = self.regions.iter().enumerate().find(|(_, r)| {
173 r.contains(region_to_release.base(), region_to_release.length()) && r.used()
174 });
175
176 if let Some((index, region)) = region_to_release_from {
177 self.replace(
178 index,
179 region
180 .create_split(region_to_release.base(), region_to_release.length(), None)
181 .1,
182 );
183 self.regions.dedup_by(|a, b| b.try_append(a));
184
185 Ok(())
186 } else {
187 Err(RegionPoolError::NotFound)
188 }
189 }
190
191 /// Find the region which contains the given area
192 pub fn find_containing_region(
193 &self,
Imre Kis589aa052024-09-10 20:19:47 +0200194 base: T::Base,
195 length: T::Length,
Imre Kis703482d2023-11-30 15:51:26 +0100196 ) -> Result<&T, RegionPoolError> {
197 let region_index = match self.regions.binary_search_by(|r| r.base().cmp(&base)) {
198 Ok(exact_index) => Ok(exact_index),
199 Err(insert_index) => {
200 if insert_index > 0 {
201 Ok(insert_index - 1)
202 } else {
203 Err(RegionPoolError::NotFound)
204 }
205 }
206 }?;
207
208 if self.regions[region_index].contains(base, length) {
209 Ok(&self.regions[region_index])
210 } else {
211 Err(RegionPoolError::NotFound)
212 }
213 }
214
215 fn replace(&mut self, index: usize, replacements: Vec<T>) {
216 self.regions.splice(
217 Range {
218 start: index,
219 end: index + 1,
220 },
221 replacements,
222 );
223 }
224}
225
226#[cfg(test)]
Imre Kis42935a22024-10-17 11:30:16 +0200227mod tests {
228 use super::*;
Imre Kis703482d2023-11-30 15:51:26 +0100229
Imre Kis42935a22024-10-17 11:30:16 +0200230 #[derive(Debug, PartialEq, Eq)]
231 struct RegionExample {
Imre Kis703482d2023-11-30 15:51:26 +0100232 base: usize,
233 length: usize,
Imre Kis42935a22024-10-17 11:30:16 +0200234 used: bool,
Imre Kis703482d2023-11-30 15:51:26 +0100235 }
Imre Kis703482d2023-11-30 15:51:26 +0100236
Imre Kis42935a22024-10-17 11:30:16 +0200237 impl RegionExample {
238 fn new(base: usize, length: usize, used: bool) -> Self {
239 Self { base, length, used }
240 }
241 }
Imre Kis703482d2023-11-30 15:51:26 +0100242
Imre Kis42935a22024-10-17 11:30:16 +0200243 impl Region for RegionExample {
244 type Resource = ();
Imre Kis589aa052024-09-10 20:19:47 +0200245 type Base = usize;
246 type Length = usize;
Imre Kisf0370e82024-11-18 16:24:55 +0100247 type Alignment = usize;
Imre Kis703482d2023-11-30 15:51:26 +0100248
Imre Kis42935a22024-10-17 11:30:16 +0200249 fn base(&self) -> usize {
250 self.base
251 }
Imre Kis703482d2023-11-30 15:51:26 +0100252
Imre Kis42935a22024-10-17 11:30:16 +0200253 fn length(&self) -> usize {
254 self.length
255 }
Imre Kis703482d2023-11-30 15:51:26 +0100256
Imre Kis42935a22024-10-17 11:30:16 +0200257 fn used(&self) -> bool {
258 self.used
259 }
Imre Kis703482d2023-11-30 15:51:26 +0100260
Imre Kis42935a22024-10-17 11:30:16 +0200261 fn contains(&self, base: usize, length: usize) -> bool {
262 self.base <= base && base + length <= self.base + self.length
263 }
Imre Kis703482d2023-11-30 15:51:26 +0100264
Imre Kisf0370e82024-11-18 16:24:55 +0100265 fn try_alloc_aligned(
266 &self,
267 length: Self::Length,
268 alignment: Self::Alignment,
269 ) -> Option<Self::Base> {
270 let aligned_base = self.base.next_multiple_of(alignment);
271 let base_offset = aligned_base.checked_sub(self.base)?;
272
273 let required_length = base_offset.checked_add(length)?;
274 if required_length <= self.length {
275 Some(aligned_base)
276 } else {
277 None
278 }
279 }
280
Imre Kis42935a22024-10-17 11:30:16 +0200281 fn try_append(&mut self, other: &Self) -> bool {
282 if self.used == other.used && self.base + self.length == other.base {
283 self.length += other.length;
284 true
285 } else {
286 false
287 }
288 }
Imre Kis703482d2023-11-30 15:51:26 +0100289
Imre Kis42935a22024-10-17 11:30:16 +0200290 fn create_split(
291 &self,
292 base: usize,
293 length: usize,
294 resource: Option<Self::Resource>,
295 ) -> (Self, Vec<Self>) {
296 let mut res = Vec::new();
297 if self.base != base {
298 res.push(RegionExample::new(self.base, base - self.base, self.used));
299 }
300 res.push(RegionExample::new(base, length, resource.is_some()));
301 if self.base + self.length != base + length {
302 res.push(RegionExample::new(
303 base + length,
304 (self.base + self.length) - (base + length),
305 self.used,
306 ));
307 }
Imre Kis703482d2023-11-30 15:51:26 +0100308
Imre Kis42935a22024-10-17 11:30:16 +0200309 (RegionExample::new(base, length, resource.is_some()), res)
310 }
311 }
Imre Kis703482d2023-11-30 15:51:26 +0100312
Imre Kis42935a22024-10-17 11:30:16 +0200313 #[test]
314 fn test_region_contains() {
315 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
Imre Kis703482d2023-11-30 15:51:26 +0100316
Imre Kis42935a22024-10-17 11:30:16 +0200317 assert!(region.contains(0x4000_0000, 0x1000_0000));
318 assert!(!region.contains(0x4000_0000 - 1, 0x1000_0000 + 1));
319 assert!(!region.contains(0x4000_0000, 0x1000_0000 + 1));
320 assert!(region.contains(0x4000_0000 + 1, 0x1000_0000 - 1));
321 }
Imre Kis703482d2023-11-30 15:51:26 +0100322
Imre Kis42935a22024-10-17 11:30:16 +0200323 #[test]
Imre Kisf0370e82024-11-18 16:24:55 +0100324 fn test_region_try_alloc() {
325 let region = RegionExample::new(0x4000_1000, 0x1_0000, false);
326
327 assert_eq!(Some(0x4000_1000), region.try_alloc(0x1000, None));
328 assert_eq!(Some(0x4000_2000), region.try_alloc(0x1000, Some(0x2000)));
329 assert_eq!(None, region.try_alloc(0x1000, Some(0x10_0000)));
330 }
331
332 #[test]
Imre Kis42935a22024-10-17 11:30:16 +0200333 fn test_region_try_append() {
334 // Normal append
335 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
336 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, false);
Imre Kis703482d2023-11-30 15:51:26 +0100337
Imre Kis42935a22024-10-17 11:30:16 +0200338 assert!(region.try_append(&appending));
339 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_1000, false), region);
Imre Kis703482d2023-11-30 15:51:26 +0100340
Imre Kis42935a22024-10-17 11:30:16 +0200341 // Different used flags
342 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
343 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, true);
Imre Kis703482d2023-11-30 15:51:26 +0100344
Imre Kis42935a22024-10-17 11:30:16 +0200345 assert!(!region.try_append(&appending));
346 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
347
348 // Not continious
349 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
350 let appending = RegionExample::new(0x5000_1000, 0x0000_1000, false);
351
352 assert!(!region.try_append(&appending));
353 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
354 }
355
356 #[test]
357 fn test_region_create_split() {
358 // Cut first part
359 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
360
361 let res = region.create_split(0x4000_0000, 0x0000_1000, Some(())).1;
362 assert_eq!(RegionExample::new(0x4000_0000, 0x0000_1000, true), res[0]);
363 assert_eq!(RegionExample::new(0x4000_1000, 0x0fff_f000, false), res[1]);
364
365 // Cut last part
366 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
367
368 let res = region.create_split(0x4fff_f000, 0x0000_1000, Some(())).1;
369 assert_eq!(RegionExample::new(0x4000_0000, 0x0fff_f000, false), res[0]);
370 assert_eq!(RegionExample::new(0x4fff_f000, 0x0000_1000, true), res[1]);
371
372 // Cut middle part
373 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
374
375 let res = region.create_split(0x4020_0000, 0x0000_1000, Some(())).1;
376 assert_eq!(RegionExample::new(0x4000_0000, 0x0020_0000, false), res[0]);
377 assert_eq!(RegionExample::new(0x4020_0000, 0x0000_1000, true), res[1]);
378 assert_eq!(RegionExample::new(0x4020_1000, 0x0fdf_f000, false), res[2]);
379 }
380
381 #[test]
382 fn test_region_pool_add() {
383 let mut pool = RegionPool::new();
384 assert_eq!(
385 Ok(()),
386 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
387 );
388 assert_eq!(
389 Ok(()),
390 pool.add(RegionExample::new(0x5000_0000, 0x1000_0000, false))
391 );
392 assert_eq!(
393 Err(RegionPoolError::AlreadyUsed),
394 pool.add(RegionExample::new(0x4000_1000, 0x1000, false))
395 );
396 }
397
398 #[test]
399 fn test_pool_allocate() {
400 let mut pool = RegionPool::new();
Imre Kis703482d2023-11-30 15:51:26 +0100401 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
Imre Kis42935a22024-10-17 11:30:16 +0200402 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100403
Imre Kisf0370e82024-11-18 16:24:55 +0100404 let res = pool.allocate(0x1000, (), None);
Imre Kis42935a22024-10-17 11:30:16 +0200405 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
Imre Kisf0370e82024-11-18 16:24:55 +0100406 let res = pool.allocate(0x1_0000, (), None);
Imre Kis42935a22024-10-17 11:30:16 +0200407 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1_0000, true)), res);
Imre Kisf0370e82024-11-18 16:24:55 +0100408 let res = pool.allocate(0x2000_0000, (), None);
Imre Kis42935a22024-10-17 11:30:16 +0200409 assert_eq!(Err(RegionPoolError::NoSpace), res);
410 }
Imre Kis703482d2023-11-30 15:51:26 +0100411
Imre Kis42935a22024-10-17 11:30:16 +0200412 #[test]
Imre Kisf0370e82024-11-18 16:24:55 +0100413 fn test_pool_allocate_aligned() {
414 let mut pool = RegionPool::new();
415 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
416 .unwrap();
417
418 let res = pool.allocate(0x1000, (), Some(0x2000));
419 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
420 let res = pool.allocate(0x2000, (), Some(0x1_0000));
421 assert_eq!(Ok(RegionExample::new(0x4001_0000, 0x2000, true)), res);
422 let res = pool.allocate(0x1000, (), None);
423 // This region is allocated from the empty space between the first two regions
424 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1000, true)), res);
425 }
426
427 #[test]
Imre Kis42935a22024-10-17 11:30:16 +0200428 fn test_region_pool_acquire() {
429 let mut pool = RegionPool::new();
430 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
431 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100432
Imre Kis42935a22024-10-17 11:30:16 +0200433 let res = pool.acquire(0x4000_1000, 0x1000, ());
434 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1000, true)), res);
Imre Kisf0370e82024-11-18 16:24:55 +0100435 let res = pool.allocate(0x1_0000, (), None);
Imre Kis42935a22024-10-17 11:30:16 +0200436 assert_eq!(Ok(RegionExample::new(0x4000_2000, 0x10000, true)), res);
437 let res = pool.acquire(0x4000_1000, 0x1000, ());
438 assert_eq!(Err(RegionPoolError::AlreadyUsed), res);
439 }
Imre Kis703482d2023-11-30 15:51:26 +0100440
Imre Kis42935a22024-10-17 11:30:16 +0200441 #[test]
442 fn test_region_pool_release() {
443 let mut pool = RegionPool::new();
444 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
445 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100446
Imre Kisf0370e82024-11-18 16:24:55 +0100447 let res = pool.allocate(0x1000, (), None);
Imre Kis42935a22024-10-17 11:30:16 +0200448 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
449 assert_eq!(Ok(()), pool.release(res.unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100450
Imre Kisf0370e82024-11-18 16:24:55 +0100451 let res = pool.allocate(0x1_0000, (), None);
Imre Kis42935a22024-10-17 11:30:16 +0200452 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x10000, true)), res);
453 assert_eq!(Ok(()), pool.release(res.unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100454
Imre Kis42935a22024-10-17 11:30:16 +0200455 assert_eq!(
456 Err(RegionPoolError::NotFound),
457 pool.release(RegionExample::new(0x4000_0000, 0x1000, true))
458 );
459 }
Imre Kis703482d2023-11-30 15:51:26 +0100460
Imre Kis42935a22024-10-17 11:30:16 +0200461 #[test]
462 #[should_panic]
463 fn test_region_pool_release_not_used() {
464 let mut pool = RegionPool::new();
465 pool.release(RegionExample::new(0x4000_0000, 0x1000, false))
466 .unwrap();
467 }
Imre Kis703482d2023-11-30 15:51:26 +0100468
Imre Kis42935a22024-10-17 11:30:16 +0200469 #[test]
470 fn test_region_pool_find_containing_region() {
471 let mut pool = RegionPool::<RegionExample>::new();
472 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
473 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100474
Imre Kis42935a22024-10-17 11:30:16 +0200475 assert_eq!(
476 Err(RegionPoolError::NotFound),
477 pool.find_containing_region(0x0000_0000, 0x1000)
478 );
Imre Kis703482d2023-11-30 15:51:26 +0100479
Imre Kis42935a22024-10-17 11:30:16 +0200480 assert_eq!(
481 Err(RegionPoolError::NotFound),
482 pool.find_containing_region(0x5000_0000, 0x1000)
483 );
Imre Kis703482d2023-11-30 15:51:26 +0100484
Imre Kis42935a22024-10-17 11:30:16 +0200485 assert_eq!(
486 Err(RegionPoolError::NotFound),
487 pool.find_containing_region(0x4000_0000, 0x2000_0000)
488 );
Imre Kis703482d2023-11-30 15:51:26 +0100489
Imre Kis42935a22024-10-17 11:30:16 +0200490 assert_eq!(
491 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
492 pool.find_containing_region(0x4000_0000, 0x1000_0000)
493 );
Imre Kis703482d2023-11-30 15:51:26 +0100494
Imre Kis42935a22024-10-17 11:30:16 +0200495 assert_eq!(
496 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
497 pool.find_containing_region(0x4000_1000, 0x1000)
498 );
499 }
Imre Kis703482d2023-11-30 15:51:26 +0100500}