Skip to main content

pow2_range_allocator/
lib.rs

1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2016, Google, Inc. All rights reserved
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7//
8// Ported from zircon/kernel/lib/pow2_range_allocator/pow2_range_allocator.cc and
9// zircon/kernel/lib/pow2_range_allocator/include/lib/pow2_range_allocator.h
10
11#![no_std]
12
13use core::convert::Infallible;
14use core::pin::Pin;
15use debug::{ltracef, tracef};
16use fbl::{Array, DoublyLinkedList, DoublyLinkedListContainable, DoublyLinkedListNode};
17use pin_init::{PinInit, pin_data, pin_init, pinned_drop};
18use zx_status::Status;
19
20const LOCAL_TRACE: u32 = 0;
21
22/// Bookkeeping for a single power of 2 sized, power of 2 aligned sub-range.
23#[derive(DoublyLinkedListContainable)]
24struct Block {
25    #[dll_node]
26    node: DoublyLinkedListNode<Block>,
27    bucket: u32,
28    start: u32,
29}
30
31impl Block {
32    const fn new() -> Self {
33        Self { node: DoublyLinkedListNode::new(), bucket: 0, start: 0 }
34    }
35}
36
37/// Bookkeeping for a range of `u32`s which was handed to the allocator via `add_range`.
38#[derive(DoublyLinkedListContainable)]
39struct Range {
40    #[dll_node]
41    node: DoublyLinkedListNode<Range>,
42    start: u32,
43    len: u32,
44}
45
46impl Range {
47    const fn new() -> Self {
48        Self { node: DoublyLinkedListNode::new(), start: 0, len: 0 }
49    }
50}
51
52/// Frees every `Block` remaining in `list`.
53///
54/// The lists hold raw pointers and perform no lifecycle management, so ownership of each element
55/// has to be reclaimed explicitly.
56fn free_block_list(mut list: Pin<&mut DoublyLinkedList<*mut Block>>) {
57    while let Some(block) = list.as_mut().pop_front() {
58        // SAFETY: Every `Block` in one of the allocator's lists was allocated with
59        // `kalloc::Box::try_new` and leaked with `kalloc::Box::into_raw`.  It has just been removed
60        // from `list`, so this is the only pointer to it.
61        drop(unsafe { kalloc::Box::from_raw(block) });
62    }
63}
64
65/// Frees every `Range` remaining in `list`.
66fn free_range_list(mut list: Pin<&mut DoublyLinkedList<*mut Range>>) {
67    while let Some(range) = list.as_mut().pop_front() {
68        // SAFETY: Every `Range` in the allocator's range list was allocated with
69        // `kalloc::Box::try_new` and leaked with `kalloc::Box::into_raw`.  It has just been removed
70        // from `list`, so this is the only pointer to it.
71        drop(unsafe { kalloc::Box::from_raw(range) });
72    }
73}
74
75/// Returns the free bucket list for `bucket`.
76fn free_bucket(
77    buckets: &mut Array<DoublyLinkedList<*mut Block>>,
78    bucket: u32,
79) -> Pin<&mut DoublyLinkedList<*mut Block>> {
80    // SAFETY: The bucket lists are initialized in place in the heap allocation owned by the
81    // `Array`, which never moves its elements.
82    unsafe { Pin::new_unchecked(&mut buckets[bucket as usize]) }
83}
84
85/// All of the mutable state of a `Pow2RangeAllocator`, protected by the allocator's mutex.
86#[pin_data(PinnedDrop)]
87struct Inner {
88    #[pin]
89    ranges: DoublyLinkedList<*mut Range>,
90    #[pin]
91    unused_blocks: DoublyLinkedList<*mut Block>,
92    #[pin]
93    allocated_blocks: DoublyLinkedList<*mut Block>,
94    free_block_buckets: Array<DoublyLinkedList<*mut Block>>,
95    bucket_count: u32,
96}
97
98// SAFETY: The raw pointers held by `Inner`'s lists are exclusively owned by the allocator; they
99// point at `kalloc::Box` allocations which are never handed out and which are only ever reachable
100// with the allocator's mutex held.  Ownership of the whole state can therefore be moved between
101// threads, which is what the C++ implementation does by placing a `Pow2RangeAllocator` in shared
102// storage and serializing all access on its `DECLARE_MUTEX`.
103unsafe impl Send for Inner {}
104
105#[pinned_drop]
106impl PinnedDrop for Inner {
107    fn drop(self: Pin<&mut Self>) {
108        // The C++ implementation has no destructor at all; a `Pow2RangeAllocator` which is
109        // destroyed without calling `Free()` simply leaks its bookkeeping.  Rust's lists hold raw
110        // pointers and debug assert that they are empty when dropped, so the bookkeeping is
111        // released here instead.
112        self.release();
113    }
114}
115
116impl Inner {
117    fn new() -> impl PinInit<Self, Infallible> {
118        pin_init!(Self {
119            ranges <- DoublyLinkedList::<*mut Range>::new(),
120            unused_blocks <- DoublyLinkedList::<*mut Block>::new(),
121            allocated_blocks <- DoublyLinkedList::<*mut Block>::new(),
122            free_block_buckets: Array::new(),
123            bucket_count: 0,
124        })
125    }
126
127    fn init(self: Pin<&mut Self>, bucket_count: u32) -> Result<(), Status> {
128        let mut uninit = kalloc::Box::<[DoublyLinkedList<*mut Block>]>::try_new_uninit_slice(
129            bucket_count as usize,
130        )
131        .map_err(|_| {
132            tracef!("Failed to allocate storage for {} free bucket lists!\n", bucket_count);
133            Status::NO_MEMORY
134        })?;
135        for item in uninit.iter_mut() {
136            let init = DoublyLinkedList::<*mut Block>::new();
137            // SAFETY: `item.as_mut_ptr()` is a valid, writable pointer to uninitialized memory
138            // in the newly allocated slice.
139            unsafe {
140                let _ = init.__pinned_init(item.as_mut_ptr());
141            }
142        }
143        // SAFETY: All elements in `uninit` were initialized in the loop above.
144        let buf = unsafe { uninit.assume_init() };
145        let this = self.project();
146        *this.free_block_buckets = Array::from_box(buf);
147        *this.bucket_count = bucket_count;
148        Ok(())
149    }
150
151    /// Releases every piece of bookkeeping still held by the allocator.
152    fn release(self: Pin<&mut Self>) {
153        let this = self.project();
154        free_range_list(this.ranges);
155        free_block_list(this.unused_blocks);
156        free_block_list(this.allocated_blocks);
157        for bucket in this.free_block_buckets.iter_mut() {
158            // SAFETY: The bucket lists live in the heap allocation owned by the `Array`, which
159            // never moves its elements.
160            free_block_list(unsafe { Pin::new_unchecked(bucket) });
161        }
162    }
163
164    /// Returns a block of bookkeeping, recycling one from the unused list if possible.
165    ///
166    /// Returns `None` if a new block had to be allocated and the allocation failed.
167    fn get_unused_block(self: Pin<&mut Self>) -> Option<*mut Block> {
168        let unused_blocks = self.project().unused_blocks;
169        if !unused_blocks.is_empty() {
170            return unused_blocks.pop_front();
171        }
172
173        match kalloc::Box::try_new(Block::new()) {
174            Ok(block) => Some(kalloc::Box::into_raw(block)),
175            Err(_) => None,
176        }
177    }
178
179    /// Returns `block` to its proper free bucket, merging with its buddy as many times as possible.
180    ///
181    /// # Safety
182    ///
183    /// `block` must be a valid pointer to a `Block` owned by this allocator which is not currently
184    /// a member of any list.
185    unsafe fn return_free_block(mut self: Pin<&mut Self>, mut block: *mut Block) {
186        // The C++ implementation recurses into `ReturnFreeBlock` after a successful merge.  This is
187        // the same algorithm written as a loop.
188        loop {
189            let this = self.as_mut().project();
190            debug_assert!(!block.is_null());
191            // SAFETY: The caller guarantees that `block` points at a valid `Block` which is not in
192            // any container.  On subsequent loop iterations `block` was just erased from a bucket.
193            let block_ref = unsafe { &*block };
194            debug_assert!(block_ref.bucket < *this.bucket_count);
195            debug_assert!(!block_ref.node.in_container());
196
197            let bucket = block_ref.bucket;
198            let block_start = block_ref.start;
199            let block_len = 1u32 << bucket;
200            debug_assert_eq!(block_start & (block_len - 1), 0);
201
202            // Return the block to its proper free bucket, sorted by base ID.  Start by
203            // finding the block which should come after this block in the list.
204            let mut list = free_bucket(this.free_block_buckets, bucket);
205            let mut inserted = false;
206            {
207                let mut cursor = list.as_mut().cursor_front_mut();
208                while let Some(after) = cursor.get() {
209                    // We do not allow ranges to overlap.
210                    let after_len = 1u32 << after.bucket;
211                    let after_start = after.start;
212                    debug_assert!(
213                        (block_start >= after_start.wrapping_add(after_len))
214                            || (after_start >= block_start.wrapping_add(block_len))
215                    );
216
217                    if after_start > block_start {
218                        // SAFETY: `block` is a valid pointer to a `Block` which is not in any
219                        // container, and it is owned by this allocator so it outlives its
220                        // membership in the list.
221                        unsafe { cursor.insert_before_raw(block) };
222                        inserted = true;
223                        break;
224                    }
225                    cursor.move_next();
226                }
227            }
228
229            // If no block comes after this one, it goes on the end of the list.
230            if !inserted {
231                // SAFETY: See the `insert_before_raw` call above.
232                unsafe { list.as_mut().push_back_raw(block) };
233            }
234
235            // After this point, the bucket list owns |block|.
236
237            // Don't merge blocks in the largest bucket.
238            if bucket + 1 == *this.bucket_count {
239                return;
240            }
241
242            // Check to see if we should be merging this block into a larger aligned block.
243            let (first, second) = if (block_start & ((block_len << 1) - 1)) != 0 {
244                // Odd alignment.  This might be the second block of a merge pair.
245                // SAFETY: `block` was just inserted into `list`.
246                let mut cursor = unsafe { list.as_mut().cursor_at(&*block) };
247                cursor.move_prev();
248                let first = cursor.get().map(|b| core::ptr::from_ref(b).cast_mut());
249                (first, Some(block))
250            } else {
251                // Even alignment.  This might be the first block of a merge pair.
252                // SAFETY: `block` was just inserted into `list`.
253                let mut cursor = unsafe { list.as_mut().cursor_at(&*block) };
254                cursor.move_next();
255                let second = cursor.get().map(|b| core::ptr::from_ref(b).cast_mut());
256                (Some(block), second)
257            };
258
259            // Do these chunks fit together?
260            let (Some(first), Some(second)) = (first, second) else {
261                return;
262            };
263
264            // SAFETY: `first` and `second` are both members of `list` and therefore valid.
265            let (first_bucket, first_start, second_bucket, second_start) =
266                unsafe { ((*first).bucket, (*first).start, (*second).bucket, (*second).start) };
267            let first_len = 1u32 << first_bucket;
268            if first_start.wrapping_add(first_len) != second_start {
269                return;
270            }
271            debug_assert_eq!(first_bucket, second_bucket);
272
273            // Remove the two blocks' bookkeeping from their bucket.
274            // SAFETY: Both `first` and `second` are valid and are currently members of `list`.
275            unsafe {
276                let _ = list.as_mut().erase(&*first);
277                let _ = list.erase(&*second);
278            }
279
280            // Place one half of the bookkeeping back on the unused list.
281            // SAFETY: `second` was just erased from `list`, so it is in no container.
282            unsafe { this.unused_blocks.push_back_raw(second) };
283
284            // Reuse the other half to track the newly merged block, and place
285            // it in the next bucket size up.
286            // SAFETY: `first` was just erased from `list` and is still owned by this allocator.
287            unsafe { (*first).bucket += 1 };
288            block = first;
289        }
290    }
291
292    fn add_range(
293        mut self: Pin<&mut Self>,
294        mut range_start: u32,
295        mut range_len: u32,
296    ) -> Result<(), Status> {
297        for range in self.ranges.iter() {
298            if ((range.start >= range_start) && (range.start < range_start.wrapping_add(range_len)))
299                || ((range_start >= range.start)
300                    && (range_start < range.start.wrapping_add(range.len)))
301            {
302                tracef!(
303                    "Range [{}, {}] overlaps with existing range [{}, {}].\n",
304                    range_start,
305                    range_start.wrapping_add(range_len).wrapping_sub(1),
306                    range.start,
307                    range.start.wrapping_add(range.len).wrapping_sub(1)
308                );
309                return Err(Status::ALREADY_EXISTS);
310            }
311        }
312
313        // Allocate our range state.
314        let Ok(mut new_range) = kalloc::Box::try_new(Range::new()) else {
315            return Err(Status::NO_MEMORY);
316        };
317        new_range.start = range_start;
318        new_range.len = range_len;
319
320        // Break the range we were given into power of two aligned chunks, and place
321        // them on the new blocks list to be added to the free-blocks buckets.
322        debug_assert!(self.bucket_count != 0);
323        debug_assert!(!self.free_block_buckets.is_empty());
324        pin_init::stack_pin_init!(let new_blocks = DoublyLinkedList::<*mut Block>::new());
325
326        let mut bucket = self.bucket_count - 1;
327        let mut csize = 1u32 << bucket;
328        let max_csize = csize;
329        while range_len != 0 {
330            // Shrink the chunk size until it is aligned with the start of the
331            // range, and not larger than the number of irqs we have left.
332            let mut shrunk = false;
333            while ((range_start & (csize - 1)) != 0) || (range_len < csize) {
334                csize >>= 1;
335                bucket -= 1;
336                shrunk = true;
337            }
338
339            // If we didn't need to shrink the chunk size, perhaps we can grow it
340            // instead.
341            if !shrunk {
342                let mut tmp = csize << 1;
343                while (tmp <= max_csize) && (tmp <= range_len) && ((range_start & (tmp - 1)) == 0) {
344                    bucket += 1;
345                    csize = tmp;
346                    tmp <<= 1;
347                    debug_assert!(bucket < self.bucket_count);
348                }
349            }
350
351            // Break off a chunk of the range.
352            debug_assert_eq!(1u32 << bucket, csize);
353            debug_assert!(bucket < self.bucket_count);
354            debug_assert_eq!(range_start & (csize - 1), 0);
355            debug_assert!(csize <= range_len);
356            debug_assert!(csize != 0);
357
358            let Some(block) = self.as_mut().get_unused_block() else {
359                tracef!(
360                    "WARNING! Failed to allocate block bookkeeping with sub-range [{}, {}] still left to track.\n",
361                    range_start,
362                    range_start.wrapping_add(range_len).wrapping_sub(1)
363                );
364                free_block_list(new_blocks.as_mut());
365                return Err(Status::NO_MEMORY);
366            };
367
368            // SAFETY: `block` was just handed to us by `get_unused_block`, so it is valid and is
369            // not a member of any list.
370            unsafe {
371                (*block).bucket = bucket;
372                (*block).start = range_start;
373                new_blocks.as_mut().push_back_raw(block);
374            }
375
376            range_start += csize;
377            range_len -= csize;
378        }
379
380        // Looks like we managed to allocate everything we needed to.  Go ahead and
381        // add all of our newly allocated bookkeeping to the state.
382        // SAFETY: `new_range` was just allocated and is a member of no list.
383        unsafe { self.as_mut().project().ranges.push_back_raw(kalloc::Box::into_raw(new_range)) };
384
385        while let Some(block) = new_blocks.as_mut().pop_front() {
386            // SAFETY: `block` was just popped from `new_blocks` and is owned by this allocator.
387            unsafe { self.as_mut().return_free_block(block) };
388        }
389
390        Ok(())
391    }
392
393    fn allocate_range(mut self: Pin<&mut Self>, orig_bucket: u32) -> Result<u32, Status> {
394        // Find the smallest sized chunk which can hold the allocation and is
395        // compatible with the requested addressing capabilities.
396        let mut bucket = orig_bucket;
397        let mut block: Option<*mut Block> = None;
398        while bucket < self.bucket_count {
399            block = free_bucket(self.as_mut().project().free_block_buckets, bucket).pop_front();
400            if block.is_some() {
401                break;
402            }
403            bucket += 1;
404        }
405
406        // Nothing found, unlock and get out.
407        let Some(block) = block else {
408            return Err(Status::NO_RESOURCES);
409        };
410
411        // Looks like we have a chunk which can satisfy this allocation request.
412        // Split it as many times as needed to match the requested size.
413        // SAFETY: `block` was just popped off of a free bucket, so it is valid and in no list.
414        debug_assert_eq!(unsafe { (*block).bucket }, bucket);
415        debug_assert!(bucket >= orig_bucket);
416
417        while bucket > orig_bucket {
418            // If we failed to allocate bookkeeping for the split block, put the block
419            // we failed to split back into the free list (merging if required),
420            // then fail the allocation.
421            let Some(split_block) = self.as_mut().get_unused_block() else {
422                tracef!(
423                    "Failed to allocate free bookkeeping block when attempting to split for allocation\n"
424                );
425                // SAFETY: `block` is valid and is a member of no list.
426                unsafe { self.as_mut().return_free_block(block) };
427                return Err(Status::NO_MEMORY);
428            };
429
430            debug_assert!(bucket != 0);
431            bucket -= 1;
432
433            // SAFETY: Both `block` and `split_block` are valid and are members of no list.
434            unsafe {
435                // Cut the first chunk in half.
436                (*block).bucket = bucket;
437
438                // Fill out the bookkeeping for the second half of the chunk.
439                (*split_block).start = (*block).start + (1u32 << (*block).bucket);
440                (*split_block).bucket = bucket;
441
442                // Return the second half of the chunk to the free pool.
443                self.as_mut().return_free_block(split_block);
444            }
445        }
446
447        // Success! Mark the block as allocated and return the block to the user.
448        // SAFETY: `block` is valid and is a member of no list.
449        let range_start = unsafe { (*block).start };
450        // SAFETY: `block` is valid and is a member of no list.
451        unsafe { self.project().allocated_blocks.push_front_raw(block) };
452
453        Ok(range_start)
454    }
455
456    fn free_range(mut self: Pin<&mut Self>, range_start: u32, bucket: u32) {
457        // In a debug build, find the specific block being returned in the list of
458        // allocated blocks and use it as the bookkeeping for returning to the free
459        // bucket.  Because this is an O(n) operation, and serves only as an integrity
460        // check, we only do this in debug builds.  In release builds, we just grab
461        // any piece of bookkeeping memory off the allocated_blocks list and use
462        // that instead.
463        //
464        // The C++ implementation selects between the two arms with
465        // `#if DEBUG_ASSERT_IMPLEMENTED`.  Rust's `debug_assertions` gates `debug_assert!` in
466        // exactly the same way that `DEBUG_ASSERT_IMPLEMENTED` gates `DEBUG_ASSERT`, so it is used
467        // here to keep the port self consistent.
468        let allocated_blocks = self.as_mut().project().allocated_blocks;
469        let block: Option<*mut Block> = if cfg!(debug_assertions) {
470            allocated_blocks.erase_if(|candidate| {
471                (candidate.start == range_start) && (candidate.bucket == bucket)
472            })
473        } else {
474            let block = allocated_blocks.pop_front();
475            if let Some(block) = block {
476                // SAFETY: `block` was just popped off of `allocated_blocks`.
477                unsafe {
478                    (*block).start = range_start;
479                    (*block).bucket = bucket;
480                }
481            }
482            block
483        };
484
485        let block = block.expect("no matching allocated block to free");
486
487        // Return the block to the free buckets (merging as needed) and we are done.
488        // SAFETY: `block` was just removed from `allocated_blocks`, so it is valid and is a member
489        // of no list.
490        unsafe { self.return_free_block(block) };
491    }
492}
493
494/// `Pow2RangeAllocator` is a small utility class which partitions a set of
495/// ranges of integers into sub-ranges which are power of 2 in length and power
496/// of 2 aligned and then manages allocating and freeing the subranges for
497/// clients.  It is responsible for breaking larger sub-regions into smaller ones
498/// as needed for allocation, and for merging sub-regions into larger sub-regions
499/// as needed during free operations.
500///
501/// Its primary use is as a utility library for platforms who need to manage
502/// allocating blocks MSI IRQ IDs on behalf of the PCI bus driver, but could (in
503/// theory) be used for other things).
504#[ksync::guarded]
505pub struct Pow2RangeAllocator {
506    #[guarded_by(lock)]
507    #[pin]
508    inner: Inner,
509
510    #[mutex]
511    lock: ksync::KMutex,
512}
513
514// Like the C++ implementation, a `Pow2RangeAllocator` is meant to live in shared storage and to
515// serialize all of its access on its own mutex.
516const _: fn() = || {
517    fn assert_sync<T: Sync + ?Sized>() {}
518    assert_sync::<Pow2RangeAllocator>();
519};
520
521impl Pow2RangeAllocator {
522    /// Creates a new, uninitialized `Pow2RangeAllocator`.
523    ///
524    /// `init` must be called before any range can be added or allocated.
525    pub fn new() -> impl PinInit<Self, Infallible> {
526        pin_init!(Self {
527            inner <- ksync::kcell_init(Inner::new()),
528            lock <- ksync::KMutex::init(),
529        })
530    }
531
532    /// Initialize the state of a pow2 range allocator.
533    ///
534    /// `max_alloc_size` is the maximum size of a single contiguous allocation.  It must be a power
535    /// of 2.
536    ///
537    /// Returns a status code indicating the success or failure of the operation.
538    /// Possible return values include
539    /// ++ `ZX_ERR_INVALID_ARGS` `max_alloc_size` is zero or not a power of 2.
540    /// ++ `ZX_ERR_NO_MEMORY` Not enough memory to allocate the storage for free bucket lists.
541    pub fn init(&self, max_alloc_size: u32) -> Result<(), Status> {
542        if (max_alloc_size == 0) || !max_alloc_size.is_power_of_two() {
543            tracef!("max_alloc_size ({}) is not an integer power of two!\n", max_alloc_size);
544            return Err(Status::INVALID_ARGS);
545        }
546
547        let bucket_count = max_alloc_size.ilog2() + 1;
548
549        ksync::lock!(let mut guard = self.lock_lock());
550        let inner = guard.as_mut().inner_mut();
551        inner.init(bucket_count)
552    }
553
554    /// Free all of the state associated with a previously initialized pow2 range allocator.
555    pub fn free(&self) {
556        ksync::lock!(let mut guard = self.lock_lock());
557        let inner = guard.as_mut().inner_mut();
558
559        debug_assert!(inner.bucket_count != 0);
560        debug_assert!(!inner.free_block_buckets.is_empty());
561        debug_assert!(inner.allocated_blocks.is_empty());
562
563        inner.release();
564    }
565
566    /// Add a range of `u32`s to the pool of ranges to be allocated.
567    ///
568    /// `range_start` is the start of the `u32` range and `range_len` is its length.
569    ///
570    /// Returns a status code indicating the success or failure of the operation.
571    /// Possible return values include
572    /// ++ `ZX_ERR_INVALID_ARGS` range_len is zero, or would cause the range to wrap the
573    ///    maximum range of a `u32`.
574    /// ++ `ZX_ERR_ALREADY_EXISTS` the specified range overlaps with a range already added
575    ///    to the allocator.
576    /// ++ `ZX_ERR_NO_MEMORY` Not enough memory to allocate the bookkeeping required for
577    ///    managing the range.
578    pub fn add_range(&self, range_start: u32, range_len: u32) -> Result<(), Status> {
579        ltracef!(
580            "Adding range [{}, {}]\n",
581            range_start,
582            range_start.wrapping_add(range_len).wrapping_sub(1)
583        );
584
585        if (range_len == 0) || (range_start.wrapping_add(range_len) < range_start) {
586            return Err(Status::INVALID_ARGS);
587        }
588
589        // Enter the lock and check for overlap with pre-existing ranges.
590        ksync::lock!(let mut guard = self.lock_lock());
591        let inner = guard.as_mut().inner_mut();
592        inner.add_range(range_start, range_len)
593    }
594
595    /// Attempt to allocate a range of `u32`s from the available sub-ranges.  The
596    /// size of the allocated range must be a power of 2, and if the allocation
597    /// succeeds, it is guaranteed to be aligned on a power of 2 boundary matching its
598    /// size.
599    ///
600    /// `size` is the requested size of the region.  On success, the start of the allocated range is
601    /// returned.
602    ///
603    /// Possible error values include
604    /// ++ `ZX_ERR_INVALID_ARGS` Multiple reasons, including...
605    ///    ++ size is zero.
606    ///    ++ size is not a power of two.
607    /// ++ `ZX_ERR_NO_RESOURCES` No contiguous, aligned region could be found to satisfy
608    ///    the allocation request.
609    /// ++ `ZX_ERR_NO_MEMORY` A region could be found, but memory required for bookkeeping
610    ///    could not be allocated.
611    pub fn allocate_range(&self, size: u32) -> Result<u32, Status> {
612        if (size == 0) || !size.is_power_of_two() {
613            tracef!("Size ({}) is not an integer power of 2.\n", size);
614            return Err(Status::INVALID_ARGS);
615        }
616
617        let orig_bucket = size.ilog2();
618
619        // Lock state during allocation.
620        ksync::lock!(let mut guard = self.lock_lock());
621        let inner = guard.as_mut().inner_mut();
622
623        if orig_bucket >= inner.bucket_count {
624            tracef!(
625                "Invalid size ({}).  Valid sizes are integer powers of 2 from [1, {}]\n",
626                size,
627                1u32.checked_shl(inner.bucket_count.wrapping_sub(1)).unwrap_or(0)
628            );
629            return Err(Status::INVALID_ARGS);
630        }
631
632        inner.allocate_range(orig_bucket)
633    }
634
635    /// Free a range previously allocated using `allocate_range`.
636    ///
637    /// `range_start` is the start of the previously allocated range and `size` is its size.
638    pub fn free_range(&self, range_start: u32, size: u32) {
639        debug_assert!((size != 0) && size.is_power_of_two());
640
641        let bucket = size.ilog2();
642
643        ksync::lock!(let mut guard = self.lock_lock());
644        let inner = guard.as_mut().inner_mut();
645        inner.free_range(range_start, bucket);
646    }
647}