Add buddy phymem manager #1

Merged
tuyuyang merged 5 commits from :buddy into microkernel 2023-11-15 09:44:52 +08:00
14 changed files with 391 additions and 90 deletions

2
.gitignore vendored
View File

@ -2,5 +2,5 @@
*.o
*libmusl.a
*liblwip.a
*/build/*
*build/*
.DS_Store

View File

@ -1,6 +1,5 @@
#include "memlayout.h"
#include "mmu.h"
#include "vm.h"

View File

@ -40,7 +40,7 @@ static void _sys_irq_init()
// initialize the stacks for different mode
for (i = 0; i < sizeof(modes) / sizeof(uint32_t); i++) {
if (!(xizi_trap_driver.mode_stack_pages[i] = (uint32_t*)kalloc())) {
if (!(xizi_trap_driver.mode_stack_pages[i] = (uint32_t*)kalloc(PAGE_SIZE))) {
// panic("irq stack allocation failed");
}
init_stack(modes[i], (uint32_t)xizi_trap_driver.mode_stack_pages[i]);

View File

@ -0,0 +1,80 @@
#pragma once
#include "list.h"
#include "memlayout.h"
#include "spinlock.h"
#include "vm.h"
#include <stdbool.h>
#include <stdint.h>
#define MAX_BUDDY_ORDER (10)
#define FREE_LIST_INDEX(order) \
(1 << order)
#define IS_BUDDY_PAGE(buddy, order) \
(buddy->order == order && buddy->node.next != &buddy->node)
#define BUDDY_PAGE_INDEX(page_idx, order) \
(page_idx ^ (FREE_LIST_INDEX(order)))
#define COMBINED_PAGE_INDEX(page_idx, order) \
(page_idx & ~(FREE_LIST_INDEX(order)))
#define CALCULATE_NPAGES(size) \
(ALIGNUP(size, PAGE_SIZE) >> PTE_SHIFT)
struct KPage
{
struct double_list_node node;
union {
uint32_t order;
struct KPage *page_node;
};
};
struct KFreeList
{
uint32_t n_free_pages;
struct double_list_node list_head;
};
struct KBuddy {
uint32_t n_pages;
uint32_t use_lock;
struct spinlock lock;
struct KFreeList free_list[MAX_BUDDY_ORDER];
struct KPage *first_page;
};
/*********************************************
* Buddy system public functions
*********************************************/
/*
* Buddy system init function
* @param void
* @return void
*/
void KBuddySysInit();
/*
* Continuous pages alloc by size
* @param sizeuint32_t size of need alloc
* @return NULL or v_addr (char*) return NULL if alloc failed, or return virtual page's addr
*/
char* KBuddyAlloc(uint32_t size);
/*
* Continuous pages free from vaddr
* @param vaddr(char*) virtual addr
* @param isFreeSuccess(bool) return false if free failed, or return true
*/
bool KBuddyFree(char* vaddr);
/*
* Print current free pages for debug.
*/
void KFreePagesInfo();

View File

