Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utilities for allocating dynamic grants. #2053

Merged
merged 2 commits into from
Sep 17, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
94 changes: 78 additions & 16 deletions kernel/src/grant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::marker::PhantomData;
use core::mem::{align_of, size_of};
use core::ops::{Deref, DerefMut};
use core::ptr::{write, NonNull};
use core::ptr::{slice_from_raw_parts_mut, write, NonNull};

use crate::callback::AppId;
use crate::process::{Error, ProcessType};
Expand Down Expand Up @@ -81,32 +81,94 @@ impl<T: ?Sized> DerefMut for Owned<T> {
}

impl Allocator {
pub fn alloc<T: Default>(&mut self) -> Result<Owned<T>, Error> {
/// Allocates a new owned grant initialized to the given value.
pub fn alloc<T>(&mut self, val: T) -> Result<Owned<T>, Error> {
unsafe {
let ptr = self.alloc_unowned()?;
let ptr = self.alloc_raw()?;

// We use `ptr::write` to avoid `Drop`ping the uninitialized memory in
// case `T` implements the `Drop` trait.
write(ptr.as_ptr(), val);

Ok(Owned::new(ptr, self.appid))
}
}

// Like `alloc`, but the caller is responsible for free-ing the allocated
// memory, as it is not wrapped in a type that implements `Drop`
unsafe fn alloc_unowned<T: Default>(&mut self) -> Result<NonNull<T>, Error> {
/// Allocates a slice of n instances of a given type. Each instance is
/// initialized using the provided function.
///
/// The provided function will be called exactly `n` times, and will be
/// passed the index it's initializing, from `0` through `num_items - 1`.
///
/// # Panic Safety
///
/// If `val_func` panics, the freshly allocated memory and any values
/// already written will be leaked.
pub fn alloc_n_with<T, F>(
&mut self,
num_items: usize,
mut val_func: F,
) -> Result<Owned<[T]>, Error>
where
F: FnMut(usize) -> T,
{
unsafe {
let ptr = self.alloc_n_raw::<T>(num_items)?;

for i in 0..num_items {
write(ptr.as_ptr().add(i), val_func(i));
}

// convert `NonNull<T>` to a fat pointer `NonNull<[T]>` which includes
// the length information. We do this here as initialization is more
// convenient with the non-slice ptr.
let slice_ptr =
NonNull::new(slice_from_raw_parts_mut(ptr.as_ptr(), num_items)).unwrap();

Ok(Owned::new(slice_ptr, self.appid))
}
}

/// Like `alloc`, but the caller is responsible for free-ing the allocated
/// memory, as it is not wrapped in a type that implements `Drop`.
///
/// In contrast to `alloc_raw`, this method does initialize the returned
/// memory.
unsafe fn alloc_default_unowned<T: Default>(&mut self) -> Result<NonNull<T>, Error> {
let ptr = self.alloc_raw()?;

// We use `ptr::write` to avoid `Drop`ping the uninitialized memory in
// case `T` implements the `Drop` trait.
write(ptr.as_ptr(), T::default());

Ok(ptr)
}

/// Allocates uninitialized memory appropriate to store a `T`, and returns a
/// pointer to said memory. The caller is responsible for both initializing the
/// returned memory, and dropping it properly when finished.
unsafe fn alloc_raw<T>(&mut self) -> Result<NonNull<T>, Error> {
self.alloc_n_raw::<T>(1)
}

/// Allocates space for a dynamic number of items. The caller is responsible
/// for initializing and freeing returned memory. Returns memory appropriate
/// for storing `num_items` contiguous instances of `T`.
unsafe fn alloc_n_raw<T>(&mut self, num_items: usize) -> Result<NonNull<T>, Error> {
let alloc_size = size_of::<T>()
.checked_mul(num_items)
.ok_or(Error::OutOfMemory)?;
self.appid
.kernel
.process_map_or(Err(Error::NoSuchApp), self.appid, |process| {
process.alloc(size_of::<T>(), align_of::<T>()).map_or(
Err(Error::OutOfMemory),
|buf| {
process
.alloc(alloc_size, align_of::<T>())
.map_or(Err(Error::OutOfMemory), |buf| {
// Convert untyped `*mut u8` allocation to allocated type
let ptr = NonNull::cast::<T>(buf);

// We use `ptr::write` to avoid `Drop`ping the uninitialized memory in
// case `T` implements the `Drop` trait.
write(ptr.as_ptr(), T::default());

Ok(ptr)
},
)
})
})
}
}
Expand Down Expand Up @@ -223,7 +285,7 @@ impl<T: Default> Grant<T> {
// Note: This allocation is intentionally never
// freed. A grant region is valid once allocated
// for the lifetime of the process.
let new_region = allocator.alloc_unowned()?;
let new_region = allocator.alloc_default_unowned()?;

// Update the grant pointer in the process. Again,
// since the process struct does not know about the
Expand Down