linux/rust/kernel/bitmap.rs
Linus Torvalds 12d5fc96eb Rust changes for v7.4
Toolchain and infrastructure:
 
  - Update vendored 'zerocopy' crate to v0.8.62 (released 2026-10-08).
 
    This update advances 8 releases, with 3 of them carrying most of the
    changes. It contains new features as well as fixes. However, almost
    nothing has changed that requires updates on our side (a move of our
    non-ASCII change and the addition of an SPDX license identifier).
 
  - Generate Rust KUnit test suites for doctests reusing the
    '#[kunit_tests]' and '#[test]' attributes instead of C FFI
    functions, which allows to remove the generated C glue file.
 
    Relatedly, simplify 'kunit_assert!' by using 'file!()' and 'line!()'
    inside (which doctests override as needed), which allows to remove
    the 'file' helper and 'CONFIG_RUSTC_HAS_SPAN_FILE'.
 
  - Trim the function names in doctests generated by 'rustdoc' < 1.95.0
    when building with 'O=', which were not reproducible.
 
  - Fix the 'VM_MAYSHARE' value in the generated bindings and its
    blocklist entry, due to typos.
 
  - Build 'core' with '--cfg no_io_statics' to ensure 'core' does not
    introduce any use of 'core::sync::atomic' into the kernel image
    since upstream Rust is moving 'std::io::Error' into 'core', which
    uses a global atomic pointer.
 
    Upstream Rust merged a PR to add a test checking that builds of
    'core' are warning-free with that 'cfg' enabled.
 
  - Enable the unstable 'proc_macro_span' feature in 'proc-macro2',
    which provides 'Span::join' and thus improves the quality of the
    diagnostics of all our procedural macros, notably 'pin-init'. This
    is only used for producing diagnostics, thus there is no hard
    reliance on the feature.
 
  - Globally allow 'clippy::comparison_chain', since its suggestions are
    not always ideal and upstream downgraded it to 'pedantic' too.
 
 'kernel' crate:
 
  - New 'mem' module: add the 'Align<N>' zero-sized type so that
    alignment can be specified via const generics ('repr(align(...))'
    only works with integer literals), e.g.:
 
        const N: usize = 128;
 
        struct S {
            _align: Align<N>,
        }
 
        assert_eq!(align_of::<S>(), N);
 
  - New 'ratelimit' module: add the 'Ratelimit' abstraction for 'struct
    ratelimit_state' and, based on it, the 'pr_*_ratelimited!' printing
    macros, which can be used for printing that may be triggered by
    userspace.
 
    In addition, convert Rust Binder to use them.
 
  - 'sync' module: to construct a global 'struct ratelimit_state', add a
    'const' constructor for 'raw_spinlock_t'.
 
    In addition, move the lockdep types into a new 'sync::lockdep'
    submodule, since there were starting to be too many of them.
 
  - 'num' module: add the 'cv!' macro to create constant values ("cv")
    from an integer constant expression, with validity checked at
    compile time, inferring the target type from the context. It works
    for any type that implements the new 'FromConst' trait, e.g.:
 
        let a: Alignment = cv!(4096);
        let b: Bounded<u32, 4> = cv!(15);
 
        // Any integer constant expression works, not only literals.
        let m: NonZero<usize> = cv!(usize::MAX);
 
        // Checked narrowing, including in `const` items.
        const SMALL: u8 = cv!(200u32);
 
        // The target type can be given explicitly.
        let e = cv!(200u32 => u8);
 
        // With an explicit primitive target, the expression can use
        // generic parameters.
        const fn as_u64<const KEY: u16>() -> u64 {
            cv!(KEY => u64)
        }
 
    This avoids the verbose turbofish syntax which disincentivized using
    these types at interface boundaries (e.g. 'Alignment:🆕:<8>()').
 
  - 'alloc' module:
 
      - Allow different error types in 'KBox::pin_slice()', enabling use
        of infallible initializers.
 
      - Add 'NumaNode::id()' accessor to retrieve the underlying NUMA
        node identifier.
 
  - 'time' module:
 
      - Add 'Delta::to_jiffies_timeout()' for use in timeout arguments.
 
      - Add 'Delta::as_jiffies_unsigned()'.
 
      - Make 'Delta::ZERO' generic over the time unit.
 
      - Warn and assert when passing out-of-range inputs to 'fsleep' and
        'udelay'.
 
      - Rename 'ClockSource' to 'ClockId' to better align with naming
        conventions in C kernel.
 
      - Make 'CONFIG_HZ' value available as 'bindings::HZ'.
 
      - Add implementation detail documentation for 'HrTimer'.
 
  - 'bitmap' module:
 
      - Add contiguous area operations ('next_zero_area{,_off}', 'set'
        and 'clear'), with extra checks compared to the underlying C
        code, plus additional ones under 'CONFIG_RUST_BITMAP_HARDENED'.
        It includes tests demonstrating the edge cases.
 
      - Restrict bitmap length to at most 'i32::MAX', matching
        'BitmapVec::MAX_LEN' and simplifying a few things. This also
        fixes 'u32' casts truncating in 'copy_and_extend', which could
        otherwise lead to OOB writes.
 
  - 'bug' module:
 
      - Fix 'warn_on!' on UML which called 'warn_slowpath_fmt()' with
        the wrong arguments; and fix its '// SAFETY:' comment too.
 
      - Add a KUnit test for 'warn_on!'.
 
  - 'id_pool' module: add contiguous ID reservation via the new
    'reserve_ids' and 'release_ids' methods, built on top of the new
    bitmap area operations.
 
  - 'sizes' module: add the sub-1K size constants ('SZ_1' to 'SZ_512')
    and implement 'SizeConstants' for 'Alignment', so that one can write
    e.g. 'Alignment::SZ_8'.
 
 'macros' crate:
 
  - Add '#[optional]' attribute for optional methods in '#[vtable]' to
    replace the 'build_error!(VTABLE_DEFAULT_ERROR)' boilerplate, e.g.:
 
        #[vtable]
        pub trait Operations: Sized {
            #[optional]
            fn f(&self) -> Result;
        }
 
    In addition, apply it to miscdevice and hide generated 'HAS_'
    constants for required methods.
 
  - Allow non-ASCII characters in the 'authors' field of the 'module!'
    macro, which was unintentionally restricted when multiple authors
    support was introduced.
 
 'pin-init' crate:
 
   [ Extra context from Gary's pull request:
      "This is a cycle packed with feature updates. Two of the most
       anticipated features, tuple struct support and safe self
       reference creation, has been included in this pull request.
 
       Safe self reference is a big one and is pushed a bit late in the
       cycle. The initial plan was to include a minimum subset for this
       cycle because I was worried about potential soundness issues;
       however during Plumbers it was agreed that the features should be
       upstreamed at once. This is because the initial users (Nova,
       JobQueue) would all need the advanced features, so it is better
       to ensure that the feature get put into use for real, and it can
       iterate with users, rather than being fully designed on paper.
       The existing safety checks are already very comprehensive, and
       Benno and I are confident that if new ones are discovered, it
       wouldn't affect the overall design.
 
       That said, the the usage of the advanced features (invariance,
       existential lifetime) shall be limited to the pre-agreed users
       only, so even in case where the feature needs to be reworked, the
       blast radius is limited.
 
       Another theme in this cycle is to improve the developer
       experience when things are used wrongly. A lot of work has been
       done to use proper spans from user-provided tokens so error
       message point to things that cause them. Many common misuse
       scenarios also gain additional custom diagnostics." ]
 
  - User visible changes:
 
      - Support tuple structs.
 
      - Allow safe creation of self references.
 
      - Vastly improve diagnostics for common misuse scenarios.
 
  - Other changes:
 
      - Use 'mixed_site' hygiene consistently.
 
 'MAINTAINERS':
 
  - Add 'RUST [BUG]' entry for the Rust 'bug' module (i.e. 'warn_on!'),
    which will be maintained by FUJITA Tomonori -- thank you!
 
  - Add Alice Ryhl as co-maintainer for 'Rust [ALLOC]'.
 
 And some other fixes, cleanups and improvements.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPjU5OPd5QIZ9jqqOGXyLc2htIW0FAmrU9HoACgkQGXyLc2ht
 IW2t/A/8CWBNGMU1HlM23gKmG9z3UuOp9RdebQMQxcjKcW5+odHHLtLn5ytuKfds
 TwGHoPXBlm9iyVBTCbklJfRmVqXwGgg/Dr5PyUpFSMLPKfeTDANnseJmFzcnNtkr
 3ChqB4D1J91OWZuUbS2YqNyXYuQQHhPCfu4idi/HjqdDPDZr8bAG3RdYhydTHITu
 ahlbeisR6fyFO2ctWkxCFbbsAJ6nX37TVDF8lTKu5vxbJp5b7r4u9u2cQ2OEWrNc
 sh/n3tv7JZSoIWapOAlsDpRXX00MsPFqiO1Ytuf8JOb3kwoVDDT0Ww3dDbmkiVKN
 NEcEkYUcFRkAltscPHSos8MuElVqXQyY+ZrjCykXrFsxwKWeGXHYwBS9HBVPs49Q
 AYzdbNe+hsUfqNJo4lP/9R8U9SQTv4/0T0cvgg9W1sdYJCePuzOwuTgaFDPs6zUL
 vz/RZ6tIA0UbRQVnLnjoD8xWne+kpfDaZVinTCGqHpOBmEvPSxWwsHKj9Q5qJsC8
 1UakcpFQ3gsvXn4Od7VAL2ODrc/gxSHEG7J1Zj0625CbrSd81Ga0fvE3VxtjxpRH
 AQF9W/fnZ17pV4xBlqYZP2TR0/QNCHH8b/JCevOg1Rkafe3eqf2FjarfbrhVyOTT
 wrm9gdDVwv40j0K1tVVnOAo+ey3wjlh+Q5jqVrnu15FpMsfzxlE=
 =nyUg
 -----END PGP SIGNATURE-----

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

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

   - Update vendored 'zerocopy' crate to v0.8.62 (released 2026-10-08).

     This update advances 8 releases, with 3 of them carrying most of
     the changes. It contains new features as well as fixes. However,
     almost nothing has changed that requires updates on our side (a
     move of our non-ASCII change and the addition of an SPDX license
     identifier).

   - Generate Rust KUnit test suites for doctests reusing the
     '#[kunit_tests]' and '#[test]' attributes instead of C FFI
     functions, which allows to remove the generated C glue file.

     Relatedly, simplify 'kunit_assert!' by using 'file!()' and
     'line!()' inside (which doctests override as needed), which allows
     to remove the 'file' helper and 'CONFIG_RUSTC_HAS_SPAN_FILE'.

   - Trim the function names in doctests generated by 'rustdoc' < 1.95.0
     when building with 'O=', which were not reproducible.

   - Fix the 'VM_MAYSHARE' value in the generated bindings and its
     blocklist entry, due to typos.

   - Build 'core' with '--cfg no_io_statics' to ensure 'core' does not
     introduce any use of 'core::sync::atomic' into the kernel image
     since upstream Rust is moving 'std::io::Error' into 'core', which
     uses a global atomic pointer.

     Upstream Rust merged a PR to add a test checking that builds of
     'core' are warning-free with that 'cfg' enabled.

   - Enable the unstable 'proc_macro_span' feature in 'proc-macro2',
     which provides 'Span::join' and thus improves the quality of the
     diagnostics of all our procedural macros, notably 'pin-init'. This
     is only used for producing diagnostics, thus there is no hard
     reliance on the feature.

   - Globally allow 'clippy::comparison_chain', since its suggestions
     are not always ideal and upstream downgraded it to 'pedantic' too.

  'kernel' crate:

   - New 'mem' module: add the 'Align<N>' zero-sized type so that
     alignment can be specified via const generics ('repr(align(...))'
     only works with integer literals), e.g.:

         const N: usize = 128;

         struct S {
             _align: Align<N>,
         }

         assert_eq!(align_of::<S>(), N);

   - New 'ratelimit' module: add the 'Ratelimit' abstraction for 'struct
     ratelimit_state' and, based on it, the 'pr_*_ratelimited!' printing
     macros, which can be used for printing that may be triggered by
     userspace.

     In addition, convert Rust Binder to use them.

   - 'sync' module: to construct a global 'struct ratelimit_state', add
     a 'const' constructor for 'raw_spinlock_t'.

     In addition, move the lockdep types into a new 'sync::lockdep'
     submodule, since there were starting to be too many of them.

   - 'num' module: add the 'cv!' macro to create constant values ("cv")
     from an integer constant expression, with validity checked at
     compile time, inferring the target type from the context. It works
     for any type that implements the new 'FromConst' trait, e.g.:

         let a: Alignment = cv!(4096);
         let b: Bounded<u32, 4> = cv!(15);

         // Any integer constant expression works, not only literals.
         let m: NonZero<usize> = cv!(usize::MAX);

         // Checked narrowing, including in `const` items.
         const SMALL: u8 = cv!(200u32);

         // The target type can be given explicitly.
         let e = cv!(200u32 => u8);

         // With an explicit primitive target, the expression can use
         // generic parameters.
         const fn as_u64<const KEY: u16>() -> u64 {
             cv!(KEY => u64)
         }

     This avoids the verbose turbofish syntax which disincentivized
     using these types at interface boundaries (e.g.
     'Alignment:🆕:<8>()').

   - 'alloc' module:

       - Allow different error types in 'KBox::pin_slice()', enabling use
         of infallible initializers.

       - Add 'NumaNode::id()' accessor to retrieve the underlying NUMA
         node identifier.

   - 'time' module:

       - Add 'Delta::to_jiffies_timeout()' for use in timeout arguments.

       - Add 'Delta::as_jiffies_unsigned()'.

       - Make 'Delta::ZERO' generic over the time unit.

       - Warn and assert when passing out-of-range inputs to 'fsleep' and
         'udelay'.

       - Rename 'ClockSource' to 'ClockId' to better align with naming
         conventions in C kernel.

       - Make 'CONFIG_HZ' value available as 'bindings::HZ'.

       - Add implementation detail documentation for 'HrTimer'.

   - 'bitmap' module:

       - Add contiguous area operations ('next_zero_area{,_off}', 'set'
         and 'clear'), with extra checks compared to the underlying C
         code, plus additional ones under 'CONFIG_RUST_BITMAP_HARDENED'.
         It includes tests demonstrating the edge cases.

       - Restrict bitmap length to at most 'i32::MAX', matching
         'BitmapVec::MAX_LEN' and simplifying a few things. This also
         fixes 'u32' casts truncating in 'copy_and_extend', which could
         otherwise lead to OOB writes.

   - 'bug' module:

       - Fix 'warn_on!' on UML which called 'warn_slowpath_fmt()' with
         the wrong arguments; and fix its '// SAFETY:' comment too.

       - Add a KUnit test for 'warn_on!'.

   - 'id_pool' module: add contiguous ID reservation via the new
     'reserve_ids' and 'release_ids' methods, built on top of the new
     bitmap area operations.

   - 'sizes' module: add the sub-1K size constants ('SZ_1' to 'SZ_512')
     and implement 'SizeConstants' for 'Alignment', so that one can
     write e.g. 'Alignment::SZ_8'.

  'macros' crate:

   - Add '#[optional]' attribute for optional methods in '#[vtable]' to
     replace the 'build_error!(VTABLE_DEFAULT_ERROR)' boilerplate, e.g.:

         #[vtable]
         pub trait Operations: Sized {
             #[optional]
             fn f(&self) -> Result;
         }

     In addition, apply it to miscdevice and hide generated 'HAS_'
     constants for required methods.

   - Allow non-ASCII characters in the 'authors' field of the 'module!'
     macro, which was unintentionally restricted when multiple authors
     support was introduced.

  'pin-init' crate:

    [ Extra context from Gary's pull request:
       "This is a cycle packed with feature updates. Two of the most
        anticipated features, tuple struct support and safe self
        reference creation, has been included in this pull request.

        Safe self reference is a big one and is pushed a bit late in the
        cycle. The initial plan was to include a minimum subset for this
        cycle because I was worried about potential soundness issues;
        however during Plumbers it was agreed that the features should be
        upstreamed at once. This is because the initial users (Nova,
        JobQueue) would all need the advanced features, so it is better
        to ensure that the feature get put into use for real, and it can
        iterate with users, rather than being fully designed on paper.
        The existing safety checks are already very comprehensive, and
        Benno and I are confident that if new ones are discovered, it
        wouldn't affect the overall design.

        That said, the the usage of the advanced features (invariance,
        existential lifetime) shall be limited to the pre-agreed users
        only, so even in case where the feature needs to be reworked, the
        blast radius is limited.

        Another theme in this cycle is to improve the developer
        experience when things are used wrongly. A lot of work has been
        done to use proper spans from user-provided tokens so error
        message point to things that cause them. Many common misuse
        scenarios also gain additional custom diagnostics." ]

   - User visible changes:

       - Support tuple structs.

       - Allow safe creation of self references.

       - Vastly improve diagnostics for common misuse scenarios.

   - Other changes:

       - Use 'mixed_site' hygiene consistently.

  'MAINTAINERS':

   - Add 'RUST [BUG]' entry for the Rust 'bug' module (i.e. 'warn_on!'),
     which will be maintained by FUJITA Tomonori -- thank you!

   - Add Alice Ryhl as co-maintainer for 'Rust [ALLOC]'.

  And some other fixes, cleanups and improvements.

* tag 'rust-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux: (91 commits)
  rust: zerocopy: update to v0.8.62
  rust: zerocopy: fix link to upstream `README.md`
  rust: id_pool: add contiguous ID reservation
  rust: bitmap: add contiguous area operations
  rust: sizes: implement SizeConstants for Alignment
  rust: sizes: add sub-1K size constants
  rust: bitmap: restrict bitmap length to at most i32::MAX
  rust: bitmap: use function-level cfg on kunit test
  rust: num: add cv! macro to create values from constant expressions
  rust: bug: update the SAFETY comment for warn_slowpath_fmt() on UML
  rust: time: add Delta::as_jiffies_unsigned()
  rust: time: make Delta::ZERO generic over the time unit
  rust_binder: use pr_*_ratelimited! for printing
  rust: add pr_*_ratelimited! macros for printing
  rust: sync: add const constructor for raw_spinlock_t
  rust: sync: move lockdep types to rust/kernel/sync/lockdep.rs
  rust: pin-init: internal: suppress `explicit_outlives_requirements`
  MAINTAINERS: add entry for the Rust `bug` module
  rust: pin-init: internal: pin_data: support existential lifetimes
  rust: pin-init: internal: pin_data: support shared projection
  ...
2026-10-21 10:20:36 -10:00

896 lines
30 KiB
Rust

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
//! Rust API for bitmap.
//!
//! C headers: [`include/linux/bitmap.h`](srctree/include/linux/bitmap.h).
use crate::alloc::{AllocError, Flags};
use crate::bindings;
#[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
use crate::pr_err;
use crate::ptr::Alignment;
use core::{
num::NonZero,
ptr::NonNull, //
};
/// Represents a C bitmap. Wraps underlying C bitmap API.
///
/// # Invariants
///
/// Must reference a `[c_ulong]` long enough to fit `data.len()` bits.
/// Must not be longer than [`i32::MAX`] bits, so offsets and lengths used with
/// [`Bitmap`] functions fit in the `int` and `unsigned int` arguments of the C bitmap API.
/// This also matches [`BitmapVec::MAX_LEN`].
#[cfg_attr(CONFIG_64BIT, repr(align(8)))]
#[cfg_attr(not(CONFIG_64BIT), repr(align(4)))]
pub struct Bitmap {
data: [()],
}
macro_rules! bitmap_assert {
($cond:expr, $($arg:tt)+) => {
#[cfg(CONFIG_RUST_BITMAP_HARDENED)]
assert!($cond, $($arg)*);
}
}
macro_rules! bitmap_assert_return {
($cond:expr, $($arg:tt)+) => {
#[cfg(CONFIG_RUST_BITMAP_HARDENED)]
assert!($cond, $($arg)*);
#[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
if !($cond) {
pr_err!($($arg)*);
return
}
}
}
impl Bitmap {
/// Borrows a C bitmap.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `nbits` exceeds [`i32::MAX`].
///
/// # Safety
///
/// * `ptr` holds a non-null address of an initialized array of `unsigned long`
/// that is large enough to hold `nbits` bits.
/// * `nbits` must not exceed [`i32::MAX`].
/// * the array must not be freed for the lifetime of this [`Bitmap`]
/// * concurrent access only happens through atomic operations
pub unsafe fn from_raw<'a>(ptr: *const usize, nbits: usize) -> &'a Bitmap {
bitmap_assert!(
nbits <= i32::MAX as usize,
"`nbits` must be <= {}, was {}",
i32::MAX,
nbits
);
let data: *const [()] = core::ptr::slice_from_raw_parts(ptr.cast(), nbits);
// INVARIANT: `data` references an initialized array that can hold `nbits` bits.
// INVARIANT: The caller guarantees that `nbits` does not exceed `i32::MAX`.
// SAFETY:
// The caller guarantees that `data` (derived from `ptr` and `nbits`)
// points to a valid, initialized, and appropriately sized memory region
// that will not be freed for the lifetime 'a.
// We are casting `*const [()]` to `*const Bitmap`. The `Bitmap`
// struct is a ZST with a `data: [()]` field. This means its layout
// is compatible with a slice of `()`, and effectively it's a "thin pointer"
// (its size is 0 and alignment is 1). The `slice_from_raw_parts`
// function correctly encodes the length (number of bits, not elements)
// into the metadata of the fat pointer. Therefore, dereferencing this
// pointer as `&Bitmap` is safe given the caller's guarantees.
unsafe { &*(data as *const Bitmap) }
}
/// Borrows a C bitmap exclusively.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `nbits` exceeds [`i32::MAX`].
///
/// # Safety
///
/// * `ptr` holds a non-null address of an initialized array of `unsigned long`
/// that is large enough to hold `nbits` bits.
/// * `nbits` must not exceed [`i32::MAX`].
/// * the array must not be freed for the lifetime of this [`Bitmap`]
/// * no concurrent access may happen.
pub unsafe fn from_raw_mut<'a>(ptr: *mut usize, nbits: usize) -> &'a mut Bitmap {
bitmap_assert!(
nbits <= i32::MAX as usize,
"`nbits` must be <= {}, was {}",
i32::MAX,
nbits
);
let data: *mut [()] = core::ptr::slice_from_raw_parts_mut(ptr.cast(), nbits);
// INVARIANT: `data` references an initialized array that can hold `nbits` bits.
// INVARIANT: The caller guarantees that `nbits` does not exceed `i32::MAX`.
// SAFETY:
// The caller guarantees that `data` (derived from `ptr` and `nbits`)
// points to a valid, initialized, and appropriately sized memory region
// that will not be freed for the lifetime 'a.
// Furthermore, the caller guarantees no concurrent access will happen,
// which upholds the exclusivity requirement for a mutable reference.
// Similar to `from_raw`, casting `*mut [()]` to `*mut Bitmap` is
// safe because `Bitmap` is a ZST with a `data: [()]` field,
// making its layout compatible with a slice of `()`.
unsafe { &mut *(data as *mut Bitmap) }
}
/// Returns a raw pointer to the backing [`Bitmap`].
pub fn as_ptr(&self) -> *const usize {
core::ptr::from_ref::<Bitmap>(self).cast::<usize>()
}
/// Returns a mutable raw pointer to the backing [`Bitmap`].
pub fn as_mut_ptr(&mut self) -> *mut usize {
core::ptr::from_mut::<Bitmap>(self).cast::<usize>()
}
/// Returns length of this [`Bitmap`].
#[expect(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.data.len()
}
}
/// Holds either a pointer to array of `unsigned long` or a small bitmap.
#[repr(C)]
union BitmapRepr {
bitmap: usize,
ptr: NonNull<usize>,
}
/// Represents an owned bitmap.
///
/// Wraps underlying C bitmap API. See [`Bitmap`] for available
/// methods.
///
/// # Examples
///
/// Basic usage
///
/// ```
/// use kernel::alloc::flags::GFP_KERNEL;
/// use kernel::bitmap::BitmapVec;
///
/// let mut b = BitmapVec::new(16, GFP_KERNEL)?;
///
/// assert_eq!(16, b.len());
/// for i in 0..16 {
/// if i % 4 == 0 {
/// b.set_bit(i);
/// }
/// }
/// assert_eq!(Some(0), b.next_bit(0));
/// assert_eq!(Some(1), b.next_zero_bit(0));
/// assert_eq!(Some(4), b.next_bit(1));
/// assert_eq!(Some(5), b.next_zero_bit(4));
/// assert_eq!(Some(12), b.last_bit());
/// # Ok::<(), Error>(())
/// ```
///
/// # Invariants
///
/// * `nbits` is `<= MAX_LEN`.
/// * if `nbits <= MAX_INLINE_LEN`, then `repr` is a `usize`.
/// * otherwise, `repr` holds a non-null pointer to an initialized
/// array of `unsigned long` that is large enough to hold `nbits` bits.
pub struct BitmapVec {
/// Representation of bitmap.
repr: BitmapRepr,
/// Length of this bitmap. Must be `<= MAX_LEN`.
nbits: usize,
}
impl core::ops::Deref for BitmapVec {
type Target = Bitmap;
fn deref(&self) -> &Bitmap {
let ptr = if self.nbits <= BitmapVec::MAX_INLINE_LEN {
// SAFETY: Bitmap is represented inline.
#[allow(unused_unsafe, reason = "Safe since Rust 1.92.0")]
unsafe {
core::ptr::addr_of!(self.repr.bitmap)
}
} else {
// SAFETY: Bitmap is represented as array of `unsigned long`.
unsafe { self.repr.ptr.as_ptr() }
};
// SAFETY: We got the right pointer and invariants of [`Bitmap`] hold.
// An inline bitmap is treated like an array with single element.
unsafe { Bitmap::from_raw(ptr, self.nbits) }
}
}
impl core::ops::DerefMut for BitmapVec {
fn deref_mut(&mut self) -> &mut Bitmap {
let ptr = if self.nbits <= BitmapVec::MAX_INLINE_LEN {
// SAFETY: Bitmap is represented inline.
#[allow(unused_unsafe, reason = "Safe since Rust 1.92.0")]
unsafe {
core::ptr::addr_of_mut!(self.repr.bitmap)
}
} else {
// SAFETY: Bitmap is represented as array of `unsigned long`.
unsafe { self.repr.ptr.as_ptr() }
};
// SAFETY: We got the right pointer and invariants of [`BitmapVec`] hold.
// An inline bitmap is treated like an array with single element.
unsafe { Bitmap::from_raw_mut(ptr, self.nbits) }
}
}
/// Enable ownership transfer to other threads.
///
/// SAFETY: We own the underlying bitmap representation.
unsafe impl Send for BitmapVec {}
/// Enable unsynchronized concurrent access to [`BitmapVec`] through shared references.
///
/// SAFETY: `deref()` will return a reference to a [`Bitmap`]. Its methods
/// take immutable references are either atomic or read-only.
unsafe impl Sync for BitmapVec {}
impl Drop for BitmapVec {
fn drop(&mut self) {
if self.nbits <= BitmapVec::MAX_INLINE_LEN {
return;
}
// SAFETY: `self.ptr` was returned by the C `bitmap_zalloc`.
//
// INVARIANT: there is no other use of the `self.ptr` after this
// call and the value is being dropped so the broken invariant is
// not observable on function exit.
unsafe { bindings::bitmap_free(self.repr.ptr.as_ptr()) };
}
}
impl BitmapVec {
/// The maximum possible length of a `BitmapVec`.
pub const MAX_LEN: usize = i32::MAX as usize;
/// The maximum length that uses the inline representation.
pub const MAX_INLINE_LEN: usize = usize::BITS as usize;
/// Construct a longest possible inline [`BitmapVec`].
#[inline]
pub fn new_inline() -> Self {
// INVARIANT: `nbits <= MAX_INLINE_LEN`, so an inline bitmap is the right repr.
BitmapVec {
repr: BitmapRepr { bitmap: 0 },
nbits: BitmapVec::MAX_INLINE_LEN,
}
}
/// Constructs a new [`BitmapVec`].
///
/// Fails with [`AllocError`] when the [`BitmapVec`] could not be allocated. This
/// includes the case when `nbits` is greater than `MAX_LEN`.
#[inline]
pub fn new(nbits: usize, flags: Flags) -> Result<Self, AllocError> {
if nbits <= BitmapVec::MAX_INLINE_LEN {
return Ok(BitmapVec {
repr: BitmapRepr { bitmap: 0 },
nbits,
});
}
if nbits > Self::MAX_LEN {
return Err(AllocError);
}
let nbits_u32 = u32::try_from(nbits).unwrap();
// SAFETY: `MAX_INLINE_LEN < nbits` and `nbits <= MAX_LEN`.
let ptr = unsafe { bindings::bitmap_zalloc(nbits_u32, flags.as_raw()) };
let ptr = NonNull::new(ptr).ok_or(AllocError)?;
// INVARIANT: `ptr` returned by C `bitmap_zalloc` and `nbits` checked.
Ok(BitmapVec {
repr: BitmapRepr { ptr },
nbits,
})
}
/// Returns length of this [`Bitmap`].
#[allow(clippy::len_without_is_empty)]
#[inline]
pub fn len(&self) -> usize {
self.nbits
}
/// Fills this `Bitmap` with random bits.
#[cfg(CONFIG_FIND_BIT_BENCHMARK_RUST)]
pub fn fill_random(&mut self) {
// SAFETY: `self.as_mut_ptr` points to either an array of the
// appropriate length or one usize.
unsafe {
bindings::get_random_bytes(
self.as_mut_ptr().cast::<ffi::c_void>(),
usize::div_ceil(self.nbits, bindings::BITS_PER_LONG as usize)
* bindings::BITS_PER_LONG as usize
/ 8,
);
}
}
}
impl Bitmap {
/// Set bit with index `index`.
///
/// ATTENTION: `set_bit` is non-atomic, which differs from the naming
/// convention in C code. The corresponding C function is `__set_bit`.
///
/// If CONFIG_RUST_BITMAP_HARDENED is not enabled and `index` is greater than
/// or equal to `self.nbits`, does nothing.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `index` is greater than
/// or equal to `self.nbits`.
#[inline]
pub fn set_bit(&mut self, index: usize) {
bitmap_assert_return!(
index < self.len(),
"Bit `index` must be < {}, was {}",
self.len(),
index
);
// SAFETY: Bit `index` is within bounds.
unsafe { bindings::__set_bit(index, self.as_mut_ptr()) };
}
/// Set bit with index `index`, atomically.
///
/// This is a relaxed atomic operation (no implied memory barriers).
///
/// ATTENTION: The naming convention differs from C, where the corresponding
/// function is called `set_bit`.
///
/// If CONFIG_RUST_BITMAP_HARDENED is not enabled and `index` is greater than
/// or equal to `self.len()`, does nothing.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `index` is greater than
/// or equal to `self.len()`.
#[inline]
pub fn set_bit_atomic(&self, index: usize) {
bitmap_assert_return!(
index < self.len(),
"Bit `index` must be < {}, was {}",
self.len(),
index
);
// SAFETY: `index` is within bounds and the caller has ensured that
// there is no mix of non-atomic and atomic operations.
unsafe { bindings::set_bit(index, self.as_ptr().cast_mut()) };
}
/// Clear `index` bit.
///
/// ATTENTION: `clear_bit` is non-atomic, which differs from the naming
/// convention in C code. The corresponding C function is `__clear_bit`.
///
/// If CONFIG_RUST_BITMAP_HARDENED is not enabled and `index` is greater than
/// or equal to `self.len()`, does nothing.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `index` is greater than
/// or equal to `self.len()`.
#[inline]
pub fn clear_bit(&mut self, index: usize) {
bitmap_assert_return!(
index < self.len(),
"Bit `index` must be < {}, was {}",
self.len(),
index
);
// SAFETY: `index` is within bounds.
unsafe { bindings::__clear_bit(index, self.as_mut_ptr()) };
}
/// Clear `index` bit, atomically.
///
/// This is a relaxed atomic operation (no implied memory barriers).
///
/// ATTENTION: The naming convention differs from C, where the corresponding
/// function is called `clear_bit`.
///
/// If CONFIG_RUST_BITMAP_HARDENED is not enabled and `index` is greater than
/// or equal to `self.len()`, does nothing.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `index` is greater than
/// or equal to `self.len()`.
#[inline]
pub fn clear_bit_atomic(&self, index: usize) {
bitmap_assert_return!(
index < self.len(),
"Bit `index` must be < {}, was {}",
self.len(),
index
);
// SAFETY: `index` is within bounds and the caller has ensured that
// there is no mix of non-atomic and atomic operations.
unsafe { bindings::clear_bit(index, self.as_ptr().cast_mut()) };
}
/// Copy `src` into this [`Bitmap`] and set any remaining bits to zero.
///
/// # Examples
///
/// ```
/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
/// use kernel::bitmap::BitmapVec;
///
/// let mut long_bitmap = BitmapVec::new(256, GFP_KERNEL)?;
///
/// assert_eq!(None, long_bitmap.last_bit());
///
/// let mut short_bitmap = BitmapVec::new(16, GFP_KERNEL)?;
///
/// short_bitmap.set_bit(7);
/// long_bitmap.copy_and_extend(&short_bitmap);
/// assert_eq!(Some(7), long_bitmap.last_bit());
///
/// # Ok::<(), AllocError>(())
/// ```
#[inline]
pub fn copy_and_extend(&mut self, src: &Bitmap) {
let len = core::cmp::min(src.len(), self.len());
// SAFETY: Access to `self` and `src` is within bounds. Both lengths fit in `u32`
// because a `Bitmap` is at most `i32::MAX` bits, so the casts are lossless.
unsafe {
bindings::bitmap_copy_and_extend(
self.as_mut_ptr(),
src.as_ptr(),
len as u32,
self.len() as u32,
)
};
}
/// Finds last set bit.
///
/// # Examples
///
/// ```
/// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
/// use kernel::bitmap::BitmapVec;
///
/// let bitmap = BitmapVec::new(64, GFP_KERNEL)?;
///
/// match bitmap.last_bit() {
/// Some(idx) => {
/// pr_info!("The last bit has index {idx}.\n");
/// }
/// None => {
/// pr_info!("All bits in this bitmap are 0.\n");
/// }
/// }
/// # Ok::<(), AllocError>(())
/// ```
#[inline]
pub fn last_bit(&self) -> Option<usize> {
// SAFETY: `_find_next_bit` access is within bounds due to invariant.
let index = unsafe { bindings::_find_last_bit(self.as_ptr(), self.len()) };
if index >= self.len() {
None
} else {
Some(index)
}
}
/// Finds next set bit, starting from `start`.
///
/// Returns `None` if no bits are set on or after the given index. The
/// index `start` must be in bounds.
///
/// # Panics
///
/// Panics if `CONFIG_RUST_BITMAP_HARDENED` is enabled and `start` is
/// out of bounds.
#[inline]
pub fn next_bit(&self, start: usize) -> Option<usize> {
bitmap_assert!(
start < self.len(),
"`start` must be < {} was {}",
self.len(),
start
);
// SAFETY: `_find_next_bit` tolerates out-of-bounds arguments and returns a
// value larger than or equal to `self.len()` in that case.
let index = unsafe { bindings::_find_next_bit(self.as_ptr(), self.len(), start) };
if index >= self.len() {
None
} else {
Some(index)
}
}
/// Finds next zero bit, starting from `start`.
///
/// Returns `None` if no bits are zero on or after the given index. The
/// index `start` must be in bounds.
///
/// # Panics
///
/// Panics if `CONFIG_RUST_BITMAP_HARDENED` is enabled and `start` is
/// out of bounds.
#[inline]
pub fn next_zero_bit(&self, start: usize) -> Option<usize> {
bitmap_assert!(
start < self.len(),
"`start` must be < {} was {}",
self.len(),
start
);
// SAFETY: `_find_next_zero_bit` tolerates out-of-bounds arguments and returns a
// value larger than or equal to `self.len()` in that case.
let index = unsafe { bindings::_find_next_zero_bit(self.as_ptr(), self.len(), start) };
if index >= self.len() {
None
} else {
Some(index)
}
}
/// Finds a contiguous area of `nbits` zero bits at or after `start`, where the area plus
/// `align_offset` is aligned to `align`.
///
/// Returns the bit index of the start of the area, or [`None`] if no such area fitting in
/// the bitmap exists.
///
/// The returned index plus `align_offset` is a multiple of `align`.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `start` is out of bounds.
#[inline]
pub fn next_zero_area_off(
&self,
start: usize,
nbits: NonZero<usize>,
align: Alignment,
align_offset: usize,
) -> Option<usize> {
bitmap_assert!(
start < self.len(),
"`start` must be < {}, was {}",
self.len(),
start
);
let nr = u32::try_from(nbits.get()).ok()?;
let align_mask = align.as_usize() - 1;
// The C alignment and end arithmetic must not overflow, or it can read out of bounds.
// Overflow is only possible on 32-bit.
#[cfg(not(CONFIG_64BIT))]
align_mask
.checked_add(self.len())?
.checked_add(nbits.get())?;
// SAFETY: `bitmap_find_next_zero_area_off` is safe to use with an out of bounds `start`
// value and, given the overflow check above, never reads beyond `self.len()` bits.
let index = unsafe {
bindings::bitmap_find_next_zero_area_off(
self.as_ptr().cast_mut(),
self.len(),
start,
nr,
align_mask,
align_offset,
)
};
(index < self.len()).then_some(index)
}
/// Finds a contiguous area of `nbits` zero bits at or after `start`, aligned to `align`.
///
/// Returns the bit index of the start of the area, or [`None`] if no such area fitting in
/// the bitmap exists.
///
/// The returned index is a multiple of `align`.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `start` is out of bounds.
///
/// # Examples
///
/// ```
/// use kernel::{
/// alloc::{AllocError, flags::GFP_KERNEL},
/// bitmap::BitmapVec,
/// ptr::Alignment,
/// sizes::SizeConstants, //
/// };
///
/// let mut b = BitmapVec::new(64, GFP_KERNEL)?;
///
/// assert_eq!(Some(0), b.next_zero_area(0, cv!(8), Alignment::SZ_1));
/// b.set(0, cv!(5));
/// assert_eq!(Some(5), b.next_zero_area(0, cv!(8), Alignment::SZ_1));
/// assert_eq!(Some(8), b.next_zero_area(0, cv!(8), Alignment::SZ_8));
/// assert_eq!(None, b.next_zero_area(0, cv!(65), Alignment::SZ_1));
/// # Ok::<(), AllocError>(())
/// ```
#[inline]
pub fn next_zero_area(
&self,
start: usize,
nbits: NonZero<usize>,
align: Alignment,
) -> Option<usize> {
self.next_zero_area_off(start, nbits, align, 0)
}
/// Sets a contiguous area of `nbits` bits starting at `start`.
///
/// If CONFIG_RUST_BITMAP_HARDENED is not enabled and the area `start..start + nbits` is out of
/// bounds, does nothing.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and the area `start..start + nbits` is out
/// of bounds.
#[inline]
pub fn set(&mut self, start: usize, nbits: NonZero<usize>) {
bitmap_assert_return!(
start
.checked_add(nbits.get())
.is_some_and(|end| end <= self.len()),
"Area `start..start + nbits` ({}..{}) must be within bounds {}",
start,
start.saturating_add(nbits.get()),
self.len()
);
// SAFETY: The area `start..start + nbits` is within bounds and a `Bitmap` is at most
// `i32::MAX` bits, so the casts are lossless.
unsafe { bindings::__bitmap_set(self.as_mut_ptr(), start as u32, nbits.get() as i32) };
}
/// Clears a contiguous area of `nbits` bits starting at `start`.
///
/// If CONFIG_RUST_BITMAP_HARDENED is not enabled and the area `start..start + nbits` is out of
/// bounds, does nothing.
///
/// # Panics
///
/// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and the area `start..start + nbits` is out
/// of bounds.
#[inline]
pub fn clear(&mut self, start: usize, nbits: NonZero<usize>) {
bitmap_assert_return!(
start
.checked_add(nbits.get())
.is_some_and(|end| end <= self.len()),
"Area `start..start + nbits` ({}..{}) must be within bounds {}",
start,
start.saturating_add(nbits.get()),
self.len()
);
// SAFETY: The area `start..start + nbits` is within bounds and a `Bitmap` is at most
// `i32::MAX` bits, so the casts are lossless.
unsafe { bindings::__bitmap_clear(self.as_mut_ptr(), start as u32, nbits.get() as i32) };
}
}
#[cfg(CONFIG_RUST_BITMAP_KUNIT_TEST)]
#[macros::kunit_tests(rust_kernel_bitmap)]
mod tests {
use super::*;
use kernel::{
alloc::flags::GFP_KERNEL,
num::cv,
sizes::SizeConstants, //
};
#[test]
fn bitmap_borrow() {
let fake_bitmap: [usize; 2] = [0, 0];
let fake_bitmap_len = 2 * usize::BITS as usize;
// SAFETY: `fake_c_bitmap` is an array of expected length.
let b = unsafe { Bitmap::from_raw(fake_bitmap.as_ptr(), fake_bitmap_len) };
assert_eq!(fake_bitmap_len, b.len());
assert_eq!(None, b.next_bit(0));
}
#[test]
fn bitmap_copy() {
let fake_bitmap: usize = 0xFF;
// SAFETY: `fake_c_bitmap` can be used as one-element array of expected length.
let b = unsafe { Bitmap::from_raw(core::ptr::addr_of!(fake_bitmap), 8) };
assert_eq!(8, b.len());
assert_eq!(None, b.next_zero_bit(0));
}
#[test]
fn bitmap_vec_new() -> Result<(), AllocError> {
let b = BitmapVec::new(0, GFP_KERNEL)?;
assert_eq!(0, b.len());
let b = BitmapVec::new(3, GFP_KERNEL)?;
assert_eq!(3, b.len());
let b = BitmapVec::new(1024, GFP_KERNEL)?;
assert_eq!(1024, b.len());
// Requesting too large values results in [`AllocError`].
let res = BitmapVec::new(1 << 31, GFP_KERNEL);
assert!(res.is_err());
Ok(())
}
#[test]
fn bitmap_set_clear_find() -> Result<(), AllocError> {
let mut b = BitmapVec::new(128, GFP_KERNEL)?;
// Zero-initialized
assert_eq!(None, b.next_bit(0));
assert_eq!(Some(0), b.next_zero_bit(0));
assert_eq!(None, b.last_bit());
b.set_bit(17);
assert_eq!(Some(17), b.next_bit(0));
assert_eq!(Some(17), b.next_bit(17));
assert_eq!(None, b.next_bit(18));
assert_eq!(Some(17), b.last_bit());
b.set_bit(107);
assert_eq!(Some(17), b.next_bit(0));
assert_eq!(Some(17), b.next_bit(17));
assert_eq!(Some(107), b.next_bit(18));
assert_eq!(Some(107), b.last_bit());
b.clear_bit(17);
assert_eq!(Some(107), b.next_bit(0));
assert_eq!(Some(107), b.last_bit());
Ok(())
}
#[test]
#[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
let mut b = BitmapVec::new(128, GFP_KERNEL)?;
b.set_bit(2048);
b.set_bit_atomic(2048);
b.clear_bit(2048);
b.clear_bit_atomic(2048);
assert_eq!(None, b.next_bit(2048));
assert_eq!(None, b.next_zero_bit(2048));
assert_eq!(None, b.last_bit());
Ok(())
}
// TODO: uncomment once kunit supports `#[should_panic]`.
// #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
// #[test]
// #[should_panic]
// fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
// let mut b = BitmapVec::new(128, GFP_KERNEL)?;
//
// b.set_bit(2048);
// }
#[test]
fn bitmap_copy_and_extend() -> Result<(), AllocError> {
let mut long_bitmap = BitmapVec::new(256, GFP_KERNEL)?;
long_bitmap.set_bit(3);
long_bitmap.set_bit(200);
let mut short_bitmap = BitmapVec::new(32, GFP_KERNEL)?;
short_bitmap.set_bit(17);
long_bitmap.copy_and_extend(&short_bitmap);
// Previous bits have been cleared.
assert_eq!(Some(17), long_bitmap.next_bit(0));
assert_eq!(Some(17), long_bitmap.last_bit());
Ok(())
}
#[test]
fn bitmap_area_set_clear_find() -> Result<(), AllocError> {
let mut b = BitmapVec::new(128, GFP_KERNEL)?;
assert_eq!(Some(0), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
b.set(0, cv!(5)); // Now contains {[0, 5)}.
assert_eq!(Some(0), b.next_bit(0));
assert_eq!(Some(4), b.next_bit(4));
assert_eq!(Some(5), b.next_zero_bit(0));
assert_eq!(Some(5), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
assert_eq!(Some(8), b.next_zero_area(0, cv!(5), Alignment::SZ_8));
b.set(8, cv!(8)); // Now contains {[0, 5), [8, 16)}.
assert_eq!(Some(16), b.next_zero_area(0, cv!(4), Alignment::SZ_16));
assert_eq!(Some(16), b.next_zero_area(0, cv!(4), Alignment::SZ_1));
b.clear(0, cv!(5)); // Now contains {[8, 16)}.
assert_eq!(Some(0), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
assert_eq!(Some(8), b.next_bit(0));
assert_eq!(Some(15), b.last_bit());
b.set(60, cv!(10)); // Now contains {[8, 16), [60, 70)}.
assert_eq!(Some(60), b.next_bit(16));
assert_eq!(Some(69), b.last_bit());
assert_eq!(Some(16), b.next_zero_area(9, cv!(40), Alignment::SZ_1));
assert_eq!(Some(70), b.next_zero_area(0, cv!(45), Alignment::SZ_1));
b.clear(62, cv!(6)); // Now contains {[8, 16), [60, 62), [68, 70)}.
assert_eq!(Some(62), b.next_zero_area(60, cv!(6), Alignment::SZ_1));
assert_eq!(Some(61), b.next_bit(61));
assert_eq!(Some(69), b.last_bit());
Ok(())
}
#[test]
fn bitmap_area_exhaustion() -> Result<(), AllocError> {
let mut b = BitmapVec::new(64, GFP_KERNEL)?;
assert_eq!(None, b.next_zero_area(0, cv!(65), Alignment::SZ_1));
assert_eq!(None, b.next_zero_area(0, cv!(usize::MAX), Alignment::SZ_1));
assert_eq!(None, b.next_zero_area(1, cv!(usize::MAX), Alignment::SZ_1));
b.set_bit(0); // Now contains {[0, 1)}.
assert_eq!(None, b.next_zero_area(0, cv!(usize::MAX), Alignment::SZ_1));
b.set(0, cv!(61)); // Now contains {[0, 61)}.
assert_eq!(None, b.next_zero_area(0, cv!(4), Alignment::SZ_1));
assert_eq!(Some(61), b.next_zero_area(0, cv!(3), Alignment::SZ_1));
assert_eq!(None, b.next_zero_area(0, cv!(1), Alignment::SZ_64));
Ok(())
}
#[test]
fn bitmap_area_off() -> Result<(), AllocError> {
let mut b = BitmapVec::new(64, GFP_KERNEL)?;
b.set(0, cv!(5)); // Now contains {[0, 5)}.
// The area plus align_offset starts at a multiple of the alignment.
assert_eq!(Some(7), b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 1));
assert_eq!(Some(5), b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 3));
// A zero offset behaves like next_zero_area().
assert_eq!(
b.next_zero_area(0, cv!(8), Alignment::SZ_8),
b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 0)
);
Ok(())
}
#[test]
#[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
fn owned_bitmap_area_out_of_bounds() -> Result<(), AllocError> {
let mut b = BitmapVec::new(64, GFP_KERNEL)?;
// Should be ignored since out of bounds.
b.set(64, cv!(4));
b.set(62, cv!(8));
b.set(usize::MAX, cv!(1));
b.clear(usize::MAX, cv!(1));
b.clear(2048, cv!(8));
assert_eq!(None, b.next_bit(0));
assert_eq!(None, b.next_zero_area(64, cv!(1), Alignment::SZ_1));
Ok(())
}
}