-
Notifications
You must be signed in to change notification settings - Fork 430
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
Pinned module #997
base: rust-next
Are you sure you want to change the base?
Pinned module #997
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
#![feature(new_uninit)] | ||
#![feature(pin_macro)] | ||
#![feature(receiver_trait)] | ||
#![feature(type_alias_impl_trait)] | ||
#![feature(unsize)] | ||
|
||
// Ensure conditional compilation based on the kernel configuration works; | ||
|
@@ -59,7 +60,7 @@ const __LOG_PREFIX: &[u8] = b"rust_kernel\0"; | |
/// The top level entrypoint to implementing a kernel module. | ||
/// | ||
/// For any teardown or cleanup operations, your type may implement [`Drop`]. | ||
pub trait Module: Sized + Sync { | ||
pub trait Module: Sized + Sync + Send { | ||
/// Called at module initialization time. | ||
/// | ||
/// Use this method to perform whatever setup or registration your module | ||
|
@@ -69,6 +70,34 @@ pub trait Module: Sized + Sync { | |
fn init(module: &'static ThisModule) -> error::Result<Self>; | ||
} | ||
|
||
/// A module that is pinned and initialised in-place. | ||
pub trait InPlaceModule: Sync + Send { | ||
/// The type that implements the pinned initialisation of the module. | ||
type Init: init::PinInit<Self, error::Error>; | ||
|
||
/// Creates an initialiser for the module. | ||
/// | ||
/// It is called when the module is loaded. | ||
fn init(module: &'static ThisModule) -> error::Result<Self::Init>; | ||
} | ||
|
||
impl<T: Module> InPlaceModule for T { | ||
type Init = impl init::PinInit<Self, error::Error>; | ||
|
||
fn init(module: &'static ThisModule) -> error::Result<Self::Init> { | ||
let m = <Self as Module>::init(module)?; | ||
|
||
let initer = move |slot: *mut Self| { | ||
// SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`. | ||
unsafe { slot.write(m) }; | ||
Ok(()) | ||
}; | ||
|
||
// SAFETY: On success, `initer` always fully initialises an instance of `Self`. | ||
Ok(unsafe { init::pin_init_from_closure(initer) }) | ||
Comment on lines
+87
to
+97
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. |
||
} | ||
} | ||
|
||
/// Equivalent to `THIS_MODULE` in the C API. | ||
/// | ||
/// C header: `include/linux/export.h` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -208,7 +208,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { | |
#[used] | ||
static __IS_RUST_MODULE: () = (); | ||
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. Why is all of this stuff not placed inside of We must do this, otherwise people can just call 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.
By the way, I don't recall why we did not use a 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. I think it is fine for 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. I think eventually we'd want to switch to 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. Ah, yeah, it would move it a bit away though. But what is wrong with the private module? Or rather, when would you reach for 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.
people can still refer to When stuff is inside of 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. And with 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.
How? I am talking about making the functions fully private, i.e. private-in-private, which would still export the symbol ( 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. that also works, but if you need them to be public, you can use 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. I support making things as inaccessible as possible, but if the only difference is that we avoid a compile-time error that should not be triggered, then I think simplicity is best vs. I see anonymous modules were considered as an alternative for unnamed constants -- that would have been cleaner for this use case and simpler to understand. |
||
|
||
static mut __MOD: Option<{type_}> = None; | ||
static mut __MOD: core::mem::MaybeUninit<{type_}> = core::mem::MaybeUninit::uninit(); | ||
|
||
// SAFETY: `__this_module` is constructed by the kernel at load time and will not be | ||
// freed until the module is unloaded. | ||
|
@@ -270,23 +270,21 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { | |
}} | ||
|
||
fn __init() -> core::ffi::c_int {{ | ||
match <{type_} as kernel::Module>::init(&THIS_MODULE) {{ | ||
Ok(m) => {{ | ||
unsafe {{ | ||
__MOD = Some(m); | ||
}} | ||
return 0; | ||
}} | ||
Err(e) => {{ | ||
return e.to_errno(); | ||
}} | ||
let initer = match <{type_} as kernel::InPlaceModule>::init(&THIS_MODULE) {{ | ||
Ok(i) => i, | ||
Err(e) => return e.to_errno(), | ||
}}; | ||
|
||
match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{ | ||
Ok(m) => 0, | ||
Err(e) => e.to_errno(), | ||
}} | ||
}} | ||
|
||
fn __exit() {{ | ||
unsafe {{ | ||
// Invokes `drop()` on `__MOD`, which should be used for cleanup. | ||
__MOD = None; | ||
__MOD.assume_init_drop(); | ||
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. Does the 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. Correct. If |
||
}} | ||
}} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o | ||
obj-$(CONFIG_SAMPLE_RUST_INPLACE) += rust_inplace.o | ||
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o | ||
|
||
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
//! Rust minimal in-place sample. | ||
|
||
use kernel::prelude::*; | ||
|
||
module! { | ||
type: RustInPlace, | ||
name: "rust_inplace", | ||
author: "Rust for Linux Contributors", | ||
description: "Rust minimal in-place sample", | ||
license: "GPL", | ||
} | ||
|
||
#[pin_data(PinnedDrop)] | ||
struct RustInPlace { | ||
numbers: Vec<i32>, | ||
} | ||
Comment on lines
+15
to
+18
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. I think it would be beneficial to also put something in here that people can't put into non-in-place modules, e.g. a 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. I believe the main motivation for this PR is indeed to be able to use a module-wide |
||
|
||
impl kernel::InPlaceModule for RustInPlace { | ||
type Init = impl PinInit<Self, Error>; | ||
fn init(_module: &'static ThisModule) -> Result<Self::Init> { | ||
pr_info!("Rust minimal sample (init)\n"); | ||
pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); | ||
|
||
let mut numbers = Vec::new(); | ||
numbers.try_push(72)?; | ||
numbers.try_push(108)?; | ||
numbers.try_push(200)?; | ||
|
||
Ok(try_pin_init!(Self { numbers })) | ||
} | ||
} | ||
|
||
#[pinned_drop] | ||
impl PinnedDrop for RustInPlace { | ||
fn drop(self: Pin<&mut Self>) { | ||
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. We have a few folks suggest that instead of using a drop implementation, that we should do have We decided against it back then, which I think was the right decision. But I think for the pinned/static case,the ergonomics of a method in the module to clean up (which takes a We should perhaps consider it. What do you all think? 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. There are a few problems with an explicit
We could change Why do you think that the ergonomics of 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. With pin-ini, we have to add These are all magic as far as I can tell with no easy way for someone who knows about regular 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. I see your point, however if you have an explicit I am not sure how we could improve the situation... 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. To make it not callable from other places, we can have an extra argument of some type T whose constructor is unsafe. Note, however, that I'm not convinced yet that an 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.
That is how Another thing to consider with the |
||
pr_info!("My numbers are {:?}\n", self.numbers); | ||
pr_info!("Rust minimal inplace sample (exit)\n"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -277,7 +277,7 @@ $(obj)/%.lst: $(src)/%.c FORCE | |
# Compile Rust sources (.rs) | ||
# --------------------------------------------------------------------------- | ||
|
||
rust_allowed_features := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro | ||
rust_allowed_features := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro,type_alias_impl_trait | ||
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. I am not informed on the status of this feature, maybe @bjorn3 knows more? I have seen some general movement in the area (e.g. impl trait in type alias) but no idea how far away this is. |
||
|
||
rust_common_cmd = \ | ||
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \ | ||
|
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.
Why are you using
Result<impl Init<Self, Error>>
here? There are then two places that can fail:Just wanted to check that you actually want that. Not really sure if we want that, I would need to view some examples to decide for myself.