Skip to content

Commit ad59baa

Browse files
committed
slab, rust: extend kmalloc() alignment guarantees to remove Rust padding
Slab allocators have been guaranteeing natural alignment for power-of-two sizes since commit 59bb479 ("mm, sl[aou]b: guarantee natural alignment for kmalloc(power-of-two)"), while any other sizes are guaranteed to be aligned only to ARCH_KMALLOC_MINALIGN bytes (although in practice are aligned more than that in non-debug scenarios). Rust's allocator API specifies size and alignment per allocation, which have to satisfy the following rules, per Alice Ryhl [1]: 1. The alignment is a power of two. 2. The size is non-zero. 3. When you round up the size to the next multiple of the alignment, then it must not overflow the signed type isize / ssize_t. In order to map this to kmalloc()'s guarantees, some requested allocation sizes have to be padded to the next power-of-two size [2]. For example, an allocation of size 96 and alignment of 32 will be padded to an allocation of size 128, because the existing kmalloc-96 bucket doesn't guarantee alignent above ARCH_KMALLOC_MINALIGN. Without slab debugging active, the layout of the kmalloc-96 slabs however naturally align the objects to 32 bytes, so extending the size to 128 bytes is wasteful. To improve the situation we can extend the kmalloc() alignment guarantees in a way that 1) doesn't change the current slab layout (and thus does not increase internal fragmentation) when slab debugging is not active 2) reduces waste in the Rust allocator use case 3) is a superset of the current guarantee for power-of-two sizes. The extended guarantee is that alignment is at least the largest power-of-two divisor of the requested size. For power-of-two sizes the largest divisor is the size itself, but let's keep this case documented separately for clarity. For current kmalloc size buckets, it means kmalloc-96 will guarantee alignment of 32 bytes and kmalloc-196 will guarantee 64 bytes. This covers the rules 1 and 2 above of Rust's API as long as the size is a multiple of the alignment. The Rust layer should now only need to round up the size to the next multiple if it isn't, while enforcing the rule 3. Implementation-wise, this changes the alignment calculation in create_boot_cache(). While at it also do the calulation only for caches with the SLAB_KMALLOC flag, because the function is also used to create the initial kmem_cache and kmem_cache_node caches, where no alignment guarantee is necessary. In the Rust allocator's krealloc_aligned(), remove the code that padded sizes to the next power of two (suggested by Alice Ryhl) as it's no longer necessary with the new guarantees. Reported-by: Alice Ryhl <[email protected]> Reported-by: Boqun Feng <[email protected]> Link: https://lore.kernel.org/all/CAH5fLggjrbdUuT-H-5vbQfMazjRDpp2%2Bk3%[email protected]/ [1] Link: https://lore.kernel.org/all/CAH5fLghsZRemYUwVvhk77o6y1foqnCeDzW4WZv6ScEWna2+_jw@mail.gmail.com/ [2] Reviewed-by: Boqun Feng <[email protected]> Acked-by: Roman Gushchin <[email protected]> Reviewed-by: Alice Ryhl <[email protected]> Signed-off-by: Vlastimil Babka <[email protected]>
1 parent 4a24bba commit ad59baa

File tree

4 files changed

+17
-20
lines changed

4 files changed

+17
-20
lines changed

Documentation/core-api/memory-allocation.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,10 @@ configuration, but it is a good practice to use `kmalloc` for objects
144144
smaller than page size.
145145

146146
The address of a chunk allocated with `kmalloc` is aligned to at least
147-
ARCH_KMALLOC_MINALIGN bytes. For sizes which are a power of two, the
148-
alignment is also guaranteed to be at least the respective size.
147+
ARCH_KMALLOC_MINALIGN bytes. For sizes which are a power of two, the
148+
alignment is also guaranteed to be at least the respective size. For other
149+
sizes, the alignment is guaranteed to be at least the largest power-of-two
150+
divisor of the size.
149151

150152
Chunks allocated with kmalloc() can be resized with krealloc(). Similarly
151153
to kmalloc_array(): a helper for resizing arrays is provided in the form of

include/linux/slab.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,8 @@ void *__kmalloc_large_node_noprof(size_t size, gfp_t flags, int node)
604604
*
605605
* The allocated object address is aligned to at least ARCH_KMALLOC_MINALIGN
606606
* bytes. For @size of power of two bytes, the alignment is also guaranteed
607-
* to be at least to the size.
607+
* to be at least to the size. For other sizes, the alignment is guaranteed to
608+
* be at least the largest power-of-two divisor of @size.
608609
*
609610
* The @flags argument may be one of the GFP flags defined at
610611
* include/linux/gfp_types.h and described at

mm/slab_common.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -617,11 +617,12 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name,
617617
s->size = s->object_size = size;
618618

619619
/*
620-
* For power of two sizes, guarantee natural alignment for kmalloc
621-
* caches, regardless of SL*B debugging options.
620+
* kmalloc caches guarantee alignment of at least the largest
621+
* power-of-two divisor of the size. For power-of-two sizes,
622+
* it is the size itself.
622623
*/
623-
if (is_power_of_2(size))
624-
align = max(align, size);
624+
if (flags & SLAB_KMALLOC)
625+
align = max(align, 1U << (ffs(size) - 1));
625626
s->align = calculate_alignment(flags, align, size);
626627

627628
#ifdef CONFIG_HARDENED_USERCOPY

rust/kernel/alloc/allocator.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,16 @@ pub(crate) unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: F
1818
// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
1919
let layout = new_layout.pad_to_align();
2020

21-
let mut size = layout.size();
22-
23-
if layout.align() > bindings::ARCH_SLAB_MINALIGN {
24-
// The alignment requirement exceeds the slab guarantee, thus try to enlarge the size
25-
// to use the "power-of-two" size/alignment guarantee (see comments in `kmalloc()` for
26-
// more information).
27-
//
28-
// Note that `layout.size()` (after padding) is guaranteed to be a multiple of
29-
// `layout.align()`, so `next_power_of_two` gives enough alignment guarantee.
30-
size = size.next_power_of_two();
31-
}
21+
// Note that `layout.size()` (after padding) is guaranteed to be a multiple of `layout.align()`
22+
// which together with the slab guarantees means the `krealloc` will return a properly aligned
23+
// object (see comments in `kmalloc()` for more information).
24+
let size = layout.size();
3225

3326
// SAFETY:
3427
// - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
3528
// function safety requirement.
36-
// - `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero
37-
// according to the function safety requirement) or a result from `next_power_of_two()`.
29+
// - `size` is greater than 0 since it's from `layout.size()` (which cannot be zero according
30+
// to the function safety requirement)
3831
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags.0) as *mut u8 }
3932
}
4033

0 commit comments

Comments
 (0)