Compare commits

..

6 Commits

Author SHA1 Message Date
Robert Morris
79e8024d61 param.h for USERSTACK=2 2024-09-10 16:48:31 -04:00
Robert Morris
53c5c818d3 menvcfg -> 0x30a 2024-09-04 17:31:41 -04:00
Robert Morris
56aacf5261 stimecmp -> 0x14d 2024-09-04 15:25:30 -04:00
Robert Morris
3b6dc1bb98 hack for stimecmp 2024-09-04 15:19:32 -04:00
Frans Kaashoek
6bd4e66b23 another xargs test 2024-09-03 19:56:41 -04:00
Frans Kaashoek
5ed6844af4 Util lab 2024-08-21 12:56:20 -04:00
26 changed files with 147 additions and 1438 deletions

View File

@ -61,7 +61,7 @@ endif
# riscv64-unknown-elf- or riscv64-linux-gnu-
# perhaps in /opt/riscv/bin
TOOLPREFIX = /opt/riscv/bin/riscv64-unknown-elf-
#TOOLPREFIX =
# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
@ -276,9 +276,6 @@ endif
fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS)
mkfs/mkfs fs.img README $(UEXTRA) $(UPROGS)
newfs.img:
-mv -f fs.img fs.img.bk
-include kernel/*.d user/*.d
clean:
@ -286,7 +283,7 @@ clean:
*/*.o */*.d */*.asm */*.sym \
$U/initcode $U/initcode.out $U/usys.S $U/_* \
$K/kernel \
mkfs/mkfs fs.img fs.img.bk .gdbinit __pycache__ xv6.out* \
mkfs/mkfs fs.img .gdbinit __pycache__ xv6.out* \
ph barrier
# try to generate a unique GDB port
@ -315,12 +312,7 @@ QEMUOPTS += -netdev user,id=net0,hostfwd=udp::$(FWDPORT1)-:2000,hostfwd=udp::$(F
QEMUOPTS += -device e1000,netdev=net0,bus=pcie.0
endif
# makes a new fs.img
qemu: newfs.img $K/kernel fs.img
$(QEMU) $(QEMUOPTS)
# runs with existing fs.img, if present
qemu-fs: $K/kernel fs.img
qemu: $K/kernel fs.img
$(QEMU) $(QEMUOPTS)
.gdbinit: .gdbinit.tmpl-riscv

3
README
View File

@ -31,6 +31,9 @@ Rafael Ubal, Amane Uehara, Pablo Ventura, Xi Wang, WaheedHafez,
Keiichi Watanabe, Lucas Wolf, Nicolas Wolovick, wxdao, Grant Wu, x653,
Jindong Zhang, Icenowy Zheng, ZhUyU1997, and Zou Chang Wei.
The code in the files that constitute xv6 is
Copyright 2006-2024 Frans Kaashoek, Robert Morris, and Russ Cox.
ERROR REPORTS
Please send errors and suggestions to Frans Kaashoek and Robert Morris

View File

@ -1 +1 @@
LAB=lock
LAB=util

BIN
fs.img.bk

Binary file not shown.

View File

@ -1,66 +0,0 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(0, "running kalloctest")
def test_kalloctest():
r.run_qemu(shell_script([
'kalloctest'
]), timeout=300)
@test(10, "kalloctest: test1", parent=test_kalloctest)
def test_kalloctest_test1():
r.match('^test1 OK$')
@test(10, "kalloctest: test2", parent=test_kalloctest)
def test_kalloctest_test2():
r.match('^test2 OK$')
@test(10, "kalloctest: test3", parent=test_kalloctest)
def test_kalloctest_test3():
r.match('^test3 OK$')
@test(10, "kalloctest: sbrkmuch")
def test_sbrkmuch():
r.run_qemu(shell_script([
'usertests sbrkmuch'
]), timeout=90)
r.match('^ALL TESTS PASSED$')
@test(0, "running bcachetest")
def test_bcachetest():
r.run_qemu(shell_script([
'bcachetest'
]), timeout=150)
@test(20, "bcachetest: test0", parent=test_bcachetest)
def test_bcachetest_test0():
r.match('^test0: OK$')
@test(10, "bcachetest: test1", parent=test_bcachetest)
def test_bcachetest_test1():
r.match('^test1 OK$')
@test(10, "bcachetest: test2", parent=test_bcachetest)
def test_bcachetest_test2():
r.match('^test2 OK$')
@test(10, "bcachetest: test3", parent=test_bcachetest)
def test_bcachetest_test3():
r.match('^test3 OK$')
@test(19, "usertests")
def test_usertests():
r.run_qemu(shell_script([
'usertests -q'
]), timeout=300)
r.match('^ALL TESTS PASSED$')
@test(1, "time")
def test_time():
check_time()
run_tests()

105
grade-lab-util Executable file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env python3
import re
from gradelib import *
r = Runner(save("xv6.out"))
@test(5, "sleep, no arguments")
def test_sleep_no_args():
r.run_qemu(shell_script([
'sleep'
]))
r.match(no=["exec .* failed", "$ sleep\n$"])
@test(5, "sleep, returns")
def test_sleep_no_args():
r.run_qemu(shell_script([
'sleep',
'echo OK'
]))
r.match('^OK$', no=["exec .* failed", "$ sleep\n$"])
@test(10, "sleep, makes syscall")
def test_sleep():
r.run_qemu(shell_script([
'sleep 10',
'echo FAIL'
]), stop_breakpoint('sys_sleep'))
r.match('\\$ sleep 10', no=['FAIL'])
@test(20, "pingpong")
def test_pingpong():
r.run_qemu(shell_script([
'pingpong', 'echo OK'
]))
r.match('^\\d+: received ping$', '^\\d+: received pong$', '^OK$')
@test(20, "primes")
def test_primes():
r.run_qemu(shell_script([
'primes', 'echo OK'
]))
args = ['prime %d' % i for i in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269]]
args.append('^OK$')
r.match(*args)
@test(10, "find, in current directory")
def test_find_curdir():
fn = random_str()
r.run_qemu(shell_script([
'echo > %s' % fn,
'find . %s' % fn
]))
r.match('./%s' % fn)
r.match(no=["./README", "README"])
@test(10, "find, in sub-directory")
def test_find_curdir():
dd = random_str()
fn = random_str()
r.run_qemu(shell_script([
'echo > %s' % fn,
'mkdir %s' % dd,
'echo > %s/%s' % (dd, fn),
'find %s %s' % (dd, fn)
]))
r.match('%s/%s' % (dd, fn))
r.match(no=['./%s' % fn])
@test(10, "find, recursive")
def test_find_recursive():
needle = random_str()
dirs = [random_str() for _ in range(3)]
r.run_qemu(shell_script([
'mkdir %s' % dirs[0],
'echo > %s/%s' % (dirs[0], needle),
'mkdir %s/%s' % (dirs[0], dirs[1]),
'echo > %s/%s/%s' % (dirs[0], dirs[1], needle),
'mkdir %s' % dirs[2],
'echo > %s/%s' % (dirs[2], needle),
'find . %s' % needle
]))
r.match('./%s/%s' % (dirs[0], needle),
'./%s/%s/%s' % (dirs[0], dirs[1], needle),
'./%s/%s' % (dirs[2], needle))
@test(10, "xargs")
def test_xargs():
r.run_qemu(shell_script([
'sh < xargstest.sh',
'echo DONE',
], 'DONE'))
matches = re.findall("hello", r.qemu.output)
assert_equal(len(matches), 3, "Number of appearances of 'hello'")
@test(9, "xargs, multi-line echo")
def test_xargs_multiline():
r.run_qemu(shell_script(['(echo 1 ; echo 2) | xargs echo']))
r.match('^1$', '^2$')
@test(1, "time")
def test_time():
check_time()
run_tests()

View File

@ -1,7 +1,3 @@
#ifdef LAB_MMAP
typedef unsigned long size_t;
typedef long int off_t;
#endif
struct buf;
struct context;
struct file;
@ -121,10 +117,6 @@ void initlock(struct spinlock*, char*);
void release(struct spinlock*);
void push_off(void);
void pop_off(void);
int atomic_read4(int *addr);
#ifdef LAB_LOCK
void freelock(struct spinlock*);
#endif
// sleeplock.c
void acquiresleep(struct sleeplock*);
@ -181,12 +173,6 @@ uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
#if defined(LAB_PGTBL) || defined(SOL_MMAP)
void vmprint(pagetable_t);
#endif
#ifdef LAB_PGTBL
pte_t* pgpte(pagetable_t, uint64);
#endif
// plic.c
void plicinit(void);
@ -201,39 +187,3 @@ void virtio_disk_intr(void);
// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
#ifdef LAB_PGTBL
// vmcopyin.c
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
#endif
#ifdef LAB_LOCK
// stats.c
void statsinit(void);
void statsinc(void);
// sprintf.c
int snprintf(char*, unsigned long, const char*, ...);
#endif
#ifdef KCSAN
void kcsaninit();
#endif
#ifdef LAB_NET
// pci.c
void pci_init();
// e1000.c
void e1000_init(uint32 *);
void e1000_intr(void);
int e1000_transmit(char *, int);
// net.c
void netinit(void);
void net_rx(char *buf, int len);
#endif

View File

@ -38,4 +38,3 @@ struct devsw {
extern struct devsw devsw[];
#define CONSOLE 1
#define STATS 2

View File

@ -295,11 +295,11 @@ ilock(struct inode *ip)
struct buf *bp;
struct dinode *dip;
if(ip == 0 || atomic_read4(&ip->ref) < 1)
if(ip == 0 || ip->ref < 1)
panic("ilock");
acquiresleep(&ip->lock);
if(ip->valid == 0){
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode*)bp->data + ip->inum%IPB;
@ -320,7 +320,7 @@ ilock(struct inode *ip)
void
iunlock(struct inode *ip)
{
if(ip == 0 || !holdingsleep(&ip->lock) || atomic_read4(&ip->ref) < 1)
if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)
panic("iunlock");
releasesleep(&ip->lock);
@ -416,6 +416,7 @@ bmap(struct inode *ip, uint bn)
brelse(bp);
return addr;
}
panic("bmap: out of range");
}
@ -446,7 +447,7 @@ itrunc(struct inode *ip)
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
ip->size = 0;
iupdate(ip);
}

View File

@ -1,323 +0,0 @@
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "riscv.h"
#include "proc.h"
#include "defs.h"
//
// Race detector using gcc's thread sanitizer. It delays all stores
// and loads and monitors if any other CPU is using the same address.
// If so, we have a race and print out the backtrace of the thread
// that raced and the thread that set the watchpoint.
//
//
// To run with kcsan:
// make clean
// make KCSAN=1 qemu
//
// The number of watch points.
#define NWATCH (NCPU)
// The number of cycles to delay stores, whatever that means on qemu.
//#define DELAY_CYCLES 20000
#define DELAY_CYCLES 200000
#define MAXTRACE 20
int
trace(uint64 *trace, int maxtrace)
{
uint64 i = 0;
push_off();
uint64 fp = r_fp();
uint64 ra, low = PGROUNDDOWN(fp) + 16, high = PGROUNDUP(fp);
while(!(fp & 7) && fp >= low && fp < high){
ra = *(uint64*)(fp - 8);
fp = *(uint64*)(fp - 16);
trace[i++] = ra;
if(i >= maxtrace)
break;
}
pop_off();
return i;
}
struct watch {
uint64 addr;
int write;
int race;
uint64 trace[MAXTRACE];
int tracesz;
};
struct {
struct spinlock lock;
struct watch points[NWATCH];
int on;
} tsan;
static struct watch*
wp_lookup(uint64 addr)
{
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
if(w->addr == addr) {
return w;
}
}
return 0;
}
static int
wp_install(uint64 addr, int write)
{
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
if(w->addr == 0) {
w->addr = addr;
w->write = write;
w->tracesz = trace(w->trace, MAXTRACE);
return 1;
}
}
panic("wp_install");
return 0;
}
static void
wp_remove(uint64 addr)
{
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
if(w->addr == addr) {
w->addr = 0;
w->tracesz = 0;
return;
}
}
panic("remove");
}
static void
printtrace(uint64 *t, int n)
{
int i;
for(i = 0; i < n; i++) {
printf("%p\n", (void*) t[i]);
}
}
static void
race(char *s, struct watch *w) {
uint64 t[MAXTRACE];
int n;
n = trace(t, MAXTRACE);
printf("== race detected ==\n");
printf("backtrace for racing %s\n", s);
printtrace(t, n);
printf("backtrace for watchpoint:\n");
printtrace(w->trace, w->tracesz);
printf("==========\n");
}
// cycle counter
static inline uint64
r_cycle()
{
uint64 x;
asm volatile("rdcycle %0" : "=r" (x) );
return x;
}
static void delay(void) __attribute__((noinline));
static void delay() {
uint64 stop = r_cycle() + DELAY_CYCLES;
uint64 c = r_cycle();
while(c < stop) {
c = r_cycle();
}
}
static void
kcsan_read(uint64 addr, int sz)
{
struct watch *w;
acquire(&tsan.lock);
if((w = wp_lookup(addr)) != 0) {
if(w->write) {
race("load", w);
}
release(&tsan.lock);
return;
}
release(&tsan.lock);
}
static void
kcsan_write(uint64 addr, int sz)
{
struct watch *w;
acquire(&tsan.lock);
if((w = wp_lookup(addr)) != 0) {
race("store", w);
release(&tsan.lock);
}
// no watchpoint; try to install one
if(wp_install(addr, 1)) {
release(&tsan.lock);
// XXX maybe read value at addr before and after delay to catch
// races of unknown origins (e.g., device).
delay();
acquire(&tsan.lock);
wp_remove(addr);
}
release(&tsan.lock);
}
// tsan.on will only have effect with "make KCSAN=1"
void
kcsaninit(void)
{
initlock(&tsan.lock, "tsan");
tsan.on = 1;
__sync_synchronize();
}
//
// Calls inserted by compiler into kernel binary, except for this file.
//
void
__tsan_init(void)
{
}
void
__tsan_read1(uint64 addr)
{
if(!tsan.on)
return;
// kcsan_read(addr, 1);
}
void
__tsan_read2(uint64 addr)
{
if(!tsan.on)
return;
kcsan_read(addr, 2);
}
void
__tsan_read4(uint64 addr)
{
if(!tsan.on)
return;
kcsan_read(addr, 4);
}
void
__tsan_read8(uint64 addr)
{
if(!tsan.on)
return;
kcsan_read(addr, 8);
}
void
__tsan_read_range(uint64 addr, uint64 size)
{
if(!tsan.on)
return;
kcsan_read(addr, size);
}
void
__tsan_write1(uint64 addr)
{
if(!tsan.on)
return;
// kcsan_write(addr, 1);
}
void
__tsan_write2(uint64 addr)
{
if(!tsan.on)
return;
kcsan_write(addr, 2);
}
void
__tsan_write4(uint64 addr)
{
if(!tsan.on)
return;
kcsan_write(addr, 4);
}
void
__tsan_write8(uint64 addr)
{
if(!tsan.on)
return;
kcsan_write(addr, 8);
}
void
__tsan_write_range(uint64 addr, uint64 size)
{
if(!tsan.on)
return;
kcsan_write(addr, size);
}
void
__tsan_atomic_thread_fence(int order)
{
__sync_synchronize();
}
uint32
__tsan_atomic32_load(uint *ptr, uint *val, int order)
{
uint t;
__atomic_load(ptr, &t, __ATOMIC_SEQ_CST);
return t;
}
void
__tsan_atomic32_store(uint *ptr, uint val, int order)
{
__atomic_store(ptr, &val, __ATOMIC_SEQ_CST);
}
// We don't use this
void
__tsan_func_entry(uint64 pc)
{
}
// We don't use this
void
__tsan_func_exit(void)
{
}

View File

@ -12,9 +12,6 @@ main()
{
if(cpuid() == 0){
consoleinit();
#if defined(LAB_LOCK)
statsinit();
#endif
printfinit();
printf("\n");
printf("xv6 kernel is booting\n");
@ -31,17 +28,11 @@ main()
iinit(); // inode table
fileinit(); // file table
virtio_disk_init(); // emulated hard disk
#ifdef LAB_NET
pci_init();
#endif
userinit(); // first user process
#ifdef KCSAN
kcsaninit();
#endif
__sync_synchronize();
started = 1;
} else {
while(atomic_read4((int *) &started) == 0)
while(started == 0)
;
__sync_synchronize();
printf("hart %d starting\n", cpuid());

View File

@ -1,4 +1,8 @@
#define NPROC 64 // maximum number of processes
#ifdef LAB_FS
#define NPROC 10 // maximum number of processes
#else
#define NPROC 64 // maximum number of processes (speedsup bigfile)
#endif
#define NCPU 8 // maximum number of CPUs
#define NOFILE 16 // open files per process
#define NFILE 100 // open files per system
@ -9,7 +13,21 @@
#define MAXOPBLOCKS 10 // max # of blocks any FS op writes
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
#define NBUF (MAXOPBLOCKS*3) // size of disk block cache
#define FSSIZE 2000 // size of file system in blocks
#ifdef LAB_FS
#define FSSIZE 200000 // size of file system in blocks
#else
#ifdef LAB_LOCK
#define FSSIZE 10000 // size of file system in blocks
#else
#define FSSIZE 2000 // size of file system in blocks
#endif
#endif
#define MAXPATH 128 // maximum file path name
#define USERSTACK 1 // user stack pages
#ifdef LAB_UTIL
#define USERSTACK 2 // user stack pages
#else
#define USERSTACK 1 // user stack pages
#endif

View File

@ -68,9 +68,6 @@ pipeclose(struct pipe *pi, int writable)
}
if(pi->readopen == 0 && pi->writeopen == 0){
release(&pi->lock);
#ifdef LAB_LOCK
freelock(&pi->lock);
#endif
kfree((char*)pi);
} else
release(&pi->lock);

View File

@ -8,52 +8,12 @@
#include "proc.h"
#include "defs.h"
#ifdef LAB_LOCK
#define NLOCK 500
static struct spinlock *locks[NLOCK];
struct spinlock lock_locks;
void
freelock(struct spinlock *lk)
{
acquire(&lock_locks);
int i;
for (i = 0; i < NLOCK; i++) {
if(locks[i] == lk) {
locks[i] = 0;
break;
}
}
release(&lock_locks);
}
static void
findslot(struct spinlock *lk) {
acquire(&lock_locks);
int i;
for (i = 0; i < NLOCK; i++) {
if(locks[i] == 0) {
locks[i] = lk;
release(&lock_locks);
return;
}
}
panic("findslot");
}
#endif
void
initlock(struct spinlock *lk, char *name)
{
lk->name = name;
lk->locked = 0;
lk->cpu = 0;
#ifdef LAB_LOCK
lk->nts = 0;
lk->n = 0;
findslot(lk);
#endif
}
// Acquire the lock.
@ -65,21 +25,12 @@ acquire(struct spinlock *lk)
if(holding(lk))
panic("acquire");
#ifdef LAB_LOCK
__sync_fetch_and_add(&(lk->n), 1);
#endif
// On RISC-V, sync_lock_test_and_set turns into an atomic swap:
// a5 = 1
// s1 = &lk->locked
// amoswap.w.aq a5, a5, (s1)
while(__sync_lock_test_and_set(&lk->locked, 1) != 0) {
#ifdef LAB_LOCK
__sync_fetch_and_add(&(lk->nts), 1);
#else
;
#endif
}
while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
;
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
@ -157,61 +108,3 @@ pop_off(void)
if(c->noff == 0 && c->intena)
intr_on();
}
// Read a shared 32-bit value without holding a lock
int
atomic_read4(int *addr) {
uint32 val;
__atomic_load(addr, &val, __ATOMIC_SEQ_CST);
return val;
}
#ifdef LAB_LOCK
int
snprint_lock(char *buf, int sz, struct spinlock *lk)
{
int n = 0;
if(lk->n > 0) {
n = snprintf(buf, sz, "lock: %s: #test-and-set %d #acquire() %d\n",
lk->name, lk->nts, lk->n);
}
return n;
}
int
statslock(char *buf, int sz) {
int n;
int tot = 0;
acquire(&lock_locks);
n = snprintf(buf, sz, "--- lock kmem/bcache stats\n");
for(int i = 0; i < NLOCK; i++) {
if(locks[i] == 0)
break;
if(strncmp(locks[i]->name, "bcache", strlen("bcache")) == 0 ||
strncmp(locks[i]->name, "kmem", strlen("kmem")) == 0) {
tot += locks[i]->nts;
n += snprint_lock(buf +n, sz-n, locks[i]);
}
}
n += snprintf(buf+n, sz-n, "--- top 5 contended locks:\n");
int last = 100000000;
// stupid way to compute top 5 contended locks
for(int t = 0; t < 5; t++) {
int top = 0;
for(int i = 0; i < NLOCK; i++) {
if(locks[i] == 0)
break;
if(locks[i]->nts > locks[top]->nts && locks[i]->nts < last) {
top = i;
}
}
n += snprint_lock(buf+n, sz-n, locks[top]);
last = locks[top]->nts;
}
n += snprintf(buf+n, sz-n, "tot= %d\n", tot);
release(&lock_locks);
return n;
}
#endif

View File

@ -5,9 +5,5 @@ struct spinlock {
// For debugging:
char *name; // Name of lock.
struct cpu *cpu; // The cpu holding the lock.
#ifdef LAB_LOCK
int nts;
int n;
#endif
};

View File

@ -1,88 +0,0 @@
#include <stdarg.h>
#include "types.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "riscv.h"
#include "defs.h"
static char digits[] = "0123456789abcdef";
static int
sputc(char *s, char c)
{
*s = c;
return 1;
}
static int
sprintint(char *s, int xx, int base, int sign)
{
char buf[16];
int i, n;
uint x;
if(sign && (sign = xx < 0))
x = -xx;
else
x = xx;
i = 0;
do {
buf[i++] = digits[x % base];
} while((x /= base) != 0);
if(sign)
buf[i++] = '-';
n = 0;
while(--i >= 0)
n += sputc(s+n, buf[i]);
return n;
}
int
snprintf(char *buf, unsigned long sz, const char *fmt, ...)
{
va_list ap;
int i, c;
int off = 0;
char *s;
va_start(ap, fmt);
for(i = 0; off < sz && (c = fmt[i] & 0xff) != 0; i++){
if(c != '%'){
off += sputc(buf+off, c);
continue;
}
c = fmt[++i] & 0xff;
if(c == 0)
break;
switch(c){
case 'd':
off += sprintint(buf+off, va_arg(ap, int), 10, 1);
break;
case 'x':
off += sprintint(buf+off, va_arg(ap, int), 16, 1);
break;
case 's':
if((s = va_arg(ap, char*)) == 0)
s = "(null)";
for(; *s && off < sz; s++)
off += sputc(buf+off, *s);
break;
case '%':
off += sputc(buf+off, '%');
break;
default:
// Print unknown % sequence to draw attention.
off += sputc(buf+off, '%');
off += sputc(buf+off, c);
break;
}
}
return off;
}

View File

@ -32,11 +32,6 @@ start()
w_mideleg(0xffff);
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
#ifdef KCSAN
// allow supervisor to read cycle counter register
w_mcounteren(r_mcounteren()|0x3);
#endif
// configure Physical Memory Protection to give supervisor mode
// access to all of physical memory.
w_pmpaddr0(0x3fffffffffffffull);

View File

@ -1,69 +0,0 @@
#include <stdarg.h>
#include "types.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "file.h"
#include "riscv.h"
#include "defs.h"
#define BUFSZ 4096
static struct {
struct spinlock lock;
char buf[BUFSZ];
int sz;
int off;
} stats;
int statscopyin(char*, int);
int statslock(char*, int);
int
statswrite(int user_src, uint64 src, int n)
{
return -1;
}
int
statsread(int user_dst, uint64 dst, int n)
{
int m;
acquire(&stats.lock);
if(stats.sz == 0) {
#ifdef LAB_PGTBL
stats.sz = statscopyin(stats.buf, BUFSZ);
#endif
#ifdef LAB_LOCK
stats.sz = statslock(stats.buf, BUFSZ);
#endif
}
m = stats.sz - stats.off;
if (m > 0) {
if(m > n)
m = n;
if(either_copyout(user_dst, dst, stats.buf+stats.off, m) != -1) {
stats.off += m;
}
} else {
m = -1;
stats.sz = 0;
stats.off = 0;
}
release(&stats.lock);
return m;
}
void
statsinit(void)
{
initlock(&stats.lock, "stats");
devsw[STATS].read = statsread;
devsw[STATS].write = statswrite;
}

View File

@ -212,28 +212,6 @@ alloc3_desc(int *idx)
return 0;
}
#ifdef LAB_LOCK
//
// check that there are at most NBUF distinct
// struct buf's, which the lock lab requires.
//
static struct buf *xbufs[NBUF];
static void
checkbuf(struct buf *b)
{
for(int i = 0; i < NBUF; i++){
if(xbufs[i] == b){
return;
}
if(xbufs[i] == 0){
xbufs[i] = b;
return;
}
}
panic("more than NBUF bufs");
}
#endif
void
virtio_disk_rw(struct buf *b, int write)
{
@ -241,10 +219,6 @@ virtio_disk_rw(struct buf *b, int write)
acquire(&disk.vdisk_lock);
#ifdef LAB_LOCK
checkbuf(b);
#endif
// the spec's Section 5.2 says that legacy block operations use
// three descriptors: one for type/reserved/sector, one for the
// data, one for a 1-byte status result.

View File

@ -1,400 +0,0 @@
#include "kernel/fcntl.h"
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/riscv.h"
#include "kernel/fs.h"
#include "user/user.h"
void test0();
void test1();
void test2();
void test3();
#define SZ 4096
char buf[SZ];
int
main(int argc, char *argv[])
{
test0();
test1();
test2();
test3();
exit(0);
}
void
createfile(char *file, int nblock)
{
int fd;
char buf[BSIZE];
int i;
fd = open(file, O_CREATE | O_RDWR);
if(fd < 0){
printf("createfile %s failed\n", file);
exit(-1);
}
for(i = 0; i < nblock; i++) {
if(write(fd, buf, sizeof(buf)) != sizeof(buf)) {
printf("write %s failed\n", file);
exit(-1);
}
}
close(fd);
}
void
readfile(char *file, int nbytes, int inc)
{
char buf[BSIZE];
int fd;
int i;
if(inc > BSIZE) {
printf("readfile: inc too large\n");
exit(-1);
}
if ((fd = open(file, O_RDONLY)) < 0) {
printf("readfile open %s failed\n", file);
exit(-1);
}
for (i = 0; i < nbytes; i += inc) {
if(read(fd, buf, inc) != inc) {
printf("read %s failed for block %d (%d)\n", file, i, nbytes);
exit(-1);
}
}
close(fd);
}
int ntas(int print)
{
int n;
char *c;
if (statistics(buf, SZ) <= 0) {
fprintf(2, "ntas: no stats\n");
}
c = strchr(buf, '=');
n = atoi(c+2);
if(print)
printf("%s", buf);
return n;
}
// Test reading small files concurrently
void
test0()
{
char file[2];
char dir[2];
enum { N = 10, NCHILD = 3 };
int m, n;
dir[0] = '0';
dir[1] = '\0';
file[0] = 'F';
file[1] = '\0';
printf("start test0\n");
for(int i = 0; i < NCHILD; i++){
dir[0] = '0' + i;
mkdir(dir);
if (chdir(dir) < 0) {
printf("chdir failed\n");
exit(1);
}
unlink(file);
createfile(file, N);
if (chdir("..") < 0) {
printf("chdir failed\n");
exit(1);
}
}
m = ntas(0);
for(int i = 0; i < NCHILD; i++){
dir[0] = '0' + i;
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
if (chdir(dir) < 0) {
printf("chdir failed\n");
exit(1);
}
readfile(file, N*BSIZE, 1);
exit(0);
}
}
int status = 0;
for(int i = 0; i < NCHILD; i++){
wait(&status);
if (status != 0) {
printf("FAIL: a child failed\n");
exit(1);
}
}
printf("test0 results:\n");
n = ntas(1);
if (n-m < 500)
printf("test0: OK\n");
else
printf("test0: FAIL\n");
}
// Test bcache evictions by reading a large file concurrently
void test1()
{
char file[3];
enum { N = 200, BIG=100, NCHILD=2 };
printf("start test1\n");
file[0] = 'B';
file[2] = '\0';
for(int i = 0; i < NCHILD; i++){
file[1] = '0' + i;
unlink(file);
if (i == 0) {
createfile(file, BIG);
} else {
createfile(file, 1);
}
}
for(int i = 0; i < NCHILD; i++){
file[1] = '0' + i;
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
if (i==0) {
for (i = 0; i < N; i++) {
readfile(file, BIG*BSIZE, BSIZE);
}
unlink(file);
exit(0);
} else {
for (i = 0; i < N*20; i++) {
readfile(file, 1, BSIZE);
}
unlink(file);
}
exit(0);
}
}
int status = 0;
for(int i = 0; i < NCHILD; i++){
wait(&status);
if (status != 0) {
printf("FAIL: a child failed\n");
exit(1);
}
}
printf("\ntest1 OK\n");
}
//
// test concurrent creates.
//
void
test2()
{
int nc = 4;
char file[16];
printf("start test2\n");
mkdir("d2");
file[0] = 'd';
file[1] = '2';
file[2] = '/';
// remove any stale existing files.
for(int i = 0; i < 50; i++){
for(int ci = 0; ci < nc; ci++){
file[3] = 'a' + ci;
file[4] = '0' + i;
file[5] = '\0';
unlink(file);
}
}
int pids[nc];
for(int ci = 0; ci < nc; ci++){
pids[ci] = fork();
if(pids[ci] < 0){
printf("test2: fork failed\n");
exit(1);
}
if(pids[ci] == 0){
char me = "abcdefghijklmnop"[ci];
int pid = getpid();
int nf = (ci == 0 ? 10 : 15);
// create nf files.
for(int i = 0; i < nf; i++){
file[3] = me;
file[4] = '0' + i;
file[5] = '\0';
int fd = open(file, O_CREATE | O_RDWR);
if(fd < 0){
printf("test2: create %s failed\n", file);
exit(1);
}
int xx = (pid << 16) | i;
for(int nw = 0; nw < 2; nw++){
// the sleep() increases the chance of simultaneous
// calls to bget().
sleep(1);
if(write(fd, &xx, sizeof(xx)) <= 0){
printf("test2: write %s failed\n", file);
exit(1);
}
}
close(fd);
}
// read back the nf files.
for(int i = 0; i < nf; i++){
file[3] = me;
file[4] = '0' + i;
file[5] = '\0';
// printf("r %s\n", file);
int fd = open(file, O_RDWR);
if(fd < 0){
printf("test2: open %s failed\n", file);
exit(1);
}
int xx = (pid << 16) | i;
for(int nr = 0; nr < 2; nr++){
int z = 0;
sleep(1);
int n = read(fd, &z, sizeof(z));
if(n != sizeof(z)){
printf("test2: read %s returned %d, expected %ld\n", file, n, sizeof(z));
exit(1);
}
if(z != xx){
printf("test2: file %s contained %d, not %d\n", file, z, xx);
exit(1);
}
}
close(fd);
}
// delete the nf files.
for(int i = 0; i < nf; i++){
file[3] = me;
file[4] = '0' + i;
file[5] = '\0';
//printf("u %s\n", file);
if(unlink(file) != 0){
printf("test2: unlink %s failed\n", file);
exit(1);
}
}
exit(0);
}
}
int ok = 1;
for(int ci = 0; ci < nc; ci++){
int st = 0;
int ret = wait(&st);
if(ret <= 0){
printf("test2: wait() failed\n");
ok = 0;
}
if(st != 0)
ok = 0;
}
if(ok) {
printf("\ntest2 OK\n");
} else {
printf("test2 failed\n");
}
}
//
// generate big log transactions to check that bget() can
// make use of any of the NBUF buffers for any block number.
//
void
test3()
{
int nc = 5;
char file[16];
printf("start test3\n");
mkdir("d2");
file[0] = 'd';
file[1] = '2';
file[2] = '/';
int pids[nc];
for(int ci = 0; ci < nc; ci++){
pids[ci] = fork();
if(pids[ci] < 0){
printf("test3: fork failed\n");
exit(1);
}
if(pids[ci] == 0){
file[3] = 'a' + ci;
file[4] = '\0';
unlink(file);
int fd = open(file, O_CREATE | O_RDWR);
if(fd < 0){
printf("test3: create %s failed\n", file);
exit(1);
}
write(fd, "x", 1);
static char junk[12*512];
for(int i = 0; i < 12; i++){
sleep(1);
write(fd, junk, sizeof(junk));
}
exit(0);
}
}
int ok = 1;
for(int ci = 0; ci < nc; ci++){
int st = 0;
int ret = wait(&st);
if(ret <= 0){
printf("test3: wait() failed\n");
ok = 0;
}
if(st != 0)
ok = 0;
}
for(int ci = 0; ci < nc; ci++){
file[3] = 'a' + ci;
file[4] = '\0';
unlink(file);
}
if(ok) {
printf("\ntest3 OK\n");
} else {
printf("test3 failed\n");
}
}

View File

@ -18,7 +18,6 @@ main(void)
if(open("console", O_RDWR) < 0){
mknod("console", CONSOLE, 0);
mknod("statistics", STATS, 0);
open("console", O_RDWR);
}
dup(0); // stdout

View File

@ -1,198 +0,0 @@
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/riscv.h"
#include "kernel/memlayout.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#define NCHILD 2
#define N 100000
#define SZ 4096
void test1(void);
void test2(void);
void test3(void);
char buf[SZ];
int countfree();
int
main(int argc, char *argv[])
{
test1();
test2();
test3();
exit(0);
}
int ntas(int print)
{
int n;
char *c;
if (statistics(buf, SZ) <= 0) {
fprintf(2, "ntas: no stats\n");
}
c = strchr(buf, '=');
n = atoi(c+2);
if(print)
printf("%s", buf);
return n;
}
// Test concurrent kallocs and kfrees
void test1(void)
{
void *a, *a1;
int n, m;
printf("start test1\n");
m = ntas(0);
for(int i = 0; i < NCHILD; i++){
int pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
for(i = 0; i < N; i++) {
a = sbrk(4096);
*(int *)(a+4) = 1;
a1 = sbrk(-4096);
if (a1 != a + 4096) {
printf("test1: FAIL wrong sbrk\n");
exit(1);
}
}
exit(0);
}
}
int status = 0;
for(int i = 0; i < NCHILD; i++){
wait(&status);
if (status != 0) {
printf("FAIL: a child failed\n");
exit(1);
}
}
printf("test1 results:\n");
n = ntas(1);
if(n-m < 10)
printf("test1 OK\n");
else
printf("test1 FAIL\n");
}
// Test stealing
void test2() {
int free0 = countfree();
int free1;
int n = (PHYSTOP-KERNBASE)/PGSIZE;
printf("start test2\n");
printf("total free number of pages: %d (out of %d)\n", free0, n);
if(n - free0 > 1000) {
printf("test2 FAILED: cannot allocate enough memory");
exit(1);
}
for (int i = 0; i < 50; i++) {
free1 = countfree();
if(i % 10 == 9)
printf(".");
if(free1 != free0) {
printf("test2 FAIL: losing pages %d %d\n", free0, free1);
exit(1);
}
}
printf("\ntest2 OK\n");
}
// Test concurrent kalloc/kfree and stealing
void test3(void)
{
uint64 a, a1;
int n, m;
m = ntas(0);
printf("start test3\n");
int pid;
for(int i = 0; i < NCHILD; i++){
pid = fork();
if(pid < 0){
printf("fork failed");
exit(-1);
}
if(pid == 0){
if (i == 0) {
for(i = 0; i < N; i++) {
a = (uint64) sbrk(4096);
if(a == 0xffffffffffffffff){
// no freemem
continue;
}
*(int *)(a+4) = 1;
a1 = (uint64) sbrk(-4096);
if (a1 != a + 4096) {
printf("test3 FAIL: wrong sbrk\n");
exit(1);
}
if ((i + 1) % 10000 == 0) {
printf(".");
}
}
printf("child done %d\n", i);
exit(0);
} else {
while (1) {
int free0 = countfree();
int free1 = countfree();
if(free0 - free1 > 1) {
printf("test3 FAIL: losing pages %d %d\n", free0, free1);
exit(1);
}
}
}
}
}
int status = 0;
for(int i = 0; i < NCHILD-1; i++){
wait(&status);
if (status != 0) {
printf("a child failed\n");
exit(1);
}
}
kill(pid);
n = ntas(1);
if(n-m < 4000)
printf("\ntest3 OK\n");
else
printf("test3 FAIL m %d n %d\n", m, n);
}
//
// countfree() from usertests.c
//
int
countfree()
{
uint64 sz0 = (uint64)sbrk(0);
int n = 0;
while(1){
uint64 a = (uint64) sbrk(4096);
if(a == 0xffffffffffffffff){
break;
}
// modify the memory to make sure it's really allocated.
*(char *)(a + 4096 - 1) = 1;
n += 1;
}
sbrk(-((uint64)sbrk(0) - sz0));
return n;
}

View File

@ -1,24 +0,0 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
int
statistics(void *buf, int sz)
{
int fd, i, n;
fd = open("statistics", O_RDONLY);
if(fd < 0) {
fprintf(2, "stats: open failed\n");
exit(1);
}
for (i = 0; i < sz; ) {
if ((n = read(fd, buf+i, sz-i)) < 0) {
break;
}
i += n;
}
close(fd);
return i;
}

View File

@ -1,24 +0,0 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
#include "user/user.h"
#define SZ 4096
char buf[SZ];
int
main(void)
{
int i, n;
while (1) {
n = statistics(buf, SZ);
for (i = 0; i < n; i++) {
write(1, buf+i, 1);
}
if (n != SZ)
break;
}
exit(0);
}

View File

@ -1,7 +1,3 @@
#ifdef LAB_MMAP
typedef unsigned long size_t;
typedef long int off_t;
#endif
struct stat;
// system calls
@ -26,17 +22,6 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
#ifdef LAB_NET
int bind(uint16);
int unbind(uint16);
int send(uint16, uint32, uint16, char *, uint32);
int recv(uint16, uint32*, uint16*, char *, uint32);
#endif
#ifdef LAB_PGTBL
int ugetpid(void);
uint64 pgpte(void*);
void kpgtbl(void);
#endif
// ulib.c
int stat(const char*, struct stat*);
@ -52,9 +37,6 @@ void* memset(void*, int, uint);
int atoi(const char*);
int memcmp(const void *, const void *, uint);
void *memcpy(void *, const void *, uint);
#ifdef LAB_LOCK
int statistics(void*, int);
#endif
// umalloc.c
void* malloc(uint);

6
user/xargstest.sh Normal file
View File

@ -0,0 +1,6 @@
mkdir a
echo hello > a/b
mkdir c
echo hello > c/b
echo hello > b
find . b | xargs grep hello