From 12e42f40c52f15d7b60de2a15224426f192a15ed Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 7 May 2023 01:21:50 +0300 Subject: [PATCH] Kernel/LibC: add free function for FixedWidthAllocator I have to rework the syscall API and allocators in process. For now this works well enough :) --- .../kernel/Memory/FixedWidthAllocator.h | 6 +- kernel/include/kernel/Process.h | 1 + kernel/include/kernel/Syscall.h | 1 + kernel/kernel/Memory/FixedWidthAllocator.cpp | 73 +++++++++++++++++-- kernel/kernel/Process.cpp | 8 ++ kernel/kernel/Syscall.cpp | 8 ++ libc/stdlib.cpp | 6 +- libc/unistd.cpp | 6 ++ userspace/test.c | 48 ++++-------- 9 files changed, 112 insertions(+), 45 deletions(-) diff --git a/kernel/include/kernel/Memory/FixedWidthAllocator.h b/kernel/include/kernel/Memory/FixedWidthAllocator.h index 82215f35..44a0bd58 100644 --- a/kernel/include/kernel/Memory/FixedWidthAllocator.h +++ b/kernel/include/kernel/Memory/FixedWidthAllocator.h @@ -17,7 +17,7 @@ namespace Kernel ~FixedWidthAllocator(); vaddr_t allocate(); - void deallocate(vaddr_t); + bool deallocate(vaddr_t); uint32_t allocation_size() const { return m_allocation_size; } @@ -26,8 +26,10 @@ namespace Kernel { node* prev { nullptr }; node* next { nullptr }; + bool allocated { false }; }; - vaddr_t address_of(const node*) const; + vaddr_t address_of_node(const node*) const; + node* node_from_address(vaddr_t) const; void allocate_page_for_node_if_needed(const node*); private: diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 8d67779e..35d26d44 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -57,6 +57,7 @@ namespace Kernel BAN::ErrorOr set_working_directory(BAN::StringView); BAN::ErrorOr allocate(size_t); + void free(void*); void termid(char*) const; diff --git a/kernel/include/kernel/Syscall.h b/kernel/include/kernel/Syscall.h index c6e810f7..0d3574ce 100644 --- a/kernel/include/kernel/Syscall.h +++ b/kernel/include/kernel/Syscall.h @@ -8,6 +8,7 @@ #define SYS_SEEK 6 #define SYS_OPEN 7 #define SYS_ALLOC 8 +#define SYS_FREE 9 #include diff --git a/kernel/kernel/Memory/FixedWidthAllocator.cpp b/kernel/kernel/Memory/FixedWidthAllocator.cpp index 35484c31..e0865cec 100644 --- a/kernel/kernel/Memory/FixedWidthAllocator.cpp +++ b/kernel/kernel/Memory/FixedWidthAllocator.cpp @@ -87,6 +87,9 @@ namespace Kernel node* node = m_free_list; + ASSERT(!node->allocated); + node->allocated = true; + m_free_list = node->next; if (m_free_list) m_free_list->prev = nullptr; @@ -100,23 +103,50 @@ namespace Kernel m_allocated++; allocate_page_for_node_if_needed(node); - return address_of(node); + return address_of_node(node); } - void FixedWidthAllocator::deallocate(paddr_t addr) + bool FixedWidthAllocator::deallocate(vaddr_t address) { - (void)addr; - ASSERT_NOT_REACHED(); + if (address % m_allocation_size) + return false; + + node* node = node_from_address(address); + if (node == nullptr) + return false; + + + if (!node->allocated) + { + dwarnln("deallocate called on unallocated address"); + return true; + } + node->allocated = false; + + if (node->prev) + node->prev->next = node->next; + if (node->next) + node->next->prev = node->prev; + + node->next = m_free_list; + node->prev = nullptr; + + if (m_free_list) + m_free_list->prev = node; + m_free_list = node; + + m_allocated--; + return true; } - vaddr_t FixedWidthAllocator::address_of(const node* node) const + vaddr_t FixedWidthAllocator::address_of_node(const node* node) const { uint32_t index = node - (struct node*)m_nodes_page; - uint32_t page_index = index / (PAGE_SIZE / sizeof(struct node)); + uint32_t page_index = index / (PAGE_SIZE / m_allocation_size); ASSERT(page_index < PAGE_SIZE / sizeof(vaddr_t)); - uint32_t offset = index % (PAGE_SIZE / sizeof(struct node)); + uint32_t offset = index % (PAGE_SIZE / m_allocation_size); vaddr_t page_begin = ((vaddr_t*)m_allocated_pages)[page_index]; ASSERT(page_begin); @@ -124,11 +154,38 @@ namespace Kernel return page_begin + offset * m_allocation_size; } + FixedWidthAllocator::node* FixedWidthAllocator::node_from_address(vaddr_t address) const + { + // TODO: This probably should be optimized from O(n) preferably to O(1) but I + // don't want to think about performance now. + + ASSERT(address % m_allocation_size == 0); + + vaddr_t page_begin = address / PAGE_SIZE * PAGE_SIZE; + + for (uint32_t page_index = 0; page_index < PAGE_SIZE / sizeof(vaddr_t); page_index++) + { + vaddr_t vaddr = ((vaddr_t*)m_allocated_pages)[page_index]; + if (vaddr != page_begin) + continue; + + uint32_t offset = (address - page_begin) / m_allocation_size; + + node* result = (node*)m_nodes_page; + result += page_index * PAGE_SIZE / m_allocation_size; + result += offset; + ASSERT(address_of_node(result) == address); + return result; + } + + return nullptr; + } + void FixedWidthAllocator::allocate_page_for_node_if_needed(const node* node) { uint32_t index = node - (struct node*)m_nodes_page; - uint32_t page_index = index / (PAGE_SIZE / sizeof(struct node)); + uint32_t page_index = index / (PAGE_SIZE / m_allocation_size); ASSERT(page_index < PAGE_SIZE / sizeof(vaddr_t)); vaddr_t& page_vaddr = ((vaddr_t*)m_allocated_pages)[page_index]; diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 84364d21..28fe10ab 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -386,6 +386,14 @@ namespace Kernel return BAN::Error::from_errno(ENOMEM); } + void Process::free(void* ptr) + { + for (auto& allocator : m_fixed_width_allocators) + if (allocator.deallocate((vaddr_t)ptr)) + return; + dwarnln("free called on pointer that was not allocated"); + } + void Process::termid(char* buffer) const { if (m_tty == nullptr) diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index fe2e0d6a..1b56dbd8 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -63,6 +63,11 @@ namespace Kernel return (long)res.value(); } + void sys_free(void* ptr) + { + Process::current().free(ptr); + } + extern "C" long cpp_syscall_handler(int syscall, void* arg1, void* arg2, void* arg3) { Thread::current().set_in_syscall(true); @@ -96,6 +101,9 @@ namespace Kernel case SYS_ALLOC: ret = sys_alloc((size_t)arg1); break; + case SYS_FREE: + sys_free(arg1); + break; default: Kernel::panic("Unknown syscall {}", syscall); } diff --git a/libc/stdlib.cpp b/libc/stdlib.cpp index c9143431..329dc167 100644 --- a/libc/stdlib.cpp +++ b/libc/stdlib.cpp @@ -79,7 +79,9 @@ void* calloc(size_t nmemb, size_t size) return ptr; } -void free(void*) +void free(void* ptr) { - + if (ptr == nullptr) + return; + syscall(SYS_FREE, ptr); } diff --git a/libc/unistd.cpp b/libc/unistd.cpp index f53815a9..3d43839b 100644 --- a/libc/unistd.cpp +++ b/libc/unistd.cpp @@ -74,6 +74,12 @@ long syscall(long syscall, ...) ret = Kernel::syscall(SYS_ALLOC, bytes); break; } + case SYS_FREE: + { + void* ptr = va_arg(args, void*); + ret = Kernel::syscall(SYS_FREE, ptr); + break; + } default: puts("LibC: Unhandeled syscall"); } diff --git a/userspace/test.c b/userspace/test.c index f56886a0..c4646947 100644 --- a/userspace/test.c +++ b/userspace/test.c @@ -3,42 +3,24 @@ int main() { - void* ptr = malloc(10); - if (ptr == NULL) + for (int i = 0; i < 10; i++) { - perror("malloc"); - return 1; - } - *(int*)ptr = 5; - putc('0' + *(int*)ptr, stdout); - return 0; - - FILE* fp = fopen("/boot/grub/grub.cfg", "r"); - if (fp == NULL) - { - perror("fopen"); - return 1; - } - - for (;;) - { - char buffer[128]; - size_t nread = fread(buffer, 1, sizeof(buffer) - 1, fp); - if (nread == 0) + int* ptrs[10]; + for (int j = 0; j < 10; j++) { - if (ferror(fp)) - perror("fread"); - break; + ptrs[j] = malloc(10); + if (ptrs[j] == NULL) + { + perror("malloc"); + return 1; + } + *ptrs[j] = j; + putc('0' + *ptrs[j], stdout); } - buffer[nread] = '\0'; - fputs(buffer, stdout); + for (int j = 0; j < 10; j++) + free(ptrs[j]); + putc('\n', stdout); } - - if (fclose(fp) == EOF) - { - perror("fclose"); - return 1; - } - + return 0; }