-
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
Conversation
This allows modules to be initialised in-place in pinned memory, which enables the usage of pinned types (e.g., mutexes, spinlocks, driver registrations, etc.) in modules without any extra allocations. Drivers that don't need this may continue to implement `Module` without any changes. Signed-off-by: Wedson Almeida Filho <[email protected]>
This is a modifid version of rust_minimal that is initialised in-place. Signed-off-by: Wedson Almeida Filho <[email protected]>
Do we expect eventually all modules will just use the in-place interface, or will there always be a use case for the "simple" version? |
There's very little cost to having the "simple" case, so I think we should keep it. If we eventually get to a state where the ergonomics of the pinned one are close enough to the unpinned version (they're a bit cumbersome now, as we can see in the sample), then I think we could reconsider. |
|
||
#[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 comment
The 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 deinit
or exit
method in kernel::Module
.
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 Pin<&mut self>
) are better I think.
We should perhaps consider it.
What do you all think?
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.
There are a few problems with an explicit exit
/deinit
function:
- people could call it like a normal function, so you cannot do things that you can do in
drop
(e.g. drop other things) - we have to blanket impl
Drop
for allModule
s, which would prevent people from adding their owndrop
functionality
We could change module!
to allow custom drop impls, but I feel like keeping it like this makes more sense.
Why do you think that the ergonomics of exit
would be better than PinnedDrop::drop
?
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.
With pin-ini, we have to add (PinnedDrop)
to pin_data
attribute of the module, then we have to add the #[pinned_drop]
attribute to the PinnedDrop
impl block.
These are all magic as far as I can tell with no easy way for someone who knows about regular Drop
impls to find this.
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.
I see your point, however if you have an explicit exit
function on Module
, then that could be called multiple times by user code.
You could make the function unsafe
, but that feels weird to me, since then you could no longer grep for unsafe
to check if they use unsafe
code.
You could of course mimic the #[pinned_drop]
macro that makes sure nobody can call the function accidentally, but then we could also just use the drop approach.
I am not sure how we could improve the situation...
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.
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 exit
function in Module
is necessarily the way to go. I'm just saying we should consider it.
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.
To make it not callable from other places, we can have an extra argument of some type T whose constructor is unsafe.
That is how #[pinned_drop]
currently does things (it adds this parameter to the function). I think it would also be a source of confusion for users to have this visible.
Another thing to consider with the exit
function approach is that we then need to have a blanket impl of Drop
for all modules. Users will not be able to have a normal drop function, which I think is unfortunate, since that will also create confusion.
/// Creates an initialiser for the module. | ||
/// | ||
/// It is called when the module is loaded. | ||
fn init(module: &'static ThisModule) -> error::Result<Self::Init>; |
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:
- the creation of the initializer
- the initialization itself.
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.
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) }) |
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.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
Why is all of this stuff not placed inside of const _: () = { ... };
? That way it is inaccessible to the outside and we can avoid the __
naming (this also allows multiple module!
invocations in the same module).
We must do this, otherwise people can just call __exit()
anywhere (since it is a safe function).
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.
global_asm!
cannot go there, and then others would be forced to go out too. What about a private module? I don't think we need/want several module!
invocations (it could be interesting to consider, though, but it would need wider discussion).
By the way, I don't recall why we did not use a no_mangle
for that static
(prefixing it with the module name or similar).
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.
I think it is fine for global_asm!
to be outside. The functions used by global_asm!
are #[no_mangle]
, so they would not be forced out?
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.
I think eventually we'd want to switch to asm_sym
and stop doing the no_mangle
stuff.
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.
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 const _
instead?
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.
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
const _
instead?
people can still refer to __private_module::exit
and call the exit function. Calling it twice will result in a UAF.
When stuff is inside of const _: () = {...};
then nobody outside of that block can refer to the things inside.
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.
And with asm_sym
, we could create a module inside of the const _
block where we put the global_asm!
.
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.
people can still refer to
__private_module::exit
and call theexit
function.
How? I am talking about making the functions fully private, i.e. private-in-private, which would still export the symbol (no_mangle
/used
).
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.
that also works, but if you need them to be public, you can use const _
. I think it is just better to make them fully inaccessible even if they require unsafe
like the pub extern \"C\" fn __{name}_init()
or pub static __{name}_initcall
.
Also const _
avoids compile errors for people who name their module the same as the macro-generated one.
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.
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. const _: () { ... }
+ a mod
inside for global_asm!
and/or changing the order.
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.
}} | ||
}} | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Does the __exit()
function never get called, when __init
returned non-zero?
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.
Correct. If init
fails, exit
is never called.
@@ -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 comment
The 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.
#[pin_data(PinnedDrop)] | ||
struct RustInPlace { | ||
numbers: Vec<i32>, | ||
} |
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.
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 Mutex
, of coruse sync
would have to land first.
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.
I believe the main motivation for this PR is indeed to be able to use a module-wide Mutex
without having to use ctor tricks?
c9b5ce6
to
ce1c54f
Compare
9ee7197
to
6ce162a
Compare
No description provided.