mm/slub: allow to set node and align in k[v]realloc

Reimplement k[v]realloc_node() to be able to set node and alignment should
a user need to do so.  In order to do that while retaining the maximal
backward compatibility, add k[v]realloc_node_align() functions and
redefine the rest of API using these new ones.

While doing that, we also keep the number of _noprof variants to a
minimum, which implies some changes to the existing users of older _noprof
functions, that basically being bcachefs.

With that change we also provide the ability for the Rust part of the
kernel to set node and alignment in its K[v]xxx [re]allocations.

Link: https://lkml.kernel.org/r/20250806124147.1724658-1-vitaly.wool@konsulko.se
Signed-off-by: Vitaly Wool <vitaly.wool@konsulko.se>
Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Danilo Krummrich <dakr@kernel.org>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Jann Horn <jannh@google.com>
Cc: Kent Overstreet <kent.overstreet@linux.dev>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Uladzislau Rezki (Sony) <urezki@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Vitaly Wool 2025-08-06 14:41:47 +02:00 committed by Andrew Morton
parent 4c5d336588
commit 2cd8231796
6 changed files with 74 additions and 34 deletions

View File

@ -21,7 +21,7 @@ int __bch2_darray_resize_noprof(darray_char *d, size_t element_size, size_t new_
return -ENOMEM; return -ENOMEM;
void *data = likely(bytes < INT_MAX) void *data = likely(bytes < INT_MAX)
? kvmalloc_noprof(bytes, gfp) ? kvmalloc_node_align_noprof(bytes, 1, gfp, NUMA_NO_NODE)
: vmalloc_noprof(bytes); : vmalloc_noprof(bytes);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;

View File

@ -61,7 +61,7 @@ static inline void *bch2_kvmalloc_noprof(size_t n, gfp_t flags)
{ {
void *p = unlikely(n >= INT_MAX) void *p = unlikely(n >= INT_MAX)
? vmalloc_noprof(n) ? vmalloc_noprof(n)
: kvmalloc_noprof(n, flags & ~__GFP_ZERO); : kvmalloc_node_align_noprof(n, 1, flags & ~__GFP_ZERO, NUMA_NO_NODE);
if (p && (flags & __GFP_ZERO)) if (p && (flags & __GFP_ZERO))
memset(p, 0, n); memset(p, 0, n);
return p; return p;

View File

@ -67,7 +67,7 @@ static inline int copy_to_bpfptr_offset(bpfptr_t dst, size_t offset,
static inline void *kvmemdup_bpfptr_noprof(bpfptr_t src, size_t len) static inline void *kvmemdup_bpfptr_noprof(bpfptr_t src, size_t len)
{ {
void *p = kvmalloc_noprof(len, GFP_USER | __GFP_NOWARN); void *p = kvmalloc_node_align_noprof(len, 1, GFP_USER | __GFP_NOWARN, NUMA_NO_NODE);
if (!p) if (!p)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);

View File

@ -465,9 +465,13 @@ int kmem_cache_shrink(struct kmem_cache *s);
/* /*
* Common kmalloc functions provided by all allocators * Common kmalloc functions provided by all allocators
*/ */
void * __must_check krealloc_noprof(const void *objp, size_t new_size, void * __must_check krealloc_node_align_noprof(const void *objp, size_t new_size,
gfp_t flags) __realloc_size(2); unsigned long align,
#define krealloc(...) alloc_hooks(krealloc_noprof(__VA_ARGS__)) gfp_t flags, int nid) __realloc_size(2);
#define krealloc_noprof(_o, _s, _f) krealloc_node_align_noprof(_o, _s, 1, _f, NUMA_NO_NODE)
#define krealloc_node_align(...) alloc_hooks(krealloc_node_align_noprof(__VA_ARGS__))
#define krealloc_node(_o, _s, _f, _n) krealloc_node_align(_o, _s, 1, _f, _n)
#define krealloc(...) krealloc_node(__VA_ARGS__, NUMA_NO_NODE)
void kfree(const void *objp); void kfree(const void *objp);
void kfree_sensitive(const void *objp); void kfree_sensitive(const void *objp);
@ -1041,18 +1045,20 @@ static inline __alloc_size(1) void *kzalloc_noprof(size_t size, gfp_t flags)
#define kzalloc(...) alloc_hooks(kzalloc_noprof(__VA_ARGS__)) #define kzalloc(...) alloc_hooks(kzalloc_noprof(__VA_ARGS__))
#define kzalloc_node(_size, _flags, _node) kmalloc_node(_size, (_flags)|__GFP_ZERO, _node) #define kzalloc_node(_size, _flags, _node) kmalloc_node(_size, (_flags)|__GFP_ZERO, _node)
void *__kvmalloc_node_noprof(DECL_BUCKET_PARAMS(size, b), gfp_t flags, int node) __alloc_size(1); void *__kvmalloc_node_noprof(DECL_BUCKET_PARAMS(size, b), unsigned long align,
#define kvmalloc_node_noprof(size, flags, node) \ gfp_t flags, int node) __alloc_size(1);
__kvmalloc_node_noprof(PASS_BUCKET_PARAMS(size, NULL), flags, node) #define kvmalloc_node_align_noprof(_size, _align, _flags, _node) \
#define kvmalloc_node(...) alloc_hooks(kvmalloc_node_noprof(__VA_ARGS__)) __kvmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, NULL), _align, _flags, _node)
#define kvmalloc_node_align(...) \
#define kvmalloc(_size, _flags) kvmalloc_node(_size, _flags, NUMA_NO_NODE) alloc_hooks(kvmalloc_node_align_noprof(__VA_ARGS__))
#define kvmalloc_noprof(_size, _flags) kvmalloc_node_noprof(_size, _flags, NUMA_NO_NODE) #define kvmalloc_node(_s, _f, _n) kvmalloc_node_align(_s, 1, _f, _n)
#define kvmalloc(...) kvmalloc_node(__VA_ARGS__, NUMA_NO_NODE)
#define kvzalloc(_size, _flags) kvmalloc(_size, (_flags)|__GFP_ZERO) #define kvzalloc(_size, _flags) kvmalloc(_size, (_flags)|__GFP_ZERO)
#define kvzalloc_node(_size, _flags, _node) kvmalloc_node(_size, (_flags)|__GFP_ZERO, _node) #define kvzalloc_node(_size, _flags, _node) kvmalloc_node(_size, (_flags)|__GFP_ZERO, _node)
#define kmem_buckets_valloc(_b, _size, _flags) \ #define kmem_buckets_valloc(_b, _size, _flags) \
alloc_hooks(__kvmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, NUMA_NO_NODE)) alloc_hooks(__kvmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), 1, _flags, NUMA_NO_NODE))
static inline __alloc_size(1, 2) void * static inline __alloc_size(1, 2) void *
kvmalloc_array_node_noprof(size_t n, size_t size, gfp_t flags, int node) kvmalloc_array_node_noprof(size_t n, size_t size, gfp_t flags, int node)
@ -1062,7 +1068,7 @@ kvmalloc_array_node_noprof(size_t n, size_t size, gfp_t flags, int node)
if (unlikely(check_mul_overflow(n, size, &bytes))) if (unlikely(check_mul_overflow(n, size, &bytes)))
return NULL; return NULL;
return kvmalloc_node_noprof(bytes, flags, node); return kvmalloc_node_align_noprof(bytes, 1, flags, node);
} }
#define kvmalloc_array_noprof(...) kvmalloc_array_node_noprof(__VA_ARGS__, NUMA_NO_NODE) #define kvmalloc_array_noprof(...) kvmalloc_array_node_noprof(__VA_ARGS__, NUMA_NO_NODE)
@ -1073,9 +1079,12 @@ kvmalloc_array_node_noprof(size_t n, size_t size, gfp_t flags, int node)
#define kvcalloc_node(...) alloc_hooks(kvcalloc_node_noprof(__VA_ARGS__)) #define kvcalloc_node(...) alloc_hooks(kvcalloc_node_noprof(__VA_ARGS__))
#define kvcalloc(...) alloc_hooks(kvcalloc_noprof(__VA_ARGS__)) #define kvcalloc(...) alloc_hooks(kvcalloc_noprof(__VA_ARGS__))
void *kvrealloc_noprof(const void *p, size_t size, gfp_t flags) void *kvrealloc_node_align_noprof(const void *p, size_t size, unsigned long align,
__realloc_size(2); gfp_t flags, int nid) __realloc_size(2);
#define kvrealloc(...) alloc_hooks(kvrealloc_noprof(__VA_ARGS__)) #define kvrealloc_node_align(...) \
alloc_hooks(kvrealloc_node_align_noprof(__VA_ARGS__))
#define kvrealloc_node(_p, _s, _f, _n) kvrealloc_node_align(_p, _s, 1, _f, _n)
#define kvrealloc(...) kvrealloc_node(__VA_ARGS__, NUMA_NO_NODE)
extern void kvfree(const void *addr); extern void kvfree(const void *addr);
DEFINE_FREE(kvfree, void *, if (!IS_ERR_OR_NULL(_T)) kvfree(_T)) DEFINE_FREE(kvfree, void *, if (!IS_ERR_OR_NULL(_T)) kvfree(_T))

View File

@ -184,8 +184,8 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
static struct lock_class_key __key; static struct lock_class_key __key;
tbl = alloc_hooks_tag(ht->alloc_tag, tbl = alloc_hooks_tag(ht->alloc_tag,
kvmalloc_node_noprof(struct_size(tbl, buckets, nbuckets), kvmalloc_node_align_noprof(struct_size(tbl, buckets, nbuckets),
gfp|__GFP_ZERO, NUMA_NO_NODE)); 1, gfp|__GFP_ZERO, NUMA_NO_NODE));
size = nbuckets; size = nbuckets;

View File

@ -4881,7 +4881,7 @@ void kfree(const void *object)
EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kfree);
static __always_inline __realloc_size(2) void * static __always_inline __realloc_size(2) void *
__do_krealloc(const void *p, size_t new_size, gfp_t flags) __do_krealloc(const void *p, size_t new_size, unsigned long align, gfp_t flags, int nid)
{ {
void *ret; void *ret;
size_t ks = 0; size_t ks = 0;
@ -4895,6 +4895,16 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)
if (!kasan_check_byte(p)) if (!kasan_check_byte(p))
return NULL; return NULL;
/*
* If reallocation is not necessary (e. g. the new size is less
* than the current allocated size), the current allocation will be
* preserved unless __GFP_THISNODE is set. In the latter case a new
* allocation on the requested node will be attempted.
*/
if (unlikely(flags & __GFP_THISNODE) && nid != NUMA_NO_NODE &&
nid != page_to_nid(virt_to_page(p)))
goto alloc_new;
if (is_kfence_address(p)) { if (is_kfence_address(p)) {
ks = orig_size = kfence_ksize(p); ks = orig_size = kfence_ksize(p);
} else { } else {
@ -4917,6 +4927,10 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)
if (new_size > ks) if (new_size > ks)
goto alloc_new; goto alloc_new;
/* If the old object doesn't satisfy the new alignment, allocate a new one */
if (!IS_ALIGNED((unsigned long)p, align))
goto alloc_new;
/* Zero out spare memory. */ /* Zero out spare memory. */
if (want_init_on_alloc(flags)) { if (want_init_on_alloc(flags)) {
kasan_disable_current(); kasan_disable_current();
@ -4939,7 +4953,7 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)
return (void *)p; return (void *)p;
alloc_new: alloc_new:
ret = kmalloc_node_track_caller_noprof(new_size, flags, NUMA_NO_NODE, _RET_IP_); ret = kmalloc_node_track_caller_noprof(new_size, flags, nid, _RET_IP_);
if (ret && p) { if (ret && p) {
/* Disable KASAN checks as the object's redzone is accessed. */ /* Disable KASAN checks as the object's redzone is accessed. */
kasan_disable_current(); kasan_disable_current();
@ -4951,14 +4965,19 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)
} }
/** /**
* krealloc - reallocate memory. The contents will remain unchanged. * krealloc_node_align - reallocate memory. The contents will remain unchanged.
* @p: object to reallocate memory for. * @p: object to reallocate memory for.
* @new_size: how many bytes of memory are required. * @new_size: how many bytes of memory are required.
* @align: desired alignment.
* @flags: the type of memory to allocate. * @flags: the type of memory to allocate.
* @nid: NUMA node or NUMA_NO_NODE
* *
* If @p is %NULL, krealloc() behaves exactly like kmalloc(). If @new_size * If @p is %NULL, krealloc() behaves exactly like kmalloc(). If @new_size
* is 0 and @p is not a %NULL pointer, the object pointed to is freed. * is 0 and @p is not a %NULL pointer, the object pointed to is freed.
* *
* Only alignments up to those guaranteed by kmalloc() will be honored. Please see
* Documentation/core-api/memory-allocation.rst for more details.
*
* If __GFP_ZERO logic is requested, callers must ensure that, starting with the * If __GFP_ZERO logic is requested, callers must ensure that, starting with the
* initial memory allocation, every subsequent call to this API for the same * initial memory allocation, every subsequent call to this API for the same
* memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that * memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that
@ -4983,7 +5002,8 @@ __do_krealloc(const void *p, size_t new_size, gfp_t flags)
* *
* Return: pointer to the allocated memory or %NULL in case of error * Return: pointer to the allocated memory or %NULL in case of error
*/ */
void *krealloc_noprof(const void *p, size_t new_size, gfp_t flags) void *krealloc_node_align_noprof(const void *p, size_t new_size, unsigned long align,
gfp_t flags, int nid)
{ {
void *ret; void *ret;
@ -4992,13 +5012,13 @@ void *krealloc_noprof(const void *p, size_t new_size, gfp_t flags)
return ZERO_SIZE_PTR; return ZERO_SIZE_PTR;
} }
ret = __do_krealloc(p, new_size, flags); ret = __do_krealloc(p, new_size, align, flags, nid);
if (ret && kasan_reset_tag(p) != kasan_reset_tag(ret)) if (ret && kasan_reset_tag(p) != kasan_reset_tag(ret))
kfree(p); kfree(p);
return ret; return ret;
} }
EXPORT_SYMBOL(krealloc_noprof); EXPORT_SYMBOL(krealloc_node_align_noprof);
static gfp_t kmalloc_gfp_adjust(gfp_t flags, size_t size) static gfp_t kmalloc_gfp_adjust(gfp_t flags, size_t size)
{ {
@ -5029,9 +5049,13 @@ static gfp_t kmalloc_gfp_adjust(gfp_t flags, size_t size)
* failure, fall back to non-contiguous (vmalloc) allocation. * failure, fall back to non-contiguous (vmalloc) allocation.
* @size: size of the request. * @size: size of the request.
* @b: which set of kmalloc buckets to allocate from. * @b: which set of kmalloc buckets to allocate from.
* @align: desired alignment.
* @flags: gfp mask for the allocation - must be compatible (superset) with GFP_KERNEL. * @flags: gfp mask for the allocation - must be compatible (superset) with GFP_KERNEL.
* @node: numa node to allocate from * @node: numa node to allocate from
* *
* Only alignments up to those guaranteed by kmalloc() will be honored. Please see
* Documentation/core-api/memory-allocation.rst for more details.
*
* Uses kmalloc to get the memory but if the allocation fails then falls back * Uses kmalloc to get the memory but if the allocation fails then falls back
* to the vmalloc allocator. Use kvfree for freeing the memory. * to the vmalloc allocator. Use kvfree for freeing the memory.
* *
@ -5041,7 +5065,8 @@ static gfp_t kmalloc_gfp_adjust(gfp_t flags, size_t size)
* *
* Return: pointer to the allocated memory of %NULL in case of failure * Return: pointer to the allocated memory of %NULL in case of failure
*/ */
void *__kvmalloc_node_noprof(DECL_BUCKET_PARAMS(size, b), gfp_t flags, int node) void *__kvmalloc_node_noprof(DECL_BUCKET_PARAMS(size, b), unsigned long align,
gfp_t flags, int node)
{ {
void *ret; void *ret;
@ -5071,7 +5096,7 @@ void *__kvmalloc_node_noprof(DECL_BUCKET_PARAMS(size, b), gfp_t flags, int node)
* about the resulting pointer, and cannot play * about the resulting pointer, and cannot play
* protection games. * protection games.
*/ */
return __vmalloc_node_range_noprof(size, 1, VMALLOC_START, VMALLOC_END, return __vmalloc_node_range_noprof(size, align, VMALLOC_START, VMALLOC_END,
flags, PAGE_KERNEL, VM_ALLOW_HUGE_VMAP, flags, PAGE_KERNEL, VM_ALLOW_HUGE_VMAP,
node, __builtin_return_address(0)); node, __builtin_return_address(0));
} }
@ -5115,14 +5140,19 @@ void kvfree_sensitive(const void *addr, size_t len)
EXPORT_SYMBOL(kvfree_sensitive); EXPORT_SYMBOL(kvfree_sensitive);
/** /**
* kvrealloc - reallocate memory; contents remain unchanged * kvrealloc_node_align - reallocate memory; contents remain unchanged
* @p: object to reallocate memory for * @p: object to reallocate memory for
* @size: the size to reallocate * @size: the size to reallocate
* @align: desired alignment
* @flags: the flags for the page level allocator * @flags: the flags for the page level allocator
* @nid: NUMA node id
* *
* If @p is %NULL, kvrealloc() behaves exactly like kvmalloc(). If @size is 0 * If @p is %NULL, kvrealloc() behaves exactly like kvmalloc(). If @size is 0
* and @p is not a %NULL pointer, the object pointed to is freed. * and @p is not a %NULL pointer, the object pointed to is freed.
* *
* Only alignments up to those guaranteed by kmalloc() will be honored. Please see
* Documentation/core-api/memory-allocation.rst for more details.
*
* If __GFP_ZERO logic is requested, callers must ensure that, starting with the * If __GFP_ZERO logic is requested, callers must ensure that, starting with the
* initial memory allocation, every subsequent call to this API for the same * initial memory allocation, every subsequent call to this API for the same
* memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that * memory allocation is flagged with __GFP_ZERO. Otherwise, it is possible that
@ -5136,17 +5166,18 @@ EXPORT_SYMBOL(kvfree_sensitive);
* *
* Return: pointer to the allocated memory or %NULL in case of error * Return: pointer to the allocated memory or %NULL in case of error
*/ */
void *kvrealloc_noprof(const void *p, size_t size, gfp_t flags) void *kvrealloc_node_align_noprof(const void *p, size_t size, unsigned long align,
gfp_t flags, int nid)
{ {
void *n; void *n;
if (is_vmalloc_addr(p)) if (is_vmalloc_addr(p))
return vrealloc_noprof(p, size, flags); return vrealloc_node_align_noprof(p, size, align, flags, nid);
n = krealloc_noprof(p, size, kmalloc_gfp_adjust(flags, size)); n = krealloc_node_align_noprof(p, size, align, kmalloc_gfp_adjust(flags, size), nid);
if (!n) { if (!n) {
/* We failed to krealloc(), fall back to kvmalloc(). */ /* We failed to krealloc(), fall back to kvmalloc(). */
n = kvmalloc_noprof(size, flags); n = kvmalloc_node_align_noprof(size, align, flags, nid);
if (!n) if (!n)
return NULL; return NULL;
@ -5162,7 +5193,7 @@ void *kvrealloc_noprof(const void *p, size_t size, gfp_t flags)
return n; return n;
} }
EXPORT_SYMBOL(kvrealloc_noprof); EXPORT_SYMBOL(kvrealloc_node_align_noprof);
struct detached_freelist { struct detached_freelist {
struct slab *slab; struct slab *slab;