#include #include #include #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) { if (runq->proc_amount >= runq->max_procs) return; struct Process *new_p = Process(runq->next_proc_id, max_page_accesses, total_pages_owned); 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; } KERNEL_sanity_check_memory_lists(); if ((cp == starting_page) || (!cp)) break; } free(tp); } 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 } KERNEL_sanity_check_memory_lists(); } 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]); } }