Skip to content

Commit 2ad9ef9

Browse files
Danilo Krummrichfbq
authored andcommitted
mm: kvmalloc: align kvrealloc() with krealloc()
Besides the obvious (and desired) difference between krealloc() and kvrealloc(), there is some inconsistency in their function signatures and behavior: - krealloc() frees the memory when the requested size is zero, whereas kvrealloc() simply returns a pointer to the existing allocation. - krealloc() behaves like kmalloc() if a NULL pointer is passed, whereas kvrealloc() does not accept a NULL pointer at all and, if passed, would fault instead. - krealloc() is self-contained, whereas kvrealloc() relies on the caller to provide the size of the previous allocation. Inconsistent behavior throughout allocation APIs is error prone, hence make kvrealloc() behave like krealloc(), which seems superior in all mentioned aspects. Besides that, implementing kvrealloc() by making use of krealloc() and vrealloc() provides oppertunities to grow (and shrink) allocations more efficiently. For instance, vrealloc() can be optimized to allocate and map additional pages to grow the allocation or unmap and free unused pages to shrink the allocation. Signed-off-by: Danilo Krummrich <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Acked-by: Michal Hocko <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 4d31865 commit 2ad9ef9

File tree

8 files changed

+66
-44
lines changed

8 files changed

+66
-44
lines changed

arch/arm64/kvm/nested.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
6262
*/
6363
num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
6464
tmp = kvrealloc(kvm->arch.nested_mmus,
65-
size_mul(sizeof(*kvm->arch.nested_mmus), kvm->arch.nested_mmus_size),
6665
size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus),
6766
GFP_KERNEL_ACCOUNT | __GFP_ZERO);
6867
if (!tmp)

arch/powerpc/platforms/pseries/papr-vpd.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,7 @@ static int vpd_blob_extend(struct vpd_blob *blob, const char *data, size_t len)
156156
const char *old_ptr = blob->data;
157157
char *new_ptr;
158158

159-
new_ptr = old_ptr ?
160-
kvrealloc(old_ptr, old_len, new_len, GFP_KERNEL_ACCOUNT) :
161-
kvmalloc(len, GFP_KERNEL_ACCOUNT);
162-
159+
new_ptr = kvrealloc(old_ptr, new_len, GFP_KERNEL_ACCOUNT);
163160
if (!new_ptr)
164161
return -ENOMEM;
165162

