[PATCH v5 1/2] rust: allocator: add KUnit tests for alignment guarantees

Hui Zhu posted 2 patches 2 months, 1 week ago
There is a newer version of this series
[PATCH v5 1/2] rust: allocator: add KUnit tests for alignment guarantees
Posted by Hui Zhu 2 months, 1 week ago
From: Hui Zhu <zhuhui@kylinos.cn>

Add comprehensive tests to verify correct alignment handling in Rust
allocator wrappers. The tests validate:

That kmalloc respects both standard (128-byte) and page-size
(8192-byte) alignments when allocating structs with explicit alignment
attributes.

That vmalloc correctly handles standard alignments but intentionally
rejects allocations requiring alignments larger than its capabilities.

That kvmalloc mirrors vmalloc's constraints, accepting standard
alignments but rejecting excessive alignment requirements.

The test infrastructure uses specialized aligned structs (Blob and
LargeAlignBlob) and a test harness (TestAlign) to validate pointer
alignment through different allocation paths. This ensures our Rust
allocators correctly propagate kernel allocation constraints.

Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
---
 rust/kernel/alloc/allocator.rs | 58 ++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
index aa2dfa9dca4c..bcc916240f11 100644
--- a/rust/kernel/alloc/allocator.rs
+++ b/rust/kernel/alloc/allocator.rs
@@ -187,3 +187,61 @@ unsafe fn realloc(
         unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
     }
 }
+
+#[macros::kunit_tests(rust_allocator_kunit)]
+mod tests {
+    use super::*;
+    use core::mem::MaybeUninit;
+    use kernel::prelude::*;
+
+    const TEST_SIZE: usize = 1024;
+    const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;
+
+    // These two structs are used to test allocating aligned memory.
+    // they don't need to be accessed, so they're marked as dead_code.
+    #[allow(dead_code)]
+    #[repr(align(128))]
+    struct Blob([u8; TEST_SIZE]);
+    #[allow(dead_code)]
+    #[repr(align(8192))]
+    struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);
+
+    struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);
+    impl<T, A: Allocator> TestAlign<T, A> {
+        fn new() -> Result<Self> {
+            Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))
+        }
+
+        fn alignment_valid(&self, align: usize) -> bool {
+            assert!(align.is_power_of_two());
+
+            let addr = self.0.as_ptr() as usize;
+            if addr & (align - 1) != 0 {
+                false
+            } else {
+                true
+            }
+        }
+    }
+
+    #[test]
+    fn test_alignment() -> Result<()> {
+        let ta = TestAlign::<Blob, Kmalloc>::new()?;
+        assert!(ta.alignment_valid(128));
+
+        let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;
+        assert!(ta.alignment_valid(8192));
+
+        let ta = TestAlign::<Blob, Vmalloc>::new()?;
+        assert!(ta.alignment_valid(128));
+
+        assert!(TestAlign::<LargeAlignBlob, Vmalloc>::new().is_err());
+
+        let ta = TestAlign::<Blob, KVmalloc>::new()?;
+        assert!(ta.alignment_valid(128));
+
+        assert!(TestAlign::<LargeAlignBlob, KVmalloc>::new().is_err());
+
+        Ok(())
+    }
+}
-- 
2.43.0
Re: [PATCH v5 1/2] rust: allocator: add KUnit tests for alignment guarantees
Posted by Danilo Krummrich 2 months, 1 week ago
On Fri Jul 25, 2025 at 9:02 AM CEST, Hui Zhu wrote:
> From: Hui Zhu <zhuhui@kylinos.cn>
>
> Add comprehensive tests to verify correct alignment handling in Rust
> allocator wrappers. The tests validate:
>
> That kmalloc respects both standard (128-byte) and page-size
> (8192-byte) alignments when allocating structs with explicit alignment
> attributes.
>
> That vmalloc correctly handles standard alignments but intentionally
> rejects allocations requiring alignments larger than its capabilities.
>
> That kvmalloc mirrors vmalloc's constraints, accepting standard
> alignments but rejecting excessive alignment requirements.
>
> The test infrastructure uses specialized aligned structs (Blob and
> LargeAlignBlob) and a test harness (TestAlign) to validate pointer
> alignment through different allocation paths. This ensures our Rust
> allocators correctly propagate kernel allocation constraints.
>
> Co-developed-by: Geliang Tang <geliang@kernel.org>
> Signed-off-by: Geliang Tang <geliang@kernel.org>
> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>

Thanks, this looks good. I think it would be good to rebase onto [1], since it
will likely land in the same cycle. Additionally, two nits below.

As a follow-up we could also test alignment in the context of
Allocator::realloc(), i.e. when growing and shrinking buffers or requesting a
different NUMA node.

