* SPDX-License-Identifier: MulanPSL-2.0
*/
#include "utils/falcon_shmem_allocator.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
int FalconShmemAllocatorInit(FalconShmemAllocator *allocator, char *shmem, uint64_t size)
{
uint32_t pageCount = (size - sizeof(PaddedAtomic64) * (1 + FALCON_SHMEM_ALLOCATOR_FREE_LIST_COUNT)) /
(sizeof(PaddedAtomic64) + FALCON_SHMEM_ALLOCATOR_PAGE_SIZE);
if (pageCount == 0)
return -1;
allocator->shmem = shmem;
allocator->size = size;
allocator->pageCount = pageCount;
allocator->signatureCounter = (PaddedAtomic64 *)shmem;
allocator->freeListHint = allocator->signatureCounter + 1;
allocator->pageCntlArray = allocator->freeListHint + FALCON_SHMEM_ALLOCATOR_FREE_LIST_COUNT;
allocator->allocatableSpaceBase = (char *)(allocator->pageCntlArray + pageCount);
return 0;
}
int64_t FalconShmemAllocatorGetUniqueSignature(FalconShmemAllocator *allocator)
{
return (int64_t)atomic_fetch_add_explicit(&allocator->signatureCounter->data, 1, memory_order_relaxed) + 1;
}
static inline uint64_t GetNextPowerOfTwo(uint64_t num)
{
if (num == 0 || (num & ((uint64_t)1 << 63)) != 0) {
return 0;
}
if (num == 1) {
return 1;
}
return ((uint64_t)1 << (64 - __builtin_clzll(num - 1)));
}
static uint64_t LevelBlockMask[FALCON_SHMEM_ALLOCATOR_FREE_LIST_COUNT] = {0x0000000000000001,
0x0000000100000001,
0x0001000100010001,
0x0101010101010101,
0x1111111111111111,
0x5555555555555555,
0xFFFFFFFFFFFFFFFF};
static uint64_t LevelBlockOccupyBitMap[FALCON_SHMEM_ALLOCATOR_FREE_LIST_COUNT] = {0xFFFFFFFFFFFFFFFF,
0x00000000FFFFFFFF,
0x000000000000FFFF,
0x00000000000000FF,
0x000000000000000F,
0x0000000000000003,
0x0000000000000001};
uint64_t FalconShmemAllocatorMalloc(FalconShmemAllocator *allocator, uint64_t size)
{
if (size > FALCON_SHMEM_ALLOCATOR_MAX_SUPPORT_ALLOC_SIZE - sizeof(MemoryHdr)) {
printf("asked size exceed limit, size: %" PRIu64 ".", size);
fflush(stdout);
return 0;
}
uint64_t requiredSize = size + sizeof(MemoryHdr);
if (requiredSize < FALCON_SHMEM_ALLOCATOR_MIN_SUPPORT_ALLOC_SIZE)
requiredSize = FALCON_SHMEM_ALLOCATOR_MIN_SUPPORT_ALLOC_SIZE;
else
requiredSize = GetNextPowerOfTwo(requiredSize);
int level = __builtin_ctzll(FALCON_SHMEM_ALLOCATOR_PAGE_SIZE) - __builtin_ctzll(requiredSize);
for (int scan = 0; scan < 2; scan++) {
uint64_t start;
if (scan == 0) {
start = atomic_load_explicit(&allocator->freeListHint[level].data, memory_order_relaxed);
} else {
start = 0;
}
for (uint32_t pageNo = start; pageNo < allocator->pageCount; ++pageNo) {
uint64_t bitmap = atomic_load_explicit(&allocator->pageCntlArray[pageNo].data, memory_order_relaxed);
uint64_t allocatedShift = -1;
bool succeed = false;
bool pageIsFull = false;
while (true) {
uint64_t expected;
uint64_t desired;
if (level == 0) {
if (bitmap != 0)
break;
expected = 0;
desired = ~(uint64_t)0;
allocatedShift = FALCON_SHMEM_ALLOCATOR_PAGE_SIZE * pageNo;
} else {
expected = bitmap;
int shift = 1;
for (int j = FALCON_SHMEM_ALLOCATOR_FREE_LIST_COUNT - 1; j > level; --j) {
bitmap = ((bitmap >> shift) | bitmap) & LevelBlockMask[j - 1];
shift <<= 1;
}
if (bitmap == LevelBlockMask[level])
break;
int firstEmptyBlockInLevel = __builtin_ctzll(~bitmap & LevelBlockMask[level]);
allocatedShift = FALCON_SHMEM_ALLOCATOR_PAGE_SIZE * pageNo +
FALCON_SHMEM_ALLOCATOR_MIN_BLOCK_SIZE * firstEmptyBlockInLevel;
if (allocatedShift + requiredSize == FALCON_SHMEM_ALLOCATOR_PAGE_SIZE)
pageIsFull = true;
desired = expected | (LevelBlockOccupyBitMap[level] << firstEmptyBlockInLevel);
}
if (atomic_compare_exchange_strong_explicit(&allocator->pageCntlArray[pageNo].data,
&expected,
desired,
memory_order_relaxed,
memory_order_relaxed)) {
succeed = true;
if (scan == 0) {
if (pageIsFull || pageNo != start) {
atomic_compare_exchange_strong_explicit(&allocator->freeListHint[level].data,
&start,
pageIsFull ? pageNo + 1 : pageNo,
memory_order_relaxed,
memory_order_relaxed);
}
} else {
if (!pageIsFull) {
uint64_t freeHint =
atomic_load_explicit(&allocator->freeListHint[level].data, memory_order_relaxed);
while (true) {
if (freeHint <= pageNo)
break;
if (atomic_compare_exchange_weak_explicit(&allocator->freeListHint[level].data,
&freeHint,
pageNo,
memory_order_relaxed,
memory_order_relaxed))
break;
}
}
}
break;
}
bitmap = expected;
}
if (succeed) {
if (allocatedShift == -1) {
printf("unexpected situation in FalconShmemAllocatorMalloc.");
fflush(stdout);
return 0;
}
MemoryHdr *hdr = (MemoryHdr *)FALCON_SHMEM_ALLOCATOR_GET_POINTER(allocator, allocatedShift);
hdr->size = size;
hdr->capacity = requiredSize;
hdr->signature = 0;
return allocatedShift + sizeof(MemoryHdr);
}
}
}
printf("FalconShmemAllocatorMalloc: Cannot find a segment.");
fflush(stdout);
return 0;
}
void FalconShmemAllocatorFree(FalconShmemAllocator *allocator, uint64_t shift)
{
shift -= sizeof(MemoryHdr);
MemoryHdr *hdr = (MemoryHdr *)FALCON_SHMEM_ALLOCATOR_GET_POINTER(allocator, shift);
uint64_t capacity = hdr->capacity;
if (capacity < FALCON_SHMEM_ALLOCATOR_MIN_SUPPORT_ALLOC_SIZE ||
capacity > FALCON_SHMEM_ALLOCATOR_MAX_SUPPORT_ALLOC_SIZE)
return;
int level = __builtin_ctzll(FALCON_SHMEM_ALLOCATOR_MAX_SUPPORT_ALLOC_SIZE) - __builtin_ctzll(capacity);
if (capacity != (FALCON_SHMEM_ALLOCATOR_MAX_SUPPORT_ALLOC_SIZE >> level))
return;
uint64_t pageNo = shift / FALCON_SHMEM_ALLOCATOR_PAGE_SIZE;
uint32_t blockNo = shift / FALCON_SHMEM_ALLOCATOR_MIN_BLOCK_SIZE - pageNo * FALCON_SHMEM_ALLOCATOR_STATE_BIT_COUNT;
uint64_t occupyBitmap = LevelBlockOccupyBitMap[level] << blockNo;
atomic_fetch_and_explicit(&allocator->pageCntlArray[pageNo].data, ~occupyBitmap, memory_order_relaxed);
uint64_t freeHint = atomic_load_explicit(&allocator->freeListHint[level].data, memory_order_relaxed);
while (true) {
if (freeHint <= pageNo)
break;
if (atomic_compare_exchange_weak_explicit(&allocator->freeListHint[level].data,
&freeHint,
pageNo,
memory_order_relaxed,
memory_order_relaxed))
break;
}
}