cowtest passed
This commit is contained in:
@@ -60,9 +60,13 @@ void ramdiskintr(void);
|
||||
void ramdiskrw(struct buf*);
|
||||
|
||||
// kalloc.c
|
||||
#define PLUS 1
|
||||
#define SUB 0
|
||||
void* kalloc(void);
|
||||
void kfree(void *);
|
||||
void kinit(void);
|
||||
void reference_operation(void* pa, int op);
|
||||
void* cowcopy_pa(void* pa);
|
||||
|
||||
// log.c
|
||||
void initlog(int, struct superblock*);
|
||||
@@ -173,6 +177,8 @@ 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);
|
||||
int iscowpage(uint64 va);
|
||||
void startcowcopy(uint64 va);
|
||||
|
||||
// plic.c
|
||||
void plicinit(void);
|
||||
|
||||
@@ -8,12 +8,23 @@
|
||||
#include "spinlock.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
void freerange(void *pa_start, void *pa_end);
|
||||
uint64 getRefIdx(uint64 pa);
|
||||
|
||||
struct spinlock reference_lock; // 加锁
|
||||
int pm_reference[(PHYSTOP - KERNBASE)/PGSIZE]; // 记录物理页的引用计数
|
||||
|
||||
extern char end[]; // first address after kernel.
|
||||
// defined by kernel.ld.
|
||||
|
||||
// va映射为idx
|
||||
uint64
|
||||
getRefIdx(uint64 pa)
|
||||
{
|
||||
return (pa-KERNBASE)/PGSIZE;
|
||||
}
|
||||
|
||||
|
||||
struct run {
|
||||
struct run *next;
|
||||
};
|
||||
@@ -27,6 +38,7 @@ void
|
||||
kinit()
|
||||
{
|
||||
initlock(&kmem.lock, "kmem");
|
||||
initlock(&reference_lock, "pm_reference");
|
||||
freerange(end, (void*)PHYSTOP);
|
||||
}
|
||||
|
||||
@@ -51,15 +63,19 @@ 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);
|
||||
acquire(&reference_lock);
|
||||
pm_reference[getRefIdx((uint64)pa)] --;
|
||||
if(pm_reference[getRefIdx((uint64)pa)] <= 0){
|
||||
// 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);
|
||||
}
|
||||
|
||||
r = (struct run*)pa;
|
||||
|
||||
acquire(&kmem.lock);
|
||||
r->next = kmem.freelist;
|
||||
kmem.freelist = r;
|
||||
release(&kmem.lock);
|
||||
release(&reference_lock);
|
||||
}
|
||||
|
||||
// Allocate one 4096-byte page of physical memory.
|
||||
@@ -76,7 +92,43 @@ kalloc(void)
|
||||
kmem.freelist = r->next;
|
||||
release(&kmem.lock);
|
||||
|
||||
if(r)
|
||||
if(r){
|
||||
memset((char*)r, 5, PGSIZE); // fill with junk
|
||||
pm_reference[getRefIdx((uint64) r)] = 1;
|
||||
}
|
||||
return (void*)r;
|
||||
}
|
||||
|
||||
// reference操作
|
||||
void
|
||||
reference_operation(void* pa, int op)
|
||||
{
|
||||
acquire(&reference_lock);
|
||||
if(op) pm_reference[getRefIdx((uint64)pa)]++;
|
||||
else pm_reference[getRefIdx((uint64)pa)]--;
|
||||
release(&reference_lock);
|
||||
}
|
||||
|
||||
void*
|
||||
cowcopy_pa(void* pa){
|
||||
acquire(&reference_lock);
|
||||
if(pm_reference[getRefIdx((uint64)pa)] <= 1){
|
||||
release(&reference_lock);
|
||||
return pa;
|
||||
}
|
||||
|
||||
char* new = kalloc();
|
||||
if(new == 0){
|
||||
release(&reference_lock);
|
||||
panic("out of memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memmove((void*)new, pa, PGSIZE);
|
||||
|
||||
// 变更引用计数
|
||||
pm_reference[getRefIdx((uint64)pa)] --;
|
||||
release(&reference_lock);
|
||||
return (void*)new;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "spinlock.h"
|
||||
// Saved registers for kernel context switches.
|
||||
struct context {
|
||||
uint64 ra;
|
||||
|
||||
@@ -362,6 +362,7 @@ typedef uint64 *pagetable_t; // 512 PTEs
|
||||
#define PTE_W (1L << 2)
|
||||
#define PTE_X (1L << 3)
|
||||
#define PTE_U (1L << 4) // user can access
|
||||
#define PTE_COW (1L << 8)
|
||||
|
||||
// shift a physical address to the right place for a PTE.
|
||||
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Mutual exclusion lock.
|
||||
struct spinlock {
|
||||
uint locked; // Is the lock held?
|
||||
uint64 locked; // Is the lock held?
|
||||
|
||||
// For debugging:
|
||||
char *name; // Name of lock.
|
||||
|
||||
@@ -67,6 +67,8 @@ usertrap(void)
|
||||
syscall();
|
||||
} else if((which_dev = devintr()) != 0){
|
||||
// ok
|
||||
} else if((r_scause() == 15 || r_scause() == 13) && iscowpage(r_stval())){
|
||||
startcowcopy(r_stval());
|
||||
} else {
|
||||
printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
|
||||
printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
|
||||
|
||||
123
kernel/vm.c
123
kernel/vm.c
@@ -5,6 +5,8 @@
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
#include "fs.h"
|
||||
#include "proc.h"
|
||||
#include "spinlock.h"
|
||||
|
||||
/*
|
||||
* the kernel's page table.
|
||||
@@ -309,13 +311,14 @@ uvmfree(pagetable_t pagetable, uint64 sz)
|
||||
// physical memory.
|
||||
// returns 0 on success, -1 on failure.
|
||||
// frees any allocated pages on failure.
|
||||
// 如果父进程的一个 PTE 本身就是不可写的,则没有必要PTE_COW
|
||||
// 因为子进程对这块物理页也一定是不可写的,那么就没必要复制,因此只有当父进程的 PTE_W 标记时,才去附加 PTE_COW。
|
||||
int
|
||||
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)
|
||||
@@ -323,14 +326,20 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
|
||||
if((*pte & PTE_V) == 0)
|
||||
panic("uvmcopy: page not present");
|
||||
pa = PTE2PA(*pte);
|
||||
|
||||
// lab5: Copy on write
|
||||
// father
|
||||
// 如果该页本身就不可写,那么子进程肯定也不可写,不用对其考虑COW
|
||||
if(*pte & PTE_W){
|
||||
*pte &= ~PTE_W;
|
||||
*pte |= PTE_COW;
|
||||
}
|
||||
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);
|
||||
// child
|
||||
if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){
|
||||
goto err;
|
||||
}
|
||||
reference_operation((void*)pa, PLUS);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -338,7 +347,6 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
|
||||
uvmunmap(new, 0, i / PGSIZE, 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// mark a PTE invalid for user access.
|
||||
// used by exec for the user stack guard page.
|
||||
void
|
||||
@@ -352,35 +360,6 @@ uvmclear(pagetable_t pagetable, uint64 va)
|
||||
*pte &= ~PTE_U;
|
||||
}
|
||||
|
||||
// Copy from kernel to user.
|
||||
// Copy len bytes from src to virtual address dstva in a given page table.
|
||||
// Return 0 on success, -1 on error.
|
||||
int
|
||||
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
||||
{
|
||||
uint64 n, va0, pa0;
|
||||
pte_t *pte;
|
||||
|
||||
while(len > 0){
|
||||
va0 = PGROUNDDOWN(dstva);
|
||||
if(va0 >= MAXVA)
|
||||
return -1;
|
||||
pte = walk(pagetable, va0, 0);
|
||||
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
|
||||
(*pte & PTE_W) == 0)
|
||||
return -1;
|
||||
pa0 = PTE2PA(*pte);
|
||||
n = PGSIZE - (dstva - va0);
|
||||
if(n > len)
|
||||
n = len;
|
||||
memmove((void *)(pa0 + (dstva - va0)), src, n);
|
||||
|
||||
len -= n;
|
||||
src += n;
|
||||
dstva = va0 + PGSIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy from user to kernel.
|
||||
// Copy len bytes to dst from virtual address srcva in a given page table.
|
||||
@@ -449,3 +428,75 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否为cowpage
|
||||
int
|
||||
iscowpage(uint64 va){
|
||||
struct proc* p = myproc();
|
||||
va = PGROUNDDOWN((uint64)va);
|
||||
if(va >= MAXVA) // 要在walk之前
|
||||
return 0;
|
||||
pte_t* pte = walk(p->pagetable,va,0);
|
||||
if(pte == 0)
|
||||
return 0;
|
||||
if((va < p->sz)&& (*pte & PTE_COW) && (*pte & PTE_V))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cow 的主要操作,将该 PTE 重新映射至新的物理内存,并修改flag
|
||||
void
|
||||
startcowcopy(uint64 va){
|
||||
struct proc* p = myproc();
|
||||
va = PGROUNDDOWN((uint64)va);
|
||||
pte_t* pte = walk(p->pagetable,va,0);
|
||||
uint64 pa = PTE2PA(*pte);
|
||||
|
||||
void* new = cowcopy_pa((void*)pa);
|
||||
if((uint64)new == 0){
|
||||
panic("cowcopy_pa err\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
uint64 flags = (PTE_FLAGS(*pte) | PTE_W) & (~PTE_COW);
|
||||
// 确保PTE_U没有丢失
|
||||
if (!(flags & PTE_U)) flags |= PTE_U;
|
||||
uvmunmap(p->pagetable, va, 1, 0); // 不包含kfree,因为ref--在cowcopy_pa中已经进行了
|
||||
|
||||
if(mappages(p->pagetable, va, PGSIZE, (uint64)new, flags) == -1){
|
||||
kfree(new);
|
||||
panic("cow mappages failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from kernel to user.
|
||||
// Copy len bytes from src to virtual address dstva in a given page table.
|
||||
// Return 0 on success, -1 on error.
|
||||
int
|
||||
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
||||
{
|
||||
uint64 n, va0, pa0;
|
||||
|
||||
while(len > 0){
|
||||
va0 = PGROUNDDOWN(dstva);
|
||||
pa0 = walkaddr(pagetable, va0);
|
||||
if(pa0 == 0)
|
||||
return -1;
|
||||
|
||||
if(iscowpage(va0)){
|
||||
startcowcopy(va0);
|
||||
pa0 = walkaddr(pagetable, va0);
|
||||
}
|
||||
|
||||
n = PGSIZE - (dstva - va0);
|
||||
if(n > len)
|
||||
n = len;
|
||||
memmove((void *)(pa0 + (dstva - va0)), src, n);
|
||||
|
||||
len -= n;
|
||||
src += n;
|
||||
dstva = va0 + PGSIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user