@ -4,5 +4,55 @@ struct list_node {
struct list_node* next;
};
struct double_list_node {
struct double_list_node *next;
struct double_list_node *prev;
};
#define CONTAINER_OF(item, type, member) \
((type*)((char*)(item) - (unsigned long)(&((type*)0)->member)))
#define IS_DOUBLE_LIST_EMPTY(head) \
((head)->next == (head))
/*********************************************
*
* Double linked list operation functions
*
*********************************************/
__attribute__((always_inline))
static void inline doubleListNodeInit(struct double_list_node *list)
{
list->next = list;
list->prev = list;
}
__attribute__((always_inline))
static void inline _double_list_add(struct double_list_node* new, struct double_list_node* prev, struct double_list_node* next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
__attribute__((always_inline))
static void inline _double_list_del(struct double_list_node *prev, struct double_list_node *next)
{
next->prev = prev;
prev->next = next;
}
__attribute__((always_inline))
static void inline doubleListAddOnHead(struct double_list_node *new, struct double_list_node *head)
{
_double_list_add(new, head, head->next);
}
__attribute__((always_inline))
static void inline doubleListDel(struct double_list_node *entry)
{
_double_list_del(entry->prev, entry->next);
entry->next = entry;
entry->prev = entry;
}

View File

@ -6,10 +6,13 @@ extern void KPrintf(char* fmt, ...);
KPrintf("LOG: [%s] ", __func__); \
KPrintf(f, ##args)
#define DEBUG(f, args...) \
KPrintf("DEBUG: [%s] ", __func__); \
#define DEBUG_PRINTF(f, args...) \
KPrintf(f, ##args)
#define DEBUG(f, args...) \
DEBUG_PRINTF("DEBUG: [%s] ", __func__); \
DEBUG_PRINTF(f, ##args)
#define ERROR(f, args...) \
KPrintf("ERROR: [%s] ", __func__); \
KPrintf(f, ##args)

View File

@ -1,28 +1,11 @@
#pragma once
#include "memlayout.h"
#include "mmu.h"
#include "spinlock.h"
#include <string.h>
struct run {
struct run* next;
};
struct {
struct spinlock lock;
// int use_lock;
struct run* freelist;
} kmem;
extern int nr_free_pages;
#include "vm.h"
void phymem_init();
void freerange(void* vstart, void* vend);
bool kfree(char* vaddr);
void kfree(char* v);
char* kalloc(uint32_t size);
char* kalloc(void);
void show_phymem_info();

View File

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#define ALIGNUP(sz, al) (((uint32_t)(sz) + (uint32_t)(al)-1) & ~((uint32_t)(al)-1))
#define ALIGNDOWN(sz, al) ((uint32_t)(sz) & ~((uint32_t)(al)-1))
@ -14,6 +15,8 @@
#define PTE_IDX(v) (((uintptr_t)(v) >> PTE_SHIFT) & (NUM_PTE - 1))
#define PTE_ADDR(v) ALIGNDOWN(v, PTE_SZ)
#define PAGE_SIZE PTE_SZ
#define PDE_SHIFT 20
// mem range represented by 1 pde
#define PDE_SZ (1 << PDE_SHIFT)

View File

@ -39,6 +39,8 @@ extern void proc_switch(struct context**, struct context*);
void kmain(void)
{
phymem_init();
LOG("Buddy System Init Done.\r\n");
show_phymem_info();
xizi_kern_driver_init();
@ -51,26 +53,28 @@ void kmain(void)
struct PCB* pcb = 0;
DEBUG("Begin| nr_free_pcb: %d\r\n", xizi_proc_maintainer.nr_pcb_free);
DEBUG("Begin| nr_free_pages: %d\r\n", nr_free_pages);
show_phymem_info();
pcb = xizi_proc_maintainer.new_pcb();
DEBUG("New PCB| nr_free_pages: %d\r\n", nr_free_pages);
show_phymem_info();
DEBUG("New PCB| nr_free_pcb: %d\r\n", xizi_proc_maintainer.nr_pcb_free);
extern char _binary_initcode_start[], _binary_initcode_end[];
DEBUG("initcode start: %x, initcode end: %x\r\n", _binary_initcode_start, _binary_initcode_end);
xizi_pager.map_pages(&pcb->pgdir, 0, (uintptr_t)_binary_initcode_start, (uintptr_t)(_binary_initcode_end - _binary_initcode_start), AP_KU);
DEBUG("Map pages| nr_free_pages: %d\r\n", nr_free_pages);
show_phymem_info();
xizi_proc_maintainer.free_pcb(pcb);
DEBUG("Free PCB| nr_free_pages: %d\r\n", nr_free_pages);
show_phymem_info();
DEBUG("Free PCB| nr_free_pcb: %d\r\n", xizi_proc_maintainer.nr_pcb_free);
LOG("kernel init done.\r\n");
struct PCB* proc = first_proc();
show_phymem_info();
struct CPU* cpu = cur_cpu();
cpu->proc = proc;
proc_switch(&cpu->scheduler, proc->main_thread.context);

View File

@ -1,3 +1,3 @@
SRC_FILES:= phymem.c pager.c
SRC_FILES:= phymem.c pager.c buddy.c
include $(KERNEL_ROOT)/compiler.mk

View File

@ -0,0 +1,224 @@
/*
* Copyright (c) 2020 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
/**
* @file: buddy.c
* @brief: the buddy management of system memory
* @version: 3.0
* @author: AIIT XUOS Lab
* @date: 2023/4/27
*
*/
#include "buddy.h"
#include "log.h"
struct KBuddy global_KBuddy;
static uint32_t v_manager_mem_start;
extern char kern_end[];
static void _buddy_split_page(struct KPage* page, uint32_t low_order, uint32_t high_order, struct KFreeList* list)
{
uint32_t size = (1 << high_order);
while (high_order > low_order) {
list--;
size >>= 1;
doubleListAddOnHead(&page[size].node, &list->list_head);
list->n_free_pages++;
high_order--;
}
}
__attribute__((always_inline)) static void inline _buddy_set_pages_order(struct KPage* page, int order)
{
int i;
struct KPage* p = NULL;
page->order = order;
for (i = 1; i < FREE_LIST_INDEX(order); i++) {
p = page + i;
p->page_node = page;
}
}
__attribute__((always_inline)) static void inline _buddy_page_to_vaddr(struct KPage* page, char** vaddr)
{
uint32_t offset = page - global_KBuddy.first_page;
*vaddr = (char*)(v_manager_mem_start + (offset << PTE_SHIFT));
return;
}
__attribute__((always_inline)) static void inline _buddy_vaddr_to_page(struct KPage** page, char* vaddr)
{
uint32_t offset = (uint32_t)vaddr - v_manager_mem_start;
*page = (struct KPage*)(global_KBuddy.first_page + (offset >> PTE_SHIFT));
return;
}
// find free page and split it to small page if need
static struct KPage* KBuddyPagesAlloc(int nPages)
{
struct KPage* page = NULL;
struct KFreeList* list = NULL;
int i = 0, order = 0;
spinlock_lock(&global_KBuddy.lock);
// find order
for (order = 0; (FREE_LIST_INDEX(order)) < nPages; order++)
;
// find the free page list
for (i = order; i < MAX_BUDDY_ORDER; i++) {
list = global_KBuddy.free_list + i;
if (IS_DOUBLE_LIST_EMPTY(&list->list_head)) {
continue;
}
// find the page
page = CONTAINER_OF(list->list_head.next, struct KPage, node);
doubleListDel(&page->node);
list->n_free_pages--;
// split the page to some small pages
_buddy_split_page(page, order, i, list);
// set the pages' order
_buddy_set_pages_order(page, order);
spinlock_unlock(&global_KBuddy.lock);
return page;
}
// there is no enough free page to satisfy the nPages
spinlock_unlock(&global_KBuddy.lock);
return NULL;
}
// free continuous pages from page pointer
static void KBuddyPagesFree(struct KPage* page)
{
struct KPage* buddy = NULL;
uint32_t order = (page->order >= MAX_BUDDY_ORDER) ? 0 : page->order;
uint32_t buddy_idx = 0, new_buddy_idx = 0;
uint32_t page_idx = page - global_KBuddy.first_page;
spinlock_lock(&global_KBuddy.lock);
for (; order < MAX_BUDDY_ORDER - 1; order++) {
// find and delete buddy to combine
buddy_idx = BUDDY_PAGE_INDEX(page_idx, order);
if (buddy_idx > global_KBuddy.n_pages - 1) {
break;
}
buddy = page + (buddy_idx - page_idx);
if (!IS_BUDDY_PAGE(buddy, order)) {
break;
}
// remove buddy
doubleListDel(&buddy->node);
global_KBuddy.free_list[order].n_free_pages--;
buddy->order = MAX_BUDDY_ORDER;
// update page and page_idx after combined
new_buddy_idx = COMBINED_PAGE_INDEX(page_idx, order);
page = page + (new_buddy_idx - page_idx);
page_idx = new_buddy_idx;
}
page->order = order;
doubleListAddOnHead(&page->node, &global_KBuddy.free_list[order].list_head);
global_KBuddy.free_list[order].n_free_pages++;
spinlock_unlock(&global_KBuddy.lock);
return;
}
void KBuddySysInit()
{
uint32_t i = 0;
struct KPage* page = NULL;
struct KFreeList* free_list = NULL;
// init spinlock
spinlock_init(&global_KBuddy.lock, "kbuddy");
// init global kernel Buddy system
uint32_t n_page_total = ALIGNDOWN(((uint32_t)P2V(PHYSTOP) - (uint32_t)&kern_end), PAGE_SIZE) >> PTE_SHIFT;
uint32_t k_page_size = ALIGNUP((n_page_total * sizeof(struct KPage)), PAGE_SIZE);
uint32_t n_k_page = k_page_size >> PTE_SHIFT;
global_KBuddy.n_pages = n_page_total - n_k_page;
v_manager_mem_start = (uint32_t)&kern_end + k_page_size;
global_KBuddy.first_page = (struct KPage*)&kern_end;
memset(global_KBuddy.first_page, 0, k_page_size);
// init each free page list from 2^0 to 2^8
for (; i < MAX_BUDDY_ORDER; i++) {
free_list = global_KBuddy.free_list + i;
doubleListNodeInit(&free_list->list_head);
free_list->n_free_pages = 0;
}
// init and free each page
for (i = 0; i < global_KBuddy.n_pages; i++) {
page = global_KBuddy.first_page + i;
page->order = MAX_BUDDY_ORDER;
}
for (i = 0; i < global_KBuddy.n_pages; i++) {
page = global_KBuddy.first_page + i;
doubleListNodeInit(&page->node);
KBuddyPagesFree(page);
}
}
char* KBuddyAlloc(uint32_t size)
{
int nPages = CALCULATE_NPAGES(size);
struct KPage* page = KBuddyPagesAlloc(nPages);
if (page == NULL)
return NULL;
char* v_addr = NULL;
_buddy_page_to_vaddr(page, &v_addr);
return v_addr;
}
bool KBuddyFree(char* vaddr)
{
struct KPage* page = NULL;
if ((uint32_t)vaddr % (PAGE_SIZE)) {
ERROR("kbuddyfree - unaligned\r\n");
return false;
}
if ((uint32_t)vaddr < (uint32_t)&kern_end) {
ERROR("kbuddyfree - under kern_end\r\n");
return false;
}
if (V2P(vaddr) >= PHYSTOP) {
ERROR("kbuddyfree - over PHYSTOP\r\n");
return false;
}
_buddy_vaddr_to_page(&page, vaddr);
KBuddyPagesFree(page);
return true;
}
void KFreePagesInfo()
{
DEBUG("Buddy structure:");
for (int j = 0; j < MAX_BUDDY_ORDER; j++) {
DEBUG_PRINTF(" %d ", global_KBuddy.free_list[j].n_free_pages);
}
DEBUG_PRINTF("\r\n");
}

View File

@ -8,7 +8,7 @@
static bool _new_user_pgdir(struct PageDirectory* pgdir, uintptr_t vrange)
{
void* new_pgdir_addr = 0;
if ((new_pgdir_addr = kalloc()) == NULL) {
if ((new_pgdir_addr = kalloc(4 * PAGE_SIZE)) == NULL) {
LOG("new pgdir address: %x\r\n", new_pgdir_addr);
return false;
}
@ -29,12 +29,12 @@ static uintptr_t* _page_walk(struct PageDirectory* pgdir, uintptr_t vaddr, bool
if (*pde != 0) {
pgtbl_vaddr = P2V(PTE_ADDR(*pde));
} else {
if (!alloc || !(pgtbl_vaddr = (uintptr_t*)kalloc())) {
if (!alloc || !(pgtbl_vaddr = (uintptr_t*)kalloc(sizeof(uintptr_t) * NUM_PTE))) {
return 0;
}
LOG("alloc %x for pgtbl\r\n", pgtbl_vaddr);
memset(pgtbl_vaddr, 0, PGSIZE);
memset(pgtbl_vaddr, 0, sizeof(uintptr_t) * NUM_PTE);
*pde = V2P(pgtbl_vaddr) | PDE_COARSE;
}

View File

@ -18,71 +18,26 @@
* @date: 2023/4/27
*
*/
#include <stdint.h>
#include "phymem.h"
extern char kern_end[]; // first address after kernel loaded from ELF file
// defined by the kernel linker script in kernel.ld
int nr_free_pages;
extern void KPrintf(char* fmt, ...);
#include "buddy.h"
void phymem_init()
{
spinlock_init(&kmem.lock, "kmem");
nr_free_pages = 0;
// kmem.use_lock = 0;
freerange(kern_end, P2V(PHYSTOP));
KBuddySysInit();
}
void freerange(void* vstart, void* vend)
bool kfree(char* vaddr)
{
char* p;
p = (char*)ALIGNUP((uint32_t)vstart, 4 * PGSIZE);
for (; p + 4 * PGSIZE <= (char*)vend; p += 4 * PGSIZE) {
kfree(p);
}
return KBuddyFree(vaddr);
}
void kfree(char* v)
char* kalloc(uint32_t size)
{
struct run* r;
if ((uint32_t)v % (4 * PGSIZE))
KPrintf("kfree - unaligned\r\n");
if (v < kern_end)
KPrintf("kfree - under kern_end\r\n");
if ((uint32_t)V2P(v) >= (uint32_t)PHYSTOP)
KPrintf("kfree - over PHYSTOP, v: %x, p: %x\r\n", v, V2P(v));
// Fill with junk to catch dangling refs.
memset(v, 0, 4 * PGSIZE);
// freeing a page
spinlock_lock(&kmem.lock);
r = (struct run*)v;
r->next = kmem.freelist;
kmem.freelist = r;
nr_free_pages += 1;
spinlock_unlock(&kmem.lock);
return KBuddyAlloc(size);
}
char* kalloc(void)
void show_phymem_info()
{
struct run* r = NULL;
spinlock_lock(&kmem.lock);
if ((r = kmem.freelist))
kmem.freelist = r->next;
nr_free_pages -= 1;
spinlock_unlock(&kmem.lock);
return (char*)r;
KFreePagesInfo();
}

View File

@ -8,14 +8,14 @@ struct CPU global_cpus[NR_CPU];
// return: number of PCBs stored in one page
static int _alloc_page_for_pcb()
{
void* proc_addr = kalloc();
void* proc_addr = kalloc(PAGE_SIZE);
if (proc_addr == NULL) {
return 0;
}
LOG("proc page addr: %x\r\n", proc_addr);
void* proc_end = proc_addr + PGSIZE - sizeof(struct PCB);
void* proc_end = proc_addr + PAGE_SIZE - sizeof(struct PCB);
spinlock_lock(&xizi_proc_maintainer.lock);
@ -139,7 +139,7 @@ static struct PCB* _new_pcb()
// init main thread of proc
pcb->main_thread.proc = pcb;
// alloc stack page for proc
if ((void*)(pcb->main_thread.stack_addr = (uintptr_t)kalloc()) == NULL) {
if ((void*)(pcb->main_thread.stack_addr = (uintptr_t)kalloc(PAGE_SIZE)) == NULL) {
_dealloc_pcb(pcb);
return NULL;
}