[1] https://lore.kernel.org/lkml/20250715135645.2230065-1-vitaly.wool@konsulko.se/

> ---
>  rust/kernel/alloc/allocator.rs | 58 ++++++++++++++++++++++++++++++++++
>  1 file changed, 58 insertions(+)
>
> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> index aa2dfa9dca4c..bcc916240f11 100644
> --- a/rust/kernel/alloc/allocator.rs
> +++ b/rust/kernel/alloc/allocator.rs
> @@ -187,3 +187,61 @@ unsafe fn realloc(
>          unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
>      }
>  }
> +
> +#[macros::kunit_tests(rust_allocator_kunit)]
> +mod tests {
> +    use super::*;
> +    use core::mem::MaybeUninit;
> +    use kernel::prelude::*;
> +

--8<--

> +    const TEST_SIZE: usize = 1024;
> +    const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;
> +
> +    // These two structs are used to test allocating aligned memory.
> +    // they don't need to be accessed, so they're marked as dead_code.
> +    #[allow(dead_code)]

This should be #[expect(dead_code)].

> +    #[repr(align(128))]
> +    struct Blob([u8; TEST_SIZE]);
> +    #[allow(dead_code)]
> +    #[repr(align(8192))]
> +    struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);
> +
> +    struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);
> +    impl<T, A: Allocator> TestAlign<T, A> {
> +        fn new() -> Result<Self> {
> +            Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))
> +        }
> +
> +        fn alignment_valid(&self, align: usize) -> bool {
> +            assert!(align.is_power_of_two());
> +
> +            let addr = self.0.as_ptr() as usize;
> +            if addr & (align - 1) != 0 {
> +                false
> +            } else {
> +                true
> +            }

This can just be

	addr & (align - 1) == 0

instead of the conditional clause.

> +        }
> +    }

We could move all the above into test_alignment() given that it's likely only
needed from there.

> +
> +    #[test]
> +    fn test_alignment() -> Result<()> {
> +        let ta = TestAlign::<Blob, Kmalloc>::new()?;
> +        assert!(ta.alignment_valid(128));
> +
> +        let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;
> +        assert!(ta.alignment_valid(8192));
> +
> +        let ta = TestAlign::<Blob, Vmalloc>::new()?;
> +        assert!(ta.alignment_valid(128));
> +
> +        assert!(TestAlign::<LargeAlignBlob, Vmalloc>::new().is_err());
> +
> +        let ta = TestAlign::<Blob, KVmalloc>::new()?;
> +        assert!(ta.alignment_valid(128));
> +
> +        assert!(TestAlign::<LargeAlignBlob, KVmalloc>::new().is_err());
> +
> +        Ok(())
> +    }
> +}
> -- 
> 2.43.0
Re: [PATCH v5 1/2] rust: allocator: add KUnit tests for alignment guarantees
Posted by Danilo Krummrich 2 months, 1 week ago
(Cc: Andrew)

On Fri Jul 25, 2025 at 11:50 AM CEST, Danilo Krummrich wrote:
> On Fri Jul 25, 2025 at 9:02 AM CEST, Hui Zhu wrote:
>> From: Hui Zhu <zhuhui@kylinos.cn>
>>
>> Add comprehensive tests to verify correct alignment handling in Rust
>> allocator wrappers. The tests validate:
>>
>> That kmalloc respects both standard (128-byte) and page-size
>> (8192-byte) alignments when allocating structs with explicit alignment
>> attributes.
>>
>> That vmalloc correctly handles standard alignments but intentionally
>> rejects allocations requiring alignments larger than its capabilities.
>>
>> That kvmalloc mirrors vmalloc's constraints, accepting standard
>> alignments but rejecting excessive alignment requirements.
>>
>> The test infrastructure uses specialized aligned structs (Blob and
>> LargeAlignBlob) and a test harness (TestAlign) to validate pointer
>> alignment through different allocation paths. This ensures our Rust
>> allocators correctly propagate kernel allocation constraints.
>>
>> Co-developed-by: Geliang Tang <geliang@kernel.org>
>> Signed-off-by: Geliang Tang <geliang@kernel.org>
>> Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
>
> Thanks, this looks good. I think it would be good to rebase onto [1], since it
> will likely land in the same cycle. Additionally, two nits below.

Please also Cc: Andrew for subsequent submissions, since this will, due to the
interaction with [1] likely go through his tree.

