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