* Fixed bug relating to nested locking in interrupt handlers. The nested lock

caused interrupts to be reenabled (due to unlock), which caused a race. The
problems were especially visible on slower machines.
* Relocated free memory parsing to process manager. This saved quite some
code at the kernel level. Text size was reduced by about 650 bytes.
* Removed locks for updating the realtime in the clock's main loop and the
get_uptime function. Interrupts are no longer reentrant, so realtime is
immediately updated.
This commit is contained in:
Jorrit Herder 2005-06-17 09:09:54 +00:00
parent 90b80ad31e
commit e0a98a4d65
13 changed files with 76 additions and 206 deletions

View file

@ -1,3 +1,4 @@
#define NEW_TIME_COUNT 1
/* The file contais the clock task, which handles all time related functions.
* Important events that are handled by the CLOCK include alarm timers and
* (re)scheduling user processes.
@ -84,7 +85,9 @@ PRIVATE clock_t realtime; /* real time clock */
/* Variables for and changed by the CLOCK's interrupt handler. */
PRIVATE irq_hook_t clock_hook;
#if ! NEW_TIME_COUNT
PRIVATE clock_t pending_ticks; /* ticks seen by low level only */
#endif
PRIVATE int sched_ticks = SCHED_RATE; /* counter: when 0, call scheduler */
PRIVATE struct proc *prev_ptr; /* last user process run by clock */
@ -107,11 +110,13 @@ PUBLIC void clock_task()
/* Go get a message. */
receive(ANY, &m);
#if ! NEW_TIME_COUNT
/* Transfer ticks seen by the low level handler. */
lock(8, "realtime");
realtime += pending_ticks;
pending_ticks = 0;
unlock(8);
#endif
/* Handle the request. */
switch (m.m_type) {
@ -202,10 +207,12 @@ irq_hook_t *hook;
* The variables which are changed require more care:
* rp->p_user_time, rp->p_sys_time:
* These are protected by explicit locks in system.c.
#if ! NEW_TIME_COUNT
* pending_ticks:
* This is protected by explicit locks in clock.c. Don't
* update realtime directly, since there are too many
* references to it to guard conveniently.
#endif
* lost_ticks:
* Clock ticks counted outside the clock task.
* sched_ticks, prev_ptr:
@ -222,7 +229,9 @@ irq_hook_t *hook;
register struct proc *rp;
register unsigned ticks;
message m;
#if ! NEW_TIME_COUNT
clock_t now;
#endif
/* Acknowledge the PS/2 clock interrupt. */
if (machine.ps_mca) outb(PORT_B, inb(PORT_B) | CLOCK_ACK_BIT);
@ -232,10 +241,16 @@ irq_hook_t *hook;
* process is running, charge the billable process for system time as well.
* Thus the unbillable process' user time is the billable user's system time.
*/
#if NEW_TIME_COUNT
ticks = lost_ticks + 1;
lost_ticks = 0;
realtime += ticks;
#else
ticks = lost_ticks + 1;
lost_ticks = 0;
pending_ticks += ticks;
now = realtime + pending_ticks;
#endif
/* Update administration. */
proc_ptr->p_user_time += ticks;
@ -244,11 +259,15 @@ irq_hook_t *hook;
/* Check if do_clocktick() must be called. Done for alarms and scheduling.
* If bill_ptr == prev_ptr, there are no ready users so don't need sched().
*/
#if NEW_TIME_COUNT
if (next_timeout <= realtime || (sched_ticks == 1 && bill_ptr == prev_ptr
#else
if (next_timeout <= now || (sched_ticks == 1 && bill_ptr == prev_ptr
#endif
&& rdy_head[PPRI_USER] != NIL_PROC))
{
m.NOTIFY_TYPE = HARD_INT;
lock_notify(CLOCK, &m);
int_notify(CLOCK, &m);
}
else if (--sched_ticks <= 0) {
sched_ticks = SCHED_RATE; /* reset the quantum */
@ -266,12 +285,16 @@ PUBLIC clock_t get_uptime()
/* Get and return the current clock uptime in ticks.
* Be careful about pending_ticks.
*/
#if NEW_TIME_COUNT
return(realtime);
#else
clock_t uptime;
lock(9, "get_uptime");
uptime = realtime + pending_ticks;
unlock(9);
return(uptime);
#endif
}

View file

@ -49,7 +49,7 @@ unsigned vec_nr;
ep = &ex_data[vec_nr];
if (vec_nr == 2) { /* spurious NMI on some machines */
kprintf("got spurious NMI\n",NO_ARG);
kprintf("got spurious NMI\n",NO_NUM);
return;
}
@ -68,19 +68,7 @@ unsigned vec_nr;
kprintf("pc = %d:", (unsigned) saved_proc->p_reg.cs);
kprintf("0x%x\n", (unsigned) saved_proc->p_reg.pc);
/* If the exception originates in the kernel, shut down MINIX. Otherwise,
* kill the process that caused it. If MINIX is shut down and the stop
* sequence is skipped, the kprintf() output cannot be flushed by the TTY
* driver. This leaves the user with a hanging system without proper
* notification ...
*/
if (istaskp(saved_proc)) { /* serious problem */
kernel_exception = TRUE; /* directly shutdown */
panic("exception in a kernel task", NO_NUM);
} else {
clear_proc(saved_proc->p_nr);
kprintf("%s was killed by MINIX due to an exception",
karg(saved_proc->p_name));
}
kernel_exception = TRUE; /* directly shutdown */
panic("exception in a kernel task", NO_NUM);
}

View file

@ -22,7 +22,6 @@ EXTERN struct kinfo kinfo; /* kernel information for users */
EXTERN struct machine machine; /* machine information for users */
EXTERN struct kmessages kmess; /* diagnostic messages in kernel */
EXTERN struct randomness krandom; /* gather kernel random information */
EXTERN struct memory mem[NR_MEMS]; /* base and size of chunks of memory */
/* Process scheduling information and the kernel reentry count. */
EXTERN struct proc *proc_ptr; /* pointer to currently running process */

View file

@ -45,7 +45,7 @@ int mine;
/* Initialize the 8259s, finishing with all interrupts disabled. This is
* only done in protected mode, in real mode we don't touch the 8259s, but
* use the BIOS locations instead. The flag "mine" is set if the 8259s are
* to be programmed for Minix, or to be reset to what the BIOS expects.
* to be programmed for MINIX, or to be reset to what the BIOS expects.
*/
int i;

View file

@ -11,7 +11,6 @@
* kstrcmp: lexicographical comparison of two strings
* kstrlen: get number of non-null characters in string
* kstrncpy: copy string and pad or copy up to n chars
* kstrtoulb: convert string to unsigned long value
*
* This file contains the routines that take care of kernel messages, i.e.,
* diagnostic output within the kernel. Kernel messages are not directly
@ -152,7 +151,7 @@ PRIVATE void kputc(c)
int c; /* character to append */
{
/* Accumulate a single character for a kernel message. Send a notification
* the to TTY driver if the buffer if a END_OF_KMESS is encountered.
* the to TTY driver if an END_OF_KMESS is encountered.
*/
message m;
if (c != END_OF_KMESS) {
@ -225,45 +224,3 @@ PUBLIC char *kstrncpy(char *ret, register const char *s2, register size_t n)
}
/*=========================================================================*
* kstrtoul *
*=========================================================================*/
PUBLIC unsigned long kstrtoul(strptr, endptr, base)
const char *strptr; /* pointer to string to be parsed */
char ** const endptr; /* store pointer to end here */
int base;
{
/* A simplified version of strtoul() for the kernel to prevent including the
* one in the ASNI library. No whitespaces are skipped, the numeric value is
* expected at the start of 'string'.
*/
register unsigned long val = 0;
register int c;
register unsigned int v;
int overflow = 0;
/* Get rid of 0x or 0X for hexidecimal values. */
if (base==16 && *strptr=='0' && (*++strptr=='x' || *strptr=='X'))
strptr++;
/* Now parse the actual unsigned long number. */
for (;;) {
c = *strptr;
if ('0' <= c && c <= '9') v = c - '0';
else if ('a' <= c && c <= 'z') v = c - 'a' + 0xa;
else if ('A' <= c && c <= 'Z') v = c - 'A' + 0xA;
else break; /* end of number */
if (v >= base) break; /* end of number */
if (val > (ULONG_MAX - v) / base) overflow = 1;
val = (val*base) + v;
strptr++;
}
/* Tell caller where parsing ended unless a NULL pointer was passed. */
if (endptr) *endptr = (char *) strptr;
/* Done, return parsed value or maximum value on overflow. */
return (overflow) ? ULONG_MAX : val;
}

View file

@ -114,19 +114,6 @@ PUBLIC void main()
rp->p_memmap[S].mem_phys = text_base + text_clicks + data_clicks;
rp->p_memmap[S].mem_vir = data_clicks; /* empty - stack is in data */
/* Remove server memory from the free memory list. The boot monitor
* promises to put processes at the start of memory chunks. The
* tasks all use same base address, so only the first task changes
* the memory lists. The servers and init have their own memory
* spaces and their memory will be removed from the list.
*/
for (memp = mem; memp < &mem[NR_MEMS]; memp++) {
if (memp->base == text_base) {
memp->base += text_clicks + data_clicks;
memp->size -= text_clicks + data_clicks;
}
}
/* Set initial register values. The processor status word for tasks
* is different from that of other processes because tasks can
* access I/O; this is not allowed to less-privileged processes
@ -144,7 +131,6 @@ PUBLIC void main()
}
/* Set ready. The HARDWARE task is never ready. */
#if ENABLE_K_DEBUGGING
rp->p_ready = 0;
#endif
@ -163,30 +149,16 @@ PUBLIC void main()
hdrindex ++;
phys_copy(aout + hdrindex * A_MINHDR,vir2phys(&e_hdr),(phys_bytes) A_MINHDR);
if (e_hdr.a_flags & A_IMG) {
kinfo.bootdev_base = e_hdr.a_syms;
kinfo.bootdev_size = e_hdr.a_data;
/* Remove from free list, to prevent being overwritten. */
bootdev_base = e_hdr.a_syms >> CLICK_SHIFT;
bootdev_clicks = (e_hdr.a_total + CLICK_SIZE-1) >> CLICK_SHIFT;
for (memp = mem; memp < &mem[NR_MEMS]; memp++) {
if (memp->base == bootdev_base) {
memp->base += bootdev_clicks;
memp->size -= bootdev_clicks;
}
}
}
#endif
/* This actually is not needed, because ready() already set 'proc_ptr.' */
lock_pick_proc();
bill_ptr = proc_addr(IDLE); /* it has to point somewhere */
/* MINIX is now ready. Display the startup banner to the user and return
* to the assembly code to start running the current process.
/* MINIX is now ready. All boot image processes are on the ready queue.
* Return to the assembly code to start running the current process.
*/
announce();
bill_ptr = proc_addr(IDLE); /* it has to point somewhere */
announce(); /* print MINIX startup banner */
restart();
}
@ -199,7 +171,7 @@ PRIVATE void announce(void)
{
/* Display the MINIX startup banner. */
kprintf("MINIX %s. Copyright 2001 Prentice-Hall, Inc.\n",
karg(kinfo.version));
karg(OS_RELEASE "." OS_VERSION));
#if (CHIP == INTEL)
/* Real mode, or 16/32-bit protected mode? */
@ -249,10 +221,10 @@ int how; /* 0 = halt, 1 = reboot, 2 = panic!, ... */
shutting_down = TRUE; /* flag for sys_exit() */
tmr_arg(&shutdown_timer)->ta_int = how; /* pass how in timer */
if (kernel_exception) { /* set in exception() */
kprintf("\nAn exception occured; skipping stop sequence.\n", NO_ARG);
kprintf("\nAn exception occured; skipping stop sequence.\n", NO_NUM);
shutdown(&shutdown_timer); /* TTY isn't scheduled */
} else {
kprintf("\nNotifying system services about MINIX shutdown.\n", NO_ARG);
kprintf("\nNotifying system services about MINIX shutdown.\n", NO_NUM);
set_timer(&shutdown_timer, get_uptime(), stop_sequence);
}
}

View file

@ -25,7 +25,7 @@ int n;
if (s != NULL) {
kprintf("\nKernel panic: %s", karg(s));
if (n != NO_NUM) kprintf(" %d", n);
kprintf("\n",NO_ARG);
kprintf("\n",NO_NUM);
}
prepare_shutdown(RBT_PANIC);
}