> As a follow-up we could also test alignment in the context of
> Allocator::realloc(), i.e. when growing and shrinking buffers or requesting a
> different NUMA node.
>
> [1] https://lore.kernel.org/lkml/20250715135645.2230065-1-vitaly.wool@konsulko.se/
>
>> ---
>>  rust/kernel/alloc/allocator.rs | 58 ++++++++++++++++++++++++++++++++++
>>  1 file changed, 58 insertions(+)
>>
>> diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
>> index aa2dfa9dca4c..bcc916240f11 100644
>> --- a/rust/kernel/alloc/allocator.rs
>> +++ b/rust/kernel/alloc/allocator.rs
>> @@ -187,3 +187,61 @@ unsafe fn realloc(
>>          unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
>>      }
>>  }
>> +
>> +#[macros::kunit_tests(rust_allocator_kunit)]
>> +mod tests {
>> +    use super::*;
>> +    use core::mem::MaybeUninit;
>> +    use kernel::prelude::*;
>> +
>
> --8<--
>
>> +    const TEST_SIZE: usize = 1024;
>> +    const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;
>> +
>> +    // These two structs are used to test allocating aligned memory.
>> +    // they don't need to be accessed, so they're marked as dead_code.
>> +    #[allow(dead_code)]
>
> This should be #[expect(dead_code)].
>
>> +    #[repr(align(128))]
>> +    struct Blob([u8; TEST_SIZE]);
>> +    #[allow(dead_code)]
>> +    #[repr(align(8192))]
>> +    struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);
>> +
>> +    struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);
>> +    impl<T, A: Allocator> TestAlign<T, A> {
>> +        fn new() -> Result<Self> {
>> +            Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))
>> +        }
>> +
>> +        fn alignment_valid(&self, align: usize) -> bool {
>> +            assert!(align.is_power_of_two());
>> +
>> +            let addr = self.0.as_ptr() as usize;
>> +            if addr & (align - 1) != 0 {
>> +                false
>> +            } else {
>> +                true
>> +            }
>
> This can just be
>
> 	addr & (align - 1) == 0
>
> instead of the conditional clause.
>
>> +        }
>> +    }
>
> We could move all the above into test_alignment() given that it's likely only
> needed from there.
>
>> +
>> +    #[test]
>> +    fn test_alignment() -> Result<()> {
>> +        let ta = TestAlign::<Blob, Kmalloc>::new()?;
>> +        assert!(ta.alignment_valid(128));
>> +
>> +        let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;
>> +        assert!(ta.alignment_valid(8192));
>> +
>> +        let ta = TestAlign::<Blob, Vmalloc>::new()?;
>> +        assert!(ta.alignment_valid(128));
>> +
>> +        assert!(TestAlign::<LargeAlignBlob, Vmalloc>::new().is_err());
>> +
>> +        let ta = TestAlign::<Blob, KVmalloc>::new()?;
>> +        assert!(ta.alignment_valid(128));
>> +
>> +        assert!(TestAlign::<LargeAlignBlob, KVmalloc>::new().is_err());
>> +
>> +        Ok(())
>> +    }
>> +}
>> -- 
>> 2.43.0
Re: [PATCH v5 1/2] rust: allocator: add KUnit tests for alignment guarantees
Posted by Hui Zhu 2 months, 1 week ago
2025年7月25日 18:02, "Danilo Krummrich" <dakr@kernel.org mailto:dakr@kernel.org?to=%22Danilo%20Krummrich%22%20%3Cdakr%40kernel.org%3E > 写到:


