Initial fix of upstream merge.

This commit is contained in:
Geoffrey Challen 2017-02-09 10:09:30 -05:00
commit a662851491
42 changed files with 828 additions and 74 deletions

98
CHANGES
View File

@ -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
------------------------------
@ -53,6 +143,14 @@ OS/161 2.0.2 released 20160112
anyway. If they aren't actually constant because of bugs, reading
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
- In testbin/multiexec, if fork fails partway through, continue
with the forks we got. Otherwise the subprocesses we started hang

View File

@ -69,8 +69,6 @@ struct trapframe {
uint32_t tf_s7;
uint32_t tf_t8;
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_sp;
uint32_t tf_s8;

View File

@ -101,6 +101,8 @@ mips_general_end:
.text
.type common_exception,@function
.ent common_exception
.cfi_startproc
.cfi_signal_frame
common_exception:
mfc0 k0, c0_status /* Get status register */
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
* one more for proper (64-bit) stack alignment.
*/
addi sp, sp, -168
addi sp, sp, -160
.cfi_def_cfa sp, 0
/*
* Save general registers.
@ -143,70 +146,89 @@ common_exception:
*
* The order here must match mips/include/trapframe.h.
*
* gdb disassembles this code to try to figure out what registers
* are where, and it isn't very bright. So in order to make gdb be
* able to trace the stack back through here, we play some silly
* games.
* gdb uses the .cfi_offset assembler directives inserted below to
* to figure out where each register is stored. Since we've marked
* this function as a "signal handler" with the .cfi_signal_frame
* directive, gdb won't complain about the fact that the stack
* is noncontiguous (if we're coming from userland).
*
* In particular:
* (1) We store the return address register into the epc slot,
* which makes gdb think it's the return address slot. Then
* we store the real epc value over that.
* (2) We store the current sp into the sp slot, which makes gdb
* think it's the stack pointer slot. Then we store the real
* value.
* (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.
* We also play a trick with the return address: we mark the ra
* register as stored to the stack normally and then mark the
* return address for *this* function as being in the k1 register
* using the .cfi_return_column directive. gdb is then able to
* recognize that the ra we've stored here is the return address
* for the function that was executing when this exception was
* taken.
*
* This logic has not been tested against a recent gdb and has
* probably bitrotted. Someone(TM) should figure out what gdb
* currently expects -- or maybe even patch gdb to understand a
* better form of this that doesn't waste so many cycles.
* All of the cfi (call frame information) material is compiled
* into the .eh_frame section of the compiled kernel.
*/
sw ra, 160(sp) /* dummy for gdb */
sw s8, 156(sp) /* save s8 */
sw sp, 152(sp) /* dummy for gdb */
sw gp, 148(sp) /* save gp */
sw k1, 144(sp) /* dummy for gdb */
sw k0, 140(sp) /* dummy for gdb */
sw k1, 152(sp) /* real saved sp */
sw s8, 148(sp) /* save s8 */
.cfi_offset s8, 148
sw k1, 144(sp) /* real saved sp */
.cfi_offset sp, 144
sw gp, 140(sp) /* save gp */
nop /* delay slot for store */
.cfi_offset gp, 140
.cfi_return_column k1
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)
.cfi_offset t9, 136
sw t8, 132(sp)
.cfi_offset t8, 132
sw s7, 128(sp)
.cfi_offset s7, 128
sw s6, 124(sp)
.cfi_offset s6, 124
sw s5, 120(sp)
.cfi_offset s5, 120
sw s4, 116(sp)
.cfi_offset s4, 116
sw s3, 112(sp)
.cfi_offset s3, 112
sw s2, 108(sp)
.cfi_offset s2, 108
sw s1, 104(sp)
.cfi_offset s1, 104
sw s0, 100(sp)
.cfi_offset s0, 100
sw t7, 96(sp)
.cfi_offset t7, 96
sw t6, 92(sp)
.cfi_offset t6, 92
sw t5, 88(sp)
.cfi_offset t5, 88
sw t4, 84(sp)
.cfi_offset t4, 84
sw t3, 80(sp)
.cfi_offset t3, 80
sw t2, 76(sp)
.cfi_offset t2, 76
sw t1, 72(sp)
.cfi_offset t1, 72
sw t0, 68(sp)
.cfi_offset t0, 68
sw a3, 64(sp)
.cfi_offset a3, 64
sw a2, 60(sp)
.cfi_offset a2, 60
sw a1, 56(sp)
.cfi_offset a1, 56
sw a0, 52(sp)
.cfi_offset a0, 52
sw v1, 48(sp)
.cfi_offset v1, 48
sw v0, 44(sp)
.cfi_offset v0, 44
sw AT, 40(sp)
.cfi_offset AT, 40
sw ra, 36(sp)
.cfi_offset ra, 36
/*
* Save special registers.
@ -227,11 +249,6 @@ common_exception:
mfc0 t4, c0_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.
*/
@ -260,9 +277,6 @@ common_exception:
jal mips_trap /* call it */
nop /* delay slot */
/* Something must be here or gdb doesn't find the stack frame. */
nop
/*
* Now restore stuff and return from the exception.
* Interrupts should be off.
@ -309,20 +323,17 @@ exception_return:
lw s7, 128(sp)
lw t8, 132(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 */
/* 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) */
lw sp, 144(sp) /* fetch saved sp (must be last) */
/* done */
jr k0 /* jump back */
jr k1 /* jump back */
rfe /* in delay slot */
.cfi_endproc
.end common_exception
/*

View File

@ -130,8 +130,8 @@ mips_trap(struct trapframe *tf)
bool iskern;
int spl;
/* The trap frame is supposed to be 37 registers long. */
KASSERT(sizeof(struct trapframe)==(37*4));
/* The trap frame is supposed to be 35 registers long. */
KASSERT(sizeof(struct trapframe)==(35*4));
/*
* Extract the exception code info from the register fields.

View File

@ -54,6 +54,14 @@
*
* These arrays are also used to start up new CPUs, for roughly the
* 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];

View File

@ -42,6 +42,7 @@
#include <mainbus.h>
#include <sys161/bus.h>
#include <lamebus/lamebus.h>
#include <lamebus/ltrace.h>
#include "autoconf.h"
/*
@ -275,6 +276,15 @@ mainbus_send_ipi(struct cpu *target)
lamebus_assert_ipi(lamebus, target);
}
/*
* Trigger the debugger.
*/
void
mainbus_debugger(void)
{
ltrace_stop(0);
}
/*
* Interrupt dispatcher.
*/

