spz-lab3/src/kernel.c

345 lines
9.4 KiB
C

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "kernel.h"
#include "process.h"
#include "config.h"
extern struct RunQ *runq;
extern struct PhysPage *first_free_page;
extern struct PhysPage *first_busy_page;
extern size_t system_time;
// metrics
extern int free_pages_cnt;
extern int busy_pages_cnt;
struct RunQ *
RunQ(size_t max_procs)
{
struct RunQ *runq = malloc(sizeof(struct RunQ));
runq->current_proc = NULL;
runq->max_procs = max_procs;
runq->proc_amount = 0;
runq->next_proc_id = 1;
return runq;
}
void
RUNQ_add_process(size_t max_page_accesses,
size_t total_pages_owned,
size_t ws_size)
{
if (runq->proc_amount >= runq->max_procs)
return;
struct Process *new_p = Process(runq->next_proc_id, max_page_accesses, total_pages_owned, ws_size);
if (!runq->current_proc) {
runq->current_proc = new_p;
new_p->next = new_p;
new_p->prev = new_p;
} else {
new_p->next = runq->current_proc;
new_p->prev = runq->current_proc->prev;
new_p->next->prev = new_p;
new_p->prev->next = new_p;
runq->current_proc = new_p;
}
runq->proc_amount++;
runq->next_proc_id++;
}
void
RUNQ_remove_current_process(void)
{
struct Process *tp = runq->current_proc;
printf("[kernel:remove_current_process] Cleaning up process id %d\n", tp->id);
if (runq->current_proc == runq->current_proc->next) {
runq->current_proc = NULL;
} else {
// detach this process from the list
tp->prev->next = tp->next;
tp->next->prev = tp->prev;
runq->current_proc = tp->next;
}
// free pages allocated by this process
struct PhysPage *starting_page = first_busy_page;
struct PhysPage *cp = first_busy_page;
for (size_t i = 0; i < PHYSICAL_PAGE_AMOUNT; i++) {
if (cp->pt == tp->pt) {
printf("[kernel:remove_current_process] Found ppn #%d (pte %d#%d), freeing it\n", cp->ppn, tp->id, cp->pt_index);
// detach page from the busy list
if (cp == cp->next) {
first_busy_page = NULL;
} else {
cp->prev->next = cp->next;
cp->next->prev = cp->prev;
first_busy_page = cp->next;
}
busy_pages_cnt--;
// attach page to the free list
if (!first_free_page) {
first_free_page = cp;
cp->next = cp;
cp->prev = cp;
} else {
cp->next = first_free_page;
cp->prev = first_free_page->prev;
cp->prev->next = cp;
cp->next->prev = cp;
}
free_pages_cnt++;
// reset busy flag
cp->busy_flag = 0;
cp = first_busy_page;
} else {
cp = cp->next;
}
#if SANITY_CHECK_ENABLED == 1
KERNEL_sanity_check_memory_lists();
#endif
if ((cp == starting_page) || (!cp))
break;
}
PROCESS_destroy(tp);
runq->proc_amount--;
}
void
KERNEL_page_fault(struct PageTableEntry *pt, size_t page_no)
{
printf("[kernel:page_fault] Handling %d started\n", page_no);
if (first_free_page) {
free_pages_cnt--;
printf("[kernel:page_fault] Found free page #%d, using it\n", first_free_page->ppn);
pt[page_no].ppn = first_free_page->ppn;
pt[page_no].p = 1;
first_free_page->busy_flag = 1;
first_free_page->pt = pt;
first_free_page->pt_index = page_no;
// ---- free list -> busy list ----
struct PhysPage *this_page = first_free_page;
if (first_free_page->next != first_free_page) {
// reconnect free list items together
first_free_page->prev->next = first_free_page->next;
first_free_page->next->prev = first_free_page->prev;
first_free_page = first_free_page->next;
} else {
// clear list if it only had this page
first_free_page = NULL;
}
if (!first_busy_page) {
// initialize the busy list with this page
first_busy_page = this_page;
first_busy_page->prev = first_busy_page;
first_busy_page->next = first_busy_page;
} else {
// insert this page at the end of the list
this_page->next = first_busy_page;
this_page->prev = first_busy_page->prev;
this_page->prev->next = this_page;
this_page->next->prev = this_page;
}
busy_pages_cnt++;
} else {
printf("[kernel:page_fault] No free pages available, trying to swap...\n");
#if PAGE_REPLACEMENT_ALGORITHM == 1
printf("[kernel:page_fault:random] Selected physical page #%d for replacement\n", first_busy_page->ppn);
// clear presence 'bit' from old PTE
first_busy_page->pt[first_busy_page->pt_index].p = 0;
// update physical page data
first_busy_page->pt = pt;
first_busy_page->pt_index = page_no;
// update PTE data
pt[page_no].p = 1;
pt[page_no].ppn = first_busy_page->ppn;
// move hand to next physical page
first_busy_page = first_busy_page->next;
printf("[kernel:page_fault:random] Auto-advanced the list of busy pages to ppn %d\n", first_busy_page->ppn);
#elif PAGE_REPLACEMENT_ALGORITHM == 2
// first pass (no reference flag + out of time window)
for (size_t i = 0; i < PHYSICAL_PAGE_AMOUNT; i++) {
if (first_busy_page->pt[first_busy_page->pt_index].r) {
printf("[kernel:page_fault:wsclock] ppn %d: r = 1, time %d -> %d\n",
first_busy_page->ppn, first_busy_page->last_accessed, system_time);
first_busy_page->last_accessed = system_time;
first_busy_page->pt[first_busy_page->pt_index].r = 0;
first_busy_page = first_busy_page->next;
} else if ((system_time - first_busy_page->last_accessed) > WSCLOCK_TIME_WINDOW) {
printf("[kernel:page_fault:wsclock] ppn %d: r = 0, time_window = %d (> %d), using it\n",
first_busy_page->ppn, system_time - first_busy_page->last_accessed, WSCLOCK_TIME_WINDOW);
first_busy_page->pt[first_busy_page->pt_index].p = 0;
first_busy_page->pt = pt;
first_busy_page->pt_index = page_no;
pt[page_no].p = 1;
pt[page_no].ppn = first_busy_page->ppn;
first_busy_page = first_busy_page->next;
goto page_replacement_resolved;
} else {
printf("[kernel:page_fault:wsclock] ppn %d: r = 0, time_window = %d (<= %d), still active\n",
first_busy_page->ppn, system_time - first_busy_page->last_accessed, WSCLOCK_TIME_WINDOW);
first_busy_page = first_busy_page->next;
}
}
// fallback to random
printf("[kernel:page_fault:wsclock] Failed to find non-active page, randomly selecting ppn %d\n", first_busy_page->ppn);
// clear presence 'bit' from old PTE
first_busy_page->pt[first_busy_page->pt_index].p = 0;
// update physical page data
first_busy_page->pt = pt;
first_busy_page->pt_index = page_no;
// update PTE data
pt[page_no].p = 1;
pt[page_no].ppn = first_busy_page->ppn;
// move hand to next physical page
first_busy_page = first_busy_page->next;
printf("[kernel:page_fault:wsclock] Auto-advanced the list of busy pages to ppn %d\n", first_busy_page->ppn);
page_replacement_resolved:
#endif
}
#if SANITY_CHECK_ENABLED == 1
KERNEL_sanity_check_memory_lists();
#endif
}
void KERNEL_update_job(size_t page_amount)
{
if (first_busy_page == NULL) {
printf("[kernel:update_job] No pages to update\n");
return;
}
struct PhysPage *starting_page = first_busy_page;
for (size_t i = 0; i < page_amount; i++) {
if (first_busy_page->pt[first_busy_page->pt_index].r) {
printf("[kernel:update_job] ppn %d updated time %d -> %d\n", first_busy_page->ppn, first_busy_page->last_accessed, system_time);
first_busy_page->pt[first_busy_page->pt_index].r = 0;
first_busy_page->last_accessed = system_time;
} else {
printf("[kernel:update_job] ppn %d is up-to-date (time = %d)\n", first_busy_page->ppn, first_busy_page->last_accessed);
}
first_busy_page = first_busy_page->next;
if (first_busy_page == starting_page) {
printf("[kernel:update_job] Looped around the busy page list, exiting after %d iterations\n", i);
break;
}
}
}
void
KERNEL_sanity_check_memory_lists(void)
{
size_t ppns_free[PHYSICAL_PAGE_AMOUNT] = {0};
struct PhysPage *p = first_free_page;
struct PhysPage *cp = first_free_page;
if (!first_free_page) {
#if VERBOSE_SANITY_CHECK >= 1
printf("[kernel:sanity_check_memory_lists] Free page list is empty, skipping it\n");
#endif
} else {
do {
if (!cp)
printf("[kernel:sanity_check_memory_lists] Free page is NULL, circular list is corrupted\n");
if (cp->busy_flag)
printf("[kernel:sanity_check_memory_lists] Free page #%d with set busy flag\n", cp->ppn);
ppns_free[cp->ppn] += 1;
#if VERBOSE_SANITY_CHECK >= 2
printf("[kernel:sanity_check_memory_lists] Processed free page #%d\n", cp->ppn);
#endif
if (ppns_free[cp->ppn] > 1)
printf("[kernel:sanity_check_memory_lists] Free page #%d listed more than once (%d times)\n", cp->ppn, ppns_free[cp->ppn]);
cp = cp->next;
} while (cp != p);
}
size_t ppns_busy[PHYSICAL_PAGE_AMOUNT] = {0};
p = first_busy_page;
cp = first_busy_page;
if (!first_busy_page) {
#if VERBOSE_SANITY_CHECK >= 1
printf("[kernel:sanity_check_memory_lists] Busy page list is empty, skipping it\n");
#endif
} else {
do {
if (!cp)
printf("[kernel:sanity_check_memory_lists] Busy page is NULL, circular list is corrupted\n");
if (!cp->busy_flag)
printf("[kernel:sanity_check_memory_lists] Busy page #%d with clear busy flag\n", cp->ppn);
ppns_busy[cp->ppn] += 1;
#if VERBOSE_SANITY_CHECK >= 2
printf("[kernel:sanity_check_memory_lists] Processed busy page #%d\n", cp->ppn);
#endif
if (ppns_busy[cp->ppn] > 1)
printf("[kernel:sanity_check_memory_lists] Busy page #%d listed more than once (%d times)\n", cp->ppn, ppns_busy[cp->ppn]);
cp = cp->next;
} while (cp != p);
}
for (size_t i = 0; i < PHYSICAL_PAGE_AMOUNT; i++) {
if (ppns_free[i] >= 1 && ppns_busy[i] >= 1)
printf("[kernel:sanity_check_memory_lists] Page #%d is listed in both lists (%d times in total)\n", i, ppns_busy[i] + ppns_free[i]);
}
}