> 
> (Cc: Andrew)
> 
> On Fri Jul 25, 2025 at 11:50 AM CEST, Danilo Krummrich wrote:
> 
> > 
> > On Fri Jul 25, 2025 at 9:02 AM CEST, Hui Zhu wrote:
> > 
> > > 
> > > From: Hui Zhu <zhuhui@kylinos.cn>
> > > 
> > >  Add comprehensive tests to verify correct alignment handling in Rust
> > >  allocator wrappers. The tests validate:
> > > 
> > >  That kmalloc respects both standard (128-byte) and page-size
> > >  (8192-byte) alignments when allocating structs with explicit alignment
> > >  attributes.
> > > 
> > >  That vmalloc correctly handles standard alignments but intentionally
> > >  rejects allocations requiring alignments larger than its capabilities.
> > > 
> > >  That kvmalloc mirrors vmalloc's constraints, accepting standard
> > >  alignments but rejecting excessive alignment requirements.
> > > 
> > >  The test infrastructure uses specialized aligned structs (Blob and
> > >  LargeAlignBlob) and a test harness (TestAlign) to validate pointer
> > >  alignment through different allocation paths. This ensures our Rust
> > >  allocators correctly propagate kernel allocation constraints.
> > > 
> > >  Co-developed-by: Geliang Tang <geliang@kernel.org>
> > >  Signed-off-by: Geliang Tang <geliang@kernel.org>
> > >  Signed-off-by: Hui Zhu <zhuhui@kylinos.cn>
> > > 
> >  Thanks, this looks good. I think it would be good to rebase onto [1], since it
> >  will likely land in the same cycle. Additionally, two nits below.
> > 
> Please also Cc: Andrew for subsequent submissions, since this will, due to the
> interaction with [1] likely go through his tree.
> 
> > 
> > As a follow-up we could also test alignment in the context of
> >  Allocator::realloc(), i.e. when growing and shrinking buffers or requesting a
> >  different NUMA node.
> > 
> >  [1] https://lore.kernel.org/lkml/20250715135645.2230065-1-vitaly.wool@konsulko.se/
> > 
> > > 
> > > ---
> > >  rust/kernel/alloc/allocator.rs | 58 ++++++++++++++++++++++++++++++++++
> > >  1 file changed, 58 insertions(+)
> > > 
> > >  diff --git a/rust/kernel/alloc/allocator.rs b/rust/kernel/alloc/allocator.rs
> > >  index aa2dfa9dca4c..bcc916240f11 100644
> > >  --- a/rust/kernel/alloc/allocator.rs
> > >  +++ b/rust/kernel/alloc/allocator.rs
> > >  @@ -187,3 +187,61 @@ unsafe fn realloc(
> > >  unsafe { ReallocFunc::KVREALLOC.call(ptr, layout, old_layout, flags) }
> > >  }
> > >  }
> > >  +
> > >  +#[macros::kunit_tests(rust_allocator_kunit)]
> > >  +mod tests {
> > >  + use super::*;
> > >  + use core::mem::MaybeUninit;
> > >  + use kernel::prelude::*;
> > >  +
> > > 
> >  --8<--
> > 
> > > 
> > > + const TEST_SIZE: usize = 1024;
> > >  + const TEST_LARGE_ALIGN_SIZE: usize = kernel::page::PAGE_SIZE * 4;
> > >  +
> > >  + // These two structs are used to test allocating aligned memory.
> > >  + // they don't need to be accessed, so they're marked as dead_code.
> > >  + #[allow(dead_code)]
> > > 
> >  This should be #[expect(dead_code)].
> > 
> > > 
> > > + #[repr(align(128))]
> > >  + struct Blob([u8; TEST_SIZE]);
> > >  + #[allow(dead_code)]
> > >  + #[repr(align(8192))]
> > >  + struct LargeAlignBlob([u8; TEST_LARGE_ALIGN_SIZE]);
> > >  +
> > >  + struct TestAlign<T, A: Allocator>(Box<MaybeUninit<T>, A>);
> > >  + impl<T, A: Allocator> TestAlign<T, A> {
> > >  + fn new() -> Result<Self> {
> > >  + Ok(Self(Box::<_, A>::new_uninit(GFP_KERNEL)?))
> > >  + }
> > >  +
> > >  + fn alignment_valid(&self, align: usize) -> bool {
> > >  + assert!(align.is_power_of_two());
> > >  +
> > >  + let addr = self.0.as_ptr() as usize;
> > >  + if addr & (align - 1) != 0 {
> > >  + false
> > >  + } else {
> > >  + true
> > >  + }
> > > 
> >  This can just be
> > 
> >  addr & (align - 1) == 0
> > 
> >  instead of the conditional clause.
> > 
> > > 
> > > + }
> > >  + }
> > > 
> >  We could move all the above into test_alignment() given that it's likely only
> >  needed from there.
> > 
> > > 

Hi Danilo,

Thanks!

I sent v6 version that rebased on [1].

Best,
Hui

[1] https://lore.kernel.org/lkml/20250715135645.2230065-1-vitaly.wool@konsulko.se/

> > > +
> > >  + #[test]
> > >  + fn test_alignment() -> Result<()> {
> > >  + let ta = TestAlign::<Blob, Kmalloc>::new()?;
> > >  + assert!(ta.alignment_valid(128));
> > >  +
> > >  + let ta = TestAlign::<LargeAlignBlob, Kmalloc>::new()?;
> > >  + assert!(ta.alignment_valid(8192));
> > >  +
> > >  + let ta = TestAlign::<Blob, Vmalloc>::new()?;
> > >  + assert!(ta.alignment_valid(128));
> > >  +
> > >  + assert!(TestAlign::<LargeAlignBlob, Vmalloc>::new().is_err());
> > >  +
> > >  + let ta = TestAlign::<Blob, KVmalloc>::new()?;
> > >  + assert!(ta.alignment_valid(128));
> > >  +
> > >  + assert!(TestAlign::<LargeAlignBlob, KVmalloc>::new().is_err());
> > >  +
> > >  + Ok(())
> > >  + }
> > >  +}
> > >  -- 
> > >  2.43.0
> > >
> >
>