Rust changes for v6.18

Toolchain and infrastructure:
 
  - Derive 'Zeroable' for all structs and unions generated by 'bindgen'
    where possible and corresponding cleanups. To do so, add the
    'pin-init' crate as a dependency to 'bindings' and 'uapi'.
 
    It also includes its first use in the 'cpufreq' module, with more to
    come in the next cycle.
 
  - Add warning to the 'rustdoc' target to detect broken 'srctree/' links
    and fix existing cases.
 
  - Remove support for unused (since v6.16) host '#[test]'s, simplifying
    the 'rusttest' target. Tests should generally run within KUnit.
 
 'kernel' crate:
 
  - Add 'ptr' module with a new 'Alignment' type, which is always a power
    of two and is used to validate that a given value is a valid
    alignment and to perform masking and alignment operations:
 
        // Checked at build time.
        assert_eq!(Alignment:🆕:<16>().as_usize(), 16);
 
        // Checked at runtime.
        assert_eq!(Alignment::new_checked(15), None);
 
        assert_eq!(Alignment::of::<u8>().log2(), 0);
 
        assert_eq!(0x25u8.align_down(Alignment:🆕:<0x10>()), 0x20);
        assert_eq!(0x5u8.align_up(Alignment:🆕:<0x10>()), Some(0x10));
        assert_eq!(u8::MAX.align_up(Alignment:🆕:<0x10>()), None);
 
    It also includes its first use in Nova.
 
  - Add 'core::mem::{align,size}_of{,_val}' to the prelude, matching
    Rust 1.80.0.
 
  - Keep going with the steps on our migration to the standard library
    'core::ffi::CStr' type (use 'kernel::{fmt, prelude::fmt!}' and use
    upstream method names).
 
  - 'error' module: improve 'Error::from_errno' and 'to_result'
    documentation, including examples/tests.
 
  - 'sync' module: extend 'aref' submodule documentation now that it
    exists, and more updates to complete the ongoing move of 'ARef' and
    'AlwaysRefCounted' to 'sync::aref'.
 
  - 'list' module: add an example/test for 'ListLinksSelfPtr' usage.
 
  - 'alloc' module:
 
    - Implement 'Box::pin_slice()', which constructs a pinned slice of
      elements.
 
    - Provide information about the minimum alignment guarantees of
      'Kmalloc', 'Vmalloc' and 'KVmalloc'.
 
    - Take minimum alignment guarantees of allocators for
      'ForeignOwnable' into account.
 
    - Remove the 'allocator_test' (including 'Cmalloc').
 
    - Add doctest for 'Vec::as_slice()'.
 
    - Constify various methods.
 
  - 'time' module:
 
    - Add methods on 'HrTimer' that can only be called with exclusive
      access to an unarmed timer, or from timer callback context.
 
    - Add arithmetic operations to 'Instant' and 'Delta'.
 
    - Add a few convenience and access methods to 'HrTimer' and
      'Instant'.
 
 'macros' crate:
 
  - Reduce collections in 'quote!' macro.
 
 And a few other cleanups and improvements.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPjU5OPd5QIZ9jqqOGXyLc2htIW0FAmjZb3kACgkQGXyLc2ht
 IW2+PA//T23FOYFjN2M+N2qBFocL4qBK0nSjp1UnnTsJ7ohlLU3orApY/Nl2DJTq
 oO7SrWrdw6OVapvJN9IC2Jk0SfgFEiGu4L/eDg/xzkRmw89GGOOv+gp8gzz190mH
 vZS5Nbbvs1GOlALA0BxwJG0vXtAu1de284/v0CCzXms6tCxSaUSes0vB7JNNzBSW
 u73StEM5WlU3giGvnREl2lyX+jUFwG3mtfwpOuNavSYi3yO9Kg1pIIeP7ie/qrKF
 30D8X3VacO2JcGC1qpQPsFCSfIlNl0yjkVPpFi8mIQO/XEqcej3tlojXq9oyP9Tj
 tXcQL37ayBYnFSMPbelbOyTsgIyU9WwIJF+4V8u1H2C89k3f7/zqj+RMvW4y90Dc
 /43z0OwW/N5PzUQ/EyTY0JYfMeNcsOyVcGXYawD/0pZuHgOz39WHPJSdq+wMpZUy
 XESd6tr7ZHZudDsX+oq0hO1AI3pwkMvievFKG7ZtUiIcR9slv246M+WroOJcZUJ3
 6I9v/f/z9rxsIYExQA2rTHiJ0+GAuXZ5lH5x/owZEZmzN3WLCHwuMoaIp/oL6387
 y17yBpDtp6ar4B1KJILjuyjTF/kehazCNy7uiG1P8KTiCRUUTueIDYs257NMghg2
 VKkyfdABAwgnsdrGLQXgRmI09RHg0xqslgT11DiPmLVVxJYCeLI=
 =+VG2
 -----END PGP SIGNATURE-----

Merge tag 'rust-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux

Pull rust updates from Miguel Ojeda:
 "Toolchain and infrastructure:

   - Derive 'Zeroable' for all structs and unions generated by 'bindgen'
     where possible and corresponding cleanups. To do so, add the
     'pin-init' crate as a dependency to 'bindings' and 'uapi'.

     It also includes its first use in the 'cpufreq' module, with more
     to come in the next cycle.

   - Add warning to the 'rustdoc' target to detect broken 'srctree/'
     links and fix existing cases.

   - Remove support for unused (since v6.16) host '#[test]'s,
     simplifying the 'rusttest' target. Tests should generally run
     within KUnit.

  'kernel' crate:

   - Add 'ptr' module with a new 'Alignment' type, which is always a
     power of two and is used to validate that a given value is a valid
     alignment and to perform masking and alignment operations:

         // Checked at build time.
         assert_eq!(Alignment:🆕:<16>().as_usize(), 16);

         // Checked at runtime.
         assert_eq!(Alignment::new_checked(15), None);

         assert_eq!(Alignment::of::<u8>().log2(), 0);

         assert_eq!(0x25u8.align_down(Alignment:🆕:<0x10>()), 0x20);
         assert_eq!(0x5u8.align_up(Alignment:🆕:<0x10>()), Some(0x10));
         assert_eq!(u8::MAX.align_up(Alignment:🆕:<0x10>()), None);

     It also includes its first use in Nova.

   - Add 'core::mem::{align,size}_of{,_val}' to the prelude, matching
     Rust 1.80.0.

   - Keep going with the steps on our migration to the standard library
     'core::ffi::CStr' type (use 'kernel::{fmt, prelude::fmt!}' and use
     upstream method names).

   - 'error' module: improve 'Error::from_errno' and 'to_result'
     documentation, including examples/tests.

   - 'sync' module: extend 'aref' submodule documentation now that it
     exists, and more updates to complete the ongoing move of 'ARef' and
     'AlwaysRefCounted' to 'sync::aref'.

   - 'list' module: add an example/test for 'ListLinksSelfPtr' usage.

   - 'alloc' module:

      - Implement 'Box::pin_slice()', which constructs a pinned slice of
        elements.

      - Provide information about the minimum alignment guarantees of
        'Kmalloc', 'Vmalloc' and 'KVmalloc'.

      - Take minimum alignment guarantees of allocators for
        'ForeignOwnable' into account.

      - Remove the 'allocator_test' (including 'Cmalloc').

      - Add doctest for 'Vec::as_slice()'.

      - Constify various methods.

   - 'time' module:

      - Add methods on 'HrTimer' that can only be called with exclusive
        access to an unarmed timer, or from timer callback context.

      - Add arithmetic operations to 'Instant' and 'Delta'.

      - Add a few convenience and access methods to 'HrTimer' and
        'Instant'.

  'macros' crate:

   - Reduce collections in 'quote!' macro.

  And a few other cleanups and improvements"

* tag 'rust-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux: (58 commits)
  gpu: nova-core: use Alignment for alignment-related operations
  rust: add `Alignment` type
  rust: macros: reduce collections in `quote!` macro
  rust: acpi: use `core::ffi::CStr` method names
  rust: of: use `core::ffi::CStr` method names
  rust: net: use `core::ffi::CStr` method names
  rust: miscdevice: use `core::ffi::CStr` method names
  rust: kunit: use `core::ffi::CStr` method names
  rust: firmware: use `core::ffi::CStr` method names
  rust: drm: use `core::ffi::CStr` method names
  rust: cpufreq: use `core::ffi::CStr` method names
  rust: configfs: use `core::ffi::CStr` method names
  rust: auxiliary: use `core::ffi::CStr` method names
  drm/panic: use `core::ffi::CStr` method names
  rust: device: use `kernel::{fmt,prelude::fmt!}`
  rust: sync: use `kernel::{fmt,prelude::fmt!}`
  rust: seq_file: use `kernel::{fmt,prelude::fmt!}`
  rust: kunit: use `kernel::{fmt,prelude::fmt!}`
  rust: file: use `kernel::{fmt,prelude::fmt!}`
  rust: device: use `kernel::{fmt,prelude::fmt!}`
  ...
pull/1354/merge
Linus Torvalds 2025-09-30 19:12:49 -07:00
commit f4e0ff7e45
61 changed files with 1054 additions and 315 deletions

View File

@ -147,7 +147,6 @@ Numerical operations [NUMM]
Nova uses integer operations that are not part of the standard library (or not Nova uses integer operations that are not part of the standard library (or not
implemented in an optimized way for the kernel). These include: implemented in an optimized way for the kernel). These include:
- Aligning up and down to a power of two,
- The "Find Last Set Bit" (`fls` function of the C part of the kernel) - The "Find Last Set Bit" (`fls` function of the C part of the kernel)
operation. operation.

View File

