blob: 4c81160907248cb46a1f3d740a04a39fefe052ef [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 Kis703482d2023-11-30 15:51:26 +010019
20 /// Get base address
Imre Kis589aa052024-09-10 20:19:47 +020021 fn base(&self) -> Self::Base;
Imre Kis703482d2023-11-30 15:51:26 +010022
23 // Get length
Imre Kis589aa052024-09-10 20:19:47 +020024 fn length(&self) -> Self::Length;
Imre Kis703482d2023-11-30 15:51:26 +010025
26 /// Check if the region is used
27 fn used(&self) -> bool;
28
29 /// Check if an area defined by its base address and length is inside the region
Imre Kis589aa052024-09-10 20:19:47 +020030 fn contains(&self, base: Self::Base, length: Self::Length) -> bool;
Imre Kis703482d2023-11-30 15:51:26 +010031
32 /// Try append the parameter region. Return true on success.
33 fn try_append(&mut self, other: &Self) -> bool;
34
35 /// Split region into multiple regions by the area passed as a parameter. It returns the region
36 /// of the area as the first member of the return tuple and all the new sliced regions as the
37 /// second member of the tuple.
38 /// The function has to handle four cases:
39 /// * The area matches the region exactly -> return one region
40 /// * The area is at the beginning of the region -> return two areas
41 /// * The area is at the end of the region -> return two areas
42 /// * The area is in the middle of the region -> return three areas
43 fn create_split(
44 &self,
Imre Kis589aa052024-09-10 20:19:47 +020045 base: Self::Base,
46 length: Self::Length,
Imre Kis703482d2023-11-30 15:51:26 +010047 resource: Option<Self::Resource>,
48 ) -> (Self, Vec<Self>);
49}
50
51/// Region pool error type
52#[derive(Debug, PartialEq)]
53pub enum RegionPoolError {
54 NoSpace,
55 AlreadyUsed,
56 NotFound,
57}
58
59/// Region pool
60pub struct RegionPool<T: Region> {
61 regions: Vec<T>,
62}
63
64impl<T: Region> RegionPool<T> {
65 /// Create empty region pool
66 pub fn new() -> Self {
67 Self {
68 regions: Vec::new(),
69 }
70 }
71
72 /// Add a region to the pool
73 pub fn add(&mut self, region: T) -> Result<(), RegionPoolError> {
74 if !self
75 .regions
76 .iter()
77 .any(|r| r.contains(region.base(), region.length()))
78 {
79 self.regions.push(region);
80 self.regions.sort_by_key(|a| a.base());
81
82 Ok(())
83 } else {
84 Err(RegionPoolError::AlreadyUsed)
85 }
86 }
87
88 /// Allocate a region from the pool of a given length. It will select an area in which the
89 /// region fits and has the minimal size. Then it splits this area to get the allocated region
90 /// with the exact size.
Imre Kis589aa052024-09-10 20:19:47 +020091 pub fn allocate(
92 &mut self,
93 length: T::Length,
94 resource: T::Resource,
95 ) -> Result<T, RegionPoolError> {
Imre Kis703482d2023-11-30 15:51:26 +010096 let region_to_allocate_from = self
97 .regions
98 .iter()
99 .enumerate()
100 .filter(|(_index, region)| region.length() >= length && !region.used())
101 .min_by_key(|(_index_a, region)| region.length());
102
103 if let Some((index, region)) = region_to_allocate_from {
104 let (new_region, split_regions) =
105 region.create_split(region.base(), length, Some(resource));
106 self.replace(index, split_regions);
107 Ok(new_region)
108 } else {
109 Err(RegionPoolError::NoSpace)
110 }
111 }
112
113 /// Acquire a region with the given base address and length.
114 pub fn acquire(
115 &mut self,
Imre Kis589aa052024-09-10 20:19:47 +0200116 base: T::Base,
117 length: T::Length,
Imre Kis703482d2023-11-30 15:51:26 +0100118 resource: T::Resource,
119 ) -> Result<T, RegionPoolError> {
120 let region_to_acquire_from = self
121 .regions
122 .iter()
123 .enumerate()
124 .find(|(_, region)| region.contains(base, length) && !region.used());
125
126 if let Some((index, region)) = region_to_acquire_from {
127 let (new_region, split_regions) = region.create_split(base, length, Some(resource));
128 self.replace(index, split_regions);
129 Ok(new_region)
130 } else {
131 Err(RegionPoolError::AlreadyUsed)
132 }
133 }
134
135 /// Release region
136 pub fn release(&mut self, region_to_release: T) -> Result<(), RegionPoolError> {
137 assert!(region_to_release.used());
138
139 let region_to_release_from = self.regions.iter().enumerate().find(|(_, r)| {
140 r.contains(region_to_release.base(), region_to_release.length()) && r.used()
141 });
142
143 if let Some((index, region)) = region_to_release_from {
144 self.replace(
145 index,
146 region
147 .create_split(region_to_release.base(), region_to_release.length(), None)
148 .1,
149 );
150 self.regions.dedup_by(|a, b| b.try_append(a));
151
152 Ok(())
153 } else {
154 Err(RegionPoolError::NotFound)
155 }
156 }
157
158 /// Find the region which contains the given area
159 pub fn find_containing_region(
160 &self,
Imre Kis589aa052024-09-10 20:19:47 +0200161 base: T::Base,
162 length: T::Length,
Imre Kis703482d2023-11-30 15:51:26 +0100163 ) -> Result<&T, RegionPoolError> {
164 let region_index = match self.regions.binary_search_by(|r| r.base().cmp(&base)) {
165 Ok(exact_index) => Ok(exact_index),
166 Err(insert_index) => {
167 if insert_index > 0 {
168 Ok(insert_index - 1)
169 } else {
170 Err(RegionPoolError::NotFound)
171 }
172 }
173 }?;
174
175 if self.regions[region_index].contains(base, length) {
176 Ok(&self.regions[region_index])
177 } else {
178 Err(RegionPoolError::NotFound)
179 }
180 }
181
182 fn replace(&mut self, index: usize, replacements: Vec<T>) {
183 self.regions.splice(
184 Range {
185 start: index,
186 end: index + 1,
187 },
188 replacements,
189 );
190 }
191}
192
193#[cfg(test)]
Imre Kis42935a22024-10-17 11:30:16 +0200194mod tests {
195 use super::*;
Imre Kis703482d2023-11-30 15:51:26 +0100196
Imre Kis42935a22024-10-17 11:30:16 +0200197 #[derive(Debug, PartialEq, Eq)]
198 struct RegionExample {
Imre Kis703482d2023-11-30 15:51:26 +0100199 base: usize,
200 length: usize,
Imre Kis42935a22024-10-17 11:30:16 +0200201 used: bool,
Imre Kis703482d2023-11-30 15:51:26 +0100202 }
Imre Kis703482d2023-11-30 15:51:26 +0100203
Imre Kis42935a22024-10-17 11:30:16 +0200204 impl RegionExample {
205 fn new(base: usize, length: usize, used: bool) -> Self {
206 Self { base, length, used }
207 }
208 }
Imre Kis703482d2023-11-30 15:51:26 +0100209
Imre Kis42935a22024-10-17 11:30:16 +0200210 impl Region for RegionExample {
211 type Resource = ();
Imre Kis589aa052024-09-10 20:19:47 +0200212 type Base = usize;
213 type Length = usize;
Imre Kis703482d2023-11-30 15:51:26 +0100214
Imre Kis42935a22024-10-17 11:30:16 +0200215 fn base(&self) -> usize {
216 self.base
217 }
Imre Kis703482d2023-11-30 15:51:26 +0100218
Imre Kis42935a22024-10-17 11:30:16 +0200219 fn length(&self) -> usize {
220 self.length
221 }
Imre Kis703482d2023-11-30 15:51:26 +0100222
Imre Kis42935a22024-10-17 11:30:16 +0200223 fn used(&self) -> bool {
224 self.used
225 }
Imre Kis703482d2023-11-30 15:51:26 +0100226
Imre Kis42935a22024-10-17 11:30:16 +0200227 fn contains(&self, base: usize, length: usize) -> bool {
228 self.base <= base && base + length <= self.base + self.length
229 }
Imre Kis703482d2023-11-30 15:51:26 +0100230
Imre Kis42935a22024-10-17 11:30:16 +0200231 fn try_append(&mut self, other: &Self) -> bool {
232 if self.used == other.used && self.base + self.length == other.base {
233 self.length += other.length;
234 true
235 } else {
236 false
237 }
238 }
Imre Kis703482d2023-11-30 15:51:26 +0100239
Imre Kis42935a22024-10-17 11:30:16 +0200240 fn create_split(
241 &self,
242 base: usize,
243 length: usize,
244 resource: Option<Self::Resource>,
245 ) -> (Self, Vec<Self>) {
246 let mut res = Vec::new();
247 if self.base != base {
248 res.push(RegionExample::new(self.base, base - self.base, self.used));
249 }
250 res.push(RegionExample::new(base, length, resource.is_some()));
251 if self.base + self.length != base + length {
252 res.push(RegionExample::new(
253 base + length,
254 (self.base + self.length) - (base + length),
255 self.used,
256 ));
257 }
Imre Kis703482d2023-11-30 15:51:26 +0100258
Imre Kis42935a22024-10-17 11:30:16 +0200259 (RegionExample::new(base, length, resource.is_some()), res)
260 }
261 }
Imre Kis703482d2023-11-30 15:51:26 +0100262
Imre Kis42935a22024-10-17 11:30:16 +0200263 #[test]
264 fn test_region_contains() {
265 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
Imre Kis703482d2023-11-30 15:51:26 +0100266
Imre Kis42935a22024-10-17 11:30:16 +0200267 assert!(region.contains(0x4000_0000, 0x1000_0000));
268 assert!(!region.contains(0x4000_0000 - 1, 0x1000_0000 + 1));
269 assert!(!region.contains(0x4000_0000, 0x1000_0000 + 1));
270 assert!(region.contains(0x4000_0000 + 1, 0x1000_0000 - 1));
271 }
Imre Kis703482d2023-11-30 15:51:26 +0100272
Imre Kis42935a22024-10-17 11:30:16 +0200273 #[test]
274 fn test_region_try_append() {
275 // Normal append
276 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
277 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, false);
Imre Kis703482d2023-11-30 15:51:26 +0100278
Imre Kis42935a22024-10-17 11:30:16 +0200279 assert!(region.try_append(&appending));
280 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_1000, false), region);
Imre Kis703482d2023-11-30 15:51:26 +0100281
Imre Kis42935a22024-10-17 11:30:16 +0200282 // Different used flags
283 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
284 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, true);
Imre Kis703482d2023-11-30 15:51:26 +0100285
Imre Kis42935a22024-10-17 11:30:16 +0200286 assert!(!region.try_append(&appending));
287 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
288
289 // Not continious
290 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
291 let appending = RegionExample::new(0x5000_1000, 0x0000_1000, false);
292
293 assert!(!region.try_append(&appending));
294 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
295 }
296
297 #[test]
298 fn test_region_create_split() {
299 // Cut first part
300 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
301
302 let res = region.create_split(0x4000_0000, 0x0000_1000, Some(())).1;
303 assert_eq!(RegionExample::new(0x4000_0000, 0x0000_1000, true), res[0]);
304 assert_eq!(RegionExample::new(0x4000_1000, 0x0fff_f000, false), res[1]);
305
306 // Cut last part
307 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
308
309 let res = region.create_split(0x4fff_f000, 0x0000_1000, Some(())).1;
310 assert_eq!(RegionExample::new(0x4000_0000, 0x0fff_f000, false), res[0]);
311 assert_eq!(RegionExample::new(0x4fff_f000, 0x0000_1000, true), res[1]);
312
313 // Cut middle part
314 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
315
316 let res = region.create_split(0x4020_0000, 0x0000_1000, Some(())).1;
317 assert_eq!(RegionExample::new(0x4000_0000, 0x0020_0000, false), res[0]);
318 assert_eq!(RegionExample::new(0x4020_0000, 0x0000_1000, true), res[1]);
319 assert_eq!(RegionExample::new(0x4020_1000, 0x0fdf_f000, false), res[2]);
320 }
321
322 #[test]
323 fn test_region_pool_add() {
324 let mut pool = RegionPool::new();
325 assert_eq!(
326 Ok(()),
327 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
328 );
329 assert_eq!(
330 Ok(()),
331 pool.add(RegionExample::new(0x5000_0000, 0x1000_0000, false))
332 );
333 assert_eq!(
334 Err(RegionPoolError::AlreadyUsed),
335 pool.add(RegionExample::new(0x4000_1000, 0x1000, false))
336 );
337 }
338
339 #[test]
340 fn test_pool_allocate() {
341 let mut pool = RegionPool::new();
Imre Kis703482d2023-11-30 15:51:26 +0100342 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
Imre Kis42935a22024-10-17 11:30:16 +0200343 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100344
Imre Kis42935a22024-10-17 11:30:16 +0200345 let res = pool.allocate(0x1000, ());
346 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
347 let res = pool.allocate(0x1_0000, ());
348 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1_0000, true)), res);
349 let res = pool.allocate(0x2000_0000, ());
350 assert_eq!(Err(RegionPoolError::NoSpace), res);
351 }
Imre Kis703482d2023-11-30 15:51:26 +0100352
Imre Kis42935a22024-10-17 11:30:16 +0200353 #[test]
354 fn test_region_pool_acquire() {
355 let mut pool = RegionPool::new();
356 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
357 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100358
Imre Kis42935a22024-10-17 11:30:16 +0200359 let res = pool.acquire(0x4000_1000, 0x1000, ());
360 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1000, true)), res);
361 let res = pool.allocate(0x1_0000, ());
362 assert_eq!(Ok(RegionExample::new(0x4000_2000, 0x10000, true)), res);
363 let res = pool.acquire(0x4000_1000, 0x1000, ());
364 assert_eq!(Err(RegionPoolError::AlreadyUsed), res);
365 }
Imre Kis703482d2023-11-30 15:51:26 +0100366
Imre Kis42935a22024-10-17 11:30:16 +0200367 #[test]
368 fn test_region_pool_release() {
369 let mut pool = RegionPool::new();
370 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
371 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100372
Imre Kis42935a22024-10-17 11:30:16 +0200373 let res = pool.allocate(0x1000, ());
374 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
375 assert_eq!(Ok(()), pool.release(res.unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100376
Imre Kis42935a22024-10-17 11:30:16 +0200377 let res = pool.allocate(0x1_0000, ());
378 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x10000, true)), res);
379 assert_eq!(Ok(()), pool.release(res.unwrap()));
Imre Kis703482d2023-11-30 15:51:26 +0100380
Imre Kis42935a22024-10-17 11:30:16 +0200381 assert_eq!(
382 Err(RegionPoolError::NotFound),
383 pool.release(RegionExample::new(0x4000_0000, 0x1000, true))
384 );
385 }
Imre Kis703482d2023-11-30 15:51:26 +0100386
Imre Kis42935a22024-10-17 11:30:16 +0200387 #[test]
388 #[should_panic]
389 fn test_region_pool_release_not_used() {
390 let mut pool = RegionPool::new();
391 pool.release(RegionExample::new(0x4000_0000, 0x1000, false))
392 .unwrap();
393 }
Imre Kis703482d2023-11-30 15:51:26 +0100394
Imre Kis42935a22024-10-17 11:30:16 +0200395 #[test]
396 fn test_region_pool_find_containing_region() {
397 let mut pool = RegionPool::<RegionExample>::new();
398 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
399 .unwrap();
Imre Kis703482d2023-11-30 15:51:26 +0100400
Imre Kis42935a22024-10-17 11:30:16 +0200401 assert_eq!(
402 Err(RegionPoolError::NotFound),
403 pool.find_containing_region(0x0000_0000, 0x1000)
404 );
Imre Kis703482d2023-11-30 15:51:26 +0100405
Imre Kis42935a22024-10-17 11:30:16 +0200406 assert_eq!(
407 Err(RegionPoolError::NotFound),
408 pool.find_containing_region(0x5000_0000, 0x1000)
409 );
Imre Kis703482d2023-11-30 15:51:26 +0100410
Imre Kis42935a22024-10-17 11:30:16 +0200411 assert_eq!(
412 Err(RegionPoolError::NotFound),
413 pool.find_containing_region(0x4000_0000, 0x2000_0000)
414 );
Imre Kis703482d2023-11-30 15:51:26 +0100415
Imre Kis42935a22024-10-17 11:30:16 +0200416 assert_eq!(
417 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
418 pool.find_containing_region(0x4000_0000, 0x1000_0000)
419 );
Imre Kis703482d2023-11-30 15:51:26 +0100420
Imre Kis42935a22024-10-17 11:30:16 +0200421 assert_eq!(
422 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
423 pool.find_containing_region(0x4000_1000, 0x1000)
424 );
425 }
Imre Kis703482d2023-11-30 15:51:26 +0100426}