forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 462
Add XArray abstraction #1019
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
Draft
matthewtgilbride
wants to merge
37
commits into
Rust-for-Linux:rust-dev
Choose a base branch
from
matthewtgilbride:rust-dev-xarray
base: rust-dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Add XArray abstraction #1019
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
520a56a
rust: allocator: Prevents mis-aligned allocation
fbq 3d8d9bc
rust: make `UnsafeCell` the outer type in `Opaque`
Darksonn 7671527
rust: types: make `Opaque` be `!Unpin`
y86-dev 0ad204c
kbuild: rust_is_available: remove -v option
masahir0y 70a61eb
kbuild: rust_is_available: fix version check when CC has multiple arg…
ruscur a250bde
docs: rust: add paragraph about finding a suitable `libclang`
ojeda 00383f7
kbuild: rust_is_available: print docs reference
ojeda 8b2e73a
kbuild: rust_is_available: add check for `bindgen` invocation
ojeda eb9199d
kbuild: rust_is_available: check that environment variables are set
ojeda 6bed841
kbuild: rust_is_available: fix confusion when a version appears in th…
ojeda 600eb66
kbuild: rust_is_available: normalize version matching
ojeda 6d4220b
kbuild: rust_is_available: handle failures calling `$RUSTC`/`$BINDGEN`
ojeda 10452fc
kbuild: rust_is_available: check that output looks as expected
ojeda 6a4e6dc
kbuild: rust_is_available: add test suite
ojeda f2f2787
rust: alloc: Add realloc and alloc_zeroed to the GlobalAlloc impl
fbq 53dd54f
rust: bindgen: upgrade to 0.65.1
Shinyzenith 02ddf5a
rust: init: make doctests compilable/testable
ojeda 276f14d
rust: str: make doctests compilable/testable
ojeda c30db85
rust: sync: make doctests compilable/testable
ojeda 4009de1
rust: types: make doctests compilable/testable
ojeda e9e95cd
rust: support running Rust documentation tests as KUnit ones
ojeda eff7a66
MAINTAINERS: add Rust KUnit files to the KUnit entry
ojeda 72fbe4a
rust: macros: fix redefine const_name in `vtable`
cqs21 8e87a0d
rust: macros: add `paste!` proc macro
nbdd0121 7c6e564
arm64: rust: Enable Rust support for AArch64
JamieCunliffe 522ba05
arm64: rust: Enable PAC support for Rust.
JamieCunliffe b1555e1
arm64: Restrict Rust support to little endian only.
JamieCunliffe 19b33fc
rust: bindgen: update installation instructions
matthewtgilbride c1c510f
rust: xarray: Add an abstraction for XArray
asahilina 91f4147
rust: xarray: add rudimentary doctest
matthewtgilbride 6c19acb
rust: xarray: add `get_mutable` and `find_mut`
matthewtgilbride 3016eac
rust: xarray: add reservation mechanism
asahilina 90ddba2
Why does this cuase NULL pointer dereference?
matthewtgilbride 702746d
Just me thrashing about - figuring out NPE
matthewtgilbride 976067b
rust: delete `ForeignOwnable::borrow_mut`
Darksonn ac520c2
rust: add improved version of `ForeignOwnable::borrow_mut`
Darksonn 642ff49
rust: xarray: figure out pin and get_scoped
matthewtgilbride File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev
Previous commit
rust: xarray: figure out pin and get_scoped
This needs to be rebased
- Loading branch information
commit 642ff49d21e45075e6d0df9428651c3242639d0c
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | |
---|---|---|---|
|
@@ -7,12 +7,13 @@ | ||
use crate::{ | |||
bindings, | |||
error::{Error, Result}, | |||
prelude::*, | |||
types::{ForeignOwnable, Opaque, ScopeGuard}, | |||
}; | |||
use core::{ | |||
marker::{PhantomData, PhantomPinned}, | |||
pin::Pin, | |||
ptr::NonNull, | |||
ptr::{addr_of_mut, NonNull}, | |||
}; | |||
|
|||
/// Flags passed to `XArray::new` to configure the `XArray`. | |||
|
@@ -50,14 +51,18 @@ pub mod flags { | ||
/// rudimentary read/write operations. | |||
/// | |||
/// ``` | |||
/// use core::{ | |||
/// borrow::Borrow, | |||
/// ops::{ Deref, DerefMut } | |||
/// }; | |||
/// use kernel::{ | |||
/// pin_init, | |||
/// sync::Arc, | |||
/// xarray::{XArray, flags::LOCK_IRQ} | |||
/// xarray::{ XArray, flags::LOCK_IRQ } | |||
/// }; | |||
/// | |||
/// let xarr: Box<XArray<Arc<usize>>> = Box::try_new(XArray::new(LOCK_IRQ))?; | |||
/// let xarr: Pin<Box<XArray<Arc<usize>>>> = Box::into_pin(xarr); | |||
/// let xarr: Pin<&XArray<Arc<usize>>> = xarr.as_ref(); | |||
/// let xarr = Box::pin_init(XArray::<Arc<usize>>::new(LOCK_IRQ)).unwrap(); | |||
/// let xarr = xarr.as_ref(); | |||
/// | |||
/// // `get` and `set` are used to read/write values. | |||
/// assert!(xarr.get(0).is_none()); | |||
|
@@ -82,7 +87,7 @@ pub mod flags { | ||
/// ``` | |||
/// | |||
/// In this example, we create a new `XArray` and demonstrate | |||
/// reading values that are not `Clone`, as well as mutating values. | |||
/// reading and/or mutating values that are not `T::Borrowed<'a>: Into<T>`. | |||
/// | |||
/// ``` | |||
/// use core::{ | |||
|
@@ -91,30 +96,23 @@ pub mod flags { | ||
/// }; | |||
/// use kernel::xarray::{XArray, flags::LOCK_IRQ}; | |||
/// | |||
/// let xarr: Box<XArray<Box<usize>>> = Box::try_new(XArray::new(LOCK_IRQ))?; | |||
/// let mut xarr: Pin<Box<XArray<Box<usize>>>> = Box::into_pin(xarr); | |||
/// let xarr = Box::pin_init(XArray::<Box<usize>>::new(LOCK_IRQ)).unwrap(); | |||
/// let xarr = xarr.as_ref(); | |||
/// | |||
/// // If the type is not `T::Borrowed<'a>: Into<T>`, use `get_scoped` to access a shared reference | |||
/// // from a closure. | |||
/// assert!(xarr.get_scoped(0, |x| x.is_none())); | |||
/// xarr.set(0, Box::try_new(0)?); | |||
/// xarr.get_scoped(0, |x| assert_eq!(*x.unwrap(), 0)); | |||
/// | |||
/// // Create scopes so that references can be dropped | |||
/// // in order to take a new, mutable/immutable ones afterwards. | |||
/// { | |||
/// let xarr: Pin<&XArray<Box<usize>>> = xarr.as_ref(); | |||
/// // If the type is not `Clone`, use `get_shared` to access a shared reference | |||
/// // from a closure. | |||
/// assert!(xarr.get_shared(0, |x| x.is_none())); | |||
/// xarr.set(0, Box::try_new(0)?); | |||
/// xarr.get_shared(0, |x| assert_eq!(*x.unwrap(), 0)); | |||
/// } | |||
/// { | |||
/// let mut xarr = xarr.as_mut(); | |||
/// let mut value = xarr.get_mutable(0).unwrap(); | |||
/// let mut value = value.deref_mut().as_mut(); | |||
/// assert_eq!(value, &mut 0); | |||
/// *value = 1; | |||
/// } | |||
/// { | |||
/// let xarr = xarr.as_ref(); | |||
/// xarr.get_shared(0, |x| assert_eq!(*x.unwrap(), 1)); | |||
/// } | |||
/// // `get_scoped` also gives mutable access inside the closure. | |||
/// xarr.get_scoped(0, |x| { | |||
/// let mut_x = x.unwrap(); | |||
/// assert_eq!(mut_x, &0); | |||
/// *mut_x = 1; | |||
/// }); | |||
/// | |||
/// xarr.get_scoped(0, |x| assert_eq!(*x.unwrap(), 1)); | |||
/// | |||
/// # Ok::<(), Error>(()) | |||
/// ``` | |||
|
@@ -127,39 +125,28 @@ pub struct XArray<T: ForeignOwnable> { | ||
|
|||
impl<T: ForeignOwnable> XArray<T> { | |||
/// Creates a new `XArray` with the given flags. | |||
pub fn new(flags: Flags) -> XArray<T> { | |||
let xa = Opaque::uninit(); | |||
|
|||
// SAFETY: We have just created `xa`. This data structure does not require | |||
// pinning. | |||
unsafe { bindings::xa_init_flags(xa.get(), flags) }; | |||
|
|||
// INVARIANT: Initialize the `XArray` with a valid `xa`. | |||
XArray { | |||
xa, | |||
_p: PhantomData, | |||
_q: PhantomPinned, | |||
pub fn new(flags: Flags) -> impl PinInit<Self> { | |||
unsafe { | |||
kernel::init::pin_init_from_closure(move |slot: *mut XArray<T>| { | |||
bindings::xa_init_flags(Opaque::raw_get(addr_of_mut!((*slot).xa)), flags); | |||
Ok(()) | |||
}) | |||
} | |||
} | |||
|
|||
/// Replaces an entry with a new value, returning the old value (if any). | |||
pub fn replace(self: Pin<&Self>, index: usize, value: T) -> Result<Option<T>> { | |||
pub fn replace(&self, index: u64, value: T) -> Result<Option<T>> { | |||
let new = value.into_foreign(); | |||
// SAFETY: `new` just came from into_foreign(), and we dismiss this guard if | |||
// the xa_store operation succeeds and takes ownership of the pointer. | |||
let guard = ScopeGuard::new(|| unsafe { | |||
T::from_foreign(new); | |||
drop(T::from_foreign(new)); | |||
}); | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant, and we are storing | |||
// a `T::into_foreign()` result which upholds the later invariants. | |||
let old = unsafe { | |||
bindings::xa_store( | |||
self.xa.get(), | |||
index.try_into()?, | |||
new as *mut _, | |||
bindings::GFP_KERNEL, | |||
) | |||
bindings::xa_store(self.xa.get(), index, new as *mut _, bindings::GFP_KERNEL) | |||
}; | |||
|
|||
let ret = unsafe { bindings::xa_err(old) }; | |||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing |
|||
|
@@ -178,47 +165,36 @@ impl<T: ForeignOwnable> XArray<T> { | ||
} | |||
|
|||
/// Replaces an entry with a new value, dropping the old value (if any). | |||
pub fn set(self: Pin<&Self>, index: usize, value: T) -> Result { | |||
pub fn set(&self, index: u64, value: T) -> Result { | |||
self.replace(index, value)?; | |||
Ok(()) | |||
} | |||
|
|||
/// Looks up a reference to an entry in the array, cloning it | |||
/// and returning the cloned value to the user. | |||
/// | |||
pub fn get(self: Pin<&Self>, index: u64) -> Option<T::Borrowed<'_>> | |||
pub fn get(&self, index: u64) -> Option<T> | |||
where | |||
T: Clone, | |||
for<'a> T::BorrowedMut<'a>: Into<T>, | |||
{ | |||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
unsafe { bindings::xa_lock(self.xa.get()) }; | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
let p = unsafe { bindings::xa_load(self.xa.get(), index) } as *const T; | |||
|
|||
let t = NonNull::new(p as *mut T).map(|p| unsafe { T::borrow(p.clone().as_ptr() as _) }); | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
unsafe { bindings::xa_unlock(self.xa.get()) }; | |||
|
|||
t | |||
self.get_scoped(index, |x| x.map(|v| v.into())) | |||
} | |||
|
|||
/// Looks up and a reference to an entry in the array, calling the user | |||
/// provided function on the resulting `Option<&T>` to return a value | |||
/// computed from the reference. Use this function if you need shared | |||
/// access to a `&T` that is not `Clone`. | |||
/// computed from the reference. Use this function if you need | |||
/// (possibly mutable) access to a `&T` that is not `T::Borrowed<'a>: Into<T>`. | |||
/// | |||
pub fn get_shared<F, R>(self: Pin<&Self>, index: u64, f: F) -> R | |||
pub fn get_scoped<F, R>(&self, index: u64, f: F) -> R | |||
where | |||
F: FnOnce(Option<T::Borrowed<'_>>) -> R, | |||
F: FnOnce(Option<T::BorrowedMut<'_>>) -> R, | |||
{ | |||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
unsafe { bindings::xa_lock(self.xa.get()) }; | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
let p = unsafe { bindings::xa_load(self.xa.get(), index) }; | |||
let t = NonNull::new(p as *mut T).map(|p| unsafe { T::borrow(p.as_ptr() as _) }); | |||
let t = NonNull::new(p as *mut T).map(|p| unsafe { T::borrow_mut(p.as_ptr() as _) }); | |||
let r = f(t); | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
|
@@ -227,25 +203,9 @@ impl<T: ForeignOwnable> XArray<T> { | ||
r | |||
} | |||
|
|||
/// Looks up and returns a *mutable* reference to an entry in the array. | |||
pub fn get_mutable(self: Pin<&mut Self>, index: u64) -> Option<ScopeGuard<T, fn(T)>> { | |||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
unsafe { bindings::xa_lock(self.xa.get()) }; | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
let p = unsafe { bindings::xa_load(self.xa.get(), index) }; | |||
|
|||
let t = NonNull::new(p as *mut T).map(|p| unsafe { T::borrow_mut(p.as_ptr() as _) }); | |||
|
|||
// SAFETY: `self.xa` is always valid by the type invariant. | |||
unsafe { bindings::xa_unlock(self.xa.get()) }; | |||
|
|||
t | |||
} | |||
|
|||
/// Removes and returns an entry, returning it if it existed. | |||
pub fn remove(self: Pin<&Self>, index: usize) -> Option<T> { | |||
let p = unsafe { bindings::xa_erase(self.xa.get(), index.try_into().ok()?) }; | |||
pub fn remove(&self, index: u64) -> Option<T> { | |||
let p = unsafe { bindings::xa_erase(self.xa.get(), index) }; | |||
if p.is_null() { | |||
None | |||
} else { | |||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This invariant should be documented.