@ -51,7 +51,7 @@ impl kernel::InPlaceModule for NullBlkModule {
.logical_block_size(4096)? .logical_block_size(4096)?
.physical_block_size(4096)? .physical_block_size(4096)?
.rotational(false) .rotational(false)
.build(format_args!("rnullb{}", 0), tagset) .build(fmt!("rnullb{}", 0), tagset)
})(); })();
try_pin_init!(Self { try_pin_init!(Self {

View File

@ -968,7 +968,7 @@ pub unsafe extern "C" fn drm_panic_qr_generate(
// nul-terminated string. // nul-terminated string.
let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) }; let url_cstr: &CStr = unsafe { CStr::from_char_ptr(url) };
let segments = &[ let segments = &[
&Segment::Binary(url_cstr.as_bytes()), &Segment::Binary(url_cstr.to_bytes()),
&Segment::Numeric(&data_slice[0..data_len]), &Segment::Numeric(&data_slice[0..data_len]),
]; ];
match EncodedMsg::new(segments, tmp_slice) { match EncodedMsg::new(segments, tmp_slice) {

View File

@ -3,6 +3,7 @@
use core::ops::Range; use core::ops::Range;
use kernel::prelude::*; use kernel::prelude::*;
use kernel::ptr::{Alignable, Alignment};
use kernel::sizes::*; use kernel::sizes::*;
use kernel::types::ARef; use kernel::types::ARef;
use kernel::{dev_warn, device}; use kernel::{dev_warn, device};
@ -130,10 +131,9 @@ impl FbLayout {
}; };
let frts = { let frts = {
const FRTS_DOWN_ALIGN: u64 = SZ_128K as u64; const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
const FRTS_SIZE: u64 = SZ_1M as u64; const FRTS_SIZE: u64 = SZ_1M as u64;
// TODO[NUMM]: replace with `align_down` once it lands. let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE;
let frts_base = (vga_workspace.start & !(FRTS_DOWN_ALIGN - 1)) - FRTS_SIZE;
frts_base..frts_base + FRTS_SIZE frts_base..frts_base + FRTS_SIZE
}; };

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
use kernel::{device, devres::Devres, error::code::*, pci, prelude::*, sync::Arc}; use kernel::{device, devres::Devres, error::code::*, fmt, pci, prelude::*, sync::Arc};
use crate::driver::Bar0; use crate::driver::Bar0;
use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon}; use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
@ -12,7 +12,6 @@ use crate::gfw;
use crate::regs; use crate::regs;
use crate::util; use crate::util;
use crate::vbios::Vbios; use crate::vbios::Vbios;
use core::fmt;
macro_rules! define_chipset { macro_rules! define_chipset {
({ $($variant:ident = $value:expr),* $(,)* }) => ({ $($variant:ident = $value:expr),* $(,)* }) =>

View File

@ -149,10 +149,10 @@ macro_rules! register {
// TODO[REGA]: display the raw hex value, then the value of all the fields. This requires // TODO[REGA]: display the raw hex value, then the value of all the fields. This requires
// matching the fields, which will complexify the syntax considerably... // matching the fields, which will complexify the syntax considerably...
impl ::core::fmt::Debug for $name { impl ::kernel::fmt::Debug for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
f.debug_tuple(stringify!($name)) f.debug_tuple(stringify!($name))
.field(&format_args!("0x{0:x}", &self.0)) .field(&::kernel::prelude::fmt!("0x{0:x}", &self.0))
.finish() .finish()
} }
} }

View File

@ -10,6 +10,7 @@ use kernel::device;
use kernel::error::Result; use kernel::error::Result;
use kernel::pci; use kernel::pci;
use kernel::prelude::*; use kernel::prelude::*;
use kernel::ptr::{Alignable, Alignment};
/// The offset of the VBIOS ROM in the BAR0 space. /// The offset of the VBIOS ROM in the BAR0 space.
const ROM_OFFSET: usize = 0x300000; const ROM_OFFSET: usize = 0x300000;
@ -177,8 +178,7 @@ impl<'a> Iterator for VbiosIterator<'a> {
// Advance to next image (aligned to 512 bytes). // Advance to next image (aligned to 512 bytes).
self.current_offset += image_size; self.current_offset += image_size;
// TODO[NUMM]: replace with `align_up` once it lands. self.current_offset = self.current_offset.align_up(Alignment::new::<512>())?;
self.current_offset = self.current_offset.next_multiple_of(512);
Some(Ok(full_image)) Some(Ok(full_image))
} }

View File

@ -98,6 +98,12 @@ quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
# and then retouch the generated files. # and then retouch the generated files.
rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \ rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins \
rustdoc-kernel rustdoc-pin_init rustdoc-kernel rustdoc-pin_init
$(Q)grep -Ehro '<a href="srctree/([^"]+)"' $(rustdoc_output) | \
cut -d'"' -f2 | cut -d/ -f2- | while read f; do \
if [ ! -e "$(srctree)/$$f" ]; then \
echo "warning: srctree/ link to $$f does not exist"; \
fi \
done
$(Q)cp $(srctree)/Documentation/images/logo.svg $(rustdoc_output)/static.files/ $(Q)cp $(srctree)/Documentation/images/logo.svg $(rustdoc_output)/static.files/
$(Q)cp $(srctree)/Documentation/images/COPYING-logo $(rustdoc_output)/static.files/ $(Q)cp $(srctree)/Documentation/images/COPYING-logo $(rustdoc_output)/static.files/
$(Q)find $(rustdoc_output) -name '*.html' -type f -print0 | xargs -0 sed -Ei \ $(Q)find $(rustdoc_output) -name '*.html' -type f -print0 | xargs -0 sed -Ei \
@ -193,12 +199,12 @@ rusttestlib-kernel: $(src)/kernel/lib.rs rusttestlib-bindings rusttestlib-uapi \
$(obj)/bindings.o FORCE $(obj)/bindings.o FORCE
+$(call if_changed,rustc_test_library) +$(call if_changed,rustc_test_library)
rusttestlib-bindings: private rustc_target_flags = --extern ffi rusttestlib-bindings: private rustc_target_flags = --extern ffi --extern pin_init
rusttestlib-bindings: $(src)/bindings/lib.rs rusttestlib-ffi FORCE rusttestlib-bindings: $(src)/bindings/lib.rs rusttestlib-ffi rusttestlib-pin_init FORCE
+$(call if_changed,rustc_test_library) +$(call if_changed,rustc_test_library)
rusttestlib-uapi: private rustc_target_flags = --extern ffi rusttestlib-uapi: private rustc_target_flags = --extern ffi --extern pin_init
rusttestlib-uapi: $(src)/uapi/lib.rs rusttestlib-ffi FORCE rusttestlib-uapi: $(src)/uapi/lib.rs rusttestlib-ffi rusttestlib-pin_init FORCE
+$(call if_changed,rustc_test_library) +$(call if_changed,rustc_test_library)
quiet_cmd_rustdoc_test = RUSTDOC T $< quiet_cmd_rustdoc_test = RUSTDOC T $<
@ -248,7 +254,7 @@ quiet_cmd_rustc_test = $(RUSTC_OR_CLIPPY_QUIET) T $<
$(objtree)/$(obj)/test/$(subst rusttest-,,$@) $(rust_test_quiet) \ $(objtree)/$(obj)/test/$(subst rusttest-,,$@) $(rust_test_quiet) \
$(rustc_test_run_flags) $(rustc_test_run_flags)
rusttest: rusttest-macros rusttest-kernel rusttest: rusttest-macros
rusttest-macros: private rustc_target_flags = --extern proc_macro \ rusttest-macros: private rustc_target_flags = --extern proc_macro \
--extern macros --extern kernel --extern pin_init --extern macros --extern kernel --extern pin_init
@ -258,13 +264,6 @@ rusttest-macros: $(src)/macros/lib.rs \
+$(call if_changed,rustc_test) +$(call if_changed,rustc_test)
+$(call if_changed,rustdoc_test) +$(call if_changed,rustdoc_test)
rusttest-kernel: private rustc_target_flags = --extern ffi --extern pin_init \
--extern build_error --extern macros --extern bindings --extern uapi
rusttest-kernel: $(src)/kernel/lib.rs rusttestlib-ffi rusttestlib-kernel \
rusttestlib-build_error rusttestlib-macros rusttestlib-bindings \
rusttestlib-uapi rusttestlib-pin_init FORCE
+$(call if_changed,rustc_test)
ifdef CONFIG_CC_IS_CLANG ifdef CONFIG_CC_IS_CLANG
bindgen_c_flags = $(c_flags) bindgen_c_flags = $(c_flags)
else else
@ -531,17 +530,19 @@ $(obj)/ffi.o: private skip_gendwarfksyms = 1
$(obj)/ffi.o: $(src)/ffi.rs $(obj)/compiler_builtins.o FORCE $(obj)/ffi.o: $(src)/ffi.rs $(obj)/compiler_builtins.o FORCE
+$(call if_changed_rule,rustc_library) +$(call if_changed_rule,rustc_library)
$(obj)/bindings.o: private rustc_target_flags = --extern ffi $(obj)/bindings.o: private rustc_target_flags = --extern ffi --extern pin_init
$(obj)/bindings.o: $(src)/bindings/lib.rs \ $(obj)/bindings.o: $(src)/bindings/lib.rs \
$(obj)/ffi.o \ $(obj)/ffi.o \
$(obj)/pin_init.o \
$(obj)/bindings/bindings_generated.rs \ $(obj)/bindings/bindings_generated.rs \
$(obj)/bindings/bindings_helpers_generated.rs FORCE $(obj)/bindings/bindings_helpers_generated.rs FORCE
+$(call if_changed_rule,rustc_library) +$(call if_changed_rule,rustc_library)
$(obj)/uapi.o: private rustc_target_flags = --extern ffi $(obj)/uapi.o: private rustc_target_flags = --extern ffi --extern pin_init
$(obj)/uapi.o: private skip_gendwarfksyms = 1 $(obj)/uapi.o: private skip_gendwarfksyms = 1
$(obj)/uapi.o: $(src)/uapi/lib.rs \ $(obj)/uapi.o: $(src)/uapi/lib.rs \
$(obj)/ffi.o \ $(obj)/ffi.o \
$(obj)/pin_init.o \
$(obj)/uapi/uapi_generated.rs FORCE $(obj)/uapi/uapi_generated.rs FORCE
+$(call if_changed_rule,rustc_library) +$(call if_changed_rule,rustc_library)

View File

@ -34,3 +34,8 @@
# We use const helpers to aid bindgen, to avoid conflicts when constants are # We use const helpers to aid bindgen, to avoid conflicts when constants are
# recognized, block generation of the non-helper constants. # recognized, block generation of the non-helper constants.
--blocklist-item ARCH_SLAB_MINALIGN --blocklist-item ARCH_SLAB_MINALIGN
--blocklist-item ARCH_KMALLOC_MINALIGN
# Structs should implement `Zeroable` when all of their fields do.
--with-derive-custom-struct .*=MaybeZeroable
--with-derive-custom-union .*=MaybeZeroable

View File

@ -84,6 +84,7 @@
/* `bindgen` gets confused at certain things. */ /* `bindgen` gets confused at certain things. */
const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN = ARCH_KMALLOC_MINALIGN;
const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE; const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE;
const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC;
const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL; const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL;

View File

@ -31,11 +31,19 @@
#[allow(clippy::undocumented_unsafe_blocks)] #[allow(clippy::undocumented_unsafe_blocks)]
#[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))] #[cfg_attr(CONFIG_RUSTC_HAS_UNNECESSARY_TRANSMUTES, allow(unnecessary_transmutes))]
mod bindings_raw { mod bindings_raw {
use pin_init::{MaybeZeroable, Zeroable};
// Manual definition for blocklisted types. // Manual definition for blocklisted types.
type __kernel_size_t = usize; type __kernel_size_t = usize;
type __kernel_ssize_t = isize; type __kernel_ssize_t = isize;
type __kernel_ptrdiff_t = isize; type __kernel_ptrdiff_t = isize;
// `bindgen` doesn't automatically do this, see
// <https://github.com/rust-lang/rust-bindgen/issues/3196>
//
// SAFETY: `__BindgenBitfieldUnit<Storage>` is a newtype around `Storage`.
unsafe impl<Storage> Zeroable for __BindgenBitfieldUnit<Storage> where Storage: Zeroable {}
// Use glob import here to expose all helpers. // Use glob import here to expose all helpers.
// Symbols defined within the module will take precedence to the glob import. // Symbols defined within the module will take precedence to the glob import.
pub use super::bindings_helper::*; pub use super::bindings_helper::*;

View File

@ -37,11 +37,8 @@ impl DeviceId {
/// Create a new device id from an ACPI 'id' string. /// Create a new device id from an ACPI 'id' string.
#[inline(always)] #[inline(always)]
pub const fn new(id: &'static CStr) -> Self { pub const fn new(id: &'static CStr) -> Self {
build_assert!( let src = id.to_bytes_with_nul();
id.len_with_nul() <= Self::ACPI_ID_LEN, build_assert!(src.len() <= Self::ACPI_ID_LEN, "ID exceeds 16 bytes");
"ID exceeds 16 bytes"
);
let src = id.as_bytes_with_nul();
// Replace with `bindings::acpi_device_id::default()` once stabilized for `const`. // Replace with `bindings::acpi_device_id::default()` once stabilized for `const`.
// SAFETY: FFI type is valid to be zero-initialized. // SAFETY: FFI type is valid to be zero-initialized.
let mut acpi: bindings::acpi_device_id = unsafe { core::mem::zeroed() }; let mut acpi: bindings::acpi_device_id = unsafe { core::mem::zeroed() };

View File

@ -2,18 +2,11 @@
//! Implementation of the kernel's memory allocation infrastructure. //! Implementation of the kernel's memory allocation infrastructure.
#[cfg(not(any(test, testlib)))]
pub mod allocator; pub mod allocator;
pub mod kbox; pub mod kbox;
pub mod kvec; pub mod kvec;
pub mod layout; pub mod layout;
#[cfg(any(test, testlib))]
pub mod allocator_test;
#[cfg(any(test, testlib))]
pub use self::allocator_test as allocator;
pub use self::kbox::Box; pub use self::kbox::Box;
pub use self::kbox::KBox; pub use self::kbox::KBox;
pub use self::kbox::KVBox; pub use self::kbox::KVBox;
@ -137,6 +130,14 @@ pub mod flags {
/// - Implementers must ensure that all trait functions abide by the guarantees documented in the /// - Implementers must ensure that all trait functions abide by the guarantees documented in the
/// `# Guarantees` sections. /// `# Guarantees` sections.
pub unsafe trait Allocator { pub unsafe trait Allocator {
/// The minimum alignment satisfied by all allocations from this allocator.
///
/// # Guarantees
///
/// Any pointer allocated by this allocator is guaranteed to be aligned to `MIN_ALIGN` even if
/// the requested layout has a smaller alignment.
const MIN_ALIGN: usize;
/// Allocate memory based on `layout` and `flags`. /// Allocate memory based on `layout` and `flags`.
/// ///
/// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout /// On success, returns a buffer represented as `NonNull<[u8]>` that satisfies the layout

View File

@ -17,6 +17,8 @@ use crate::alloc::{AllocError, Allocator};
use crate::bindings; use crate::bindings;
use crate::pr_warn; use crate::pr_warn;
const ARCH_KMALLOC_MINALIGN: usize = bindings::ARCH_KMALLOC_MINALIGN;
/// The contiguous kernel allocator. /// The contiguous kernel allocator.
/// ///
/// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also /// `Kmalloc` is typically used for physically contiguous allocations up to page size, but also
@ -128,6 +130,8 @@ impl Kmalloc {
// - passing a pointer to a valid memory allocation is OK, // - passing a pointer to a valid memory allocation is OK,
// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
unsafe impl Allocator for Kmalloc { unsafe impl Allocator for Kmalloc {
const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;
#[inline] #[inline]
unsafe fn realloc( unsafe fn realloc(
ptr: Option<NonNull<u8>>, ptr: Option<NonNull<u8>>,
@ -147,6 +151,8 @@ unsafe impl Allocator for Kmalloc {
// - passing a pointer to a valid memory allocation is OK, // - passing a pointer to a valid memory allocation is OK,
// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
unsafe impl Allocator for Vmalloc { unsafe impl Allocator for Vmalloc {
const MIN_ALIGN: usize = kernel::page::PAGE_SIZE;
#[inline] #[inline]
unsafe fn realloc( unsafe fn realloc(
ptr: Option<NonNull<u8>>, ptr: Option<NonNull<u8>>,
@ -171,6 +177,8 @@ unsafe impl Allocator for Vmalloc {
// - passing a pointer to a valid memory allocation is OK, // - passing a pointer to a valid memory allocation is OK,
// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. // - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same.
unsafe impl Allocator for KVmalloc { unsafe impl Allocator for KVmalloc {
const MIN_ALIGN: usize = ARCH_KMALLOC_MINALIGN;
#[inline] #[inline]
unsafe fn realloc( unsafe fn realloc(
ptr: Option<NonNull<u8>>, ptr: Option<NonNull<u8>>,

View File

@ -1,124 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
//! So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users
//! of those types (e.g. `CString`) use kernel allocators for instantiation.
//!
//! In order to allow userspace test cases to make use of such types as well, implement the
//! `Cmalloc` allocator within the `allocator_test` module and type alias all kernel allocators to
//! `Cmalloc`. The `Cmalloc` allocator uses libc's `realloc()` function as allocator backend.
#![allow(missing_docs)]
use super::{flags::*, AllocError, Allocator, Flags};
use core::alloc::Layout;
use core::cmp;
use core::ptr;
use core::ptr::NonNull;
/// The userspace allocator based on libc.
pub struct Cmalloc;
pub type Kmalloc = Cmalloc;
pub type Vmalloc = Kmalloc;
pub type KVmalloc = Kmalloc;
impl Cmalloc {
/// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of
/// `layout`.
pub fn aligned_layout(layout: Layout) -> Layout {
// Note that `layout.size()` (after padding) is guaranteed to be a multiple of
// `layout.align()` which together with the slab guarantees means that `Kmalloc` will return
// a properly aligned object (see comments in `kmalloc()` for more information).
layout.pad_to_align()
}
}
extern "C" {
#[link_name = "aligned_alloc"]
fn libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void;
#[link_name = "free"]
fn libc_free(ptr: *mut crate::ffi::c_void);
}
// SAFETY:
// - memory remains valid until it is explicitly freed,
// - passing a pointer to a valid memory allocation created by this `Allocator` is always OK,
// - `realloc` provides the guarantees as provided in the `# Guarantees` section.
unsafe impl Allocator for Cmalloc {
unsafe fn realloc(
ptr: Option<NonNull<u8>>,
layout: Layout,
old_layout: Layout,
flags: Flags,
) -> Result<NonNull<[u8]>, AllocError> {
let src = match ptr {
Some(src) => {
if old_layout.size() == 0 {
ptr::null_mut()
} else {
src.as_ptr()
}
}
None => ptr::null_mut(),
};
if layout.size() == 0 {
// SAFETY: `src` is either NULL or was previously allocated with this `Allocator`
unsafe { libc_free(src.cast()) };
return Ok(NonNull::slice_from_raw_parts(
crate::alloc::dangling_from_layout(layout),
0,
));
}
// ISO C (ISO/IEC 9899:2011) defines `aligned_alloc`:
//
// > The value of alignment shall be a valid alignment supported by the implementation
// [...].
//
// As an example of the "supported by the implementation" requirement, POSIX.1-2001 (IEEE
// 1003.1-2001) defines `posix_memalign`:
//
// > The value of alignment shall be a power of two multiple of sizeof (void *).
//
// and POSIX-based implementations of `aligned_alloc` inherit this requirement. At the time
// of writing, this is known to be the case on macOS (but not in glibc).
//
// Satisfy the stricter requirement to avoid spurious test failures on some platforms.
let min_align = core::mem::size_of::<*const crate::ffi::c_void>();
let layout = layout.align_to(min_align).map_err(|_| AllocError)?;
let layout = layout.pad_to_align();
// SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or
// exceeds the given size and alignment requirements.
let dst = unsafe { libc_aligned_alloc(layout.align(), layout.size()) }.cast::<u8>();
let dst = NonNull::new(dst).ok_or(AllocError)?;
if flags.contains(__GFP_ZERO) {
// SAFETY: The preceding calls to `libc_aligned_alloc` and `NonNull::new`
// guarantee that `dst` points to memory of at least `layout.size()` bytes.
unsafe { dst.as_ptr().write_bytes(0, layout.size()) };
}
if !src.is_null() {
// SAFETY:
// - `src` has previously been allocated with this `Allocator`; `dst` has just been
// newly allocated, hence the memory regions do not overlap.
// - both` src` and `dst` are properly aligned and valid for reads and writes
unsafe {
ptr::copy_nonoverlapping(
src,
dst.as_ptr(),
cmp::min(layout.size(), old_layout.size()),
)
};
}
// SAFETY: `src` is either NULL or was previously allocated with this `Allocator`
unsafe { libc_free(src.cast()) };
Ok(NonNull::slice_from_raw_parts(dst, layout.size()))
}
}

View File

@ -7,7 +7,6 @@ use super::allocator::{KVmalloc, Kmalloc, Vmalloc};
use super::{AllocError, Allocator, Flags}; use super::{AllocError, Allocator, Flags};
use core::alloc::Layout; use core::alloc::Layout;
use core::borrow::{Borrow, BorrowMut}; use core::borrow::{Borrow, BorrowMut};
use core::fmt;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem::ManuallyDrop; use core::mem::ManuallyDrop;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
@ -17,6 +16,7 @@ use core::ptr::NonNull;
use core::result::Result; use core::result::Result;
use crate::ffi::c_void; use crate::ffi::c_void;
use crate::fmt;
use crate::init::InPlaceInit; use crate::init::InPlaceInit;
use crate::types::ForeignOwnable; use crate::types::ForeignOwnable;
use pin_init::{InPlaceWrite, Init, PinInit, ZeroableOption}; use pin_init::{InPlaceWrite, Init, PinInit, ZeroableOption};
@ -290,6 +290,83 @@ where
Ok(Self::new(x, flags)?.into()) Ok(Self::new(x, flags)?.into())
} }
/// Construct a pinned slice of elements `Pin<Box<[T], A>>`.
///
/// This is a convenient means for creation of e.g. slices of structrures containing spinlocks
/// or mutexes.
///
/// # Examples
///
/// ```
/// use kernel::sync::{new_spinlock, SpinLock};
///
/// struct Inner {
/// a: u32,
/// b: u32,
/// }
///
/// #[pin_data]
/// struct Example {
/// c: u32,
/// #[pin]
/// d: SpinLock<Inner>,
/// }
///
/// impl Example {
/// fn new() -> impl PinInit<Self, Error> {
/// try_pin_init!(Self {
/// c: 10,
/// d <- new_spinlock!(Inner { a: 20, b: 30 }),
/// })
/// }
/// }
///
/// // Allocate a boxed slice of 10 `Example`s.
/// let s = KBox::pin_slice(
/// | _i | Example::new(),
/// 10,
/// GFP_KERNEL
/// )?;
///
/// assert_eq!(s[5].c, 10);
/// assert_eq!(s[3].d.lock().a, 20);
/// # Ok::<(), Error>(())
/// ```
pub fn pin_slice<Func, Item, E>(
mut init: Func,
len: usize,
flags: Flags,
) -> Result<Pin<Box<[T], A>>, E>
where
Func: FnMut(usize) -> Item,
Item: PinInit<T, E>,
E: From<AllocError>,
{
let mut buffer = super::Vec::<T, A>::with_capacity(len, flags)?;
for i in 0..len {
let ptr = buffer.spare_capacity_mut().as_mut_ptr().cast();
// SAFETY:
// - `ptr` is a valid pointer to uninitialized memory.
// - `ptr` is not used if an error is returned.
// - `ptr` won't be moved until it is dropped, i.e. it is pinned.
unsafe { init(i).__pinned_init(ptr)? };
// SAFETY:
// - `i + 1 <= len`, hence we don't exceed the capacity, due to the call to
// `with_capacity()` above.
// - The new value at index buffer.len() + 1 is the only element being added here, and
// it has been initialized above by `init(i).__pinned_init(ptr)`.
unsafe { buffer.inc_len(1) };
}
let (ptr, _, _) = buffer.into_raw_parts();
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
// SAFETY: `slice` points to an allocation allocated with `A` (`buffer`) and holds a valid
// `[T]`.
Ok(Pin::from(unsafe { Box::from_raw(slice) }))
}
/// Convert a [`Box<T,A>`] to a [`Pin<Box<T,A>>`]. If `T` does not implement /// Convert a [`Box<T,A>`] to a [`Pin<Box<T,A>>`]. If `T` does not implement
/// [`Unpin`], then `x` will be pinned in memory and can't be moved. /// [`Unpin`], then `x` will be pinned in memory and can't be moved.
pub fn into_pin(this: Self) -> Pin<Self> { pub fn into_pin(this: Self) -> Pin<Self> {
@ -401,12 +478,17 @@ where
} }
// SAFETY: The pointer returned by `into_foreign` comes from a well aligned // SAFETY: The pointer returned by `into_foreign` comes from a well aligned
// pointer to `T`. // pointer to `T` allocated by `A`.
unsafe impl<T: 'static, A> ForeignOwnable for Box<T, A> unsafe impl<T: 'static, A> ForeignOwnable for Box<T, A>
where where
A: Allocator, A: Allocator,
{ {
const FOREIGN_ALIGN: usize = core::mem::align_of::<T>(); const FOREIGN_ALIGN: usize = if core::mem::align_of::<T>() < A::MIN_ALIGN {
A::MIN_ALIGN
} else {
core::mem::align_of::<T>()
};
type Borrowed<'a> = &'a T; type Borrowed<'a> = &'a T;
type BorrowedMut<'a> = &'a mut T; type BorrowedMut<'a> = &'a mut T;
@ -435,12 +517,12 @@ where
} }
// SAFETY: The pointer returned by `into_foreign` comes from a well aligned // SAFETY: The pointer returned by `into_foreign` comes from a well aligned
// pointer to `T`. // pointer to `T` allocated by `A`.
unsafe impl<T: 'static, A> ForeignOwnable for Pin<Box<T, A>> unsafe impl<T: 'static, A> ForeignOwnable for Pin<Box<T, A>>
where where
A: Allocator, A: Allocator,
{ {
const FOREIGN_ALIGN: usize = core::mem::align_of::<T>(); const FOREIGN_ALIGN: usize = <Box<T, A> as ForeignOwnable>::FOREIGN_ALIGN;
type Borrowed<'a> = Pin<&'a T>; type Borrowed<'a> = Pin<&'a T>;
type BorrowedMut<'a> = Pin<&'a mut T>; type BorrowedMut<'a> = Pin<&'a mut T>;

View File

@ -7,9 +7,9 @@ use super::{
layout::ArrayLayout, layout::ArrayLayout,
AllocError, Allocator, Box, Flags, AllocError, Allocator, Box, Flags,
}; };
use crate::fmt;
use core::{ use core::{
borrow::{Borrow, BorrowMut}, borrow::{Borrow, BorrowMut},
fmt,
marker::PhantomData, marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit}, mem::{ManuallyDrop, MaybeUninit},
ops::Deref, ops::Deref,
@ -175,7 +175,7 @@ where
/// Returns the number of elements that can be stored within the vector without allocating /// Returns the number of elements that can be stored within the vector without allocating
/// additional memory. /// additional memory.
pub fn capacity(&self) -> usize { pub const fn capacity(&self) -> usize {
if const { Self::is_zst() } { if const { Self::is_zst() } {
usize::MAX usize::MAX
} else { } else {
@ -185,7 +185,7 @@ where
/// Returns the number of elements stored within the vector. /// Returns the number of elements stored within the vector.
#[inline] #[inline]
pub fn len(&self) -> usize { pub const fn len(&self) -> usize {
self.len self.len
} }
@ -196,7 +196,7 @@ where
/// - `additional` must be less than or equal to `self.capacity - self.len`. /// - `additional` must be less than or equal to `self.capacity - self.len`.
/// - All elements within the interval [`self.len`,`self.len + additional`) must be initialized. /// - All elements within the interval [`self.len`,`self.len + additional`) must be initialized.
#[inline] #[inline]
pub unsafe fn inc_len(&mut self, additional: usize) { pub const unsafe fn inc_len(&mut self, additional: usize) {
// Guaranteed by the type invariant to never underflow. // Guaranteed by the type invariant to never underflow.
debug_assert!(additional <= self.capacity() - self.len()); debug_assert!(additional <= self.capacity() - self.len());
// INVARIANT: By the safety requirements of this method this represents the exact number of // INVARIANT: By the safety requirements of this method this represents the exact number of
@ -224,6 +224,16 @@ where
} }
/// Returns a slice of the entire vector. /// Returns a slice of the entire vector.
///
/// # Examples
///
/// ```
/// let mut v = KVec::new();
/// v.push(1, GFP_KERNEL)?;
/// v.push(2, GFP_KERNEL)?;
/// assert_eq!(v.as_slice(), &[1, 2]);
/// # Ok::<(), Error>(())
/// ```
#[inline] #[inline]
pub fn as_slice(&self) -> &[T] { pub fn as_slice(&self) -> &[T] {
self self
@ -245,7 +255,7 @@ where
/// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw /// Returns a raw pointer to the vector's backing buffer, or, if `T` is a ZST, a dangling raw
/// pointer. /// pointer.
#[inline] #[inline]
pub fn as_ptr(&self) -> *const T { pub const fn as_ptr(&self) -> *const T {
self.ptr.as_ptr() self.ptr.as_ptr()
} }
@ -261,7 +271,7 @@ where
/// assert!(!v.is_empty()); /// assert!(!v.is_empty());
/// ``` /// ```
#[inline] #[inline]
pub fn is_empty(&self) -> bool { pub const fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
@ -1294,7 +1304,7 @@ impl<'vec, T> Drop for DrainAll<'vec, T> {
} }
} }
#[macros::kunit_tests(rust_kvec_kunit)] #[macros::kunit_tests(rust_kvec)]
mod tests { mod tests {
use super::*; use super::*;
use crate::prelude::*; use crate::prelude::*;

View File

@ -2,7 +2,7 @@
//! Errors for the [`Vec`] type. //! Errors for the [`Vec`] type.
use core::fmt::{self, Debug, Formatter}; use kernel::fmt::{self, Debug, Formatter};
use kernel::prelude::*; use kernel::prelude::*;
/// Error type for [`Vec::push_within_capacity`]. /// Error type for [`Vec::push_within_capacity`].

View File

@ -80,7 +80,7 @@ impl<T> ArrayLayout<T> {
/// # Safety /// # Safety
/// ///
/// `len` must be a value, for which `len * size_of::<T>() <= isize::MAX` is true. /// `len` must be a value, for which `len * size_of::<T>() <= isize::MAX` is true.
pub unsafe fn new_unchecked(len: usize) -> Self { pub const unsafe fn new_unchecked(len: usize) -> Self {
// INVARIANT: By the safety requirements of this function // INVARIANT: By the safety requirements of this function
// `len * size_of::<T>() <= isize::MAX`. // `len * size_of::<T>() <= isize::MAX`.
Self { Self {

View File

@ -105,8 +105,8 @@ pub struct DeviceId(bindings::auxiliary_device_id);
impl DeviceId { impl DeviceId {
/// Create a new [`DeviceId`] from name. /// Create a new [`DeviceId`] from name.
pub const fn new(modname: &'static CStr, name: &'static CStr) -> Self { pub const fn new(modname: &'static CStr, name: &'static CStr) -> Self {
let name = name.as_bytes_with_nul(); let name = name.to_bytes_with_nul();
let modname = modname.as_bytes_with_nul(); let modname = modname.to_bytes_with_nul();
// TODO: Replace with `bindings::auxiliary_device_id::default()` once stabilized for // TODO: Replace with `bindings::auxiliary_device_id::default()` once stabilized for
// `const`. // `const`.

View File

@ -82,7 +82,7 @@
//! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?; //! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
//! let mut disk = gen_disk::GenDiskBuilder::new() //! let mut disk = gen_disk::GenDiskBuilder::new()
//! .capacity_sectors(4096) //! .capacity_sectors(4096)
//! .build(format_args!("myblk"), tagset)?; //! .build(fmt!("myblk"), tagset)?;
//! //!
//! # Ok::<(), kernel::error::Error>(()) //! # Ok::<(), kernel::error::Error>(())
//! ``` //! ```

View File

@ -3,12 +3,12 @@
//! Generic disk abstraction. //! Generic disk abstraction.
//! //!
//! C header: [`include/linux/blkdev.h`](srctree/include/linux/blkdev.h) //! C header: [`include/linux/blkdev.h`](srctree/include/linux/blkdev.h)
//! C header: [`include/linux/blk_mq.h`](srctree/include/linux/blk_mq.h) //! C header: [`include/linux/blk-mq.h`](srctree/include/linux/blk-mq.h)
use crate::block::mq::{raw_writer::RawWriter, Operations, TagSet}; use crate::block::mq::{raw_writer::RawWriter, Operations, TagSet};
use crate::fmt::{self, Write};
use crate::{bindings, error::from_err_ptr, error::Result, sync::Arc}; use crate::{bindings, error::from_err_ptr, error::Result, sync::Arc};
use crate::{error, static_lock_class}; use crate::{error, static_lock_class};
use core::fmt::{self, Write};
/// A builder for [`GenDisk`]. /// A builder for [`GenDisk`].
/// ///

View File

@ -1,8 +1,7 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
use core::fmt::{self, Write};
use crate::error::Result; use crate::error::Result;
use crate::fmt::{self, Write};
use crate::prelude::EINVAL; use crate::prelude::EINVAL;
/// A mutable reference to a byte buffer where a string can be written into. /// A mutable reference to a byte buffer where a string can be written into.

View File

@ -263,7 +263,7 @@ impl<Data> Group<Data> {
try_pin_init!(Self { try_pin_init!(Self {
group <- pin_init::init_zeroed().chain(|v: &mut Opaque<bindings::config_group>| { group <- pin_init::init_zeroed().chain(|v: &mut Opaque<bindings::config_group>| {
let place = v.get(); let place = v.get();
let name = name.as_bytes_with_nul().as_ptr(); let name = name.to_bytes_with_nul().as_ptr();
// SAFETY: It is safe to initialize a group once it has been zeroed. // SAFETY: It is safe to initialize a group once it has been zeroed.
unsafe { unsafe {
bindings::config_group_init_type_name(place, name.cast(), item_type.as_ptr()) bindings::config_group_init_type_name(place, name.cast(), item_type.as_ptr())
@ -613,7 +613,7 @@ where
pub const fn new(name: &'static CStr) -> Self { pub const fn new(name: &'static CStr) -> Self {
Self { Self {
attribute: Opaque::new(bindings::configfs_attribute { attribute: Opaque::new(bindings::configfs_attribute {
ca_name: name.as_char_ptr(), ca_name: crate::str::as_char_ptr_in_const_context(name),
ca_owner: core::ptr::null_mut(), ca_owner: core::ptr::null_mut(),
ca_mode: 0o660, ca_mode: 0o660,
show: Some(Self::show), show: Some(Self::show),

View File

@ -109,6 +109,7 @@ impl CpuId {
/// unexpectedly due to preemption or CPU migration. It should only be /// unexpectedly due to preemption or CPU migration. It should only be
/// used when the context ensures that the task remains on the same CPU /// used when the context ensures that the task remains on the same CPU
/// or the users could use a stale (yet valid) CPU ID. /// or the users could use a stale (yet valid) CPU ID.
#[inline]
pub fn current() -> Self { pub fn current() -> Self {
// SAFETY: raw_smp_processor_id() always returns a valid CPU ID. // SAFETY: raw_smp_processor_id() always returns a valid CPU ID.
unsafe { Self::from_u32_unchecked(bindings::raw_smp_processor_id()) } unsafe { Self::from_u32_unchecked(bindings::raw_smp_processor_id()) }

View File

@ -27,7 +27,6 @@ use crate::clk::Clk;
use core::{ use core::{
cell::UnsafeCell, cell::UnsafeCell,
marker::PhantomData, marker::PhantomData,
mem::MaybeUninit,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
pin::Pin, pin::Pin,
ptr, ptr,
@ -1013,12 +1012,11 @@ impl<T: Driver> Registration<T> {
} else { } else {
None None
}, },
// SAFETY: All zeros is a valid value for `bindings::cpufreq_driver`. ..pin_init::zeroed()
..unsafe { MaybeUninit::zeroed().assume_init() }
}; };
const fn copy_name(name: &'static CStr) -> [c_char; CPUFREQ_NAME_LEN] { const fn copy_name(name: &'static CStr) -> [c_char; CPUFREQ_NAME_LEN] {
let src = name.as_bytes_with_nul(); let src = name.to_bytes_with_nul();
let mut dst = [0; CPUFREQ_NAME_LEN]; let mut dst = [0; CPUFREQ_NAME_LEN];
build_assert!(src.len() <= CPUFREQ_NAME_LEN); build_assert!(src.len() <= CPUFREQ_NAME_LEN);

View File

@ -5,10 +5,10 @@
//! C header: [`include/linux/device.h`](srctree/include/linux/device.h) //! C header: [`include/linux/device.h`](srctree/include/linux/device.h)
use crate::{ use crate::{
bindings, bindings, fmt,
types::{ARef, ForeignOwnable, Opaque}, types::{ARef, ForeignOwnable, Opaque},
}; };
use core::{fmt, marker::PhantomData, ptr}; use core::{marker::PhantomData, ptr};
#[cfg(CONFIG_PRINTK)] #[cfg(CONFIG_PRINTK)]
use crate::c_str; use crate::c_str;
@ -596,7 +596,7 @@ macro_rules! impl_device_context_into_aref {
macro_rules! dev_printk { macro_rules! dev_printk {
($method:ident, $dev:expr, $($f:tt)*) => { ($method:ident, $dev:expr, $($f:tt)*) => {
{ {
($dev).$method(::core::format_args!($($f)*)); ($dev).$method($crate::prelude::fmt!($($f)*));
} }
} }
} }

View File

@ -11,6 +11,7 @@ use crate::{
alloc::KVec, alloc::KVec,
bindings, bindings,
error::{to_result, Result}, error::{to_result, Result},
fmt,
prelude::*, prelude::*,
str::{CStr, CString}, str::{CStr, CString},
types::{ARef, Opaque}, types::{ARef, Opaque},
@ -68,16 +69,16 @@ impl FwNode {
unsafe { bindings::is_of_node(self.as_raw()) } unsafe { bindings::is_of_node(self.as_raw()) }
} }
/// Returns an object that implements [`Display`](core::fmt::Display) for /// Returns an object that implements [`Display`](fmt::Display) for
/// printing the name of a node. /// printing the name of a node.
/// ///
/// This is an alternative to the default `Display` implementation, which /// This is an alternative to the default `Display` implementation, which
/// prints the full path. /// prints the full path.
pub fn display_name(&self) -> impl core::fmt::Display + '_ { pub fn display_name(&self) -> impl fmt::Display + '_ {
struct FwNodeDisplayName<'a>(&'a FwNode); struct FwNodeDisplayName<'a>(&'a FwNode);
impl core::fmt::Display for FwNodeDisplayName<'_> { impl fmt::Display for FwNodeDisplayName<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// SAFETY: `self` is valid by its type invariant. // SAFETY: `self` is valid by its type invariant.
let name = unsafe { bindings::fwnode_get_name(self.0.as_raw()) }; let name = unsafe { bindings::fwnode_get_name(self.0.as_raw()) };
if name.is_null() { if name.is_null() {
@ -87,7 +88,7 @@ impl FwNode {
// - `fwnode_get_name` returns null or a valid C string. // - `fwnode_get_name` returns null or a valid C string.
// - `name` was checked to be non-null. // - `name` was checked to be non-null.
let name = unsafe { CStr::from_char_ptr(name) }; let name = unsafe { CStr::from_char_ptr(name) };
write!(f, "{name}") fmt::Display::fmt(name, f)
} }
} }
@ -351,8 +352,8 @@ impl FwNodeReferenceArgs {
} }
} }
impl core::fmt::Debug for FwNodeReferenceArgs { impl fmt::Debug for FwNodeReferenceArgs {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.as_slice()) write!(f, "{:?}", self.as_slice())
} }
} }
@ -377,8 +378,8 @@ enum Node<'a> {
Owned(ARef<FwNode>), Owned(ARef<FwNode>),
} }
impl core::fmt::Display for FwNode { impl fmt::Display for FwNode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// The logic here is the same as the one in lib/vsprintf.c // The logic here is the same as the one in lib/vsprintf.c
// (fwnode_full_name_string). // (fwnode_full_name_string).
@ -413,9 +414,9 @@ impl core::fmt::Display for FwNode {
// SAFETY: `fwnode_get_name_prefix` returns null or a // SAFETY: `fwnode_get_name_prefix` returns null or a
// valid C string. // valid C string.
let prefix = unsafe { CStr::from_char_ptr(prefix) }; let prefix = unsafe { CStr::from_char_ptr(prefix) };
write!(f, "{prefix}")?; fmt::Display::fmt(prefix, f)?;
} }
write!(f, "{}", fwnode.display_name())?; fmt::Display::fmt(&fwnode.display_name(), f)?;
} }
Ok(()) Ok(())

View File

@ -9,8 +9,8 @@ use crate::{
device::{Bound, Core}, device::{Bound, Core},
error::{to_result, Result}, error::{to_result, Result},
prelude::*, prelude::*,
sync::aref::ARef,
transmute::{AsBytes, FromBytes}, transmute::{AsBytes, FromBytes},
types::ARef,
}; };
/// Trait to be implemented by DMA capable bus devices. /// Trait to be implemented by DMA capable bus devices.

View File

@ -2,7 +2,7 @@
//! DRM device. //! DRM device.
//! //!
//! C header: [`include/linux/drm/drm_device.h`](srctree/include/linux/drm/drm_device.h) //! C header: [`include/drm/drm_device.h`](srctree/include/drm/drm_device.h)
use crate::{ use crate::{
alloc::allocator::Kmalloc, alloc::allocator::Kmalloc,
@ -82,8 +82,8 @@ impl<T: drm::Driver> Device<T> {
major: T::INFO.major, major: T::INFO.major,
minor: T::INFO.minor, minor: T::INFO.minor,
patchlevel: T::INFO.patchlevel, patchlevel: T::INFO.patchlevel,
name: T::INFO.name.as_char_ptr().cast_mut(), name: crate::str::as_char_ptr_in_const_context(T::INFO.name).cast_mut(),
desc: T::INFO.desc.as_char_ptr().cast_mut(), desc: crate::str::as_char_ptr_in_const_context(T::INFO.desc).cast_mut(),
driver_features: drm::driver::FEAT_GEM, driver_features: drm::driver::FEAT_GEM,
ioctls: T::IOCTLS.as_ptr(), ioctls: T::IOCTLS.as_ptr(),

View File

@ -2,7 +2,7 @@
//! DRM driver core. //! DRM driver core.
//! //!
//! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h) //! C header: [`include/drm/drm_drv.h`](srctree/include/drm/drm_drv.h)
use crate::{ use crate::{
bindings, device, devres, drm, bindings, device, devres, drm,

View File

@ -2,7 +2,7 @@
//! DRM File objects. //! DRM File objects.
//! //!
//! C header: [`include/linux/drm/drm_file.h`](srctree/include/linux/drm/drm_file.h) //! C header: [`include/drm/drm_file.h`](srctree/include/drm/drm_file.h)
use crate::{bindings, drm, error::Result, prelude::*, types::Opaque}; use crate::{bindings, drm, error::Result, prelude::*, types::Opaque};
use core::marker::PhantomData; use core::marker::PhantomData;

View File

@ -2,7 +2,7 @@
//! DRM GEM API //! DRM GEM API
//! //!
//! C header: [`include/linux/drm/drm_gem.h`](srctree/include/linux/drm/drm_gem.h) //! C header: [`include/drm/drm_gem.h`](srctree/include/drm/drm_gem.h)
use crate::{ use crate::{
alloc::flags::*, alloc::flags::*,

View File

@ -2,7 +2,7 @@
//! DRM IOCTL definitions. //! DRM IOCTL definitions.
//! //!
//! C header: [`include/linux/drm/drm_ioctl.h`](srctree/include/linux/drm/drm_ioctl.h) //! C header: [`include/drm/drm_ioctl.h`](srctree/include/drm/drm_ioctl.h)
use crate::ioctl; use crate::ioctl;

View File

@ -2,7 +2,9 @@
//! Kernel errors. //! Kernel errors.
//! //!
//! C header: [`include/uapi/asm-generic/errno-base.h`](srctree/include/uapi/asm-generic/errno-base.h) //! C header: [`include/uapi/asm-generic/errno-base.h`](srctree/include/uapi/asm-generic/errno-base.h)\
//! C header: [`include/uapi/asm-generic/errno.h`](srctree/include/uapi/asm-generic/errno.h)\
//! C header: [`include/linux/errno.h`](srctree/include/linux/errno.h)
use crate::{ use crate::{
alloc::{layout::LayoutError, AllocError}, alloc::{layout::LayoutError, AllocError},
@ -101,8 +103,23 @@ pub struct Error(NonZeroI32);
impl Error { impl Error {
/// Creates an [`Error`] from a kernel error code. /// Creates an [`Error`] from a kernel error code.
/// ///
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would /// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`).
/// be returned in such a case. ///
/// It is a bug to pass an out-of-range `errno`. [`code::EINVAL`] is returned in such a case.
///
/// # Examples
///
/// ```
/// assert_eq!(Error::from_errno(-1), EPERM);
/// assert_eq!(Error::from_errno(-2), ENOENT);
/// ```
///
/// The following calls are considered a bug:
///
/// ```
/// assert_eq!(Error::from_errno(0), EINVAL);
/// assert_eq!(Error::from_errno(-1000000), EINVAL);
/// ```
pub fn from_errno(errno: crate::ffi::c_int) -> Error { pub fn from_errno(errno: crate::ffi::c_int) -> Error {
if let Some(error) = Self::try_from_errno(errno) { if let Some(error) = Self::try_from_errno(errno) {
error error
@ -158,7 +175,7 @@ impl Error {
} }
/// Returns a string representing the error, if one exists. /// Returns a string representing the error, if one exists.
#[cfg(not(any(test, testlib)))] #[cfg(not(testlib))]
pub fn name(&self) -> Option<&'static CStr> { pub fn name(&self) -> Option<&'static CStr> {
// SAFETY: Just an FFI call, there are no extra safety requirements. // SAFETY: Just an FFI call, there are no extra safety requirements.
let ptr = unsafe { bindings::errname(-self.0.get()) }; let ptr = unsafe { bindings::errname(-self.0.get()) };
@ -175,7 +192,7 @@ impl Error {
/// When `testlib` is configured, this always returns `None` to avoid the dependency on a /// When `testlib` is configured, this always returns `None` to avoid the dependency on a
/// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still /// kernel function so that tests that use this (e.g., by calling [`Result::unwrap`]) can still
/// run in userspace. /// run in userspace.
#[cfg(any(test, testlib))] #[cfg(testlib)]
pub fn name(&self) -> Option<&'static CStr> { pub fn name(&self) -> Option<&'static CStr> {
None None
} }
@ -375,8 +392,43 @@ impl From<core::convert::Infallible> for Error {
/// [Rust documentation]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html /// [Rust documentation]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html
pub type Result<T = (), E = Error> = core::result::Result<T, E>; pub type Result<T = (), E = Error> = core::result::Result<T, E>;
/// Converts an integer as returned by a C kernel function to an error if it's negative, and /// Converts an integer as returned by a C kernel function to a [`Result`].
/// `Ok(())` otherwise. ///
/// If the integer is negative, an [`Err`] with an [`Error`] as given by [`Error::from_errno`] is
/// returned. This means the integer must be `>= -MAX_ERRNO`.
///
/// Otherwise, it returns [`Ok`].
///
/// It is a bug to pass an out-of-range negative integer. `Err(EINVAL)` is returned in such a case.
///
/// # Examples
///
/// This function may be used to easily perform early returns with the [`?`] operator when working
/// with C APIs within Rust abstractions:
///
/// ```
/// # use kernel::error::to_result;
/// # mod bindings {
/// # #![expect(clippy::missing_safety_doc)]
/// # use kernel::prelude::*;
/// # pub(super) unsafe fn f1() -> c_int { 0 }
/// # pub(super) unsafe fn f2() -> c_int { EINVAL.to_errno() }
/// # }
/// fn f() -> Result {
/// // SAFETY: ...
/// to_result(unsafe { bindings::f1() })?;
///
/// // SAFETY: ...
/// to_result(unsafe { bindings::f2() })?;
///
/// // ...
///
/// Ok(())
/// }
/// # assert_eq!(f(), Err(EINVAL));
/// ```
///
/// [`?`]: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
pub fn to_result(err: crate::ffi::c_int) -> Result { pub fn to_result(err: crate::ffi::c_int) -> Result {
if err < 0 { if err < 0 {
Err(Error::from_errno(err)) Err(Error::from_errno(err))

View File

@ -291,7 +291,7 @@ impl<const N: usize> ModInfoBuilder<N> {
let module_name = this.module_name; let module_name = this.module_name;
if !this.module_name.is_empty() { if !this.module_name.is_empty() {
this = this.push_internal(module_name.as_bytes_with_nul()); this = this.push_internal(module_name.to_bytes_with_nul());
if N != 0 { if N != 0 {
// Re-use the space taken by the NULL terminator and swap it with the '.' separator. // Re-use the space taken by the NULL terminator and swap it with the '.' separator.

View File

@ -11,6 +11,7 @@ use crate::{
bindings, bindings,
cred::Credential, cred::Credential,
error::{code::*, to_result, Error, Result}, error::{code::*, to_result, Error, Result},
fmt,
sync::aref::{ARef, AlwaysRefCounted}, sync::aref::{ARef, AlwaysRefCounted},
types::{NotThreadSafe, Opaque}, types::{NotThreadSafe, Opaque},
}; };
@ -460,8 +461,8 @@ impl From<BadFdError> for Error {
} }
} }
impl core::fmt::Debug for BadFdError { impl fmt::Debug for BadFdError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad("EBADF") f.pad("EBADF")
} }
} }

View File

@ -6,8 +6,8 @@
//! //!
//! Reference: <https://docs.kernel.org/dev-tools/kunit/index.html> //! Reference: <https://docs.kernel.org/dev-tools/kunit/index.html>
use crate::fmt;
use crate::prelude::*; use crate::prelude::*;
use core::fmt;
#[cfg(CONFIG_PRINTK)] #[cfg(CONFIG_PRINTK)]
use crate::c_str; use crate::c_str;
@ -74,14 +74,14 @@ macro_rules! kunit_assert {
// mistake (it is hidden to prevent that). // mistake (it is hidden to prevent that).
// //
// This mimics KUnit's failed assertion format. // This mimics KUnit's failed assertion format.
$crate::kunit::err(format_args!( $crate::kunit::err($crate::prelude::fmt!(
" # {}: ASSERTION FAILED at {FILE}:{LINE}\n", " # {}: ASSERTION FAILED at {FILE}:{LINE}\n",
$name $name
)); ));
$crate::kunit::err(format_args!( $crate::kunit::err($crate::prelude::fmt!(
" Expected {CONDITION} to be true, but is false\n" " Expected {CONDITION} to be true, but is false\n"
)); ));
$crate::kunit::err(format_args!( $crate::kunit::err($crate::prelude::fmt!(
" Failure not reported to KUnit since this is a non-KUnit task\n" " Failure not reported to KUnit since this is a non-KUnit task\n"
)); ));
break 'out; break 'out;
@ -102,12 +102,12 @@ macro_rules! kunit_assert {
unsafe impl Sync for UnaryAssert {} unsafe impl Sync for UnaryAssert {}
static LOCATION: Location = Location($crate::bindings::kunit_loc { static LOCATION: Location = Location($crate::bindings::kunit_loc {
file: FILE.as_char_ptr(), file: $crate::str::as_char_ptr_in_const_context(FILE),
line: LINE, line: LINE,
}); });
static ASSERTION: UnaryAssert = UnaryAssert($crate::bindings::kunit_unary_assert { static ASSERTION: UnaryAssert = UnaryAssert($crate::bindings::kunit_unary_assert {
assert: $crate::bindings::kunit_assert {}, assert: $crate::bindings::kunit_assert {},
condition: CONDITION.as_char_ptr(), condition: $crate::str::as_char_ptr_in_const_context(CONDITION),
expected_true: true, expected_true: true,
}); });
@ -202,7 +202,7 @@ pub const fn kunit_case(
) -> kernel::bindings::kunit_case { ) -> kernel::bindings::kunit_case {
kernel::bindings::kunit_case { kernel::bindings::kunit_case {
run_case: Some(run_case), run_case: Some(run_case),
name: name.as_char_ptr(), name: kernel::str::as_char_ptr_in_const_context(name),
attr: kernel::bindings::kunit_attributes { attr: kernel::bindings::kunit_attributes {
speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
}, },

View File

@ -17,6 +17,7 @@
// the unstable features in use. // the unstable features in use.
// //
// Stable since Rust 1.79.0. // Stable since Rust 1.79.0.
#![feature(generic_nonzero)]
#![feature(inline_const)] #![feature(inline_const)]
// //
// Stable since Rust 1.81.0. // Stable since Rust 1.81.0.
@ -28,6 +29,7 @@
// Stable since Rust 1.83.0. // Stable since Rust 1.83.0.
#![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_as_mut_ptr)]
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
#![feature(const_option)]
#![feature(const_ptr_write)] #![feature(const_ptr_write)]
#![feature(const_refs_to_cell)] #![feature(const_refs_to_cell)]
// //
@ -110,6 +112,7 @@ pub mod pid_namespace;
pub mod platform; pub mod platform;
pub mod prelude; pub mod prelude;
pub mod print; pub mod print;
pub mod ptr;
pub mod rbtree; pub mod rbtree;
pub mod regulator; pub mod regulator;
pub mod revocable; pub mod revocable;
@ -206,7 +209,7 @@ impl ThisModule {
} }
} }
#[cfg(not(any(testlib, test)))] #[cfg(not(testlib))]
#[panic_handler] #[panic_handler]
fn panic(info: &core::panic::PanicInfo<'_>) -> ! { fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
pr_emerg!("{}\n", info); pr_emerg!("{}\n", info);

View File

@ -38,6 +38,8 @@ pub use self::arc_field::{define_list_arc_field_getter, ListArcField};
/// ///
/// # Examples /// # Examples
/// ///
/// Use [`ListLinks`] as the type of the intrusive field.
///
/// ``` /// ```
/// use kernel::list::*; /// use kernel::list::*;
/// ///
@ -140,6 +142,124 @@ pub use self::arc_field::{define_list_arc_field_getter, ListArcField};
/// } /// }
/// # Result::<(), Error>::Ok(()) /// # Result::<(), Error>::Ok(())
/// ``` /// ```
///
/// Use [`ListLinksSelfPtr`] as the type of the intrusive field. This allows a list of trait object
/// type.
///
/// ```
/// use kernel::list::*;
///
/// trait Foo {
/// fn foo(&self) -> (&'static str, i32);
/// }
///
/// #[pin_data]
/// struct DTWrap<T: ?Sized> {
/// #[pin]
/// links: ListLinksSelfPtr<DTWrap<dyn Foo>>,
/// value: T,
/// }
///
/// impl<T> DTWrap<T> {
/// fn new(value: T) -> Result<ListArc<Self>> {
/// ListArc::pin_init(try_pin_init!(Self {
/// value,
/// links <- ListLinksSelfPtr::new(),
/// }), GFP_KERNEL)
/// }
/// }
///
/// impl_list_arc_safe! {
/// impl{T: ?Sized} ListArcSafe<0> for DTWrap<T> { untracked; }
/// }
/// impl_list_item! {
/// impl ListItem<0> for DTWrap<dyn Foo> { using ListLinksSelfPtr { self.links }; }
/// }
///
/// // Create a new empty list.
/// let mut list = List::<DTWrap<dyn Foo>>::new();
/// {
/// assert!(list.is_empty());
/// }
///
/// struct A(i32);
/// // `A` returns the inner value for `foo`.
/// impl Foo for A { fn foo(&self) -> (&'static str, i32) { ("a", self.0) } }
///
/// struct B;
/// // `B` always returns 42.
/// impl Foo for B { fn foo(&self) -> (&'static str, i32) { ("b", 42) } }
///
/// // Insert 3 element using `push_back()`.
/// list.push_back(DTWrap::new(A(15))?);
/// list.push_back(DTWrap::new(A(32))?);
/// list.push_back(DTWrap::new(B)?);
///
/// // Iterate over the list to verify the nodes were inserted correctly.
/// // [A(15), A(32), B]
/// {
/// let mut iter = list.iter();
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("a", 15));
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("a", 32));
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("b", 42));
/// assert!(iter.next().is_none());
///
/// // Verify the length of the list.
/// assert_eq!(list.iter().count(), 3);
/// }
///
/// // Pop the items from the list using `pop_back()` and verify the content.
/// {
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("b", 42));
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 32));
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 15));
/// }
///
/// // Insert 3 elements using `push_front()`.
/// list.push_front(DTWrap::new(A(15))?);
/// list.push_front(DTWrap::new(A(32))?);
/// list.push_front(DTWrap::new(B)?);
///
/// // Iterate over the list to verify the nodes were inserted correctly.
/// // [B, A(32), A(15)]
/// {
/// let mut iter = list.iter();
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("b", 42));
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("a", 32));
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("a", 15));
/// assert!(iter.next().is_none());
///
/// // Verify the length of the list.
/// assert_eq!(list.iter().count(), 3);
/// }
///
/// // Pop the items from the list using `pop_front()` and verify the content.
/// {
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 15));
/// assert_eq!(list.pop_back().ok_or(EINVAL)?.value.foo(), ("a", 32));
/// }
///
/// // Push `list2` to `list` through `push_all_back()`.
/// // list: [B]
/// // list2: [B, A(25)]
/// {
/// let mut list2 = List::<DTWrap<dyn Foo>>::new();
/// list2.push_back(DTWrap::new(B)?);
/// list2.push_back(DTWrap::new(A(25))?);
///
/// list.push_all_back(&mut list2);
///
/// // list: [B, B, A(25)]
/// // list2: []
/// let mut iter = list.iter();
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("b", 42));
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("b", 42));
/// assert_eq!(iter.next().ok_or(EINVAL)?.value.foo(), ("a", 25));
/// assert!(iter.next().is_none());
/// assert!(list2.is_empty());
/// }
/// # Result::<(), Error>::Ok(())
/// ```
pub struct List<T: ?Sized + ListItem<ID>, const ID: u64 = 0> { pub struct List<T: ?Sized + ListItem<ID>, const ID: u64 = 0> {
first: *mut ListLinksFields, first: *mut ListLinksFields,
_ty: PhantomData<ListArc<T, ID>>, _ty: PhantomData<ListArc<T, ID>>,

View File

@ -34,7 +34,7 @@ impl MiscDeviceOptions {
// SAFETY: All zeros is valid for this C type. // SAFETY: All zeros is valid for this C type.
let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() }; let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() };
result.minor = bindings::MISC_DYNAMIC_MINOR as ffi::c_int; result.minor = bindings::MISC_DYNAMIC_MINOR as ffi::c_int;
result.name = self.name.as_char_ptr(); result.name = crate::str::as_char_ptr_in_const_context(self.name);
result.fops = MiscdeviceVTable::<T>::build(); result.fops = MiscdeviceVTable::<T>::build();
result result
} }

View File

@ -497,7 +497,7 @@ unsafe impl Sync for DriverVTable {}
pub const fn create_phy_driver<T: Driver>() -> DriverVTable { pub const fn create_phy_driver<T: Driver>() -> DriverVTable {
// INVARIANT: All the fields of `struct phy_driver` are initialized properly. // INVARIANT: All the fields of `struct phy_driver` are initialized properly.
DriverVTable(Opaque::new(bindings::phy_driver { DriverVTable(Opaque::new(bindings::phy_driver {
name: T::NAME.as_char_ptr().cast_mut(), name: crate::str::as_char_ptr_in_const_context(T::NAME).cast_mut(),
flags: T::FLAGS, flags: T::FLAGS,
phy_id: T::PHY_DEVICE_ID.id(), phy_id: T::PHY_DEVICE_ID.id(),
phy_id_mask: T::PHY_DEVICE_ID.mask_as_int(), phy_id_mask: T::PHY_DEVICE_ID.mask_as_int(),

View File

@ -34,7 +34,7 @@ unsafe impl RawDeviceIdIndex for DeviceId {
impl DeviceId { impl DeviceId {
/// Create a new device id from an OF 'compatible' string. /// Create a new device id from an OF 'compatible' string.
pub const fn new(compatible: &'static CStr) -> Self { pub const fn new(compatible: &'static CStr) -> Self {
let src = compatible.as_bytes_with_nul(); let src = compatible.to_bytes_with_nul();
// Replace with `bindings::of_device_id::default()` once stabilized for `const`. // Replace with `bindings::of_device_id::default()` once stabilized for `const`.
// SAFETY: FFI type is valid to be zero-initialized. // SAFETY: FFI type is valid to be zero-initialized.
let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() }; let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() };

View File

@ -12,7 +12,10 @@
//! ``` //! ```
#[doc(no_inline)] #[doc(no_inline)]
pub use core::pin::Pin; pub use core::{
mem::{align_of, align_of_val, size_of, size_of_val},
pin::Pin,
};
pub use ::ffi::{ pub use ::ffi::{
c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,

228
rust/kernel/ptr.rs Normal file
View File

@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-2.0
//! Types and functions to work with pointers and addresses.
use core::fmt::Debug;
use core::mem::align_of;
use core::num::NonZero;
use crate::build_assert;
/// Type representing an alignment, which is always a power of two.
///
/// It is used to validate that a given value is a valid alignment, and to perform masking and
/// alignment operations.
///
/// This is a temporary substitute for the [`Alignment`] nightly type from the standard library,
/// and to be eventually replaced by it.
///
/// [`Alignment`]: https://github.com/rust-lang/rust/issues/102070
///
/// # Invariants
///
/// An alignment is always a power of two.
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Alignment(NonZero<usize>);
impl Alignment {
/// Validates that `ALIGN` is a power of two at build-time, and returns an [`Alignment`] of the
/// same value.
///
/// A build error is triggered if `ALIGN` is not a power of two.
///
/// # Examples
///
/// ```
/// use kernel::ptr::Alignment;
///
/// let v = Alignment::new::<16>();
/// assert_eq!(v.as_usize(), 16);
/// ```
#[inline(always)]
pub const fn new<const ALIGN: usize>() -> Self {
build_assert!(
ALIGN.is_power_of_two(),
"Provided alignment is not a power of two."
);
// INVARIANT: `align` is a power of two.
// SAFETY: `align` is a power of two, and thus non-zero.
Self(unsafe { NonZero::new_unchecked(ALIGN) })
}
/// Validates that `align` is a power of two at runtime, and returns an
/// [`Alignment`] of the same value.
///
/// Returns [`None`] if `align` is not a power of two.
///
/// # Examples
///
/// ```
/// use kernel::ptr::Alignment;
///
/// assert_eq!(Alignment::new_checked(16), Some(Alignment::new::<16>()));
/// assert_eq!(Alignment::new_checked(15), None);
/// assert_eq!(Alignment::new_checked(1), Some(Alignment::new::<1>()));
/// assert_eq!(Alignment::new_checked(0), None);
/// ```
#[inline(always)]
pub const fn new_checked(align: usize) -> Option<Self> {
if align.is_power_of_two() {
// INVARIANT: `align` is a power of two.
// SAFETY: `align` is a power of two, and thus non-zero.
Some(Self(unsafe { NonZero::new_unchecked(align) }))
} else {
None
}
}
/// Returns the alignment of `T`.
///
/// This is equivalent to [`align_of`], but with the return value provided as an [`Alignment`].
#[inline(always)]
pub const fn of<T>() -> Self {
#![allow(clippy::incompatible_msrv)]
// This cannot panic since alignments are always powers of two.
//
// We unfortunately cannot use `new` as it would require the `generic_const_exprs` feature.
const { Alignment::new_checked(align_of::<T>()).unwrap() }
}
/// Returns this alignment as a [`usize`].
///
/// It is guaranteed to be a power of two.
///
/// # Examples
///
/// ```
/// use kernel::ptr::Alignment;
///
/// assert_eq!(Alignment::new::<16>().as_usize(), 16);
/// ```
#[inline(always)]
pub const fn as_usize(self) -> usize {
self.as_nonzero().get()
}
/// Returns this alignment as a [`NonZero`].
///
/// It is guaranteed to be a power of two.
///
/// # Examples
///
/// ```
/// use kernel::ptr::Alignment;
///
/// assert_eq!(Alignment::new::<16>().as_nonzero().get(), 16);
/// ```
#[inline(always)]
pub const fn as_nonzero(self) -> NonZero<usize> {
// Allow the compiler to know that the value is indeed a power of two. This can help
// optimize some operations down the line, like e.g. replacing divisions by bit shifts.
if !self.0.is_power_of_two() {
// SAFETY: Per the invariants, `self.0` is always a power of two so this block will
// never be reached.
unsafe { core::hint::unreachable_unchecked() }
}
self.0
}
/// Returns the base-2 logarithm of the alignment.
///
/// # Examples
///
/// ```
/// use kernel::ptr::Alignment;
///
/// assert_eq!(Alignment::of::<u8>().log2(), 0);
/// assert_eq!(Alignment::new::<16>().log2(), 4);
/// ```
#[inline(always)]
pub const fn log2(self) -> u32 {
self.0.ilog2()
}
/// Returns the mask for this alignment.
///
/// This is equivalent to `!(self.as_usize() - 1)`.
///
/// # Examples
///
/// ```
/// use kernel::ptr::Alignment;
///
/// assert_eq!(Alignment::new::<0x10>().mask(), !0xf);
/// ```
#[inline(always)]
pub const fn mask(self) -> usize {
// No underflow can occur as the alignment is guaranteed to be a power of two, and thus is
// non-zero.
!(self.as_usize() - 1)
}
}
/// Trait for items that can be aligned against an [`Alignment`].
pub trait Alignable: Sized {
/// Aligns `self` down to `alignment`.
///
/// # Examples
///
/// ```
/// use kernel::ptr::{Alignable, Alignment};
///
/// assert_eq!(0x2f_usize.align_down(Alignment::new::<0x10>()), 0x20);
/// assert_eq!(0x30usize.align_down(Alignment::new::<0x10>()), 0x30);
/// assert_eq!(0xf0u8.align_down(Alignment::new::<0x1000>()), 0x0);
/// ```
fn align_down(self, alignment: Alignment) -> Self;
/// Aligns `self` up to `alignment`, returning `None` if aligning would result in an overflow.
///
/// # Examples
///
/// ```
/// use kernel::ptr::{Alignable, Alignment};
///
/// assert_eq!(0x4fusize.align_up(Alignment::new::<0x10>()), Some(0x50));
/// assert_eq!(0x40usize.align_up(Alignment::new::<0x10>()), Some(0x40));
/// assert_eq!(0x0usize.align_up(Alignment::new::<0x10>()), Some(0x0));
/// assert_eq!(u8::MAX.align_up(Alignment::new::<0x10>()), None);
/// assert_eq!(0x10u8.align_up(Alignment::new::<0x100>()), None);
/// assert_eq!(0x0u8.align_up(Alignment::new::<0x100>()), Some(0x0));
/// ```
fn align_up(self, alignment: Alignment) -> Option<Self>;
}
/// Implement [`Alignable`] for unsigned integer types.
macro_rules! impl_alignable_uint {
($($t:ty),*) => {
$(
impl Alignable for $t {
#[inline(always)]
fn align_down(self, alignment: Alignment) -> Self {
// The operands of `&` need to be of the same type so convert the alignment to
// `Self`. This means we need to compute the mask ourselves.
::core::num::NonZero::<Self>::try_from(alignment.as_nonzero())
.map(|align| self & !(align.get() - 1))
// An alignment larger than `Self` always aligns down to `0`.
.unwrap_or(0)
}
#[inline(always)]
fn align_up(self, alignment: Alignment) -> Option<Self> {
let aligned_down = self.align_down(alignment);
if self == aligned_down {
Some(aligned_down)
} else {
Self::try_from(alignment.as_usize())
.ok()
.and_then(|align| aligned_down.checked_add(align))
}
}
}
)*
};
}
impl_alignable_uint!(u8, u16, u32, u64, usize);

View File

@ -4,7 +4,7 @@
//! //!
//! C header: [`include/linux/seq_file.h`](srctree/include/linux/seq_file.h) //! C header: [`include/linux/seq_file.h`](srctree/include/linux/seq_file.h)
use crate::{bindings, c_str, types::NotThreadSafe, types::Opaque}; use crate::{bindings, c_str, fmt, types::NotThreadSafe, types::Opaque};
/// A utility for generating the contents of a seq file. /// A utility for generating the contents of a seq file.
#[repr(transparent)] #[repr(transparent)]
@ -31,7 +31,7 @@ impl SeqFile {
/// Used by the [`seq_print`] macro. /// Used by the [`seq_print`] macro.
#[inline] #[inline]
pub fn call_printf(&self, args: core::fmt::Arguments<'_>) { pub fn call_printf(&self, args: fmt::Arguments<'_>) {
// SAFETY: Passing a void pointer to `Arguments` is valid for `%pA`. // SAFETY: Passing a void pointer to `Arguments` is valid for `%pA`.
unsafe { unsafe {
bindings::seq_printf( bindings::seq_printf(
@ -47,7 +47,7 @@ impl SeqFile {
#[macro_export] #[macro_export]
macro_rules! seq_print { macro_rules! seq_print {
($m:expr, $($arg:tt)+) => ( ($m:expr, $($arg:tt)+) => (
$m.call_printf(format_args!($($arg)+)) $m.call_printf($crate::prelude::fmt!($($arg)+))
); );
} }
pub use seq_print; pub use seq_print;

View File

@ -19,6 +19,7 @@
use crate::{ use crate::{
alloc::{AllocError, Flags, KBox}, alloc::{AllocError, Flags, KBox},
ffi::c_void, ffi::c_void,
fmt,
init::InPlaceInit, init::InPlaceInit,
sync::Refcount, sync::Refcount,
try_init, try_init,
@ -27,7 +28,6 @@ use crate::{
use core::{ use core::{
alloc::Layout, alloc::Layout,
borrow::{Borrow, BorrowMut}, borrow::{Borrow, BorrowMut},
fmt,
marker::PhantomData, marker::PhantomData,
mem::{ManuallyDrop, MaybeUninit}, mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
@ -367,10 +367,10 @@ impl<T: ?Sized> Arc<T> {
} }
} }
// SAFETY: The pointer returned by `into_foreign` comes from a well aligned // SAFETY: The pointer returned by `into_foreign` was originally allocated as an
// pointer to `ArcInner<T>`. // `KBox<ArcInner<T>>`, so that type is what determines the alignment.
unsafe impl<T: 'static> ForeignOwnable for Arc<T> { unsafe impl<T: 'static> ForeignOwnable for Arc<T> {
const FOREIGN_ALIGN: usize = core::mem::align_of::<ArcInner<T>>(); const FOREIGN_ALIGN: usize = <KBox<ArcInner<T>> as ForeignOwnable>::FOREIGN_ALIGN;
type Borrowed<'a> = ArcBorrow<'a, T>; type Borrowed<'a> = ArcBorrow<'a, T>;
type BorrowedMut<'a> = Self::Borrowed<'a>; type BorrowedMut<'a> = Self::Borrowed<'a>;

View File

@ -1,6 +1,21 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
//! Internal reference counting support. //! Internal reference counting support.
//!
//! Many C types already have their own reference counting mechanism (e.g. by storing a
//! `refcount_t`). This module provides support for directly using their internal reference count
//! from Rust; instead of making users have to use an additional Rust-reference count in the form of
//! [`Arc`].
//!
//! The smart pointer [`ARef<T>`] acts similarly to [`Arc<T>`] in that it holds a refcount on the
//! underlying object, but this refcount is internal to the object. It essentially is a Rust
//! implementation of the `get_` and `put_` pattern used in C for reference counting.
//!
//! To make use of [`ARef<MyType>`], `MyType` needs to implement [`AlwaysRefCounted`]. It is a trait
//! for accessing the internal reference count of an object of the `MyType` type.
//!
//! [`Arc`]: crate::sync::Arc
//! [`Arc<T>`]: crate::sync::Arc
use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull}; use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull};
@ -97,7 +112,7 @@ impl<T: AlwaysRefCounted> ARef<T> {
/// ///
/// ``` /// ```
/// use core::ptr::NonNull; /// use core::ptr::NonNull;
/// use kernel::types::{ARef, AlwaysRefCounted}; /// use kernel::sync::aref::{ARef, AlwaysRefCounted};
/// ///
/// struct Empty {} /// struct Empty {}
/// ///

View File

@ -9,7 +9,8 @@ use crate::{
ffi::{c_int, c_long, c_uint}, ffi::{c_int, c_long, c_uint},
mm::MmWithUser, mm::MmWithUser,
pid_namespace::PidNamespace, pid_namespace::PidNamespace,
types::{ARef, NotThreadSafe, Opaque}, sync::aref::ARef,
types::{NotThreadSafe, Opaque},
}; };
use core::{ use core::{
cmp::{Eq, PartialEq}, cmp::{Eq, PartialEq},
@ -76,7 +77,7 @@ macro_rules! current {
/// incremented when creating `State` and decremented when it is dropped: /// incremented when creating `State` and decremented when it is dropped:
/// ///
/// ``` /// ```
/// use kernel::{task::Task, types::ARef}; /// use kernel::{task::Task, sync::aref::ARef};
/// ///
/// struct State { /// struct State {
/// creator: ARef<Task>, /// creator: ARef<Task>,
@ -347,7 +348,7 @@ impl CurrentTask {
} }
// SAFETY: The type invariants guarantee that `Task` is always refcounted. // SAFETY: The type invariants guarantee that `Task` is always refcounted.
unsafe impl crate::types::AlwaysRefCounted for Task { unsafe impl crate::sync::aref::AlwaysRefCounted for Task {
#[inline] #[inline]
fn inc_ref(&self) { fn inc_ref(&self) {
// SAFETY: The existence of a shared reference means that the refcount is nonzero. // SAFETY: The existence of a shared reference means that the refcount is nonzero.

View File

@ -25,6 +25,7 @@
//! C header: [`include/linux/ktime.h`](srctree/include/linux/ktime.h). //! C header: [`include/linux/ktime.h`](srctree/include/linux/ktime.h).
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ops;
pub mod delay; pub mod delay;
pub mod hrtimer; pub mod hrtimer;
@ -200,9 +201,31 @@ impl<C: ClockSource> Instant<C> {
pub(crate) fn as_nanos(&self) -> i64 { pub(crate) fn as_nanos(&self) -> i64 {
self.inner self.inner
} }
/// Create an [`Instant`] from a `ktime_t` without checking if it is non-negative.
///
/// # Panics
///
/// On debug builds, this function will panic if `ktime` is not in the range from 0 to
/// `KTIME_MAX`.
///
/// # Safety
///
/// The caller promises that `ktime` is in the range from 0 to `KTIME_MAX`.
#[inline]
pub(crate) unsafe fn from_ktime(ktime: bindings::ktime_t) -> Self {
debug_assert!(ktime >= 0);
// INVARIANT: Our safety contract ensures that `ktime` is in the range from 0 to
// `KTIME_MAX`.
Self {
inner: ktime,
_c: PhantomData,
}
}
} }
impl<C: ClockSource> core::ops::Sub for Instant<C> { impl<C: ClockSource> ops::Sub for Instant<C> {
type Output = Delta; type Output = Delta;
// By the type invariant, it never overflows. // By the type invariant, it never overflows.
@ -214,6 +237,46 @@ impl<C: ClockSource> core::ops::Sub for Instant<C> {
} }
} }
impl<T: ClockSource> ops::Add<Delta> for Instant<T> {
type Output = Self;
#[inline]
fn add(self, rhs: Delta) -> Self::Output {
// INVARIANT: With arithmetic over/underflow checks enabled, this will panic if we overflow
// (e.g. go above `KTIME_MAX`)
let res = self.inner + rhs.nanos;
// INVARIANT: With overflow checks enabled, we verify here that the value is >= 0
#[cfg(CONFIG_RUST_OVERFLOW_CHECKS)]
assert!(res >= 0);
Self {
inner: res,
_c: PhantomData,
}
}
}
impl<T: ClockSource> ops::Sub<Delta> for Instant<T> {
type Output = Self;
#[inline]
fn sub(self, rhs: Delta) -> Self::Output {
// INVARIANT: With arithmetic over/underflow checks enabled, this will panic if we overflow
// (e.g. go above `KTIME_MAX`)
let res = self.inner - rhs.nanos;
// INVARIANT: With overflow checks enabled, we verify here that the value is >= 0
#[cfg(CONFIG_RUST_OVERFLOW_CHECKS)]
assert!(res >= 0);
Self {
inner: res,
_c: PhantomData,
}
}
}
/// A span of time. /// A span of time.
/// ///
/// This struct represents a span of time, with its value stored as nanoseconds. /// This struct represents a span of time, with its value stored as nanoseconds.
@ -224,6 +287,78 @@ pub struct Delta {
nanos: i64, nanos: i64,
} }
impl ops::Add for Delta {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self {
nanos: self.nanos + rhs.nanos,
}
}
}
impl ops::AddAssign for Delta {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.nanos += rhs.nanos;
}
}
impl ops::Sub for Delta {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self {
nanos: self.nanos - rhs.nanos,
}
}
}
impl ops::SubAssign for Delta {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.nanos -= rhs.nanos;
}
}
impl ops::Mul<i64> for Delta {
type Output = Self;
#[inline]
fn mul(self, rhs: i64) -> Self::Output {
Self {
nanos: self.nanos * rhs,
}
}
}
impl ops::MulAssign<i64> for Delta {
#[inline]
fn mul_assign(&mut self, rhs: i64) {
self.nanos *= rhs;
}
}
impl ops::Div for Delta {
type Output = i64;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
#[cfg(CONFIG_64BIT)]
{
self.nanos / rhs.nanos
}
#[cfg(not(CONFIG_64BIT))]
{
// SAFETY: This function is always safe to call regardless of the input values
unsafe { bindings::div64_s64(self.nanos, rhs.nanos) }
}
}
}
impl Delta { impl Delta {
/// A span of time equal to zero. /// A span of time equal to zero.
pub const ZERO: Self = Self { nanos: 0 }; pub const ZERO: Self = Self { nanos: 0 };
@ -312,4 +447,30 @@ impl Delta {
bindings::ktime_to_ms(self.as_nanos()) bindings::ktime_to_ms(self.as_nanos())
} }
} }
/// Return `self % dividend` where `dividend` is in nanoseconds.
///
/// The kernel doesn't have any emulation for `s64 % s64` on 32 bit platforms, so this is
/// limited to 32 bit dividends.
#[inline]
pub fn rem_nanos(self, dividend: i32) -> Self {
#[cfg(CONFIG_64BIT)]
{
Self {
nanos: self.as_nanos() % i64::from(dividend),
}
}
#[cfg(not(CONFIG_64BIT))]
{
let mut rem = 0;
// SAFETY: `rem` is in the stack, so we can always provide a valid pointer to it.
unsafe { bindings::div_s64_rem(self.as_nanos(), dividend, &mut rem) };
Self {
nanos: i64::from(rem),
}
}
}
} }

View File

@ -69,9 +69,14 @@
use super::{ClockSource, Delta, Instant}; use super::{ClockSource, Delta, Instant};
use crate::{prelude::*, types::Opaque}; use crate::{prelude::*, types::Opaque};
use core::marker::PhantomData; use core::{marker::PhantomData, ptr::NonNull};
use pin_init::PinInit; use pin_init::PinInit;
/// A type-alias to refer to the [`Instant<C>`] for a given `T` from [`HrTimer<T>`].
///
/// Where `C` is the [`ClockSource`] of the [`HrTimer`].
pub type HrTimerInstant<T> = Instant<<<T as HasHrTimer<T>>::TimerMode as HrTimerMode>::Clock>;
/// A timer backed by a C `struct hrtimer`. /// A timer backed by a C `struct hrtimer`.
/// ///
/// # Invariants /// # Invariants
@ -163,6 +168,84 @@ impl<T> HrTimer<T> {
// handled on the C side. // handled on the C side.
unsafe { bindings::hrtimer_cancel(c_timer_ptr) != 0 } unsafe { bindings::hrtimer_cancel(c_timer_ptr) != 0 }
} }
/// Forward the timer expiry for a given timer pointer.
///
/// # Safety
///
/// - `self_ptr` must point to a valid `Self`.
/// - The caller must either have exclusive access to the data pointed at by `self_ptr`, or be
/// within the context of the timer callback.
#[inline]
unsafe fn raw_forward(self_ptr: *mut Self, now: HrTimerInstant<T>, interval: Delta) -> u64
where
T: HasHrTimer<T>,
{
// SAFETY:
// * The C API requirements for this function are fulfilled by our safety contract.
// * `self_ptr` is guaranteed to point to a valid `Self` via our safety contract
unsafe {
bindings::hrtimer_forward(Self::raw_get(self_ptr), now.as_nanos(), interval.as_nanos())
}
}
/// Conditionally forward the timer.
///
/// If the timer expires after `now`, this function does nothing and returns 0. If the timer
/// expired at or before `now`, this function forwards the timer by `interval` until the timer
/// expires after `now` and then returns the number of times the timer was forwarded by
/// `interval`.
///
/// This function is mainly useful for timer types which can provide exclusive access to the
/// timer when the timer is not running. For forwarding the timer from within the timer callback
/// context, see [`HrTimerCallbackContext::forward()`].
///
/// Returns the number of overruns that occurred as a result of the timer expiry change.
pub fn forward(self: Pin<&mut Self>, now: HrTimerInstant<T>, interval: Delta) -> u64
where
T: HasHrTimer<T>,
{
// SAFETY: `raw_forward` does not move `Self`
let this = unsafe { self.get_unchecked_mut() };
// SAFETY: By existence of `Pin<&mut Self>`, the pointer passed to `raw_forward` points to a
// valid `Self` that we have exclusive access to.
unsafe { Self::raw_forward(this, now, interval) }
}
/// Conditionally forward the timer.
///
/// This is a variant of [`forward()`](Self::forward) that uses an interval after the current
/// time of the base clock for the [`HrTimer`].
pub fn forward_now(self: Pin<&mut Self>, interval: Delta) -> u64
where
T: HasHrTimer<T>,
{
self.forward(HrTimerInstant::<T>::now(), interval)
}
/// Return the time expiry for this [`HrTimer`].
///
/// This value should only be used as a snapshot, as the actual expiry time could change after
/// this function is called.
pub fn expires(&self) -> HrTimerInstant<T>
where
T: HasHrTimer<T>,
{
// SAFETY: `self` is an immutable reference and thus always points to a valid `HrTimer`.
let c_timer_ptr = unsafe { HrTimer::raw_get(self) };
// SAFETY:
// - Timers cannot have negative ktime_t values as their expiration time.
// - There's no actual locking here, a racy read is fine and expected
unsafe {
Instant::from_ktime(
// This `read_volatile` is intended to correspond to a READ_ONCE call.
// FIXME(read_once): Replace with `read_once` when available on the Rust side.
core::ptr::read_volatile(&raw const ((*c_timer_ptr).node.expires)),
)
}
}
} }
/// Implemented by pointer types that point to structs that contain a [`HrTimer`]. /// Implemented by pointer types that point to structs that contain a [`HrTimer`].
@ -300,9 +383,13 @@ pub trait HrTimerCallback {
type Pointer<'a>: RawHrTimerCallback; type Pointer<'a>: RawHrTimerCallback;
/// Called by the timer logic when the timer fires. /// Called by the timer logic when the timer fires.
fn run(this: <Self::Pointer<'_> as RawHrTimerCallback>::CallbackTarget<'_>) -> HrTimerRestart fn run(
this: <Self::Pointer<'_> as RawHrTimerCallback>::CallbackTarget<'_>,
ctx: HrTimerCallbackContext<'_, Self>,
) -> HrTimerRestart
where where
Self: Sized; Self: Sized,
Self: HasHrTimer<Self>;
} }
/// A handle representing a potentially running timer. /// A handle representing a potentially running timer.
@ -324,6 +411,8 @@ pub unsafe trait HrTimerHandle {
/// Note that the timer might be started by a concurrent start operation. If /// Note that the timer might be started by a concurrent start operation. If
/// so, the timer might not be in the **stopped** state when this function /// so, the timer might not be in the **stopped** state when this function
/// returns. /// returns.
///
/// Returns `true` if the timer was running.
fn cancel(&mut self) -> bool; fn cancel(&mut self) -> bool;
} }
@ -585,6 +674,63 @@ impl<C: ClockSource> HrTimerMode for RelativePinnedHardMode<C> {
type Expires = Delta; type Expires = Delta;
} }
/// Privileged smart-pointer for a [`HrTimer`] callback context.
///
/// Many [`HrTimer`] methods can only be called in two situations:
///
/// * When the caller has exclusive access to the `HrTimer` and the `HrTimer` is guaranteed not to
/// be running.
/// * From within the context of an `HrTimer`'s callback method.
///
/// This type provides access to said methods from within a timer callback context.
///
/// # Invariants
///
/// * The existence of this type means the caller is currently within the callback for an
/// [`HrTimer`].
/// * `self.0` always points to a live instance of [`HrTimer<T>`].
pub struct HrTimerCallbackContext<'a, T: HasHrTimer<T>>(NonNull<HrTimer<T>>, PhantomData<&'a ()>);
impl<'a, T: HasHrTimer<T>> HrTimerCallbackContext<'a, T> {
/// Create a new [`HrTimerCallbackContext`].
///
/// # Safety
///
/// This function relies on the caller being within the context of a timer callback, so it must
/// not be used anywhere except for within implementations of [`RawHrTimerCallback::run`]. The
/// caller promises that `timer` points to a valid initialized instance of
/// [`bindings::hrtimer`].
///
/// The returned `Self` must not outlive the function context of [`RawHrTimerCallback::run`]
/// where this function is called.
pub(crate) unsafe fn from_raw(timer: *mut HrTimer<T>) -> Self {
// SAFETY: The caller guarantees `timer` is a valid pointer to an initialized
// `bindings::hrtimer`
// INVARIANT: Our safety contract ensures that we're within the context of a timer callback
// and that `timer` points to a live instance of `HrTimer<T>`.
Self(unsafe { NonNull::new_unchecked(timer) }, PhantomData)
}
/// Conditionally forward the timer.
///
/// This function is identical to [`HrTimer::forward()`] except that it may only be used from
/// within the context of a [`HrTimer`] callback.
pub fn forward(&mut self, now: HrTimerInstant<T>, interval: Delta) -> u64 {
// SAFETY:
// - We are guaranteed to be within the context of a timer callback by our type invariants
// - By our type invariants, `self.0` always points to a valid `HrTimer<T>`
unsafe { HrTimer::<T>::raw_forward(self.0.as_ptr(), now, interval) }
}
/// Conditionally forward the timer.
///
/// This is a variant of [`HrTimerCallbackContext::forward()`] that uses an interval after the
/// current time of the base clock for the [`HrTimer`].
pub fn forward_now(&mut self, duration: Delta) -> u64 {
self.forward(HrTimerInstant::<T>::now(), duration)
}
}
/// Use to implement the [`HasHrTimer<T>`] trait. /// Use to implement the [`HasHrTimer<T>`] trait.
/// ///
/// See [`module`] documentation for an example. /// See [`module`] documentation for an example.

View File

@ -3,6 +3,7 @@
use super::HasHrTimer; use super::HasHrTimer;
use super::HrTimer; use super::HrTimer;
use super::HrTimerCallback; use super::HrTimerCallback;
use super::HrTimerCallbackContext;
use super::HrTimerHandle; use super::HrTimerHandle;
use super::HrTimerMode; use super::HrTimerMode;
use super::HrTimerPointer; use super::HrTimerPointer;
@ -99,6 +100,12 @@ where
// allocation from other `Arc` clones. // allocation from other `Arc` clones.
let receiver = unsafe { ArcBorrow::from_raw(data_ptr) }; let receiver = unsafe { ArcBorrow::from_raw(data_ptr) };
T::run(receiver).into_c() // SAFETY:
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
// - We are within `RawHrTimerCallback::run`
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
T::run(receiver, context).into_c()
} }
} }

View File

@ -3,6 +3,7 @@
use super::HasHrTimer; use super::HasHrTimer;
use super::HrTimer; use super::HrTimer;
use super::HrTimerCallback; use super::HrTimerCallback;
use super::HrTimerCallbackContext;
use super::HrTimerHandle; use super::HrTimerHandle;
use super::HrTimerMode; use super::HrTimerMode;
use super::RawHrTimerCallback; use super::RawHrTimerCallback;
@ -103,6 +104,12 @@ where
// here. // here.
let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) }; let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) };
T::run(receiver_pin).into_c() // SAFETY:
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
// - We are within `RawHrTimerCallback::run`
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
T::run(receiver_pin, context).into_c()
} }
} }

View File

@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
use super::{ use super::{
HasHrTimer, HrTimer, HrTimerCallback, HrTimerHandle, HrTimerMode, RawHrTimerCallback, HasHrTimer, HrTimer, HrTimerCallback, HrTimerCallbackContext, HrTimerHandle, HrTimerMode,
UnsafeHrTimerPointer, RawHrTimerCallback, UnsafeHrTimerPointer,
}; };
use core::{marker::PhantomData, pin::Pin, ptr::NonNull}; use core::{marker::PhantomData, pin::Pin, ptr::NonNull};
@ -107,6 +107,12 @@ where
// here. // here.
let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) }; let receiver_pin = unsafe { Pin::new_unchecked(receiver_ref) };
T::run(receiver_pin).into_c() // SAFETY:
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
// - We are within `RawHrTimerCallback::run`
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
T::run(receiver_pin, context).into_c()
} }
} }

View File

@ -3,6 +3,7 @@
use super::HasHrTimer; use super::HasHrTimer;
use super::HrTimer; use super::HrTimer;
use super::HrTimerCallback; use super::HrTimerCallback;
use super::HrTimerCallbackContext;
use super::HrTimerHandle; use super::HrTimerHandle;
use super::HrTimerMode; use super::HrTimerMode;
use super::HrTimerPointer; use super::HrTimerPointer;
@ -119,6 +120,12 @@ where
// `data_ptr` exist. // `data_ptr` exist.
let data_mut_ref = unsafe { Pin::new_unchecked(&mut *data_ptr) }; let data_mut_ref = unsafe { Pin::new_unchecked(&mut *data_ptr) };
T::run(data_mut_ref).into_c() // SAFETY:
// - By C API contract `timer_ptr` is the pointer that we passed when queuing the timer, so
// it is a valid pointer to a `HrTimer<T>` embedded in a `T`.
// - We are within `RawHrTimerCallback::run`
let context = unsafe { HrTimerCallbackContext::from_raw(timer_ptr) };
T::run(data_mut_ref, context).into_c()
} }
} }

View File

@ -2,7 +2,6 @@
use proc_macro::{TokenStream, TokenTree}; use proc_macro::{TokenStream, TokenTree};
#[allow(dead_code)]
pub(crate) trait ToTokens { pub(crate) trait ToTokens {
fn to_tokens(&self, tokens: &mut TokenStream); fn to_tokens(&self, tokens: &mut TokenStream);
} }
@ -47,121 +46,116 @@ impl ToTokens for TokenStream {
/// `quote` crate but provides only just enough functionality needed by the current `macros` crate. /// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
macro_rules! quote_spanned { macro_rules! quote_spanned {
($span:expr => $($tt:tt)*) => {{ ($span:expr => $($tt:tt)*) => {{
let mut tokens: ::std::vec::Vec<::proc_macro::TokenTree>; let mut tokens = ::proc_macro::TokenStream::new();
#[allow(clippy::vec_init_then_push)]
{ {
tokens = ::std::vec::Vec::new();
let span = $span; let span = $span;
quote_spanned!(@proc tokens span $($tt)*); quote_spanned!(@proc tokens span $($tt)*);
} }
::proc_macro::TokenStream::from_iter(tokens) tokens
}}; }};
(@proc $v:ident $span:ident) => {}; (@proc $v:ident $span:ident) => {};
(@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => { (@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => {
let mut ts = ::proc_macro::TokenStream::new(); $crate::quote::ToTokens::to_tokens(&$id, &mut $v);
$crate::quote::ToTokens::to_tokens(&$id, &mut ts);
$v.extend(ts);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => { (@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => {
for token in $id { for token in $id {
let mut ts = ::proc_macro::TokenStream::new(); $crate::quote::ToTokens::to_tokens(&token, &mut $v);
$crate::quote::ToTokens::to_tokens(&token, &mut ts);
$v.extend(ts);
} }
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => { (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
#[allow(unused_mut)] #[allow(unused_mut)]
let mut tokens = ::std::vec::Vec::<::proc_macro::TokenTree>::new(); let mut tokens = ::proc_macro::TokenStream::new();
quote_spanned!(@proc tokens $span $($inner)*); quote_spanned!(@proc tokens $span $($inner)*);
$v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new( $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Parenthesis, ::proc_macro::Delimiter::Parenthesis,
::proc_macro::TokenStream::from_iter(tokens) tokens,
))); ))]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => { (@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => {
let mut tokens = ::std::vec::Vec::new(); let mut tokens = ::proc_macro::TokenStream::new();
quote_spanned!(@proc tokens $span $($inner)*); quote_spanned!(@proc tokens $span $($inner)*);
$v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new( $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Bracket, ::proc_macro::Delimiter::Bracket,
::proc_macro::TokenStream::from_iter(tokens) tokens,
))); ))]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => { (@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => {
let mut tokens = ::std::vec::Vec::new(); let mut tokens = ::proc_macro::TokenStream::new();
quote_spanned!(@proc tokens $span $($inner)*); quote_spanned!(@proc tokens $span $($inner)*);
$v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new( $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Brace, ::proc_macro::Delimiter::Brace,
::proc_macro::TokenStream::from_iter(tokens) tokens,
))); ))]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident :: $($tt:tt)*) => { (@proc $v:ident $span:ident :: $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::Spacing::Joint, ::proc_macro::Spacing::Alone].map(|spacing| {
::proc_macro::Punct::new(':', ::proc_macro::Spacing::Joint) ::proc_macro::TokenTree::Punct(::proc_macro::Punct::new(':', spacing))
)); }));
$v.push(::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone)
));
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident : $($tt:tt)*) => { (@proc $v:ident $span:ident : $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident , $($tt:tt)*) => { (@proc $v:ident $span:ident , $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new(',', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new(',', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident @ $($tt:tt)*) => { (@proc $v:ident $span:ident @ $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('@', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new('@', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident ! $($tt:tt)*) => { (@proc $v:ident $span:ident ! $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('!', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new('!', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident ; $($tt:tt)*) => { (@proc $v:ident $span:ident ; $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident + $($tt:tt)*) => { (@proc $v:ident $span:ident + $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident = $($tt:tt)*) => { (@proc $v:ident $span:ident = $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident # $($tt:tt)*) => { (@proc $v:ident $span:ident # $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Punct( $v.extend([::proc_macro::TokenTree::Punct(
::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone) ::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone),
)); )]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident _ $($tt:tt)*) => { (@proc $v:ident $span:ident _ $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new("_", $span))); $v.extend([::proc_macro::TokenTree::Ident(
::proc_macro::Ident::new("_", $span),
)]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
(@proc $v:ident $span:ident $id:ident $($tt:tt)*) => { (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
$v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span))); $v.extend([::proc_macro::TokenTree::Ident(
::proc_macro::Ident::new(stringify!($id), $span),
)]);
quote_spanned!(@proc $v $span $($tt)*); quote_spanned!(@proc $v $span $($tt)*);
}; };
} }

View File

@ -34,4 +34,6 @@ type __kernel_size_t = usize;
type __kernel_ssize_t = isize; type __kernel_ssize_t = isize;
type __kernel_ptrdiff_t = isize; type __kernel_ptrdiff_t = isize;
use pin_init::MaybeZeroable;
include!(concat!(env!("OBJTREE"), "/rust/uapi/uapi_generated.rs")); include!(concat!(env!("OBJTREE"), "/rust/uapi/uapi_generated.rs"));

View File

@ -94,7 +94,7 @@ impl configfs::AttributeOperations<0> for Configuration {
fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
pr_info!("Show message\n"); pr_info!("Show message\n");
let data = container.message; let data = container.message.to_bytes();
page[0..data.len()].copy_from_slice(data); page[0..data.len()].copy_from_slice(data);
Ok(data.len()) Ok(data.len())
} }

View File

@ -10,7 +10,7 @@ use kernel::{
dma::{CoherentAllocation, Device, DmaMask}, dma::{CoherentAllocation, Device, DmaMask},
pci, pci,
prelude::*, prelude::*,
types::ARef, sync::aref::ARef,
}; };
struct DmaSampleDriver { struct DmaSampleDriver {

View File

@ -139,8 +139,8 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
"exclude_dirs": [], "exclude_dirs": [],
} }
append_crate_with_generated("bindings", ["core", "ffi"]) append_crate_with_generated("bindings", ["core", "ffi", "pin_init"])
append_crate_with_generated("uapi", ["core", "ffi"]) append_crate_with_generated("uapi", ["core", "ffi", "pin_init"])
append_crate_with_generated("kernel", ["core", "macros", "build_error", "pin_init", "ffi", "bindings", "uapi"]) append_crate_with_generated("kernel", ["core", "macros", "build_error", "pin_init", "ffi", "bindings", "uapi"])
def is_root_crate(build_file, target): def is_root_crate(build_file, target):

View File

@ -202,7 +202,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
// This follows the syntax for declaring test metadata in the proposed KTAP v2 spec, which may // This follows the syntax for declaring test metadata in the proposed KTAP v2 spec, which may
// be used for the proposed KUnit test attributes API. Thus hopefully this will make migration // be used for the proposed KUnit test attributes API. Thus hopefully this will make migration
// easier later on. // easier later on.
::kernel::kunit::info(format_args!(" # {kunit_name}.location: {real_path}:{line}\n")); ::kernel::kunit::info(fmt!(" # {kunit_name}.location: {real_path}:{line}\n"));
/// The anchor where the test code body starts. /// The anchor where the test code body starts.
#[allow(unused)] #[allow(unused)]