1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
@@ -63,6 +63,7 @@ void ramdiskrw(struct buf*); void* kalloc(void); void kfree(void *); void kinit(void); +void incrfcount(void*); // log.c void initlog(int, struct superblock*); @@ -145,6 +146,8 @@ void trapinit(void); void trapinithart(void); extern struct spinlock tickslock; void usertrapret(void); +int iscowpage(pagetable_t, uint64); +int cowfault(pagetable_t, uint64); // uart.c void uartinit(void); @@ -170,6 +173,7 @@ uint64 walkaddr(pagetable_t, uint64); int copyout(pagetable_t, uint64, char *, uint64); int copyin(pagetable_t, char *, uint64, uint64); int copyinstr(pagetable_t, char *, uint64, uint64); +pte_t* walk(pagetable_t, uint64, int); // plic.c void plicinit(void);
@@ -14,6 +14,11 @@ void freerange(void *pa_start, void *pa_end); extern char end[]; // first address after kernel. // defined by kernel.ld. +#define PA2RFIDX(pa) ((((uint64)pa) - KERNBASE) / PGSIZE) + +int rfcount[(PHYSTOP - KERNBASE) / PGSIZE]; +struct spinlock rflock; + struct run { struct run *next; }; @@ -27,6 +32,7 @@ void kinit() { initlock(&kmem.lock, "kmem"); + initlock(&rflock, "rflock"); freerange(end, (void*)PHYSTOP); } @@ -51,15 +57,17 @@ kfree(void *pa) if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) panic("kfree"); - // Fill with junk to catch dangling refs. - memset(pa, 1, PGSIZE); - - r = (struct run*)pa; - - acquire(&kmem.lock); - r->next = kmem.freelist; - kmem.freelist = r; - release(&kmem.lock); + acquire(&rflock); + if(--rfcount[PA2RFIDX(pa)] <= 0){ + memset(pa, 1, PGSIZE); + // Fill with junk to catch dangling refs. + r = (struct run*)pa; + acquire(&kmem.lock); + r->next = kmem.freelist; + kmem.freelist = r; + release(&kmem.lock); + } + release(&rflock); } // Allocate one 4096-byte page of physical memory. @@ -76,7 +84,15 @@ kalloc(void) kmem.freelist = r->next; release(&kmem.lock); - if(r) + if(r) { memset((char*)r, 5, PGSIZE); // fill with junk + rfcount[PA2RFIDX(r)] = 1; + } return (void*)r; } + +void incrfcount(void* pa){ + acquire(&rflock); + ++rfcount[PA2RFIDX(pa)]; + release(&rflock); +} \ No newline at end of file
@@ -343,6 +343,8 @@ sfence_vma() #define PTE_W (1L << 2) #define PTE_X (1L << 3) #define PTE_U (1L << 4) // 1 -> user can access +#define PTE_COW (1L << 8) // 1 -> is a COW page + // shift a physical address to the right place for a PTE. #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
@@ -29,6 +29,42 @@ trapinithart(void) w_stvec((uint64)kernelvec); } + +int iscowpage(pagetable_t pgtbl, uint64 va) { + if (va >= MAXVA) return 0; + pte_t *pte = walk(pgtbl, va, 0); + if (pte == 0) return 0; + if ((*pte & PTE_V) == 0) return 0; + if ((*pte & PTE_U) == 0) return 0; + return *pte & PTE_COW; +} + +int cowfault(pagetable_t pagetable, uint64 va) { + uint64 va0 = PGROUNDDOWN(va); + pte_t* pte; + if((pte = walk(pagetable, va0, 0)) == 0) return -1; + + uint64 flags = PTE_FLAGS(*pte); + uint64 pa0 = PTE2PA(*pte); + + flags &= (~PTE_COW); // clear COW bit + flags |= PTE_W; // set write bit + + uint64 mem; + if ((mem = (uint64)kalloc()) == 0) return -1; + memmove((void *)mem, (void *)pa0, PGSIZE); + + // remove old PTE + uvmunmap(pagetable, va0, 1, 1); + + // install new PTE + if(mappages(pagetable, va0, PGSIZE, mem, flags) < 0){ + kfree((void *)mem); + return -1; + } + return 0; +} + // // handle an interrupt, exception, or system call from user space. // called from trampoline.S @@ -67,7 +103,12 @@ usertrap(void) syscall(); } else if((which_dev = devintr()) != 0){ // ok - } else { + } else if (r_scause() == 15 && iscowpage(p->pagetable, r_stval())) { + if (cowfault(p->pagetable, r_stval()) < 0) { + p->killed = 1; + } + } + else { printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); p->killed = 1;
@@ -303,22 +303,20 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) pte_t *pte; uint64 pa, i; uint flags; - char *mem; for(i = 0; i < sz; i += PGSIZE){ if((pte = walk(old, i, 0)) == 0) panic("uvmcopy: pte should exist"); if((*pte & PTE_V) == 0) panic("uvmcopy: page not present"); + *pte &= ~PTE_W; // set write bit + *pte |= PTE_COW; // clear COW bit pa = PTE2PA(*pte); flags = PTE_FLAGS(*pte); - if((mem = kalloc()) == 0) - goto err; - memmove(mem, (char*)pa, PGSIZE); - if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){ - kfree(mem); + if(mappages(new, i, PGSIZE, pa, flags) != 0){ goto err; } + incrfcount((void*)pa); // increment reference count to pa } return 0; @@ -350,6 +348,9 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len) while(len > 0){ va0 = PGROUNDDOWN(dstva); + if (iscowpage(pagetable, va0)) { + cowfault(pagetable, va0); + } pa0 = walkaddr(pagetable, va0); if(pa0 == 0) return -1;
new file mode 100644
@@ -0,0 +1 @@ +20
|