Initial fix of upstream merge.
This commit is contained in:
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
/*
|
||||
|
@@ -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.
|
||||
|
@@ -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];
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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.
|
||||
|
@@ -7,6 +7,7 @@
|
||||
include conf/conf.kern # get definitions of available options
|
||||
|
||||
#debug # Optimizing compile (no debug).
|
||||
#debugonly
|
||||
options noasserts # Disable assertions.
|
||||
|
||||
#
|
||||
|
@@ -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.
|
||||
|
@@ -7,6 +7,7 @@
|
||||
include conf/conf.kern # get definitions of available options
|
||||
|
||||
#debug # Optimizing compile (no debug).
|
||||
#debugonly
|
||||
options noasserts # Disable assertions.
|
||||
|
||||
#
|
||||
|
@@ -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
|
||||
#
|
||||
|
@@ -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";
|
||||
}
|
||||
|
||||
|
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];
|
||||
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
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 ROUNDUP(a,b) (DIVROUNDUP(a,b)*b)
|
||||
#define ROUNDUP(a,b) (DIVROUNDUP(a,b)*(b))
|
||||
|
||||
|
||||
#endif /* _LIB_H_ */
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
};
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
@@ -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 },
|
||||
|
@@ -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);
|
||||
|
@@ -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
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);
|
||||
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;
|
||||
|
@@ -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
|
||||
|
@@ -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));
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user