Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
X
xv6-public
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
问题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
银宸时代
OS Lab Group
奖励实验
xv6-public
提交
62dddc8a
提交
62dddc8a
10月 27, 2011
创建
作者:
Silas Boyd-Wickizer
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Remove some dead code.
上级
cbecab91
隐藏空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
1 行增加
和
1475 行删除
+1
-1475
ide.c
ide.c
+1
-8
proc.c
proc.c
+0
-680
vm.c
vm.c
+0
-787
没有找到文件。
ide.c
浏览文件 @
62dddc8a
#include "types.h"
#include "kernel.h"
void
iderw
(
struct
buf
*
b
)
{
panic
(
"iderw"
);
}
#if 0
// Simple PIO-based (non-DMA) IDE driver code.
#include "types.h"
...
...
@@ -169,4 +162,4 @@ iderw(struct buf *b)
release
(
&
idelock
);
}
#endif
proc.c
浏览文件 @
62dddc8a
...
...
@@ -725,683 +725,3 @@ wait(void)
release
(
&
myproc
()
->
lock
);
}
}
#if 0
extern void forkret(void);
extern void trapret(void);
struct kstack_tag kstack_tag[NCPU];
enum { sched_debug = 0 };
void
pinit(void)
{
int c;
nspid = nsalloc(0);
if (nspid == 0)
panic("pinit");
nsrunq = nsalloc(1);
if (nsrunq == 0)
panic("pinit runq");
for (c = 0; c < NCPU; c++)
idle[c] = 1;
}
//PAGEBREAK: 32
// Look in the process table for an UNUSED proc.
// If found, change state to EMBRYO and initialize
// state required to run in the kernel.
// Otherwise return 0.
static struct proc*
allocproc(void)
{
struct proc *p;
char *sp;
p = kmalloc(sizeof(struct proc));
if (p == 0) return 0;
memset(p, 0, sizeof(*p));
p->state = EMBRYO;
p->pid = ns_allockey(nspid);
p->epoch = INF;
p->cpuid = cpu->id;
p->on_runq = -1;
p->cpu_pin = 0;
p->mtrace_stacks.curr = -1;
snprintf(p->lockname, sizeof(p->lockname), "cv:proc:%d", p->pid);
initlock(&p->lock, p->lockname+3);
initcondvar(&p->cv, p->lockname);
if (ns_insert(nspid, KI(p->pid), (void *) p) < 0)
panic("allocproc: ns_insert");
// Allocate kernel stack if possible.
if((p->kstack = kalloc()) == 0){
if (ns_remove(nspid, KI(p->pid), p) == 0)
panic("allocproc: ns_remove");
rcu_delayed(p, kmfree);
return 0;
}
sp = p->kstack + KSTACKSIZE;
// Leave room for trap frame.
sp -= sizeof *p->tf;
p->tf = (struct trapframe*)sp;
// Set up new context to start executing at forkret,
// which returns to trapret.
sp -= 8;
*(uint*)sp = (uint)trapret;
sp -= sizeof *p->context;
p->context = (struct context*)sp;
memset(p->context, 0, sizeof *p->context);
p->context->eip = (uint)forkret;
return p;
}
// Mark a process RUNNABLE and add it to the runq
// of its cpu. Caller must hold p->lock so that
// some other core doesn't start running the
// process before the caller has finished setting
// the process up, and to cope with racing callers
// e.g. two wakeups on same process. and to
// allow atomic addrun(); sched();
void
addrun(struct proc *p)
{
#if SPINLOCK_DEBUG
if(!holding(&p->lock))
panic("addrun no p->lock");
#endif
if (p->on_runq >= 0)
panic("addrun on runq already");
ns_insert(nsrunq, KI(p->cpuid), p);
p->on_runq = p->cpuid;
}
void
delrun(struct proc *p)
{
#if SPINLOCK_DEBUG
if(!holding(&p->lock))
panic("delrun no p->lock");
#endif
if (p->on_runq < 0)
panic("delrun not on runq");
if (ns_remove(nsrunq, KI(p->on_runq), p) == 0)
panic("delrun: ns_remove");
p->on_runq = -1;
}
//PAGEBREAK: 32
// Set up first user process.
void
userinit(void)
{
struct proc *p;
extern char _binary_initcode_start[], _binary_initcode_size[];
p = allocproc();
initproc = p;
if((p->vmap = vmap_alloc()) == 0)
panic("userinit: out of vmaps?");
struct vmnode *vmn = vmn_allocpg(PGROUNDUP((int)_binary_initcode_size) / PGSIZE);
if(vmn == 0)
panic("userinit: vmn_allocpg");
if(vmap_insert(p->vmap, vmn, 0) < 0)
panic("userinit: vmap_insert");
if(copyout(p->vmap, 0, _binary_initcode_start, (int)_binary_initcode_size) < 0)
panic("userinit: copyout");
memset(p->tf, 0, sizeof(*p->tf));
p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
p->tf->es = p->tf->ds;
p->tf->ss = p->tf->ds;
p->tf->eflags = FL_IF;
p->tf->esp = PGSIZE;
p->tf->eip = 0; // beginning of initcode.S
safestrcpy(p->name, "initcode", sizeof(p->name));
p->cwd = 0; // forkret will fix in the process's context
acquire(&p->lock);
addrun(p);
p->state = RUNNABLE;
release(&p->lock);
for (uint c = 0; c < NCPU; c++) {
struct proc *rcup = allocproc();
rcup->vmap = vmap_alloc();
rcup->context->eip = (uint) rcu_gc_worker;
rcup->cwd = 0;
rcup->cpuid = c;
rcup->cpu_pin = 1;
acquire(&rcup->lock);
rcup->state = RUNNABLE;
addrun(rcup);
release(&rcup->lock);
}
}
// Grow/shrink current process's memory by n bytes.
// Growing may allocate vmas and physical memory,
// but avoids interfering with any existing vma.
// Assumes vmas around proc->brk are part of the growable heap.
// Shrinking just decreases proc->brk; doesn't deallocate.
// Return 0 on success, -1 on failure.
int
growproc(int n)
{
struct vmap *m = proc->vmap;
if(n < 0 && 0 - n <= proc->brk){
proc->brk += n;
return 0;
}
if(n < 0 || n > USERTOP || proc->brk + n > USERTOP)
return -1;
acquire(&m->lock);
// find first unallocated address in brk..brk+n
uint newstart = proc->brk;
uint newn = n;
rcu_begin_read();
while(newn > 0){
struct vma *e = vmap_lookup(m, newstart, 1);
if(e == 0)
break;
if(e->va_end >= newstart + newn){
newstart += newn;
newn = 0;
break;
}
newn -= e->va_end - newstart;
newstart = e->va_end;
}
rcu_end_read();
if(newn <= 0){
// no need to allocate
proc->brk += n;
release(&m->lock);
switchuvm(proc);
return 0;
}
// is there space for newstart..newstart+newn?
if(vmap_lookup(m, newstart, newn) != 0){
cprintf("growproc: not enough room in address space; brk %d n %d\n",
proc->brk, n);
return -1;
}
// would the newly allocated region abut the next-higher
// vma? we can't allow that, since then a future sbrk()
// would start to use the next region (e.g. the stack).
if(vmap_lookup(m, PGROUNDUP(newstart+newn), 1) != 0){
cprintf("growproc: would abut next vma; brk %d n %d\n",
proc->brk, n);
return -1;
}
struct vmnode *vmn = vmn_allocpg(PGROUNDUP(newn) / PGSIZE);
if(vmn == 0){
release(&m->lock);
cprintf("growproc: vmn_allocpg failed\n");
return -1;
}
release(&m->lock); // XXX
if(vmap_insert(m, vmn, newstart) < 0){
vmn_free(vmn);
cprintf("growproc: vmap_insert failed\n");
return -1;
}
proc->brk += n;
switchuvm(proc);
return 0;
}
// Create a new process copying p as the parent.
// Sets up stack to return as if from system call.
// Caller must set state of returned proc to RUNNABLE.
int
fork(int flags)
{
int i, pid;
struct proc *np;
uint cow = 1;
// cprintf("%d: fork\n", proc->pid);
// Allocate process.
if((np = allocproc()) == 0)
return -1;
if(flags == 0) {
// Copy process state from p.
if((np->vmap = vmap_copy(proc->vmap, cow)) == 0){
kfree(np->kstack);
np->kstack = 0;
np->state = UNUSED;
if (ns_remove(nspid, KI(np->pid), np) == 0)
panic("fork: ns_remove");
rcu_delayed(np, kmfree);
return -1;
}
} else {
np->vmap = proc->vmap;
__sync_fetch_and_add(&np->vmap->ref, 1);
}
np->brk = proc->brk;
np->parent = proc;
*np->tf = *proc->tf;
// Clear %eax so that fork returns 0 in the child.
np->tf->eax = 0;
for(i = 0; i < NOFILE; i++)
if(proc->ofile[i])
np->ofile[i] = filedup(proc->ofile[i]);
np->cwd = idup(proc->cwd);
pid = np->pid;
safestrcpy(np->name, proc->name, sizeof(proc->name));
acquire(&proc->lock);
SLIST_INSERT_HEAD(&proc->childq, np, child_next);
release(&proc->lock);
acquire(&np->lock);
addrun(np);
np->state = RUNNABLE;
release(&np->lock);
migrate(np);
// cprintf("%d: fork done (pid %d)\n", proc->pid, pid);
return pid;
}
// Exit the current process. Does not return.
// An exited process remains in the zombie state
// until its parent calls wait() to find out it exited.
void
exit(void)
{
struct proc *p, *np;
int fd;
int wakeupinit;
if(proc == initproc)
panic("init exiting");
// Close all open files.
for(fd = 0; fd < NOFILE; fd++){
if(proc->ofile[fd]){
fileclose(proc->ofile[fd]);
proc->ofile[fd] = 0;
}
}
iput(proc->cwd);
proc->cwd = 0;
// Pass abandoned children to init.
wakeupinit = 0;
SLIST_FOREACH_SAFE(p, &proc->childq, child_next, np) {
acquire(&p->lock);
p->parent = initproc;
if(p->state == ZOMBIE)
wakeupinit = 1;
SLIST_REMOVE(&proc->childq, p, proc, child_next);
release(&p->lock);
acquire(&initproc->lock);
SLIST_INSERT_HEAD(&initproc->childq, p, child_next);
release(&initproc->lock);
}
// Parent might be sleeping in wait().
acquire(&proc->lock);
cv_wakeup(&proc->parent->cv);
if (wakeupinit)
cv_wakeup(&initproc->cv);
// Jump into the scheduler, never to return.
proc->state = ZOMBIE;
sched();
panic("zombie exit");
}
// Wait for a child process to exit and return its pid.
// Return -1 if this process has no children.
int
wait(void)
{
struct proc *p, *np;
int havekids, pid;
for(;;){
// Scan children for ZOMBIEs
havekids = 0;
acquire(&proc->lock);
SLIST_FOREACH_SAFE(p, &proc->childq, child_next, np) {
havekids = 1;
acquire(&p->lock);
if(p->state == ZOMBIE){
release(&p->lock); // noone else better be trying to lock p
pid = p->pid;
SLIST_REMOVE(&proc->childq, p, proc, child_next);
release(&proc->lock);
kfree(p->kstack);
p->kstack = 0;
vmap_decref(p->vmap);
p->state = UNUSED;
if (ns_remove(nspid, KI(p->pid), p) == 0)
panic("wait: ns_remove");
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->killed = 0;
rcu_delayed(p, kmfree);
return pid;
}
release(&p->lock);
}
// No point waiting if we don't have any children.
if(!havekids || proc->killed){
release(&proc->lock);
return -1;
}
// Wait for children to exit. (See wakeup1 call in proc_exit.)
cv_sleep(&proc->cv, &proc->lock);
release(&proc->lock);
}
}
void
migrate(struct proc *p)
{
int c;
for (c = 0; c < NCPU; c++) {
if (c == cpu->id)
continue;
if (idle[c]) { // OK if there is a race
acquire(&p->lock);
if (p->state != RUNNABLE || p->cpu_pin) {
release(&p->lock);
continue;
}
if (sched_debug)
cprintf("cpu%d: migrate %d to %d\n", cpu->id, p->pid, c);
delrun(p);
p->curcycles = 0;
p->cpuid = c;
addrun(p);
idle[c] = 0;
if (p == proc) {
proc->state = RUNNABLE;
sched();
}
release(&p->lock);
return;
}
}
}
//PAGEBREAK: 42
// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns. It loops, doing:
// - choose a process to run
// - swtch to start running that process
// - eventually that process transfers control
// via swtch back to the scheduler.
static void *
choose_runnable(void *pp, void *arg)
{
struct proc *p = pp;
if (p->state == RUNNABLE)
return p;
return 0;
}
void
scheduler(void)
{
// allocate a fake PID for each scheduler thread
struct proc *schedp = allocproc();
if (!schedp)
panic("scheduler allocproc");
proc = schedp;
proc->cpu_pin = 1;
// Enabling mtrace calls in scheduler generates many mtrace_call_entrys.
// mtrace_call_set(1, cpu->id);
mtrace_kstack_start(scheduler, schedp);
for(;;){
// Enable interrupts on this processor.
sti();
struct proc *p = ns_enumerate_key(nsrunq, KI(cpu->id), choose_runnable, 0);
if (p) {
acquire(&p->lock);
if (p->state != RUNNABLE) {
release(&p->lock);
} else {
if (idle[cpu->id])
idle[cpu->id] = 0;
// Switch to chosen process. It is the process's job
// to release proc->lock and then reacquire it
// before jumping back to us.
proc = p;
switchuvm(p);
p->state = RUNNING;
p->tsc = rdtsc();
mtrace_kstack_pause(schedp);
if (p->context->eip != (uint)forkret &&
p->context->eip != (uint)rcu_gc_worker)
{
mtrace_kstack_resume(proc);
}
mtrace_call_set(1, cpu->id);
swtch(&cpu->scheduler, proc->context);
mtrace_kstack_resume(schedp);
mtrace_call_set(0, cpu->id);
switchkvm();
// Process is done running for now.
// It should have changed its p->state before coming back.
proc = schedp;
if (p->state != RUNNABLE)
delrun(p);
release(&p->lock);
}
} else {
if (steal()) {
if (idle[cpu->id])
idle[cpu->id] = 0;
} else {
if (!idle[cpu->id])
idle[cpu->id] = 1;
}
}
int now = ticks;
if (now - cpu->last_rcu_gc_ticks > 100) {
rcu_gc();
cpu->last_rcu_gc_ticks = now;
}
if (idle[cpu->id]) {
sti();
hlt();
}
}
}
// Enter scheduler. Must hold only proc->lock
// and have changed proc->state.
void
sched(void)
{
int intena;
#if SPINLOCK_DEBUG
if(!holding(&proc->lock))
panic("sched proc->lock");
#endif
if(cpu->ncli != 1)
panic("sched locks");
if(proc->state == RUNNING)
panic("sched running");
if(readeflags()&FL_IF)
panic("sched interruptible");
intena = cpu->intena;
proc->curcycles += rdtsc() - proc->tsc;
if (proc->state == ZOMBIE)
mtrace_kstack_stop(proc);
else
mtrace_kstack_pause(proc);
mtrace_call_set(0, cpu->id);
swtch(&proc->context, cpu->scheduler);
cpu->intena = intena;
}
// Give up the CPU for one scheduling round.
void
yield(void)
{
acquire(&proc->lock); //DOC: yieldlock
proc->state = RUNNABLE;
sched();
release(&proc->lock);
}
// A fork child's very first scheduling by scheduler()
// will swtch here. "Return" to user space.
void
forkret(void)
{
// Still holding proc->lock from scheduler.
release(&proc->lock);
// just for the first process. can't do it earlier
// b/c file system code needs a process context
// in which to call cv_sleep().
if(proc->cwd == 0) {
mtrace_kstack_start(forkret, proc);
proc->cwd = namei("/");
mtrace_kstack_stop(proc);
}
// Return to "caller", actually trapret (see allocproc).
}
// Kill the process with the given pid.
// Process won't exit until it returns
// to user space (see trap in trap.c).
int
kill(int pid)
{
struct proc *p;
p = (struct proc *) ns_lookup(nspid, KI(pid));
if (p == 0) {
panic("kill");
return -1;
}
acquire(&p->lock);
p->killed = 1;
if(p->state == SLEEPING){
// XXX
// we need to wake p up if it is cv_sleep()ing.
// can't change p from SLEEPING to RUNNABLE since that
// would make some condvar->waiters a dangling reference,
// and the non-zero p->cv_next will cause a future panic.
// can't call cv_wakeup(p->oncv) since that results in
// deadlock (addrun() acquires p->lock).
// can't release p->lock then call cv_wakeup() since the
// cv might be deallocated while we're using it
// (pipes dynamically allocate condvars).
}
release(&p->lock);
return 0;
}
void *procdump(void *vk, void *v, void *arg)
{
struct proc *p = (struct proc *) v;
static char *states[] = {
[UNUSED] "unused",
[EMBRYO] "embryo",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
char *state;
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
state = states[p->state];
else
state = "???";
cprintf("%d %s %s %d, ", p->pid, state, p->name, p->cpuid);
uint pc[10];
if(p->state == SLEEPING){
getcallerpcs((uint*)p->context->ebp+2, pc);
for(int i=0; i<10 && pc[i] != 0; i++)
cprintf(" %p", pc[i]);
}
cprintf("\n");
return 0;
}
//PAGEBREAK: 36
// Print a process listing to console. For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdumpall(void)
{
ns_enumerate(nspid, procdump, 0);
}
#endif
vm.c
浏览文件 @
62dddc8a
...
...
@@ -697,790 +697,3 @@ vmap_remove(struct vmap *m, uptr va_start, u64 len)
release
(
&
m
->
lock
);
return
0
;
}
#if 0
void
printpgdir(pde_t *pgdir)
{
pde_t *pde;
pte_t *pgtab;
pte_t pte;
for (uint i = 0; i < NPDENTRIES; i++) {
pde = &pgdir[i];
pde_t pdev = *pde;
if (pdev & PTE_P) {
pgtab = (pte_t*)p2v(PTE_ADDR(pdev));
cprintf("%d: p 0x%x v 0x%x\n", i, PTE_ADDR(pdev), pgtab);
for (uint j = 0; j < NPTENTRIES; j++) {
pte = pgtab[j];
if (pte & PTE_P) {
void *pg = p2v(PTE_ADDR(pte));
if (pg != 0)
; // cprintf(" %d: 0x%x v 0x%x\n", j, pte, pg);
}
}
}
}
}
// Return the address of the PTE in page table pgdir
// that corresponds to linear address va. If create!=0,
// create any required page table pages.
static pte_t *
walkpgdir(pde_t *pgdir, const void *va, int create)
{
pde_t *pde;
pte_t *pgtab;
retry:
pde = &pgdir[PDX(va)];
pde_t pdev = *pde;
if(pdev & PTE_P){
pgtab = (pte_t*)p2v(PTE_ADDR(pdev));
} else {
if(!create || (pgtab = (pte_t*)kalloc()) == 0)
return 0;
// Make sure all those PTE_P bits are zero.
memset(pgtab, 0, PGSIZE);
// The permissions here are overly generous, but they can
// be further restricted by the permissions in the page table
// entries, if necessary.
if (!__sync_bool_compare_and_swap(pde, pdev, v2p(pgtab) | PTE_P | PTE_W | PTE_U)) {
kfree((void*) pgtab);
goto retry;
}
}
return &pgtab[PTX(va)];
}
// Create PTEs for linear addresses starting at la that refer to
// physical addresses starting at pa. la and size might not
// be page-aligned.
static int
mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm)
{
char *a, *last;
pte_t *pte;
a = PGROUNDDOWN(la);
last = PGROUNDDOWN(la + size - 1);
for(;;){
pte = walkpgdir(pgdir, a, 1);
if(pte == 0)
return -1;
if(*pte & PTE_P)
panic("remap");
*pte = pa | perm | PTE_P;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
void
updatepages(pde_t *pgdir, void *begin, void *end, int perm)
{
char *a, *last;
pte_t *pte;
a = PGROUNDDOWN(begin);
last = PGROUNDDOWN(end);
for (;;) {
pte = walkpgdir(pgdir, a, 1);
if(pte != 0) {
if (perm == 0) *pte = 0;
else *pte = PTE_ADDR(*pte) | perm | PTE_P;
}
if (a == last)
break;
a += PGSIZE;
}
}
// There is one page table per process, plus one that's used
// when a CPU is not running any process (kpgdir).
// A user process uses the same page table as the kernel; the
// page protection bits prevent it from using anything other
// than its memory.
//
// setupkvm() and exec() set up every page table like this:
// 0..KERNBASE : user memory (text, data, stack, heap), mapped to some phys mem
// KERNBASE+640K..KERNBASE+1M: mapped to 640K..1M
// KERNBASE+1M..KERNBASE+end : mapped to 1M..end
// KERNBASE+end..KERBASE+PHYSTOP : mapped to end..PHYSTOP (free memory)
// 0xfe000000..0 : mapped direct (devices such as ioapic)
//
// The kernel allocates memory for its heap and for user memory
// between kernend and the end of physical memory (PHYSTOP).
// The virtual address space of each user program includes the kernel
// (which is inaccessible in user mode). The user program sits in
// the bottom of the address space, and the kernel at the top at KERNBASE.
static struct kmap {
void *l;
uint p;
uint e;
int perm;
} kmap[] = {
{ (void *)IOSPACEB, IOSPACEB, IOSPACEE, PTE_W}, // I/O space
{ P2V(IOSPACEB), IOSPACEB, IOSPACEE, PTE_W}, // I/O space
{ (void *)KERNLINK, V2P(KERNLINK), V2P(data), 0}, // kernel text, rodata
{ data, V2P(data), PHYSTOP, PTE_W}, // kernel data, memory
{ (void*)0xFE000000, 0xFE000000, 0, PTE_W}, // device mappings
};
// Set up kernel part of a page table.
static pde_t*
setupkvm(void)
{
pde_t *pgdir;
struct kmap *k;
if((pgdir = (pde_t*)kalloc()) == 0)
return 0;
memset(pgdir, 0, PGSIZE);
k = kmap;
for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
if(mappages(pgdir, k->l, k->e - k->p, (uint)k->p, k->perm) < 0)
return 0;
return pgdir;
}
// Allocate one page table for the machine for the kernel address
// space for scheduler processes.
void
kvmalloc(void)
{
kpgdir = setupkvm();
switchkvm();
}
// Turn on paging.
void
vmenable(void)
{
uint cr0;
switchkvm(); // load kpgdir into cr3
cr0 = rcr0();
cr0 |= CR0_PG;
lcr0(cr0);
struct cpu *c = &cpus[0];
lgdt((void *)v2p((void *)(c->gdt)), sizeof(c->gdt));
loadgs(SEG_KCPU << 3);
loadfs(SEG_KDATA << 3);
loades(SEG_KDATA << 3);
loadds(SEG_KDATA << 3);
loadss(SEG_KDATA << 3);
__asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (SEG_KCODE << 3)); // reload cs
}
// Free a page table and all the physical memory pages
// in the user part.
static void
freevm(pde_t *pgdir)
{
uint i;
if(pgdir == 0)
panic("freevm: no pgdir");
for(i = 0; i < NPDENTRIES; i++){
if(pgdir[i] & PTE_P)
kfree(p2v(PTE_ADDR(pgdir[i])));
}
kfree(pgdir);
}
struct vmnode *
vmn_alloc(uint npg, uint type)
{
struct vmnode *n = kmalloc(sizeof(struct vmnode));
if (n == 0) {
cprintf("out of vmnodes");
return 0;
}
if(npg > NELEM(n->page)) {
panic("vmnode too big\n");
}
memset(n, 0, sizeof(struct vmnode));
n->npages = npg;
n->type = type;
return n;
}
static int
vmn_doallocpg(struct vmnode *n)
{
for(uint i = 0; i < n->npages; i++) {
if((n->page[i] = kalloc()) == 0)
return -1;
memset((char *) n->page[i], 0, PGSIZE);
}
return 0;
}
struct vmnode *
vmn_allocpg(uint npg)
{
struct vmnode *n = vmn_alloc(npg, EAGER);
if (n == 0) return 0;
if (vmn_doallocpg(n) < 0) {
vmn_free(n);
return 0;
}
return n;
}
void
vmn_free(struct vmnode *n)
{
for(uint i = 0; i < n->npages; i++) {
if (n->page[i]) {
kfree(n->page[i]);
n->page[i] = 0;
}
}
if (n->ip)
iput(n->ip);
n->ip = 0;
kmfree(n);
}
static void
vmn_decref(struct vmnode *n)
{
if(__sync_sub_and_fetch(&n->ref, 1) == 0)
vmn_free(n);
}
struct vmnode *
vmn_copy(struct vmnode *n)
{
struct vmnode *c = vmn_alloc(n->npages, n->type);
if(c != 0) {
c->type = n->type;
if (n->type == ONDEMAND) {
c->ip = idup(n->ip);
c->offset = n->offset;
c->sz = c->sz;
}
if (n->page[0]) { // If the first page is present, all of them are present
if (vmn_doallocpg(c) < 0) {
cprintf("vmn_copy: out of memory\n");
vmn_free(c);
return 0;
}
for(uint i = 0; i < n->npages; i++) {
memmove(c->page[i], n->page[i], PGSIZE);
}
}
}
return c;
}
static struct vma *
vma_alloc(void)
{
struct vma *e = kmalloc(sizeof(struct vma));
if (e == 0)
return 0;
memset(e, 0, sizeof(struct vma));
e->va_type = PRIVATE;
snprintf(e->lockname, sizeof(e->lockname), "vma:%p", e);
initlock(&e->lock, e->lockname);
return e;
}
struct vmap *
vmap_alloc(void)
{
struct vmap *m = kmalloc(sizeof(struct vmap));
if (m == 0)
return 0;
memset(m, 0, sizeof(struct vmap));
snprintf(m->lockname, sizeof(m->lockname), "vmap:%p", m);
initlock(&m->lock, m->lockname);
m->ref = 1;
m->pgdir = setupkvm();
if (m->pgdir == 0) {
cprintf("vmap_alloc: setupkvm out of memory\n");
kmfree(m);
return 0;
}
return m;
}
static void
vma_free(void *p)
{
struct vma *e = (struct vma *) p;
if(e->n)
vmn_decref(e->n);
kmfree(e);
}
#ifdef TREE
struct state {
int share;
void *pgdir;
struct node *root;
};
static int
vmap_free_vma(struct kv *kv, void *p)
{
struct state *st = (struct state *) p;
vma_free(kv->val);
st->root = tree_remove(st->root, kv->key);
return 1;
}
static void
vmap_free(void *p)
{
struct vmap *m = (struct vmap *) p;
struct state *st = kmalloc(sizeof(struct state));
st->root = m->root;
tree_foreach(m->root, vmap_free_vma, st);
m->root = st->root;
freevm(m->pgdir);
kmfree(st);
m->pgdir = 0;
m->alloc = 0;
}
#else
static void
vmap_free(void *p)
{
struct vmap *m = (struct vmap *) p;
for(uint i = 0; i < NELEM(m->e); i++) {
if (m->e[i])
vma_free(m->e[i]);
}
freevm(m->pgdir);
m->pgdir = 0;
m->alloc = 0;
}
#endif
void
vmap_decref
(
struct
vmap
*
m
)
{
if
(
__sync_sub_and_fetch
(
&
m
->
ref
,
1
)
==
0
)
vmap_free
(
m
);
}
// Does any vma overlap start..start+len?
// If yes, return the vma pointer.
// If no, return 0.
// This code can't handle regions at the very end
// of the address space, e.g. 0xffffffff..0x0
// We key vma's by their end address.
struct
vma
*
vmap_lookup
(
struct
vmap
*
m
,
uint
start
,
uint
len
)
{
if
(
start
+
len
<
start
)
panic
(
"vmap_lookup bad len"
);
#ifdef TREE
struct
kv
*
kv
=
tree_find_gt
(
m
->
root
,
start
);
// find vma with va_end > start
if
(
kv
!=
0
)
{
struct
vma
*
e
=
(
struct
vma
*
)
(
kv
->
val
);
if
(
e
->
va_end
<=
e
->
va_start
)
panic
(
"malformed va"
);
if
(
e
->
va_start
<
start
+
len
&&
e
->
va_end
>
start
)
{
return
e
;
}
}
#else
for
(
uint
i
=
0
;
i
<
NELEM
(
m
->
e
);
i
++
){
struct
vma
*
e
=
m
->
e
[
i
];
if
(
e
)
{
if
(
e
->
va_end
<=
e
->
va_start
)
// XXX shouldn't this involve start and len?
panic
(
"vmap_lookup bad vma"
);
if
(
e
->
va_start
<
start
+
len
&&
e
->
va_end
>
start
)
return
e
;
}
}
#endif
return
0
;
}
int
vmap_insert
(
struct
vmap
*
m
,
struct
vmnode
*
n
,
uint
va_start
)
{
acquire
(
&
m
->
lock
);
uint
len
=
n
->
npages
*
PGSIZE
;
if
(
vmap_lookup
(
m
,
va_start
,
len
)){
cprintf
(
"vmap_insert: overlap
\n
"
);
release
(
&
m
->
lock
);
return
-
1
;
}
#ifdef TREE
struct
vma
*
e
=
vma_alloc
();
struct
kv
kv
;
if
(
e
==
0
)
{
release
(
&
m
->
lock
);
return
-
1
;
}
e
->
va_start
=
va_start
;
e
->
va_end
=
va_start
+
len
;
e
->
n
=
n
;
__sync_fetch_and_add
(
&
n
->
ref
,
1
);
kv
.
key
=
e
->
va_end
;
kv
.
val
=
e
;
m
->
root
=
tree_insert
(
m
->
root
,
&
kv
);
release
(
&
m
->
lock
);
return
0
;
#else
for
(
uint
i
=
0
;
i
<
NELEM
(
m
->
e
);
i
++
)
{
if
(
m
->
e
[
i
])
continue
;
m
->
e
[
i
]
=
vma_alloc
();
if
(
m
->
e
[
i
]
==
0
)
return
-
1
;
m
->
e
[
i
]
->
va_start
=
va_start
;
m
->
e
[
i
]
->
va_end
=
va_start
+
len
;
m
->
e
[
i
]
->
n
=
n
;
__sync_fetch_and_add
(
&
n
->
ref
,
1
);
release
(
&
m
->
lock
);
return
0
;
}
release
(
&
m
->
lock
);
cprintf
(
"vmap_insert: out of vma slots
\n
"
);
return
-
1
;
#endif
}
int
vmap_remove
(
struct
vmap
*
m
,
uint
va_start
,
uint
len
)
{
acquire
(
&
m
->
lock
);
uint
va_end
=
va_start
+
len
;
#ifdef TREE
struct
kv
*
kv
=
tree_find_gt
(
m
->
root
,
va_start
);
if
(
kv
==
0
)
panic
(
"no vma?"
);
struct
vma
*
e
=
(
struct
vma
*
)
kv
->
val
;
if
(
e
->
va_start
!=
va_start
||
e
->
va_end
!=
va_end
)
{
cprintf
(
"vmap_remove: partial unmap unsupported
\n
"
);
release
(
&
m
->
lock
);
return
-
1
;
}
m
->
root
=
tree_remove
(
m
->
root
,
va_start
+
len
);
rcu_delayed
(
e
,
vma_free
);
#else
for
(
uint
i
=
0
;
i
<
NELEM
(
m
->
e
);
i
++
)
{
if
(
m
->
e
[
i
]
&&
(
m
->
e
[
i
]
->
va_start
<
va_end
&&
m
->
e
[
i
]
->
va_end
>
va_start
))
{
if
(
m
->
e
[
i
]
->
va_start
!=
va_start
||
m
->
e
[
i
]
->
va_end
!=
va_end
)
{
release
(
&
m
->
lock
);
cprintf
(
"vmap_remove: partial unmap unsupported
\n
"
);
return
-
1
;
}
rcu_delayed
(
m
->
e
[
i
],
vma_free
);
m
->
e
[
i
]
=
0
;
}
}
#endif
release
(
&
m
->
lock
);
return
0
;
}
static
int
vmap_copy_vma
(
struct
kv
*
kv
,
void
*
_st
)
{
struct
state
*
st
=
(
struct
state
*
)
_st
;
struct
vma
*
e
=
(
struct
vma
*
)
kv
->
val
;
struct
vma
*
c
=
vma_alloc
();
if
(
c
==
0
)
{
return
0
;
}
c
->
va_start
=
e
->
va_start
;
c
->
va_end
=
e
->
va_end
;
if
(
st
->
share
)
{
c
->
n
=
e
->
n
;
c
->
va_type
=
COW
;
acquire
(
&
e
->
lock
);
e
->
va_type
=
COW
;
updatepages
(
st
->
pgdir
,
(
void
*
)
(
e
->
va_start
),
(
void
*
)
(
e
->
va_end
),
PTE_COW
);
release
(
&
e
->
lock
);
}
else
{
c
->
n
=
vmn_copy
(
e
->
n
);
c
->
va_type
=
e
->
va_type
;
}
if
(
c
->
n
==
0
)
{
return
0
;
}
__sync_fetch_and_add
(
&
c
->
n
->
ref
,
1
);
struct
kv
kv1
;
kv1
.
key
=
c
->
va_end
;
kv1
.
val
=
(
void
*
)
c
;
st
->
root
=
tree_insert
(
st
->
root
,
&
kv1
);
return
1
;
}
struct
vmap
*
vmap_copy
(
struct
vmap
*
m
,
int
share
)
{
struct
vmap
*
c
=
vmap_alloc
();
if
(
c
==
0
)
return
0
;
acquire
(
&
m
->
lock
);
#ifdef TREE
struct
state
*
st
=
kmalloc
(
sizeof
(
struct
state
));
st
->
share
=
share
;
st
->
pgdir
=
m
->
pgdir
;
st
->
root
=
c
->
root
;
if
(
!
tree_foreach
(
m
->
root
,
vmap_copy_vma
,
st
))
{
vmap_free
(
c
);
release
(
&
m
->
lock
);
kmfree
(
st
);
return
0
;
}
c
->
root
=
st
->
root
;
kmfree
(
st
);
#else
for
(
uint
i
=
0
;
i
<
NELEM
(
m
->
e
);
i
++
)
{
if
(
m
->
e
[
i
]
==
0
)
continue
;
c
->
e
[
i
]
=
vma_alloc
();
if
(
c
->
e
[
i
]
==
0
)
{
release
(
&
m
->
lock
);
vmap_free
(
c
);
return
0
;
}
c
->
e
[
i
]
->
va_start
=
m
->
e
[
i
]
->
va_start
;
c
->
e
[
i
]
->
va_end
=
m
->
e
[
i
]
->
va_end
;
if
(
share
)
{
c
->
e
[
i
]
->
n
=
m
->
e
[
i
]
->
n
;
c
->
e
[
i
]
->
va_type
=
COW
;
acquire
(
&
m
->
e
[
i
]
->
lock
);
m
->
e
[
i
]
->
va_type
=
COW
;
updatepages
(
m
->
pgdir
,
(
void
*
)
(
m
->
e
[
i
]
->
va_start
),
(
void
*
)
(
m
->
e
[
i
]
->
va_end
),
PTE_COW
);
release
(
&
m
->
e
[
i
]
->
lock
);
}
else
{
c
->
e
[
i
]
->
n
=
vmn_copy
(
m
->
e
[
i
]
->
n
);
c
->
e
[
i
]
->
va_type
=
m
->
e
[
i
]
->
va_type
;
}
if
(
c
->
e
[
i
]
->
n
==
0
)
{
release
(
&
m
->
lock
);
vmap_free
(
c
);
return
0
;
}
__sync_fetch_and_add
(
&
c
->
e
[
i
]
->
n
->
ref
,
1
);
}
#endif
if
(
share
)
lcr3
(
v2p
(
m
->
pgdir
));
// Reload hardware page table
release
(
&
m
->
lock
);
return
c
;
}
static
int
vmn_doload
(
struct
vmnode
*
vmn
,
struct
inode
*
ip
,
uint
offset
,
uint
sz
)
{
for
(
uint
i
=
0
;
i
<
sz
;
i
+=
PGSIZE
){
uint
n
;
char
*
p
=
vmn
->
page
[
i
/
PGSIZE
];
if
(
sz
-
i
<
PGSIZE
)
n
=
sz
-
i
;
else
n
=
PGSIZE
;
if
(
readi
(
ip
,
p
,
offset
+
i
,
n
)
!=
n
)
return
-
1
;
}
return
0
;
}
// Load a program segment into a vmnode.
int
vmn_load
(
struct
vmnode
*
vmn
,
struct
inode
*
ip
,
uint
offset
,
uint
sz
)
{
if
(
vmn
->
type
==
ONDEMAND
)
{
vmn
->
ip
=
ip
;
vmn
->
offset
=
offset
;
vmn
->
sz
=
sz
;
return
0
;
}
else
{
return
vmn_doload
(
vmn
,
ip
,
offset
,
sz
);
}
}
// Copy len bytes from p to user address va in vmap.
// Most useful when vmap is not the current page table.
int
copyout
(
struct
vmap
*
vmap
,
uint
va
,
void
*
p
,
uint
len
)
{
char
*
buf
=
(
char
*
)
p
;
while
(
len
>
0
){
uint
va0
=
(
uint
)
PGROUNDDOWN
(
va
);
rcu_begin_read
();
struct
vma
*
vma
=
vmap_lookup
(
vmap
,
va
,
1
);
if
(
vma
==
0
)
{
rcu_end_read
();
return
-
1
;
}
acquire
(
&
vma
->
lock
);
uint
pn
=
(
va0
-
vma
->
va_start
)
/
PGSIZE
;
char
*
p0
=
vma
->
n
->
page
[
pn
];
if
(
p0
==
0
)
panic
(
"copyout: missing page"
);
uint
n
=
PGSIZE
-
(
va
-
va0
);
if
(
n
>
len
)
n
=
len
;
memmove
(
p0
+
(
va
-
va0
),
buf
,
n
);
len
-=
n
;
buf
+=
n
;
va
=
va0
+
PGSIZE
;
release
(
&
vma
->
lock
);
rcu_end_read
();
}
return
0
;
}
int
copyin
(
struct
vmap
*
vmap
,
uint
va
,
void
*
p
,
uint
len
)
{
char
*
buf
=
(
char
*
)
p
;
while
(
len
>
0
){
uint
va0
=
(
uint
)
PGROUNDDOWN
(
va
);
rcu_begin_read
();
struct
vma
*
vma
=
vmap_lookup
(
vmap
,
va
,
1
);
if
(
vma
==
0
)
{
rcu_end_read
();
return
-
1
;
}
acquire
(
&
vma
->
lock
);
uint
pn
=
(
va0
-
vma
->
va_start
)
/
PGSIZE
;
char
*
p0
=
vma
->
n
->
page
[
pn
];
if
(
p0
==
0
)
panic
(
"copyout: missing page"
);
uint
n
=
PGSIZE
-
(
va
-
va0
);
if
(
n
>
len
)
n
=
len
;
memmove
(
buf
,
p0
+
(
va
-
va0
),
n
);
len
-=
n
;
buf
+=
n
;
va
=
va0
+
PGSIZE
;
release
(
&
vma
->
lock
);
rcu_end_read
();
}
return
0
;
}
static
struct
vma
*
pagefault_ondemand
(
struct
vmap
*
vmap
,
uint
va
,
uint
err
,
struct
vma
*
m
)
{
if
(
vmn_doallocpg
(
m
->
n
)
<
0
)
{
panic
(
"pagefault: couldn't allocate pages"
);
}
release
(
&
m
->
lock
);
if
(
vmn_doload
(
m
->
n
,
m
->
n
->
ip
,
m
->
n
->
offset
,
m
->
n
->
sz
)
<
0
)
{
panic
(
"pagefault: couldn't load"
);
}
m
=
vmap_lookup
(
vmap
,
va
,
1
);
if
(
!
m
)
panic
(
"pagefault_ondemand"
);
acquire
(
&
m
->
lock
);
// re-acquire lock on m
return
m
;
}
static
int
pagefault_wcow
(
struct
vmap
*
vmap
,
uint
va
,
pte_t
*
pte
,
struct
vma
*
m
,
uint
npg
)
{
// Always make a copy of n, even if this process has the only ref, because other processes
// may change ref count while this process is handling wcow
struct
vmnode
*
n
=
m
->
n
;
struct
vmnode
*
c
=
vmn_copy
(
m
->
n
);
if
(
c
==
0
)
{
cprintf
(
"pagefault_wcow: out of mem
\n
"
);
return
-
1
;
}
c
->
ref
=
1
;
m
->
va_type
=
PRIVATE
;
m
->
n
=
c
;
// Update the hardware page tables to reflect the change to the vma
updatepages
(
vmap
->
pgdir
,
(
void
*
)
m
->
va_start
,
(
void
*
)
m
->
va_end
,
0
);
pte
=
walkpgdir
(
vmap
->
pgdir
,
(
const
void
*
)
va
,
0
);
*
pte
=
v2p
(
m
->
n
->
page
[
npg
])
|
PTE_P
|
PTE_U
|
PTE_W
;
// drop my ref to vmnode
vmn_decref
(
n
);
return
0
;
}
int
pagefault
(
struct
vmap
*
vmap
,
uint
va
,
uint
err
)
{
pte_t
*
pte
=
walkpgdir
(
vmap
->
pgdir
,
(
const
void
*
)
va
,
1
);
if
((
*
pte
&
(
PTE_P
|
PTE_U
|
PTE_W
))
==
(
PTE_P
|
PTE_U
|
PTE_W
))
{
// optimize checks of args to syscals
return
0
;
}
// cprintf("%d: pagefault 0x%x err 0x%x pte 0x%x\n", proc->pid, va, err, *pte);
rcu_begin_read
();
struct
vma
*
m
=
vmap_lookup
(
vmap
,
va
,
1
);
if
(
m
==
0
)
{
// cprintf("pagefault: no vma\n");
rcu_end_read
();
return
-
1
;
}
acquire
(
&
m
->
lock
);
uint
npg
=
(
PGROUNDDOWN
(
va
)
-
m
->
va_start
)
/
PGSIZE
;
// cprintf("%d: pagefault: valid vma 0x%x 0x%x %d (cow=%d)\n", proc->pid, m->va_start,
// m->va_type, COW);
// if (m->n)
// cprintf("page %d 0x%x %d %d\n", npg, m->n->page[npg], m->n->type, ONDEMAND);
if
(
m
->
n
&&
m
->
n
->
type
==
ONDEMAND
&&
m
->
n
->
page
[
npg
]
==
0
)
{
m
=
pagefault_ondemand
(
vmap
,
va
,
err
,
m
);
}
if
(
m
->
va_type
==
COW
&&
(
err
&
FEC_WR
))
{
if
(
pagefault_wcow
(
vmap
,
va
,
pte
,
m
,
npg
)
<
0
)
{
release
(
&
m
->
lock
);
rcu_end_read
();
return
-
1
;
}
}
else
if
(
m
->
va_type
==
COW
)
{
*
pte
=
v2p
(
m
->
n
->
page
[
npg
])
|
PTE_P
|
PTE_U
|
PTE_COW
;
}
else
{
if
(
m
->
n
->
ref
!=
1
)
{
panic
(
"pagefault"
);
}
*
pte
=
v2p
(
m
->
n
->
page
[
npg
])
|
PTE_P
|
PTE_U
|
PTE_W
;
}
lcr3
(
v2p
(
vmap
->
pgdir
));
// Reload hardware page tables
release
(
&
m
->
lock
);
rcu_end_read
();
return
1
;
}
#endif
编写
预览
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论