Skip to content

Commit 11021cd

Browse files
Nicolas Pitrekartben
Nicolas Pitre
authored andcommitted
kernel: sys_heap: decouple realloc from aligned_realloc
When sys_heap_realloc() is expressed in terms of sys_heap_aligned_realloc() it invokes a longer aligned allocation code path with an extra runtime overhead even though no alignment is necessary. Let's reference and invoke the aligned allocation code path only when an actual aligned allocation is requested. This opens the possibility for the linker to garbage-collect the aligning code otherwise. Improve realloc documentation while at it. Signed-off-by: Nicolas Pitre <[email protected]>
1 parent 457fa60 commit 11021cd

File tree

2 files changed

+82
-34
lines changed

2 files changed

+82
-34
lines changed

include/zephyr/sys/sys_heap.h

+16-3
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,29 @@ void sys_heap_free(struct sys_heap *heap, void *mem);
179179
*
180180
* @param heap Heap from which to allocate
181181
* @param ptr Original pointer returned from a previous allocation
182+
* @param bytes Number of bytes requested for the new block
183+
* @return Pointer to memory the caller can now use, or NULL
184+
*/
185+
void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes);
186+
187+
/** @brief Expand the size of an existing allocation
188+
*
189+
* Behaves in all ways like sys_heap_realloc(), except that the returned
190+
* memory (if available) will have a starting address in memory which
191+
* is a multiple of the specified power-of-two alignment value in
192+
* bytes. In-place expansion will be attempted only if the provided memory
193+
* pointer conforms to the specified alignment value otherwise the data will be
194+
* moved to a new memory block.
195+
*
196+
* @param heap Heap from which to allocate
197+
* @param ptr Original pointer returned from a previous allocation
182198
* @param align Alignment in bytes, must be a power of two
183199
* @param bytes Number of bytes requested for the new block
184200
* @return Pointer to memory the caller can now use, or NULL
185201
*/
186202
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
187203
size_t align, size_t bytes);
188204

189-
#define sys_heap_realloc(heap, ptr, bytes) \
190-
sys_heap_aligned_realloc(heap, ptr, 0, bytes)
191-
192205
/** @brief Return allocated memory size
193206
*
194207
* Returns the size, in bytes, of a block returned from a successful

lib/heap/heap.c

+66-31
Original file line numberDiff line numberDiff line change
@@ -383,37 +383,24 @@ void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes)
383383
return mem;
384384
}
385385

386-
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
387-
size_t align, size_t bytes)
386+
static bool inplace_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
388387
{
389388
struct z_heap *h = heap->heap;
390389

391-
/* special realloc semantics */
392-
if (ptr == NULL) {
393-
return sys_heap_aligned_alloc(heap, align, bytes);
394-
}
395-
if (bytes == 0) {
396-
sys_heap_free(heap, ptr);
397-
return NULL;
398-
}
399-
400-
__ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
401-
402390
if (size_too_big(h, bytes)) {
403-
return NULL;
391+
return false;
404392
}
405393

406394
chunkid_t c = mem_to_chunkid(h, ptr);
407-
chunkid_t rc = right_chunk(h, c);
408395
size_t align_gap = (uint8_t *)ptr - (uint8_t *)chunk_mem(h, c);
409396
chunksz_t chunks_need = bytes_to_chunksz(h, bytes + align_gap);
410397

411-
if (align && ((uintptr_t)ptr & (align - 1))) {
412-
/* ptr is not sufficiently aligned */
413-
} else if (chunk_size(h, c) == chunks_need) {
398+
if (chunk_size(h, c) == chunks_need) {
414399
/* We're good already */
415-
return ptr;
416-
} else if (chunk_size(h, c) > chunks_need) {
400+
return true;
401+
}
402+
403+
if (chunk_size(h, c) > chunks_need) {
417404
/* Shrink in place, split off and free unused suffix */
418405
#ifdef CONFIG_SYS_HEAP_LISTENER
419406
size_t bytes_freed = chunksz_to_bytes(h, chunk_size(h, c));
@@ -435,9 +422,13 @@ void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
435422
bytes_freed);
436423
#endif
437424

438-
return ptr;
439-
} else if (!chunk_used(h, rc) &&
440-
(chunk_size(h, c) + chunk_size(h, rc) >= chunks_need)) {
425+
return true;
426+
}
427+
428+
chunkid_t rc = right_chunk(h, c);
429+
430+
if (!chunk_used(h, rc) &&
431+
(chunk_size(h, c) + chunk_size(h, rc) >= chunks_need)) {
441432
/* Expand: split the right chunk and append */
442433
chunksz_t split_size = chunks_need - chunk_size(h, c);
443434

@@ -466,22 +457,66 @@ void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
466457
bytes_freed);
467458
#endif
468459

460+
return true;
461+
}
462+
463+
return false;
464+
}
465+
466+
void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
467+
{
468+
/* special realloc semantics */
469+
if (ptr == NULL) {
470+
return sys_heap_alloc(heap, bytes);
471+
}
472+
if (bytes == 0) {
473+
sys_heap_free(heap, ptr);
474+
return NULL;
475+
}
476+
477+
if (inplace_realloc(heap, ptr, bytes)) {
478+
return ptr;
479+
}
480+
481+
/* In-place realloc was not possible: fallback to allocate and copy. */
482+
void *ptr2 = sys_heap_alloc(heap, bytes);
483+
484+
if (ptr2 != NULL) {
485+
size_t prev_size = sys_heap_usable_size(heap, ptr);
486+
487+
memcpy(ptr2, ptr, MIN(prev_size, bytes));
488+
sys_heap_free(heap, ptr);
489+
}
490+
return ptr2;
491+
}
492+
493+
void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
494+
size_t align, size_t bytes)
495+
{
496+
/* special realloc semantics */
497+
if (ptr == NULL) {
498+
return sys_heap_aligned_alloc(heap, align, bytes);
499+
}
500+
if (bytes == 0) {
501+
sys_heap_free(heap, ptr);
502+
return NULL;
503+
}
504+
505+
__ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
506+
507+
if ((align == 0 || ((uintptr_t)ptr & (align - 1)) == 0) &&
508+
inplace_realloc(heap, ptr, bytes)) {
469509
return ptr;
470-
} else {
471-
;
472510
}
473511

474512
/*
475-
* Fallback: allocate and copy
476-
*
477-
* Note for heap listener notification:
478-
* The calls to allocation and free functions generate
479-
* notification already, so there is no need to those here.
513+
* Either ptr is not sufficiently aligned for in-place realloc or
514+
* in-place realloc was not possible: fallback to allocate and copy.
480515
*/
481516
void *ptr2 = sys_heap_aligned_alloc(heap, align, bytes);
482517

483518
if (ptr2 != NULL) {
484-
size_t prev_size = chunksz_to_bytes(h, chunk_size(h, c)) - align_gap;
519+
size_t prev_size = sys_heap_usable_size(heap, ptr);
485520

486521
memcpy(ptr2, ptr, MIN(prev_size, bytes));
487522
sys_heap_free(heap, ptr);

0 commit comments

Comments
 (0)