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
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
(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
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 > > > > > >
© 2016 - 2025 Red Hat, Inc.