View File

@ -3,7 +3,9 @@
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.

View File

@ -7,6 +7,7 @@
include conf/conf.kern # get definitions of available options
#debug # Optimizing compile (no debug).
#debugonly
options noasserts # Disable assertions.
#

View File

@ -5,6 +5,8 @@
include conf/conf.kern # get definitions of available options
debug # Compile with debug info.
#debugonly # Compile with debug info only (no -Og).
#options hangman # Deadlock detection. (off by default)
#
# Device drivers for hardware.

View File

@ -7,6 +7,7 @@
include conf/conf.kern # get definitions of available options
#debug # Optimizing compile (no debug).
#debugonly
options noasserts # Disable assertions.
#

View File

@ -334,6 +334,9 @@ file thread/synch.c
file thread/thread.c
file thread/threadlist.c
defoption hangman
optfile hangman thread/hangman.c
#
# Process system
#

View File

@ -12,7 +12,8 @@
# Recognized directives:
#
# 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
# optfile <sym> <file> if option <sym> is enabled, 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["file"] = 2;
nfields["debug"] = 1;
nfields["debugonly"] = 1;
nfields["defoption"] = 2;
nfields["optfile"] = 3;
nfields["optofffile"] = 3;
@ -776,6 +778,9 @@ echo -n ' files.mk'
# Default: optimize.
BEGIN { debugflags="-O2"; }
$1=="debug" {
debugflags="-g -Og";
}
$1=="debugonly" {
debugflags="-g";
}

View 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

View File

@ -88,6 +88,11 @@ struct cpu {
struct tlbshootdown c_shootdown[TLBSHOOTDOWN_MAX];
unsigned c_numshootdown;
struct spinlock c_ipi_lock;
/*
* Accessed by other cpus. Protected inside hangman.c.
*/
HANGMAN_ACTOR(c_hangman);
};
/*

84
kern/include/hangman.h Normal file
View 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 */

View File

@ -194,7 +194,7 @@ void kprintf_bootstrap(void);
*/
#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_ */

View File

@ -55,6 +55,9 @@ size_t mainbus_ramsize(void);
/* Switch on an inter-processor interrupt. (Low-level.) */
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
* and should generally not be called directly - md_poweroff, for

View File

@ -36,6 +36,7 @@
*/
#include <cdefs.h>
#include <hangman.h>
/* Inlining support - for making sure an out-of-line copy gets built */
#ifndef SPINLOCK_INLINE
@ -57,12 +58,18 @@
struct spinlock {
volatile spinlock_data_t splk_lock; /* Memory word where we spin. */
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.
*/
#ifdef OPT_HANGMAN
#define SPINLOCK_INITIALIZER { SPINLOCK_DATA_INITIALIZER, NULL, \
HANGMAN_LOCKABLE_INITIALIZER }
#else
#define SPINLOCK_INITIALIZER { SPINLOCK_DATA_INITIALIZER, NULL }
#endif
/*
* Spinlock functions.

View File

@ -74,6 +74,7 @@ void V(struct semaphore *);
*/
struct lock {
char *lk_name;
HANGMAN_LOCKABLE(lk_hangman); /* Deadlock detector hook. */
// add what you need here
// (don't forget to mark things volatile as needed)
};

