Your task is to implement copy-on-write fork in the xv6 kernel. You are done if your modified kernel executes both the cowtest and ‘usertests -q’ programs successfully.
struct { int reference_count[PHYSTOP / PGSIZE]; // Array of process number using this page struct spinlock lock; } ppages;
1 2 3 4 5 6 7 8 9 10 11
// Initialize the physical pages tracking array void initppages() { // Assume all pages are used by the kernel initially, then freed in function freerange for (int i = 0; i < sizeof(ppages.reference_count) / sizeof(int); i++) { ppages.reference_count[i] = 1; } // Initialize the ppages lock initlock(&ppages.lock, "ppages"); }
void * kalloc(void) { ... if(r) { memset((char*)r, 5, PGSIZE); // fill with junk ppages_increase((uint64)r); // Increase reference count for the allocated page } ... }
void kfree(void *pa) { ... acquire(&ppages.lock); int count = get_reference_count((uint64)pa); if(count <= 0) { panic("kfree: reference count is already zero"); } else if(count > 1) { // Decrement the reference count for the physical page ppages.reference_count[(uint64)pa / PGSIZE]--; release(&ppages.lock); return; // Do not free if still in use } // count is 1 -- the page should be freed normally ppages.reference_count[(uint64)pa / PGSIZE] = 0; // Set reference count to zero release(&ppages.lock); ... }
acquire(&ppages.lock); if((count = get_reference_count(pa)) <= 0) { release(&ppages.lock); panic("cow_handler: reference count is zero"); } else if(count == 1) { // the page is already private, no need to copy // just modify the flags flags = (flags & ~PTE_C) | PTE_W; // Clear the copy-on-write flag and set writable *pte |= flags; release(&ppages.lock); } else { // The page is shared, we need to create a private copy
// Initially, release the ppages lock to avoid deadlock // because kalloc() need to acquire ppages.lock !!!!! release(&ppages.lock);
// Allocate a new page of memory if((mem = kalloc()) == 0) { return -1; // Allocation failed } memmove(mem, (void*)pa, PGSIZE); // Copy the contents of the original page to the new page flags = (flags & ~PTE_C) | PTE_W; // Clear the copy-on-write flag and set writable *pte = PA2PTE(mem) | flags; // Update the page table entry to point to the new page
kfree((void*)pa); // Free the original page } return 0; }