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
This commit is contained in:
parent
69332d1918
commit
350e63f7a9
8 changed files with 455 additions and 615 deletions
10
Makefile
10
Makefile
|
@ -67,6 +67,10 @@ usertests : usertests.o $(ULIB)
|
||||||
$(LD) -N -e main -Ttext 0 -o usertests usertests.o $(ULIB)
|
$(LD) -N -e main -Ttext 0 -o usertests usertests.o $(ULIB)
|
||||||
$(OBJDUMP) -S usertests > usertests.asm
|
$(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)
|
echo : echo.o $(ULIB)
|
||||||
$(LD) -N -e main -Ttext 0 -o echo echo.o $(ULIB)
|
$(LD) -N -e main -Ttext 0 -o echo echo.o $(ULIB)
|
||||||
$(OBJDUMP) -S echo > echo.asm
|
$(OBJDUMP) -S echo > echo.asm
|
||||||
|
@ -102,12 +106,12 @@ rm : rm.o $(ULIB)
|
||||||
mkfs : mkfs.c fs.h
|
mkfs : mkfs.c fs.h
|
||||||
cc -o mkfs mkfs.c
|
cc -o mkfs mkfs.c
|
||||||
|
|
||||||
fs.img : mkfs 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
|
./mkfs fs.img userfs usertests echo cat readme init sh ls mkdir rm fstests
|
||||||
|
|
||||||
-include *.d
|
-include *.d
|
||||||
|
|
||||||
clean :
|
clean :
|
||||||
rm -f *.o *.d *.asm vectors.S parport.out \
|
rm -f *.o *.d *.asm vectors.S parport.out \
|
||||||
bootblock kernel xv6.img user1 userfs usertests \
|
bootblock kernel xv6.img user1 userfs usertests \
|
||||||
fs.img mkfs echo init
|
fs.img mkfs echo init fstests
|
||||||
|
|
283
Notes
283
Notes
|
@ -22,32 +22,14 @@ no kernel malloc(), just kalloc() for user core
|
||||||
|
|
||||||
user pointers aren't valid in the kernel
|
user pointers aren't valid in the kernel
|
||||||
|
|
||||||
setting up first process
|
are interrupts turned on in the kernel? yes.
|
||||||
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?
|
|
||||||
|
|
||||||
pass curproc explicitly, or implicit from cpu #?
|
pass curproc explicitly, or implicit from cpu #?
|
||||||
e.g. argument to newproc()?
|
e.g. argument to newproc()?
|
||||||
hmm, you need a global curproc[cpu] for trap() &c
|
hmm, you need a global curproc[cpu] for trap() &c
|
||||||
|
|
||||||
test stack expansion
|
no stack expansion
|
||||||
|
|
||||||
test running out of memory, process slots
|
test running out of memory, process slots
|
||||||
|
|
||||||
we can't really use a separate stack segment, since stack addresses
|
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
|
both can grow, without committing 4GB of physical memory? does this
|
||||||
mean we need paging?
|
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?
|
perhaps have fixed-size stack, put it in the data segment?
|
||||||
|
|
||||||
oops, if kernel stack is in contiguous user phys mem, then moving
|
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()
|
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
|
locks
|
||||||
init_lock
|
init_lock
|
||||||
sequences CPU startup
|
sequences CPU startup
|
||||||
|
@ -110,37 +69,17 @@ locks
|
||||||
memory allocator
|
memory allocator
|
||||||
printf
|
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
|
in general, the table locks protect both free-ness and
|
||||||
public variables of table elements
|
public variables of table elements
|
||||||
in many cases you can use table elements w/o a lock
|
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
|
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
|
lock order
|
||||||
per-pipe lock
|
per-pipe lock
|
||||||
proc_table_lock fd_table_lock kalloc_lock
|
proc_table_lock fd_table_lock kalloc_lock
|
||||||
console_lock
|
console_lock
|
||||||
|
|
||||||
condition variable + mutex that protects it
|
do you have to be holding the mutex in order to call wakeup()? yes
|
||||||
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()?
|
|
||||||
|
|
||||||
device interrupts don't clear FL_IF
|
device interrupts don't clear FL_IF
|
||||||
so a recursive timer interrupt is possible
|
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
|
blocks and inodes have ad-hoc sleep-locks
|
||||||
provide a single mechanism?
|
provide a single mechanism?
|
||||||
|
|
||||||
need to lock bufs in bio between bread and brelse
|
|
||||||
|
|
||||||
test 14-character file names
|
test 14-character file names
|
||||||
and file arguments longer than 14
|
and file arguments longer than 14
|
||||||
and directories longer than one sector
|
|
||||||
|
|
||||||
kalloc() can return 0; do callers handle this right?
|
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!
|
OH! recursive interrupts will use up any amount of cpu[].stack!
|
||||||
underflow and wrecks *previous* cpu's struct
|
underflow and wrecks *previous* cpu's struct
|
||||||
|
|
||||||
|
@ -360,15 +108,26 @@ mkdir
|
||||||
sh arguments
|
sh arguments
|
||||||
sh redirection
|
sh redirection
|
||||||
indirect blocks
|
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?
|
is there a create/create race for same file name?
|
||||||
resulting in two entries w/ same name in directory?
|
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: 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
|
test: one process opens a file while another deletes it
|
||||||
|
test: mkdir. deadlock d/.. vs ../d
|
||||||
|
|
||||||
wdir should use writei, to avoid special-case block allocation
|
make proc[0] runnable
|
||||||
also readi
|
cpu early tss and gdt
|
||||||
is dir locked? probably
|
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 *)
|
||||||
|
|
380
fstests.c
Normal file
380
fstests.c
Normal file
|
@ -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();
|
||||||
|
}
|
23
main.c
23
main.c
|
@ -29,10 +29,14 @@ main0(void)
|
||||||
// clear BSS
|
// clear BSS
|
||||||
memset(edata, 0, end - edata);
|
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
|
// Make sure interrupts stay disabled on all processors
|
||||||
// until each signals it is ready, by pretending to hold
|
// until each signals it is ready, by pretending to hold
|
||||||
// an extra lock.
|
// 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++){
|
for(i=0; i<NCPU; i++){
|
||||||
cpus[i].nlock++;
|
cpus[i].nlock++;
|
||||||
cpus[i].guard1 = 0xdeadbeef;
|
cpus[i].guard1 = 0xdeadbeef;
|
||||||
|
@ -55,16 +59,9 @@ main0(void)
|
||||||
fd_init();
|
fd_init();
|
||||||
iinit();
|
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
|
// fix process 0 so that copyproc() will work
|
||||||
p = &proc[0];
|
p = &proc[0];
|
||||||
|
p->state = IDLEPROC;
|
||||||
p->sz = 4 * PAGE;
|
p->sz = 4 * PAGE;
|
||||||
p->mem = kalloc(p->sz);
|
p->mem = kalloc(p->sz);
|
||||||
memset(p->mem, 0, 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->es = p->tf->ds = p->tf->ss = (SEG_UDATA << 3) | 3;
|
||||||
p->tf->cs = (SEG_UCODE << 3) | 3;
|
p->tf->cs = (SEG_UCODE << 3) | 3;
|
||||||
p->tf->eflags = FL_IF;
|
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
|
// initialize I/O devices, let them enable interrupts
|
||||||
console_init();
|
console_init();
|
||||||
|
@ -117,7 +115,8 @@ mpmain(void)
|
||||||
lapic_timerinit();
|
lapic_timerinit();
|
||||||
lapic_enableintr();
|
lapic_enableintr();
|
||||||
|
|
||||||
setupsegs(&proc[cpu()]);
|
// make sure there's a TSS
|
||||||
|
setupsegs(0);
|
||||||
|
|
||||||
cpuid(0, 0, 0, 0, 0); // memory barrier
|
cpuid(0, 0, 0, 0, 0); // memory barrier
|
||||||
cpus[cpu()].booted = 1;
|
cpus[cpu()].booted = 1;
|
||||||
|
|
69
proc.c
69
proc.c
|
@ -11,7 +11,7 @@ struct spinlock proc_table_lock;
|
||||||
|
|
||||||
struct proc proc[NPROC];
|
struct proc proc[NPROC];
|
||||||
struct proc *curproc[NCPU];
|
struct proc *curproc[NCPU];
|
||||||
int next_pid = NCPU;
|
int next_pid = 1;
|
||||||
extern void forkret(void);
|
extern void forkret(void);
|
||||||
extern void forkret1(struct trapframe*);
|
extern void forkret1(struct trapframe*);
|
||||||
|
|
||||||
|
@ -22,28 +22,39 @@ pinit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set up a process's task state and segment descriptors
|
* set up CPU's segment descriptors and task state for a
|
||||||
* correctly, given its current size and address in memory.
|
* given process. If p==0, set up for "idle" state for
|
||||||
* this should be called whenever the latter change.
|
* when scheduler() isn't running any process.
|
||||||
* doesn't change the cpu's current segmentation setup.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
setupsegs(struct proc *p)
|
setupsegs(struct proc *p)
|
||||||
{
|
{
|
||||||
memset(&p->ts, 0, sizeof(struct taskstate));
|
struct cpu *c = &cpus[cpu()];
|
||||||
p->ts.ss0 = SEG_KDATA << 3;
|
|
||||||
p->ts.esp0 = (uint)(p->kstack + KSTACKSIZE);
|
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!
|
// XXX it may be wrong to modify the current segment table!
|
||||||
|
|
||||||
p->gdt[0] = SEG_NULL;
|
c->gdt[0] = SEG_NULL;
|
||||||
p->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024, 0); // xxx
|
c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024, 0); // xxx
|
||||||
p->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
|
c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
|
||||||
p->gdt[SEG_TSS] = SEG16(STS_T32A, (uint) &p->ts,
|
c->gdt[SEG_TSS] = SEG16(STS_T32A, (uint) &c->ts, sizeof(c->ts), 0);
|
||||||
sizeof(p->ts), 0);
|
c->gdt[SEG_TSS].s = 0;
|
||||||
p->gdt[SEG_TSS].s = 0;
|
if(p){
|
||||||
p->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz, 3);
|
c->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[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.
|
// Look in the process table for an UNUSED proc.
|
||||||
|
@ -102,9 +113,6 @@ copyproc(struct proc* p)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize segment table.
|
|
||||||
setupsegs(np);
|
|
||||||
|
|
||||||
// Copy trapframe registers from parent.
|
// Copy trapframe registers from parent.
|
||||||
np->tf = (struct trapframe*)(np->kstack + KSTACKSIZE) - 1;
|
np->tf = (struct trapframe*)(np->kstack + KSTACKSIZE) - 1;
|
||||||
*np->tf = *p->tf;
|
*np->tf = *p->tf;
|
||||||
|
@ -159,26 +167,11 @@ scheduler(void)
|
||||||
if(p->state != RUNNABLE)
|
if(p->state != RUNNABLE)
|
||||||
continue;
|
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
|
// Switch to chosen process. It is the process's job
|
||||||
// to release proc_table_lock and then reacquire it
|
// to release proc_table_lock and then reacquire it
|
||||||
// before jumping back to us.
|
// before jumping back to us.
|
||||||
if(0) cprintf("cpu%d: run %d\n", cpu(), p-proc);
|
|
||||||
|
setupsegs(p);
|
||||||
curproc[cpu()] = p;
|
curproc[cpu()] = p;
|
||||||
p->state = RUNNING;
|
p->state = RUNNING;
|
||||||
if(setjmp(&cpus[cpu()].jmpbuf) == 0)
|
if(setjmp(&cpus[cpu()].jmpbuf) == 0)
|
||||||
|
@ -199,9 +192,7 @@ scheduler(void)
|
||||||
panic("scheduler lock");
|
panic("scheduler lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
setupsegs(&proc[cpu()]);
|
setupsegs(0);
|
||||||
|
|
||||||
// XXX if not holding proc_table_lock panic.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
release(&proc_table_lock);
|
release(&proc_table_lock);
|
||||||
|
|
4
proc.h
4
proc.h
|
@ -48,8 +48,6 @@ struct proc{
|
||||||
struct fd *fds[NOFILE];
|
struct fd *fds[NOFILE];
|
||||||
struct inode *cwd;
|
struct inode *cwd;
|
||||||
|
|
||||||
struct taskstate ts; // only to give cpu address of kernel stack
|
|
||||||
struct segdesc gdt[NSEGS];
|
|
||||||
uint esp; // kernel stack pointer
|
uint esp; // kernel stack pointer
|
||||||
uint ebp; // kernel frame pointer
|
uint ebp; // kernel frame pointer
|
||||||
|
|
||||||
|
@ -67,6 +65,8 @@ extern struct proc *curproc[NCPU]; // can be NULL if no proc running.
|
||||||
struct cpu {
|
struct cpu {
|
||||||
uchar apicid; // Local APIC ID
|
uchar apicid; // Local APIC ID
|
||||||
struct jmpbuf jmpbuf;
|
struct jmpbuf jmpbuf;
|
||||||
|
struct taskstate ts; // only to give cpu address of kernel stack
|
||||||
|
struct segdesc gdt[NSEGS];
|
||||||
int guard1;
|
int guard1;
|
||||||
char mpstack[MPSTACK]; // per-cpu start-up stack
|
char mpstack[MPSTACK]; // per-cpu start-up stack
|
||||||
int guard2;
|
int guard2;
|
||||||
|
|
7
trap.c
7
trap.c
|
@ -41,8 +41,8 @@ trap(struct trapframe *tf)
|
||||||
panic("interrupt while holding a lock");
|
panic("interrupt while holding a lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cpu() == 1 && curproc[cpu()] == 0){
|
if(curproc[cpu()] == 0){
|
||||||
if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + 512){
|
if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + MPSTACK){
|
||||||
cprintf("&tf %x mpstack %x\n", &tf, cpus[cpu()].mpstack);
|
cprintf("&tf %x mpstack %x\n", &tf, cpus[cpu()].mpstack);
|
||||||
panic("trap cpu stack");
|
panic("trap cpu stack");
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,8 @@ trap(struct trapframe *tf)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cprintf("trap %d\n", v);
|
cprintf("trap %d from cpu %d eip %x\n", v, cpu(), tf->eip);
|
||||||
|
panic("trap");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
294
usertests.c
294
usertests.c
|
@ -115,305 +115,11 @@ exitwait(void)
|
||||||
puts("exitwait ok\n");
|
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
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
puts("usertests starting\n");
|
puts("usertests starting\n");
|
||||||
|
|
||||||
linktest();
|
|
||||||
unlinkread();
|
|
||||||
createdelete();
|
|
||||||
twofiles();
|
|
||||||
sharedfd();
|
|
||||||
pipe1();
|
pipe1();
|
||||||
preempt();
|
preempt();
|
||||||
exitwait();
|
exitwait();
|
||||||
|
|
Loading…
Reference in a new issue