View File

@ -93,6 +93,7 @@ struct thread {
struct switchframe *t_context; /* Saved register context (on stack) */
struct cpu *t_cpu; /* CPU thread runs on */
struct proc *t_proc; /* Process thread belongs to */
HANGMAN_ACTOR(t_hangman); /* Deadlock detector hook */
/*
* Interrupt state fields.

View File

@ -34,7 +34,7 @@
* Leave this alone, so we can tell what version of the OS/161 base
* 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.

View File

@ -35,6 +35,8 @@
#include <lib.h>
#include <uio.h>
#include <clock.h>
#include <mainbus.h>
#include <synch.h>
#include <thread.h>
#include <proc.h>
#include <vfs.h>
@ -247,6 +249,21 @@ cmd_sync(int nargs, char **args)
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.
*/
@ -261,6 +278,81 @@ cmd_panic(int nargs, char **args)
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.
*/
@ -463,7 +555,9 @@ static const char *opsmenu[] = {
"[cd] Change directory ",
"[pwd] Print current directory ",
"[sync] Sync filesystems ",
"[debug] Drop to debugger ",
"[panic] Intentional panic ",
"[deadlock] Intentional deadlock ",
"[q] Quit and shut down ",
NULL
};
@ -614,7 +708,9 @@ static struct {
{ "cd", cmd_chdir },
{ "pwd", cmd_pwd },
{ "sync", cmd_sync },
{ "debug", cmd_debug },
{ "panic", cmd_panic },
{ "deadlock", cmd_deadlock },
{ "q", cmd_quit },
{ "exit", cmd_quit },
{ "halt", cmd_quit },

View File

@ -156,6 +156,9 @@ arraytest2(int nargs, char **args)
(void)nargs;
(void)args;
/* Silence warning with gcc 4.8 -Og (but not -O2) */
x = 0;
kprintf("Beginning large array test...\n");
a = array_create();
KASSERT(a != NULL);

View File

@ -681,7 +681,7 @@ checkfilesystem(int nargs, char **args)
char *device;
if (nargs != 2) {
kprintf("Usage: fs[12345] filesystem:\n");
kprintf("Usage: fs[123456] filesystem:\n");
return EINVAL;
}

182
kern/thread/hangman.c Normal file
View 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);
}

View File

@ -52,6 +52,7 @@ spinlock_init(struct spinlock *splk)
{
spinlock_data_set(&splk->splk_lock, 0);
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);
}
mycpu->c_spinlocks++;
HANGMAN_WAIT(&curcpu->c_hangman, &splk->splk_hangman);
}
else {
mycpu = NULL;
@ -112,6 +115,10 @@ spinlock_acquire(struct spinlock *splk)
membar_store_any();
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(curcpu->c_spinlocks > 0);
curcpu->c_spinlocks--;
HANGMAN_RELEASE(&curcpu->c_hangman, &splk->splk_hangman);
}
splk->splk_holder = NULL;

View File