drivers/gpu/drm/drm_exec.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ static int drm_exec_obj_locked(struct drm_exec *exec,
145145
size_t size = exec->max_objects * sizeof(void *);
146146
void *tmp;
147147

148-
tmp = kvrealloc(exec->objects, size, size + PAGE_SIZE,
149-
GFP_KERNEL);
148+
tmp = kvrealloc(exec->objects, size + PAGE_SIZE, GFP_KERNEL);
150149
if (!tmp)
151150
return -ENOMEM;
152151

fs/xfs/xfs_log_recover.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2128,7 +2128,7 @@ xlog_recover_add_to_cont_trans(
21282128
old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
21292129
old_len = item->ri_buf[item->ri_cnt-1].i_len;
21302130

2131-
ptr = kvrealloc(old_ptr, old_len, len + old_len, GFP_KERNEL);
2131+
ptr = kvrealloc(old_ptr, len + old_len, GFP_KERNEL);
21322132
if (!ptr)
21332133
return -ENOMEM;
21342134
memcpy(&ptr[old_len], dp, len);

include/linux/slab.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,8 +841,8 @@ kvmalloc_array_node_noprof(size_t n, size_t size, gfp_t flags, int node)
841841
#define kvcalloc_node(...) alloc_hooks(kvcalloc_node_noprof(__VA_ARGS__))
842842
#define kvcalloc(...) alloc_hooks(kvcalloc_noprof(__VA_ARGS__))
843843

844-
extern void *kvrealloc_noprof(const void *p, size_t oldsize, size_t newsize, gfp_t flags)
845-
__realloc_size(3);
844+
void *kvrealloc_noprof(const void *p, size_t size, gfp_t flags)
845+
__realloc_size(2);
846846
#define kvrealloc(...) alloc_hooks(kvrealloc_noprof(__VA_ARGS__))
847847

848848
extern void kvfree(const void *addr);

kernel/resource.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,7 @@ int walk_system_ram_res_rev(u64 start, u64 end, void *arg,
450450
/* re-alloc */
451451
struct resource *rams_new;
452452

453-
rams_new = kvrealloc(rams, rams_size * sizeof(struct resource),
454-
(rams_size + 16) * sizeof(struct resource),
453+
rams_new = kvrealloc(rams, (rams_size + 16) * sizeof(struct resource),
455454
GFP_KERNEL);
456455
if (!rams_new)
457456
goto out;

lib/fortify_kunit.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,7 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(vmalloc)
306306
orig = kvmalloc(prev_size, gfp); \
307307
KUNIT_EXPECT_TRUE(test, orig != NULL); \
308308
checker(((expected_pages) * PAGE_SIZE) * 2, \
309-
kvrealloc(orig, prev_size, \
310-
((alloc_pages) * PAGE_SIZE) * 2, gfp), \
309+
kvrealloc(orig, ((alloc_pages) * PAGE_SIZE) * 2, gfp), \
311310
kvfree(p)); \
312311
} while (0)
313312
DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)

mm/util.c

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,28 @@ unsigned long vm_mmap(struct file *file, unsigned long addr,
608608
}
609609
EXPORT_SYMBOL(vm_mmap);
610610

611+
static gfp_t kmalloc_gfp_adjust(gfp_t flags, size_t size)
612+
{
613+
/*
614+
* We want to attempt a large physically contiguous block first because
615+
* it is less likely to fragment multiple larger blocks and therefore
616+
* contribute to a long term fragmentation less than vmalloc fallback.
617+
* However make sure that larger requests are not too disruptive - no
618+
* OOM killer and no allocation failure warnings as we have a fallback.
619+
*/
620+
if (size > PAGE_SIZE) {
621+
flags |= __GFP_NOWARN;
622+
623+
if (!(flags & __GFP_RETRY_MAYFAIL))
624+
flags |= __GFP_NORETRY;
625+
626+
/* nofail semantic is implemented by the vmalloc fallback */
627+
flags &= ~__GFP_NOFAIL;
628+
}
629+
630+
return flags;
631+
}
632+
611633
/**
612634
* __kvmalloc_node - attempt to allocate physically contiguous memory, but upon
613635
* failure, fall back to non-contiguous (vmalloc) allocation.
@@ -627,32 +649,15 @@ EXPORT_SYMBOL(vm_mmap);
627649
*/
628650
void *__kvmalloc_node_noprof(DECL_BUCKET_PARAMS(size, b), gfp_t flags, int node)
629651
{
630-
gfp_t kmalloc_flags = flags;
631652
void *ret;
632653

633-
/*
634-
* We want to attempt a large physically contiguous block first because
635-
* it is less likely to fragment multiple larger blocks and therefore
636-
* contribute to a long term fragmentation less than vmalloc fallback.
637-
* However make sure that larger requests are not too disruptive - no
638-
* OOM killer and no allocation failure warnings as we have a fallback.
639-
*/
640-
if (size > PAGE_SIZE) {
641-
kmalloc_flags |= __GFP_NOWARN;
642-
643-
if (!(kmalloc_flags & __GFP_RETRY_MAYFAIL))
644-
kmalloc_flags |= __GFP_NORETRY;
645-
646-
/* nofail semantic is implemented by the vmalloc fallback */
647-
kmalloc_flags &= ~__GFP_NOFAIL;
648-
}
649-
650-
ret = __kmalloc_node_noprof(PASS_BUCKET_PARAMS(size, b), kmalloc_flags, node);
651-
652654
/*
653655
* It doesn't really make sense to fallback to vmalloc for sub page
654656
* requests
655657
*/
658+
ret = __kmalloc_node_noprof(PASS_BUCKET_PARAMS(size, b),
659+
kmalloc_gfp_adjust(flags, size),
660+
node);
656661
if (ret || size <= PAGE_SIZE)
657662
return ret;
658663

@@ -715,18 +720,42 @@ void kvfree_sensitive(const void *addr, size_t len)
715720
}
716721
EXPORT_SYMBOL(kvfree_sensitive);
717722

718-
void *kvrealloc_noprof(const void *p, size_t oldsize, size_t newsize, gfp_t flags)
723+
/**
724+
* kvrealloc - reallocate memory; contents remain unchanged
725+
* @p: object to reallocate memory for
726+
* @size: the size to reallocate
727+
* @flags: the flags for the page level allocator
728+
*
729+
* The contents of the object pointed to are preserved up to the lesser of the
730+
* new and old size (__GFP_ZERO flag is effectively ignored).
731+
*
732+
* If @p is %NULL, kvrealloc() behaves exactly like kvmalloc(). If @size is 0
733+
* and @p is not a %NULL pointer, the object pointed to is freed.
734+
*
735+
* Return: pointer to the allocated memory or %NULL in case of error
736+
*/
737+
void *kvrealloc_noprof(const void *p, size_t size, gfp_t flags)
719738
{
720-
void *newp;
739+
void *n;
740+
741+
if (is_vmalloc_addr(p))
742+
return vrealloc_noprof(p, size, flags);
743+
744+
n = krealloc_noprof(p, size, kmalloc_gfp_adjust(flags, size));
745+
if (!n) {
746+
/* We failed to krealloc(), fall back to kvmalloc(). */
747+
n = kvmalloc_noprof(size, flags);
748+
if (!n)
749+
return NULL;
750+
751+
if (p) {
752+
/* We already know that `p` is not a vmalloc address. */
753+
memcpy(n, p, ksize(p));
754+
kfree(p);
755+
}
756+
}
721757

722-
if (oldsize >= newsize)
723-
return (void *)p;
724-
newp = kvmalloc_noprof(newsize, flags);
725-
if (!newp)
726-
return NULL;
727-
memcpy(newp, p, oldsize);
728-
kvfree(p);
729-
return newp;
758+
return n;
730759
}
731760
EXPORT_SYMBOL(kvrealloc_noprof);
732761

0 commit comments

Comments
 (0)