From bc8fad4038bcb6bb30e159e5a8e1ad45e354617e Mon Sep 17 00:00:00 2001 From: stanislav_shchetinin Date: Tue, 22 Oct 2024 15:55:18 +0300 Subject: [PATCH 1/4] Fixed typo (#25) --- doc/lab/1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lab/1.md b/doc/lab/1.md index 3b56d6a..c6581ba 100644 --- a/doc/lab/1.md +++ b/doc/lab/1.md @@ -40,7 +40,7 @@ Задача системных вызовов — дать программам из user-space возможность выполнять привилегированные команды. -Реализуем новый системный вызов `dump`. Он будет выводить на экран состояние регистров `s2—s12` вызывающего процесса. +Реализуем новый системный вызов `dump`. Он будет выводить на экран состояние регистров `s2—s11` вызывающего процесса. Чтобы системный вызов был доступен из user-space, добавим в файл [user/user.h](/user/user.h) объявление функции `dump`, как это сделано для других системных вызовов. В файл [user/usys.pl](/user/usys.pl) добавьте строку `entry("dump")` — он отвечает за генерацию ассемблерных инструкций для совершения системного вызова. From fd8ece0ca0035b162335568453513aa5745f9f38 Mon Sep 17 00:00:00 2001 From: Victor Smirnov <53015676+vityaman@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:06:31 +0300 Subject: [PATCH 2/4] Update repo.md --- doc/setup/repo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/setup/repo.md b/doc/setup/repo.md index b594c2e..fe191f7 100644 --- a/doc/setup/repo.md +++ b/doc/setup/repo.md @@ -14,7 +14,7 @@ git clone git@github.com:secs-dev/xv6-riscv.git ## Сдача ЛР -Добавьте своего преподавателя в репозиторий в роли участника с правами на чтение. Когда ЛР будет готова, сделайте `MR` и призовите преподавателя на ревью. +Порядок сдачи ЛР описан в документе [Порядок прохождения Code Review](https://github.com/secs-dev/os-course/blob/main/doc/code-review.md). ## Поддержка From a751244ad3d05c2c8f7f5e42bd70dc287859b4f9 Mon Sep 17 00:00:00 2001 From: Victor Smirnov <53015676+vityaman@users.noreply.github.com> Date: Fri, 25 Oct 2024 21:15:07 +0300 Subject: [PATCH 3/4] Update doc/lab/1.md --- doc/lab/1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lab/1.md b/doc/lab/1.md index c6581ba..1fce226 100644 --- a/doc/lab/1.md +++ b/doc/lab/1.md @@ -68,7 +68,7 @@ 3. `uint64 *return_value` — адрес, по которому необходимо вернуть значение -Обратите внимание, что в целях безопасности регистры процесса может смотреть только сам процесс и его родитель. +Обратите внимание, что в целях безопасности регистры процесса может смотреть только сам процесс и его предки. В этом системном вызове, в отличие от `dump`, вам понадобится корректно обрабатывать и возвращать ошибки: From 577119ab566c5e2b1603344bf547a6c7d21af727 Mon Sep 17 00:00:00 2001 From: Razinkin Alexander <160290174+DecafMangoITMO@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:18:35 +0300 Subject: [PATCH 4/4] Add buddy allocator (#28) Completely add buddy allocator sources with linked list implementation. Co-authored-by: DecafMango --- Makefile | 4 +- kernel/buddy.c | 333 +++++++++++++++++++++++++++++++++++++++++++++++++ kernel/defs.h | 16 +++ kernel/list.c | 59 +++++++++ kernel/list.h | 6 + 5 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 kernel/buddy.c create mode 100644 kernel/list.c create mode 100644 kernel/list.h diff --git a/Makefile b/Makefile index 25db657..218d080 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,9 @@ OBJS = \ $K/sysfile.o \ $K/kernelvec.o \ $K/plic.o \ - $K/virtio_disk.o + $K/virtio_disk.o \ + $K/buddy.o \ + $K/list.o # riscv64-unknown-elf- or riscv64-linux-gnu- # perhaps in /opt/riscv/bin diff --git a/kernel/buddy.c b/kernel/buddy.c new file mode 100644 index 0000000..8080234 --- /dev/null +++ b/kernel/buddy.c @@ -0,0 +1,333 @@ +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "spinlock.h" +#include "riscv.h" +#include "defs.h" +#include "list.h" + +// Buddy allocator + +static int nsizes; // the number of entries in bd_sizes array + +#define LEAF_SIZE 16 // The smallest block size +#define MAXSIZE (nsizes - 1) // Largest index in bd_sizes array +#define BLK_SIZE(k) ((1L << (k)) * LEAF_SIZE) // Size of block at size k +#define HEAP_SIZE BLK_SIZE(MAXSIZE) +#define NBLK(k) (1 << (MAXSIZE - k)) // Number of block at size k +#define ROUNDUP(n, sz) \ + (((((n)-1) / (sz)) + 1) * (sz)) // Round up to the next multiple of sz + +typedef struct list Bd_list; + +// The allocator has sz_info for each size k. Each sz_info has a free +// list, an array alloc to keep track which blocks have been +// allocated, and an split array to to keep track which blocks have +// been split. The arrays are of type char (which is 1 byte), but the +// allocator uses 1 bit per block (thus, one char records the info of +// 8 blocks). +struct sz_info { + Bd_list free; + char *alloc; + char *split; +}; +typedef struct sz_info Sz_info; + +static Sz_info *bd_sizes; +static void *bd_base; // start address of memory managed by the buddy allocator +static struct spinlock lock; + +// Return 1 if bit at position index in array is set to 1 +int bit_isset(char *array, int index) { + char b = array[index / 8]; + char m = (1 << (index % 8)); + return (b & m) == m; +} + +// Set bit at position index in array to 1 +void bit_set(char *array, int index) { + char b = array[index / 8]; + char m = (1 << (index % 8)); + array[index / 8] = (b | m); +} + +// Clear bit at position index in array +void bit_clear(char *array, int index) { + char b = array[index / 8]; + char m = (1 << (index % 8)); + array[index / 8] = (b & ~m); +} + +// Print a bit vector as a list of ranges of 1 bits +void bd_print_vector(char *vector, int len) { + int last, lb; + + last = 1; + lb = 0; + for (int b = 0; b < len; b++) { + if (last == bit_isset(vector, b)) continue; + if (last == 1) printf(" [%d, %d)", lb, b); + lb = b; + last = bit_isset(vector, b); + } + if (lb == 0 || last == 1) { + printf(" [%d, %d)", lb, len); + } + printf("\n"); +} + +// Print buddy's data structures +void bd_print() { + for (int k = 0; k < nsizes; k++) { + printf("size %d (blksz %ld nblk %d): free list: ", k, BLK_SIZE(k), NBLK(k)); + lst_print(&bd_sizes[k].free); + printf(" alloc:"); + bd_print_vector(bd_sizes[k].alloc, NBLK(k)); + if (k > 0) { + printf(" split:"); + bd_print_vector(bd_sizes[k].split, NBLK(k)); + } + } +} + +// What is the first k such that 2^k >= n? +int firstk(uint64 n) { + int k = 0; + uint64 size = LEAF_SIZE; + + while (size < n) { + k++; + size *= 2; + } + return k; +} + +// Compute the block index for address p at size k +int blk_index(int k, char *p) { + int n = p - (char *)bd_base; + return n / BLK_SIZE(k); +} + +// Convert a block index at size k back into an address +void *addr(int k, int bi) { + int n = bi * BLK_SIZE(k); + return (char *)bd_base + n; +} + +// allocate nbytes, but malloc won't return anything smaller than LEAF_SIZE +void *bd_malloc(uint64 nbytes) { + int fk, k; + + acquire(&lock); + + // Find a free block >= nbytes, starting with smallest k possible + fk = firstk(nbytes); + for (k = fk; k < nsizes; k++) { + if (!lst_empty(&bd_sizes[k].free)) break; + } + if (k >= nsizes) { // No free blocks? + release(&lock); + return 0; + } + + // Found a block; pop it and potentially split it. + char *p = lst_pop(&bd_sizes[k].free); + bit_set(bd_sizes[k].alloc, blk_index(k, p)); + for (; k > fk; k--) { + // split a block at size k and mark one half allocated at size k-1 + // and put the buddy on the free list at size k-1 + char *q = p + BLK_SIZE(k - 1); // p's buddy + bit_set(bd_sizes[k].split, blk_index(k, p)); + bit_set(bd_sizes[k - 1].alloc, blk_index(k - 1, p)); + lst_push(&bd_sizes[k - 1].free, q); + } + release(&lock); + + return p; +} + +// Find the size of the block that p points to. +int size(char *p) { + for (int k = 0; k < nsizes; k++) { + if (bit_isset(bd_sizes[k + 1].split, blk_index(k + 1, p))) { + return k; + } + } + return 0; +} + +// Free memory pointed to by p, which was earlier allocated using +// bd_malloc. +void bd_free(void *p) { + void *q; + int k; + + acquire(&lock); + for (k = size(p); k < MAXSIZE; k++) { + int bi = blk_index(k, p); + int buddy = (bi % 2 == 0) ? bi + 1 : bi - 1; + bit_clear(bd_sizes[k].alloc, bi); // free p at size k + if (bit_isset(bd_sizes[k].alloc, buddy)) { // is buddy allocated? + break; // break out of loop + } + // budy is free; merge with buddy + q = addr(k, buddy); + lst_remove(q); // remove buddy from free list + if (buddy % 2 == 0) { + p = q; + } + // at size k+1, mark that the merged buddy pair isn't split + // anymore + bit_clear(bd_sizes[k + 1].split, blk_index(k + 1, p)); + } + lst_push(&bd_sizes[k].free, p); + release(&lock); +} + +// Compute the first block at size k that doesn't contain p +int blk_index_next(int k, char *p) { + int n = (p - (char *)bd_base) / BLK_SIZE(k); + if ((p - (char *)bd_base) % BLK_SIZE(k) != 0) n++; + return n; +} + +int _log2(uint64 n) { + int k = 0; + while (n > 1) { + k++; + n = n >> 1; + } + return k; +} + +// Mark memory from [start, stop), starting at size 0, as allocated. +void bd_mark(void *start, void *stop) { + int bi, bj; + + if (((uint64)start % LEAF_SIZE != 0) || ((uint64)stop % LEAF_SIZE != 0)) + panic("bd_mark"); + + for (int k = 0; k < nsizes; k++) { + bi = blk_index(k, start); + bj = blk_index_next(k, stop); + for (; bi < bj; bi++) { + if (k > 0) { + // if a block is allocated at size k, mark it as split too. + bit_set(bd_sizes[k].split, bi); + } + bit_set(bd_sizes[k].alloc, bi); + } + } +} + +// If a block is marked as allocated and the buddy is free, put the +// buddy on the free list at size k. +int bd_initfree_pair(int k, int bi) { + int buddy = (bi % 2 == 0) ? bi + 1 : bi - 1; + int free = 0; + if (bit_isset(bd_sizes[k].alloc, bi) != bit_isset(bd_sizes[k].alloc, buddy)) { + // one of the pair is free + free = BLK_SIZE(k); + if (bit_isset(bd_sizes[k].alloc, bi)) + lst_push(&bd_sizes[k].free, addr(k, buddy)); // put buddy on free list + else + lst_push(&bd_sizes[k].free, addr(k, bi)); // put bi on free list + } + return free; +} + +// Initialize the free lists for each size k. For each size k, there +// are only two pairs that may have a buddy that should be on free list: +// bd_left and bd_right. +int bd_initfree(void *bd_left, void *bd_right) { + int free = 0; + + for (int k = 0; k < MAXSIZE; k++) { // skip max size + int left = blk_index_next(k, bd_left); + int right = blk_index(k, bd_right); + free += bd_initfree_pair(k, left); + if (right <= left) continue; + free += bd_initfree_pair(k, right); + } + return free; +} + +// Mark the range [bd_base,p) as allocated +int bd_mark_data_structures(char *p) { + int meta = p - (char *)bd_base; + printf("bd: %d meta bytes for managing %ld bytes of memory\n", meta, + BLK_SIZE(MAXSIZE)); + bd_mark(bd_base, p); + return meta; +} + +// Mark the range [end, HEAPSIZE) as allocated +int bd_mark_unavailable(void *end, void *left) { + int unavailable = BLK_SIZE(MAXSIZE) - (end - bd_base); + if (unavailable > 0) unavailable = ROUNDUP(unavailable, LEAF_SIZE); + printf("bd: 0x%x bytes unavailable\n", unavailable); + + void *bd_end = bd_base + BLK_SIZE(MAXSIZE) - unavailable; + bd_mark(bd_end, bd_base + BLK_SIZE(MAXSIZE)); + return unavailable; +} + +// Initialize the buddy allocator: it manages memory from [base, end). +void bd_init(void *base, void *end) { + char *p = (char *)ROUNDUP((uint64)base, LEAF_SIZE); + int sz; + + initlock(&lock, "buddy"); + bd_base = (void *)p; + + // compute the number of sizes we need to manage [base, end) + nsizes = _log2(((char *)end - p) / LEAF_SIZE) + 1; + if ((char *)end - p > BLK_SIZE(MAXSIZE)) { + nsizes++; // round up to the next power of 2 + } + + printf("bd: memory sz is %ld bytes; allocate an size array of length %d\n", + (char *)end - p, nsizes); + + // allocate bd_sizes array + bd_sizes = (Sz_info *)p; + p += sizeof(Sz_info) * nsizes; + memset(bd_sizes, 0, sizeof(Sz_info) * nsizes); + + // initialize free list and allocate the alloc array for each size k + for (int k = 0; k < nsizes; k++) { + lst_init(&bd_sizes[k].free); + sz = sizeof(char) * ROUNDUP(NBLK(k), 8) / 8; + bd_sizes[k].alloc = p; + memset(bd_sizes[k].alloc, 0, sz); + p += sz; + } + + // allocate the split array for each size k, except for k = 0, since + // we will not split blocks of size k = 0, the smallest size. + for (int k = 1; k < nsizes; k++) { + sz = sizeof(char) * (ROUNDUP(NBLK(k), 8)) / 8; + bd_sizes[k].split = p; + memset(bd_sizes[k].split, 0, sz); + p += sz; + } + p = (char *)ROUNDUP((uint64)p, LEAF_SIZE); + + // done allocating; mark the memory range [base, p) as allocated, so + // that buddy will not hand out that memory. + int meta = bd_mark_data_structures(p); + + // mark the unavailable memory range [end, HEAP_SIZE) as allocated, + // so that buddy will not hand out that memory. + int unavailable = bd_mark_unavailable(end, p); + void *bd_end = bd_base + BLK_SIZE(MAXSIZE) - unavailable; + + // initialize free lists for each size k + int free = bd_initfree(p, bd_end); + + // check if the amount that is free is what we expect + if (free != BLK_SIZE(MAXSIZE) - meta - unavailable) { + printf("free %d %ld\n", free, BLK_SIZE(MAXSIZE) - meta - unavailable); + panic("bd_init: free mem"); + } +} diff --git a/kernel/defs.h b/kernel/defs.h index d1b6bb9..f947e24 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -8,6 +8,7 @@ struct spinlock; struct sleeplock; struct stat; struct superblock; +struct list; // bio.c void binit(void); @@ -187,3 +188,18 @@ void virtio_disk_intr(void); // number of elements in fixed-size array #define NELEM(x) (sizeof(x)/sizeof((x)[0])) + +// list.c +void lst_init(struct list*); +void lst_remove(struct list*); +void lst_push(struct list*, void *); +void* lst_pop(struct list*); +void lst_print(struct list*); +int lst_empty(struct list*); + +// buddy.c +void bd_init(void*,void*); +void bd_free(void*); +void *bd_malloc(uint64); + + diff --git a/kernel/list.c b/kernel/list.c new file mode 100644 index 0000000..94267e3 --- /dev/null +++ b/kernel/list.c @@ -0,0 +1,59 @@ +#include "list.h" + +#include "types.h" +#include "param.h" +#include "memlayout.h" +#include "spinlock.h" +#include "riscv.h" +#include "defs.h" + +// double-linked, circular list. double-linked makes remove +// fast. circular simplifies code, because don't have to check for +// empty list in insert and remove. + +void +lst_init(struct list *lst) +{ + lst->next = lst; + lst->prev = lst; +} + +int +lst_empty(struct list *lst) { + return lst->next == lst; +} + +void +lst_remove(struct list *e) { + e->prev->next = e->next; + e->next->prev = e->prev; +} + +void* +lst_pop(struct list *lst) { + if(lst->next == lst) + panic("lst_pop"); + struct list *p = lst->next; + lst_remove(p); + return (void *)p; +} + +void +lst_push(struct list *lst, void *p) +{ + struct list *e = (struct list *) p; + e->next = lst->next; + e->prev = lst; + lst->next->prev = p; + lst->next = e; +} + +void +lst_print(struct list *lst) +{ + for (struct list *p = lst->next; p != lst; p = p->next) { + printf(" %p", p); + } + printf("\n"); +} + diff --git a/kernel/list.h b/kernel/list.h new file mode 100644 index 0000000..c5a595b --- /dev/null +++ b/kernel/list.h @@ -0,0 +1,6 @@ +#pragma once + +struct list { + struct list *next; + struct list *prev; +}; \ No newline at end of file