intcopy_on_write(pagetable_t pagetable, uint64 va) { uint64 a = PGROUNDDOWN(va); pte_t *pte = walk(pagetable, a, 0); uint64 old_pa = PTE2PA(*pte); if(ref[PXIDX(old_pa)] == 1) // this is the last reference to old_pa, so there is no need to make a copy of it { *pte &= (~PTE_COW); *pte |= PTE_W; return1; } uint64 flags = PTE_FLAGS(*pte); if(!(flags & PTE_COW)) { printf("copy_on_write: not a copy-on-write page\n"); return0; } uint64 pa = (uint64)kalloc(); if(pa == 0) { printf("copy_on_write: no free page\n"); return0; } uint64 perm = (flags | PTE_W) & (~PTE_COW); memmove((void *)pa, (void *)old_pa, PGSIZE); uvmunmap(pagetable, a, 1, 1);// unmap old mappings and map it to a new one if(mappages(pagetable, a, PGSIZE, pa, perm) != 0) { kfree((void*)pa); printf("copy_on_write: mappages failed\n"); return0; } ref[PXIDX(pa)]++; return1; }
void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { uint64 a; pte_t *pte;
if((va % PGSIZE) != 0) panic("uvmunmap: not aligned");
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ if((pte = walk(pagetable, a, 0)) == 0) panic("uvmunmap: walk"); if((*pte & PTE_V) == 0) panic("uvmunmap: not mapped"); if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); uint64 pa = PTE2PA(*pte); // no matter we free the physical page or not, it is a must to decrement the reference count ref[PXIDX(pa)]--; if(do_free && !ref[PXIDX(pa)]) kfree((void*)pa); *pte = 0; } }