-
Notifications
You must be signed in to change notification settings - Fork 441
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
Improvement to Error #267
Improvement to Error #267
Conversation
So that `Result::expect` can be used. Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
rust/kernel/error.rs
Outdated
/// Kernel pointers can be used to store error values (see | ||
/// [`Error::from_kernel_ptr`]), therefore this function returns `Err` if `ptr | ||
/// indicates an error, otherwise returns `Ok(ptr)`. | ||
#[allow(dead_code)] |
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.
Note: I have to make this as #[allow(dead_code)]
because there is no real user since I separate this from my thread PR #109, this can be removed once we finish our discussion and have real user merged
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.
Thank you for preparing this !
rust/kernel/error.rs
Outdated
/// | ||
/// - [`core::usize::MAX - bindings::MAX_ERRNO`,..,`core::usize::MAX] is the | ||
/// range for error values stored in pointer. | ||
pub fn from_kernel_ptr(ptr: *mut c_types::c_void) -> Option<Error> { |
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.
The kernel pointer we want to convert from isn't necessarily a *mut c_void
, right? It's always a pointer to a bindings::
type, or c_void
. Casts are not idiomatic, so can we avoid the cast by using a template here? E.g. from_kernel_ptr<T>(ptr: *mut T)
.
Also, why have a pub
function that returns Option<Error>
? Where would this be used outside of error.rs
? Would it be simpler to have a single pub ptr_to_result()
function that returns Result<*mut T>
?
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.
The kernel pointer we want to convert from isn't necessarily a
*mut c_void
, right? It's always a pointer to abindings::
type, orc_void
. Casts are not idiomatic, so can we avoid the cast by using a template here? E.g.from_kernel_ptr<T>(ptr: *mut T)
.
Try to avoid template functions when possible. Would unnecessary template functions increase the code size? For example, if we have from_kernel_ptr<i32>
and from_kernel_ptr<i8>
, there will be two functions instead of one.
Also, why have a
pub
function that returnsOption<Error>
? Where would this be used outside oferror.rs
? Would it be simpler to have a singlepub ptr_to_result()
function that returnsResult<*mut T>
?
Oh, I just thought this might be useful, some one may use it in the future. I'm OK if we remove 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.
Try to avoid template functions when possible. Would unnecessary template functions increase the code size? For example, if we have
from_kernel_ptr<i32>
andfrom_kernel_ptr<i8>
, there will be two functions instead of one.
Perhaps the template instantiation would just get optimized away by LLVM? Maybe from_kernel_ptr<i32>
and from_kernel_ptr<i8>
will both result in the same function (inlined or not) which just calls IS_ERR()
and ERR_PTR()
? Or perhaps one or two asm instructions if the kernel helpers are not used.
Maybe that's worth verifying? Premature optimization and roots of all evil etc :)
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 took a quick look at the asm generated for this:
pub fn init_mmio_platform_resource(
pdev: &mut PlatformDevice,
index: u32,
cfg: &RegmapConfig,
) -> Result<Self> {
let iomem = Self::devm_platform_ioremap_resource(pdev, index)?;
Self::devm_regmap_init_mmio(pdev, iomem, cfg)
}
where
fn devm_platform_ioremap_resource(
pdev: &mut PlatformDevice,
index: u32,
) -> Result<*mut c_types::c_void> {
// SAFETY: FFI call.
unsafe {
ptr_err_check(bindings::devm_platform_ioremap_resource(
pdev.to_ptr(),
index,
))
}
}
pub(crate) unsafe fn ptr_err_check<T>(ptr: *mut T) -> Result<*mut T> {
if rust_helper_is_err(ptr as _) {
return Err(Error::from_kernel_errno(
rust_helper_ptr_err(ptr as _) as i32
));
}
Ok(ptr)
}
and it does look like the ptr_err_check<T>()
template function is simply optimized away (together with the Self::
helper):
0001407c <_RNvMs0_NtCsbDqzXfLQacH_6kernel6regmapNtB5_6Regmap27init_mmio_platform_resource>:
1407c: e92d4bf0 push {r4, r5, r6, r7, r8, r9, fp, lr}
14080: e24dd0a8 sub sp, sp, #168 ; 0xa8
14084: e1a04000 mov r4, r0
14088: e5900000 ldr r0, [r0]
1408c: e1a06002 mov r6, r2
14090: ebfffffe bl 0 <devm_platform_ioremap_resource>
14094: e1a05000 mov r5, r0
14098: ebfffffe bl 0 <rust_helper_is_err>
1409c: e3500000 cmp r0, #0
140a0: 1a000029 bne 1414c <_RNvMs0_NtCsbDqzXfLQacH_6kernel6regmapNtB5_6Regmap27init_mmio_platform_resource+0xd0>
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.
Note that I agree we need a template ptr_err_check
or ptr_to_result
, because the return type will use the template parameter, and that will empower us with some strong type checking. I also made ptr_to_result
as a template function.
And if we think that from_kernel_ptr
is worth discussing, I don't think the template parameter will buy us anything, it only appears at the input parameter, and the type information gets lost afterwards. So it's really unnecessary ;-(
Anyway thanks for looking into the generated code to prove I'm wrong ;-)
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.
Anyway thanks for looking into the generated code to prove I'm wrong ;-)
I didn't want to imply that you were wrong - you were asking a question that I didn't know the answer to either (Would unnecessary template functions increase the code size?) and it sounded like an interesting question.
I apologize if it came across that way.
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.
Anyway thanks for looking into the generated code to prove I'm wrong ;-)
I didn't want to imply that you were wrong - you were asking a question that I didn't know the answer to either (Would unnecessary template functions increase the code size?) and it sounded like an interesting question.
I apologize if it came across that way.
No need to apologize ;-) Actually I'm glad you looked into it.
Note that I actually kinda answered my question myself with "For example, if we have from_kernel_ptr and from_kernel_ptr, there will be two functions instead of one.", so literally, I was wrong and you simply showed the answer is opposite.
rust/kernel/error.rs
Outdated
let value = ptr as usize; | ||
|
||
if value >= core::usize::MAX - bindings::MAX_ERRNO as usize { | ||
Some(Error::from_kernel_errno(value as c_types::c_int)) |
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.
Is casting pointers to c_int
idiomatic? And aren't some pointers wider than c_int
? Could we perhaps use the C function provided by the kernel, i.e. PTR_ERR()
? We know it'll always work, even if the kernel encodes the errno
s differently. And it would save us from having to carefully document/prove that these casts are safe.
(edited: added safety comment)
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 done the check on value
, so it's guaranteed that it's safe to convert to c_int
. As for using PTR_ERR()
, as I said in another PR, we would have extra cost on function calls.
rust/kernel/error.rs
Outdated
pub fn from_kernel_ptr(ptr: *mut c_types::c_void) -> Option<Error> { | ||
let value = ptr as usize; | ||
|
||
if value >= core::usize::MAX - bindings::MAX_ERRNO as usize { |
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 this expose a kernel implementation detail (i.e. the way errors are encoded inside pointers)? Would it be more safe to use the kernel function IS_ERR()
here? We know it'll always work, even if the kernel encodes the errno
s differently. And it would save us from having to carefully document/prove that the as usize
casts are safe (i.e. won't overflow).
(edited: added safety comment)
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 tend to agree (and mistakes are easy to make, e.g. see the missing + 1
).
The as usize
should both be fine (the first is from a pointer and the second could be put into a const
variable, which I would do anyway).
rust/kernel/error.rs
Outdated
/// Converts a kernel pointer to a [`Result`]. | ||
/// | ||
/// Kernel pointers can be used to store error values (see | ||
/// [`Error::from_kernel_ptr`]), therefore this function returns `Err` if `ptr |
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.
/// [`Error::from_kernel_ptr`]), therefore this function returns `Err` if `ptr | |
/// [`Error::from_kernel_ptr`]), therefore this function returns `Err` if `ptr` |
rust/kernel/error.rs
Outdated
/// - [0, .., `core::usize::MAX - bindings::MAX_ERRNO`) is the range for | ||
/// normal values of pointer. | ||
/// | ||
/// - [`core::usize::MAX - bindings::MAX_ERRNO`,..,`core::usize::MAX] is the |
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.
/// - [`core::usize::MAX - bindings::MAX_ERRNO`,..,`core::usize::MAX] is the | |
/// - [`core::usize::MAX - bindings::MAX_ERRNO`, ..,`core::usize::MAX`] is the |
rust/kernel/error.rs
Outdated
/// # Pointer value range | ||
/// | ||
/// According to `include/linux/err.h`: | ||
/// - [0, .., `core::usize::MAX - bindings::MAX_ERRNO`) is the range for |
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.
/// - [0, .., `core::usize::MAX - bindings::MAX_ERRNO`) is the range for | |
/// - [`0`, .., `core::usize::MAX - bindings::MAX_ERRNO`) is the range for |
rust/kernel/error.rs
Outdated
pub fn from_kernel_ptr(ptr: *mut c_types::c_void) -> Option<Error> { | ||
let value = ptr as usize; | ||
|
||
if value >= core::usize::MAX - bindings::MAX_ERRNO as usize { |
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.
Is this correct? core::usize::MAX - bindings::MAX_ERRNO as usize
is -4096
, rather than -4095
.
if value >= core::usize::MAX - bindings::MAX_ERRNO as usize { | |
if value >= core::usize::MAX - bindings::MAX_ERRNO as usize + 1 { |
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 you're right. This is indeed an embarrassing bug, and prove the point ;-)
From #254:
|
Yep, I also hope that cross-language LTO could work, I'd like also to try some experiments myself. In the meanwhile, @TheSven73 feel free to submit your version as a PR, I will close this one once your version has been fully reviewed. |
When PRs are as closely reviewed as we're doing here, I feel that they are always co-owned. So I wouldn't like to call it "my version". A new PR just means we try again, from a different starting point. |
"your version" was simply a reference ;-) I could say "the implementation which reuses the Linux functions/macros" but I was lazy typing. Hope you don't mind ;-) |
Don't mind at all :) Alternative suggestion in #268 |
Drop the "ptr-to-err" commit since @TheSven73 already uploaded #268, only keep the commit that makes |
@ojeda could you approve the CI, now I drop the ptr-to-err commit, so only remaining commit is the one that make |
Approved! |
A silly question maybe, but do we want I could be wrong though - maybe there are use cases that I don't know of? |
Given C's We could even think about disallowing In any case, please also note that it is likely that we will need to switch Rust panics to not-actual-kernel-panics, instead killing the current thread or something like that. |
Review of
|
@ojeda Is this PR (with only one commit) OK to merge? I'm thinking about rebasing the thread PR, which depends on this. If there is anything I need to improve for this, please let me know. |
Sure, LGTM. |
It would make more sense to use |
Filed in #287 |
Result::Expect
to workError