1use std::sync::{Arc, Mutex};
2
3use twizzler::{
4 error::{GenericError, TwzError},
5 marker::{BaseType, Invariant},
6 object::ObjectBuilder,
7};
8use twizzler_abi::object::{MAX_SIZE, NULLPAGE_SIZE};
9
10use super::{Access, DeviceSync, DmaObject, DmaOptions, DmaRegion, DmaSliceRegion, DMA_PAGE_SIZE};
11
12#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
13pub(super) struct SplitPageRange {
14 start: usize,
15 len: usize,
16}
17
18pub(super) enum Split {
19 Single(SplitPageRange),
20 Multiple(SplitPageRange, SplitPageRange),
21}
22
23impl SplitPageRange {
24 fn new(start: usize, len: usize) -> Self {
25 Self { start, len }
26 }
27
28 fn split(self, newlen: usize) -> Split {
29 let start = self.start;
30 let len = self.len;
31 if newlen == 0 || newlen == len {
32 return Split::Single(Self { start, len });
33 }
34 Split::Multiple(
35 Self { start, len: newlen },
36 Self {
37 start: start + newlen,
38 len: len - newlen,
39 },
40 )
41 }
42
43 fn merge(self, other: Self) -> Self {
44 let (first, second) = if self.start < other.start {
45 (self, other)
46 } else {
47 (other, self)
48 };
49 assert!(first.adjacent_before(&second));
50
51 Self {
52 start: first.start,
53 len: first.len + second.len,
54 }
55 }
56
57 fn adjacent_before(&self, other: &Self) -> bool {
58 self.start < other.start && self.start + self.len == other.start
59 }
60
61 fn len(&self) -> usize {
62 self.len
63 }
64
65 #[cfg(test)]
66 fn start(&self) -> usize {
67 self.start
68 }
69
70 fn offset(&self) -> usize {
71 self.start * DMA_PAGE_SIZE
72 }
73}
74
75#[cfg(test)]
76pub mod tests_split_page_range {
77 use super::SplitPageRange;
78 use crate::dma::pool::compact_range_list;
79
80 #[test]
81 fn spr_split_multiple() {
82 let r = SplitPageRange::new(2, 7);
83 let split = r.split(4);
84 if let super::Split::Multiple(a, b) = split {
85 assert_eq!(a.len(), 4);
86 assert_eq!(a.start(), 2);
87 assert_eq!(b.len(), 3);
88 assert_eq!(b.start(), 6);
89 } else {
90 panic!("split broken");
91 }
92 }
93
94 #[test]
95 fn spr_split_single1() {
96 let r = SplitPageRange::new(2, 7);
97 let split = r.split(7);
98 if let super::Split::Single(r) = split {
99 assert_eq!(r.len(), 7);
100 assert_eq!(r.start(), 2);
101 } else {
102 panic!("split broken");
103 }
104 }
105
106 #[test]
107 fn spr_split_single2() {
108 let r = SplitPageRange::new(2, 7);
109 let split = r.split(0);
110 if let super::Split::Single(r) = split {
111 assert_eq!(r.len(), 7);
112 assert_eq!(r.start(), 2);
113 } else {
114 panic!("split broken");
115 }
116 }
117
118 #[test]
119 fn spr_merge() {
120 let a = SplitPageRange::new(2, 4);
121 let b = SplitPageRange::new(6, 3);
122 let r = a.merge(b);
123 assert_eq!(r.start(), 2);
124 assert_eq!(r.len(), 7);
125 }
126
127 #[test]
128 fn spr_adj() {
129 let a = SplitPageRange::new(2, 4);
130 let b = SplitPageRange::new(1, 1);
131 let c = SplitPageRange::new(6, 4);
132
133 assert!(!a.adjacent_before(&b));
134 assert!(b.adjacent_before(&a));
135 assert!(!a.adjacent_before(&a));
136 assert!(a.adjacent_before(&c));
137 }
138
139 #[test]
140 fn spr_merge_alg() {
141 let a = SplitPageRange::new(2, 4);
142 let b = SplitPageRange::new(0, 1);
143 let c = SplitPageRange::new(6, 4);
144 let x = SplitPageRange::new(2, 8);
145 let mut list = vec![a.clone(), b.clone(), c.clone()];
146 let single_list = vec![a.clone()];
147 let slw: Vec<_> = single_list.windows(2).collect();
148 assert!(slw.is_empty());
149
150 compact_range_list(&mut list);
151
152 assert_eq!(list, vec![b, x]);
153 }
154}
155
156pub(super) struct AllocatableDmaObject {
157 dma: DmaObject,
158 freelist: Mutex<Vec<SplitPageRange>>,
159}
160
161pub struct DmaPool {
163 opts: DmaOptions,
164 spec: ObjectBuilder<()>,
165 access: Access,
166 objects: Mutex<Vec<Arc<AllocatableDmaObject>>>,
167}
168
169#[repr(C)]
170struct EmptyBase;
171
172unsafe impl Invariant for EmptyBase {}
173impl BaseType for EmptyBase {}
174
175fn compact_range_list(list: &mut Vec<SplitPageRange>) {
178 list.sort();
179 loop {
180 let pairs: Vec<_> = list
181 .windows(2)
182 .enumerate()
183 .filter_map(|(idx, ranges)| {
184 if ranges[0].adjacent_before(&ranges[1]) {
185 Some(idx)
186 } else {
187 None
188 }
189 })
190 .collect();
191
192 if pairs.is_empty() {
193 break;
194 }
195
196 for pair in pairs.iter().rev() {
198 let second = list.remove(pair + 1);
200 let new = list[*pair].clone().merge(second);
201 list[*pair] = new;
202 }
203 }
204}
205
206impl AllocatableDmaObject {
207 pub(super) fn dma_object(&self) -> &DmaObject {
208 &self.dma
209 }
210
211 pub(super) fn free(&self, range: SplitPageRange) {
212 let mut freelist = self.freelist.lock().unwrap();
213 freelist.push(range);
214
215 compact_range_list(&mut freelist);
216 }
218
219 fn allocate(&self, len: usize) -> Option<SplitPageRange> {
220 let mut freelist = self.freelist.lock().unwrap();
221 let nr_pages = (len - 1) / DMA_PAGE_SIZE + 1;
222 let index = freelist.iter().position(|range| range.len() >= nr_pages)?;
223
224 let range = freelist.remove(index);
225 Some(match range.split(nr_pages) {
226 Split::Single(r) => r,
227 Split::Multiple(alloc, extra) => {
228 freelist.push(extra);
229 alloc
230 }
231 })
232 }
233
234 fn new(spec: ObjectBuilder<()>) -> Result<AllocatableDmaObject, TwzError> {
235 Ok(AllocatableDmaObject {
236 dma: DmaObject::new::<EmptyBase>(
238 spec.cast()
239 .build(EmptyBase)
240 .map_err(|_| GenericError::Other)?,
241 ),
242 freelist: Mutex::new(vec![SplitPageRange::new(
243 1,
244 (MAX_SIZE - NULLPAGE_SIZE * 2) / DMA_PAGE_SIZE,
245 )]),
246 })
247 }
248}
249
250impl DmaPool {
251 pub fn new(spec: ObjectBuilder<()>, access: Access, opts: DmaOptions) -> Self {
255 Self {
256 opts,
257 spec,
258 access,
259 objects: Mutex::new(vec![]),
260 }
261 }
262
263 pub fn default_spec() -> ObjectBuilder<()> {
264 ObjectBuilder::default()
265 }
266
267 fn new_object(&self) -> Result<Arc<AllocatableDmaObject>, TwzError> {
268 let obj = Arc::new(AllocatableDmaObject::new(self.spec.clone())?);
269 Ok(obj)
270 }
271
272 fn do_allocate(
273 &self,
274 len: usize,
275 ) -> Result<(Arc<AllocatableDmaObject>, SplitPageRange), TwzError> {
276 if len > MAX_SIZE - NULLPAGE_SIZE * 2 {
277 return Err(TwzError::INVALID_ARGUMENT);
278 }
279 let mut objects = self.objects.lock().unwrap();
280 for obj in &*objects {
281 if let Some(pagerange) = obj.allocate(len) {
282 return Ok((obj.clone(), pagerange));
283 }
284 }
285 let obj = self.new_object()?;
286 objects.push(obj);
287 drop(objects);
288 self.do_allocate(len)
289 }
290
291 pub fn allocate<'a, T: DeviceSync>(&'a self, init: T) -> Result<DmaRegion<T>, TwzError> {
294 let len = core::mem::size_of::<T>();
295 let (ado, range) = self.do_allocate(len)?;
296 let mut reg = DmaRegion::new(
297 len,
298 self.access,
299 self.opts,
300 range.offset(),
301 Some((ado.clone(), range)),
302 );
303 reg.fill(init);
304 Ok(reg)
305 }
306
307 pub fn allocate_array<'a, T: DeviceSync + Clone>(
310 &'a self,
311 count: usize,
312 init: T,
313 ) -> Result<DmaSliceRegion<T>, TwzError> {
314 let len = core::mem::size_of::<T>() * count;
315 let (ado, range) = self.do_allocate(len)?;
316 let mut reg = DmaSliceRegion::new(
317 len,
318 self.access,
319 self.opts,
320 range.offset(),
321 count,
322 Some((ado.clone(), range)),
323 );
324 reg.fill(init);
325 Ok(reg)
326 }
327
328 pub fn allocate_array_with<'a, T: DeviceSync>(
331 &'a self,
332 count: usize,
333 init: impl Fn() -> T,
334 ) -> Result<DmaSliceRegion<T>, TwzError> {
335 let len = core::mem::size_of::<T>() * count;
336 let (ado, range) = self.do_allocate(len)?;
337 let mut reg = DmaSliceRegion::new(
338 len,
339 self.access,
340 self.opts,
341 range.offset(),
342 count,
343 Some((ado.clone(), range)),
344 );
345 reg.fill_with(init);
346 Ok(reg)
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use super::DmaPool;
353 use crate::dma::{Access, DmaOptions};
354
355 #[test]
356 fn allocate() {
357 let pool = DmaPool::new(
358 DmaPool::default_spec(),
359 Access::BiDirectional,
360 DmaOptions::empty(),
361 );
362
363 let _res = pool.allocate(u32::MAX).unwrap();
364 }
365}