From: Miguel Ojeda <[email protected]>
This is v2 of the first batch of changes to upstream the rest of
the Rust support. v1 is at:
https://lore.kernel.org/rust-for-linux/[email protected]/
I collected the tags and applied the agreed changes. The full diff
follows (with respect to v1), since they are minor enough.
I will be applying the patches tomorrow, since v1 has had more than
three weeks of review time.
diff --git a/rust/build_error.rs b/rust/build_error.rs
index 0ff6b33059aa..fa24eeef9929 100644
--- a/rust/build_error.rs
+++ b/rust/build_error.rs
@@ -2,19 +2,26 @@
//! Build-time error.
//!
-//! This crate provides a function `build_error`, which will panic in
-//! compile-time if executed in const context, and will cause a build error
-//! if not executed at compile time and the optimizer does not optimise away the
-//! call.
+//! This crate provides a [const function][const-functions] `build_error`, which will panic in
+//! compile-time if executed in [const context][const-context], and will cause a build error
+//! if not executed at compile time and the optimizer does not optimise away the call.
//!
//! It is used by `build_assert!` in the kernel crate, allowing checking of
//! conditions that could be checked statically, but could not be enforced in
-//! Rust yet (e.g. perform some checks in const functions, but those
+//! Rust yet (e.g. perform some checks in [const functions][const-functions], but those
//! functions could still be called in the runtime).
+//!
+//! For details on constant evaluation in Rust, please see the [Reference][const-eval].
+//!
+//! [const-eval]: https://doc.rust-lang.org/reference/const_eval.html
+//! [const-functions]: https://doc.rust-lang.org/reference/const_eval.html#const-functions
+//! [const-context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
#![no_std]
-/// Panics if executed in const context, or triggers a build error if not.
+/// Panics if executed in [const context][const-context], or triggers a build error if not.
+///
+/// [const-context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
#[inline(never)]
#[cold]
#[export_name = "rust_build_error"]
diff --git a/rust/kernel/std_vendor.rs b/rust/kernel/std_vendor.rs
index da57b4e521f4..b3e68b24a8c6 100644
--- a/rust/kernel/std_vendor.rs
+++ b/rust/kernel/std_vendor.rs
@@ -35,9 +35,12 @@
/// This is useful when debugging issues that only occur in release
/// builds or when debugging in release mode is significantly faster.
///
-/// Note that the macro is intended as a debugging tool and therefore you
-/// should avoid having uses of it in version control for long periods
-/// (other than in tests and similar).
+/// Note that the macro is intended as a temporary debugging tool to be
+/// used during development. Therefore, avoid committing `dbg!` macro
+/// invocations into the kernel tree.
+///
+/// For debug output that is intended to be kept in the kernel tree,
+/// use [`pr_debug`] and similar facilities instead.
///
/// # Stability
///
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index ffac633423db..b771310fa4a4 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -129,18 +129,6 @@ impl CStr {
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
}
- /// Creates a [`CStr`] from a `[u8]`, panic if input is not valid.
- ///
- /// This function is only meant to be used by `c_str!` macro, so
- /// crates using `c_str!` macro don't have to enable `const_panic` feature.
- #[doc(hidden)]
- pub const fn from_bytes_with_nul_unwrap(bytes: &[u8]) -> &Self {
- match Self::from_bytes_with_nul(bytes) {
- Ok(v) => v,
- Err(_) => panic!("string contains interior NUL"),
- }
- }
-
/// Creates a [`CStr`] from a `[u8]` without performing any additional
/// checks.
///
@@ -349,7 +337,10 @@ where
macro_rules! c_str {
($str:expr) => {{
const S: &str = concat!($str, "\0");
- const C: &$crate::str::CStr = $crate::str::CStr::from_bytes_with_nul_unwrap(S.as_bytes());
+ const C: &$crate::str::CStr = match $crate::str::CStr::from_bytes_with_nul(S.as_bytes()) {
+ Ok(v) => v,
+ Err(_) => panic!("string contains interior NUL"),
+ };
C
}};
}
diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs
index 3b5a9dd70e8a..7e4b450f3a50 100644
--- a/rust/macros/concat_idents.rs
+++ b/rust/macros/concat_idents.rs
@@ -18,6 +18,6 @@ pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
assert_eq!(expect_punct(&mut it), ',');
let b = expect_ident(&mut it);
assert!(it.next().is_none(), "only two idents can be concatenated");
- let res = Ident::new(&(a.to_string() + &b.to_string()), b.span());
+ let res = Ident::new(&format!("{a}{b}"), b.span());
TokenStream::from_iter([TokenTree::Ident(res)])
}
Björn Roy Baron (1):
rust: macros: add `concat_idents!` proc macro
Finn Behrens (1):
rust: error: declare errors using macro
Gary Guo (9):
rust: macros: add `#[vtable]` proc macro
rust: macros: take string literals in `module!`
rust: str: add `BStr` type
rust: str: add `b_str!` macro
rust: str: add `CStr` type
rust: str: implement several traits for `CStr`
rust: str: add `c_str!` macro
rust: add `build_error` crate
rust: build_assert: add `build_{error,assert}!` macros
Miguel Ojeda (7):
rust: prelude: split re-exports into groups
rust: print: add more `pr_*!` levels
rust: print: add `pr_cont!` macro
rust: samples: add `rust_print` example
rust: alloc: add `RawVec::try_with_capacity_in()` constructor
rust: alloc: add `Vec::try_with_capacity{,_in}()` constructors
rust: static_assert: add `static_assert!` macro
Milan Landaverde (1):
rust: str: add `CStr` unit tests
Niklas Mohrin (1):
rust: std_vendor: add `dbg!` macro based on `std`'s one
Viktor Garske (1):
rust: error: add codes from `errno-base.h`
Wedson Almeida Filho (7):
rust: error: add `From` implementations for `Error`
rust: prelude: add `error::code::*` constant items
rust: str: add `Formatter` type
rust: str: add `CString` type
rust: str: add `fmt!` macro
rust: types: add `Either` type
rust: types: add `Opaque` type
lib/Kconfig.debug | 16 +
rust/Makefile | 22 +-
rust/alloc/raw_vec.rs | 33 +-
rust/alloc/vec/mod.rs | 89 +++++
rust/build_error.rs | 31 ++
rust/exports.c | 5 +
rust/kernel/build_assert.rs | 82 +++++
rust/kernel/error.rs | 90 ++++-
rust/kernel/lib.rs | 9 +
rust/kernel/prelude.rs | 20 +-
rust/kernel/print.rs | 214 +++++++++++-
rust/kernel/static_assert.rs | 34 ++
rust/kernel/std_vendor.rs | 163 ++++++++++
rust/kernel/str.rs | 523 +++++++++++++++++++++++++++++-
rust/kernel/types.rs | 37 +++
rust/macros/concat_idents.rs | 23 ++
rust/macros/helpers.rs | 24 +-
rust/macros/lib.rs | 108 +++++-
rust/macros/module.rs | 10 +-
rust/macros/vtable.rs | 95 ++++++
samples/rust/Kconfig | 10 +
samples/rust/Makefile | 1 +
samples/rust/rust_minimal.rs | 8 +-
samples/rust/rust_print.rs | 54 +++
scripts/generate_rust_analyzer.py | 8 +-
25 files changed, 1667 insertions(+), 42 deletions(-)
create mode 100644 rust/build_error.rs
create mode 100644 rust/kernel/build_assert.rs
create mode 100644 rust/kernel/static_assert.rs
create mode 100644 rust/kernel/std_vendor.rs
create mode 100644 rust/kernel/types.rs
create mode 100644 rust/macros/concat_idents.rs
create mode 100644 rust/macros/vtable.rs
create mode 100644 samples/rust/rust_print.rs
base-commit: f0c4d9fc9cc9462659728d168387191387e903cc
--
2.38.1
From: Finn Behrens <[email protected]>
Add a macro to declare errors, which simplifies the work needed to
add each one, avoids repetition of the code and makes it easier to
change the way they are declared.
Signed-off-by: Finn Behrens <[email protected]>
Reviewed-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/error.rs | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 466b2a8fe569..b843f3445483 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -8,8 +8,16 @@ use alloc::collections::TryReserveError;
/// Contains the C-compatible error codes.
pub mod code {
- /// Out of memory.
- pub const ENOMEM: super::Error = super::Error(-(crate::bindings::ENOMEM as i32));
+ macro_rules! declare_err {
+ ($err:tt $(,)? $($doc:expr),+) => {
+ $(
+ #[doc = $doc]
+ )*
+ pub const $err: super::Error = super::Error(-(crate::bindings::$err as i32));
+ };
+ }
+
+ declare_err!(ENOMEM, "Out of memory.");
}
/// Generic integer kernel error.
--
2.38.1
From: Niklas Mohrin <[email protected]>
The Rust standard library has a really handy macro, `dbg!` [1,2].
It prints the source location (filename and line) along with the raw
source code that is invoked with and the `Debug` representation
of the given expression, e.g.:
let a = 2;
let b = dbg!(a * 2) + 1;
// ^-- prints: [src/main.rs:2] a * 2 = 4
assert_eq!(b, 5);
Port the macro over to the `kernel` crate inside a new module
called `std_vendor`, using `pr_info!` instead of `eprintln!` and
make the rules about committing uses of `dbg!` into version control
more concrete (i.e. tailored for the kernel).
Since the source code for the macro is taken from the standard
library source (with only minor adjustments), the new file is
licensed under `Apache 2.0 OR MIT`, just like the original [3,4].
Link: https://doc.rust-lang.org/std/macro.dbg.html [1]
Link: https://github.com/rust-lang/rust/blob/master/library/std/src/macros.rs#L212 [2]
Link: https://github.com/rust-lang/rust/blob/master/library/std/Cargo.toml [3]
Link: https://github.com/rust-lang/rust/blob/master/COPYRIGHT [4]
Signed-off-by: Niklas Mohrin <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/lib.rs | 2 +
rust/kernel/prelude.rs | 2 +-
rust/kernel/std_vendor.rs | 163 ++++++++++++++++++++++++++++++++++++++
3 files changed, 166 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/std_vendor.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index ffc6626a6d29..d6371c9c8453 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -26,6 +26,8 @@ mod allocator;
pub mod error;
pub mod prelude;
pub mod print;
+#[doc(hidden)]
+pub mod std_vendor;
pub mod str;
#[doc(hidden)]
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 89c2c9f4e7a7..345fc9075d1f 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -17,7 +17,7 @@ pub use alloc::{boxed::Box, vec::Vec};
pub use macros::{module, vtable};
-pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
+pub use super::{dbg, pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::error::{code::*, Error, Result};
diff --git a/rust/kernel/std_vendor.rs b/rust/kernel/std_vendor.rs
new file mode 100644
index 000000000000..b3e68b24a8c6
--- /dev/null
+++ b/rust/kernel/std_vendor.rs
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//! The contents of this file come from the Rust standard library, hosted in
+//! the <https://github.com/rust-lang/rust> repository, licensed under
+//! "Apache-2.0 OR MIT" and adapted for kernel use. For copyright details,
+//! see <https://github.com/rust-lang/rust/blob/master/COPYRIGHT>.
+
+/// [`std::dbg`], but using [`pr_info`] instead of [`eprintln`].
+///
+/// Prints and returns the value of a given expression for quick and dirty
+/// debugging.
+///
+/// An example:
+///
+/// ```rust
+/// let a = 2;
+/// # #[allow(clippy::dbg_macro)]
+/// let b = dbg!(a * 2) + 1;
+/// // ^-- prints: [src/main.rs:2] a * 2 = 4
+/// assert_eq!(b, 5);
+/// ```
+///
+/// The macro works by using the `Debug` implementation of the type of
+/// the given expression to print the value with [`printk`] along with the
+/// source location of the macro invocation as well as the source code
+/// of the expression.
+///
+/// Invoking the macro on an expression moves and takes ownership of it
+/// before returning the evaluated expression unchanged. If the type
+/// of the expression does not implement `Copy` and you don't want
+/// to give up ownership, you can instead borrow with `dbg!(&expr)`
+/// for some expression `expr`.
+///
+/// The `dbg!` macro works exactly the same in release builds.
+/// This is useful when debugging issues that only occur in release
+/// builds or when debugging in release mode is significantly faster.
+///
+/// Note that the macro is intended as a temporary debugging tool to be
+/// used during development. Therefore, avoid committing `dbg!` macro
+/// invocations into the kernel tree.
+///
+/// For debug output that is intended to be kept in the kernel tree,
+/// use [`pr_debug`] and similar facilities instead.
+///
+/// # Stability
+///
+/// The exact output printed by this macro should not be relied upon
+/// and is subject to future changes.
+///
+/// # Further examples
+///
+/// With a method call:
+///
+/// ```rust
+/// # #[allow(clippy::dbg_macro)]
+/// fn foo(n: usize) {
+/// if dbg!(n.checked_sub(4)).is_some() {
+/// // ...
+/// }
+/// }
+///
+/// foo(3)
+/// ```
+///
+/// This prints to the kernel log:
+///
+/// ```text,ignore
+/// [src/main.rs:4] n.checked_sub(4) = None
+/// ```
+///
+/// Naive factorial implementation:
+///
+/// ```rust
+/// # #[allow(clippy::dbg_macro)]
+/// # {
+/// fn factorial(n: u32) -> u32 {
+/// if dbg!(n <= 1) {
+/// dbg!(1)
+/// } else {
+/// dbg!(n * factorial(n - 1))
+/// }
+/// }
+///
+/// dbg!(factorial(4));
+/// # }
+/// ```
+///
+/// This prints to the kernel log:
+///
+/// ```text,ignore
+/// [src/main.rs:3] n <= 1 = false
+/// [src/main.rs:3] n <= 1 = false
+/// [src/main.rs:3] n <= 1 = false
+/// [src/main.rs:3] n <= 1 = true
+/// [src/main.rs:4] 1 = 1
+/// [src/main.rs:5] n * factorial(n - 1) = 2
+/// [src/main.rs:5] n * factorial(n - 1) = 6
+/// [src/main.rs:5] n * factorial(n - 1) = 24
+/// [src/main.rs:11] factorial(4) = 24
+/// ```
+///
+/// The `dbg!(..)` macro moves the input:
+///
+/// ```ignore
+/// /// A wrapper around `usize` which importantly is not Copyable.
+/// #[derive(Debug)]
+/// struct NoCopy(usize);
+///
+/// let a = NoCopy(42);
+/// let _ = dbg!(a); // <-- `a` is moved here.
+/// let _ = dbg!(a); // <-- `a` is moved again; error!
+/// ```
+///
+/// You can also use `dbg!()` without a value to just print the
+/// file and line whenever it's reached.
+///
+/// Finally, if you want to `dbg!(..)` multiple values, it will treat them as
+/// a tuple (and return it, too):
+///
+/// ```
+/// # #[allow(clippy::dbg_macro)]
+/// assert_eq!(dbg!(1usize, 2u32), (1, 2));
+/// ```
+///
+/// However, a single argument with a trailing comma will still not be treated
+/// as a tuple, following the convention of ignoring trailing commas in macro
+/// invocations. You can use a 1-tuple directly if you need one:
+///
+/// ```
+/// # #[allow(clippy::dbg_macro)]
+/// # {
+/// assert_eq!(1, dbg!(1u32,)); // trailing comma ignored
+/// assert_eq!((1,), dbg!((1u32,))); // 1-tuple
+/// # }
+/// ```
+///
+/// [`std::dbg`]: https://doc.rust-lang.org/std/macro.dbg.html
+/// [`eprintln`]: https://doc.rust-lang.org/std/macro.eprintln.html
+/// [`printk`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html
+#[macro_export]
+macro_rules! dbg {
+ // NOTE: We cannot use `concat!` to make a static string as a format argument
+ // of `pr_info!` because `file!` could contain a `{` or
+ // `$val` expression could be a block (`{ .. }`), in which case the `pr_info!`
+ // will be malformed.
+ () => {
+ $crate::pr_info!("[{}:{}]\n", ::core::file!(), ::core::line!())
+ };
+ ($val:expr $(,)?) => {
+ // Use of `match` here is intentional because it affects the lifetimes
+ // of temporaries - https://stackoverflow.com/a/48732525/1063961
+ match $val {
+ tmp => {
+ $crate::pr_info!("[{}:{}] {} = {:#?}\n",
+ ::core::file!(), ::core::line!(), ::core::stringify!($val), &tmp);
+ tmp
+ }
+ }
+ };
+ ($($val:expr),+ $(,)?) => {
+ ($($crate::dbg!($val)),+,)
+ };
+}
--
2.38.1
From: Miguel Ojeda <[email protected]>
Currently, only `pr_info!` (for the minimal sample) and
`pr_emerg!` (for the panic handler) are there.
Add the other levels as new macros, i.e. `pr_alert!`, `pr_crit!`,
`pr_err!`, `pr_warn!`, `pr_notice!` and `pr_debug!`.
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Gary Guo <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Reviewed-by: Boqun Feng <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
Reviewed-by: Sergio Gonzalez Collado <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 2 +-
rust/kernel/print.rs | 154 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index f8219285d8c0..6a1c6b38327f 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -17,7 +17,7 @@ pub use alloc::{boxed::Box, vec::Vec};
pub use macros::module;
-pub use super::{pr_emerg, pr_info};
+pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::error::{Error, Result};
diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
index 55db5a1ba752..694f51c6da5c 100644
--- a/rust/kernel/print.rs
+++ b/rust/kernel/print.rs
@@ -74,7 +74,13 @@ pub mod format_strings {
// Furthermore, `static` instead of `const` is used to share the strings
// for all the kernel.
pub static EMERG: [u8; LENGTH] = generate(false, bindings::KERN_EMERG);
+ pub static ALERT: [u8; LENGTH] = generate(false, bindings::KERN_ALERT);
+ pub static CRIT: [u8; LENGTH] = generate(false, bindings::KERN_CRIT);
+ pub static ERR: [u8; LENGTH] = generate(false, bindings::KERN_ERR);
+ pub static WARNING: [u8; LENGTH] = generate(false, bindings::KERN_WARNING);
+ pub static NOTICE: [u8; LENGTH] = generate(false, bindings::KERN_NOTICE);
pub static INFO: [u8; LENGTH] = generate(false, bindings::KERN_INFO);
+ pub static DEBUG: [u8; LENGTH] = generate(false, bindings::KERN_DEBUG);
}
/// Prints a message via the kernel's [`_printk`].
@@ -172,6 +178,126 @@ macro_rules! pr_emerg (
)
);
+/// Prints an alert-level message (level 1).
+///
+/// Use this level if action must be taken immediately.
+///
+/// Equivalent to the kernel's [`pr_alert`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_alert`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_alert
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_alert!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_alert (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::ALERT, $($arg)*)
+ )
+);
+
+/// Prints a critical-level message (level 2).
+///
+/// Use this level for critical conditions.
+///
+/// Equivalent to the kernel's [`pr_crit`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_crit`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_crit
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_crit!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_crit (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::CRIT, $($arg)*)
+ )
+);
+
+/// Prints an error-level message (level 3).
+///
+/// Use this level for error conditions.
+///
+/// Equivalent to the kernel's [`pr_err`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_err`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_err
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_err!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_err (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::ERR, $($arg)*)
+ )
+);
+
+/// Prints a warning-level message (level 4).
+///
+/// Use this level for warning conditions.
+///
+/// Equivalent to the kernel's [`pr_warn`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_warn`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_warn
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_warn!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_warn (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::WARNING, $($arg)*)
+ )
+);
+
+/// Prints a notice-level message (level 5).
+///
+/// Use this level for normal but significant conditions.
+///
+/// Equivalent to the kernel's [`pr_notice`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_notice`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_notice
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_notice!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_notice (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::NOTICE, $($arg)*)
+ )
+);
+
/// Prints an info-level message (level 6).
///
/// Use this level for informational messages.
@@ -196,3 +322,31 @@ macro_rules! pr_info (
$crate::print_macro!($crate::print::format_strings::INFO, $($arg)*)
)
);
+
+/// Prints a debug-level message (level 7).
+///
+/// Use this level for debug messages.
+///
+/// Equivalent to the kernel's [`pr_debug`] macro, except that it doesn't support dynamic debug
+/// yet.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_debug`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_debug
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_debug!("hello {}\n", "there");
+/// ```
+#[macro_export]
+#[doc(alias = "print")]
+macro_rules! pr_debug (
+ ($($arg:tt)*) => (
+ if cfg!(debug_assertions) {
+ $crate::print_macro!($crate::print::format_strings::DEBUG, $($arg)*)
+ }
+ )
+);
--
2.38.1
From: Björn Roy Baron <[email protected]>
This macro provides similar functionality to the unstable feature
`concat_idents` without having to rely on it.
For instance:
let x_1 = 42;
let x_2 = concat_idents!(x, _1);
assert!(x_1 == x_2);
It has different behavior with respect to macro hygiene. Unlike
the unstable `concat_idents!` macro, it allows, for example,
referring to local variables by taking the span of the second
macro as span for the output identifier.
Signed-off-by: Björn Roy Baron <[email protected]>
Reviewed-by: Finn Behrens <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/macros/concat_idents.rs | 23 +++++++++++++++++++
rust/macros/lib.rs | 44 ++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 rust/macros/concat_idents.rs
diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs
new file mode 100644
index 000000000000..7e4b450f3a50
--- /dev/null
+++ b/rust/macros/concat_idents.rs
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use proc_macro::{token_stream, Ident, TokenStream, TokenTree};
+
+use crate::helpers::expect_punct;
+
+fn expect_ident(it: &mut token_stream::IntoIter) -> Ident {
+ if let Some(TokenTree::Ident(ident)) = it.next() {
+ ident
+ } else {
+ panic!("Expected Ident")
+ }
+}
+
+pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
+ let mut it = ts.into_iter();
+ let a = expect_ident(&mut it);
+ assert_eq!(expect_punct(&mut it), ',');
+ let b = expect_ident(&mut it);
+ assert!(it.next().is_none(), "only two idents can be concatenated");
+ let res = Ident::new(&format!("{a}{b}"), b.span());
+ TokenStream::from_iter([TokenTree::Ident(res)])
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 91764bfb1f89..15555e7ff487 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -2,6 +2,7 @@
//! Crate for all kernel procedural macros.
+mod concat_idents;
mod helpers;
mod module;
@@ -70,3 +71,46 @@ use proc_macro::TokenStream;
pub fn module(ts: TokenStream) -> TokenStream {
module::module(ts)
}
+
+/// Concatenate two identifiers.
+///
+/// This is useful in macros that need to declare or reference items with names
+/// starting with a fixed prefix and ending in a user specified name. The resulting
+/// identifier has the span of the second argument.
+///
+/// # Examples
+///
+/// ```ignore
+/// use kernel::macro::concat_idents;
+///
+/// macro_rules! pub_no_prefix {
+/// ($prefix:ident, $($newname:ident),+) => {
+/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
+/// };
+/// }
+///
+/// pub_no_prefix!(
+/// binder_driver_return_protocol_,
+/// BR_OK,
+/// BR_ERROR,
+/// BR_TRANSACTION,
+/// BR_REPLY,
+/// BR_DEAD_REPLY,
+/// BR_TRANSACTION_COMPLETE,
+/// BR_INCREFS,
+/// BR_ACQUIRE,
+/// BR_RELEASE,
+/// BR_DECREFS,
+/// BR_NOOP,
+/// BR_SPAWN_LOOPER,
+/// BR_DEAD_BINDER,
+/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
+/// BR_FAILED_REPLY
+/// );
+///
+/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
+/// ```
+#[proc_macro]
+pub fn concat_idents(ts: TokenStream) -> TokenStream {
+ concat_idents::concat_idents(ts)
+}
--
2.38.1
From: Viktor Garske <[email protected]>
Only a few codes were added so far. With the `declare_err!`
macro in place, add the remaining ones (which is most of them)
from `include/uapi/asm-generic/errno-base.h`.
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Viktor Garske <[email protected]>
Reviewed-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/error.rs | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index b843f3445483..861746f2422d 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -17,7 +17,40 @@ pub mod code {
};
}
+ declare_err!(EPERM, "Operation not permitted.");
+ declare_err!(ENOENT, "No such file or directory.");
+ declare_err!(ESRCH, "No such process.");
+ declare_err!(EINTR, "Interrupted system call.");
+ declare_err!(EIO, "I/O error.");
+ declare_err!(ENXIO, "No such device or address.");
+ declare_err!(E2BIG, "Argument list too long.");
+ declare_err!(ENOEXEC, "Exec format error.");
+ declare_err!(EBADF, "Bad file number.");
+ declare_err!(ECHILD, "Exec format error.");
+ declare_err!(EAGAIN, "Try again.");
declare_err!(ENOMEM, "Out of memory.");
+ declare_err!(EACCES, "Permission denied.");
+ declare_err!(EFAULT, "Bad address.");
+ declare_err!(ENOTBLK, "Block device required.");
+ declare_err!(EBUSY, "Device or resource busy.");
+ declare_err!(EEXIST, "File exists.");
+ declare_err!(EXDEV, "Cross-device link.");
+ declare_err!(ENODEV, "No such device.");
+ declare_err!(ENOTDIR, "Not a directory.");
+ declare_err!(EISDIR, "Is a directory.");
+ declare_err!(EINVAL, "Invalid argument.");
+ declare_err!(ENFILE, "File table overflow.");
+ declare_err!(EMFILE, "Too many open files.");
+ declare_err!(ENOTTY, "Not a typewriter.");
+ declare_err!(ETXTBSY, "Text file busy.");
+ declare_err!(EFBIG, "File too large.");
+ declare_err!(ENOSPC, "No space left on device.");
+ declare_err!(ESPIPE, "Illegal seek.");
+ declare_err!(EROFS, "Read-only file system.");
+ declare_err!(EMLINK, "Too many links.");
+ declare_err!(EPIPE, "Broken pipe.");
+ declare_err!(EDOM, "Math argument out of domain of func.");
+ declare_err!(ERANGE, "Math result not representable.");
}
/// Generic integer kernel error.
--
2.38.1
From: Gary Guo <[email protected]>
Add `c_str!`, which is a convenience macro that creates a new `CStr`
from a string literal.
It is designed to be similar to a `str` in usage, and it is usable
in const contexts, for instance:
const X: &CStr = c_str!("Example");
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 3ed685cb5a3c..a995db36486f 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -321,6 +321,29 @@ where
}
}
+/// Creates a new [`CStr`] from a string literal.
+///
+/// The string literal should not contain any `NUL` bytes.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::c_str;
+/// # use kernel::str::CStr;
+/// const MY_CSTR: &CStr = c_str!("My awesome CStr!");
+/// ```
+#[macro_export]
+macro_rules! c_str {
+ ($str:expr) => {{
+ const S: &str = concat!($str, "\0");
+ const C: &$crate::str::CStr = match $crate::str::CStr::from_bytes_with_nul(S.as_bytes()) {
+ Ok(v) => v,
+ Err(_) => panic!("string contains interior NUL"),
+ };
+ C
+ }};
+}
+
#[cfg(test)]
mod tests {
use super::*;
--
2.38.1
From: Miguel Ojeda <[email protected]>
Add example to exercise the printing macros (`pr_*!`) introduced
in the previous patches.
Reviewed-by: Finn Behrens <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
Tested-by: Sergio González Collado <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
samples/rust/Kconfig | 10 +++++++
samples/rust/Makefile | 1 +
samples/rust/rust_print.rs | 54 ++++++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
create mode 100644 samples/rust/rust_print.rs
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 841e0906e943..b0f74a81c8f9 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL
If unsure, say N.
+config SAMPLE_RUST_PRINT
+ tristate "Printing macros"
+ help
+ This option builds the Rust printing macros sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_print.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 1daba5f8658a..03086dabbea4 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
+obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
new file mode 100644
index 000000000000..09f737790f3f
--- /dev/null
+++ b/samples/rust/rust_print.rs
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust printing macros sample.
+
+use kernel::pr_cont;
+use kernel::prelude::*;
+
+module! {
+ type: RustPrint,
+ name: b"rust_print",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust printing macros sample",
+ license: b"GPL",
+}
+
+struct RustPrint;
+
+impl kernel::Module for RustPrint {
+ fn init(_module: &'static ThisModule) -> Result<Self> {
+ pr_info!("Rust printing macros sample (init)\n");
+
+ pr_emerg!("Emergency message (level 0) without args\n");
+ pr_alert!("Alert message (level 1) without args\n");
+ pr_crit!("Critical message (level 2) without args\n");
+ pr_err!("Error message (level 3) without args\n");
+ pr_warn!("Warning message (level 4) without args\n");
+ pr_notice!("Notice message (level 5) without args\n");
+ pr_info!("Info message (level 6) without args\n");
+
+ pr_info!("A line that");
+ pr_cont!(" is continued");
+ pr_cont!(" without args\n");
+
+ pr_emerg!("{} message (level {}) with args\n", "Emergency", 0);
+ pr_alert!("{} message (level {}) with args\n", "Alert", 1);
+ pr_crit!("{} message (level {}) with args\n", "Critical", 2);
+ pr_err!("{} message (level {}) with args\n", "Error", 3);
+ pr_warn!("{} message (level {}) with args\n", "Warning", 4);
+ pr_notice!("{} message (level {}) with args\n", "Notice", 5);
+ pr_info!("{} message (level {}) with args\n", "Info", 6);
+
+ pr_info!("A {} that", "line");
+ pr_cont!(" is {}", "continued");
+ pr_cont!(" with {}\n", "args");
+
+ Ok(RustPrint)
+ }
+}
+
+impl Drop for RustPrint {
+ fn drop(&mut self) {
+ pr_info!("Rust printing macros sample (exit)\n");
+ }
+}
--
2.38.1
From: Gary Guo <[email protected]>
Instead of taking binary string literals, take string ones instead,
making it easier for users to define a module, i.e. instead of
calling `module!` like:
module! {
...
name: b"rust_minimal",
...
}
now it is called as:
module! {
...
name: "rust_minimal",
...
}
Module names, aliases and license strings are restricted to
ASCII only. However, the author and the description allows UTF-8.
For simplicity (avoid parsing), escape sequences and raw string
literals are not yet handled.
Link: https://github.com/Rust-for-Linux/linux/issues/252
Link: https://lore.kernel.org/lkml/[email protected]/
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/macros/helpers.rs | 24 ++++++++++++++++++------
rust/macros/lib.rs | 12 ++++++------
rust/macros/module.rs | 10 +++++-----
samples/rust/rust_minimal.rs | 8 ++++----
samples/rust/rust_print.rs | 8 ++++----
5 files changed, 37 insertions(+), 25 deletions(-)
diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
index cdc7dc6135d2..cf7ad950dc1e 100644
--- a/rust/macros/helpers.rs
+++ b/rust/macros/helpers.rs
@@ -18,10 +18,16 @@ pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
}
}
-pub(crate) fn try_byte_string(it: &mut token_stream::IntoIter) -> Option<String> {
- try_literal(it).and_then(|byte_string| {
- if byte_string.starts_with("b\"") && byte_string.ends_with('\"') {
- Some(byte_string[2..byte_string.len() - 1].to_string())
+pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
+ try_literal(it).and_then(|string| {
+ if string.starts_with('\"') && string.ends_with('\"') {
+ let content = &string[1..string.len() - 1];
+ if content.contains('\\') {
+ panic!("Escape sequences in string literals not yet handled");
+ }
+ Some(content.to_string())
+ } else if string.starts_with("r\"") {
+ panic!("Raw string literals are not yet handled");
} else {
None
}
@@ -40,8 +46,14 @@ pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char {
}
}
-pub(crate) fn expect_byte_string(it: &mut token_stream::IntoIter) -> String {
- try_byte_string(it).expect("Expected byte string")
+pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
+ try_string(it).expect("Expected string")
+}
+
+pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String {
+ let string = try_string(it).expect("Expected string");
+ assert!(string.is_ascii(), "Expected ASCII string");
+ string
}
pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index e40caaf0a656..c1d385e345b9 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -25,20 +25,20 @@ use proc_macro::TokenStream;
///
/// module!{
/// type: MyModule,
-/// name: b"my_kernel_module",
-/// author: b"Rust for Linux Contributors",
-/// description: b"My very own kernel module!",
-/// license: b"GPL",
+/// name: "my_kernel_module",
+/// author: "Rust for Linux Contributors",
+/// description: "My very own kernel module!",
+/// license: "GPL",
/// params: {
/// my_i32: i32 {
/// default: 42,
/// permissions: 0o000,
-/// description: b"Example of i32",
+/// description: "Example of i32",
/// },
/// writeable_i32: i32 {
/// default: 42,
/// permissions: 0o644,
-/// description: b"Example of i32",
+/// description: "Example of i32",
/// },
/// },
/// }
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 186a5b8be23c..a7e363c2b044 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -108,11 +108,11 @@ impl ModuleInfo {
match key.as_str() {
"type" => info.type_ = expect_ident(it),
- "name" => info.name = expect_byte_string(it),
- "author" => info.author = Some(expect_byte_string(it)),
- "description" => info.description = Some(expect_byte_string(it)),
- "license" => info.license = expect_byte_string(it),
- "alias" => info.alias = Some(expect_byte_string(it)),
+ "name" => info.name = expect_string_ascii(it),
+ "author" => info.author = Some(expect_string(it)),
+ "description" => info.description = Some(expect_string(it)),
+ "license" => info.license = expect_string_ascii(it),
+ "alias" => info.alias = Some(expect_string_ascii(it)),
_ => panic!(
"Unknown key \"{}\". Valid keys are: {:?}.",
key, EXPECTED_KEYS
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
index 54ad17685742..dc05f4bbe27e 100644
--- a/samples/rust/rust_minimal.rs
+++ b/samples/rust/rust_minimal.rs
@@ -6,10 +6,10 @@ use kernel::prelude::*;
module! {
type: RustMinimal,
- name: b"rust_minimal",
- author: b"Rust for Linux Contributors",
- description: b"Rust minimal sample",
- license: b"GPL",
+ name: "rust_minimal",
+ author: "Rust for Linux Contributors",
+ description: "Rust minimal sample",
+ license: "GPL",
}
struct RustMinimal {
diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
index 09f737790f3f..8b39d9cef6d1 100644
--- a/samples/rust/rust_print.rs
+++ b/samples/rust/rust_print.rs
@@ -7,10 +7,10 @@ use kernel::prelude::*;
module! {
type: RustPrint,
- name: b"rust_print",
- author: b"Rust for Linux Contributors",
- description: b"Rust printing macros sample",
- license: b"GPL",
+ name: "rust_print",
+ author: "Rust for Linux Contributors",
+ description: "Rust printing macros sample",
+ license: "GPL",
}
struct RustPrint;
--
2.38.1
From: Gary Guo <[email protected]>
Add the `b_str!` macro, which creates a new `BStr` from
a string literal.
It is usable in const contexts, for instance:
const X: &BStr = b_str!("Example");
Signed-off-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 3aa1a0cb9bf8..95eb757c619d 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -9,6 +9,27 @@ use core::fmt;
/// `BStr` is simply an alias to `[u8]`, but has a more evident semantical meaning.
pub type BStr = [u8];
+/// Creates a new [`BStr`] from a string literal.
+///
+/// `b_str!` converts the supplied string literal to byte string, so non-ASCII
+/// characters can be included.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::b_str;
+/// # use kernel::str::BStr;
+/// const MY_BSTR: &BStr = b_str!("My awesome BStr!");
+/// ```
+#[macro_export]
+macro_rules! b_str {
+ ($str:literal) => {{
+ const S: &'static str = $str;
+ const C: &'static $crate::str::BStr = S.as_bytes();
+ C
+ }};
+}
+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1
From: Miguel Ojeda <[email protected]>
Split the prelude re-exports into groups: first the ones coming
from the `core` crate, then `alloc`, then our own crates and
finally the ones from modules from `kernel` itself (i.e. `super`).
We are doing this manually for the moment, but ideally, long-term,
this could be automated via `rustfmt` with options such as
`group_imports` and `imports_granularity` (both currently unstable).
Reviewed-by: Boqun Feng <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 495e22250726..f8219285d8c0 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -11,10 +11,14 @@
//! use kernel::prelude::*;
//! ```
-pub use super::{
- error::{Error, Result},
- pr_emerg, pr_info, ThisModule,
-};
-pub use alloc::{boxed::Box, vec::Vec};
pub use core::pin::Pin;
+
+pub use alloc::{boxed::Box, vec::Vec};
+
pub use macros::module;
+
+pub use super::{pr_emerg, pr_info};
+
+pub use super::error::{Error, Result};
+
+pub use super::ThisModule;
--
2.38.1
From: Milan Landaverde <[email protected]>
Add unit tests for `CStr::from_bytes_with_nul()` and
`CStr::from_bytes_with_nul_unchecked()`.
These serve as an example of the first unit tests for Rust code
(i.e. different from documentation tests).
Signed-off-by: Milan Landaverde <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 11d297c1a61c..3ed685cb5a3c 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -321,6 +321,35 @@ where
}
}
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_cstr_to_str() {
+ let good_bytes = b"\xf0\x9f\xa6\x80\0";
+ let checked_cstr = CStr::from_bytes_with_nul(good_bytes).unwrap();
+ let checked_str = checked_cstr.to_str().unwrap();
+ assert_eq!(checked_str, "????");
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_cstr_to_str_panic() {
+ let bad_bytes = b"\xc3\x28\0";
+ let checked_cstr = CStr::from_bytes_with_nul(bad_bytes).unwrap();
+ checked_cstr.to_str().unwrap();
+ }
+
+ #[test]
+ fn test_cstr_as_str_unchecked() {
+ let good_bytes = b"\xf0\x9f\x90\xA7\0";
+ let checked_cstr = CStr::from_bytes_with_nul(good_bytes).unwrap();
+ let unchecked_str = unsafe { checked_cstr.as_str_unchecked() };
+ assert_eq!(unchecked_str, "????");
+ }
+}
+
/// Allows formatting of [`fmt::Arguments`] into a raw buffer.
///
/// It does not fail if callers write past the end of the buffer so that they can calculate the
--
2.38.1
From: Miguel Ojeda <[email protected]>
This level is a bit different from the rest since it does not
pass the module name to the `_printk()` call.
Thus add a new parameter to the general `print_macro!` to
handle it differently.
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Gary Guo <[email protected]>
Signed-off-by: Gary Guo <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
Reviewed-by: Sergio González Collado <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/print.rs | 72 ++++++++++++++++++++++++++++++++++++++------
1 file changed, 63 insertions(+), 9 deletions(-)
diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
index 694f51c6da5c..29bf9c2e8aee 100644
--- a/rust/kernel/print.rs
+++ b/rust/kernel/print.rs
@@ -81,6 +81,7 @@ pub mod format_strings {
pub static NOTICE: [u8; LENGTH] = generate(false, bindings::KERN_NOTICE);
pub static INFO: [u8; LENGTH] = generate(false, bindings::KERN_INFO);
pub static DEBUG: [u8; LENGTH] = generate(false, bindings::KERN_DEBUG);
+ pub static CONT: [u8; LENGTH] = generate(true, bindings::KERN_CONT);
}
/// Prints a message via the kernel's [`_printk`].
@@ -111,6 +112,26 @@ pub unsafe fn call_printk(
}
}
+/// Prints a message via the kernel's [`_printk`] for the `CONT` level.
+///
+/// Public but hidden since it should only be used from public macros.
+///
+/// [`_printk`]: ../../../../include/linux/printk.h
+#[doc(hidden)]
+#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
+pub fn call_printk_cont(args: fmt::Arguments<'_>) {
+ // `_printk` does not seem to fail in any path.
+ //
+ // SAFETY: The format string is fixed.
+ #[cfg(CONFIG_PRINTK)]
+ unsafe {
+ bindings::_printk(
+ format_strings::CONT.as_ptr() as _,
+ &args as *const _ as *const c_void,
+ );
+ }
+}
+
/// Performs formatting and forwards the string to [`call_printk`].
///
/// Public but hidden since it should only be used from public macros.
@@ -120,7 +141,7 @@ pub unsafe fn call_printk(
#[allow(clippy::crate_in_macro_def)]
macro_rules! print_macro (
// The non-continuation cases (most of them, e.g. `INFO`).
- ($format_string:path, $($arg:tt)+) => (
+ ($format_string:path, false, $($arg:tt)+) => (
// SAFETY: This hidden macro should only be called by the documented
// printing macros which ensure the format string is one of the fixed
// ones. All `__LOG_PREFIX`s are null-terminated as they are generated
@@ -134,6 +155,13 @@ macro_rules! print_macro (
);
}
);
+
+ // The `CONT` case.
+ ($format_string:path, true, $($arg:tt)+) => (
+ $crate::print::call_printk_cont(
+ format_args!($($arg)+),
+ );
+ );
);
/// Stub for doctests
@@ -174,7 +202,7 @@ macro_rules! print_macro (
#[macro_export]
macro_rules! pr_emerg (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::EMERG, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::EMERG, false, $($arg)*)
)
);
@@ -198,7 +226,7 @@ macro_rules! pr_emerg (
#[macro_export]
macro_rules! pr_alert (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::ALERT, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::ALERT, false, $($arg)*)
)
);
@@ -222,7 +250,7 @@ macro_rules! pr_alert (
#[macro_export]
macro_rules! pr_crit (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::CRIT, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::CRIT, false, $($arg)*)
)
);
@@ -246,7 +274,7 @@ macro_rules! pr_crit (
#[macro_export]
macro_rules! pr_err (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::ERR, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::ERR, false, $($arg)*)
)
);
@@ -270,7 +298,7 @@ macro_rules! pr_err (
#[macro_export]
macro_rules! pr_warn (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::WARNING, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::WARNING, false, $($arg)*)
)
);
@@ -294,7 +322,7 @@ macro_rules! pr_warn (
#[macro_export]
macro_rules! pr_notice (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::NOTICE, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::NOTICE, false, $($arg)*)
)
);
@@ -319,7 +347,7 @@ macro_rules! pr_notice (
#[doc(alias = "print")]
macro_rules! pr_info (
($($arg:tt)*) => (
- $crate::print_macro!($crate::print::format_strings::INFO, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::INFO, false, $($arg)*)
)
);
@@ -346,7 +374,33 @@ macro_rules! pr_info (
macro_rules! pr_debug (
($($arg:tt)*) => (
if cfg!(debug_assertions) {
- $crate::print_macro!($crate::print::format_strings::DEBUG, $($arg)*)
+ $crate::print_macro!($crate::print::format_strings::DEBUG, false, $($arg)*)
}
)
);
+
+/// Continues a previous log message in the same line.
+///
+/// Use only when continuing a previous `pr_*!` macro (e.g. [`pr_info!`]).
+///
+/// Equivalent to the kernel's [`pr_cont`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// `alloc::format!` for information about the formatting syntax.
+///
+/// [`pr_cont`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_cont
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::pr_cont;
+/// pr_info!("hello");
+/// pr_cont!(" {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_cont (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
+ )
+);
--
2.38.1
From: Wedson Almeida Filho <[email protected]>
It is convenient to have all the `Error` constant items (such as
`EINVAL`) available as-is everywhere (i.e. for code using the kernel
prelude such as kernel modules).
Therefore, add all of them to the prelude.
For instance, this allows to write `Err(EINVAL)` to create
a kernel `Result`:
fn f() -> Result<...> {
...
Err(EINVAL)
}
Signed-off-by: Wedson Almeida Filho <[email protected]>
Reviewed-by: Gary Guo <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 7c4c35bf3c66..1e08b08e9420 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -19,6 +19,6 @@ pub use macros::{module, vtable};
pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
-pub use super::error::{Error, Result};
+pub use super::error::{code::*, Error, Result};
pub use super::ThisModule;
--
2.38.1
From: Gary Guo <[email protected]>
This procedural macro attribute provides a simple way to declare
a trait with a set of operations that later users can partially
implement, providing compile-time `HAS_*` boolean associated
constants that indicate whether a particular operation was overridden.
This is useful as the Rust counterpart to structs like
`file_operations` where some pointers may be `NULL`, indicating
an operation is not provided.
For instance:
#[vtable]
trait Operations {
fn read(...) -> Result<usize> {
Err(EINVAL)
}
fn write(...) -> Result<usize> {
Err(EINVAL)
}
}
#[vtable]
impl Operations for S {
fn read(...) -> Result<usize> {
...
}
}
assert_eq!(<S as Operations>::HAS_READ, true);
assert_eq!(<S as Operations>::HAS_WRITE, false);
Signed-off-by: Gary Guo <[email protected]>
Reviewed-by: Sergio González Collado <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/prelude.rs | 2 +-
rust/macros/lib.rs | 52 +++++++++++++++++++++++
rust/macros/vtable.rs | 95 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 148 insertions(+), 1 deletion(-)
create mode 100644 rust/macros/vtable.rs
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 6a1c6b38327f..7c4c35bf3c66 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -15,7 +15,7 @@ pub use core::pin::Pin;
pub use alloc::{boxed::Box, vec::Vec};
-pub use macros::module;
+pub use macros::{module, vtable};
pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 15555e7ff487..e40caaf0a656 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -5,6 +5,7 @@
mod concat_idents;
mod helpers;
mod module;
+mod vtable;
use proc_macro::TokenStream;
@@ -72,6 +73,57 @@ pub fn module(ts: TokenStream) -> TokenStream {
module::module(ts)
}
+/// Declares or implements a vtable trait.
+///
+/// Linux's use of pure vtables is very close to Rust traits, but they differ
+/// in how unimplemented functions are represented. In Rust, traits can provide
+/// default implementation for all non-required methods (and the default
+/// implementation could just return `Error::EINVAL`); Linux typically use C
+/// `NULL` pointers to represent these functions.
+///
+/// This attribute is intended to close the gap. Traits can be declared and
+/// implemented with the `#[vtable]` attribute, and a `HAS_*` associated constant
+/// will be generated for each method in the trait, indicating if the implementor
+/// has overridden a method.
+///
+/// This attribute is not needed if all methods are required.
+///
+/// # Examples
+///
+/// ```ignore
+/// use kernel::prelude::*;
+///
+/// // Declares a `#[vtable]` trait
+/// #[vtable]
+/// pub trait Operations: Send + Sync + Sized {
+/// fn foo(&self) -> Result<()> {
+/// Err(EINVAL)
+/// }
+///
+/// fn bar(&self) -> Result<()> {
+/// Err(EINVAL)
+/// }
+/// }
+///
+/// struct Foo;
+///
+/// // Implements the `#[vtable]` trait
+/// #[vtable]
+/// impl Operations for Foo {
+/// fn foo(&self) -> Result<()> {
+/// # Err(EINVAL)
+/// // ...
+/// }
+/// }
+///
+/// assert_eq!(<Foo as Operations>::HAS_FOO, true);
+/// assert_eq!(<Foo as Operations>::HAS_BAR, false);
+/// ```
+#[proc_macro_attribute]
+pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
+ vtable::vtable(attr, ts)
+}
+
/// Concatenate two identifiers.
///
/// This is useful in macros that need to declare or reference items with names
diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
new file mode 100644
index 000000000000..34d5e7fb5768
--- /dev/null
+++ b/rust/macros/vtable.rs
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
+use std::collections::HashSet;
+use std::fmt::Write;
+
+pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
+ let mut tokens: Vec<_> = ts.into_iter().collect();
+
+ // Scan for the `trait` or `impl` keyword.
+ let is_trait = tokens
+ .iter()
+ .find_map(|token| match token {
+ TokenTree::Ident(ident) => match ident.to_string().as_str() {
+ "trait" => Some(true),
+ "impl" => Some(false),
+ _ => None,
+ },
+ _ => None,
+ })
+ .expect("#[vtable] attribute should only be applied to trait or impl block");
+
+ // Retrieve the main body. The main body should be the last token tree.
+ let body = match tokens.pop() {
+ Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group,
+ _ => panic!("cannot locate main body of trait or impl block"),
+ };
+
+ let mut body_it = body.stream().into_iter();
+ let mut functions = Vec::new();
+ let mut consts = HashSet::new();
+ while let Some(token) = body_it.next() {
+ match token {
+ TokenTree::Ident(ident) if ident.to_string() == "fn" => {
+ let fn_name = match body_it.next() {
+ Some(TokenTree::Ident(ident)) => ident.to_string(),
+ // Possibly we've encountered a fn pointer type instead.
+ _ => continue,
+ };
+ functions.push(fn_name);
+ }
+ TokenTree::Ident(ident) if ident.to_string() == "const" => {
+ let const_name = match body_it.next() {
+ Some(TokenTree::Ident(ident)) => ident.to_string(),
+ // Possibly we've encountered an inline const block instead.
+ _ => continue,
+ };
+ consts.insert(const_name);
+ }
+ _ => (),
+ }
+ }
+
+ let mut const_items;
+ if is_trait {
+ const_items = "
+ /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
+ /// attribute when implementing this trait.
+ const USE_VTABLE_ATTR: ();
+ "
+ .to_owned();
+
+ for f in functions {
+ let gen_const_name = format!("HAS_{}", f.to_uppercase());
+ // Skip if it's declared already -- this allows user override.
+ if consts.contains(&gen_const_name) {
+ continue;
+ }
+ // We don't know on the implementation-site whether a method is required or provided
+ // so we have to generate a const for all methods.
+ write!(
+ const_items,
+ "/// Indicates if the `{f}` method is overridden by the implementor.
+ const {gen_const_name}: bool = false;",
+ )
+ .unwrap();
+ }
+ } else {
+ const_items = "const USE_VTABLE_ATTR: () = ();".to_owned();
+
+ for f in functions {
+ let gen_const_name = format!("HAS_{}", f.to_uppercase());
+ if consts.contains(&gen_const_name) {
+ continue;
+ }
+ write!(const_items, "const {gen_const_name}: bool = true;").unwrap();
+ }
+ }
+
+ let new_body = vec![const_items.parse().unwrap(), body.stream()]
+ .into_iter()
+ .collect();
+ tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, new_body)));
+ tokens.into_iter().collect()
+}
--
2.38.1
From: Wedson Almeida Filho <[email protected]>
Introduce the new `types` module of the `kernel` crate with
`Either` as its first type.
`Either<L, R>` is a sum type that always holds either a value
of type `L` (`Left` variant) or `R` (`Right` variant).
For instance:
struct Executor {
queue: Either<BoxedQueue, &'static Queue>,
}
Signed-off-by: Wedson Almeida Filho <[email protected]>
Reviewed-by: Wei Liu <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/lib.rs | 1 +
rust/kernel/types.rs | 12 ++++++++++++
2 files changed, 13 insertions(+)
create mode 100644 rust/kernel/types.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index a3abc110ff97..53040fa9e897 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -31,6 +31,7 @@ mod static_assert;
#[doc(hidden)]
pub mod std_vendor;
pub mod str;
+pub mod types;
#[doc(hidden)]
pub use bindings;
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
new file mode 100644
index 000000000000..3b0c44769708
--- /dev/null
+++ b/rust/kernel/types.rs
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel types.
+
+/// A sum type that always holds either a value of type `L` or `R`.
+pub enum Either<L, R> {
+ /// Constructs an instance of [`Either`] containing a value of type `L`.
+ Left(L),
+
+ /// Constructs an instance of [`Either`] containing a value of type `R`.
+ Right(R),
+}
--
2.38.1
From: Wedson Almeida Filho <[email protected]>
Add the `CString` type, which is an owned string that is guaranteed
to have exactly one `NUL` byte at the end, i.e. the owned equivalent
to `CStr` introduced earlier.
It is used for interoperability with kernel APIs that take C strings.
In order to do so, implement the `RawFormatter::new()` constructor
and the `RawFormatter::bytes_written()` method as well.
Signed-off-by: Wedson Almeida Filho <[email protected]>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/str.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 89 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index ce207d1b3d2a..17dc8d273302 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -2,6 +2,7 @@
//! String representations.
+use alloc::vec::Vec;
use core::fmt::{self, Write};
use core::ops::{self, Deref, Index};
@@ -384,13 +385,22 @@ mod tests {
/// is less than `end`.
pub(crate) struct RawFormatter {
// Use `usize` to use `saturating_*` functions.
- #[allow(dead_code)]
beg: usize,
pos: usize,
end: usize,
}
impl RawFormatter {
+ /// Creates a new instance of [`RawFormatter`] with an empty buffer.
+ fn new() -> Self {
+ // INVARIANT: The buffer is empty, so the region that needs to be writable is empty.
+ Self {
+ beg: 0,
+ pos: 0,
+ end: 0,
+ }
+ }
+
/// Creates a new instance of [`RawFormatter`] with the given buffer pointers.
///
/// # Safety
@@ -429,6 +439,11 @@ impl RawFormatter {
pub(crate) fn pos(&self) -> *mut u8 {
self.pos as _
}
+
+ /// Return the number of bytes written to the formatter.
+ pub(crate) fn bytes_written(&self) -> usize {
+ self.pos - self.beg
+ }
}
impl fmt::Write for RawFormatter {
@@ -469,7 +484,6 @@ impl Formatter {
///
/// The memory region starting at `buf` and extending for `len` bytes must be valid for writes
/// for the lifetime of the returned [`Formatter`].
- #[allow(dead_code)]
pub(crate) unsafe fn from_buffer(buf: *mut u8, len: usize) -> Self {
// SAFETY: The safety requirements of this function satisfy those of the callee.
Self(unsafe { RawFormatter::from_buffer(buf, len) })
@@ -496,3 +510,76 @@ impl fmt::Write for Formatter {
}
}
}
+
+/// An owned string that is guaranteed to have exactly one `NUL` byte, which is at the end.
+///
+/// Used for interoperability with kernel APIs that take C strings.
+///
+/// # Invariants
+///
+/// The string is always `NUL`-terminated and contains no other `NUL` bytes.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::str::CString;
+///
+/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap();
+/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes());
+///
+/// let tmp = "testing";
+/// let s = CString::try_from_fmt(fmt!("{tmp}{}", 123)).unwrap();
+/// assert_eq!(s.as_bytes_with_nul(), "testing123\0".as_bytes());
+///
+/// // This fails because it has an embedded `NUL` byte.
+/// let s = CString::try_from_fmt(fmt!("a\0b{}", 123));
+/// assert_eq!(s.is_ok(), false);
+/// ```
+pub struct CString {
+ buf: Vec<u8>,
+}
+
+impl CString {
+ /// Creates an instance of [`CString`] from the given formatted arguments.
+ pub fn try_from_fmt(args: fmt::Arguments<'_>) -> Result<Self, Error> {
+ // Calculate the size needed (formatted string plus `NUL` terminator).
+ let mut f = RawFormatter::new();
+ f.write_fmt(args)?;
+ f.write_str("\0")?;
+ let size = f.bytes_written();
+
+ // Allocate a vector with the required number of bytes, and write to it.
+ let mut buf = Vec::try_with_capacity(size)?;
+ // SAFETY: The buffer stored in `buf` is at least of size `size` and is valid for writes.
+ let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
+ f.write_fmt(args)?;
+ f.write_str("\0")?;
+
+ // SAFETY: The number of bytes that can be written to `f` is bounded by `size`, which is
+ // `buf`'s capacity. The contents of the buffer have been initialised by writes to `f`.
+ unsafe { buf.set_len(f.bytes_written()) };
+
+ // Check that there are no `NUL` bytes before the end.
+ // SAFETY: The buffer is valid for read because `f.bytes_written()` is bounded by `size`
+ // (which the minimum buffer size) and is non-zero (we wrote at least the `NUL` terminator)
+ // so `f.bytes_written() - 1` doesn't underflow.
+ let ptr = unsafe { bindings::memchr(buf.as_ptr().cast(), 0, (f.bytes_written() - 1) as _) };
+ if !ptr.is_null() {
+ return Err(EINVAL);
+ }
+
+ // INVARIANT: We wrote the `NUL` terminator and checked above that no other `NUL` bytes
+ // exist in the buffer.
+ Ok(Self { buf })
+ }
+}
+
+impl Deref for CString {
+ type Target = CStr;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The type invariants guarantee that the string is `NUL`-terminated and that no
+ // other `NUL` bytes exist.
+ unsafe { CStr::from_bytes_with_nul_unchecked(self.buf.as_slice()) }
+ }
+}
--
2.38.1
On Fri, Dec 02, 2022 at 05:14:58PM +0100, [email protected] wrote:
> From: Wedson Almeida Filho <[email protected]>
>
> Introduce the new `types` module of the `kernel` crate with
> `Either` as its first type.
>
> `Either<L, R>` is a sum type that always holds either a value
> of type `L` (`Left` variant) or `R` (`Right` variant).
>
> For instance:
>
> struct Executor {
> queue: Either<BoxedQueue, &'static Queue>,
> }
This specific example seems like it would be better served by the
existing `Cow` type.
On Fri, 2 Dec 2022 17:14:36 +0100
[email protected] wrote:
> From: Björn Roy Baron <[email protected]>
>
> This macro provides similar functionality to the unstable feature
> `concat_idents` without having to rely on it.
>
> For instance:
>
> let x_1 = 42;
> let x_2 = concat_idents!(x, _1);
> assert!(x_1 == x_2);
>
> It has different behavior with respect to macro hygiene. Unlike
> the unstable `concat_idents!` macro, it allows, for example,
> referring to local variables by taking the span of the second
> macro as span for the output identifier.
>
> Signed-off-by: Björn Roy Baron <[email protected]>
> Reviewed-by: Finn Behrens <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> rust/macros/concat_idents.rs | 23 +++++++++++++++++++
> rust/macros/lib.rs | 44 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 67 insertions(+)
> create mode 100644 rust/macros/concat_idents.rs
>
> diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs
> new file mode 100644
> index 000000000000..7e4b450f3a50
> --- /dev/null
> +++ b/rust/macros/concat_idents.rs
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use proc_macro::{token_stream, Ident, TokenStream, TokenTree};
> +
> +use crate::helpers::expect_punct;
> +
> +fn expect_ident(it: &mut token_stream::IntoIter) -> Ident {
> + if let Some(TokenTree::Ident(ident)) = it.next() {
> + ident
> + } else {
> + panic!("Expected Ident")
> + }
> +}
> +
> +pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
> + let mut it = ts.into_iter();
> + let a = expect_ident(&mut it);
> + assert_eq!(expect_punct(&mut it), ',');
> + let b = expect_ident(&mut it);
> + assert!(it.next().is_none(), "only two idents can be concatenated");
> + let res = Ident::new(&format!("{a}{b}"), b.span());
> + TokenStream::from_iter([TokenTree::Ident(res)])
> +}
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 91764bfb1f89..15555e7ff487 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -2,6 +2,7 @@
>
> //! Crate for all kernel procedural macros.
>
> +mod concat_idents;
> mod helpers;
> mod module;
>
> @@ -70,3 +71,46 @@ use proc_macro::TokenStream;
> pub fn module(ts: TokenStream) -> TokenStream {
> module::module(ts)
> }
> +
> +/// Concatenate two identifiers.
> +///
> +/// This is useful in macros that need to declare or reference items with names
> +/// starting with a fixed prefix and ending in a user specified name. The resulting
> +/// identifier has the span of the second argument.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// use kernel::macro::concat_idents;
> +///
> +/// macro_rules! pub_no_prefix {
> +/// ($prefix:ident, $($newname:ident),+) => {
> +/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
> +/// };
> +/// }
> +///
> +/// pub_no_prefix!(
> +/// binder_driver_return_protocol_,
> +/// BR_OK,
> +/// BR_ERROR,
> +/// BR_TRANSACTION,
> +/// BR_REPLY,
> +/// BR_DEAD_REPLY,
> +/// BR_TRANSACTION_COMPLETE,
> +/// BR_INCREFS,
> +/// BR_ACQUIRE,
> +/// BR_RELEASE,
> +/// BR_DECREFS,
> +/// BR_NOOP,
> +/// BR_SPAWN_LOOPER,
> +/// BR_DEAD_BINDER,
> +/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
> +/// BR_FAILED_REPLY
> +/// );
> +///
> +/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
> +/// ```
> +#[proc_macro]
> +pub fn concat_idents(ts: TokenStream) -> TokenStream {
> + concat_idents::concat_idents(ts)
> +}
Reviewed-by: Gary Guo <[email protected]>
On Fri, Dec 2, 2022 at 5:15 PM <[email protected]> wrote:
>
> This is v2 of the first batch of changes to upstream the rest of
> the Rust support. v1 is at:
>
> https://lore.kernel.org/rust-for-linux/[email protected]/
>
> I collected the tags and applied the agreed changes. The full diff
> follows (with respect to v1), since they are minor enough.
>
> I will be applying the patches tomorrow, since v1 has had more than
> three weeks of review time.
Queued.
Cheers,
Miguel
On Sat, Dec 3, 2022 at 12:42 AM Josh Triplett <[email protected]> wrote:
>
> This specific example seems like it would be better served by the
> existing `Cow` type.
Yeah, possibly -- it is taken from one of the use cases in the full
repository. Perhaps it was deemed simpler, or providing a way to go to
an owned instance was to be avoided.
Thanks for taking a look!
Cheers,
Miguel
On Fri, 2 Dec 2022 15:41:59 -0800
Josh Triplett <[email protected]> wrote:
> On Fri, Dec 02, 2022 at 05:14:58PM +0100, [email protected] wrote:
> > From: Wedson Almeida Filho <[email protected]>
> >
> > Introduce the new `types` module of the `kernel` crate with
> > `Either` as its first type.
> >
> > `Either<L, R>` is a sum type that always holds either a value
> > of type `L` (`Left` variant) or `R` (`Right` variant).
> >
> > For instance:
> >
> > struct Executor {
> > queue: Either<BoxedQueue, &'static Queue>,
> > }
>
> This specific example seems like it would be better served by the
> existing `Cow` type.
We use `no_global_oom_handling`, which gates most `ToOwned`
implementations (e.g. `str` cannot implement `to_owned()` because it
cannot guarantee allocation success).
So the Rust `Cow` is pretty much useless in the kernel.
Best,
Gary
On Sun, 4 Dec 2022 at 10:31, Gary Guo <[email protected]> wrote:
>
> On Fri, 2 Dec 2022 15:41:59 -0800
> Josh Triplett <[email protected]> wrote:
>
> > On Fri, Dec 02, 2022 at 05:14:58PM +0100, [email protected] wrote:
> > > From: Wedson Almeida Filho <[email protected]>
> > >
> > > Introduce the new `types` module of the `kernel` crate with
> > > `Either` as its first type.
> > >
> > > `Either<L, R>` is a sum type that always holds either a value
> > > of type `L` (`Left` variant) or `R` (`Right` variant).
> > >
> > > For instance:
> > >
> > > struct Executor {
> > > queue: Either<BoxedQueue, &'static Queue>,
> > > }
> >
> > This specific example seems like it would be better served by the
> > existing `Cow` type.
>
> We use `no_global_oom_handling`, which gates most `ToOwned`
> implementations (e.g. `str` cannot implement `to_owned()` because it
> cannot guarantee allocation success).
>
> So the Rust `Cow` is pretty much useless in the kernel.
It's also implemented in `std`, which the kernel doesn't include.
(Which is actually good for us, since we can't really use it.)
Josh, how do you feel about adding a `TryToOwned` trait to
`core::borrow`? This would be similar to the precedent of `TryFrom` in
addition to `From` for the fallible case, and would be usable by the
kernel.
On Sun, Dec 4, 2022 at 6:36 PM Wedson Almeida Filho <[email protected]> wrote:
>
> It's also implemented in `std`, which the kernel doesn't include.
> (Which is actually good for us, since we can't really use it.)
We have it around in the kernel (the `std` one is a re-export), so one
"could" replace the `Either` with `Cow` in the case of the commit
message via ignoring the to-owned side of it (but I assume Josh didn't
mean to suggest that).
Anyway, it can be easily configured out from our `alloc`, so I will
send the patch.
Cheers,
Miguel
On Sun, Dec 04, 2022 at 05:36:08PM +0000, Wedson Almeida Filho wrote:
> On Sun, 4 Dec 2022 at 10:31, Gary Guo <[email protected]> wrote:
> >
> > On Fri, 2 Dec 2022 15:41:59 -0800
> > Josh Triplett <[email protected]> wrote:
> >
> > > On Fri, Dec 02, 2022 at 05:14:58PM +0100, [email protected] wrote:
> > > > From: Wedson Almeida Filho <[email protected]>
> > > >
> > > > Introduce the new `types` module of the `kernel` crate with
> > > > `Either` as its first type.
> > > >
> > > > `Either<L, R>` is a sum type that always holds either a value
> > > > of type `L` (`Left` variant) or `R` (`Right` variant).
> > > >
> > > > For instance:
> > > >
> > > > struct Executor {
> > > > queue: Either<BoxedQueue, &'static Queue>,
> > > > }
> > >
> > > This specific example seems like it would be better served by the
> > > existing `Cow` type.
> >
> > We use `no_global_oom_handling`, which gates most `ToOwned`
> > implementations (e.g. `str` cannot implement `to_owned()` because it
> > cannot guarantee allocation success).
> >
> > So the Rust `Cow` is pretty much useless in the kernel.
>
> It's also implemented in `std`, which the kernel doesn't include.
> (Which is actually good for us, since we can't really use it.)
>
> Josh, how do you feel about adding a `TryToOwned` trait to
> `core::borrow`? This would be similar to the precedent of `TryFrom` in
> addition to `From` for the fallible case, and would be usable by the
> kernel.
I'd expect it to be in alloc rather than core (though I suppose it
doesn't *fundamentally* depend on an allocator itself), but I'd be happy
to see a fallible version proposed, yeah.
On 2 Dec 2022, at 17:14, [email protected] wrote:
> From: Gary Guo <[email protected]>
>
> This procedural macro attribute provides a simple way to declare
> a trait with a set of operations that later users can partially
> implement, providing compile-time `HAS_*` boolean associated
> constants that indicate whether a particular operation was overridden.
>
> This is useful as the Rust counterpart to structs like
> `file_operations` where some pointers may be `NULL`, indicating
> an operation is not provided.
>
> For instance:
>
> #[vtable]
> trait Operations {
> fn read(...) -> Result<usize> {
> Err(EINVAL)
> }
>
> fn write(...) -> Result<usize> {
> Err(EINVAL)
> }
> }
>
> #[vtable]
> impl Operations for S {
> fn read(...) -> Result<usize> {
> ...
> }
> }
>
> assert_eq!(<S as Operations>::HAS_READ, true);
> assert_eq!(<S as Operations>::HAS_WRITE, false);
>
> Signed-off-by: Gary Guo <[email protected]>
> Reviewed-by: Sergio González Collado <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
Reviewed-by: Finn Behrens <[email protected]>
Regards,
Finn
> ---
> rust/kernel/prelude.rs | 2 +-
> rust/macros/lib.rs | 52 +++++++++++++++++++++++
> rust/macros/vtable.rs | 95 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 148 insertions(+), 1 deletion(-)
> create mode 100644 rust/macros/vtable.rs
>
> diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
> index 6a1c6b38327f..7c4c35bf3c66 100644
> --- a/rust/kernel/prelude.rs
> +++ b/rust/kernel/prelude.rs
> @@ -15,7 +15,7 @@ pub use core::pin::Pin;
>
> pub use alloc::{boxed::Box, vec::Vec};
>
> -pub use macros::module;
> +pub use macros::{module, vtable};
>
> pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
>
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 15555e7ff487..e40caaf0a656 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -5,6 +5,7 @@
> mod concat_idents;
> mod helpers;
> mod module;
> +mod vtable;
>
> use proc_macro::TokenStream;
>
> @@ -72,6 +73,57 @@ pub fn module(ts: TokenStream) -> TokenStream {
> module::module(ts)
> }
>
> +/// Declares or implements a vtable trait.
> +///
> +/// Linux's use of pure vtables is very close to Rust traits, but they differ
> +/// in how unimplemented functions are represented. In Rust, traits can provide
> +/// default implementation for all non-required methods (and the default
> +/// implementation could just return `Error::EINVAL`); Linux typically use C
> +/// `NULL` pointers to represent these functions.
> +///
> +/// This attribute is intended to close the gap. Traits can be declared and
> +/// implemented with the `#[vtable]` attribute, and a `HAS_*` associated constant
> +/// will be generated for each method in the trait, indicating if the implementor
> +/// has overridden a method.
> +///
> +/// This attribute is not needed if all methods are required.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// use kernel::prelude::*;
> +///
> +/// // Declares a `#[vtable]` trait
> +/// #[vtable]
> +/// pub trait Operations: Send + Sync + Sized {
> +/// fn foo(&self) -> Result<()> {
> +/// Err(EINVAL)
> +/// }
> +///
> +/// fn bar(&self) -> Result<()> {
> +/// Err(EINVAL)
> +/// }
> +/// }
> +///
> +/// struct Foo;
> +///
> +/// // Implements the `#[vtable]` trait
> +/// #[vtable]
> +/// impl Operations for Foo {
> +/// fn foo(&self) -> Result<()> {
> +/// # Err(EINVAL)
> +/// // ...
> +/// }
> +/// }
> +///
> +/// assert_eq!(<Foo as Operations>::HAS_FOO, true);
> +/// assert_eq!(<Foo as Operations>::HAS_BAR, false);
> +/// ```
> +#[proc_macro_attribute]
> +pub fn vtable(attr: TokenStream, ts: TokenStream) -> TokenStream {
> + vtable::vtable(attr, ts)
> +}
> +
> /// Concatenate two identifiers.
> ///
> /// This is useful in macros that need to declare or reference items with names
> diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
> new file mode 100644
> index 000000000000..34d5e7fb5768
> --- /dev/null
> +++ b/rust/macros/vtable.rs
> @@ -0,0 +1,95 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
> +use std::collections::HashSet;
> +use std::fmt::Write;
> +
> +pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
> + let mut tokens: Vec<_> = ts.into_iter().collect();
> +
> + // Scan for the `trait` or `impl` keyword.
> + let is_trait = tokens
> + .iter()
> + .find_map(|token| match token {
> + TokenTree::Ident(ident) => match ident.to_string().as_str() {
> + "trait" => Some(true),
> + "impl" => Some(false),
> + _ => None,
> + },
> + _ => None,
> + })
> + .expect("#[vtable] attribute should only be applied to trait or impl block");
> +
> + // Retrieve the main body. The main body should be the last token tree.
> + let body = match tokens.pop() {
> + Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group,
> + _ => panic!("cannot locate main body of trait or impl block"),
> + };
> +
> + let mut body_it = body.stream().into_iter();
> + let mut functions = Vec::new();
> + let mut consts = HashSet::new();
> + while let Some(token) = body_it.next() {
> + match token {
> + TokenTree::Ident(ident) if ident.to_string() == "fn" => {
> + let fn_name = match body_it.next() {
> + Some(TokenTree::Ident(ident)) => ident.to_string(),
> + // Possibly we've encountered a fn pointer type instead.
> + _ => continue,
> + };
> + functions.push(fn_name);
> + }
> + TokenTree::Ident(ident) if ident.to_string() == "const" => {
> + let const_name = match body_it.next() {
> + Some(TokenTree::Ident(ident)) => ident.to_string(),
> + // Possibly we've encountered an inline const block instead.
> + _ => continue,
> + };
> + consts.insert(const_name);
> + }
> + _ => (),
> + }
> + }
> +
> + let mut const_items;
> + if is_trait {
> + const_items = "
> + /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
> + /// attribute when implementing this trait.
> + const USE_VTABLE_ATTR: ();
> + "
> + .to_owned();
> +
> + for f in functions {
> + let gen_const_name = format!("HAS_{}", f.to_uppercase());
> + // Skip if it's declared already -- this allows user override.
> + if consts.contains(&gen_const_name) {
> + continue;
> + }
> + // We don't know on the implementation-site whether a method is required or provided
> + // so we have to generate a const for all methods.
> + write!(
> + const_items,
> + "/// Indicates if the `{f}` method is overridden by the implementor.
> + const {gen_const_name}: bool = false;",
> + )
> + .unwrap();
> + }
> + } else {
> + const_items = "const USE_VTABLE_ATTR: () = ();".to_owned();
> +
> + for f in functions {
> + let gen_const_name = format!("HAS_{}", f.to_uppercase());
> + if consts.contains(&gen_const_name) {
> + continue;
> + }
> + write!(const_items, "const {gen_const_name}: bool = true;").unwrap();
> + }
> + }
> +
> + let new_body = vec![const_items.parse().unwrap(), body.stream()]
> + .into_iter()
> + .collect();
> + tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, new_body)));
> + tokens.into_iter().collect()
> +}
> --
> 2.38.1
On 2 Dec 2022, at 17:14, [email protected] wrote:
> From: Viktor Garske <[email protected]>
>
> Only a few codes were added so far. With the `declare_err!`
> macro in place, add the remaining ones (which is most of them)
> from `include/uapi/asm-generic/errno-base.h`.
>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Viktor Garske <[email protected]>
> Reviewed-by: Gary Guo <[email protected]>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <[email protected]>
Reviewed-by: Finn Behrens <[email protected]>
Regards,
Finn
> ---
> rust/kernel/error.rs | 33 +++++++++++++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index b843f3445483..861746f2422d 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -17,7 +17,40 @@ pub mod code {
> };
> }
>
> + declare_err!(EPERM, "Operation not permitted.");
> + declare_err!(ENOENT, "No such file or directory.");
> + declare_err!(ESRCH, "No such process.");
> + declare_err!(EINTR, "Interrupted system call.");
> + declare_err!(EIO, "I/O error.");
> + declare_err!(ENXIO, "No such device or address.");
> + declare_err!(E2BIG, "Argument list too long.");
> + declare_err!(ENOEXEC, "Exec format error.");
> + declare_err!(EBADF, "Bad file number.");
> + declare_err!(ECHILD, "Exec format error.");
> + declare_err!(EAGAIN, "Try again.");
> declare_err!(ENOMEM, "Out of memory.");
> + declare_err!(EACCES, "Permission denied.");
> + declare_err!(EFAULT, "Bad address.");
> + declare_err!(ENOTBLK, "Block device required.");
> + declare_err!(EBUSY, "Device or resource busy.");
> + declare_err!(EEXIST, "File exists.");
> + declare_err!(EXDEV, "Cross-device link.");
> + declare_err!(ENODEV, "No such device.");
> + declare_err!(ENOTDIR, "Not a directory.");
> + declare_err!(EISDIR, "Is a directory.");
> + declare_err!(EINVAL, "Invalid argument.");
> + declare_err!(ENFILE, "File table overflow.");
> + declare_err!(EMFILE, "Too many open files.");
> + declare_err!(ENOTTY, "Not a typewriter.");
> + declare_err!(ETXTBSY, "Text file busy.");
> + declare_err!(EFBIG, "File too large.");
> + declare_err!(ENOSPC, "No space left on device.");
> + declare_err!(ESPIPE, "Illegal seek.");
> + declare_err!(EROFS, "Read-only file system.");
> + declare_err!(EMLINK, "Too many links.");
> + declare_err!(EPIPE, "Broken pipe.");
> + declare_err!(EDOM, "Math argument out of domain of func.");
> + declare_err!(ERANGE, "Math result not representable.");
> }
>
> /// Generic integer kernel error.
> --
> 2.38.1
On Tue, Dec 6, 2022 at 1:50 PM Finn Behrens <[email protected]> wrote:
>
> Reviewed-by: Finn Behrens <[email protected]>
Thanks a lot for these, Finn -- I already queued the patches on the
weekend, but if for some reason the series needs to be dropped and
reapplied, I will pick your new tags.
Cheers,
Miguel