Skip to content

Commit

Permalink
rust: add dev_* print macros.
Browse files Browse the repository at this point in the history
Implement `dev_*` print macros for `device::Device`.

They behave like the macros with the same names in C, i.e., they print
messages to the kernel ring buffer with the given level, prefixing the
messages with corresponding device information.

Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Danilo Krummrich <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
wedsonaf authored and gregkh committed Nov 5, 2024
1 parent 7f15c46 commit 5c7ca6f
Show file tree
Hide file tree
Showing 2 changed files with 320 additions and 1 deletion.
319 changes: 318 additions & 1 deletion rust/kernel/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use crate::{
bindings,
types::{ARef, Opaque},
};
use core::ptr;
use core::{fmt, ptr};

#[cfg(CONFIG_PRINTK)]
use crate::c_str;

/// A reference-counted device.
///
Expand Down Expand Up @@ -73,6 +76,110 @@ impl Device {
// SAFETY: Guaranteed by the safety requirements of the function.
unsafe { &*ptr.cast() }
}

/// Prints an emergency-level message (level 0) prefixed with device information.
///
/// More details are available from [`dev_emerg`].
///
/// [`dev_emerg`]: crate::dev_emerg
pub fn pr_emerg(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_EMERG, args) };
}

/// Prints an alert-level message (level 1) prefixed with device information.
///
/// More details are available from [`dev_alert`].
///
/// [`dev_alert`]: crate::dev_alert
pub fn pr_alert(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_ALERT, args) };
}

/// Prints a critical-level message (level 2) prefixed with device information.
///
/// More details are available from [`dev_crit`].
///
/// [`dev_crit`]: crate::dev_crit
pub fn pr_crit(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_CRIT, args) };
}

/// Prints an error-level message (level 3) prefixed with device information.
///
/// More details are available from [`dev_err`].
///
/// [`dev_err`]: crate::dev_err
pub fn pr_err(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_ERR, args) };
}

/// Prints a warning-level message (level 4) prefixed with device information.
///
/// More details are available from [`dev_warn`].
///
/// [`dev_warn`]: crate::dev_warn
pub fn pr_warn(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_WARNING, args) };
}

/// Prints a notice-level message (level 5) prefixed with device information.
///
/// More details are available from [`dev_notice`].
///
/// [`dev_notice`]: crate::dev_notice
pub fn pr_notice(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_NOTICE, args) };
}

/// Prints an info-level message (level 6) prefixed with device information.
///
/// More details are available from [`dev_info`].
///
/// [`dev_info`]: crate::dev_info
pub fn pr_info(&self, args: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_INFO, args) };
}

/// Prints a debug-level message (level 7) prefixed with device information.
///
/// More details are available from [`dev_dbg`].
///
/// [`dev_dbg`]: crate::dev_dbg
pub fn pr_dbg(&self, args: fmt::Arguments<'_>) {
if cfg!(debug_assertions) {
// SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
unsafe { self.printk(bindings::KERN_DEBUG, args) };
}
}

/// Prints the provided message to the console.
///
/// # Safety
///
/// Callers must ensure that `klevel` is null-terminated; in particular, one of the
/// `KERN_*`constants, for example, `KERN_CRIT`, `KERN_ALERT`, etc.
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) {
// SAFETY: `klevel` is null-terminated and one of the kernel constants. `self.as_raw`
// is valid because `self` is valid. The "%pA" format string expects a pointer to
// `fmt::Arguments`, which is what we're passing as the last argument.
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_dev_printk(
klevel as *const _ as *const core::ffi::c_char,
self.as_raw(),
c_str!("%pA").as_char_ptr(),
&msg as *const _ as *const core::ffi::c_void,
)
};
}
}

// SAFETY: Instances of `Device` are always reference-counted.
Expand All @@ -94,3 +201,213 @@ unsafe impl Send for Device {}
// SAFETY: `Device` can be shared among threads because all immutable methods are protected by the
// synchronization in `struct device`.
unsafe impl Sync for Device {}

#[doc(hidden)]
#[macro_export]
macro_rules! dev_printk {
($method:ident, $dev:expr, $($f:tt)*) => {
{
($dev).$method(core::format_args!($($f)*));
}
}
}

/// Prints an emergency-level message (level 0) prefixed with device information.
///
/// This level should be used if the system is unusable.
///
/// Equivalent to the kernel's `dev_emerg` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_emerg!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_emerg {
($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*); }
}

/// Prints an alert-level message (level 1) prefixed with device information.
///
/// This level should be used if action must be taken immediately.
///
/// Equivalent to the kernel's `dev_alert` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_alert!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_alert {
($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*); }
}

/// Prints a critical-level message (level 2) prefixed with device information.
///
/// This level should be used in critical conditions.
///
/// Equivalent to the kernel's `dev_crit` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_crit!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_crit {
($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*); }
}

/// Prints an error-level message (level 3) prefixed with device information.
///
/// This level should be used in error conditions.
///
/// Equivalent to the kernel's `dev_err` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_err!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_err {
($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*); }
}

/// Prints a warning-level message (level 4) prefixed with device information.
///
/// This level should be used in warning conditions.
///
/// Equivalent to the kernel's `dev_warn` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_warn!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_warn {
($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*); }
}

/// Prints a notice-level message (level 5) prefixed with device information.
///
/// This level should be used in normal but significant conditions.
///
/// Equivalent to the kernel's `dev_notice` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_notice!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_notice {
($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*); }
}

/// Prints an info-level message (level 6) prefixed with device information.
///
/// This level should be used for informational messages.
///
/// Equivalent to the kernel's `dev_info` macro.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_info!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_info {
($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*); }
}

/// Prints a debug-level message (level 7) prefixed with device information.
///
/// This level should be used for debug messages.
///
/// Equivalent to the kernel's `dev_dbg` macro, except that it doesn't support dynamic debug yet.
///
/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
/// [`core::fmt`] and `alloc::format!`.
///
/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
///
/// # Examples
///
/// ```
/// # use kernel::device::Device;
///
/// fn example(dev: &Device) {
/// dev_dbg!(dev, "hello {}\n", "there");
/// }
/// ```
#[macro_export]
macro_rules! dev_dbg {
($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*); }
}
2 changes: 2 additions & 0 deletions rust/kernel/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub use super::build_assert;
// `super::std_vendor` is hidden, which makes the macro inline for some reason.
#[doc(no_inline)]
pub use super::dbg;
pub use super::fmt;
pub use super::{dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn};
pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};

pub use super::{init, pin_init, try_init, try_pin_init};
Expand Down

0 comments on commit 5c7ca6f

Please sign in to comment.