Initial fix of upstream merge.
This commit is contained in:
commit
a662851491
98
CHANGES
98
CHANGES
@ -12,6 +12,96 @@ various other people, all of whom are (hopefully) listed below.
|
|||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
OS/161 2.0.3 released 20160124
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
20170124 dholland in base
|
||||||
|
- Remove obsolete, redundant, or not useful test programs:
|
||||||
|
guzzle (same as hog)
|
||||||
|
kitchen (equivalent to multiexec -n 4 sink)
|
||||||
|
sink (same as conman)
|
||||||
|
sty (equivalent to multiexec -n 6 hog)
|
||||||
|
quinthuge (offers little over triplehuge, can be done with multiexec)
|
||||||
|
quintmat, quintsort (ditto)
|
||||||
|
|
||||||
|
20170118 dholland in base
|
||||||
|
- Make -g -Og the flags when "debug" is enabled in a kernel config
|
||||||
|
(which it is in the non-OPT ones) and add an additional kernel
|
||||||
|
config verb "debugonly" to get just -g in case that becomes
|
||||||
|
necessary. This should significantly improve the output code
|
||||||
|
quality from gcc without compromising debugging. (However, gcc
|
||||||
|
being gcc, it also sometimes leads to additional spurious
|
||||||
|
warnings that don't occur with either -g or -O2.)
|
||||||
|
|
||||||
|
20170118 dholland in base
|
||||||
|
- Add a MI mainbus_debugger() function that goes through the right
|
||||||
|
MD paths to trigger the debugger hook in the ltrace device. Also
|
||||||
|
add a menu function "debug" to trigger it.
|
||||||
|
|
||||||
|
20170118 dholland in base
|
||||||
|
- Add some bits to forktest to try to catch the case where the fork
|
||||||
|
child returns from the next system call instead of from fork.
|
||||||
|
(Which is a moderately common bug, caused by races copying the
|
||||||
|
trapframe information in the kernel.)
|
||||||
|
|
||||||
|
20170117 dholland in base
|
||||||
|
- Add a menu command "deadlock" to intentionally deadlock.
|
||||||
|
|
||||||
|
20170117 dholland in base, from Sam Fishman
|
||||||
|
- Fix parallelvm -w so that if one of the forks fails the whole
|
||||||
|
thing doesn't wedge.
|
||||||
|
|
||||||
|
20170117 dholland in base, reported by Sam Fishman
|
||||||
|
- Don't do semfs I/O from NULL, or from/to insufficiently sized
|
||||||
|
buffers. Like the 20150615 change, except covering the rest of
|
||||||
|
the tests that use the semaphores that were doing it wrong:
|
||||||
|
multiexec, parallelvm, and schedpong.
|
||||||
|
|
||||||
|
20170117 dholland in base, from Sam Fishman
|
||||||
|
- Add assembler directives to exception-mips1.S that tell gdb how
|
||||||
|
to read trap frames correctly. Garbage-collect old stuff left
|
||||||
|
over from making it work with a (much) older version of gdb a
|
||||||
|
long time back. This also usually makes it possible to trace back
|
||||||
|
through a syscall into a userlevel process; include a gdb script
|
||||||
|
with tools for making this useful.
|
||||||
|
|
||||||
|
20170117 dholland in base
|
||||||
|
- Merge the deadlock detector into base. It was a success last year.
|
||||||
|
- Mention in the comments that the hangman hooks in locks need to
|
||||||
|
be called atomically.
|
||||||
|
|
||||||
|
20170117 dholland in base, reported by Jeffrey Cai, patch from Sam Fishman
|
||||||
|
- Fix off-by-one in tac that makes it skip the first line of files.
|
||||||
|
|
||||||
|
20170117 dholland in base, from Sam Fishman
|
||||||
|
- Make badcall's "pipe with unaligned pointer" test clean up after
|
||||||
|
itself if the operation succeeds. Otherwise it leaks fds and that
|
||||||
|
can intefere with other tests.
|
||||||
|
|
||||||
|
20170116 dholland in base, reported by Sam Fishman
|
||||||
|
- Don't allow opening an entirely empty pathname to succeed, and
|
||||||
|
don't allow success for this case in badcall either.
|
||||||
|
|
||||||
|
20170116 dholland in base, from Sasha Fedorova
|
||||||
|
- Fix write buffer size in filetest.
|
||||||
|
|
||||||
|
20160325 dholland in base
|
||||||
|
- Fix macro parenthesis bug in ROUNDUP().
|
||||||
|
|
||||||
|
20160304 dholland in base, from Sam Fishman
|
||||||
|
- Make runtest.py handle spacing in the command strings it's given.
|
||||||
|
|
||||||
|
20160216 dholland in base
|
||||||
|
- Fix spacing problems in ls -l output for large files.
|
||||||
|
|
||||||
|
20160203 dholland in base
|
||||||
|
- Expand comments attached to cpustacks[]/cputhreads[], prompted by
|
||||||
|
James Mickens.
|
||||||
|
|
||||||
|
20160125 dholland in base, from Nikhil Benesch.
|
||||||
|
- Fix stupid argument handling bug in test.py.
|
||||||
|
|
||||||
|
|
||||||
OS/161 2.0.2 released 20160112
|
OS/161 2.0.2 released 20160112
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@ -53,6 +143,14 @@ OS/161 2.0.2 released 20160112
|
|||||||
anyway. If they aren't actually constant because of bugs, reading
|
anyway. If they aren't actually constant because of bugs, reading
|
||||||
a stale or even garbage value is not going to hurt more.
|
a stale or even garbage value is not going to hurt more.
|
||||||
|
|
||||||
|
20160107 dholland in deadlock-detector
|
||||||
|
- Add a deadlock detector. For now this will be supplied to
|
||||||
|
instructors as a supplementary patch, because it intrudes into
|
||||||
|
the synchronization primitives and affects what students do
|
||||||
|
there. We are planning to try it on our students this coming
|
||||||
|
semester; if that works out well, I'll probably merge it into
|
||||||
|
base.
|
||||||
|
|
||||||
20160107 dholland in base
|
20160107 dholland in base
|
||||||
- In testbin/multiexec, if fork fails partway through, continue
|
- In testbin/multiexec, if fork fails partway through, continue
|
||||||
with the forks we got. Otherwise the subprocesses we started hang
|
with the forks we got. Otherwise the subprocesses we started hang
|
||||||
|
@ -69,8 +69,6 @@ struct trapframe {
|
|||||||
uint32_t tf_s7;
|
uint32_t tf_s7;
|
||||||
uint32_t tf_t8;
|
uint32_t tf_t8;
|
||||||
uint32_t tf_t9;
|
uint32_t tf_t9;
|
||||||
uint32_t tf_k0; /* dummy (see exception-mips1.S comments) */
|
|
||||||
uint32_t tf_k1; /* dummy */
|
|
||||||
uint32_t tf_gp;
|
uint32_t tf_gp;
|
||||||
uint32_t tf_sp;
|
uint32_t tf_sp;
|
||||||
uint32_t tf_s8;
|
uint32_t tf_s8;
|
||||||
|
@ -101,6 +101,8 @@ mips_general_end:
|
|||||||
.text
|
.text
|
||||||
.type common_exception,@function
|
.type common_exception,@function
|
||||||
.ent common_exception
|
.ent common_exception
|
||||||
|
.cfi_startproc
|
||||||
|
.cfi_signal_frame
|
||||||
common_exception:
|
common_exception:
|
||||||
mfc0 k0, c0_status /* Get status register */
|
mfc0 k0, c0_status /* Get status register */
|
||||||
andi k0, k0, CST_KUp /* Check the we-were-in-user-mode bit */
|
andi k0, k0, CST_KUp /* Check the we-were-in-user-mode bit */
|
||||||
@ -130,11 +132,12 @@ common_exception:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate stack space for 37 words to hold the trap frame,
|
* Allocate stack space for 35 words to hold the trap frame,
|
||||||
* plus four more words for a minimal argument block, plus
|
* plus four more words for a minimal argument block, plus
|
||||||
* one more for proper (64-bit) stack alignment.
|
* one more for proper (64-bit) stack alignment.
|
||||||
*/
|
*/
|
||||||
addi sp, sp, -168
|
addi sp, sp, -160
|
||||||
|
.cfi_def_cfa sp, 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save general registers.
|
* Save general registers.
|
||||||
@ -143,70 +146,89 @@ common_exception:
|
|||||||
*
|
*
|
||||||
* The order here must match mips/include/trapframe.h.
|
* The order here must match mips/include/trapframe.h.
|
||||||
*
|
*
|
||||||
* gdb disassembles this code to try to figure out what registers
|
* gdb uses the .cfi_offset assembler directives inserted below to
|
||||||
* are where, and it isn't very bright. So in order to make gdb be
|
* to figure out where each register is stored. Since we've marked
|
||||||
* able to trace the stack back through here, we play some silly
|
* this function as a "signal handler" with the .cfi_signal_frame
|
||||||
* games.
|
* directive, gdb won't complain about the fact that the stack
|
||||||
|
* is noncontiguous (if we're coming from userland).
|
||||||
*
|
*
|
||||||
* In particular:
|
* We also play a trick with the return address: we mark the ra
|
||||||
* (1) We store the return address register into the epc slot,
|
* register as stored to the stack normally and then mark the
|
||||||
* which makes gdb think it's the return address slot. Then
|
* return address for *this* function as being in the k1 register
|
||||||
* we store the real epc value over that.
|
* using the .cfi_return_column directive. gdb is then able to
|
||||||
* (2) We store the current sp into the sp slot, which makes gdb
|
* recognize that the ra we've stored here is the return address
|
||||||
* think it's the stack pointer slot. Then we store the real
|
* for the function that was executing when this exception was
|
||||||
* value.
|
* taken.
|
||||||
* (3) gdb also assumes that saved registers in a function are
|
|
||||||
* saved in order. This is why we put epc where it is, and
|
|
||||||
* handle the real value of ra afterwards.
|
|
||||||
* (4) Because gdb will think we're saving k0 and k1, we need to
|
|
||||||
* leave slots for them in the trap frame, even though the
|
|
||||||
* stuff we save there is useless.
|
|
||||||
*
|
*
|
||||||
* This logic has not been tested against a recent gdb and has
|
* All of the cfi (call frame information) material is compiled
|
||||||
* probably bitrotted. Someone(TM) should figure out what gdb
|
* into the .eh_frame section of the compiled kernel.
|
||||||
* currently expects -- or maybe even patch gdb to understand a
|
|
||||||
* better form of this that doesn't waste so many cycles.
|
|
||||||
*/
|
*/
|
||||||
sw ra, 160(sp) /* dummy for gdb */
|
sw s8, 148(sp) /* save s8 */
|
||||||
sw s8, 156(sp) /* save s8 */
|
.cfi_offset s8, 148
|
||||||
sw sp, 152(sp) /* dummy for gdb */
|
sw k1, 144(sp) /* real saved sp */
|
||||||
sw gp, 148(sp) /* save gp */
|
.cfi_offset sp, 144
|
||||||
sw k1, 144(sp) /* dummy for gdb */
|
sw gp, 140(sp) /* save gp */
|
||||||
sw k0, 140(sp) /* dummy for gdb */
|
|
||||||
|
|
||||||
sw k1, 152(sp) /* real saved sp */
|
|
||||||
nop /* delay slot for store */
|
nop /* delay slot for store */
|
||||||
|
.cfi_offset gp, 140
|
||||||
|
|
||||||
|
.cfi_return_column k1
|
||||||
mfc0 k1, c0_epc /* Copr.0 reg 13 == PC for exception */
|
mfc0 k1, c0_epc /* Copr.0 reg 13 == PC for exception */
|
||||||
sw k1, 160(sp) /* real saved PC */
|
sw k1, 152(sp) /* real saved PC */
|
||||||
|
.cfi_offset k1, 152
|
||||||
|
|
||||||
sw t9, 136(sp)
|
sw t9, 136(sp)
|
||||||
|
.cfi_offset t9, 136
|
||||||
sw t8, 132(sp)
|
sw t8, 132(sp)
|
||||||
|
.cfi_offset t8, 132
|
||||||
sw s7, 128(sp)
|
sw s7, 128(sp)
|
||||||
|
.cfi_offset s7, 128
|
||||||
sw s6, 124(sp)
|
sw s6, 124(sp)
|
||||||
|
.cfi_offset s6, 124
|
||||||
sw s5, 120(sp)
|
sw s5, 120(sp)
|
||||||
|
.cfi_offset s5, 120
|
||||||
sw s4, 116(sp)
|
sw s4, 116(sp)
|
||||||
|
.cfi_offset s4, 116
|
||||||
sw s3, 112(sp)
|
sw s3, 112(sp)
|
||||||
|
.cfi_offset s3, 112
|
||||||
sw s2, 108(sp)
|
sw s2, 108(sp)
|
||||||
|
.cfi_offset s2, 108
|
||||||
sw s1, 104(sp)
|
sw s1, 104(sp)
|
||||||
|
.cfi_offset s1, 104
|
||||||
sw s0, 100(sp)
|
sw s0, 100(sp)
|
||||||
|
.cfi_offset s0, 100
|
||||||
sw t7, 96(sp)
|
sw t7, 96(sp)
|
||||||
|
.cfi_offset t7, 96
|
||||||
sw t6, 92(sp)
|
sw t6, 92(sp)
|
||||||
|
.cfi_offset t6, 92
|
||||||
sw t5, 88(sp)
|
sw t5, 88(sp)
|
||||||
|
.cfi_offset t5, 88
|
||||||
sw t4, 84(sp)
|
sw t4, 84(sp)
|
||||||
|
.cfi_offset t4, 84
|
||||||
sw t3, 80(sp)
|
sw t3, 80(sp)
|
||||||
|
.cfi_offset t3, 80
|
||||||
sw t2, 76(sp)
|
sw t2, 76(sp)
|
||||||
|
.cfi_offset t2, 76
|
||||||
sw t1, 72(sp)
|
sw t1, 72(sp)
|
||||||
|
.cfi_offset t1, 72
|
||||||
sw t0, 68(sp)
|
sw t0, 68(sp)
|
||||||
|
.cfi_offset t0, 68
|
||||||
sw a3, 64(sp)
|
sw a3, 64(sp)
|
||||||
|
.cfi_offset a3, 64
|
||||||
sw a2, 60(sp)
|
sw a2, 60(sp)
|
||||||
|
.cfi_offset a2, 60
|
||||||
sw a1, 56(sp)
|
sw a1, 56(sp)
|
||||||
|
.cfi_offset a1, 56
|
||||||
sw a0, 52(sp)
|
sw a0, 52(sp)
|
||||||
|
.cfi_offset a0, 52
|
||||||
sw v1, 48(sp)
|
sw v1, 48(sp)
|
||||||
|
.cfi_offset v1, 48
|
||||||
sw v0, 44(sp)
|
sw v0, 44(sp)
|
||||||
|
.cfi_offset v0, 44
|
||||||
sw AT, 40(sp)
|
sw AT, 40(sp)
|
||||||
|
.cfi_offset AT, 40
|
||||||
|
|
||||||
sw ra, 36(sp)
|
sw ra, 36(sp)
|
||||||
|
.cfi_offset ra, 36
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save special registers.
|
* Save special registers.
|
||||||
@ -227,11 +249,6 @@ common_exception:
|
|||||||
mfc0 t4, c0_cause
|
mfc0 t4, c0_cause
|
||||||
sw t4, 24(sp) /* Copr.0 reg 13 == exception cause */
|
sw t4, 24(sp) /* Copr.0 reg 13 == exception cause */
|
||||||
|
|
||||||
/*
|
|
||||||
* Pretend to save $0 for gdb's benefit.
|
|
||||||
*/
|
|
||||||
sw $0, 12(sp)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load the curthread register if coming from user mode.
|
* Load the curthread register if coming from user mode.
|
||||||
*/
|
*/
|
||||||
@ -260,9 +277,6 @@ common_exception:
|
|||||||
jal mips_trap /* call it */
|
jal mips_trap /* call it */
|
||||||
nop /* delay slot */
|
nop /* delay slot */
|
||||||
|
|
||||||
/* Something must be here or gdb doesn't find the stack frame. */
|
|
||||||
nop
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now restore stuff and return from the exception.
|
* Now restore stuff and return from the exception.
|
||||||
* Interrupts should be off.
|
* Interrupts should be off.
|
||||||
@ -309,20 +323,17 @@ exception_return:
|
|||||||
lw s7, 128(sp)
|
lw s7, 128(sp)
|
||||||
lw t8, 132(sp)
|
lw t8, 132(sp)
|
||||||
lw t9, 136(sp)
|
lw t9, 136(sp)
|
||||||
|
lw gp, 140(sp) /* restore gp */
|
||||||
|
/* 144(sp) stack pointer - below */
|
||||||
|
lw s8, 148(sp) /* restore s8 */
|
||||||
|
lw k1, 152(sp) /* fetch exception return PC into k1 */
|
||||||
|
|
||||||
/* 140(sp) "saved" k0 was dummy garbage anyway */
|
lw sp, 144(sp) /* fetch saved sp (must be last) */
|
||||||
/* 144(sp) "saved" k1 was dummy garbage anyway */
|
|
||||||
|
|
||||||
lw gp, 148(sp) /* restore gp */
|
|
||||||
/* 152(sp) stack pointer - below */
|
|
||||||
lw s8, 156(sp) /* restore s8 */
|
|
||||||
lw k0, 160(sp) /* fetch exception return PC into k0 */
|
|
||||||
|
|
||||||
lw sp, 152(sp) /* fetch saved sp (must be last) */
|
|
||||||
|
|
||||||
/* done */
|
/* done */
|
||||||
jr k0 /* jump back */
|
jr k1 /* jump back */
|
||||||
rfe /* in delay slot */
|
rfe /* in delay slot */
|
||||||
|
.cfi_endproc
|
||||||
.end common_exception
|
.end common_exception
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -130,8 +130,8 @@ mips_trap(struct trapframe *tf)
|
|||||||
bool iskern;
|
bool iskern;
|
||||||
int spl;
|
int spl;
|
||||||
|
|
||||||
/* The trap frame is supposed to be 37 registers long. */
|
/* The trap frame is supposed to be 35 registers long. */
|
||||||
KASSERT(sizeof(struct trapframe)==(37*4));
|
KASSERT(sizeof(struct trapframe)==(35*4));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract the exception code info from the register fields.
|
* Extract the exception code info from the register fields.
|
||||||
|
@ -54,6 +54,14 @@
|
|||||||
*
|
*
|
||||||
* These arrays are also used to start up new CPUs, for roughly the
|
* These arrays are also used to start up new CPUs, for roughly the
|
||||||
* same reasons.
|
* same reasons.
|
||||||
|
*
|
||||||
|
* The values in the current cpu's slots in these arrays are updated
|
||||||
|
* with the current thread's information in trap.c before heading to
|
||||||
|
* userlevel, as well as being initialized in cpu_machdep_init below.
|
||||||
|
* This means that (unless something really horrible happens) on entry
|
||||||
|
* to the kernel, and when a new CPU starts up in cpu_start_secondary,
|
||||||
|
* they will have the information needed to figure out who we are and
|
||||||
|
* proceed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
vaddr_t cpustacks[MAXCPUS];
|
vaddr_t cpustacks[MAXCPUS];
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include <mainbus.h>
|
#include <mainbus.h>
|
||||||
#include <sys161/bus.h>
|
#include <sys161/bus.h>
|
||||||
#include <lamebus/lamebus.h>
|
#include <lamebus/lamebus.h>
|
||||||
|
#include <lamebus/ltrace.h>
|
||||||
#include "autoconf.h"
|
#include "autoconf.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -275,6 +276,15 @@ mainbus_send_ipi(struct cpu *target)
|
|||||||
lamebus_assert_ipi(lamebus, target);
|
lamebus_assert_ipi(lamebus, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger the debugger.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mainbus_debugger(void)
|
||||||
|
{
|
||||||
|
ltrace_stop(0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interrupt dispatcher.
|
* Interrupt dispatcher.
|
||||||
*/
|
*/
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
include conf/conf.kern # get definitions of available options
|
include conf/conf.kern # get definitions of available options
|
||||||
|
|
||||||
debug # Compile with debug info.
|
debug # Compile with debug info and -Og.
|
||||||
|
#debugonly # Compile with debug info only (no -Og).
|
||||||
|
options hangman # Deadlock detection. (off by default)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device drivers for hardware.
|
# Device drivers for hardware.
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
include conf/conf.kern # get definitions of available options
|
include conf/conf.kern # get definitions of available options
|
||||||
|
|
||||||
#debug # Optimizing compile (no debug).
|
#debug # Optimizing compile (no debug).
|
||||||
|
#debugonly
|
||||||
options noasserts # Disable assertions.
|
options noasserts # Disable assertions.
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
include conf/conf.kern # get definitions of available options
|
include conf/conf.kern # get definitions of available options
|
||||||
|
|
||||||
debug # Compile with debug info.
|
debug # Compile with debug info.
|
||||||
|
#debugonly # Compile with debug info only (no -Og).
|
||||||
|
#options hangman # Deadlock detection. (off by default)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device drivers for hardware.
|
# Device drivers for hardware.
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
include conf/conf.kern # get definitions of available options
|
include conf/conf.kern # get definitions of available options
|
||||||
|
|
||||||
#debug # Optimizing compile (no debug).
|
#debug # Optimizing compile (no debug).
|
||||||
|
#debugonly
|
||||||
options noasserts # Disable assertions.
|
options noasserts # Disable assertions.
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -334,6 +334,9 @@ file thread/synch.c
|
|||||||
file thread/thread.c
|
file thread/thread.c
|
||||||
file thread/threadlist.c
|
file thread/threadlist.c
|
||||||
|
|
||||||
|
defoption hangman
|
||||||
|
optfile hangman thread/hangman.c
|
||||||
|
|
||||||
#
|
#
|
||||||
# Process system
|
# Process system
|
||||||
#
|
#
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
# Recognized directives:
|
# Recognized directives:
|
||||||
#
|
#
|
||||||
# file <filename> use source file
|
# file <filename> use source file
|
||||||
# debug turn on debug info
|
# debug turn on debug info and -Og
|
||||||
|
# debugonly turn on debug info without -Og
|
||||||
# defoption <sym> define an option
|
# defoption <sym> define an option
|
||||||
# optfile <sym> <file> if option <sym> is enabled, use file <file>
|
# optfile <sym> <file> if option <sym> is enabled, use file <file>
|
||||||
# optofffile <sym> <file> if option <sym> is disabled, use file <file>
|
# optofffile <sym> <file> if option <sym> is disabled, use file <file>
|
||||||
@ -97,6 +98,7 @@ echo "$CONFNAME" $CONFTMP | awk '
|
|||||||
nfields["include"] = 2;
|
nfields["include"] = 2;
|
||||||
nfields["file"] = 2;
|
nfields["file"] = 2;
|
||||||
nfields["debug"] = 1;
|
nfields["debug"] = 1;
|
||||||
|
nfields["debugonly"] = 1;
|
||||||
nfields["defoption"] = 2;
|
nfields["defoption"] = 2;
|
||||||
nfields["optfile"] = 3;
|
nfields["optfile"] = 3;
|
||||||
nfields["optofffile"] = 3;
|
nfields["optofffile"] = 3;
|
||||||
@ -776,6 +778,9 @@ echo -n ' files.mk'
|
|||||||
# Default: optimize.
|
# Default: optimize.
|
||||||
BEGIN { debugflags="-O2"; }
|
BEGIN { debugflags="-O2"; }
|
||||||
$1=="debug" {
|
$1=="debug" {
|
||||||
|
debugflags="-g -Og";
|
||||||
|
}
|
||||||
|
$1=="debugonly" {
|
||||||
debugflags="-g";
|
debugflags="-g";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
40
kern/gdbscripts/mips-userland
Normal file
40
kern/gdbscripts/mips-userland
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# commands to interact with userland
|
||||||
|
|
||||||
|
define load-userprogram
|
||||||
|
init-if-undefined $userprog_loaded = 0
|
||||||
|
if !$userprog_loaded || !$_streq($arg0, $cur_userprog)
|
||||||
|
add-symbol-file $arg0 0x4000b0
|
||||||
|
# remove after adding the new file in case add-symbol-file fails
|
||||||
|
if $userprog_loaded
|
||||||
|
remove-symbol-file -a 0x4000b0
|
||||||
|
end
|
||||||
|
set $userprog_loaded = 1
|
||||||
|
set $cur_userprog = $arg0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
document load-userprogram
|
||||||
|
Loads a single user program into gdb's symbol table, removing
|
||||||
|
the previous user program loaded if one exists.
|
||||||
|
Usage: load-userprogram <path-to-prog>
|
||||||
|
end
|
||||||
|
alias -a lu = load-userprogram
|
||||||
|
|
||||||
|
define auto-userprogram
|
||||||
|
set $pname = curthread->t_proc->p_name
|
||||||
|
if $pname != 0 && $pname[0] != 0 && !$_streq($pname, "[kernel]")
|
||||||
|
if $pname[0] == '/'
|
||||||
|
set $pname = $pname + 1
|
||||||
|
end
|
||||||
|
# This dumbness works around the fact that gdb doesn't
|
||||||
|
# expand convenience variables when you execute commands.
|
||||||
|
# And setting the third parameter to True silences the command.
|
||||||
|
python gdb.execute("load-userprogram " + str(gdb.parse_and_eval("$pname")).split()[1], False, True)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
document auto-userprogram
|
||||||
|
Calls load-userprogram on whatever is curproc->p_name. It is meant
|
||||||
|
to work with kernels that set p_name to an absolute path to the
|
||||||
|
program, and it is designed to be called from hook-stop.
|
||||||
|
Usage: auto-userprogram
|
||||||
|
end
|
||||||
|
|
@ -88,6 +88,11 @@ struct cpu {
|
|||||||
struct tlbshootdown c_shootdown[TLBSHOOTDOWN_MAX];
|
struct tlbshootdown c_shootdown[TLBSHOOTDOWN_MAX];
|
||||||
unsigned c_numshootdown;
|
unsigned c_numshootdown;
|
||||||
struct spinlock c_ipi_lock;
|
struct spinlock c_ipi_lock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accessed by other cpus. Protected inside hangman.c.
|
||||||
|
*/
|
||||||
|
HANGMAN_ACTOR(c_hangman);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
84
kern/include/hangman.h
Normal file
84
kern/include/hangman.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015
|
||||||
|
* The President and Fellows of Harvard College.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HANGMAN_H
|
||||||
|
#define HANGMAN_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple deadlock detector. Enable with "options hangman" in the
|
||||||
|
* kernel config.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opt-hangman.h"
|
||||||
|
|
||||||
|
#if OPT_HANGMAN
|
||||||
|
|
||||||
|
struct hangman_actor {
|
||||||
|
const char *a_name;
|
||||||
|
const struct hangman_lockable *a_waiting;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hangman_lockable {
|
||||||
|
const char *l_name;
|
||||||
|
const struct hangman_actor *l_holding;
|
||||||
|
};
|
||||||
|
|
||||||
|
void hangman_wait(struct hangman_actor *a, struct hangman_lockable *l);
|
||||||
|
void hangman_acquire(struct hangman_actor *a, struct hangman_lockable *l);
|
||||||
|
void hangman_release(struct hangman_actor *a, struct hangman_lockable *l);
|
||||||
|
|
||||||
|
#define HANGMAN_ACTOR(sym) struct hangman_actor sym
|
||||||
|
#define HANGMAN_LOCKABLE(sym) struct hangman_lockable sym
|
||||||
|
|
||||||
|
#define HANGMAN_ACTORINIT(a, n) ((a)->a_name = (n), (a)->a_waiting = NULL)
|
||||||
|
#define HANGMAN_LOCKABLEINIT(l, n) ((l)->l_name = (n), (l)->l_holding = NULL)
|
||||||
|
|
||||||
|
#define HANGMAN_LOCKABLE_INITIALIZER { "spinlock", NULL }
|
||||||
|
|
||||||
|
#define HANGMAN_WAIT(a, l) hangman_wait(a, l)
|
||||||
|
#define HANGMAN_ACQUIRE(a, l) hangman_acquire(a, l)
|
||||||
|
#define HANGMAN_RELEASE(a, l) hangman_release(a, l)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define HANGMAN_ACTOR(sym)
|
||||||
|
#define HANGMAN_LOCKABLE(sym)
|
||||||
|
|
||||||
|
#define HANGMAN_ACTORINIT(a, name)
|
||||||
|
#define HANGMAN_LOCKABLEINIT(a, name)
|
||||||
|
|
||||||
|
#define HANGMAN_LOCKABLE_INITIALIZER
|
||||||
|
|
||||||
|
#define HANGMAN_WAIT(a, l)
|
||||||
|
#define HANGMAN_ACQUIRE(a, l)
|
||||||
|
#define HANGMAN_RELEASE(a, l)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* HANGMAN_H */
|
@ -194,7 +194,7 @@ void kprintf_bootstrap(void);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define DIVROUNDUP(a,b) (((a)+(b)-1)/(b))
|
#define DIVROUNDUP(a,b) (((a)+(b)-1)/(b))
|
||||||
#define ROUNDUP(a,b) (DIVROUNDUP(a,b)*b)
|
#define ROUNDUP(a,b) (DIVROUNDUP(a,b)*(b))
|
||||||
|
|
||||||
|
|
||||||
#endif /* _LIB_H_ */
|
#endif /* _LIB_H_ */
|
||||||
|
@ -55,6 +55,9 @@ size_t mainbus_ramsize(void);
|
|||||||
/* Switch on an inter-processor interrupt. (Low-level.) */
|
/* Switch on an inter-processor interrupt. (Low-level.) */
|
||||||
void mainbus_send_ipi(struct cpu *target);
|
void mainbus_send_ipi(struct cpu *target);
|
||||||
|
|
||||||
|
/* Request breaking into the debugger, where available. */
|
||||||
|
void mainbus_debugger(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The various ways to shut down the system. (These are very low-level
|
* The various ways to shut down the system. (These are very low-level
|
||||||
* and should generally not be called directly - md_poweroff, for
|
* and should generally not be called directly - md_poweroff, for
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cdefs.h>
|
#include <cdefs.h>
|
||||||
|
#include <hangman.h>
|
||||||
|
|
||||||
/* Inlining support - for making sure an out-of-line copy gets built */
|
/* Inlining support - for making sure an out-of-line copy gets built */
|
||||||
#ifndef SPINLOCK_INLINE
|
#ifndef SPINLOCK_INLINE
|
||||||
@ -57,12 +58,18 @@
|
|||||||
struct spinlock {
|
struct spinlock {
|
||||||
volatile spinlock_data_t splk_lock; /* Memory word where we spin. */
|
volatile spinlock_data_t splk_lock; /* Memory word where we spin. */
|
||||||
struct cpu *splk_holder; /* CPU holding this lock. */
|
struct cpu *splk_holder; /* CPU holding this lock. */
|
||||||
|
HANGMAN_LOCKABLE(splk_hangman); /* Deadlock detector hook. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializer for cases where a spinlock needs to be static or global.
|
* Initializer for cases where a spinlock needs to be static or global.
|
||||||
*/
|
*/
|
||||||
|
#ifdef OPT_HANGMAN
|
||||||
|
#define SPINLOCK_INITIALIZER { SPINLOCK_DATA_INITIALIZER, NULL, \
|
||||||
|
HANGMAN_LOCKABLE_INITIALIZER }
|
||||||
|
#else
|
||||||
#define SPINLOCK_INITIALIZER { SPINLOCK_DATA_INITIALIZER, NULL }
|
#define SPINLOCK_INITIALIZER { SPINLOCK_DATA_INITIALIZER, NULL }
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Spinlock functions.
|
* Spinlock functions.
|
||||||
|
@ -74,6 +74,7 @@ void V(struct semaphore *);
|
|||||||
*/
|
*/
|
||||||
struct lock {
|
struct lock {
|
||||||
char *lk_name;
|
char *lk_name;
|
||||||
|
HANGMAN_LOCKABLE(lk_hangman); /* Deadlock detector hook. */
|
||||||
// add what you need here
|
// add what you need here
|
||||||
// (don't forget to mark things volatile as needed)
|
// (don't forget to mark things volatile as needed)
|
||||||
};
|
};
|
||||||
|
@ -93,6 +93,7 @@ struct thread {
|
|||||||
struct switchframe *t_context; /* Saved register context (on stack) */
|
struct switchframe *t_context; /* Saved register context (on stack) */
|
||||||
struct cpu *t_cpu; /* CPU thread runs on */
|
struct cpu *t_cpu; /* CPU thread runs on */
|
||||||
struct proc *t_proc; /* Process thread belongs to */
|
struct proc *t_proc; /* Process thread belongs to */
|
||||||
|
HANGMAN_ACTOR(t_hangman); /* Deadlock detector hook */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Interrupt state fields.
|
* Interrupt state fields.
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
* Leave this alone, so we can tell what version of the OS/161 base
|
* Leave this alone, so we can tell what version of the OS/161 base
|
||||||
* code we gave you.
|
* code we gave you.
|
||||||
*/
|
*/
|
||||||
#define BASE_VERSION "2.0.2"
|
#define BASE_VERSION "2.0.3"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Change this as you see fit in the course of hacking the system.
|
* Change this as you see fit in the course of hacking the system.
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include <lib.h>
|
#include <lib.h>
|
||||||
#include <uio.h>
|
#include <uio.h>
|
||||||
#include <clock.h>
|
#include <clock.h>
|
||||||
|
#include <mainbus.h>
|
||||||
|
#include <synch.h>
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <proc.h>
|
#include <proc.h>
|
||||||
#include <vfs.h>
|
#include <vfs.h>
|
||||||
@ -247,6 +249,21 @@ cmd_sync(int nargs, char **args)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command for dropping to the debugger.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
int
|
||||||
|
cmd_debug(int nargs, char **args)
|
||||||
|
{
|
||||||
|
(void)nargs;
|
||||||
|
(void)args;
|
||||||
|
|
||||||
|
mainbus_debugger();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Command for doing an intentional panic.
|
* Command for doing an intentional panic.
|
||||||
*/
|
*/
|
||||||
@ -261,6 +278,81 @@ cmd_panic(int nargs, char **args)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subthread for intentially deadlocking.
|
||||||
|
*/
|
||||||
|
struct deadlock {
|
||||||
|
struct lock *lock1;
|
||||||
|
struct lock *lock2;
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
void
|
||||||
|
cmd_deadlockthread(void *ptr, unsigned long num)
|
||||||
|
{
|
||||||
|
struct deadlock *dl = ptr;
|
||||||
|
|
||||||
|
(void)num;
|
||||||
|
|
||||||
|
/* If it doesn't wedge right away, keep trying... */
|
||||||
|
while (1) {
|
||||||
|
lock_acquire(dl->lock2);
|
||||||
|
lock_acquire(dl->lock1);
|
||||||
|
kprintf("+");
|
||||||
|
lock_release(dl->lock1);
|
||||||
|
lock_release(dl->lock2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Command for doing an intentional deadlock.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
int
|
||||||
|
cmd_deadlock(int nargs, char **args)
|
||||||
|
{
|
||||||
|
struct deadlock dl;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
(void)nargs;
|
||||||
|
(void)args;
|
||||||
|
|
||||||
|
dl.lock1 = lock_create("deadlock1");
|
||||||
|
if (dl.lock1 == NULL) {
|
||||||
|
kprintf("lock_create failed\n");
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
dl.lock2 = lock_create("deadlock2");
|
||||||
|
if (dl.lock2 == NULL) {
|
||||||
|
lock_destroy(dl.lock1);
|
||||||
|
kprintf("lock_create failed\n");
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = thread_fork(args[0] /* thread name */,
|
||||||
|
NULL /* kernel thread */,
|
||||||
|
cmd_deadlockthread /* thread function */,
|
||||||
|
&dl /* thread arg */, 0 /* thread arg */);
|
||||||
|
if (result) {
|
||||||
|
kprintf("thread_fork failed: %s\n", strerror(result));
|
||||||
|
lock_release(dl.lock1);
|
||||||
|
lock_destroy(dl.lock2);
|
||||||
|
lock_destroy(dl.lock1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If it doesn't wedge right away, keep trying... */
|
||||||
|
while (1) {
|
||||||
|
lock_acquire(dl.lock1);
|
||||||
|
lock_acquire(dl.lock2);
|
||||||
|
kprintf(".");
|
||||||
|
lock_release(dl.lock2);
|
||||||
|
lock_release(dl.lock1);
|
||||||
|
}
|
||||||
|
/* NOTREACHED */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Command for shutting down.
|
* Command for shutting down.
|
||||||
*/
|
*/
|
||||||
@ -463,7 +555,9 @@ static const char *opsmenu[] = {
|
|||||||
"[cd] Change directory ",
|
"[cd] Change directory ",
|
||||||
"[pwd] Print current directory ",
|
"[pwd] Print current directory ",
|
||||||
"[sync] Sync filesystems ",
|
"[sync] Sync filesystems ",
|
||||||
|
"[debug] Drop to debugger ",
|
||||||
"[panic] Intentional panic ",
|
"[panic] Intentional panic ",
|
||||||
|
"[deadlock] Intentional deadlock ",
|
||||||
"[q] Quit and shut down ",
|
"[q] Quit and shut down ",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
@ -614,7 +708,9 @@ static struct {
|
|||||||
{ "cd", cmd_chdir },
|
{ "cd", cmd_chdir },
|
||||||
{ "pwd", cmd_pwd },
|
{ "pwd", cmd_pwd },
|
||||||
{ "sync", cmd_sync },
|
{ "sync", cmd_sync },
|
||||||
|
{ "debug", cmd_debug },
|
||||||
{ "panic", cmd_panic },
|
{ "panic", cmd_panic },
|
||||||
|
{ "deadlock", cmd_deadlock },
|
||||||
{ "q", cmd_quit },
|
{ "q", cmd_quit },
|
||||||
{ "exit", cmd_quit },
|
{ "exit", cmd_quit },
|
||||||
{ "halt", cmd_quit },
|
{ "halt", cmd_quit },
|
||||||
|
@ -156,6 +156,9 @@ arraytest2(int nargs, char **args)
|
|||||||
(void)nargs;
|
(void)nargs;
|
||||||
(void)args;
|
(void)args;
|
||||||
|
|
||||||
|
/* Silence warning with gcc 4.8 -Og (but not -O2) */
|
||||||
|
x = 0;
|
||||||
|
|
||||||
kprintf("Beginning large array test...\n");
|
kprintf("Beginning large array test...\n");
|
||||||
a = array_create();
|
a = array_create();
|
||||||
KASSERT(a != NULL);
|
KASSERT(a != NULL);
|
||||||
|
@ -681,7 +681,7 @@ checkfilesystem(int nargs, char **args)
|
|||||||
char *device;
|
char *device;
|
||||||
|
|
||||||
if (nargs != 2) {
|
if (nargs != 2) {
|
||||||
kprintf("Usage: fs[12345] filesystem:\n");
|
kprintf("Usage: fs[123456] filesystem:\n");
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
182
kern/thread/hangman.c
Normal file
182
kern/thread/hangman.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2015
|
||||||
|
* The President and Fellows of Harvard College.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple deadlock detector.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <spl.h>
|
||||||
|
#include <spinlock.h>
|
||||||
|
#include <hangman.h>
|
||||||
|
|
||||||
|
static struct spinlock hangman_lock = SPINLOCK_INITIALIZER;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for a path through the waits-for graph that goes from START to
|
||||||
|
* TARGET.
|
||||||
|
*
|
||||||
|
* Because lockables can only be held by one actor, and actors can
|
||||||
|
* only be waiting for one thing at a time, this turns out to be
|
||||||
|
* quite simple.
|
||||||
|
*/
|
||||||
|
static
|
||||||
|
void
|
||||||
|
hangman_check(const struct hangman_lockable *start,
|
||||||
|
const struct hangman_actor *target)
|
||||||
|
{
|
||||||
|
const struct hangman_actor *cur;
|
||||||
|
|
||||||
|
cur = start->l_holding;
|
||||||
|
while (cur != NULL) {
|
||||||
|
if (cur == target) {
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
if (cur->a_waiting == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur = cur->a_waiting->l_holding;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
found:
|
||||||
|
/*
|
||||||
|
* None of this can change while we print it (that's the point
|
||||||
|
* of it being a deadlock) so drop hangman_lock while
|
||||||
|
* printing; otherwise we can come back via kprintf_spinlock
|
||||||
|
* and that makes a mess. But force splhigh() explicitly so
|
||||||
|
* the console prints in polled mode and to discourage other
|
||||||
|
* things from running in the middle of the printout.
|
||||||
|
*/
|
||||||
|
splhigh();
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
|
||||||
|
kprintf("hangman: Detected lock cycle!\n");
|
||||||
|
kprintf("hangman: in %s (%p);\n", target->a_name, target);
|
||||||
|
kprintf("hangman: waiting for %s (%p), but:\n", start->l_name, start);
|
||||||
|
kprintf(" lockable %s (%p)\n", start->l_name, start);
|
||||||
|
cur = start->l_holding;
|
||||||
|
while (cur != target) {
|
||||||
|
kprintf(" held by actor %s (%p)\n", cur->a_name, cur);
|
||||||
|
kprintf(" waiting for lockable %s (%p)\n",
|
||||||
|
cur->a_waiting->l_name, cur->a_waiting);
|
||||||
|
cur = cur->a_waiting->l_holding;
|
||||||
|
}
|
||||||
|
kprintf(" held by actor %s (%p)\n", cur->a_name, cur);
|
||||||
|
panic("Deadlock.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that a is about to wait for l.
|
||||||
|
*
|
||||||
|
* Note that there's no point calling this if a isn't going to wait,
|
||||||
|
* because in that case l->l_holding will be null and the check
|
||||||
|
* won't go anywhere.
|
||||||
|
*
|
||||||
|
* One could also maintain in memory a graph of all requests ever
|
||||||
|
* seen, in order to detect lock order inversions that haven't
|
||||||
|
* actually deadlocked; but there are a lot of ways in which this is
|
||||||
|
* tricky and problematic. For now we'll settle for just detecting and
|
||||||
|
* reporting deadlocks that do happen.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hangman_wait(struct hangman_actor *a,
|
||||||
|
struct hangman_lockable *l)
|
||||||
|
{
|
||||||
|
if (l == &hangman_lock.splk_hangman) {
|
||||||
|
/* don't recurse */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_acquire(&hangman_lock);
|
||||||
|
|
||||||
|
if (a->a_waiting != NULL) {
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
panic("hangman_wait: already waiting for something?\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
hangman_check(l, a);
|
||||||
|
a->a_waiting = l;
|
||||||
|
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hangman_acquire(struct hangman_actor *a,
|
||||||
|
struct hangman_lockable *l)
|
||||||
|
{
|
||||||
|
if (l == &hangman_lock.splk_hangman) {
|
||||||
|
/* don't recurse */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_acquire(&hangman_lock);
|
||||||
|
|
||||||
|
if (a->a_waiting != l) {
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
panic("hangman_acquire: not waiting for lock %s (%p)\n",
|
||||||
|
l->l_name, l);
|
||||||
|
}
|
||||||
|
if (l->l_holding != NULL) {
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
panic("hangman_acquire: lock %s (%p) still held by %s (%p)\n",
|
||||||
|
l->l_name, l, a->a_name, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
l->l_holding = a;
|
||||||
|
a->a_waiting = NULL;
|
||||||
|
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hangman_release(struct hangman_actor *a,
|
||||||
|
struct hangman_lockable *l)
|
||||||
|
{
|
||||||
|
if (l == &hangman_lock.splk_hangman) {
|
||||||
|
/* don't recurse */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_acquire(&hangman_lock);
|
||||||
|
|
||||||
|
if (a->a_waiting != NULL) {
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
panic("hangman_release: waiting for something?\n");
|
||||||
|
}
|
||||||
|
if (l->l_holding != a) {
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
panic("hangman_release: not the holder\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
l->l_holding = NULL;
|
||||||
|
|
||||||
|
spinlock_release(&hangman_lock);
|
||||||
|
}
|
@ -52,6 +52,7 @@ spinlock_init(struct spinlock *splk)
|
|||||||
{
|
{
|
||||||
spinlock_data_set(&splk->splk_lock, 0);
|
spinlock_data_set(&splk->splk_lock, 0);
|
||||||
splk->splk_holder = NULL;
|
splk->splk_holder = NULL;
|
||||||
|
HANGMAN_LOCKABLEINIT(&splk->splk_hangman, "spinlock");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -85,6 +86,8 @@ spinlock_acquire(struct spinlock *splk)
|
|||||||
panic("Deadlock on spinlock %p\n", splk);
|
panic("Deadlock on spinlock %p\n", splk);
|
||||||
}
|
}
|
||||||
mycpu->c_spinlocks++;
|
mycpu->c_spinlocks++;
|
||||||
|
|
||||||
|
HANGMAN_WAIT(&curcpu->c_hangman, &splk->splk_hangman);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mycpu = NULL;
|
mycpu = NULL;
|
||||||
@ -112,6 +115,10 @@ spinlock_acquire(struct spinlock *splk)
|
|||||||
|
|
||||||
membar_store_any();
|
membar_store_any();
|
||||||
splk->splk_holder = mycpu;
|
splk->splk_holder = mycpu;
|
||||||
|
|
||||||
|
if (CURCPU_EXISTS()) {
|
||||||
|
HANGMAN_ACQUIRE(&curcpu->c_hangman, &splk->splk_hangman);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -125,6 +132,7 @@ spinlock_release(struct spinlock *splk)
|
|||||||
KASSERT(splk->splk_holder == curcpu->c_self);
|
KASSERT(splk->splk_holder == curcpu->c_self);
|
||||||
KASSERT(curcpu->c_spinlocks > 0);
|
KASSERT(curcpu->c_spinlocks > 0);
|
||||||
curcpu->c_spinlocks--;
|
curcpu->c_spinlocks--;
|
||||||
|
HANGMAN_RELEASE(&curcpu->c_hangman, &splk->splk_hangman);
|
||||||
}
|
}
|
||||||
|
|
||||||
splk->splk_holder = NULL;
|
splk->splk_holder = NULL;
|
||||||
|
@ -154,6 +154,8 @@ lock_create(const char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANGMAN_LOCKABLEINIT(&lock->lk_hangman, lock->lk_name);
|
||||||
|
|
||||||
// add stuff here as needed
|
// add stuff here as needed
|
||||||
|
|
||||||
return lock;
|
return lock;
|
||||||
@ -173,14 +175,23 @@ lock_destroy(struct lock *lock)
|
|||||||
void
|
void
|
||||||
lock_acquire(struct lock *lock)
|
lock_acquire(struct lock *lock)
|
||||||
{
|
{
|
||||||
|
/* Call this (atomically) before waiting for a lock */
|
||||||
|
//HANGMAN_WAIT(&curthread->t_hangman, &lock->lk_hangman);
|
||||||
|
|
||||||
// Write this
|
// Write this
|
||||||
|
|
||||||
(void)lock; // suppress warning until code gets written
|
(void)lock; // suppress warning until code gets written
|
||||||
|
|
||||||
|
/* Call this (atomically) once the lock is acquired */
|
||||||
|
//HANGMAN_ACQUIRE(&curthread->t_hangman, &lock->lk_hangman);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
lock_release(struct lock *lock)
|
lock_release(struct lock *lock)
|
||||||
{
|
{
|
||||||
|
/* Call this (atomically) when the lock is released */
|
||||||
|
//HANGMAN_RELEASE(&curthread->t_hangman, &lock->lk_hangman);
|
||||||
|
|
||||||
// Write this
|
// Write this
|
||||||
|
|
||||||
(void)lock; // suppress warning until code gets written
|
(void)lock; // suppress warning until code gets written
|
||||||
|
@ -145,6 +145,7 @@ thread_create(const char *name)
|
|||||||
thread->t_context = NULL;
|
thread->t_context = NULL;
|
||||||
thread->t_cpu = NULL;
|
thread->t_cpu = NULL;
|
||||||
thread->t_proc = NULL;
|
thread->t_proc = NULL;
|
||||||
|
HANGMAN_ACTORINIT(&thread->t_hangman, thread->t_name);
|
||||||
|
|
||||||
/* Interrupt state fields */
|
/* Interrupt state fields */
|
||||||
thread->t_in_interrupt = false;
|
thread->t_in_interrupt = false;
|
||||||
@ -244,6 +245,8 @@ cpu_create(unsigned hardware_number)
|
|||||||
curcpu->c_curthread = curthread;
|
curcpu->c_curthread = curthread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HANGMAN_ACTORINIT(&c->c_hangman, "cpu");
|
||||||
|
|
||||||
result = proc_addthread(kproc, c->c_curthread);
|
result = proc_addthread(kproc, c->c_curthread);
|
||||||
if (result) {
|
if (result) {
|
||||||
panic("cpu_create: proc_addthread:: %s\n", strerror(result));
|
panic("cpu_create: proc_addthread:: %s\n", strerror(result));
|
||||||
|
@ -129,6 +129,23 @@ vfs_biglock_acquire(void)
|
|||||||
if (!lock_do_i_hold(vfs_biglock)) {
|
if (!lock_do_i_hold(vfs_biglock)) {
|
||||||
lock_acquire(vfs_biglock);
|
lock_acquire(vfs_biglock);
|
||||||
}
|
}
|
||||||
|
else if (vfs_biglock_depth == 0) {
|
||||||
|
/*
|
||||||
|
* Supposedly we hold it, but the depth is 0. This may
|
||||||
|
* mean: (1) the count is messed up, or (2)
|
||||||
|
* lock_do_i_hold is lying. Since OS/161 ships out of
|
||||||
|
* the box with unimplemented locks (students
|
||||||
|
* implement them) that always return true, assume
|
||||||
|
* situation (2). In this case acquire the lock
|
||||||
|
* anyway.
|
||||||
|
*
|
||||||
|
* Once you have working locks, this won't be the
|
||||||
|
* case, and if you get here it should be situation
|
||||||
|
* (1), in which case the count is messed up and one
|
||||||
|
* can panic.
|
||||||
|
*/
|
||||||
|
lock_acquire(vfs_biglock);
|
||||||
|
}
|
||||||
vfs_biglock_depth++;
|
vfs_biglock_depth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,6 +398,9 @@ vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs)
|
|||||||
unsigned index;
|
unsigned index;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
|
/* Silence warning with gcc 4.8 -Og (but not -O2) */
|
||||||
|
index = 0;
|
||||||
|
|
||||||
vfs_biglock_acquire();
|
vfs_biglock_acquire();
|
||||||
|
|
||||||
name = kstrdup(dname);
|
name = kstrdup(dname);
|
||||||
|
@ -135,6 +135,13 @@ getdevice(char *path, char **subpath, struct vnode **startvn)
|
|||||||
|
|
||||||
KASSERT(vfs_biglock_do_i_hold());
|
KASSERT(vfs_biglock_do_i_hold());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entirely empty filenames aren't legal.
|
||||||
|
*/
|
||||||
|
if (path[0] == 0) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Locate the first colon or slash.
|
* Locate the first colon or slash.
|
||||||
*/
|
*/
|
||||||
|
@ -1113,6 +1113,10 @@ subpage_kfree(void *ptr)
|
|||||||
|
|
||||||
checksubpages();
|
checksubpages();
|
||||||
|
|
||||||
|
/* Silence warnings with gcc 4.8 -Og (but not -O2) */
|
||||||
|
prpage = 0;
|
||||||
|
blktype = 0;
|
||||||
|
|
||||||
for (pr = allbase; pr; pr = pr->next_all) {
|
for (pr = allbase; pr; pr = pr->next_all) {
|
||||||
prpage = PR_PAGEADDR(pr);
|
prpage = PR_PAGEADDR(pr);
|
||||||
blktype = PR_BLOCKTYPE(pr);
|
blktype = PR_BLOCKTYPE(pr);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# MKDIRS logic
|
# OS/161 build environment: MKDIRS logic
|
||||||
#
|
#
|
||||||
# This generates rules for all intermediate directories as well as
|
# This generates rules for all intermediate directories as well as
|
||||||
# the directories listed. (That is, if you say MKDIRS=/usr/bin/foo
|
# the directories listed. (That is, if you say MKDIRS=/usr/bin/foo
|
||||||
|
92
mk/os161.script.mk
Normal file
92
mk/os161.script.mk
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#
|
||||||
|
# OS/161 build environment: install scripts
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# TOP=../..
|
||||||
|
# .include "$(TOP)/mk/os161.config.mk"
|
||||||
|
# [defs go here]
|
||||||
|
# .include "$(TOP)/mk/os161.man.mk"
|
||||||
|
# [any extra rules go here]
|
||||||
|
#
|
||||||
|
# Variables controlling this file:
|
||||||
|
#
|
||||||
|
# EXECSCRIPTS Executable files to install.
|
||||||
|
# NONEXECSCRIPTS Non-executable files to install.
|
||||||
|
#
|
||||||
|
# SCRIPTDIR Directory under $(OSTREE) to install into,
|
||||||
|
# e.g. /hostbin. Should have a leading slash.
|
||||||
|
#
|
||||||
|
|
||||||
|
ALLSCRIPTS=$(NONEXECSCRIPTS)
|
||||||
|
|
||||||
|
# We may want these directories created. (Used by os161.baserules.mk.)
|
||||||
|
MKDIRS+=$(MYBUILDDIR)
|
||||||
|
MKDIRS+=$(INSTALLTOP)$(SCRIPTDIR)
|
||||||
|
MKDIRS+=$(OSTREE)$(SCRIPTDIR)
|
||||||
|
|
||||||
|
# Default rule: "build" the executable scripts. This sets the hash-bang
|
||||||
|
# header with the interpreter path.
|
||||||
|
# (In make the first rule found is the default.)
|
||||||
|
all: all-local
|
||||||
|
all-local: $(MYBUILDDIR) .WAIT
|
||||||
|
.for S in $(EXECSCRIPTS)
|
||||||
|
ALLSCRIPTS+=$(MYBUILDDIR)/$(S)
|
||||||
|
all-local: $(MYBUILDDIR)/$(S)
|
||||||
|
$(MYBUILDDIR)/$(S): $(S)
|
||||||
|
# This must test $@, or assign another variable; it can't test $(S).
|
||||||
|
# (This is a bmake bug.)
|
||||||
|
.if !empty(@:M*.py)
|
||||||
|
echo '#!'"$(PYTHON_INTERPRETER)" > $@.new
|
||||||
|
.else
|
||||||
|
sed -n -e '1p' < $(S) > $@.new
|
||||||
|
.endif
|
||||||
|
sed -e '1d' < $(S) >> $@.new
|
||||||
|
chmod 755 $@.new
|
||||||
|
mv -f $@.new $@
|
||||||
|
.endfor
|
||||||
|
|
||||||
|
# depend doesn't need to do anything
|
||||||
|
depend-local: ;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Install: we can install into either $(INSTALLTOP) or $(OSTREE).
|
||||||
|
# When building the whole system, we always install into the staging
|
||||||
|
# area. However, if you're working on a particular program it is
|
||||||
|
# usually convenient to be able to install it directly to $(OSTREE)
|
||||||
|
# instead of doing a complete top-level install.
|
||||||
|
#
|
||||||
|
# Note that we make a hard link instead of a copy by default to reduce
|
||||||
|
# overhead.
|
||||||
|
#
|
||||||
|
install-staging-local: $(INSTALLTOP)$(SCRIPTDIR) .WAIT
|
||||||
|
|
||||||
|
.for _F_ in $(ALLSCRIPTS)
|
||||||
|
install-staging-local: $(INSTALLTOP)$(SCRIPTDIR)/$(_F_:T)
|
||||||
|
$(INSTALLTOP)$(SCRIPTDIR)/$(_F_:T): $(_F_)
|
||||||
|
rm -f $(.TARGET)
|
||||||
|
ln $(_F_) $(.TARGET) || cp $(_F_) $(.TARGET)
|
||||||
|
.endfor
|
||||||
|
|
||||||
|
install-local: $(OSTREE)$(SCRIPTDIR) .WAIT installscripts
|
||||||
|
installscripts:
|
||||||
|
.for _F_ in $(ALLSCRIPTS)
|
||||||
|
rm -f $(OSTREE)$(SCRIPTDIR)/$(_F_:T)
|
||||||
|
ln $(_F_) $(OSTREE)$(SCRIPTDIR)/$(_F_:T) || \
|
||||||
|
cp $(_F_) $(OSTREE)$(SCRIPTDIR)/$(_F_:T)
|
||||||
|
.endfor
|
||||||
|
|
||||||
|
# clean: remove build products
|
||||||
|
clean-local:
|
||||||
|
.for S in $(EXECSCRIPTS)
|
||||||
|
rm -f $(MYBUILDDIR)/$(S)
|
||||||
|
.endfor
|
||||||
|
|
||||||
|
# Mark targets that don't represent files PHONY, to prevent various
|
||||||
|
# lossage if files by those names appear.
|
||||||
|
.PHONY: all all-local install-staging-local install-local installscripts
|
||||||
|
.PHONY: clean-local
|
||||||
|
|
||||||
|
# Finally, get the shared definitions for the most basic rules.
|
||||||
|
.include "$(TOP)/mk/os161.baserules.mk"
|
||||||
|
|
||||||
|
# End.
|
@ -177,7 +177,7 @@ print(const char *path)
|
|||||||
typech = '?';
|
typech = '?';
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%crwx------ %2d root %-8llu",
|
printf("%crwx------ %2d root %-7llu ",
|
||||||
typech,
|
typech,
|
||||||
statbuf.st_nlink,
|
statbuf.st_nlink,
|
||||||
statbuf.st_size);
|
statbuf.st_size);
|
||||||
|
@ -197,11 +197,9 @@ dumpdata(void)
|
|||||||
|
|
||||||
indexsize = dolseek(indexfd, indexname, 0, SEEK_CUR);
|
indexsize = dolseek(indexfd, indexname, 0, SEEK_CUR);
|
||||||
pos = indexsize;
|
pos = indexsize;
|
||||||
while (1) {
|
assert(pos % sizeof(x) == 0);
|
||||||
|
while (pos > 0) {
|
||||||
pos -= sizeof(x);
|
pos -= sizeof(x);
|
||||||
if (pos == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(pos >= 0);
|
assert(pos >= 0);
|
||||||
dolseek(indexfd, indexname, pos, SEEK_SET);
|
dolseek(indexfd, indexname, pos, SEEK_SET);
|
||||||
|
|
||||||
|
@ -69,6 +69,11 @@ pipe_unaligned(void)
|
|||||||
|
|
||||||
rv = pipe((int *)ptr);
|
rv = pipe((int *)ptr);
|
||||||
report_survival(rv, errno, &result);
|
report_survival(rv, errno, &result);
|
||||||
|
if (rv == 0) {
|
||||||
|
memmove(fds, ptr, 2*sizeof(int));
|
||||||
|
close(fds[0]);
|
||||||
|
close(fds[1]);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
static char writebuf[40] = "Twiddle dee dee, Twiddle dum dum.......\n";
|
static char writebuf[41] = "Twiddle dee dee, Twiddle dum dum.......\n";
|
||||||
static char readbuf[41];
|
static char readbuf[41];
|
||||||
|
|
||||||
const char *file;
|
const char *file;
|
||||||
|
@ -152,12 +152,20 @@ void
|
|||||||
test(int nowait)
|
test(int nowait)
|
||||||
{
|
{
|
||||||
int pid0, pid1, pid2, pid3;
|
int pid0, pid1, pid2, pid3;
|
||||||
|
int depth = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Caution: This generates processes geometrically.
|
* Caution: This generates processes geometrically.
|
||||||
*
|
*
|
||||||
* It is unrolled to encourage gcc to registerize the pids,
|
* It is unrolled to encourage gcc to registerize the pids,
|
||||||
* to prevent wait/exit problems if fork corrupts memory.
|
* to prevent wait/exit problems if fork corrupts memory.
|
||||||
|
*
|
||||||
|
* Note: if the depth prints trigger and show that the depth
|
||||||
|
* is too small, the most likely explanation is that the fork
|
||||||
|
* child is returning from the write() inside putchar()
|
||||||
|
* instead of from fork() and thus skipping the depth++. This
|
||||||
|
* is a fairly common problem caused by races in the kernel
|
||||||
|
* fork code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -184,18 +192,37 @@ test(int nowait)
|
|||||||
pid0 = dofork();
|
pid0 = dofork();
|
||||||
nprintf(".");
|
nprintf(".");
|
||||||
write(fd, "A", 1);
|
write(fd, "A", 1);
|
||||||
|
depth++;
|
||||||
|
if (depth != 1) {
|
||||||
|
warnx("depth %d, should be 1", depth);
|
||||||
|
}
|
||||||
check();
|
check();
|
||||||
|
|
||||||
pid1 = dofork();
|
pid1 = dofork();
|
||||||
nprintf(".");
|
nprintf(".");
|
||||||
write(fd, "B", 1);
|
write(fd, "B", 1);
|
||||||
|
depth++;
|
||||||
|
if (depth != 2) {
|
||||||
|
warnx("depth %d, should be 2", depth);
|
||||||
|
}
|
||||||
check();
|
check();
|
||||||
|
|
||||||
pid2 = dofork();
|
pid2 = dofork();
|
||||||
nprintf(".");
|
nprintf(".");
|
||||||
write(fd, "C", 1);
|
write(fd, "C", 1);
|
||||||
|
depth++;
|
||||||
|
if (depth != 3) {
|
||||||
|
warnx("depth %d, should be 3", depth);
|
||||||
|
}
|
||||||
check();
|
check();
|
||||||
|
|
||||||
pid3 = dofork();
|
pid3 = dofork();
|
||||||
nprintf(".");
|
nprintf(".");
|
||||||
write(fd, "D", 1);
|
write(fd, "D", 1);
|
||||||
|
depth++;
|
||||||
|
if (depth != 4) {
|
||||||
|
warnx("depth %d, should be 4", depth);
|
||||||
|
}
|
||||||
check();
|
check();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
* hog.c
|
* hog.c
|
||||||
* Spawned by several other user programs to test time-slicing.
|
* Spawned by several other user programs to test time-slicing.
|
||||||
*
|
*
|
||||||
* This does not differ from guzzle in any important way.
|
* Loops consuming CPU cycles.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -134,6 +134,7 @@ semP(struct usem *sem, size_t num)
|
|||||||
if (read(sem->fd, c, num) < 0) {
|
if (read(sem->fd, c, num) < 0) {
|
||||||
err(1, "%s: read", sem->name);
|
err(1, "%s: read", sem->name);
|
||||||
}
|
}
|
||||||
|
(void)c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
|
@ -273,16 +273,24 @@ static
|
|||||||
void
|
void
|
||||||
semP(struct usem *sem, size_t num)
|
semP(struct usem *sem, size_t num)
|
||||||
{
|
{
|
||||||
if (read(sem->fd, NULL, num) < 0) {
|
char c[num];
|
||||||
|
|
||||||
|
if (read(sem->fd, c, num) < 0) {
|
||||||
err(1, "%s: read", sem->name);
|
err(1, "%s: read", sem->name);
|
||||||
}
|
}
|
||||||
|
(void)c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
void
|
void
|
||||||
semV(struct usem *sem, size_t num)
|
semV(struct usem *sem, size_t num)
|
||||||
{
|
{
|
||||||
if (write(sem->fd, NULL, num) < 0) {
|
char c[num];
|
||||||
|
|
||||||
|
/* semfs does not use these values, but be conservative */
|
||||||
|
memset(c, 0, num);
|
||||||
|
|
||||||
|
if (write(sem->fd, c, num) < 0) {
|
||||||
err(1, "%s: write", sem->name);
|
err(1, "%s: write", sem->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,7 +334,12 @@ makeprocs(bool dowait)
|
|||||||
for (i=0; i<NJOBS; i++) {
|
for (i=0; i<NJOBS; i++) {
|
||||||
pids[i] = fork();
|
pids[i] = fork();
|
||||||
if (pids[i]<0) {
|
if (pids[i]<0) {
|
||||||
warn("fork");
|
warn("fork (process %d)", i);
|
||||||
|
if (dowait) {
|
||||||
|
semopen(&s1);
|
||||||
|
semV(&s1, 1);
|
||||||
|
semclose(&s1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pids[i]==0) {
|
if (pids[i]==0) {
|
||||||
/* child */
|
/* child */
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
|
||||||
@ -84,9 +85,9 @@ void
|
|||||||
Pn(struct usem *sem, unsigned count)
|
Pn(struct usem *sem, unsigned count)
|
||||||
{
|
{
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
char c;
|
char c[count];
|
||||||
|
|
||||||
r = read(sem->fd, &c, count);
|
r = read(sem->fd, c, count);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
err(1, "%s: read", sem->name);
|
err(1, "%s: read", sem->name);
|
||||||
}
|
}
|
||||||
@ -105,9 +106,12 @@ void
|
|||||||
Vn(struct usem *sem, unsigned count)
|
Vn(struct usem *sem, unsigned count)
|
||||||
{
|
{
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
char c;
|
char c[count];
|
||||||
|
|
||||||
r = write(sem->fd, &c, count);
|
/* semfs does not use these values, but be conservative */
|
||||||
|
memset(c, 0, count);
|
||||||
|
|
||||||
|
r = write(sem->fd, c, count);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
err(1, "%s: write", sem->name);
|
err(1, "%s: write", sem->name);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user