提交 62dddc8a 创建 作者: Silas Boyd-Wickizer's avatar Silas Boyd-Wickizer

Remove some dead code.

上级 cbecab91
#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
......@@ -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
......@@ -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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论