@ -154,6 +154,8 @@ lock_create(const char *name)
return NULL;
}
HANGMAN_LOCKABLEINIT(&lock->lk_hangman, lock->lk_name);
// add stuff here as needed
return lock;
@ -173,14 +175,23 @@ lock_destroy(struct lock *lock)
void
lock_acquire(struct lock *lock)
{
/* Call this (atomically) before waiting for a lock */
//HANGMAN_WAIT(&curthread->t_hangman, &lock->lk_hangman);
// Write this
(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
lock_release(struct lock *lock)
{
/* Call this (atomically) when the lock is released */
//HANGMAN_RELEASE(&curthread->t_hangman, &lock->lk_hangman);
// Write this
(void)lock; // suppress warning until code gets written

View File

@ -145,6 +145,7 @@ thread_create(const char *name)
thread->t_context = NULL;
thread->t_cpu = NULL;
thread->t_proc = NULL;
HANGMAN_ACTORINIT(&thread->t_hangman, thread->t_name);
/* Interrupt state fields */
thread->t_in_interrupt = false;
@ -244,6 +245,8 @@ cpu_create(unsigned hardware_number)
curcpu->c_curthread = curthread;
}
HANGMAN_ACTORINIT(&c->c_hangman, "cpu");
result = proc_addthread(kproc, c->c_curthread);
if (result) {
panic("cpu_create: proc_addthread:: %s\n", strerror(result));

View File

@ -129,6 +129,23 @@ vfs_biglock_acquire(void)
if (!lock_do_i_hold(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++;
}
@ -381,6 +398,9 @@ vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs)
unsigned index;
int result;
/* Silence warning with gcc 4.8 -Og (but not -O2) */
index = 0;
vfs_biglock_acquire();
name = kstrdup(dname);

View File

@ -135,6 +135,13 @@ getdevice(char *path, char **subpath, struct vnode **startvn)
KASSERT(vfs_biglock_do_i_hold());
/*
* Entirely empty filenames aren't legal.
*/
if (path[0] == 0) {
return EINVAL;
}
/*
* Locate the first colon or slash.
*/

View File

@ -1113,6 +1113,10 @@ subpage_kfree(void *ptr)
checksubpages();
/* Silence warnings with gcc 4.8 -Og (but not -O2) */
prpage = 0;
blktype = 0;
for (pr = allbase; pr; pr = pr->next_all) {
prpage = PR_PAGEADDR(pr);
blktype = PR_BLOCKTYPE(pr);

View File

@ -1,5 +1,5 @@
#
# MKDIRS logic
# OS/161 build environment: MKDIRS logic
#
# This generates rules for all intermediate directories as well as
# the directories listed. (That is, if you say MKDIRS=/usr/bin/foo

92
mk/os161.script.mk Normal file
View 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.

View File

@ -177,7 +177,7 @@ print(const char *path)
typech = '?';
}
printf("%crwx------ %2d root %-8llu",
printf("%crwx------ %2d root %-7llu ",
typech,
statbuf.st_nlink,
statbuf.st_size);

View File

@ -197,11 +197,9 @@ dumpdata(void)
indexsize = dolseek(indexfd, indexname, 0, SEEK_CUR);
pos = indexsize;
while (1) {
assert(pos % sizeof(x) == 0);
while (pos > 0) {
pos -= sizeof(x);
if (pos == 0) {
break;
}
assert(pos >= 0);
dolseek(indexfd, indexname, pos, SEEK_SET);

View File

@ -69,6 +69,11 @@ pipe_unaligned(void)
rv = pipe((int *)ptr);
report_survival(rv, errno, &result);
if (rv == 0) {
memmove(fds, ptr, 2*sizeof(int));
close(fds[0]);
close(fds[1]);
}
return result;
}

View File

@ -46,7 +46,7 @@
int
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];
const char *file;

View File

@ -152,12 +152,20 @@ void
test(int nowait)
{
int pid0, pid1, pid2, pid3;
int depth = 0;
/*
* Caution: This generates processes geometrically.
*
* It is unrolled to encourage gcc to registerize the pids,
* 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();
nprintf(".");
write(fd, "A", 1);
depth++;
if (depth != 1) {
warnx("depth %d, should be 1", depth);
}
check();
pid1 = dofork();
nprintf(".");
write(fd, "B", 1);
depth++;
if (depth != 2) {
warnx("depth %d, should be 2", depth);
}
check();
pid2 = dofork();
nprintf(".");
write(fd, "C", 1);
depth++;
if (depth != 3) {
warnx("depth %d, should be 3", depth);
}
check();
pid3 = dofork();
nprintf(".");
write(fd, "D", 1);
depth++;
if (depth != 4) {
warnx("depth %d, should be 4", depth);
}
check();
/*

View File

@ -31,7 +31,7 @@
* hog.c
* 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

View File

@ -134,6 +134,7 @@ semP(struct usem *sem, size_t num)
if (read(sem->fd, c, num) < 0) {
err(1, "%s: read", sem->name);
}
(void)c;
}
static

View File

@ -273,16 +273,24 @@ static
void
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);
}
(void)c;
}
static
void
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);
}
}
@ -326,7 +334,12 @@ makeprocs(bool dowait)
for (i=0; i<NJOBS; i++) {
pids[i] = fork();
if (pids[i]<0) {
warn("fork");
warn("fork (process %d)", i);
if (dowait) {
semopen(&s1);
semV(&s1, 1);
semclose(&s1);
}
}
if (pids[i]==0) {
/* child */

View File

@ -35,6 +35,7 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
@ -84,9 +85,9 @@ void
Pn(struct usem *sem, unsigned count)
{
ssize_t r;
char c;
char c[count];
r = read(sem->fd, &c, count);
r = read(sem->fd, c, count);
if (r < 0) {
err(1, "%s: read", sem->name);
}
@ -105,9 +106,12 @@ void
Vn(struct usem *sem, unsigned count)
{
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) {
err(1, "%s: write", sem->name);
}