blob: f6fa3b400031813a5f73990a0789156396948e51 [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)]
188#[derive(Debug, PartialEq, Eq)]
189struct RegionExample {
190 base: usize,
191 length: usize,
192 used: bool,
193}
194
195#[cfg(test)]
196impl RegionExample {
197 fn new(base: usize, length: usize, used: bool) -> Self {
198 Self { base, length, used }
199 }
200}
201
202#[cfg(test)]
203impl Region for RegionExample {
204 type Resource = ();
205
206 fn base(&self) -> usize {
207 self.base
208 }
209
210 fn length(&self) -> usize {
211 self.length
212 }
213
214 fn used(&self) -> bool {
215 self.used
216 }
217
218 fn contains(&self, base: usize, length: usize) -> bool {
219 self.base <= base && base + length <= self.base + self.length
220 }
221
222 fn try_append(&mut self, other: &Self) -> bool {
223 if self.used == other.used && self.base + self.length == other.base {
224 self.length += other.length;
225 true
226 } else {
227 false
228 }
229 }
230
231 fn create_split(
232 &self,
233 base: usize,
234 length: usize,
235 resource: Option<Self::Resource>,
236 ) -> (Self, Vec<Self>) {
237 let mut res = Vec::new();
238 if self.base != base {
239 res.push(RegionExample::new(self.base, base - self.base, self.used));
240 }
241 res.push(RegionExample::new(base, length, resource.is_some()));
242 if self.base + self.length != base + length {
243 res.push(RegionExample::new(
244 base + length,
245 (self.base + self.length) - (base + length),
246 self.used,
247 ));
248 }
249
250 (RegionExample::new(base, length, resource.is_some()), res)
251 }
252}
253
254#[test]
255fn test_region_contains() {
256 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
257
258 assert!(region.contains(0x4000_0000, 0x1000_0000));
259 assert!(!region.contains(0x4000_0000 - 1, 0x1000_0000 + 1));
260 assert!(!region.contains(0x4000_0000, 0x1000_0000 + 1));
261 assert!(region.contains(0x4000_0000 + 1, 0x1000_0000 - 1));
262}
263
264#[test]
265fn test_region_try_append() {
266 // Normal append
267 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
268 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, false);
269
270 assert!(region.try_append(&appending));
271 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_1000, false), region);
272
273 // Different used flags
274 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
275 let appending = RegionExample::new(0x5000_0000, 0x0000_1000, true);
276
277 assert!(!region.try_append(&appending));
278 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
279
280 // Not continious
281 let mut region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
282 let appending = RegionExample::new(0x5000_1000, 0x0000_1000, false);
283
284 assert!(!region.try_append(&appending));
285 assert_eq!(RegionExample::new(0x4000_0000, 0x1000_0000, false), region);
286}
287
288#[test]
289fn test_region_create_split() {
290 // Cut first part
291 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
292
293 let res = region.create_split(0x4000_0000, 0x0000_1000, Some(())).1;
294 assert_eq!(RegionExample::new(0x4000_0000, 0x0000_1000, true), res[0]);
295 assert_eq!(RegionExample::new(0x4000_1000, 0x0fff_f000, false), res[1]);
296
297 // Cut last part
298 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
299
300 let res = region.create_split(0x4fff_f000, 0x0000_1000, Some(())).1;
301 assert_eq!(RegionExample::new(0x4000_0000, 0x0fff_f000, false), res[0]);
302 assert_eq!(RegionExample::new(0x4fff_f000, 0x0000_1000, true), res[1]);
303
304 // Cut middle part
305 let region = RegionExample::new(0x4000_0000, 0x1000_0000, false);
306
307 let res = region.create_split(0x4020_0000, 0x0000_1000, Some(())).1;
308 assert_eq!(RegionExample::new(0x4000_0000, 0x0020_0000, false), res[0]);
309 assert_eq!(RegionExample::new(0x4020_0000, 0x0000_1000, true), res[1]);
310 assert_eq!(RegionExample::new(0x4020_1000, 0x0fdf_f000, false), res[2]);
311}
312
313#[test]
314fn test_region_pool_add() {
315 let mut pool = RegionPool::new();
316 assert_eq!(
317 Ok(()),
318 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
319 );
320 assert_eq!(
321 Ok(()),
322 pool.add(RegionExample::new(0x5000_0000, 0x1000_0000, false))
323 );
324 assert_eq!(
325 Err(RegionPoolError::AlreadyUsed),
326 pool.add(RegionExample::new(0x4000_1000, 0x1000, false))
327 );
328}
329
330#[test]
331fn test_pool_allocate() {
332 let mut pool = RegionPool::new();
333 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
334 .unwrap();
335
336 let res = pool.allocate(0x1000, ());
337 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
338 let res = pool.allocate(0x1_0000, ());
339 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1_0000, true)), res);
340 let res = pool.allocate(0x2000_0000, ());
341 assert_eq!(Err(RegionPoolError::NoSpace), res);
342}
343
344#[test]
345fn test_region_pool_acquire() {
346 let mut pool = RegionPool::new();
347 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
348 .unwrap();
349
350 let res = pool.acquire(0x4000_1000, 0x1000, ());
351 assert_eq!(Ok(RegionExample::new(0x4000_1000, 0x1000, true)), res);
352 let res = pool.allocate(0x1_0000, ());
353 assert_eq!(Ok(RegionExample::new(0x4000_2000, 0x10000, true)), res);
354 let res = pool.acquire(0x4000_1000, 0x1000, ());
355 assert_eq!(Err(RegionPoolError::AlreadyUsed), res);
356}
357
358#[test]
359fn test_region_pool_release() {
360 let mut pool = RegionPool::new();
361 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
362 .unwrap();
363
364 let res = pool.allocate(0x1000, ());
365 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x1000, true)), res);
366 assert_eq!(Ok(()), pool.release(res.unwrap()));
367
368 let res = pool.allocate(0x1_0000, ());
369 assert_eq!(Ok(RegionExample::new(0x4000_0000, 0x10000, true)), res);
370 assert_eq!(Ok(()), pool.release(res.unwrap()));
371
372 assert_eq!(
373 Err(RegionPoolError::NotFound),
374 pool.release(RegionExample::new(0x4000_0000, 0x1000, true))
375 );
376}
377
378#[test]
379#[should_panic]
380fn test_region_pool_release_not_used() {
381 let mut pool = RegionPool::new();
382 pool.release(RegionExample::new(0x4000_0000, 0x1000, false))
383 .unwrap();
384}
385
386#[test]
387fn test_region_pool_find_containing_region() {
388 let mut pool = RegionPool::<RegionExample>::new();
389 pool.add(RegionExample::new(0x4000_0000, 0x1000_0000, false))
390 .unwrap();
391
392 assert_eq!(
393 Err(RegionPoolError::NotFound),
394 pool.find_containing_region(0x0000_0000, 0x1000)
395 );
396
397 assert_eq!(
398 Err(RegionPoolError::NotFound),
399 pool.find_containing_region(0x5000_0000, 0x1000)
400 );
401
402 assert_eq!(
403 Err(RegionPoolError::NotFound),
404 pool.find_containing_region(0x4000_0000, 0x2000_0000)
405 );
406
407 assert_eq!(
408 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
409 pool.find_containing_region(0x4000_0000, 0x1000_0000)
410 );
411
412 assert_eq!(
413 Ok(&RegionExample::new(0x4000_0000, 0x1000_0000, false)),
414 pool.find_containing_region(0x4000_1000, 0x1000)
415 );
416}