MAINTAINERS | 15 + lib/Kconfig.debug | 13 + lib/Makefile | 1 + lib/find_bit_benchmark_rust.rs | 104 ++++++ rust/bindings/bindings_helper.h | 2 + rust/helpers/bitmap.c | 9 + rust/helpers/bitops.c | 23 ++ rust/helpers/helpers.c | 2 + rust/kernel/bitmap.rs | 597 ++++++++++++++++++++++++++++++++ rust/kernel/id_pool.rs | 226 ++++++++++++ rust/kernel/lib.rs | 2 + security/Kconfig.hardening | 10 + 12 files changed, 1004 insertions(+) create mode 100644 lib/find_bit_benchmark_rust.rs create mode 100644 rust/helpers/bitmap.c create mode 100644 rust/helpers/bitops.c create mode 100644 rust/kernel/bitmap.rs create mode 100644 rust/kernel/id_pool.rs
This series adds a Rust bitmap API for porting the approach from commit 15d9da3f818c ("binder: use bitmap for faster descriptor lookup") to Rust. The functionality in dbitmap.h makes use of bitmap and bitops. The Rust bitmap API provides a safe abstraction to underlying bitmap and bitops operations. For now, only includes method necessary for dbitmap.h, more can be added later. We perform bounds checks for hardening, violations are programmer errors that result in panics. We include set_bit_atomic and clear_bit_atomic operations. One has to avoid races with non-atomic operations, which is ensure by the Rust type system: either callers have shared references &bitmap in which case the mutations are atomic operations. Or there is a exclusive reference &mut bitmap, in which case there is no concurrent access. This series includes an optimization to represent the bitmap inline, as suggested by Yury. We ran a simple microbenchmark which shows that overall the Rust API can be expected to be about 4.5% slower than C API. We also introduce a Rust API in id_pool.rs that would replace dbitmap.h from the commit referenced above. This data structure is coupled with the bitmap API and adds support for growing and shrinking, along with fine-grained control over when allocation happens. The Binder code needs this since it holds a spinlock at the time it discovers that growing is necessary; this has to be release for performing a memory allocation with GFP_KERNEL that may cause sleep. We include example doctests that demonstrate this usage. Thanks everyone for all the helpful comments, this series has improved significantly as a result of your work. Changes v14 --> v15: - Rebased on commit 08b06c30a445 ("Merge tag 'v6.17-rc4-ksmbd-fix' of git://git.samba.org/ksmbd") Changes v13 --> v14: - Rebased on commit 8742b2d8935f ("Merge tag 'pull-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs") - applied clippy suggestions for pointer casts - in benchmark module, replace use of the Rust Ktime abstraction - in benchmark module, move newlines around for pr_cont to work with Instant, elapsed. - added Alice's Reviewed-by tag Changes v12 --> v13: - Rebased on commit 75f5f23f8787 ("Merge tag 'block-6.16-20250619' of git://git.kernel.dk/linux") - Renamed types CBitmap --> Bitmap, Bitmap --> BitmapVec. - Rewrote unit tests to not use `unwrap()` but `Result` and `?` - Replaced NonNull::as_mut() with NonNull::as_ptr() - declared local BITS_PER_LONG usize constants, fixed rustdoc Changes v11 --> v12: - Added Reviewed-by tag, changed named of benchmark module - Fixed config, rustdoc, clippy and bytes computation (Pekka). - Added test that exercises CONFIG_RUST_BITMAP_HARDENED, verified it panics. - Had to add a break to benchmark module, for the CONFIG_RUST_BITMAP_HARDENED case which enforces bounds check. Changes v10 --> v11: - Fix Kconfig dependency, Rust benchmark depends on CONFIG_RUST - Disable clippy warning for len() without is_empty() in id_pool.rs Changes v9 --> v10: - change helper to use `unsigned long` for `nr` parameter (Boqun) - add missing and fix safety comments (Boqun, Pekka) - move benchmark module output and results to #4 commit msg. - use pr_cont to avoid repeating find_bit_benchmark_rust log prefix. - Disable clippy warning for len() without is_empty() in bitmap.rs Changes v8 --> v9: - added a new type `CBitmap` that makes any C bitmap accessible with the same API, and add Deref so both Bitmap and CBitmap can share the same implementation. Full credit for this goes to Alice who suggested idea and code. - added config dependency on CONFIG_RUST that was missing from CONIG_FIND_BIT_BENCHMARK_RUST. - implemented Send for Bitmap, it is actually needed by Binder. - reworded commit msg for clarity. - removed unsafe for atomic ops. - renamed `bitmap_hardening_assert` to `bitmap_assert` and make operations do nothing and log error when RUST_BITMAP_HARDENED is off. - update author information in find_bit_benchmark_rust.rs - Various improvements to id_pool, better names and comments. Changes v7 --> v8: - added Acked-by for bindings patches - add RUST_BITMAP_HARDENED config, making extra bound-checks configurable. This is added to security/Kconfig.hardening - changed checks of `index` return value to >= - removed change to FIND_BIT_BENCHMARK Changes v6 --> v7: - Added separate unit tests in bitmap.rs and benchmark module, following the example in find_bit_benchmark.c - Added discussion about using vendored bitset to commit message. - Refined warning about naming convention Changes v5 --> v6: - Added SAFETY comment for atomic operations. - Added missing volatile to bitops set_bit and clear_bit bindings. - Fixed condition on `nbits` to be <= i32::MAX, update SAFETY comments. - Readability improvements. - Updated doc comments wording and indentation. Changes v4 --> v5: (suggested by Yury and Alice) - rebased on next-20250318 - split MAINTAINERS changes - no dependencies on [1] and [2] anymore - Viresh, please do add a separate section if you want to maintain cpumask.rs separately. - imports atomic and non-atomic variants, introduces a naming convention set_bit and set_bit_atomic on the Rust side. - changed naming and comments. Keeping `new`. - change dynamic_id_pool to id_pool - represent bitmap inline when possible - add some more tests - add myself to M: line for the Rust abstractions Changes v3 --> v4: - Rebased on Viresh's v3 [2]. - split into multiple patches, separate Rust and bindings. (Yury) - adds dynamic_id_pool.rs to show the Binder use case. (Yury) - include example usage that requires release of spinlock (Alice) - changed bounds checks to `assert!`, shorter (Yury) - fix param names in binding helpers. (Miguel) - proper rustdoc formatting, and use examples as kunit tests. (Miguel) - reduce number of Bitmap methods, and simplify API through use Option<usize> to handle the "not found" case. - make Bitmap pointer accessors private, so Rust Bitmap API provides an actual abstraction boundary (Tamir) - we still return `AllocError` in `Bitmap::new` in case client code asks for a size that is too large. Intentionally different from other bounds checks because it is not about access but allocation, and we expect that client code need never handle AllocError and nbits > u32::MAX situations differently. Changes v2 --> v3: - change `bitmap_copy` to `copy_from_bitmap_and_extend` which zeroes out extra bits. This enables dbitmap shrink and grow use cases while offering a consistent and understandable Rust API for other uses (Alice) Changes v1 --> v2: - Rebased on Yury's v2 [1] and Viresh's v3 [2] changes related to bitmap. - Removed import of `bindings::*`, keeping only prefix (Miguel) - Renamed panic methods to make more explicit (Miguel) - use markdown in doc comments and added example/kunit test (Miguel) - Added maintainer section for BITOPS API BINDINGS [RUST] (Yury) - Added M: entry for bitmap.rs which goes to Alice (Viresh, Alice) - Changed calls from find_* to _find_*, removed helpers (Yury) - Use non-atomic __set_bit and __clear_bit from Bitmap Rust API (Yury) Link [1] https://lore.kernel.org/all/20250224233938.3158-1-yury.norov@gmail.com/ Link [2] https://lore.kernel.org/rust-for-linux/cover.1742296835.git.viresh.kumar@linaro.org/ Link [v14] https://lore.kernel.org/rust-for-linux/20250813153548.1650533-1-bqe@google.com/ Burak Emir (5): rust: add bindings for bitmap.h rust: add bindings for bitops.h rust: add bitmap API. rust: add find_bit_benchmark_rust module. rust: add dynamic ID pool abstraction for bitmap MAINTAINERS | 15 + lib/Kconfig.debug | 13 + lib/Makefile | 1 + lib/find_bit_benchmark_rust.rs | 104 ++++++ rust/bindings/bindings_helper.h | 2 + rust/helpers/bitmap.c | 9 + rust/helpers/bitops.c | 23 ++ rust/helpers/helpers.c | 2 + rust/kernel/bitmap.rs | 597 ++++++++++++++++++++++++++++++++ rust/kernel/id_pool.rs | 226 ++++++++++++ rust/kernel/lib.rs | 2 + security/Kconfig.hardening | 10 + 12 files changed, 1004 insertions(+) create mode 100644 lib/find_bit_benchmark_rust.rs create mode 100644 rust/helpers/bitmap.c create mode 100644 rust/helpers/bitops.c create mode 100644 rust/kernel/bitmap.rs create mode 100644 rust/kernel/id_pool.rs base-commit: 08b06c30a44555a8b1d14950e4462a52bfa0758b -- 2.51.0.355.g5224444f11-goog
On Thu, Sep 04, 2025 at 04:50:04PM +0000, Burak Emir wrote: > This series adds a Rust bitmap API for porting the approach from > commit 15d9da3f818c ("binder: use bitmap for faster descriptor lookup") > to Rust. The functionality in dbitmap.h makes use of bitmap and bitops. Added in bitmap-for-next for testing. Thanks!
On Thu, Sep 4, 2025 at 8:02 PM Yury Norov <yury.norov@gmail.com> wrote: > > Added in bitmap-for-next for testing. Thanks! linux-next breaks with CONFIG_RUST_BITMAP_HARDENED=y: error[E0425]: cannot find function `owned_bitmap_out_of_bounds` in this scope --> rust/kernel/bitmap.rs:484:1 | 484 | #[kunit_tests(rust_kernel_bitmap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope because the proc macro doesn't support `cfg`s (moving it below `#[test]` wouldn't work either). I have filled: https://github.com/Rust-for-Linux/linux/issues/1185 so that we don't forget about it. Meanwhile, I would recommend e.g. moving the `cfg` inside the function, something like: diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs index 6e0824579781..2f00e91e9c35 100644 --- a/rust/kernel/bitmap.rs +++ b/rust/kernel/bitmap.rs @@ -551,18 +551,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> { Ok(()) } - #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] #[test] fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> { - let mut b = BitmapVec::new(128, GFP_KERNEL)?; + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] + { + 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()); + } - 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(()) } Thanks! Cheers, Miguel
Nope. Try again. diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs index 6e0824579781..2f00e91e9c35 100644 --- a/rust/kernel/bitmap.rs +++ b/rust/kernel/bitmap.rs @@ -551,18 +551,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> { Ok(()) } - #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] #[test] fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> { - let mut b = BitmapVec::new(128, GFP_KERNEL)?; + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] + { + 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()); + } - 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(()) }
On Sat, Sep 06, 2025 at 12:18:38PM +0300, Dan Carpenter wrote: > Nope. Try again. > > diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs > index 6e0824579781..2f00e91e9c35 100644 > --- a/rust/kernel/bitmap.rs > +++ b/rust/kernel/bitmap.rs > @@ -551,18 +551,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> { > Ok(()) > } > > - #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] > #[test] > fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> { > - let mut b = BitmapVec::new(128, GFP_KERNEL)?; > + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] > + { > + 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()); > + } > > - 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(()) > } Alright, the testing is definitely failed. I'll drop the series and let Burak to send v16 with all fixes merged. Thanks, Yury
On Sat, Sep 6, 2025 at 4:27 PM Yury Norov <yury.norov@gmail.com> wrote: > > Alright, the testing is definitely failed. I'll drop the series and > let Burak to send v16 with all fixes merged. What failed? Or do you mean Dan's second message? I understood that Dan was fixing the first diff he posted. (I am asking since v16 seems similar to what I posted.) Cheers, Miguel
On Mon, Sep 08, 2025 at 11:24:02AM +0200, Miguel Ojeda wrote: > I understood that Dan was fixing the first diff he posted. > Yeah. Sorry. I tested it but then I must have damanged it before I sent it. :/ regards, dan carpenter
On Sat, Sep 6, 2025 at 4:27 PM Yury Norov <yury.norov@gmail.com> wrote: > > Alright, the testing is definitely failed. I'll drop the series and > let Burak to send v16 with all fixes merged. Sorry about this. Please find v16 here: https://lore.kernel.org/rust-for-linux/20250908072158.1041611-1-bqe@google.com/T/#t - Burak
Here is the patch so people who want to build linux-next can just apply it instead of needing to cut and paste and then %s/ // or whatever... Hopefully, people aren't building kernels over the weekend and it's fixed on Monday. But just in case. regards, dan carpenter diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs index 6e0824579781..2f00e91e9c35 100644 --- a/rust/kernel/bitmap.rs +++ b/rust/kernel/bitmap.rs @@ -551,18 +551,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> { Ok(()) } - #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] #[test] fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> { - let mut b = BitmapVec::new(128, GFP_KERNEL)?; + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] + { + 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()); - 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(()) }
+ Philip Li <philip.li@intel.com> On Fri, Sep 05, 2025 at 11:29:22AM +0200, Miguel Ojeda wrote: > On Thu, Sep 4, 2025 at 8:02 PM Yury Norov <yury.norov@gmail.com> wrote: > > > > Added in bitmap-for-next for testing. Thanks! > > linux-next breaks with CONFIG_RUST_BITMAP_HARDENED=y: > > error[E0425]: cannot find function `owned_bitmap_out_of_bounds` > in this scope > --> rust/kernel/bitmap.rs:484:1 > | > 484 | #[kunit_tests(rust_kernel_bitmap)] > | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope > > because the proc macro doesn't support `cfg`s (moving it below > `#[test]` wouldn't work either). I have filled: > > https://github.com/Rust-for-Linux/linux/issues/1185 > > so that we don't forget about it. > > Meanwhile, I would recommend e.g. moving the `cfg` inside the > function, something like: > > diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs > index 6e0824579781..2f00e91e9c35 100644 > --- a/rust/kernel/bitmap.rs > +++ b/rust/kernel/bitmap.rs > @@ -551,18 +551,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> { > Ok(()) > } > > - #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] > #[test] > fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> { > - let mut b = BitmapVec::new(128, GFP_KERNEL)?; > + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] > + { > + 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()); > + } > > - 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(()) > } Thanks for the testing, Miguel! I've folded-in your fix and added your co-developed-by tag. Please let me know if it doesn't work for you. Philip, is it possible to add CONFIG_RUST_BITMAP_HARDENED=y/n in your testing too? Thanks, Yury
(Actually adding Philip in the list.) On Fri, Sep 05, 2025 at 09:18:50AM -0400, Yury ooNorov wrote: > + Philip Li <philip.li@intel.com> > > On Fri, Sep 05, 2025 at 11:29:22AM +0200, Miguel Ojeda wrote: > > On Thu, Sep 4, 2025 at 8:02 PM Yury Norov <yury.norov@gmail.com> wrote: > > > > > > Added in bitmap-for-next for testing. Thanks! > > > > linux-next breaks with CONFIG_RUST_BITMAP_HARDENED=y: > > > > error[E0425]: cannot find function `owned_bitmap_out_of_bounds` > > in this scope > > --> rust/kernel/bitmap.rs:484:1 > > | > > 484 | #[kunit_tests(rust_kernel_bitmap)] > > | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope > > > > because the proc macro doesn't support `cfg`s (moving it below > > `#[test]` wouldn't work either). I have filled: > > > > https://github.com/Rust-for-Linux/linux/issues/1185 > > > > so that we don't forget about it. > > > > Meanwhile, I would recommend e.g. moving the `cfg` inside the > > function, something like: > > > > diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs > > index 6e0824579781..2f00e91e9c35 100644 > > --- a/rust/kernel/bitmap.rs > > +++ b/rust/kernel/bitmap.rs > > @@ -551,18 +551,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> { > > Ok(()) > > } > > > > - #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] > > #[test] > > fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> { > > - let mut b = BitmapVec::new(128, GFP_KERNEL)?; > > + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))] > > + { > > + 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()); > > + } > > > > - 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(()) > > } > > Thanks for the testing, Miguel! I've folded-in your fix and added > your co-developed-by tag. Please let me know if it doesn't work for > you. > > Philip, is it possible to add CONFIG_RUST_BITMAP_HARDENED=y/n in your > testing too? > > Thanks, > Yury
On Fri, Sep 05, 2025 at 05:48:42PM -0400, Yury Norov wrote: > (Actually adding Philip in the list.) > > On Fri, Sep 05, 2025 at 09:18:50AM -0400, Yury ooNorov wrote: > > + Philip Li <philip.li@intel.com> > > > > On Fri, Sep 05, 2025 at 11:29:22AM +0200, Miguel Ojeda wrote: > > > On Thu, Sep 4, 2025 at 8:02 PM Yury Norov <yury.norov@gmail.com> wrote: > > > > > > > > Added in bitmap-for-next for testing. Thanks! > > > > > > linux-next breaks with CONFIG_RUST_BITMAP_HARDENED=y: ... > > Thanks for the testing, Miguel! I've folded-in your fix and added > > your co-developed-by tag. Please let me know if it doesn't work for > > you. > > > > Philip, is it possible to add CONFIG_RUST_BITMAP_HARDENED=y/n in your > > testing too? Hi Yury, got it, I will add this coverage in the bot. > > > > Thanks, > > Yury
> > > Thanks for the testing, Miguel! I've folded-in your fix and added > > > your co-developed-by tag. Please let me know if it doesn't work for > > > you. > > > > > > Philip, is it possible to add CONFIG_RUST_BITMAP_HARDENED=y/n in your > > > testing too? > > Hi Yury, got it, I will add this coverage in the bot. Thanks Philip!
On Fri, Sep 5, 2025 at 3:18 PM Yury ooNorov <yury.norov@gmail.com> wrote: > > Thanks for the testing, Miguel! I've folded-in your fix and added > your co-developed-by tag. Please let me know if it doesn't work for > you. You're welcome! I regularly build a bunch of arches/configs/compiler versions etc. As for the tag, you are too kind :) It is such a small thing that I feel it may look like it diminishes the work from Burak and others, so I think it may be best to drop it (and anyway I didn't take a close look at the code or the latest versions, and a SoB would need to be added either way). Thanks! Cheers, Miguel
> As for the tag, you are too kind :) It is such a small thing that I > feel it may look like it diminishes the work from Burak and others, so > I think it may be best to drop it (and anyway I didn't take a close > look at the code or the latest versions, and a SoB would need to be > added either way). OK, done
© 2016 - 2025 Red Hat, Inc.