blob: 528c69a1f38e0c175b3d8002dffd3159c9c4b776 [file]
// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#pragma once
enum buddy_consts {
/*
* Minimum allocation is 1 << BUDDY_MIN_ALLOC_SHIFT.
* Larger sizes increase internal fragmentation, but smaller
* sizes increase the space overhead of the block metadata.
*/
BUDDY_MIN_ALLOC_SHIFT = 4,
BUDDY_MIN_ALLOC_BYTES = 1 << BUDDY_MIN_ALLOC_SHIFT,
/*
* How many orders the buddy allocator can serve. Minimum block
* size is 1 << BUDDY_MIN_ALLOC_SHIFT, maximum block size is
* 1 << (BUDDY_MIN_ALLOC_SHIFT + BUDDY_CHUNK_NUM_ORDERS - 1):
* Each block has size 1 << BUDDY_MIN_ALLOC_SHIFT, and the
* allocation orders are in [0, BUDDY_CHUNK_NUM_ORDERS).
* We keep two blocks of the maximum size to retain the
* property in the code that all blocks have a buddy.
* Higher values increase the maximum allocation size,
* but also the size of the metadata for each block.
*/
BUDDY_CHUNK_NUM_ORDERS = 1 << 4,
BUDDY_CHUNK_BYTES = BUDDY_MIN_ALLOC_BYTES << (BUDDY_CHUNK_NUM_ORDERS),
/* Offset of the buddy header within a free block, see buddy.bpf.c for details */
BUDDY_HEADER_OFF = 8,
/* The maximum number of blocks a chunk may have to track. */
BUDDY_CHUNK_ITEMS = 1 << (BUDDY_CHUNK_NUM_ORDERS),
BUDDY_CHUNK_OFFSET_MASK = BUDDY_CHUNK_BYTES - 1,
/*
* Alignment for chunk allocations based on bpf_arena_alloc_pages.
* The arena allocation kfunc does not have an alignment argument,
* but that is required for all block calculations in the chunk to
* work.
*/
BUDDY_VADDR_OFFSET = BUDDY_CHUNK_BYTES,
/* Total arena virtual address space the allocator can consume. */
BUDDY_VADDR_SIZE = BUDDY_CHUNK_BYTES << 10
};
struct buddy_header {
u32 prev_index; /* "Pointer" to the previous available allocation of the same size. */
u32 next_index; /* Same for the next allocation. */
};
/*
* We bring memory into the allocator 1 MiB at a time.
*/
struct buddy_chunk {
/* The order of the current allocation for a item. 4 bits per order. */
u8 orders[BUDDY_CHUNK_ITEMS / 2];
/*
* Bit to denote whether chunk is allocated. Size of the allocated/free
* chunk found from the orders array.
*/
u8 allocated[BUDDY_CHUNK_ITEMS / 8];
/* Freelists for O(1) allocation. */
u64 freelists[BUDDY_CHUNK_NUM_ORDERS];
struct buddy_chunk __arena *next;
};
struct buddy {
struct buddy_chunk __arena *first_chunk; /* Pointer to the chunk linked list. */
arena_spinlock_t lock; /* Allocator lock */
u64 vaddr; /* Allocation into reserved vaddr */
};
#ifdef __BPF__
int buddy_init(struct buddy __arena *buddy);
int buddy_destroy(struct buddy __arena *buddy);
int buddy_free(struct buddy __arena *buddy, void __arena *free);
void __arena *buddy_alloc(struct buddy __arena *buddy, size_t size);
#endif /* __BPF__ */