View file

@ -1,5 +1,5 @@
#define NEW_ELOCKED_CHECK 1
#define NEW_SCHED_Q 1
#define NEW_SCHED_Q 0
#define OLD_SEND 0
#define OLD_RECV 0
/* This file contains essentially all of the process and message handling.
@ -11,11 +11,11 @@
* As well as several entry points used from the interrupt and task level:
*
* lock_notify: send a notification to inform a process of a system event
* int_notify: same as above, but from an interrupt handler (no locking)
* lock_send: send a message to a process
* lock_ready: put a process on one of the ready queues so it can be run
* lock_unready: remove a process from the ready queues
* lock_sched: a process has run too long; schedule another one
* lock_pick_proc: pick a process to run (used by system initialization)
*
* Changes:
* , 2005 better protection in sys_call() (Jorrit N. Herder)
@ -438,13 +438,12 @@ PUBLIC int lock_notify(dst, m_ptr)
int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
{
/* Safe gateway to mini_notify() for tasks and interrupt handlers. This
* function checks if it is called from an interrupt handler and ensures
* that the correct message source is put on the notification.
/* Safe gateway to mini_notify() for tasks. Don't use this function from the
* interrupt level, as it will reenable interrupts (because of the unlock()
* call). For interrupt handlers, int_notify() is available.
*/
int result;
struct proc *caller_ptr;
register struct proc *caller_ptr;
lock(0, "notify");
caller_ptr = (k_reenter >= 0) ? proc_addr(HARDWARE) : proc_ptr;
result = mini_notify(caller_ptr, dst, m_ptr);
@ -452,6 +451,24 @@ message *m_ptr; /* pointer to message buffer */
return(result);
}
/*==========================================================================*
* int_notify *
*==========================================================================*/
PUBLIC int int_notify(dst, m_ptr)
int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */
{
/* Gateway to mini_notify() for interrupt handlers. This function doesn't
* use lock() and unlock() because interrupts are already disabled.
*/
int result;
register struct proc *caller_ptr = proc_addr(HARDWARE);
result = mini_notify(caller_ptr, dst, m_ptr);
return(result);
}
/*===========================================================================*
* pick_proc *
*===========================================================================*/
@ -627,25 +644,14 @@ int queue;
xp->p_nextready = NIL_PROC; /* mark new end of queue */
#else
rdy_tail[queue]->p_nextready = rdy_head[queue];
rdy_tail[queue] = rdy_head[queue];
rdy_head[queue] = rdy_head[queue]->p_nextready;
rdy_tail[queue]->p_nextready = NIL_PROC;
rdy_tail[queue]->p_nextready = rdy_head[queue]; /* add expired to end */
rdy_tail[queue] = rdy_head[queue]; /* set new tail */
rdy_head[queue] = rdy_head[queue]->p_nextready; /* set new head */
rdy_tail[queue]->p_nextready = NIL_PROC; /* mark new end */
#endif
pick_proc();
}
/*==========================================================================*
* lock_pick_proc *
*==========================================================================*/
PUBLIC void lock_pick_proc()
{
/* Safe gateway to pick_proc() for tasks. */
lock(1, "pick_proc");
pick_proc();
unlock(1);
}
/*==========================================================================*
* lock_send *

View file

@ -25,9 +25,6 @@ _PROTOTYPE( int kstrncmp,
(register const char *s1, register const char *s2, register size_t n));
_PROTOTYPE( char *kstrncpy,
(char *s1, register const char *s2, register const size_t n));
_PROTOTYPE( unsigned long kstrtoul,
(const char *string, char ** const end, int base) );
#define NO_ARG 0
#define karg(arg) (karg_t) (arg)
_PROTOTYPE( void kprintf, (const char *fmt, karg_t arg) );
@ -44,8 +41,8 @@ _PROTOTYPE( void free_bit, (bit_t nr, bitchunk_t *map, bit_t nr_bits) );
/* proc.c */
_PROTOTYPE( int sys_call, (int function, int src_dest, message *m_ptr) );
_PROTOTYPE( int lock_notify, (int dst, message *m_ptr) );
_PROTOTYPE( int int_notify, (int dst, message *m_ptr) );
_PROTOTYPE( int lock_send, (int dst, message *m_ptr) );
_PROTOTYPE( void lock_pick_proc, (void) );
_PROTOTYPE( void lock_ready, (struct proc *rp) );
_PROTOTYPE( void lock_sched, (int queue) );
_PROTOTYPE( void lock_unready, (struct proc *rp) );

