summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrtm <rtm>2006-08-15 22:18:20 +0000
committerrtm <rtm>2006-08-15 22:18:20 +0000
commit350e63f7a9b1be695c0cf69e380bd96733524f25 (patch)
tree782259566c4596eef115590ff3e054fc7c5f3718
parent69332d1918fda38b25fc3ec8c786d16bb17e9e68 (diff)
downloadxv6-labs-350e63f7a9b1be695c0cf69e380bd96733524f25.tar.gz
xv6-labs-350e63f7a9b1be695c0cf69e380bd96733524f25.tar.bz2
xv6-labs-350e63f7a9b1be695c0cf69e380bd96733524f25.zip
no more proc[] entry per cpu for idle loop
each cpu[] has its own gdt and tss no per-proc gdt or tss, re-write cpu's in scheduler (you win, cliff) main0() switches to cpu[0].mpstack
-rw-r--r--Makefile10
-rw-r--r--Notes285
-rw-r--r--fstests.c380
-rw-r--r--main.c23
-rw-r--r--proc.c69
-rw-r--r--proc.h4
-rw-r--r--trap.c7
-rw-r--r--usertests.c294
8 files changed, 456 insertions, 616 deletions
diff --git a/Makefile b/Makefile
index a476cf4..9a6b02d 100644
--- a/Makefile
+++ b/Makefile
@@ -67,6 +67,10 @@ usertests : usertests.o $(ULIB)
$(LD) -N -e main -Ttext 0 -o usertests usertests.o $(ULIB)
$(OBJDUMP) -S usertests > usertests.asm
+fstests : fstests.o $(ULIB)
+ $(LD) -N -e main -Ttext 0 -o fstests fstests.o $(ULIB)
+ $(OBJDUMP) -S fstests > fstests.asm
+
echo : echo.o $(ULIB)
$(LD) -N -e main -Ttext 0 -o echo echo.o $(ULIB)
$(OBJDUMP) -S echo > echo.asm
@@ -102,12 +106,12 @@ rm : rm.o $(ULIB)
mkfs : mkfs.c fs.h
cc -o mkfs mkfs.c
-fs.img : mkfs userfs usertests echo cat readme init sh ls mkdir rm
- ./mkfs fs.img userfs usertests echo cat readme init sh ls mkdir rm
+fs.img : mkfs userfs usertests echo cat readme init sh ls mkdir rm fstests
+ ./mkfs fs.img userfs usertests echo cat readme init sh ls mkdir rm fstests
-include *.d
clean :
rm -f *.o *.d *.asm vectors.S parport.out \
bootblock kernel xv6.img user1 userfs usertests \
- fs.img mkfs echo init
+ fs.img mkfs echo init fstests
diff --git a/Notes b/Notes
index ab28ec3..dd81f1b 100644
--- a/Notes
+++ b/Notes
@@ -22,32 +22,14 @@ no kernel malloc(), just kalloc() for user core
user pointers aren't valid in the kernel
-setting up first process
- we do want a process zero, as template
- but not runnable
- just set up return-from-trap frame on new kernel stack
- fake user program that calls exec
-
-map text read-only?
-shared text?
-
-what's on the stack during a trap or sys call?
- PUSHA before scheduler switch? for callee-saved registers.
- segment contents?
- what does iret need to get out of the kernel?
- how does INT know what kernel stack to use?
-
-are interrupts turned on in the kernel? probably.
-
-per-cpu curproc
-one tss per process, or one per cpu?
-one segment array per cpu, or per process?
+are interrupts turned on in the kernel? yes.
pass curproc explicitly, or implicit from cpu #?
e.g. argument to newproc()?
hmm, you need a global curproc[cpu] for trap() &c
-test stack expansion
+no stack expansion
+
test running out of memory, process slots
we can't really use a separate stack segment, since stack addresses
@@ -56,16 +38,6 @@ data vs text. how can we have a gap between data and stack, so that
both can grow, without committing 4GB of physical memory? does this
mean we need paging?
-what's the simplest way to add the paging we need?
- one page table, re-write it each time we leave the kernel?
- page table per process?
- probably need to use 0-0xffffffff segments, so that
- both data and stack pointers always work
- so is it now worth it to make a process's phys mem contiguous?
- or could use segment limits and 4 meg pages?
- but limits would prevent using stack pointers as data pointers
- how to write-protect text? not important?
-
perhaps have fixed-size stack, put it in the data segment?
oops, if kernel stack is in contiguous user phys mem, then moving
@@ -87,19 +59,6 @@ test children being inherited by grandparent &c
some sleep()s should be interruptible by kill()
-cli/sti in acquire/release should nest!
- in case you acquire two locks
-
-what would need fixing if we got rid of kernel_lock?
- console output
- proc_exit() needs lock on proc *array* to deallocate
- kill() needs lock on proc *array*
- allocator's free list
- global fd table (really free-ness)
- sys_close() on fd table
- fork on proc list, also next pid
- hold lock until public slots in proc struct initialized
-
locks
init_lock
sequences CPU startup
@@ -110,37 +69,17 @@ locks
memory allocator
printf
-wakeup needs proc_table_lock
- so we need recursive locks?
- or you must hold the lock to call wakeup?
-
in general, the table locks protect both free-ness and
public variables of table elements
in many cases you can use table elements w/o a lock
e.g. if you are the process, or you are using an fd
-lock code shouldn't call cprintf...
-
-nasty hack to allow locks before first process,
- and to allow them in interrupts when curproc may be zero
-
-race between release and sleep in sys_wait()
-race between sys_exit waking up parent and setting state=ZOMBIE
-race in pipe code when full/empty
-
lock order
per-pipe lock
proc_table_lock fd_table_lock kalloc_lock
console_lock
-condition variable + mutex that protects it
- proc * (for wait()), proc_table_lock
- pipe structure, pipe lock
-
-systematic way to test sleep races?
- print something at the start of sleep?
-
-do you have to be holding the mutex in order to call wakeup()?
+do you have to be holding the mutex in order to call wakeup()? yes
device interrupts don't clear FL_IF
so a recursive timer interrupt is possible
@@ -156,202 +95,11 @@ inode->count counts in-memory pointers to the struct
blocks and inodes have ad-hoc sleep-locks
provide a single mechanism?
-need to lock bufs in bio between bread and brelse
-
test 14-character file names
and file arguments longer than 14
-and directories longer than one sector
kalloc() can return 0; do callers handle this right?
-why directing interrupts to cpu 1 causes trouble
- cpu 1 turns on interrupts with no tss!
- and perhaps a stale gdt (from boot)
- since it has never run a process, never called setupsegs()
- but does cpu really need the tss?
- not switching stacks
- fake process per cpu, just for tss?
- seems like a waste
- move tss to cpu[]?
- but tss points to per-process kernel stack
- would also give us a gdt
- OOPS that wasn't the problem
-
-wait for other cpu to finish starting before enabling interrupts?
- some kind of crash in ide_init ioapic_enable cprintf
-move ide_init before mp_start?
- didn't do any good
- maybe cpu0 taking ide interrupt, cpu1 getting a nested lock error
-
-cprintfs are screwed up if locking is off
- often loops forever
- hah, just use lpt alone
-
-looks like cpu0 took the ide interrupt and was the last to hold
-the lock, but cpu1 thinks it is nested
-cpu0 is in load_icode / printf / cons_putc
- probably b/c cpu1 cleared use_console_lock
-cpu1 is in scheduler() / printf / acquire
-
- 1: init timer
- 0: init timer
- cpu 1 initial nlock 1
- ne0s:t iidd el_occnkt rc
- onsole cpu 1 old caller stack 1001A5 10071D 104DFF 1049FE
- panic: acquire
- ^CNext at t=33002418
- (0) [0x00100091] 0008:0x00100091 (unk. ctxt): jmp .+0xfffffffe ; ebfe
- (1) [0x00100332] 0008:0x00100332 (unk. ctxt): jmp .+0xfffffffe
-
-why is output interleaved even before panic?
-
-does release turn on interrupts even inside an interrupt handler?
-
-overflowing cpu[] stack?
- probably not, change from 512 to 4096 didn't do anything
-
-
- 1: init timer
- 0: init timer
- cnpeus te11 linnitki aclo nnoolleek cp1u
- ss oarltd sccahleldeul esrt aocnk cpu 0111 Ej6 buf1 01A3140 C5118
- 0
- la anic1::7 0a0c0 uuirr e
- ^CNext at t=31691050
- (0) [0x00100373] 0008:0x00100373 (unk. ctxt): jmp .+0xfffffffe ; ebfe
- (1) [0x00100091] 0008:0x00100091 (unk. ctxt): jmp .+0xfffffffe ; ebfe
-
-cpu0:
-
-0: init timer
-nested lock console cpu 0 old caller stack 1001e6 101a34 1 0
- (that's mpmain)
-panic: acquire
-
-cpu1:
-
-1: init timer
-cpu 1 initial nlock 1
-start scheduler on cpu 1 jmpbuf ...
-la 107000 lr ...
- that is, nlock != 0
-
-maybe a race; acquire does
- locked = 1
- cpu = cpu()
-what if another acquire calls holding w/ locked = 1 but
- before cpu is set?
-
-if I type a lot (kbd), i get a panic
-cpu1 in scheduler: panic "holding locks in scheduler"
-cpu0 also in the same panic!
-recursive interrupt?
- FL_IF is probably set during interrupt... is that correct?
-again:
- olding locks in scheduler
- trap v 33 eip 100ED3 c (that is, interrupt while holding a lock)
- 100ed3 is in lapic_write
-again:
- trap v 33 eip 102A3C cpu 1 nlock 1 (in acquire)
- panic: interrupt while holding a lock
-again:
- trap v 33 eip 102A3C cpu 1 nlock 1
- panic: interrupt while holding a lock
-OR is it the cprintf("kbd overflow")?
- no, get panic even w/o that cprintf
-OR a release() at interrupt time turns interrupts back on?
- of course i don't think they were off...
-OK, fixing trap.c to make interrupts turn off FL_IF
- that makes it take longer, but still panics
- (maybe b/c release sets FL_IF)
-
-shouldn't something (PIC?) prevent recursive interrupts of same IRQ?
- or should FL_IF be clear during all interrupts?
-
-maybe acquire should remember old FL_IF value, release should restore
- if acquire did cli()
-
-DUH the increment of nlock in acquire() happens before the cli!
- so the panic is probably not a real problem
- test nlock, cli(), then increment?
-
-BUT now userfs doesn't do the final cat README
-
-AND w/ cprintf("kbd overflow"), panic holding locks in scheduler
- maybe also simulataneous panic("interrupt while holding a lock")
-
-again (holding down x key):
- kbd overflow
- kbd oaaniicloowh
- olding locks in scheduler
- trap v 33 eip 100F5F c^CNext at t=32166285
- (0) [0x0010033e] 0008:0010033e (unk. ctxt): jmp .+0xfffffffe (0x0010033e) ; ebfe
- (1) [0x0010005c] 0008:0010005c (unk. ctxt): jmp .+0xfffffffe (0x0010005c) ; ebfe
-cpu0 paniced due to holding locks in scheduler
-cpu1 got panic("interrupt while holding a lock")
- again in lapic_write.
- while re-enabling an IRQ?
-
-again:
-cpu 0 panic("holding locks in scheduler")
- but didn't trigger related panics earlier in scheduler or sched()
- of course the panic is right after release() and thus sti()
- so we may be seeing an interrupt that left locks held
-cpu 1 unknown panic
-why does it happen to both cpus at the same time?
-
-again:
-cpu 0 panic("holding locks in scheduler")
- but trap() didn't see any held locks on return
-cpu 1 no apparent panic
-
-again:
-cpu 0 panic: holding too many locks in scheduler
-cpu 1 panic: kbd_intr returned while holding a lock
-
-again:
-cpu 0 panic: holding too man
- la 10d70c lr 10027b
- those don't seem to be locks...
- only place non-constant lock is used is sleep()'s 2nd arg
- maybe register not preserved across context switch?
- it's in %esi...
- sched() doesn't touch %esi
- %esi is evidently callee-saved
- something to do with interrupts? since ordinarily it works
-cpu 1 panic: kbd_int returned while holding a lock
- la 107340 lr 107300
- console_lock and kbd_lock
-
-maybe console_lock is often not released due to change
- in use_console_lock (panic on other cpu)
-
-again:
-cpu 0: panic: h...
- la 10D78C lr 102CA0
-cpu 1: panic: acquire FL_IF (later than cpu 0)
-
-but if sleep() were acquiring random locks, we'd see panics
-in release, after sleep() returned.
-actually when system is idle, maybe no-one sleeps at all.
- just scheduler() and interrupts
-
-questions:
- does userfs use pipes? or fork?
- no
- does anything bad happen if process 1 exits? eg exit() in cat.c
- looks ok
- are there really no processes left?
- lock_init() so we can have a magic number?
-
-HMM maybe the variables at the end of struct cpu are being overwritten
- nlocks, lastacquire, lastrelease
- by cpu->stack?
- adding junk buffers maybe causes crash to take longer...
- when do we run on cpu stack?
- just in scheduler()?
- and interrupts from scheduler()
-
OH! recursive interrupts will use up any amount of cpu[].stack!
underflow and wrecks *previous* cpu's struct
@@ -360,15 +108,26 @@ mkdir
sh arguments
sh redirection
indirect blocks
-two bugs in unlink: don't just return if nlink > 0,
- and search for name, not inum
is there a create/create race for same file name?
resulting in two entries w/ same name in directory?
+why does shell often ignore first line of input?
test: one process unlinks a file while another links to it
-test: simultaneous create of same file
test: one process opens a file while another deletes it
-
-wdir should use writei, to avoid special-case block allocation
- also readi
- is dir locked? probably
+test: mkdir. deadlock d/.. vs ../d
+
+make proc[0] runnable
+cpu early tss and gdt
+how do we get cpu0 scheduler() to use mpstack, not proc[0].kstack?
+when iget() first sleeps, where does it longjmp to?
+maybe set up proc[0] to be runnable, with entry proc0main(), then
+ have main() call scheduler()?
+ perhaps so proc[0] uses right kstack?
+ and scheduler() uses mpstack?
+ltr sets the busy bit in the TSS, faults if already set
+ so gdt and TSS per cpu?
+ we don't want to be using some random process's gdt when it changes it.
+maybe get rid of per-proc gdt and ts
+ one per cpu
+ refresh it when needed
+ setupsegs(proc *)
diff --git a/fstests.c b/fstests.c
new file mode 100644
index 0000000..d6f630e
--- /dev/null
+++ b/fstests.c
@@ -0,0 +1,380 @@
+#include "user.h"
+#include "fcntl.h"
+
+char buf[512];
+
+// two processes write to the same file descriptor
+// is the offset shared? does inode locking work?
+void
+sharedfd()
+{
+ int fd, pid, i, n, nc, np;
+ char buf[10];
+
+ unlink("sharedfd");
+ fd = open("sharedfd", O_CREATE|O_RDWR);
+ if(fd < 0){
+ printf(1, "fstests: cannot open sharedfd for writing");
+ return;
+ }
+ pid = fork();
+ memset(buf, pid==0?'c':'p', sizeof(buf));
+ for(i = 0; i < 100; i++){
+ if(write(fd, buf, sizeof(buf)) != sizeof(buf)){
+ printf(1, "fstests: write sharedfd failed\n");
+ break;
+ }
+ }
+ if(pid == 0)
+ exit();
+ else
+ wait();
+ close(fd);
+ fd = open("sharedfd", 0);
+ if(fd < 0){
+ printf(1, "fstests: cannot open sharedfd for reading\n");
+ return;
+ }
+ nc = np = 0;
+ while((n = read(fd, buf, sizeof(buf))) > 0){
+ for(i = 0; i < sizeof(buf); i++){
+ if(buf[i] == 'c')
+ nc++;
+ if(buf[i] == 'p')
+ np++;
+ }
+ }
+ close(fd);
+ unlink("sharedfd");
+ if(nc == 1000 && np == 1000)
+ printf(1, "sharedfd ok\n");
+ else
+ printf(1, "sharedfd oops %d %d\n", nc, np);
+}
+
+// two processes write two different files at the same
+// time, to test block allocation.
+void
+twofiles()
+{
+ int fd, pid, i, j, n, total;
+ char *fname;
+
+ unlink("f1");
+ unlink("f2");
+
+ pid = fork();
+ if(pid < 0){
+ puts("fork failed\n");
+ return;
+ }
+
+ fname = pid ? "f1" : "f2";
+ fd = open(fname, O_CREATE | O_RDWR);
+ if(fd < 0){
+ puts("create failed\n");
+ exit();
+ }
+
+ memset(buf, pid?'p':'c', 512);
+ for(i = 0; i < 12; i++){
+ if((n = write(fd, buf, 500)) != 500){
+ printf(1, "write failed %d\n", n);
+ exit();
+ }
+ }
+ close(fd);
+ if(pid)
+ wait();
+ else
+ exit();
+
+ for(i = 0; i < 2; i++){
+ fd = open(i?"f1":"f2", 0);
+ total = 0;
+ while((n = read(fd, buf, sizeof(buf))) > 0){
+ for(j = 0; j < n; j++){
+ if(buf[j] != (i?'p':'c')){
+ puts("wrong char\n");
+ exit();
+ }
+ }
+ total += n;
+ }
+ close(fd);
+ if(total != 12*500){
+ printf(1, "wrong length %d\n", total);
+ exit();
+ }
+ }
+
+ unlink("f1");
+ unlink("f2");
+
+ puts("twofiles ok\n");
+}
+
+// two processes create and delete files in same directory
+void
+createdelete()
+{
+ int pid, i, fd;
+ int n = 20;
+ char name[32];
+
+ pid = fork();
+ if(pid < 0){
+ puts("fork failed\n");
+ exit();
+ }
+
+ name[0] = pid ? 'p' : 'c';
+ name[2] = '\0';
+ for(i = 0; i < n; i++){
+ name[1] = '0' + i;
+ fd = open(name, O_CREATE | O_RDWR);
+ if(fd < 0){
+ puts("create failed\n");
+ exit();
+ }
+ close(fd);
+ if(i > 0 && (i % 2 ) == 0){
+ name[1] = '0' + (i / 2);
+ if(unlink(name) < 0){
+ puts("unlink failed\n");
+ exit();
+ }
+ }
+ }
+
+ if(pid)
+ wait();
+ else
+ exit();
+
+ for(i = 0; i < n; i++){
+ name[0] = 'p';
+ name[1] = '0' + i;
+ fd = open(name, 0);
+ if((i == 0 || i >= n/2) && fd < 0){
+ printf(1, "oops createdelete %s didn't exist\n", name);
+ } else if((i >= 1 && i < n/2) && fd >= 0){
+ printf(1, "oops createdelete %s did exist\n", name);
+ }
+ if(fd >= 0)
+ close(fd);
+
+ name[0] = 'c';
+ name[1] = '0' + i;
+ fd = open(name, 0);
+ if((i == 0 || i >= n/2) && fd < 0){
+ printf(1, "oops createdelete %s didn't exist\n", name);
+ } else if((i >= 1 && i < n/2) && fd >= 0){
+ printf(1, "oops createdelete %s did exist\n", name);
+ }
+ if(fd >= 0)
+ close(fd);
+ }
+
+ for(i = 0; i < n; i++){
+ name[0] = 'p';
+ name[1] = '0' + i;
+ unlink(name);
+ name[0] = 'c';
+ unlink(name);
+ }
+
+ printf(1, "createdelete ok\n");
+}
+
+// can I unlink a file and still read it?
+void
+unlinkread()
+{
+ int fd, fd1;
+
+ fd = open("unlinkread", O_CREATE | O_RDWR);
+ if(fd < 0){
+ puts("create unlinkread failed\n");
+ exit();
+ }
+ write(fd, "hello", 5);
+ close(fd);
+
+ fd = open("unlinkread", O_RDWR);
+ if(fd < 0){
+ puts("open unlinkread failed\n");
+ exit();
+ }
+ if(unlink("unlinkread") != 0){
+ puts("unlink unlinkread failed\n");
+ exit();
+ }
+
+ fd1 = open("xxx", O_CREATE | O_RDWR);
+ write(fd1, "yyy", 3);
+ close(fd1);
+
+ if(read(fd, buf, sizeof(buf)) != 5){
+ puts("unlinkread read failed");
+ exit();
+ }
+ if(buf[0] != 'h'){
+ puts("unlinkread wrong data\n");
+ exit();
+ }
+ if(write(fd, buf, 10) != 10){
+ puts("unlinkread write failed\n");
+ exit();
+ }
+ close(fd);
+ unlink("xxx");
+ puts("unlinkread ok\n");
+}
+
+void
+linktest()
+{
+ int fd;
+
+ unlink("lf1");
+ unlink("lf2");
+
+ fd = open("lf1", O_CREATE|O_RDWR);
+ if(fd < 0){
+ puts("create lf1 failed\n");
+ exit();
+ }
+ if(write(fd, "hello", 5) != 5){
+ puts("write lf1 failed\n");
+ exit();
+ }
+ close(fd);
+
+ if(link("lf1", "lf2") < 0){
+ puts("link lf1 lf2 failed\n");
+ exit();
+ }
+ unlink("lf1");
+
+ if(open("lf1", 0) >= 0){
+ puts("unlinked lf1 but it is still there!\n");
+ exit();
+ }
+
+ fd = open("lf2", 0);
+ if(fd < 0){
+ puts("open lf2 failed\n");
+ exit();
+ }
+ if(read(fd, buf, sizeof(buf)) != 5){
+ puts("read lf2 failed\n");
+ exit();
+ }
+ close(fd);
+
+ if(link("lf2", "lf2") >= 0){
+ puts("link lf2 lf2 succeeded! oops\n");
+ exit();
+ }
+
+ unlink("lf2");
+ if(link("lf2", "lf1") >= 0){
+ puts("link non-existant succeeded! oops\n");
+ exit();
+ }
+
+ if(link(".", "lf1") >= 0){
+ puts("link . lf1 succeeded! oops\n");
+ exit();
+ }
+
+ puts("linktest ok\n");
+}
+
+// test concurrent create of the same file
+void
+concreate()
+{
+ char file[3];
+ int i, pid, n, fd;
+ char fa[40];
+ struct {
+ unsigned short inum;
+ char name[14];
+ } de;
+
+ file[0] = 'C';
+ file[2] = '\0';
+ for(i = 0; i < 40; i++){
+ file[1] = '0' + i;
+ unlink(file);
+ pid = fork();
+ if(pid && (i % 3) == 1){
+ link("C0", file);
+ } else if(pid == 0 && (i % 5) == 1){
+ link("C0", file);
+ } else {
+ fd = open(file, O_CREATE | O_RDWR);
+ if(fd < 0){
+ puts("concreate create failed\n");
+ exit();
+ }
+ close(fd);
+ }
+ if(pid == 0)
+ exit();
+ else
+ wait();
+ }
+
+ memset(fa, 0, sizeof(fa));
+ fd = open(".", 0);
+ n = 0;
+ while(read(fd, &de, sizeof(de)) > 0){
+ if(de.inum == 0)
+ continue;
+ if(de.name[0] == 'C' && de.name[2] == '\0'){
+ i = de.name[1] - '0';
+ if(i < 0 || i >= sizeof(fa)){
+ printf(1, "concreate weird file %s\n", de.name);
+ exit();
+ }
+ if(fa[i]){
+ printf(1, "concreate duplicate file %s\n", de.name);
+ exit();
+ }
+ fa[i] = 1;
+ n++;
+ }
+ }
+ close(fd);
+
+ if(n != 40){
+ puts("concreate not enough files in directory listing\n");
+ exit();
+ }
+
+ for(i = 0; i < 40; i++){
+ file[1] = '0' + i;
+ unlink(file);
+ }
+
+ puts("concreate ok\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ puts("fstests starting\n");
+
+ concreate();
+ linktest();
+ unlinkread();
+ createdelete();
+ twofiles();
+ sharedfd();
+
+ puts("fstests finished\n");
+ exit();
+}
diff --git a/main.c b/main.c
index d877208..35ea672 100644
--- a/main.c
+++ b/main.c
@@ -29,10 +29,14 @@ main0(void)
// clear BSS
memset(edata, 0, end - edata);
+ // switch to cpu0's cpu stack
+ asm volatile("movl %0, %%esp" : : "r" (cpus[0].mpstack + MPSTACK - 32));
+ asm volatile("movl %0, %%ebp" : : "r" (cpus[0].mpstack + MPSTACK));
+
// Make sure interrupts stay disabled on all processors
// until each signals it is ready, by pretending to hold
// an extra lock.
- // xxx maybe replace w/ acquire remembering if FL_IF
+ // xxx maybe replace w/ acquire remembering if FL_IF was already clear
for(i=0; i<NCPU; i++){
cpus[i].nlock++;
cpus[i].guard1 = 0xdeadbeef;
@@ -55,16 +59,9 @@ main0(void)
fd_init();
iinit();
- // create a fake process per CPU
- // so each CPU always has a tss and a gdt
- for(p = &proc[0]; p < &proc[NCPU]; p++){
- p->state = IDLEPROC;
- p->kstack = cpus[p-proc].mpstack;
- p->pid = p - proc;
- }
-
// fix process 0 so that copyproc() will work
p = &proc[0];
+ p->state = IDLEPROC;
p->sz = 4 * PAGE;
p->mem = kalloc(p->sz);
memset(p->mem, 0, p->sz);
@@ -74,8 +71,9 @@ main0(void)
p->tf->es = p->tf->ds = p->tf->ss = (SEG_UDATA << 3) | 3;
p->tf->cs = (SEG_UCODE << 3) | 3;
p->tf->eflags = FL_IF;
- setupsegs(p);
- // curproc[cpu()] = p;
+
+ // make sure there's a TSS
+ setupsegs(0);
// initialize I/O devices, let them enable interrupts
console_init();
@@ -117,7 +115,8 @@ mpmain(void)
lapic_timerinit();
lapic_enableintr();
- setupsegs(&proc[cpu()]);
+ // make sure there's a TSS
+ setupsegs(0);
cpuid(0, 0, 0, 0, 0); // memory barrier
cpus[cpu()].booted = 1;
diff --git a/proc.c b/proc.c
index 6880fd4..a7908e2 100644
--- a/proc.c
+++ b/proc.c
@@ -11,7 +11,7 @@ struct spinlock proc_table_lock;
struct proc proc[NPROC];
struct proc *curproc[NCPU];
-int next_pid = NCPU;
+int next_pid = 1;
extern void forkret(void);
extern void forkret1(struct trapframe*);
@@ -22,28 +22,39 @@ pinit(void)
}
/*
- * set up a process's task state and segment descriptors
- * correctly, given its current size and address in memory.
- * this should be called whenever the latter change.
- * doesn't change the cpu's current segmentation setup.
+ * set up CPU's segment descriptors and task state for a
+ * given process. If p==0, set up for "idle" state for
+ * when scheduler() isn't running any process.
*/
void
setupsegs(struct proc *p)
{
- memset(&p->ts, 0, sizeof(struct taskstate));
- p->ts.ss0 = SEG_KDATA << 3;
- p->ts.esp0 = (uint)(p->kstack + KSTACKSIZE);
+ struct cpu *c = &cpus[cpu()];
+
+ c->ts.ss0 = SEG_KDATA << 3;
+ if(p){
+ c->ts.esp0 = (uint)(p->kstack + KSTACKSIZE);
+ } else {
+ c->ts.esp0 = 0xffffffff;
+ }
// XXX it may be wrong to modify the current segment table!
- p->gdt[0] = SEG_NULL;
- p->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024, 0); // xxx
- p->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
- p->gdt[SEG_TSS] = SEG16(STS_T32A, (uint) &p->ts,
- sizeof(p->ts), 0);
- p->gdt[SEG_TSS].s = 0;
- p->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz, 3);
- p->gdt[SEG_UDATA] = SEG(STA_W, (uint)p->mem, p->sz, 3);
+ c->gdt[0] = SEG_NULL;
+ c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024, 0); // xxx
+ c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
+ c->gdt[SEG_TSS] = SEG16(STS_T32A, (uint) &c->ts, sizeof(c->ts), 0);
+ c->gdt[SEG_TSS].s = 0;
+ if(p){
+ c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz, 3);
+ c->gdt[SEG_UDATA] = SEG(STA_W, (uint)p->mem, p->sz, 3);
+ } else {
+ c->gdt[SEG_UCODE] = SEG_NULL;
+ c->gdt[SEG_UDATA] = SEG_NULL;
+ }
+
+ lgdt(c->gdt, sizeof c->gdt);
+ ltr(SEG_TSS << 3);
}
// Look in the process table for an UNUSED proc.
@@ -101,9 +112,6 @@ copyproc(struct proc* p)
np->state = UNUSED;
return 0;
}
-
- // Initialize segment table.
- setupsegs(np);
// Copy trapframe registers from parent.
np->tf = (struct trapframe*)(np->kstack + KSTACKSIZE) - 1;
@@ -159,26 +167,11 @@ scheduler(void)
if(p->state != RUNNABLE)
continue;
- // Run this process.
- // XXX move this into swtch or trapret or something.
- // It can run on the other stack.
- // h/w sets busy bit in TSS descriptor sometimes, and faults
- // if it's set in LTR. so clear tss descriptor busy bit.
- p->gdt[SEG_TSS].type = STS_T32A;
-
- // XXX should probably have an lgdt() function in x86.h
- // to confine all the inline assembly.
- // XXX probably ought to lgdt on trap return too, in case
- // a system call has moved a program or changed its size.
- lgdt(p->gdt, sizeof p->gdt);
- // asm volatile("lgdt %0" : : "g" (p->gdt_pd.lim));
-
- ltr(SEG_TSS << 3);
-
// Switch to chosen process. It is the process's job
// to release proc_table_lock and then reacquire it
// before jumping back to us.
- if(0) cprintf("cpu%d: run %d\n", cpu(), p-proc);
+
+ setupsegs(p);
curproc[cpu()] = p;
p->state = RUNNING;
if(setjmp(&cpus[cpu()].jmpbuf) == 0)
@@ -199,9 +192,7 @@ scheduler(void)
panic("scheduler lock");
}
- setupsegs(&proc[cpu()]);
-
- // XXX if not holding proc_table_lock panic.
+ setupsegs(0);
}
release(&proc_table_lock);
diff --git a/proc.h b/proc.h
index 88e630b..611f9b5 100644
--- a/proc.h
+++ b/proc.h
@@ -48,8 +48,6 @@ struct proc{
struct fd *fds[NOFILE];
struct inode *cwd;
- struct taskstate ts; // only to give cpu address of kernel stack
- struct segdesc gdt[NSEGS];
uint esp; // kernel stack pointer
uint ebp; // kernel frame pointer
@@ -67,6 +65,8 @@ extern struct proc *curproc[NCPU]; // can be NULL if no proc running.
struct cpu {
uchar apicid; // Local APIC ID
struct jmpbuf jmpbuf;
+ struct taskstate ts; // only to give cpu address of kernel stack
+ struct segdesc gdt[NSEGS];
int guard1;
char mpstack[MPSTACK]; // per-cpu start-up stack
int guard2;
diff --git a/trap.c b/trap.c
index 99aaa70..2bb3e9e 100644
--- a/trap.c
+++ b/trap.c
@@ -41,8 +41,8 @@ trap(struct trapframe *tf)
panic("interrupt while holding a lock");
}
- if(cpu() == 1 && curproc[cpu()] == 0){
- if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + 512){
+ if(curproc[cpu()] == 0){
+ if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + MPSTACK){
cprintf("&tf %x mpstack %x\n", &tf, cpus[cpu()].mpstack);
panic("trap cpu stack");
}
@@ -125,7 +125,8 @@ trap(struct trapframe *tf)
return;
}
- cprintf("trap %d\n", v);
+ cprintf("trap %d from cpu %d eip %x\n", v, cpu(), tf->eip);
+ panic("trap");
return;
}
diff --git a/usertests.c b/usertests.c
index c3e9113..20155c2 100644
--- a/usertests.c
+++ b/usertests.c
@@ -115,305 +115,11 @@ exitwait(void)
puts("exitwait ok\n");
}
-// two processes write to the same file descriptor
-// is the offset shared? does inode locking work?
-void
-sharedfd()
-{
- int fd, pid, i, n, nc, np;
- char buf[10];
-
- unlink("sharedfd");
- fd = open("sharedfd", O_CREATE|O_RDWR);
- if(fd < 0){
- printf(1, "usertests: cannot open sharedfd for writing");
- return;
- }
- pid = fork();
- memset(buf, pid==0?'c':'p', sizeof(buf));
- for(i = 0; i < 100; i++){
- if(write(fd, buf, sizeof(buf)) != sizeof(buf)){
- printf(1, "usertests: write sharedfd failed\n");
- break;
- }
- }
- if(pid == 0)
- exit();
- else
- wait();
- close(fd);
- fd = open("sharedfd", 0);
- if(fd < 0){
- printf(1, "usertests: cannot open sharedfd for reading\n");
- return;
- }
- nc = np = 0;
- while((n = read(fd, buf, sizeof(buf))) > 0){
- for(i = 0; i < sizeof(buf); i++){
- if(buf[i] == 'c')
- nc++;
- if(buf[i] == 'p')
- np++;
- }
- }
- close(fd);
- unlink("sharedfd");
- if(nc == 1000 && np == 1000)
- printf(1, "sharedfd ok\n");
- else
- printf(1, "sharedfd oops %d %d\n", nc, np);
-}
-
-// two processes write two different files at the same
-// time, to test block allocation.
-void
-twofiles()
-{
- int fd, pid, i, j, n, total;
- char *fname;
-
- unlink("f1");
- unlink("f2");
-
- pid = fork();
- if(pid < 0){
- puts("fork failed\n");
- return;
- }
-
- fname = pid ? "f1" : "f2";
- fd = open(fname, O_CREATE | O_RDWR);
- if(fd < 0){
- puts("create failed\n");
- exit();
- }
-
- memset(buf, pid?'p':'c', 512);
- for(i = 0; i < 12; i++){
- if((n = write(fd, buf, 500)) != 500){
- printf(1, "write failed %d\n", n);
- exit();
- }
- }
- close(fd);
- if(pid)
- wait();
- else
- exit();
-
- for(i = 0; i < 2; i++){
- fd = open(i?"f1":"f2", 0);
- total = 0;
- while((n = read(fd, buf, sizeof(buf))) > 0){
- for(j = 0; j < n; j++){
- if(buf[j] != (i?'p':'c')){
- puts("wrong char\n");
- exit();
- }
- }
- total += n;
- }
- close(fd);
- if(total != 12*500){
- printf(1, "wrong length %d\n", total);
- exit();
- }
- }
-
- unlink("f1");
- unlink("f2");
-
- puts("twofiles ok\n");
-}
-
-// two processes create and delete files in same directory
-void
-createdelete()
-{
- int pid, i, fd;
- int n = 20;
- char name[32];
-
- pid = fork();
- if(pid < 0){
- puts("fork failed\n");
- exit();
- }
-
- name[0] = pid ? 'p' : 'c';
- name[2] = '\0';
- for(i = 0; i < n; i++){
- name[1] = '0' + i;
- fd = open(name, O_CREATE | O_RDWR);
- if(fd < 0){
- puts("create failed\n");
- exit();
- }
- close(fd);
- if(i > 0 && (i % 2 ) == 0){
- name[1] = '0' + (i / 2);
- if(unlink(name) < 0){
- puts("unlink failed\n");
- exit();
- }
- }
- }
-
- if(pid)
- wait();
- else
- exit();
-
- for(i = 0; i < n; i++){
- name[0] = 'p';
- name[1] = '0' + i;
- fd = open(name, 0);
- if((i == 0 || i >= n/2) && fd < 0){
- printf(1, "oops createdelete %s didn't exist\n", name);
- } else if((i >= 1 && i < n/2) && fd >= 0){
- printf(1, "oops createdelete %s did exist\n", name);
- }
- if(fd >= 0)
- close(fd);
-
- name[0] = 'c';
- name[1] = '0' + i;
- fd = open(name, 0);
- if((i == 0 || i >= n/2) && fd < 0){
- printf(1, "oops createdelete %s didn't exist\n", name);
- } else if((i >= 1 && i < n/2) && fd >= 0){
- printf(1, "oops createdelete %s did exist\n", name);
- }
- if(fd >= 0)
- close(fd);
- }
-
- for(i = 0; i < n; i++){
- name[0] = 'p';
- name[1] = '0' + i;
- unlink(name);
- name[0] = 'c';
- unlink(name);
- }
-
- printf(1, "createdelete ok\n");
-}
-
-// can I unlink a file and still read it?
-void
-unlinkread()
-{
- int fd, fd1;
-
- fd = open("unlinkread", O_CREATE | O_RDWR);
- if(fd < 0){
- puts("create unlinkread failed\n");
- exit();
- }
- write(fd, "hello", 5);
- close(fd);
-
- fd = open("unlinkread", O_RDWR);
- if(fd < 0){
- puts("open unlinkread failed\n");
- exit();
- }
- if(unlink("unlinkread") != 0){
- puts("unlink unlinkread failed\n");
- exit();
- }
-
- fd1 = open("xxx", O_CREATE | O_RDWR);
- write(fd1, "yyy", 3);
- close(fd1);
-
- if(read(fd, buf, sizeof(buf)) != 5){
- puts("unlinkread read failed");
- exit();
- }
- if(buf[0] != 'h'){
- puts("unlinkread wrong data\n");
- exit();
- }
- if(write(fd, buf, 10) != 10){
- puts("unlinkread write failed\n");
- exit();
- }
- close(fd);
- unlink("xxx");
- puts("unlinkread ok\n");
-}
-
-void
-linktest()
-{
- int fd;
-
- unlink("lf1");
- unlink("lf2");
-
- fd = open("lf1", O_CREATE|O_RDWR);
- if(fd < 0){
- puts("create lf1 failed\n");
- exit();
- }
- if(write(fd, "hello", 5) != 5){
- puts("write lf1 failed\n");
- exit();
- }
- close(fd);
-
- if(link("lf1", "lf2") < 0){
- puts("link lf1 lf2 failed\n");
- exit();
- }
- unlink("lf1");
-
- if(open("lf1", 0) >= 0){
- puts("unlinked lf1 but it is still there!\n");
- exit();
- }
-
- fd = open("lf2", 0);
- if(fd < 0){
- puts("open lf2 failed\n");
- exit();
- }
- if(read(fd, buf, sizeof(buf)) != 5){
- puts("read lf2 failed\n");
- exit();
- }
- close(fd);
-
- if(link("lf2", "lf2") >= 0){
- puts("link lf2 lf2 succeeded! oops\n");
- exit();
- }
-
- unlink("lf2");
- if(link("lf2", "lf1") >= 0){
- puts("link non-existant succeeded! oops\n");
- exit();
- }
-
- if(link(".", "lf1") >= 0){
- puts("link . lf1 succeeded! oops\n");
- exit();
- }
-
- puts("linktest ok\n");
-}
-
int
main(int argc, char *argv[])
{
puts("usertests starting\n");
- linktest();
- unlinkread();
- createdelete();
- twofiles();
- sharedfd();
pipe1();
preempt();
exitwait();