提交 224c53b4 创建 作者: Nickolai Zeldovich's avatar Nickolai Zeldovich

harness for running parts of xv6 in linux userspace

( make HW=user o.user/utest && ./o.user/utest )
上级 a9338ce5
...@@ -46,6 +46,7 @@ include lib/Makefrag ...@@ -46,6 +46,7 @@ include lib/Makefrag
include bin/Makefrag include bin/Makefrag
include kernel/Makefrag include kernel/Makefrag
include tools/Makefrag include tools/Makefrag
-include user/Makefrag.$(HW)
$(O)/%.o: %.c $(O)/%.o: %.c
@echo " CC $@" @echo " CC $@"
......
...@@ -46,3 +46,8 @@ ...@@ -46,3 +46,8 @@
$ CC=gcc CXX=g++ ./configure --prefix=[PREFIX] \ $ CC=gcc CXX=g++ ./configure --prefix=[PREFIX] \
--enable-targets=x86_64 --enable-optimized --enable-targets=x86_64 --enable-optimized
$ CC=gcc CXX=g++ make && make install $ CC=gcc CXX=g++ make && make install
* user-space version
$ make HW=user o.user/utest
...@@ -13,31 +13,4 @@ ...@@ -13,31 +13,4 @@
#define _GLIBCXX_ATOMIC_BUILTINS_8 1 #define _GLIBCXX_ATOMIC_BUILTINS_8 1
#include "atomic_std.h" #include "atomic_std.h"
#include "atomic_util.hh"
template<class T>
bool
cmpxch(std::atomic<T> *a, T expected, T desired)
{
return a->compare_exchange_weak(expected, desired);
}
template<class T>
bool
cmpxch(volatile std::atomic<T> *a, T expected, T desired)
{
return a->compare_exchange_weak(expected, desired);
}
template<class T>
bool
cmpxch_update(std::atomic<T> *a, T *expected, T desired)
{
return a->compare_exchange_weak(*expected, desired);
}
template<class T>
bool
cmpxch_update(volatile std::atomic<T> *a, T *expected, T desired)
{
return a->compare_exchange_weak(*expected, desired);
}
#pragma once
template<class T>
bool
cmpxch(std::atomic<T> *a, T expected, T desired)
{
return a->compare_exchange_weak(expected, desired);
}
template<class T>
bool
cmpxch(volatile std::atomic<T> *a, T expected, T desired)
{
return a->compare_exchange_weak(expected, desired);
}
template<class T>
bool
cmpxch_update(std::atomic<T> *a, T *expected, T desired)
{
return a->compare_exchange_weak(*expected, desired);
}
template<class T>
bool
cmpxch_update(volatile std::atomic<T> *a, T *expected, T desired)
{
return a->compare_exchange_weak(*expected, desired);
}
#pragma once #pragma once
#include "atomic.hh"
struct crange; struct crange;
struct crange_locked; struct crange_locked;
struct range; struct range;
......
...@@ -6,4 +6,3 @@ ...@@ -6,4 +6,3 @@
#include "atomic.hh" #include "atomic.hh"
#include "proc.hh" #include "proc.hh"
#include "cpu.hh" #include "cpu.hh"
#include "ns.hh"
...@@ -7,7 +7,7 @@ u64 namehash(const strbuf<DIRSIZ>&); ...@@ -7,7 +7,7 @@ u64 namehash(const strbuf<DIRSIZ>&);
struct file { struct file {
enum { FD_NONE, FD_PIPE, FD_INODE, FD_SOCKET } type; enum { FD_NONE, FD_PIPE, FD_INODE, FD_SOCKET } type;
atomic<int> ref; // reference count std::atomic<int> ref; // reference count
char readable; char readable;
char writable; char writable;
...@@ -24,13 +24,13 @@ struct inode : public rcu_freed { ...@@ -24,13 +24,13 @@ struct inode : public rcu_freed {
u32 dev; // Device number u32 dev; // Device number
u32 inum; // Inode number u32 inum; // Inode number
u32 gen; // Generation number u32 gen; // Generation number
atomic<int> ref; // Reference count std::atomic<int> ref; // Reference count
int flags; // I_BUSY, I_VALID int flags; // I_BUSY, I_VALID
atomic<int> readbusy; std::atomic<int> readbusy;
struct condvar cv; struct condvar cv;
struct spinlock lock; struct spinlock lock;
char lockname[16]; char lockname[16];
atomic<xns<strbuf<DIRSIZ>, u32, namehash>*> dir; std::atomic<xns<strbuf<DIRSIZ>, u32, namehash>*> dir;
short type; // copy of disk inode short type; // copy of disk inode
short major; short major;
......
...@@ -37,3 +37,8 @@ class scoped_gc_epoch { ...@@ -37,3 +37,8 @@ class scoped_gc_epoch {
} }
}; };
void initgc(void);
void initprocgc(struct proc *);
void gc_start(void);
void gc_delayed(rcu_freed *);
...@@ -100,16 +100,6 @@ int dirlink(struct inode*, const char*, u32); ...@@ -100,16 +100,6 @@ int dirlink(struct inode*, const char*, u32);
void dir_init(struct inode *dp); void dir_init(struct inode *dp);
void dir_flush(struct inode *dp); void dir_flush(struct inode *dp);
// gc.c
void initgc(void);
void initprocgc(struct proc *);
void gc_start(void);
#ifdef __cplusplus
class rcu_freed;
void gc_delayed(rcu_freed *);
#endif
// hz.c // hz.c
void microdelay(u64); void microdelay(u64);
u64 nsectime(void); u64 nsectime(void);
...@@ -182,16 +172,13 @@ void userinit(void); ...@@ -182,16 +172,13 @@ void userinit(void);
int wait(void); int wait(void);
void yield(void); void yield(void);
struct proc* threadalloc(void (*fn)(void*), void *arg); struct proc* threadalloc(void (*fn)(void*), void *arg);
void threadpin(void (*fn)(void*), void *arg, const char *name, int cpu);
// prof.c // prof.c
extern int profenable; extern int profenable;
void profreset(void); void profreset(void);
void profdump(void); void profdump(void);
// rnd.c
u64 rnd();
// sampler.c // sampler.c
void sampstart(void); void sampstart(void);
int sampintr(struct trapframe*); int sampintr(struct trapframe*);
......
#pragma once #pragma once
#include "gc.hh" #include "gc.hh"
#include "atomic.hh"
using std::atomic;
// name spaces // name spaces
// XXX maybe use open hash table, no chain, better cache locality // XXX maybe use open hash table, no chain, better cache locality
...@@ -18,8 +15,8 @@ template<class K, class V> ...@@ -18,8 +15,8 @@ template<class K, class V>
class xelem : public rcu_freed { class xelem : public rcu_freed {
public: public:
V val; V val;
atomic<int> next_lock; std::atomic<int> next_lock;
atomic<xelem<K, V>*> volatile next; std::atomic<xelem<K, V>*> volatile next;
K key; K key;
xelem(const K &k, const V &v) : rcu_freed("xelem"), val(v), next_lock(0), next(0), key(k) {} xelem(const K &k, const V &v) : rcu_freed("xelem"), val(v), next_lock(0), next(0), key(k) {}
...@@ -28,14 +25,14 @@ class xelem : public rcu_freed { ...@@ -28,14 +25,14 @@ class xelem : public rcu_freed {
template<class K, class V> template<class K, class V>
struct xbucket { struct xbucket {
atomic<xelem<K, V>*> volatile chain; std::atomic<xelem<K, V>*> volatile chain;
} __attribute__((aligned (CACHELINE))); } __attribute__((aligned (CACHELINE)));
template<class K, class V, u64 (*HF)(const K&)> template<class K, class V, u64 (*HF)(const K&)>
class xns : public rcu_freed { class xns : public rcu_freed {
private: private:
bool allowdup; bool allowdup;
atomic<u64> nextkey; std::atomic<u64> nextkey;
xbucket<K, V> table[NHASH]; xbucket<K, V> table[NHASH];
public: public:
...@@ -109,8 +106,8 @@ class xns : public rcu_freed { ...@@ -109,8 +106,8 @@ class xns : public rcu_freed {
scoped_gc_epoch gc; scoped_gc_epoch gc;
for (;;) { for (;;) {
atomic<int> fakelock(0); std::atomic<int> fakelock(0);
atomic<int> *pelock = &fakelock; std::atomic<int> *pelock = &fakelock;
auto pe = &table[i].chain; auto pe = &table[i].chain;
for (;;) { for (;;) {
......
#pragma once
u64 rnd();
...@@ -27,6 +27,12 @@ operator delete(void *p) ...@@ -27,6 +27,12 @@ operator delete(void *p)
} }
void void
operator delete[](void *p)
{
kmfree(p);
}
void
__cxa_pure_virtual(void) __cxa_pure_virtual(void)
{ {
panic("__cxa_pure_virtual"); panic("__cxa_pure_virtual");
......
#include "crange_arch.hh" #include "crange_arch.hh"
#include "gc.hh" #include "gc.hh"
#include "crange.hh" #include "crange.hh"
#include "rnd.hh"
// //
// Concurrent atomic range operations using skip lists. An insert may split an // Concurrent atomic range operations using skip lists. An insert may split an
...@@ -91,7 +92,7 @@ range::~range() ...@@ -91,7 +92,7 @@ range::~range()
next[l] = (struct range *) 0xDEADBEEF; next[l] = (struct range *) 0xDEADBEEF;
} }
kmalignfree(lock); kmalignfree(lock);
kmfree(next); delete[] next;
} }
void void
...@@ -280,7 +281,7 @@ crange::add_index(int l, range *e, range *p1, markptr<range> s1) ...@@ -280,7 +281,7 @@ crange::add_index(int l, range *e, range *p1, markptr<range> s1)
if (l >= e->nlevel-1) return; if (l >= e->nlevel-1) return;
if (e->next[l+1].mark()) return; if (e->next[l+1].mark()) return;
// crange_check(cr, NULL); // crange_check(cr, NULL);
if (cmpxch(&e->curlevel, l, l+1)) { if (std::atomic_compare_exchange_strong(&e->curlevel, &l, l+1)) {
assert(e->curlevel < e->nlevel); assert(e->curlevel < e->nlevel);
// this is the core inserting at level l+1, but some core may be deleting // this is the core inserting at level l+1, but some core may be deleting
struct range *s = s1.ptr(); // XXX losing the mark bit ??? struct range *s = s1.ptr(); // XXX losing the mark bit ???
......
#include "crange_arch.hh" #include "crange_arch.hh"
#include "gc.hh" #include "gc.hh"
#include "atomic_util.hh"
#include "ns.hh"
using std::atomic;
extern u64 proc_hash(const u32&); extern u64 proc_hash(const u32&);
extern xns<u32, proc*, proc_hash> *xnspid; extern xns<u32, proc*, proc_hash> *xnspid;
...@@ -70,7 +74,8 @@ gc_move_to_tofree_cpu(int c, u64 epoch) ...@@ -70,7 +74,8 @@ gc_move_to_tofree_cpu(int c, u64 epoch)
assert(gc_state[c].delayed[fe].epoch == epoch-(NEPOCH-2)); // XXX race with setting epoch = 0 assert(gc_state[c].delayed[fe].epoch == epoch-(NEPOCH-2)); // XXX race with setting epoch = 0
// unhook list for fe epoch atomically; this shouldn't fail // unhook list for fe epoch atomically; this shouldn't fail
head = gc_state[c].delayed[fe].head; head = gc_state[c].delayed[fe].head;
while (!cmpxch_update(&gc_state[c].delayed[fe].head, &head, (rcu_freed*) 0)) {} while (!std::atomic_compare_exchange_strong(&gc_state[c].delayed[fe].head,
&head, (rcu_freed*) 0)) {}
// insert list into tofree list so that each core can free in parallel and free its elements // insert list into tofree list so that each core can free in parallel and free its elements
if(gc_state[c].tofree[fe].epoch != gc_state[c].delayed[fe].epoch) { if(gc_state[c].tofree[fe].epoch != gc_state[c].delayed[fe].epoch) {
...@@ -247,18 +252,8 @@ initgc(void) ...@@ -247,18 +252,8 @@ initgc(void)
} }
for (int c = 0; c < ncpu; c++) { for (int c = 0; c < ncpu; c++) {
struct proc *gcp; char namebuf[32];
snprintf(namebuf, sizeof(namebuf), "gc_%u", c);
gcp = threadalloc(gc_worker, NULL); threadpin(gc_worker, 0, namebuf, c);
if (gcp == NULL)
panic("threadalloc: gc_worker");
snprintf(gcp->name, sizeof(gcp->name), "gc_%u", c);
gcp->cpuid = c;
gcp->cpu_pin = 1;
acquire(&gcp->lock);
gcp->state = RUNNABLE;
addrun(gcp);
release(&gcp->lock);
} }
} }
...@@ -674,3 +674,21 @@ threadalloc(void (*fn)(void *), void *arg) ...@@ -674,3 +674,21 @@ threadalloc(void (*fn)(void *), void *arg)
p->cwd = 0; p->cwd = 0;
return p; return p;
} }
void
threadpin(void (*fn)(void*), void *arg, const char *name, int cpu)
{
struct proc *p;
p = threadalloc(fn, arg);
if (p == NULL)
panic("threadpin: alloc");
snprintf(p->name, sizeof(p->name), "%s", name);
p->cpuid = cpu;
p->cpu_pin = 1;
acquire(&p->lock);
p->state = RUNNABLE;
addrun(p);
release(&p->lock);
}
#include "types.h" #include "crange_arch.hh"
#include "kernel.hh" #include "rnd.hh"
#include "cpu.hh"
struct seed { struct seed {
u64 v; u64 v;
......
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
#define NCPU 4 // maximum number of CPUs #define NCPU 4 // maximum number of CPUs
#define MTRACE 0 #define MTRACE 0
#define PERFSIZE (512<<20ull) #define PERFSIZE (512<<20ull)
#elif defined(HW_user)
#define NCPU 256
#define MTRACE 0
#define PERFSIZE (16<<20ull)
#else #else
#error "Unknown HW" #error "Unknown HW"
#endif #endif
CXXFLAGS := -Iuser $(CXXFLAGS) -msse
$(O)/utest: $(O)/kernel/crange.o \
$(O)/kernel/gc.o \
$(O)/kernel/rnd.o \
$(O)/user/umain.o
@echo " LD $@"
$(Q)mkdir -p $(@D)
$(Q)$(CXX) -o $@ $^ -lpthread -lrt
#include <inttypes.h>
#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <atomic>
#include <utility>
extern "C" {
#include <string.h>
}
typedef uint32_t u32;
typedef uint64_t u64;
typedef uintptr_t uptr;
#define cprintf(...) printf(__VA_ARGS__)
#define panic(...) do { printf(__VA_ARGS__); assert(0); } while (0)
#define LOCKSTAT_CRANGE 0
#define LOCKSTAT_GC 0
struct spinlock {
pthread_mutex_t mu;
};
struct condvar {
pthread_cond_t cv;
};
static inline void
acquire(spinlock *s)
{
pthread_mutex_lock(&s->mu);
}
static inline void
release(spinlock *s)
{
pthread_mutex_unlock(&s->mu);
}
static inline int
tryacquire(spinlock *s)
{
return !pthread_mutex_trylock(&s->mu);
}
static inline void
initlock(spinlock *s, const char *m, int lockstat)
{
memset(s, 0, sizeof(*s));
}
static inline void
cv_wakeup(condvar *c)
{
pthread_cond_signal(&c->cv);
}
static inline void
cv_sleepto(condvar *c, spinlock *s, u64 ns)
{
timespec ts;
ts.tv_sec = ns / 1000000000;
ts.tv_nsec = ns % 1000000000;
pthread_cond_timedwait(&c->cv, &s->mu, &ts);
}
static inline u64
nsectime()
{
timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (((u64) 1000000000) * ts.tv_sec) + ts.tv_nsec;
}
static inline void
initcondvar(condvar *c, const char *m)
{
memset(c, 0, sizeof(*c));
}
static inline int
kmalign(void **ptr, size_t align, size_t size)
{
return posix_memalign(ptr, align, size);
}
static inline void
kmalignfree(void *ptr)
{
free(ptr);
}
struct proc {
spinlock gc_epoch_lock;
u64 epoch;
u64 epoch_depth;
u32 cpuid;
u32 pid;
char name[32];
};
struct cpu {
u32 id;
u32 ncli;
};
extern pthread_key_t myproc_key;
extern cpu cpus[];
extern proc procs[];
extern u32 ncpu;
extern u64 ticks;
static inline proc*
myproc()
{
return (proc*) pthread_getspecific(myproc_key);
}
static inline cpu*
mycpu()
{
return (cpu*) &cpus[myproc()->cpuid];
}
void cli();
void sti();
static inline void
pushcli()
{
cli();
mycpu()->ncli++;
}
static inline void
popcli()
{
if (--mycpu()->ncli == 0)
sti();
}
void threadpin(void (*fn)(void*), void *arg, const char *name, int cpu);
#include <unistd.h>
#include "crange_arch.hh"
#include "gc.hh"
#include "crange.hh"
#include "atomic_util.hh"
#include "ns.hh"
u64
proc_hash(const u32 &pid)
{
return pid;
}
pthread_key_t myproc_key;
cpu cpus[NCPU];
u32 ncpu;
u64 ticks;
xns<u32, proc*, proc_hash> *xnspid;
struct makeproc_info {
void (*f) (void*);
void *farg;
const char *name;
int cpu;
};
void
cli()
{
/* suspend all threads with the same cpuid */
}
void
sti()
{
/* resume all threads with the same cpuid */
}
static void*
makeproc_start(void *arg)
{
makeproc_info *mpi = (makeproc_info *) arg;
proc *p = new proc();
pthread_setspecific(myproc_key, p);
p->pid = pthread_self();
p->cpuid = mpi->cpu;
snprintf(p->name, sizeof(p->name), "%s", mpi->name);
initprocgc(p);
xnspid->insert(p->pid, p);
mpi->f(mpi->farg);
delete mpi;
return 0;
}
void
makeproc(makeproc_info *mpi)
{
pthread_t tid;
makeproc_info *mcopy = new makeproc_info(*mpi);
pthread_create(&tid, 0, &makeproc_start, mcopy);
}
void
threadpin(void (*fn)(void*), void *arg, const char *name, int cpu)
{
makeproc_info mpi = { fn, arg, name, cpu };
makeproc(&mpi);
}
int
main(int ac, char **av)
{
assert(0 == pthread_key_create(&myproc_key, 0));
for (u32 i = 0; i < NCPU; i++)
cpus[i].id = i;
ncpu = NCPU;
xnspid = new xns<u32, proc*, proc_hash>(false);
initgc();
printf("Hello world!\n");
sleep(100);
}
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论