View file

@ -12,7 +12,6 @@
#include "protect.h"
#include "proc.h"
FORWARD _PROTOTYPE( void mem_init, (_CONST char *params));
FORWARD _PROTOTYPE( char *get_value, (_CONST char *params, _CONST char *key));
/*==========================================================================*
@ -57,7 +56,8 @@ U16_t parmoff, parmsize; /* boot parameters offset and length */
/* Record miscellaneous information for user-space servers. */
kinfo.nr_procs = NR_PROCS;
kinfo.nr_tasks = NR_TASKS;
kstrncpy(kinfo.version, OS_RELEASE "." OS_VERSION, 6);
kstrncpy(kinfo.release, OS_RELEASE, 4);
kstrncpy(kinfo.version, OS_VERSION, 4);
kinfo.proc_addr = (vir_bytes) proc;
kinfo.kmem_base = vir2phys(0);
kinfo.kmem_size = (phys_bytes) &end;
@ -84,79 +84,12 @@ U16_t parmoff, parmsize; /* boot parameters offset and length */
if (kstrcmp(value, "ega") == 0) machine.vdu_ega = TRUE;
if (kstrcmp(value, "vga") == 0) machine.vdu_vga = machine.vdu_ega = TRUE;
/* Initialize free memory list from size passed by boot monitor. */
mem_init(params);
/* Return to assembler code to switch to protected mode (if 286),
* reload selectors and call main().
*/
}
/* In real mode only 1M can be addressed, and in 16-bit protected we can go
* no further than we can count in clicks. (The 286 is further limited by
* its 24 bit address bus, but we can assume in that case that no more than
* 16M memory is reported by the BIOS.)
*/
#define MAX_REAL 0x00100000L
#define MAX_16BIT (0xFFF0L << CLICK_SHIFT)
/*=========================================================================*
* mem_init *
*=========================================================================*/
PRIVATE void mem_init(params)
_CONST char *params; /* boot monitor parameters */
{
/* Initialize the free memory list from the 'memory' boot variable. Translate
* the byte offsets and sizes in this list to clicks, properly truncated. Also
* make sure that we don't exceed the maximum address space of the 286 or the
* 8086, i.e. when running in 16-bit protected mode or real mode.
*/
long base, size, limit;
char *s, *end; /* use to parse boot variable */
int i;
struct memory *memp;
#if _WORD_SIZE == 2
unsigned long max_address;
#endif
/* The available memory is determined by MINIX' boot loader as a list of
* (base:size)-pairs in boothead.s. The 'memory' boot variable is set in
* in boot.s. The format is "b0:s0,b1:s1,b2:s2", where b0:s0 is low mem,
* b1:s1 is mem between 1M and 16M, b2:s2 is mem above 16M. Pairs b1:s1
* and b2:s2 are combined if the memory is adjacent.
*/
s = get_value(params, "memory"); /* get memory boot variable */
for (i = 0; i < NR_MEMS; i++) {
memp = &mem[i]; /* next mem chunk is stored here */
base = size = 0; /* initialize next base:size pair */
if (*s != 0) { /* get fresh data, unless at end */
/* Read fresh base and expect colon as next char. */
base = kstrtoul(s, &end, 0x10); /* get number */
if (end != s && *end == ':') s = ++end; /* skip ':' */
else *s=0; /* terminate, should not happen */
/* Read fresh size and expect comma or assume end. */
size = kstrtoul(s, &end, 0x10); /* get number */
if (end != s && *end == ',') s = ++end; /* skip ',' */
else *s=0; /* found end */
}
limit = base + size; /* limit is used for validity check */
#if _WORD_SIZE == 2
max_address = kinfo.protected ? MAX_16BIT : MAX_REAL;
if (limit > max_address) limit = max_address;
#endif
base = (base + CLICK_SIZE-1) & ~(long)(CLICK_SIZE-1);
limit &= ~(long)(CLICK_SIZE-1);
if (limit <= base) continue;
memp->base = base >> CLICK_SHIFT;
memp->size = (limit - base) >> CLICK_SHIFT;
}
}
/*==========================================================================*
* get_value *
*==========================================================================*/

