Skip to main content

storage_device/
buffer_allocator.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::buffer::{
6    Buffer, BufferAllocator as BufferAllocatorTrait, OwnedBuffer, round_down, round_up,
7};
8use event_listener::{Event, EventListener, Listener as _};
9use fuchsia_sync::Mutex;
10use futures::{Future, FutureExt as _};
11use std::collections::BTreeMap;
12use std::ops::Range;
13use std::pin::Pin;
14use std::sync::Arc;
15use std::task::{Context, Poll};
16
17#[cfg(target_os = "fuchsia")]
18mod buffer_source {
19    use fuchsia_runtime::vmar_root_self;
20    use std::ops::Range;
21    use std::sync::Arc;
22    use storage_ptr_slice::MutPtrByteSlice;
23
24    /// A buffer source backed by a VMO.
25    #[derive(Debug)]
26    pub struct BufferSource {
27        base: *mut u8,
28        size: usize,
29        vmo: Arc<zx::Vmo>,
30        trusted: bool,
31    }
32
33    // SAFETY: This is required for the *mut u8 which is just the base address of the VMO mapping
34    // and doesn't stop us making BufferSource Send and Sync.
35    unsafe impl Send for BufferSource {}
36    unsafe impl Sync for BufferSource {}
37
38    impl BufferSource {
39        pub fn new(size: usize) -> Self {
40            Self::new_internal(size, false)
41        }
42
43        pub fn new_trusted(size: usize) -> Self {
44            Self::new_internal(size, true)
45        }
46
47        fn new_internal(size: usize, trusted: bool) -> Self {
48            let mut vmo = zx::Vmo::create(size as u64).unwrap();
49            if trusted {
50                // We strip the TRANSFER right to prevent the VMO handle from being transferred
51                // or duplicated to another process (such as a block device driver). Because the
52                // VMO cannot be shared remotely, we guarantee that this memory is strictly
53                // private and cannot be accessed or modified concurrently by hardware or
54                // drivers. This allows us to safely hand out standard Rust references (&[u8]
55                // and &mut [u8]) without risking undefined behavior from aliasing or concurrent
56                // mutation.
57                let rights = zx::Rights::VMO_DEFAULT & !zx::Rights::TRANSFER;
58                vmo = vmo.replace_handle(rights).unwrap();
59            }
60            let vmo = Arc::new(vmo);
61            let name = zx::Name::new("transfer-buf").unwrap();
62            vmo.set_name(&name).unwrap();
63            let flags = zx::VmarFlags::PERM_READ
64                | zx::VmarFlags::PERM_WRITE
65                | zx::VmarFlags::MAP_RANGE
66                | zx::VmarFlags::REQUIRE_NON_RESIZABLE;
67            let base = vmar_root_self().map(0, &vmo, 0, size, flags).unwrap() as *mut u8;
68            Self { base, size, vmo, trusted }
69        }
70
71        pub fn is_trusted(&self) -> bool {
72            self.trusted
73        }
74
75        pub fn slice(&self) -> *mut [u8] {
76            std::ptr::slice_from_raw_parts_mut(self.base, self.size)
77        }
78
79        pub fn size(&self) -> usize {
80            self.size
81        }
82
83        pub fn vmo(&self) -> &Arc<zx::Vmo> {
84            &self.vmo
85        }
86
87        /// Returns a mutable pointer slice for the given range.
88        ///
89        /// # Safety
90        ///
91        /// The caller must ensure that no other active references or pointer slices overlap with
92        /// this range.
93        pub(crate) unsafe fn subslice_ptr(&self, range: &Range<usize>) -> MutPtrByteSlice<'_> {
94            assert!(range.start < self.size && range.end <= self.size);
95            // SAFETY: The base pointer is valid for `size` bytes, and `range` is within bounds.
96            // The caller guarantees exclusivity.
97            unsafe {
98                MutPtrByteSlice::new(std::ptr::slice_from_raw_parts_mut(
99                    self.base.add(range.start),
100                    range.len(),
101                ))
102            }
103        }
104
105        /// Returns a mutable pointer slice with an arbitrary lifetime `'a` for the given range.
106        ///
107        /// # Safety
108        ///
109        /// The caller must ensure that no other active references or pointer slices overlap with
110        /// this range, and that `self` remains valid and mapped in memory for the entire lifetime
111        /// `'a`.
112        pub(crate) unsafe fn subslice_ptr_unbounded<'a>(
113            &self,
114            range: &Range<usize>,
115        ) -> MutPtrByteSlice<'a> {
116            assert!(range.start < self.size && range.end <= self.size);
117            // SAFETY: The base pointer is valid for `size` bytes, and `range` is within bounds.
118            // The caller guarantees exclusivity and memory liveness for `'a`.
119            unsafe {
120                MutPtrByteSlice::new(std::ptr::slice_from_raw_parts_mut(
121                    self.base.add(range.start),
122                    range.len(),
123                ))
124            }
125        }
126
127        /// Commits the range in memory to avoid future page faults.
128        pub fn commit_range(&self, range: Range<usize>) -> Result<(), zx::Status> {
129            self.vmo.op_range(zx::VmoOp::COMMIT, range.start as u64, range.len() as u64)
130        }
131
132        /// Zeroes out the range so the kerne can reclaim the pages.
133        ///
134        /// # Safety
135        ///
136        /// The range must not be allocated.
137        pub(crate) unsafe fn clean_range(&self, range: Range<usize>) {
138            let _ = self.vmo.op_range(zx::VmoOp::ZERO, range.start as u64, range.len() as u64);
139        }
140    }
141
142    impl Drop for BufferSource {
143        fn drop(&mut self) {
144            // SAFETY: This balances the `map` in `new` above.
145            unsafe {
146                let _ = vmar_root_self().unmap(self.base as usize, self.size);
147            }
148        }
149    }
150}
151
152#[cfg(not(target_os = "fuchsia"))]
153mod buffer_source {
154    use std::cell::UnsafeCell;
155    use std::ops::Range;
156    use std::pin::Pin;
157    use storage_ptr_slice::MutPtrByteSlice;
158
159    /// A basic heap-backed buffer source.
160    #[derive(Debug)]
161    pub struct BufferSource {
162        // We use an UnsafeCell here because we need interior mutability of the buffer (to hand out
163        // mutable slices to it in |buffer()|), but don't want to pay the cost of wrapping the
164        // buffer in a Mutex. We must guarantee that the Buffer objects we hand out don't overlap,
165        // but that is already a requirement for correctness.
166        data: UnsafeCell<Pin<Vec<u8>>>,
167    }
168
169    // Safe because none of the fields in BufferSource are modified, except the contents of `data`,
170    // but that is managed by the BufferAllocator.
171    unsafe impl Sync for BufferSource {}
172
173    impl BufferSource {
174        pub fn new(size: usize) -> Self {
175            Self { data: UnsafeCell::new(Pin::new(vec![0 as u8; size])) }
176        }
177
178        pub fn new_trusted(size: usize) -> Self {
179            Self::new(size)
180        }
181
182        pub fn is_trusted(&self) -> bool {
183            true
184        }
185
186        pub fn size(&self) -> usize {
187            // Safe because the reference goes out of scope as soon as we use it.
188            unsafe { (&*self.data.get()).len() }
189        }
190
191        /// Returns a mutable pointer slice for the given range.
192        ///
193        /// # Safety
194        ///
195        /// The caller must ensure that no other active references or pointer slices overlap with
196        /// this range.
197        pub(super) unsafe fn subslice_ptr(&self, range: &Range<usize>) -> MutPtrByteSlice<'_> {
198            assert!(range.start < self.size() && range.end <= self.size());
199            // SAFETY: The data vector is valid for `size()` bytes, and `range` is within bounds.
200            // The caller guarantees exclusivity.
201            unsafe {
202                let ptr = (&mut *self.data.get()).as_mut_ptr().add(range.start);
203                MutPtrByteSlice::new(std::ptr::slice_from_raw_parts_mut(ptr, range.len()))
204            }
205        }
206
207        /// Returns a mutable pointer slice with an arbitrary lifetime `'a` for the given range.
208        ///
209        /// # Safety
210        ///
211        /// The caller must ensure that no other active references or pointer slices overlap with
212        /// this range, and that `self` remains valid and mapped in memory for the entire lifetime
213        /// `'a`.
214        pub(super) unsafe fn subslice_ptr_unbounded<'a>(
215            &self,
216            range: &Range<usize>,
217        ) -> MutPtrByteSlice<'a> {
218            assert!(range.start < self.size() && range.end <= self.size());
219            // SAFETY: The data vector is valid for `size()` bytes, and `range` is within bounds.
220            // The caller guarantees exclusivity and memory liveness for `'a`.
221            unsafe {
222                let ptr = (&mut *self.data.get()).as_mut_ptr().add(range.start);
223                MutPtrByteSlice::new(std::ptr::slice_from_raw_parts_mut(ptr, range.len()))
224            }
225        }
226
227        /// Zeroes out the range.
228        ///
229        /// # Safety
230        ///
231        /// The range must not be allocated.
232        pub(super) unsafe fn clean_range(&self, range: Range<usize>) {
233            // SAFETY: The caller guarantees the range is not allocated.
234            unsafe { self.subslice_ptr(&range) }.fill(0);
235        }
236    }
237}
238
239pub use buffer_source::BufferSource;
240
241// Stores a list of offsets into a BufferSource. The size of the free ranges is determined by which
242// FreeList we are looking at.
243//
244// FreeLists are kept sorted in descending order of offset (highest offset at the front, lowest
245// offset at the back). This allows `pop()` to remove and return the lowest offset in O(1) time,
246// ensuring the allocator allocates from lowest addresses to highest addresses (first-fit).
247// This guarantees that allocations are packed into lower addresses, and that recently freed lower
248// offsets are prioritized for reuse. `PinnedBufferAllocator` relies on this property to ensure
249// allocations pack into the permanently pinned first chunk before spilling over into dynamically
250// pinned chunks.
251type FreeList = Vec<usize>;
252
253#[derive(Debug)]
254struct Inner {
255    // The index corresponds to the order of free memory blocks in the free list.
256    free_lists: Vec<FreeList>,
257    // Maps offsets to allocated length (the actual length, not the size requested by the client).
258    allocation_map: BTreeMap<usize, usize>,
259}
260
261/// BufferAllocator creates Buffer objects to be used for block device I/O requests.
262///
263/// This is implemented through a simple buddy allocation scheme. Allocations always prioritize
264/// the lowest available offset (first-fit), packing allocations toward the beginning of the
265/// memory pool and reusing recently freed lower offsets.
266#[derive(Debug)]
267pub struct BufferAllocator {
268    block_size: usize,
269    source: BufferSource,
270    inner: Mutex<Inner>,
271    event: Event,
272}
273
274// Returns the smallest order which is at least `size` bytes.
275fn order(size: usize, block_size: usize) -> usize {
276    if size <= block_size {
277        return 0;
278    }
279    let nblocks = round_up(size, block_size) / block_size;
280    nblocks.next_power_of_two().trailing_zeros() as usize
281}
282
283// Returns the largest order which is no more than `size` bytes.
284fn order_fit(size: usize, block_size: usize) -> usize {
285    assert!(size >= block_size);
286    let nblocks = round_up(size, block_size) / block_size;
287    if nblocks.is_power_of_two() {
288        nblocks.trailing_zeros() as usize
289    } else {
290        nblocks.next_power_of_two().trailing_zeros() as usize - 1
291    }
292}
293
294fn size_for_order(order: usize, block_size: usize) -> usize {
295    block_size * (1 << (order as u32))
296}
297
298fn initial_free_lists(size: usize, block_size: usize) -> Vec<FreeList> {
299    let size = round_down(size, block_size);
300    assert!(block_size <= size);
301    assert!(block_size.is_power_of_two());
302    let max_order = order_fit(size, block_size);
303    let mut free_lists = Vec::with_capacity(max_order + 1);
304    for _ in 0..=max_order {
305        free_lists.push(FreeList::new())
306    }
307    let mut offset = 0;
308    while offset < size {
309        let order = order_fit(size - offset, block_size);
310        let size = size_for_order(order, block_size);
311        free_lists[order].push(offset);
312        offset += size;
313    }
314    free_lists
315}
316
317/// A trait for buffer allocators that support asynchronous buffer allocation via event listeners.
318pub trait TryAllocateBuffer<'a> {
319    type Buffer;
320
321    /// Attempts to allocate a buffer of `size` bytes. Returns an [`EventListener`] to wait on if
322    /// memory is temporarily unavailable.
323    fn try_allocate_buffer(&'a self, size: usize) -> Result<Self::Buffer, EventListener>;
324
325    /// Allocates a buffer synchronously, blocking the current thread until memory is available.
326    fn allocate_buffer_sync(&'a self, size: usize) -> Self::Buffer {
327        loop {
328            match self.try_allocate_buffer(size) {
329                Ok(buffer) => return buffer,
330                Err(listener) => listener.wait(),
331            }
332        }
333    }
334}
335
336/// A future which will resolve to an allocated buffer.
337pub struct BufferFuture<'a, A: TryAllocateBuffer<'a> + ?Sized = BufferAllocator> {
338    allocator: &'a A,
339    size: usize,
340    listener: Option<EventListener>,
341}
342
343impl<'a, A: TryAllocateBuffer<'a> + ?Sized> BufferFuture<'a, A> {
344    pub fn new(allocator: &'a A, size: usize) -> Self {
345        Self { allocator, size, listener: None }
346    }
347}
348
349impl<'a, A: TryAllocateBuffer<'a> + ?Sized> Future for BufferFuture<'a, A> {
350    type Output = A::Buffer;
351
352    fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
353        if let Some(listener) = self.listener.as_mut() {
354            futures::ready!(listener.poll_unpin(context));
355        }
356        // Loop because we need to deal with the case where `listener` is ready immediately upon
357        // creation, in which case we ought to retry the allocation.
358        loop {
359            match self.allocator.try_allocate_buffer(self.size) {
360                Ok(buffer) => return Poll::Ready(buffer),
361                Err(mut listener) => {
362                    if listener.poll_unpin(context).is_pending() {
363                        self.listener = Some(listener);
364                        return Poll::Pending;
365                    }
366                }
367            }
368        }
369    }
370}
371
372impl BufferAllocator {
373    pub fn new(block_size: usize, source: BufferSource) -> Self {
374        let free_lists = initial_free_lists(source.size(), block_size);
375        Self {
376            block_size,
377            source,
378            inner: Mutex::new(Inner { free_lists, allocation_map: BTreeMap::new() }),
379            event: Event::new(),
380        }
381    }
382
383    /// Returns the underlying VMO if the allocator is untrusted.
384    ///
385    /// Returns `None` if `is_trusted()` is true, because exposing the VMO would allow
386    /// external modification of memory that is assumed to be unshared.
387    #[cfg(target_os = "fuchsia")]
388    pub fn vmo(&self) -> Option<Arc<zx::Vmo>> {
389        if self.is_trusted() { None } else { Some(self.source.vmo().clone()) }
390    }
391
392    pub fn is_trusted(&self) -> bool {
393        self.source.is_trusted()
394    }
395
396    pub fn block_size(&self) -> usize {
397        self.block_size
398    }
399
400    pub fn buffer_source(&self) -> &BufferSource {
401        &self.source
402    }
403
404    /// Takes the buffer source from the allocator and consumes the allocator.
405    pub fn take_buffer_source(self) -> BufferSource {
406        self.source
407    }
408
409    /// Returns an identifier for this allocator based on its memory address.
410    pub fn identifier(&self) -> usize {
411        std::ptr::from_ref(self).addr()
412    }
413
414    /// Allocates a Buffer with capacity for `size` bytes. Blocks until there are enough bytes
415    /// available to satisfy the request.
416    ///
417    /// The allocated buffer will be block-aligned and the padding up to block alignment can also
418    /// be used by the buffer.
419    ///
420    /// Allocation is O(lg(N) + M), where N = size and M = number of allocations.
421    ///
422    /// # Panics
423    ///
424    /// Panics if `size` exceeds the pool size (`self.buffer_source().size()`).
425    pub fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
426        BufferFuture::new(self, size)
427    }
428
429    /// Allocates a Buffer with capacity for `size` bytes synchronously. Blocks the current thread
430    /// until enough memory is available.
431    ///
432    /// # Panics
433    ///
434    /// Panics if `size` exceeds the pool size (`self.buffer_source().size()`).
435    pub fn allocate_buffer_sync(&self, size: usize) -> Buffer<'_> {
436        <Self as TryAllocateBuffer>::allocate_buffer_sync(self, size)
437    }
438
439    /// Allocates an OwnedBuffer with capacity for `size` bytes synchronously. Blocks the current
440    /// thread until enough memory is available.
441    ///
442    /// # Panics
443    ///
444    /// Panics if `size` exceeds the pool size (`self.buffer_source().size()`).
445    pub fn allocate_buffer_sync_owned(self: &Arc<Self>, size: usize) -> OwnedBuffer {
446        loop {
447            match self.try_allocate_buffer_owned(size) {
448                Ok(buffer) => return buffer,
449                Err(listener) => listener.wait(),
450            }
451        }
452    }
453
454    /// Allocates an OwnedBuffer non-blockingly, returning an EventListener if memory is
455    /// unavailable.
456    ///
457    /// # Panics
458    ///
459    /// Panics if `size` exceeds the pool size (`self.buffer_source().size()`).
460    pub fn try_allocate_buffer_owned(
461        self: &Arc<Self>,
462        size: usize,
463    ) -> Result<OwnedBuffer, EventListener> {
464        let buffer = self.try_allocate_buffer(size)?;
465        let range = buffer.range();
466        // SAFETY: `try_allocate_buffer` guarantees that `range` does not overlap with any other
467        // active allocations. We hold `Arc<Self>` (`self.clone()`), which guarantees that
468        // `self.source` remains valid and mapped in memory for the entire `'static` existence of
469        // `OwnedBuffer`.
470        let slice = unsafe { self.source.subslice_ptr_unbounded(&range) };
471        std::mem::forget(buffer);
472        Ok(OwnedBuffer::new(slice, range, self.clone() as Arc<dyn BufferAllocatorTrait>))
473    }
474
475    /// Like `allocate_buffer`, but returns an EventListener if the allocation cannot be satisfied.
476    /// The listener will signal when the caller should try again.
477    ///
478    /// # Panics
479    ///
480    /// Panics if `size` exceeds the pool size (`self.buffer_source().size()`).
481    pub fn try_allocate_buffer(&self, size: usize) -> Result<Buffer<'_>, EventListener> {
482        if size > self.source.size() {
483            panic!("Allocation of {} bytes would exceed limit {}", size, self.source.size());
484        }
485        let mut inner = self.inner.lock();
486        let requested_order = order(size, self.block_size());
487        assert!(requested_order < inner.free_lists.len());
488        // Pick the smallest possible order with a free entry.
489        let mut order = {
490            let mut idx = requested_order;
491            loop {
492                if idx >= inner.free_lists.len() {
493                    return Err(self.event.listen());
494                }
495                if !inner.free_lists[idx].is_empty() {
496                    break idx;
497                }
498                idx += 1;
499            }
500        };
501
502        // Split the free region until it's the right size.
503        let offset = inner.free_lists[order].pop().unwrap();
504        while order > requested_order {
505            order -= 1;
506            assert!(inner.free_lists[order].is_empty());
507            inner.free_lists[order].push(offset + self.size_for_order(order));
508        }
509
510        inner.allocation_map.insert(offset, self.size_for_order(order));
511        let range = offset..offset + size;
512        log::debug!(range:?, bytes_used = self.size_for_order(order); "Allocated");
513
514        // SAFETY: The allocator guarantees that this range does not overlap with any other
515        // active allocations. `self` guarantees `self.source` remains valid for `'a`.
516        Ok(Buffer::new(unsafe { self.source.subslice_ptr(&range) }, range, self))
517    }
518}
519
520impl BufferAllocatorTrait for BufferAllocator {
521    fn free_buffer(&self, range: Range<usize>) {
522        self.free_buffer(range);
523    }
524
525    fn is_trusted(&self) -> bool {
526        self.is_trusted()
527    }
528
529    #[cfg(target_os = "fuchsia")]
530    fn vmo(&self) -> Option<Arc<zx::Vmo>> {
531        self.vmo()
532    }
533}
534
535impl<'a> TryAllocateBuffer<'a> for BufferAllocator {
536    type Buffer = Buffer<'a>;
537
538    fn try_allocate_buffer(&'a self, size: usize) -> Result<Buffer<'a>, EventListener> {
539        self.try_allocate_buffer(size)
540    }
541}
542
543impl BufferAllocator {
544    /// Deallocation is O(lg(N) + M), where N = size and M = number of allocations.
545    #[doc(hidden)]
546    pub(crate) fn free_buffer(&self, range: Range<usize>) {
547        let mut inner = self.inner.lock();
548        let mut offset = range.start;
549        let size = inner
550            .allocation_map
551            .remove(&offset)
552            .unwrap_or_else(|| panic!("No allocation record found for {:?}", range));
553        assert!(range.end - range.start <= size);
554        log::debug!(range:?, bytes_used = size; "Freeing");
555
556        // Merge as many free slots as we can.
557        let mut order = order(size, self.block_size());
558        while order < inner.free_lists.len() - 1 {
559            let buddy = self.find_buddy(offset, order);
560            let idx = if let Ok(idx) =
561                inner.free_lists[order].binary_search_by(|probe| buddy.cmp(probe))
562            {
563                idx
564            } else {
565                break;
566            };
567            inner.free_lists[order].remove(idx);
568            offset = std::cmp::min(offset, buddy);
569            order += 1;
570        }
571
572        let idx = match inner.free_lists[order].binary_search_by(|probe| offset.cmp(probe)) {
573            Ok(_) => panic!("Unexpectedly found {} in free list {}", offset, order),
574            Err(idx) => idx,
575        };
576        inner.free_lists[order].insert(idx, offset);
577
578        // Notify all stuck tasks.  This might be inefficient, but it's simple and correct.
579        self.event.notify(usize::MAX);
580    }
581
582    fn size_for_order(&self, order: usize) -> usize {
583        size_for_order(order, self.block_size)
584    }
585
586    fn find_buddy(&self, offset: usize, order: usize) -> usize {
587        offset ^ self.size_for_order(order)
588    }
589
590    /// Zeroes out all unnallocated ranges in the transfer buffer.
591    pub fn clean_transfer_buffer(&self) {
592        let inner = self.inner.lock();
593        for (n, free_list) in inner.free_lists.iter().enumerate() {
594            for offset in free_list {
595                // SAFETY: The range is not allocated.
596                unsafe {
597                    self.source.clean_range(*offset..(*offset + size_for_order(n, self.block_size)))
598                };
599            }
600        }
601    }
602}
603
604#[cfg(target_os = "fuchsia")]
605pub use crate::pinned_buffer_allocator::{
606    DEFAULT_PIN_CHUNK_SIZE, PinnedBuffer, PinnedBufferAllocator, PinnedBufferFuture,
607};
608
609#[cfg(test)]
610mod tests {
611    use crate::buffer::BufferAllocator as BufferAllocatorTrait;
612    use crate::buffer_allocator::{BufferAllocator, BufferSource, order};
613    use fuchsia_async as fasync;
614    use futures::future::join_all;
615    use futures::pin_mut;
616    use rand::seq::IndexedRandom as _;
617    use rand::{RngExt as _, rng};
618    use std::sync::Arc;
619    use std::sync::atomic::{AtomicBool, Ordering};
620
621    #[fuchsia::test]
622    async fn test_odd_sized_buffer_source() {
623        let source = BufferSource::new(123);
624        let allocator = BufferAllocator::new(2, source);
625
626        // 123 == 64 + 32 + 16 + 8 + 2 + 1. (The last byte is unusable.)
627        let sizes = vec![64, 32, 16, 8, 2];
628        let mut bufs = vec![];
629        for size in sizes.iter() {
630            bufs.push(allocator.allocate_buffer(*size).await);
631        }
632        for (expected_size, buf) in sizes.iter().zip(bufs.iter()) {
633            assert_eq!(*expected_size, buf.len());
634        }
635        assert!(allocator.try_allocate_buffer(2).is_err());
636    }
637
638    #[fuchsia::test]
639    async fn test_allocate_buffer_read_write() {
640        let source = BufferSource::new(1024 * 1024);
641        let allocator = BufferAllocator::new(8192, source);
642
643        let mut buf = allocator.allocate_buffer(8192).await;
644        buf.fill(0xaa);
645        let mut vec = vec![0 as u8; 8192];
646        buf.copy_to_slice(&mut vec);
647        assert_eq!(vec, vec![0xaa as u8; 8192]);
648    }
649
650    #[fuchsia::test]
651    async fn test_allocate_buffer_consecutive_calls_do_not_overlap() {
652        let source = BufferSource::new(1024 * 1024);
653        let allocator = BufferAllocator::new(8192, source);
654
655        let buf1 = allocator.allocate_buffer(8192).await;
656        let buf2 = allocator.allocate_buffer(8192).await;
657        assert!(buf1.range().end <= buf2.range().start || buf2.range().end <= buf1.range().start);
658    }
659
660    #[fuchsia::test]
661    async fn test_allocate_many_buffers() {
662        let source = BufferSource::new(1024 * 1024);
663        let allocator = BufferAllocator::new(8192, source);
664
665        for _ in 0..10 {
666            let _ = allocator.allocate_buffer(8192).await;
667        }
668    }
669
670    #[fuchsia::test]
671    async fn test_allocate_small_buffers_dont_overlap() {
672        let source = BufferSource::new(1024 * 1024);
673        let allocator = BufferAllocator::new(8192, source);
674
675        let buf1 = allocator.allocate_buffer(1).await;
676        let buf2 = allocator.allocate_buffer(1).await;
677        assert!(buf1.range().end <= buf2.range().start || buf2.range().end <= buf1.range().start);
678    }
679
680    #[fuchsia::test]
681    async fn test_allocate_large_buffer() {
682        let source = BufferSource::new(1024 * 1024);
683        let allocator = BufferAllocator::new(8192, source);
684
685        let mut buf = allocator.allocate_buffer(1024 * 1024).await;
686        assert_eq!(buf.len(), 1024 * 1024);
687        buf.fill(0xaa);
688        let mut vec = vec![0 as u8; 1024 * 1024];
689        buf.copy_to_slice(&mut vec);
690        assert_eq!(vec, vec![0xaa as u8; 1024 * 1024]);
691    }
692
693    #[fuchsia::test]
694    async fn test_allocate_large_buffer_after_smaller_buffers() {
695        let source = BufferSource::new(1024 * 1024);
696        let allocator = BufferAllocator::new(8192, source);
697
698        {
699            let mut buffers = vec![];
700            while let Ok(buffer) = allocator.try_allocate_buffer(8192) {
701                buffers.push(buffer);
702            }
703        }
704        let buf = allocator.allocate_buffer(1024 * 1024).await;
705        assert_eq!(buf.len(), 1024 * 1024);
706    }
707
708    #[fuchsia::test]
709    async fn test_allocate_at_limits() {
710        let source = BufferSource::new(1024 * 1024);
711        let allocator = BufferAllocator::new(8192, source);
712
713        let mut buffers = vec![];
714        while let Ok(buffer) = allocator.try_allocate_buffer(8192) {
715            buffers.push(buffer);
716        }
717        // Deallocate a single buffer, and reallocate a single one back.
718        buffers.pop();
719        let buf = allocator.allocate_buffer(8192).await;
720        assert_eq!(buf.len(), 8192);
721    }
722
723    #[fuchsia::test(threads = 10)]
724    async fn test_random_allocs_deallocs() {
725        let source = BufferSource::new(16 * 1024 * 1024);
726        let bs = 512;
727        let allocator = Arc::new(BufferAllocator::new(bs, source));
728
729        join_all((0..10).map(|_| {
730            let allocator = allocator.clone();
731            fasync::Task::spawn(async move {
732                let mut rng = rng();
733                enum Op {
734                    Alloc,
735                    Dealloc,
736                }
737                let ops = vec![Op::Alloc, Op::Dealloc];
738                let mut buffers = vec![];
739                for _ in 0..1000 {
740                    match ops.choose(&mut rng).unwrap() {
741                        Op::Alloc => {
742                            // Rather than a uniform distribution 1..64K, first pick an order and
743                            // then pick a size within that. For example, we might pick order 3,
744                            // which would give us 8 * 512..16 * 512 as our possible range.
745                            // This way we don't bias towards larger allocations too much.
746                            let order: usize = rng.random_range(order(1, bs)..order(65536 + 1, bs));
747                            let size: usize = rng.random_range(
748                                bs * 2_usize.pow(order as u32)..bs * 2_usize.pow(order as u32 + 1),
749                            );
750                            if let Ok(mut buf) = allocator.try_allocate_buffer(size) {
751                                let val = rng.random::<u8>();
752                                buf.fill(val);
753                                let mut data = vec![0u8; size];
754                                buf.copy_to_slice(&mut data);
755                                for v in &data {
756                                    assert_eq!(v, &val);
757                                }
758                                buffers.push(buf);
759                            }
760                        }
761                        Op::Dealloc if !buffers.is_empty() => {
762                            let idx = rng.random_range(0..buffers.len());
763                            buffers.remove(idx);
764                        }
765                        _ => {}
766                    };
767                }
768            })
769        }))
770        .await;
771    }
772
773    #[fuchsia::test]
774    async fn test_buffer_refs() {
775        let source = BufferSource::new(1024 * 1024);
776        let allocator = BufferAllocator::new(512, source);
777
778        // Allocate one buffer first so that `buf` is not starting at offset 0. This helps catch
779        // bugs.
780        let _buf = allocator.allocate_buffer(512).await;
781        let mut buf = allocator.allocate_buffer(4096).await;
782        let base = buf.range().start;
783        {
784            let mut bref = buf.subslice_mut(1000..2000);
785            assert_eq!(bref.len(), 1000);
786            assert_eq!(bref.range(), base + 1000..base + 2000);
787            bref.fill(0xbb);
788            {
789                let mut bref2 = bref.reborrow().subslice_mut(0..100);
790                assert_eq!(bref2.len(), 100);
791                assert_eq!(bref2.range(), base + 1000..base + 1100);
792                bref2.fill(0xaa);
793            }
794            {
795                let mut bref2 = bref.reborrow().subslice_mut(900..1000);
796                assert_eq!(bref2.len(), 100);
797                assert_eq!(bref2.range(), base + 1900..base + 2000);
798                bref2.fill(0xcc);
799            }
800            let mut data = vec![0u8; 1000];
801            bref.copy_to_slice(&mut data);
802            assert_eq!(data[..100], vec![0xaa; 100]);
803            assert_eq!(data[100..900], vec![0xbb; 800]);
804
805            let bref = bref.subslice_mut(900..);
806            assert_eq!(bref.len(), 100);
807            let mut data = vec![0u8; 100];
808            bref.copy_to_slice(&mut data);
809            assert_eq!(data, vec![0xcc; 100]);
810        }
811        {
812            let bref = buf.as_ref();
813            assert_eq!(bref.len(), 4096);
814            assert_eq!(bref.range(), base..base + 4096);
815            let mut data = vec![0u8; 4096];
816            bref.copy_to_slice(&mut data);
817            assert_eq!(data[0..1000], vec![0x00; 1000]);
818            {
819                let bref2 = bref.subslice(1000..2000);
820                assert_eq!(bref2.len(), 1000);
821                assert_eq!(bref2.range(), base + 1000..base + 2000);
822                let mut data2 = vec![0u8; 1000];
823                bref2.copy_to_slice(&mut data2);
824                assert_eq!(data2[..100], vec![0xaa; 100]);
825                assert_eq!(data2[100..900], vec![0xbb; 800]);
826                assert_eq!(data2[900..1000], vec![0xcc; 100]);
827            }
828
829            let bref = bref.subslice(2048..);
830            assert_eq!(bref.len(), 2048);
831            let mut data = vec![0u8; 2048];
832            bref.copy_to_slice(&mut data);
833            assert_eq!(data, vec![0x00; 2048]);
834        }
835    }
836
837    #[fuchsia::test]
838    async fn test_buffer_split() {
839        let source = BufferSource::new(1024 * 1024);
840        let allocator = BufferAllocator::new(512, source);
841
842        // Allocate one buffer first so that `buf` is not starting at offset 0. This helps catch
843        // bugs.
844        let _buf = allocator.allocate_buffer(512).await;
845        let mut buf = allocator.allocate_buffer(4096).await;
846        let base = buf.range().start;
847        {
848            let bref = buf.as_mut();
849            let (mut s1, mut s2) = bref.split_at_mut(2048);
850            assert_eq!(s1.len(), 2048);
851            assert_eq!(s1.range(), base..base + 2048);
852            s1.fill(0xaa);
853            assert_eq!(s2.len(), 2048);
854            assert_eq!(s2.range(), base + 2048..base + 4096);
855            s2.fill(0xbb);
856        }
857        {
858            let bref = buf.as_ref();
859            let (s1, s2) = bref.split_at(1);
860            let (s2, s3) = s2.split_at(2047);
861            let (s3, s4) = s3.split_at(0);
862            assert_eq!(s1.len(), 1);
863            assert_eq!(s1.range(), base..base + 1);
864            assert_eq!(s2.len(), 2047);
865            assert_eq!(s2.range(), base + 1..base + 2048);
866            assert_eq!(s3.len(), 0);
867            assert_eq!(s3.range(), base + 2048..base + 2048);
868            assert_eq!(s4.len(), 2048);
869            assert_eq!(s4.range(), base + 2048..base + 4096);
870            let mut d1 = vec![0u8; 1];
871            s1.copy_to_slice(&mut d1);
872            assert_eq!(d1, vec![0xaa; 1]);
873            let mut d2 = vec![0u8; 2047];
874            s2.copy_to_slice(&mut d2);
875            assert_eq!(d2, vec![0xaa; 2047]);
876            let mut d3 = vec![0u8; 0];
877            s3.copy_to_slice(&mut d3);
878            assert_eq!(d3, &[] as &[u8]);
879            let mut d4 = vec![0u8; 2048];
880            s4.copy_to_slice(&mut d4);
881            assert_eq!(d4, vec![0xbb; 2048]);
882        }
883    }
884
885    #[fuchsia::test]
886    async fn test_blocking_allocation() {
887        let source = BufferSource::new(1024 * 1024);
888        let allocator = Arc::new(BufferAllocator::new(512, source));
889
890        let buf1 = allocator.allocate_buffer(512 * 1024).await;
891        let buf2 = allocator.allocate_buffer(512 * 1024).await;
892        let bufs_dropped = Arc::new(AtomicBool::new(false));
893
894        // buf3_fut should block until both buf1 and buf2 are done.
895        let allocator_clone = allocator.clone();
896        let bufs_dropped_clone = bufs_dropped.clone();
897        let buf3_fut = async move {
898            allocator_clone.allocate_buffer(1024 * 1024).await;
899            assert!(bufs_dropped_clone.load(Ordering::Relaxed), "Allocation finished early");
900        };
901        pin_mut!(buf3_fut);
902
903        // Each of buf_futs should block until buf3_fut is done, and they should proceed in order.
904        let mut buf_futs = vec![];
905        for _ in 0..16 {
906            let allocator_clone = allocator.clone();
907            let bufs_dropped_clone = bufs_dropped.clone();
908            let fut = async move {
909                allocator_clone.allocate_buffer(64 * 1024).await;
910                // We can't say with certainty that buf3 proceeded first, nor can we ensure these
911                // allocations proceed in order, but we can make sure that at least buf1/buf2 were
912                // done (since they exhausted the pool).
913                assert!(bufs_dropped_clone.load(Ordering::Relaxed), "Allocation finished early");
914            };
915            buf_futs.push(fut);
916        }
917
918        futures::join!(buf3_fut, join_all(buf_futs), async move {
919            std::mem::drop(buf1);
920            std::mem::drop(buf2);
921            bufs_dropped.store(true, Ordering::Relaxed);
922        });
923    }
924
925    #[fuchsia::test]
926    async fn test_clean_entire_transfer_buffer() {
927        const BUFFER_SIZE: usize = 4096;
928        let source = BufferSource::new(BUFFER_SIZE);
929        let allocator = Arc::new(BufferAllocator::new(512, source));
930
931        let mut buf = allocator.allocate_buffer(BUFFER_SIZE).await;
932        buf.fill(0xaa);
933        std::mem::drop(buf);
934
935        allocator.clean_transfer_buffer();
936        let buf = allocator.allocate_buffer(BUFFER_SIZE).await;
937        let mut data = vec![0u8; BUFFER_SIZE];
938        buf.copy_to_slice(&mut data);
939        assert_eq!(data, vec![0; BUFFER_SIZE]);
940    }
941
942    #[fuchsia::test]
943    async fn test_clean_transfer_buffer_around_allocation() {
944        let source = BufferSource::new(4096);
945        let allocator = Arc::new(BufferAllocator::new(512, source));
946
947        let mut buf1 = allocator.allocate_buffer(1024).await;
948        buf1.fill(0xaa);
949        let mut buf2 = allocator.allocate_buffer(1024).await;
950        buf2.fill(0xbb);
951        assert_eq!(buf2.range().start, 1024);
952        let mut buf3 = allocator.allocate_buffer(2048).await;
953        buf3.fill(0xcc);
954        std::mem::drop(buf1);
955        std::mem::drop(buf3);
956
957        allocator.clean_transfer_buffer();
958
959        let buf1 = allocator.allocate_buffer(1024).await;
960        let mut data1 = vec![0u8; 1024];
961        buf1.copy_to_slice(&mut data1);
962        assert_eq!(data1, vec![0; 1024]);
963
964        let mut data2 = vec![0u8; 1024];
965        buf2.copy_to_slice(&mut data2);
966        assert_eq!(data2, vec![0xbb; 1024]);
967
968        let buf3 = allocator.allocate_buffer(2048).await;
969        let mut data3 = vec![0u8; 2048];
970        buf3.copy_to_slice(&mut data3);
971        assert_eq!(data3, vec![0; 2048]);
972    }
973
974    #[fuchsia::test]
975    async fn test_safe_buffer_apis() {
976        let source = BufferSource::new(4096);
977        let allocator = BufferAllocator::new(512, source);
978
979        let mut buf = allocator.allocate_buffer(4096).await;
980
981        // Test copy_from_slice and copy_to_slice
982        let input_data = vec![0x33_u8; 4096];
983        buf.copy_from_slice(&input_data);
984        let mut output_data = vec![0_u8; 4096];
985        buf.copy_to_slice(&mut output_data);
986        assert_eq!(input_data, output_data);
987
988        // Test fill
989        buf.fill(0x55);
990        buf.copy_to_slice(&mut output_data);
991        assert_eq!(output_data, vec![0x55; 4096]);
992
993        // Test subslice
994        {
995            let mut sub_mut = buf.as_mut().subslice_mut(1024..2048);
996            assert_eq!(sub_mut.len(), 1024);
997            sub_mut.fill(0xaa);
998        }
999
1000        {
1001            let bref = buf.as_ref();
1002            let sub_ref = bref.subslice(1024..2048);
1003            assert_eq!(sub_ref.len(), 1024);
1004            let mut sub_output = vec![0_u8; 1024];
1005            sub_ref.copy_to_slice(&mut sub_output);
1006            assert_eq!(sub_output, vec![0xaa; 1024]);
1007        }
1008
1009        // Test split_at and split_at_mut
1010        {
1011            let (mut left_mut, mut right_mut) = buf.as_mut().split_at_mut(2048);
1012            assert_eq!(left_mut.len(), 2048);
1013            assert_eq!(right_mut.len(), 2048);
1014            left_mut.fill(0x11);
1015            right_mut.fill(0x22);
1016        }
1017
1018        {
1019            let bref = buf.as_ref();
1020            let (left_ref, right_ref) = bref.split_at(2048);
1021            let mut left_out = vec![0_u8; 2048];
1022            let mut right_out = vec![0_u8; 2048];
1023            left_ref.copy_to_slice(&mut left_out);
1024            right_ref.copy_to_slice(&mut right_out);
1025            assert_eq!(left_out, vec![0x11; 2048]);
1026            assert_eq!(right_out, vec![0x22; 2048]);
1027        }
1028    }
1029
1030    #[fuchsia::test]
1031    async fn test_owned_buffer() {
1032        let source = BufferSource::new(4096);
1033        let allocator = Arc::new(BufferAllocator::new(512, source));
1034
1035        let mut owned_buf = allocator.allocate_buffer_sync_owned(2048);
1036        assert_eq!(owned_buf.len(), 2048);
1037        owned_buf.as_mut_ptr_slice().fill(0xcc);
1038        assert_eq!(owned_buf.as_ptr_slice().to_vec(), vec![0xcc; 2048]);
1039
1040        // Allocating remaining 2048 bytes should succeed.
1041        let owned_buf2 = allocator.try_allocate_buffer_owned(2048).expect("Must succeed");
1042        assert_eq!(owned_buf2.len(), 2048);
1043
1044        // Pool is full (4096 bytes used). Next allocation should return an EventListener.
1045        assert!(allocator.try_allocate_buffer_owned(512).is_err());
1046
1047        // Dropping owned_buf should free its 2048 bytes back to the allocator.
1048        std::mem::drop(owned_buf);
1049
1050        // Now allocation of 2048 bytes should succeed again.
1051        let mut owned_buf3 = allocator.try_allocate_buffer_owned(2048).expect("Must succeed");
1052        owned_buf3.as_mut_ptr_slice().fill(0xdd);
1053        assert_eq!(owned_buf3.as_ptr_slice().to_vec(), vec![0xdd; 2048]);
1054    }
1055
1056    #[fuchsia::test]
1057    async fn test_allocate_buffer_sync() {
1058        let source = BufferSource::new(4096);
1059        let allocator = BufferAllocator::new(512, source);
1060
1061        let mut buf = allocator.allocate_buffer_sync(2048);
1062        assert_eq!(buf.len(), 2048);
1063        buf.as_mut_ptr_slice().fill(0xee);
1064        assert_eq!(buf.as_ptr_slice().to_vec(), vec![0xee; 2048]);
1065
1066        std::mem::drop(buf);
1067
1068        let mut buf2 = allocator.allocate_buffer_sync(4096);
1069        assert_eq!(buf2.len(), 4096);
1070        buf2.as_mut_ptr_slice().fill(0xff);
1071        assert_eq!(buf2.as_ptr_slice().to_vec(), vec![0xff; 4096]);
1072    }
1073
1074    #[fuchsia::test]
1075    async fn test_trusted_buffer_apis() {
1076        // Untrusted allocator (only relevant/testable on Fuchsia)
1077        #[cfg(target_os = "fuchsia")]
1078        {
1079            let source = BufferSource::new(4096);
1080            let allocator = BufferAllocator::new(512, source);
1081            let mut buf = allocator.allocate_buffer(4096).await;
1082            assert!(buf.try_as_slice().is_none());
1083            assert!(buf.as_mut().try_as_mut_slice().is_none());
1084        }
1085
1086        // Trusted allocator (with trusted source)
1087        {
1088            let source = BufferSource::new_trusted(4096);
1089            let allocator = BufferAllocator::new(512, source);
1090            let mut buf = allocator.allocate_buffer(4096).await;
1091            assert!(buf.try_as_slice().is_some());
1092            assert!(buf.as_mut().try_as_mut_slice().is_some());
1093
1094            // Verify we can actually read/write via slice
1095            let slice = buf.try_as_slice().unwrap();
1096            assert_eq!(slice.len(), 4096);
1097            let mut expected = vec![0u8; 4096];
1098            assert_eq!(slice, expected.as_slice());
1099
1100            let mut bref = buf.as_mut();
1101            let slice_mut = bref.try_as_mut_slice().unwrap();
1102            slice_mut[0] = 0xff;
1103            expected[0] = 0xff;
1104            assert_eq!(buf.try_as_slice().unwrap(), expected.as_slice());
1105        }
1106    }
1107
1108    #[fuchsia::test]
1109    #[cfg(target_os = "fuchsia")]
1110    async fn test_trusted_buffer_rights() {
1111        use zx;
1112        let source = BufferSource::new_trusted(4096);
1113        let vmo = source.vmo();
1114        let info = vmo.basic_info().expect("failed to get basic info");
1115        assert!(!info.rights.contains(zx::Rights::TRANSFER));
1116    }
1117
1118    #[fuchsia::test]
1119    async fn test_allocator_lowest_offset_first() {
1120        let source = BufferSource::new(8192);
1121        let allocator = BufferAllocator::new(512, source);
1122
1123        // Allocate two 1024-byte buffers.
1124        let buf0 = allocator.allocate_buffer(1024).await;
1125        let buf1 = allocator.allocate_buffer(1024).await;
1126        assert_eq!(buf0.range(), 0..1024);
1127        assert_eq!(buf1.range(), 1024..2048);
1128
1129        // Drop buf0. The lower offset (0) is freed.
1130        std::mem::drop(buf0);
1131
1132        // Next allocation should prioritize the lowest available offset (0), not 2048.
1133        let buf0_again = allocator.allocate_buffer(1024).await;
1134        assert_eq!(buf0_again.range(), 0..1024);
1135    }
1136
1137    #[fuchsia::test]
1138    #[cfg(target_os = "fuchsia")]
1139    async fn test_unpinned_buffer() {
1140        let source = BufferSource::new(4096);
1141        let standard_allocator = BufferAllocator::new(512, source);
1142        let unpinned = standard_allocator.allocate_buffer(512).await;
1143        assert_eq!(unpinned.paddrs(), None);
1144        assert_eq!(unpinned.contiguity(), None);
1145    }
1146
1147    #[fuchsia::test]
1148    #[cfg(target_os = "fuchsia")]
1149    async fn test_vmo_trusted_vs_untrusted() {
1150        let untrusted_source = BufferSource::new(4096);
1151        let untrusted_alloc = BufferAllocator::new(512, untrusted_source);
1152        assert!(!untrusted_alloc.is_trusted());
1153        assert!(untrusted_alloc.vmo().is_some());
1154        assert!(BufferAllocatorTrait::vmo(&untrusted_alloc).is_some());
1155        let untrusted_buf = untrusted_alloc.allocate_buffer(512).await;
1156        assert!(untrusted_buf.vmo().is_some());
1157
1158        let trusted_source = BufferSource::new_trusted(4096);
1159        let trusted_alloc = BufferAllocator::new(512, trusted_source);
1160        assert!(trusted_alloc.is_trusted());
1161        assert!(trusted_alloc.vmo().is_none());
1162        assert!(BufferAllocatorTrait::vmo(&trusted_alloc).is_none());
1163        let trusted_buf = trusted_alloc.allocate_buffer(512).await;
1164        assert!(trusted_buf.vmo().is_none());
1165    }
1166}