diff --git a/Kernel/Include/Memory/NBBS.h b/Kernel/Include/Memory/NBBS.h index 44baecf..f2fdaab 100644 --- a/Kernel/Include/Memory/NBBS.h +++ b/Kernel/Include/Memory/NBBS.h @@ -32,10 +32,21 @@ #define EXP2(n) (0x1ULL << (n)) #define LOG2_LOWER(n) (64ULL - __builtin_clzll(n) - 1ULL) // 64 bit -#define CAS(addr, cmp, val) __atomic_compare_exchange_n(addr, cmp, val, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define FAD(ptr, val) __atomic_add_fetch(ptr, val, __ATOMIC_SEQ_CST) +#define BCAS(ptr, expected, desired) __atomic_compare_exchange_n(ptr, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define VCAS(ptr, expected, desired) __atomic_compare_exchange_n(ptr, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? (*expected) : 0 #define LEVEL(n) LOG2_LOWER(n) +#define TRY_AGAIN_BEGIN(release_count) \ + do { \ + uint32_t ts = release_count; + +#define TRY_AGAIN_END(release_count) \ + } while (ts != release_count); + + /* * Public APIs * @@ -53,7 +64,8 @@ void nb_free(void *addr); */ uint32_t try_alloc(uint32_t node); -void freenode(uint32_t node, uint32_t level); +void freenode(uint32_t node, uint32_t upper_bound); +void free_unmark(uint32_t node, uint32_t upper_bound); /* @@ -62,6 +74,11 @@ void freenode(uint32_t node, uint32_t level); * TODO:? Explain. */ +static inline uint8_t set_coal(uint8_t val, uint32_t child) +{ + return ((uint8_t) (val | (COAL_LEFT >> (child % 2)))); +} + static inline uint8_t clean_coal(uint8_t val, uint32_t child) { return ((uint8_t) (val & !(COAL_LEFT >> (child % 2)))); @@ -74,7 +91,7 @@ static inline uint8_t mark(uint8_t val, uint32_t child) static inline uint8_t unmark(uint8_t val, uint32_t child) { - return ((uint8_t) (val & !((COAL_LEFT | OCC_LEFT) >> (child % 2)))); + return ((uint8_t) (val & !((OCC_LEFT | COAL_LEFT) >> (child % 2)))); } static inline uint8_t is_coal(uint8_t val, uint32_t child) diff --git a/Kernel/Main.c b/Kernel/Main.c index 14abd91..584c601 100644 --- a/Kernel/Main.c +++ b/Kernel/Main.c @@ -89,18 +89,23 @@ void kmain(boot_sysinfo* boot_params) klog("[kmain] nb_alloc\n"); - for (int i = 0; i < 10; i++) { - uint64_t *bruh = nb_alloc(4096); + uint64_t *bruh = nb_alloc(4096); - if (bruh) { - klog("[kmain] nb_alloc ok (0x%p)\n", bruh); - } else { - klog("[kmain] nb_alloc fail\n"); - } - - ksleep(1000); + if (bruh) { + klog("[kmain] nb_alloc ok (0x%p)\n", bruh); + } else { + klog("[kmain] nb_alloc fail\n"); } + nb_free(bruh); + + bruh = nb_alloc(4096); + + if (bruh) { + klog("[kmain] nb_alloc ok (0x%p)\n", bruh); + } else { + klog("[kmain] nb_alloc fail\n"); + } /* 2. Init PMM */ // klog("[kmain] Initializing physical memory manager...\n"); diff --git a/Kernel/Memory/NBBS.c b/Kernel/Memory/NBBS.c index 435b75b..664e4e5 100644 --- a/Kernel/Memory/NBBS.c +++ b/Kernel/Memory/NBBS.c @@ -15,16 +15,17 @@ static volatile uint8_t *tree = 0; static volatile uint32_t *index = 0; -static volatile uint64_t base_address = 0; -static volatile uint32_t depth = 0; static volatile uint64_t tree_size = 0; /* bytes */ static volatile uint64_t index_size = 0; /* bytes */ +static volatile uint64_t base_address = 0; static volatile uint64_t total_memory = 0; -static volatile uint64_t max_level = 0; +static volatile uint32_t depth = 0; +static volatile uint64_t base_level = 0; static volatile uint64_t min_size = 0; static volatile uint64_t max_size = 0; +static volatile uint32_t release_count = 0; int nb_init(uint64_t base, uint64_t size) { @@ -41,14 +42,11 @@ int nb_init(uint64_t base, uint64_t size) total_memory = size; min_size = PAGE_SIZE; depth = LOG2_LOWER(total_memory / min_size); - max_level = 0; - max_size = EXP2(depth - max_level) * min_size; + base_level = 0; + max_size = EXP2(depth - base_level) * min_size; - /* Calculate required tree size */ - uint32_t total_nodes = 1; // we have garbage node at idx 0 - for (uint32_t i = 0; i <= depth; i++) { - total_nodes += (total_memory / (EXP2(i) * min_size)); - } + /* Calculate required tree size - root node is at index 1 */ + uint32_t total_nodes = EXP2(depth + 1); /* Calculate required index size */ uint32_t total_pages = (total_memory / min_size); @@ -80,17 +78,17 @@ int nb_init(uint64_t base, uint64_t size) uint32_t try_alloc(uint32_t node) { - /* Try to mark as BUSY */ + /* Occupy the node */ uint8_t free = 0; - if (!CAS(&tree[node], &free, BUSY)) { + if (!BCAS(&tree[node], &free, BUSY)) { return node; } uint32_t current = node; uint32_t child = 0; - /* Propagate info about the occupancy up to the ancestor node(s) */ - while (max_level < LEVEL(current)) { + /* Propagate the info about the occupancy up to the ancestor node(s) */ + while (base_level < LEVEL(current)) { child = current; current = current >> 1; @@ -107,7 +105,7 @@ uint32_t try_alloc(uint32_t node) new_val = clean_coal(curr_val, child); new_val = mark(new_val, child); - } while (!CAS(&tree[current], &curr_val, new_val)); + } while (!BCAS(&tree[current], &curr_val, new_val)); } return 0; @@ -123,6 +121,8 @@ void* nb_alloc(uint64_t size) size = min_size; } + nb_alloc_again:; + uint32_t ts = release_count; uint32_t level = LOG2_LOWER(total_memory / size); if (depth < level) { @@ -154,18 +154,88 @@ void* nb_alloc(uint64_t size) } } + /* A release occured, try again */ + if (ts != release_count) { + goto nb_alloc_again; + } + return (void*) 0; - } -void freenode(uint32_t node, uint32_t level) +void free_unmark(uint32_t node, uint32_t upper_bound) +{ + uint32_t current = node; + uint32_t child = 0; + + uint8_t curr_val = 0; + uint8_t new_val = 0; + + do { + child = current; + current = current >> 1; + + do { + curr_val = tree[current]; + + if (!is_coal(curr_val, child)) { + return; + } + + new_val = unmark(curr_val, child); + } while (!BCAS(&tree[current], &curr_val, new_val)); + } while (upper_bound < LEVEL(current) && !is_occ_buddy(new_val, child)); +} + +void freenode(uint32_t node, uint32_t upper_bound) { + /* TODO: should I check for double frees? */ + if (is_free(tree[node])) { + return; + } + + /* Phase 1. Ancestors of the node are marked as coalescing */ + uint32_t current = node >> 1; + uint32_t child = node; + + while (base_level < LEVEL(child)) { + uint8_t curr_val = 0; + uint8_t new_val = 0; + uint8_t old_val = 0; + + do { + curr_val = tree[current]; + new_val = set_coal(curr_val, child); + old_val = VCAS(&tree[current], &curr_val, new_val); + } while (old_val != curr_val); + + if (is_occ_buddy(old_val, child) && !is_coal_buddy(old_val, child)) { + break; + } + child = current; + current = current >> 1; + } + + /* Phase 2. Mark the node as free */ + tree[node] = 0; + + /* Phase 3. Propagate node release upward and possibly merge buddies */ + if (LEVEL(node) != base_level) { + free_unmark(node, upper_bound); + } } void nb_free(void *addr) { + /* Range check (is this necessary?) */ + uint64_t u_addr = (uint64_t) addr; + if (u_addr < base_address || (base_address + total_memory) < u_addr) { + return; + } + uint32_t n = (u_addr - base_address) / min_size; + freenode(index[n], base_level); + FAD(&release_count, 1); }