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

Start 64-biting proc.c, rcu.c, condvar.c, and ns.c

上级 3514d335
......@@ -3,11 +3,15 @@
OBJS = \
asm.o \
cga.o \
condvar.o \
console.o \
lapic.o \
kalloc.o \
main.o \
mp.o \
ns.o \
proc.o \
rcu.o \
spinlock.o \
string.o \
uart.o \
......
#include "types.h"
#include "defs.h"
#include "param.h"
#include "x86.h"
#include "mmu.h"
......@@ -7,6 +6,8 @@
#include "condvar.h"
#include "queue.h"
#include "proc.h"
#include "kernel.h"
#include "cpu.h"
void
initcondvar(struct condvar *cv, char *n)
......@@ -18,7 +19,7 @@ initcondvar(struct condvar *cv, char *n)
void
cv_sleep(struct condvar *cv, struct spinlock *lk)
{
if(proc == 0)
if(myproc() == 0)
panic("sleep");
if(lk == 0)
......@@ -29,20 +30,20 @@ cv_sleep(struct condvar *cv, struct spinlock *lk)
release(lk);
acquire(&proc->lock);
acquire(&myproc()->lock);
if(proc->cv_next || proc->oncv)
if(myproc()->cv_next || myproc()->oncv)
panic("cv_sleep cv_next");
proc->cv_next = cv->waiters;
cv->waiters = proc;
proc->state = SLEEPING;
proc->oncv = cv;
myproc()->cv_next = cv->waiters;
cv->waiters = myproc();
myproc()->state = SLEEPING;
myproc()->oncv = cv;
release(&cv->lock);
sched();
release(&proc->lock);
release(&myproc()->lock);
// Reacquire original lock.
acquire(lk);
......
......@@ -9,6 +9,7 @@
#include "spinlock.h"
#include "kalloc.h"
#include "xv6-mtrace.h"
#include "cpu.h"
struct kmem kmems[NCPU];
......@@ -62,6 +63,59 @@ kfree_pool(struct kmem *m, char *v)
release(&m->lock);
}
static void __attribute__((unused))
kmemprint(void)
{
cprintf("free pages: [ ");
for (u32 i = 0; i < NCPU; i++)
if (i == mycpu()->id)
cprintf("<%d> ", kmems[i].nfree);
else
cprintf("%d ", kmems[i].nfree);
cprintf("]\n");
}
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
char*
kalloc(void)
{
struct run *r = 0;
// cprintf("%d: kalloc 0x%x 0x%x 0x%x 0x%x 0%x\n", cpu->id, kmem, &kmems[cpu->id], kmem->freelist, PHYSTOP, kmems[1].freelist);
u32 startcpu = mycpu()->id;
for (u32 i = 0; r == 0 && i < NCPU; i++) {
int cn = (i + startcpu) % NCPU;
struct kmem *m = &kmems[cn];
acquire(&m->lock);
r = m->freelist;
if (r) {
m->freelist = r->next;
m->nfree--;
}
release(&m->lock);
}
if (r == 0) {
cprintf("kalloc: out of memory\n");
kmemprint();
return 0;
}
mtrace_label_register(mtrace_label_block,
r,
4096,
"kalloc",
sizeof("kalloc"),
RET_EIP());
if (kalloc_memset)
memset(r, 2, PGSIZE);
return (char*)r;
}
// Memory allocator by Kernighan and Ritchie,
// The C programming Language, 2nd ed. Section 8.7.
......@@ -106,6 +160,109 @@ initkalloc(void)
kinited = 1;
}
static void
domfree(void *ap)
{
Header *bp, *p;
bp = (Header*)ap - 1;
if (kalloc_memset)
memset(ap, 3, (bp->size-1) * sizeof(*bp));
for(p = freelists[mycpu()->id].freep; !(bp > p && bp < p->ptr); p = p->ptr)
if(p >= p->ptr && (bp > p || bp < p->ptr))
break;
if(bp + bp->size == p->ptr){
bp->size += p->ptr->size;
bp->ptr = p->ptr->ptr;
} else
bp->ptr = p->ptr;
if(p + p->size == bp){
p->size += bp->size;
p->ptr = bp->ptr;
} else
p->ptr = bp;
freelists[mycpu()->id].freep = p;
}
void
kmfree(void *ap)
{
acquire(&freelists[mycpu()->id].lock);
domfree(ap);
mtrace_label_register(mtrace_label_heap,
ap,
0,
0,
0,
RET_EIP());
release(&freelists[mycpu()->id].lock);
}
// Caller should hold free_locky
static Header*
morecore(u64 nu)
{
static u64 units_per_page = PGSIZE / sizeof(Header);
char *p;
Header *hp;
if(nu != units_per_page) {
if (nu > units_per_page)
panic("morecore");
nu = units_per_page; // we allocate nu * sizeof(Header)
}
p = kalloc();
if(p == 0)
return 0;
hp = (Header*)p;
hp->size = nu;
domfree((void*)(hp + 1));
return freelists[mycpu()->id].freep;
}
void*
kmalloc(u64 nbytes)
{
Header *p, *prevp;
u64 nunits;
void *r = 0;
acquire(&freelists[mycpu()->id].lock);
nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1;
if((prevp = freelists[mycpu()->id].freep) == 0){
freelists[mycpu()->id].base.ptr =
freelists[mycpu()->id].freep = prevp = &freelists[mycpu()->id].base;
freelists[mycpu()->id].base.size = 0;
}
for(p = prevp->ptr; ; prevp = p, p = p->ptr){
if(p->size >= nunits){
if(p->size == nunits)
prevp->ptr = p->ptr;
else {
p->size -= nunits;
p += p->size;
p->size = nunits;
}
freelists[mycpu()->id].freep = prevp;
r = (void*)(p + 1);
break;
}
if(p == freelists[mycpu()->id].freep)
if((p = morecore(nunits)) == 0)
break;
}
release(&freelists[mycpu()->id].lock);
if (r)
mtrace_label_register(mtrace_label_heap,
r,
nbytes,
"kmalloc'ed",
sizeof("kmalloc'ed"),
RET_EIP());
return r;
}
......
......@@ -10,20 +10,93 @@ static inline uptr v2p(void *a) { return (uptr) a - KBASE; }
static inline void *p2v(uptr a) { return (void *) a + KBASE; }
struct spinlock;
struct condvar;
struct proc;
// cga.c
void cgaputc(char c);
// condvar.c
void initcondvar(struct condvar *, char *);
void cv_sleep(struct condvar *cv, struct spinlock*);
void cv_wakeup(struct condvar *cv);
// console.c
void cprintf(const char*, ...);
void panic(const char*) __attribute__((noreturn));
// string.c
int memcmp(const void*, const void*, u32);
void* memmove(void*, const void*, u32);
void* memset(void*, int, u32);
char* safestrcpy(char*, const char*, int);
int strlen(const char*);
int strncmp(const char*, const char*, u32);
char* strncpy(char*, const char*, int);
int strcmp(const char *p, const char *q);
// kalloc.c
char* kalloc(void);
void kfree(void *);
void* kmalloc(u64);
void kmfree(void*);
// lapic.c
int cpunum(void);
// mp.c
extern int ncpu;
// ns.c
enum {
nskey_int = 1,
nskey_ii,
nskey_str,
nskey_dirname,
nskey_iis
};
struct nskey {
int type;
union {
u64 i;
struct {
u64 a;
u64 b;
} ii;
char *s;
char *dirname;
struct {
u64 a;
u64 b;
char *s;
} iis;
} u;
};
#define KI(v) (struct nskey){.type=nskey_int,.u.i=v}
#define KII(x,y) (struct nskey){.type=nskey_ii,.u.ii.a=x,.u.ii.b=y}
#define KS(v) (struct nskey){.type=nskey_str,.u.s=v}
#define KD(v) (struct nskey){.type=nskey_dirname,.u.dirname=v}
#define KIIS(x,y,z) (struct nskey){.type=nskey_iis,.u.iis.a=x, \
.u.iis.b=y, \
.u.iis.s=z}
void nsinit(void);
struct ns* nsalloc(int allowdup);
void nsfree(struct ns*);
int ns_allockey(struct ns*);
int ns_insert(struct ns*, struct nskey key, void*);
void* ns_lookup(struct ns*, struct nskey key);
void* ns_remove(struct ns *ns, struct nskey key, void *val); // removed val
void* ns_enumerate(struct ns *ns, void *(*f)(void *, void *, void *), void *arg);
void* ns_enumerate_key(struct ns *ns, struct nskey key, void *(*f)(void *, void *), void *arg);
// proc.c
void addrun(struct proc *);
struct proc* copyproc(struct proc*);
void exit(void);
int fork(int);
int growproc(int);
int kill(int);
void pinit(void);
void procdumpall(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void userinit(void);
int wait(void);
void yield(void);
void migrate(struct proc *);
// spinlock.c
void acquire(struct spinlock*);
......@@ -34,14 +107,15 @@ void release(struct spinlock*);
void pushcli(void);
void popcli(void);
// cga.c
void cgaputc(char c);
// string.c
int memcmp(const void*, const void*, u32);
void* memmove(void*, const void*, u32);
void* memset(void*, int, u32);
char* safestrcpy(char*, const char*, int);
int strlen(const char*);
int strncmp(const char*, const char*, u32);
char* strncpy(char*, const char*, int);
int strcmp(const char *p, const char *q);
// uart.c
void uartputc(char c);
// mp.c
extern int ncpu;
// lapic.c
int cpunum(void);
......@@ -11,6 +11,7 @@ extern void initlapic(void);
extern void initseg(void);
extern void inittrap(void);
extern void initkalloc(void);
extern void initrcu(void);
void
cmain(void)
......@@ -26,6 +27,7 @@ cmain(void)
initseg();
initkalloc();
initrcu(); // initialize rcu module
cprintf("ncpu %d\n", ncpu);
panic("end");
......
#include "types.h"
#include "kernel.h"
void *
ns_enumerate(struct ns *ns, void *(*f)(void *, void *, void *), void *arg)
{
panic("ns_enumerate");
}
#if 0
#include "types.h"
#include "defs.h"
#include "spinlock.h"
#include "param.h"
......@@ -321,4 +331,4 @@ ns_enumerate_key(struct ns *ns, struct nskey key, void *(*f)(void *, void *), vo
rcu_end_read();
return 0;
}
#endif
#include "types.h"
#include "defs.h"
#include "kernel.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
......@@ -8,13 +8,24 @@
#include "condvar.h"
#include "queue.h"
#include "proc.h"
#include "xv6-kmtrace.h"
int __attribute__ ((aligned (CACHELINE))) idle[NCPU];
struct ns *nspid __attribute__ ((aligned (CACHELINE)));
struct ns *nsrunq __attribute__ ((aligned (CACHELINE)));
static struct proc *initproc __attribute__ ((aligned (CACHELINE)));
int __mpalign__ idle[NCPU];
struct ns *nspid __mpalign__;
struct ns *nsrunq __mpalign__;
void
sched(void)
{
panic("sched");
}
void
addrun(struct proc *p)
{
panic("addrun");
}
#if 0
extern void forkret(void);
extern void trapret(void);
......@@ -720,3 +731,4 @@ procdumpall(void)
{
ns_enumerate(nspid, procdump, 0);
}
#endif
#include "spinlock.h"
// Segments in proc->gdt.
// Also known to bootasm.S and trapasm.S
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define SEG_KCPU 3 // kernel per-cpu data
#define SEG_UCODE 4 // user code
#define SEG_UDATA 5 // user data+stack
#define SEG_TSS 6 // this process's task state
#define NSEGS 7
//PAGEBREAK: 17
// Saved registers for kernel context switches.
// Don't need to save all the segment registers (%cs, etc),
// because they are constant across kernel contexts.
// Don't need to save %eax, %ecx, %edx, because the
// x86 convention is that the caller has saved them.
// Contexts are stored at the bottom of the stack they
// describe; the stack pointer is the address of the context.
// The layout of the context matches the layout of the stack in swtch.S
// at the "Switch stacks" comment. Switch doesn't save eip explicitly,
// but it is on the stack and allocproc() manipulates it.
struct context {
uint edi;
uint esi;
uint ebx;
uint ebp;
uint eip;
};
enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// A memory object (physical pages or inode).
enum vmntype { EAGER, ONDEMAND};
struct vmnode {
uint npages;
char *page[128];
uint ref;
enum vmntype type;
struct inode *ip;
uint offset;
uint sz;
};
// A mapping of a chunk of an address space to
// a specific memory object.
enum vmatype { PRIVATE, COW};
struct vma {
uint va_start; // start of mapping
uint va_end; // one past the last byte
enum vmatype va_type;
struct vmnode *n;
struct spinlock lock; // serialize fault/unmap
char lockname[16];
};
#define TREE
// An address space: a set of vmas plus h/w page table.
// The elements of e[] are not ordered by address.
struct vmap {
#ifdef TREE
struct node* root;
#else
struct vma* e[16];
#endif
struct spinlock lock; // serialize map/lookup/unmap
uint ref;
uint alloc;
pde_t *pgdir; // Page table
char lockname[16];
};
// Per-process, per-stack meta data for mtrace
#define MTRACE_NSTACKS 16
#define MTRACE_TAGSHIFT 28
......@@ -84,10 +12,12 @@ struct mtrace_stacks {
unsigned long tag[MTRACE_NSTACKS];
};
enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// Per-process state
struct proc {
struct vmap *vmap; // va -> vma
uint brk; // Top of heap
uptr brk; // Top of heap
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state
volatile int pid; // Process ID
......@@ -107,56 +37,12 @@ struct proc {
SLIST_HEAD(childlist, proc) childq;
SLIST_ENTRY(proc) child_next;
struct condvar cv;
uint epoch;
uint rcu_read_depth;
u64 epoch;
u64 rcu_read_depth;
char lockname[16];
int on_runq;
int cpu_pin;
struct mtrace_stacks mtrace_stacks;
};
// Process memory is laid out contiguously, low addresses first:
// text
// original data and bss
// fixed-size stack
// expandable heap
// Per-CPU state
struct cpu {
uchar id; // Local APIC ID; index into cpus[] below
struct context *scheduler; // swtch() here to enter scheduler
struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; // x86 global descriptor table
volatile uint booted; // Has the CPU started?
int ncli; // Depth of pushcli nesting.
int intena; // Were interrupts enabled before pushcli?
int last_rcu_gc_ticks;
// Cpu-local storage variables; see below
struct cpu *cpu;
struct proc *proc; // The currently-running process.
struct kmem *kmem; // The per-core memory table
} __attribute__ ((aligned (CACHELINE)));
struct condtab {
char name[MAXNAME];
struct spinlock lock;
struct condvar condtab[NPROC];
} __attribute__ ((aligned (CACHELINE)));
extern struct cpu cpus[NCPU];
extern struct condtab condtabs[NCPU];
extern int ncpu;
extern struct ns *nspid;
// Per-CPU variables, holding pointers to the
// current cpu and to the current process.
// The asm suffix tells gcc to use "%gs:0" to refer to cpu
// and "%gs:4" to refer to proc. seginit sets up the
// %gs segment register so that %gs refers to the memory
// holding those two variables in the local cpu's struct cpu.
// This is similar to how thread-local variables are implemented
// in thread libraries such as Linux pthreads.
extern struct cpu *cpu __asm("%gs:0"); // &cpus[cpunum()].cpu
extern struct proc *proc __asm("%gs:4"); // cpus[cpunum()].proc
extern struct kmem *kmem __asm("%gs:8"); // &cpu[cpunum()].kmem
#include "types.h"
#include "defs.h"
#include "param.h"
#include "kernel.h"
#include "mmu.h"
#include "x86.h"
#include "spinlock.h"
#include "condvar.h"
#include "queue.h"
#include "proc.h"
#include "xv6-kmtrace.h"
#include "cpu.h"
#include "xv6-mtrace.h"
struct rcu {
unsigned long epoch;
......@@ -19,9 +20,9 @@ struct rcu {
} f1;
struct {
void (*dofree)(int, uint);
void (*dofree)(int, u64);
int arg1;
uint arg2;
u64 arg2;
} f2;
};
int type;
......@@ -29,24 +30,14 @@ struct rcu {
TAILQ_HEAD(rcu_head, rcu);
static struct { struct rcu_head x __attribute__((aligned (CACHELINE))); } rcu_q[NCPU];
static uint global_epoch __attribute__ ((aligned (CACHELINE)));
static struct { struct spinlock l __attribute__((aligned (CACHELINE))); } rcu_lock[NCPU];
static struct { int v __attribute__((aligned (CACHELINE))); } delayed_nfree[NCPU];
static struct { struct condvar cv __attribute__((aligned (CACHELINE))); } rcu_cv[NCPU];
static struct { struct rcu_head x __mpalign__; } rcu_q[NCPU];
static u64 global_epoch __mpalign__;
static struct { struct spinlock l __mpalign__; } rcu_lock[NCPU];
static struct { int v __mpalign__; } delayed_nfree[NCPU];
static struct { struct condvar cv __mpalign__; } rcu_cv[NCPU];
enum { rcu_debug = 0 };
void
rcuinit(void)
{
for (int i = 0; i < NCPU; i++) {
initlock(&rcu_lock[i].l, "rcu");
TAILQ_INIT(&rcu_q[i].x);
initcondvar(&rcu_cv[i].cv, "rcu_gc_cv");
}
}
struct rcu *
rcu_alloc()
{
......@@ -55,7 +46,7 @@ rcu_alloc()
void *
rcu_min(void *vkey, void *v, void *arg){
uint *min_epoch_p = arg;
u64 *min_epoch_p = arg;
struct proc *p = (struct proc *) v;
if (*min_epoch_p > p->epoch) {
*min_epoch_p = p->epoch;
......@@ -69,18 +60,18 @@ void
rcu_gc_work(void)
{
struct rcu *r, *nr;
uint min_epoch = global_epoch;
u64 min_epoch = global_epoch;
int n = 0;
ns_enumerate(nspid, rcu_min, &min_epoch);
// pushcli(); // not necessary: rcup->cpu_pin==1
acquire(&rcu_lock[cpu->id].l);
acquire(&rcu_lock[mycpu()->id].l);
for (r = TAILQ_FIRST(&rcu_q[cpu->id].x); r != NULL; r = nr) {
for (r = TAILQ_FIRST(&rcu_q[mycpu()->id].x); r != NULL; r = nr) {
if (r->epoch >= min_epoch)
break;
release(&rcu_lock[cpu->id].l);
release(&rcu_lock[mycpu()->id].l);
// cprintf("free: %d (%x %x)\n", r->epoch, r->dofree, r->item);
switch (r->type) {
......@@ -94,17 +85,17 @@ rcu_gc_work(void)
panic("rcu type");
}
acquire(&rcu_lock[cpu->id].l);
delayed_nfree[cpu->id].v--;
acquire(&rcu_lock[mycpu()->id].l);
delayed_nfree[mycpu()->id].v--;
n++;
nr = TAILQ_NEXT(r, link);
TAILQ_REMOVE(&rcu_q[cpu->id].x, r, link);
TAILQ_REMOVE(&rcu_q[mycpu()->id].x, r, link);
kmfree(r);
}
release(&rcu_lock[cpu->id].l);
release(&rcu_lock[mycpu()->id].l);
if (rcu_debug)
cprintf("rcu_gc: cpu %d n %d delayed_nfree=%d min_epoch=%d\n",
cpu->id, n, delayed_nfree[cpu->id], min_epoch);
mycpu()->id, n, delayed_nfree[mycpu()->id], min_epoch);
// popcli(); // not necessary: rcup->cpu_pin==1
// global_epoch can be bumped anywhere; this seems as good a place as any
......@@ -114,9 +105,9 @@ rcu_gc_work(void)
void
rcu_gc_worker(void)
{
release(&proc->lock); // initially held by scheduler
release(&myproc()->lock); // initially held by scheduler
mtrace_kstack_start(rcu_gc_worker, proc);
mtrace_kstack_start(rcu_gc_worker, myproc());
struct spinlock wl;
initlock(&wl, "rcu_gc_worker"); // dummy lock
......@@ -125,7 +116,7 @@ rcu_gc_worker(void)
rcu_gc_work();
acquire(&wl);
cv_sleep(&rcu_cv[cpu->id].cv, &wl);
cv_sleep(&rcu_cv[mycpu()->id].cv, &wl);
release(&wl);
}
}
......@@ -133,7 +124,7 @@ rcu_gc_worker(void)
void
rcu_gc(void)
{
cv_wakeup(&rcu_cv[cpu->id].cv);
cv_wakeup(&rcu_cv[mycpu()->id].cv);
}
// XXX Use atomic instruction to update list (instead of holding lock)
......@@ -141,11 +132,11 @@ static void
rcu_delayed_int(struct rcu *r)
{
pushcli();
acquire(&rcu_lock[cpu->id].l);
acquire(&rcu_lock[mycpu()->id].l);
// cprintf("rcu_delayed: %d\n", global_epoch);
TAILQ_INSERT_TAIL(&rcu_q[cpu->id].x, r, link);
delayed_nfree[cpu->id].v++;
release(&rcu_lock[cpu->id].l);
TAILQ_INSERT_TAIL(&rcu_q[mycpu()->id].x, r, link);
delayed_nfree[mycpu()->id].v++;
release(&rcu_lock[mycpu()->id].l);
popcli();
}
......@@ -166,7 +157,7 @@ rcu_delayed(void *e, void (*dofree)(void *))
}
void
rcu_delayed2(int a1, uint a2, void (*dofree)(int,uint))
rcu_delayed2(int a1, u64 a2, void (*dofree)(int,u64))
{
struct rcu *r = rcu_alloc();
if (r == 0)
......@@ -182,24 +173,25 @@ rcu_delayed2(int a1, uint a2, void (*dofree)(int,uint))
void
rcu_begin_read(void)
{
if (proc && proc->rcu_read_depth++ == 0)
proc->epoch = global_epoch;
if (myproc() && myproc()->rcu_read_depth++ == 0)
myproc()->epoch = global_epoch;
__sync_synchronize();
}
void
rcu_end_read(void)
{
if (proc && proc->rcu_read_depth > 0 && --proc->rcu_read_depth == 0)
proc->epoch = INF;
if (myproc() && myproc()->rcu_read_depth > 0 &&
--myproc()->rcu_read_depth == 0)
myproc()->epoch = INF;
}
void
rcu_begin_write(struct spinlock *l)
{
if (l) acquire(l);
if (l)
acquire(l);
__sync_synchronize();
rcu_begin_read();
}
......@@ -207,7 +199,16 @@ void
rcu_end_write(struct spinlock *l)
{
rcu_end_read();
if (l) release(l);
if (l)
release(l);
}
void
initrcu(void)
{
for (int i = 0; i < NCPU; i++) {
initlock(&rcu_lock[i].l, "rcu");
TAILQ_INIT(&rcu_q[i].x);
initcondvar(&rcu_cv[i].cv, "rcu_gc_cv");
}
}
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论