View file

@ -296,7 +296,7 @@ irq_hook_t *hook;
/* Build notification message and return. */
m.NOTIFY_TYPE = HARD_INT;
m.NOTIFY_ARG = hook->irq;
lock_notify(hook->proc_nr, &m);
int_notify(hook->proc_nr, &m);
return(hook->policy & IRQ_REENABLE);
}
@ -308,11 +308,11 @@ PUBLIC void cause_sig(proc_nr, sig_nr)
int proc_nr; /* process to be signalled */
int sig_nr; /* signal to be sent, 1 to _NSIG */
{
/* A task wants to send a signal to a process. Examples of such tasks are:
/* A system process wants to send a signal to a process. Examples are:
* TTY wanting to cause SIGINT upon getting a DEL
* CLOCK wanting to cause SIGALRM when timer expires
* FS also uses this to send a signal, via the SYS_KILL message. Signals are
* handled by sending a message to PM. This central function handles the
* FS wanting to cause SIGPIPE for a broken pipe
* Signals are handled by sending a message to PM. This function handles the
* signals and makes sure the PM gets them by sending a notification. The
* process being signaled is blocked while PM has not finished all signals
* for it. These signals are counted in p_pendcount, and the SIG_PENDING
@ -487,7 +487,7 @@ vir_bytes bytes; /* # of bytes to copy */
/* Check copy count. */
if (bytes <= 0) {
kprintf("v_cp: copy count problem <= 0\n", NO_ARG);
kprintf("v_cp: copy count problem <= 0\n", NO_NUM);
return(EDOM);
}
@ -523,7 +523,7 @@ vir_bytes bytes; /* # of bytes to copy */
/* Check if mapping succeeded. */
if (phys_addr[i] <= 0 && vir_addr[i]->segment != PHYS_SEG) {
kprintf("v_cp: Mapping failed ... phys <= 0\n", NO_ARG);
kprintf("v_cp: Mapping failed ... phys <= 0\n", NO_NUM);
return(EFAULT);
}
}

View file

@ -45,7 +45,7 @@ register message *m_ptr; /* pointer to request message */
/* Check if process number was given implictly with SELF and is valid. */
if (vir_addr[i].proc_nr == SELF) vir_addr[i].proc_nr = m_ptr->m_source;
if (! isokprocn(vir_addr[i].proc_nr) && vir_addr[i].segment != PHYS_SEG) {
kprintf("do_vircopy: illegal proc nr, while not phys addr\n",NO_ARG);
kprintf("do_vircopy: illegal proc nr, while not phys addr\n",NO_NUM);
return(EINVAL);
}
@ -58,7 +58,7 @@ register message *m_ptr; /* pointer to request message */
* vir_bytes. Especially copying by the PM on do_fork() is affected.
*/
if (bytes != (vir_bytes) bytes) {
kprintf("do_vircopy: overflow\n", NO_ARG);
kprintf("do_vircopy: overflow\n", NO_NUM);
return(E2BIG);
}

View file

@ -105,11 +105,6 @@ register message *m_ptr; /* pointer to request message */
src_phys = vir2phys(irq_hooks);
break;
}
case GET_MEMCHUNKS: {
length = sizeof(struct memory) * NR_MEMS;
src_phys = vir2phys(mem);
break;
}
case GET_SCHEDINFO: {
/* This is slightly complicated because we need two data structures
* at once, otherwise the scheduling information may be incorrect.