Fixed clear_proc(): properly release resources (IRQ hooks, notify buffers);

Cleaned up comments in proc.c (old code still present for comparison);
This commit is contained in:
Jorrit Herder 2005-05-30 11:05:42 +00:00
parent fcd113da7b
commit 826fee4c6d
3 changed files with 96 additions and 63 deletions

View file

@ -29,6 +29,11 @@ EXTERN struct proc *next_ptr; /* pointer to next process to run */
EXTERN char k_reenter; /* kernel reentry count (entry count less 1) */ EXTERN char k_reenter; /* kernel reentry count (entry count less 1) */
EXTERN unsigned lost_ticks; /* clock ticks counted outside clock task */ EXTERN unsigned lost_ticks; /* clock ticks counted outside clock task */
/* Declare buffer space and a bit map for notification messages. */
EXTERN struct notification notify_buffer[NR_NOTIFY_BUFS];
EXTERN bitchunk_t notify_bitmap[BITMAP_CHUNKS(NR_NOTIFY_BUFS)];
#if (CHIP == INTEL) #if (CHIP == INTEL)
/* Interrupt related variables. */ /* Interrupt related variables. */

View file

@ -2,11 +2,12 @@
#define OLD_SEND 0 #define OLD_SEND 0
#define OLD_RECV 0 #define OLD_RECV 0
/* This file contains essentially all of the process and message handling. /* This file contains essentially all of the process and message handling.
* It has one main entry point from the outside: * Together with "mpx.s" it forms the lowest layer of the MINIX kernel.
* There is one entry point from the outside:
* *
* sys_call: a system call, i.e., the kernel is trapped with an INT * sys_call: a system call, i.e., the kernel is trapped with an INT
* *
* It also has several minor entry points to be used from the task level: * 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 * lock_notify: send a notification to inform a process of a system event
* lock_send: send a message to a process * lock_send: send a message to a process
@ -16,13 +17,29 @@
* lock_pick_proc: pick a process to run (used by system initialization) * lock_pick_proc: pick a process to run (used by system initialization)
* *
* Changes: * Changes:
* , 2005 better protection in sys_call() (Jorrit N. Herder)
* May 26, 2005 optimized message passing functions (Jorrit N. Herder) * May 26, 2005 optimized message passing functions (Jorrit N. Herder)
* May 24, 2005 new, queued NOTIFY system call (Jorrit N. Herder) * May 24, 2005 new, queued NOTIFY system call (Jorrit N. Herder)
* Oct 28, 2004 non-blocking SEND and RECEIVE (Jorrit N. Herder) * Oct 28, 2004 new, non-blocking SEND and RECEIVE (Jorrit N. Herder)
* Oct 28, 2004 rewrite of sys_call() (Jorrit N. Herder) * Oct 28, 2004 rewrite of sys_call() function (Jorrit N. Herder)
* Oct 10, 2004 require SENDREC for kernel sys_call() (Jorrit N. Herder) * Aug 19, 2004 generalized multilevel scheduling (Jorrit N. Herder)
* (to protect kernel tasks from being blocked) *
* Aug 19, 2004 generalized ready()/unready() (Jorrit N. Herder) * The code here is critical to make everything work and is important for the
* overall performance of the system. A large fraction of the code deals with
* list manipulation. To make this both easy to understand and fast to execute
* pointer pointers are used throughout the code. Pointer pointers prevent
* exceptions for the head or tail of a linked list.
*
* node_t *queue, *new_node; // assume these as global variables
* node_t **xpp = &queue; // get pointer pointer to head of queue
* while (*xpp != NULL) // find last pointer of the linked list
* xpp = &(*xpp)->next; // get pointer to next pointer
* *xpp = new_node; // now replace the end (the NULL pointer)
* new_node->next = NULL; // and mark the new end of the list
*
* For example, when adding a new node to the end of the list, one normally
* makes an exception for an empty list and looks up the end of the list for
* nonempty lists. As shown above, this is not required with pointer pointers.
*/ */
#include "kernel.h" #include "kernel.h"
@ -45,8 +62,8 @@ FORWARD _PROTOTYPE( int mini_notify, (struct proc *caller_ptr, int dst,
message *m_ptr ) ); message *m_ptr ) );
FORWARD _PROTOTYPE( void ready, (struct proc *rp) ); FORWARD _PROTOTYPE( void ready, (struct proc *rp) );
FORWARD _PROTOTYPE( void sched, (int queue) );
FORWARD _PROTOTYPE( void unready, (struct proc *rp) ); FORWARD _PROTOTYPE( void unready, (struct proc *rp) );
FORWARD _PROTOTYPE( void sched, (int queue) );
FORWARD _PROTOTYPE( void pick_proc, (void) ); FORWARD _PROTOTYPE( void pick_proc, (void) );
#define BuildMess(m,n) \ #define BuildMess(m,n) \
@ -67,16 +84,18 @@ FORWARD _PROTOTYPE( void pick_proc, (void) );
#endif /* (CHIP == M68000) */ #endif /* (CHIP == M68000) */
#if DEAD_CODE /* now in glo.h */
/* Declare buffer space and a bit map for notification messages. */ /* Declare buffer space and a bit map for notification messages. */
PRIVATE struct notification notify_buffer[NR_NOTIFY_BUFS]; PRIVATE struct notification notify_buffer[NR_NOTIFY_BUFS];
PRIVATE bitchunk_t notify_bitmap[BITMAP_CHUNKS(NR_NOTIFY_BUFS)]; PRIVATE bitchunk_t notify_bitmap[BITMAP_CHUNKS(NR_NOTIFY_BUFS)];
#endif
/*===========================================================================* /*===========================================================================*
* sys_call * * sys_call *
*===========================================================================*/ *===========================================================================*/
PUBLIC int sys_call(call_nr, src_dst, m_ptr) PUBLIC int sys_call(call_nr, src_dst, m_ptr)
int call_nr; /* (NB_)SEND, (NB_)RECEIVE, SENDREC */ int call_nr; /* system call number and flags */
int src_dst; /* src to receive from or dst to send to */ int src_dst; /* src to receive from or dst to send to */
message *m_ptr; /* pointer to message in the caller's space */ message *m_ptr; /* pointer to message in the caller's space */
{ {
@ -93,15 +112,12 @@ message *m_ptr; /* pointer to message in the caller's space */
vir_clicks vlo, vhi; /* virtual clicks containing message to send */ vir_clicks vlo, vhi; /* virtual clicks containing message to send */
/* Calls directed to the kernel may only be sendrec(), because tasks always /* Calls directed to the kernel may only be sendrec(), because tasks always
* reply and may not block if the caller doesn't do receive(). Users also * reply and may not block if the caller doesn't do receive().
* may only use sendrec() to protect the process manager and file system.
*/ */
if (iskernel(src_dst) && function != SENDREC) if (iskernel(src_dst) && function != SENDREC) return(ECALLDENIED);
return(ECALLDENIED); /* SENDREC was required */
/* Verify that requested source and/ or destination is a valid process. */ /* Verify that requested source and/ or destination is a valid process. */
if (! isoksrc_dst(src_dst) && function != ECHO) if (! isoksrc_dst(src_dst) && function != ECHO) return(EBADSRCDST);
return(EBADSRCDST);
/* Check validity of message pointer. */ /* Check validity of message pointer. */
vb = (vir_bytes) m_ptr; vb = (vir_bytes) m_ptr;
@ -124,8 +140,12 @@ message *m_ptr; /* pointer to message in the caller's space */
/* Now check if the call is known and try to perform the request. The only /* Now check if the call is known and try to perform the request. The only
* system calls that exist in MINIX are sending and receiving messages. * system calls that exist in MINIX are sending and receiving messages.
* Receiving is straightforward. Sending requires to check caller's send * - SENDREC: combines SEND and RECEIVE in a single system call
* mask and whether the destination is alive. * - SEND: sender blocks until its message has been delivered
* - RECEIVE: receiver blocks until an acceptable message has arrived
* - NOTIFY: sender continues; either directly deliver the message or
* queue the notification message until it can be delivered
* - ECHO: the message directly will be echoed to the sender
*/ */
switch(function) { switch(function) {
case SENDREC: case SENDREC:
@ -207,15 +227,14 @@ unsigned flags; /* system call flags */
} }
/* Check if 'dst' is blocked waiting for this message. The destination's /* Check if 'dst' is blocked waiting for this message. The destination's
* SENDING flag may be set when SENDREC couldn't send. Await a pure RECEIVE. * SENDING flag may be set when its SENDREC call blocked while sending.
*/ */
if ( (dst_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING && if ( (dst_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING &&
(dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) { (dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) {
/* Destination is indeed waiting for this message. */ /* Destination is indeed waiting for this message. */
CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr, CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr,
dst_ptr->p_messbuf); dst_ptr->p_messbuf);
if ((dst_ptr->p_flags &= ~RECEIVING) == 0) if ((dst_ptr->p_flags &= ~RECEIVING) == 0) ready(dst_ptr);
ready(dst_ptr);
} else if ( ! (flags & NON_BLOCKING)) { } else if ( ! (flags & NON_BLOCKING)) {
/* Destination is not waiting. Block and queue caller. */ /* Destination is not waiting. Block and queue caller. */
caller_ptr->p_messbuf = m_ptr; caller_ptr->p_messbuf = m_ptr;
@ -233,13 +252,12 @@ unsigned flags; /* system call flags */
next_ptr->p_q_link = caller_ptr; next_ptr->p_q_link = caller_ptr;
} }
#else #else
xpp = &dst_ptr->p_caller_q; xpp = &dst_ptr->p_caller_q; /* find end of list */
while (*xpp != NIL_PROC) xpp = &(*xpp)->p_q_link; while (*xpp != NIL_PROC) xpp = &(*xpp)->p_q_link;
*xpp = caller_ptr; *xpp = caller_ptr; /* add caller to end */
#endif #endif
caller_ptr->p_q_link = NIL_PROC; caller_ptr->p_q_link = NIL_PROC; /* mark new end of list */
} else { } else {
kprintf("Didn't block... ;-), flags: 0x%x\n", flags);
return(ENOTREADY); return(ENOTREADY);
} }
return(OK); return(OK);
@ -254,9 +272,9 @@ int src; /* which message source is wanted */
message *m_ptr; /* pointer to message buffer */ message *m_ptr; /* pointer to message buffer */
unsigned flags; /* system call flags */ unsigned flags; /* system call flags */
{ {
/* A process or task wants to get a message. If one is already queued, /* A process or task wants to get a message. If a message is already queued,
* acquire it and deblock the sender. If no message from the desired source * acquire it and deblock the sender. If no message from the desired source
* is available, block the caller. * is available block the caller, unless the flags don't allow blocking.
*/ */
#if OLD_RECV #if OLD_RECV
register struct proc *sender_ptr; register struct proc *sender_ptr;
@ -295,25 +313,22 @@ unsigned flags; /* system call flags */
xpp = &caller_ptr->p_caller_q; xpp = &caller_ptr->p_caller_q;
while (*xpp != NIL_PROC) { while (*xpp != NIL_PROC) {
if (src == ANY || src == proc_nr(*xpp)) { if (src == ANY || src == proc_nr(*xpp)) {
/* An acceptable source has been found. Copy the message to the /* Found acceptable message. Copy it and update status. */
* caller, update the sender's status, and remove the sender from
* the caller's queue.
*/
CopyMess((*xpp)->p_nr, *xpp, (*xpp)->p_messbuf, caller_ptr, m_ptr); CopyMess((*xpp)->p_nr, *xpp, (*xpp)->p_messbuf, caller_ptr, m_ptr);
if (((*xpp)->p_flags &= ~SENDING) == 0) ready(*xpp); if (((*xpp)->p_flags &= ~SENDING) == 0) ready(*xpp);
*xpp = (*xpp)->p_q_link; /* remove from queue */ *xpp = (*xpp)->p_q_link; /* remove from queue */
return(OK); return(OK); /* report success */
} }
xpp = &(*xpp)->p_q_link; /* proceed to next */ xpp = &(*xpp)->p_q_link; /* proceed to next */
} }
#endif #endif
/* Check if there are pending notifications. */ /* Check if there are pending notifications, except for SENDREC. */
if (! (flags & FRESH_ANSWER)) { if (! (flags & FRESH_ANSWER)) {
ntf_q_pp = &caller_ptr->p_ntf_q; /* get pointer pointer */ ntf_q_pp = &caller_ptr->p_ntf_q; /* get pointer pointer */
while (*ntf_q_pp != NULL) { while (*ntf_q_pp != NULL) {
if (src == ANY || src == (*ntf_q_pp)->n_source) { if (src == ANY || src == (*ntf_q_pp)->n_source) {
/* Found notification. Assemble and copy message. */ /* Found notification. Assemble and copy message. */
BuildMess(m, *ntf_q_pp); BuildMess(m, *ntf_q_pp);
CopyMess((*ntf_q_pp)->n_source, proc_addr(HARDWARE), &m, CopyMess((*ntf_q_pp)->n_source, proc_addr(HARDWARE), &m,
@ -323,21 +338,20 @@ if (! (flags & FRESH_ANSWER)) {
*ntf_q_pp = (*ntf_q_pp)->n_next;/* remove from queue */ *ntf_q_pp = (*ntf_q_pp)->n_next;/* remove from queue */
free_bit(bit_nr, notify_bitmap, NR_NOTIFY_BUFS); free_bit(bit_nr, notify_bitmap, NR_NOTIFY_BUFS);
return(OK); /* report success */ return(OK); /* report success */
} }
ntf_q_pp = &(*ntf_q_pp)->n_next; /* proceed to next */ ntf_q_pp = &(*ntf_q_pp)->n_next; /* proceed to next */
}
} }
}
} }
/* No suitable message is available or the caller couldn't send in SENDREC. /* No suitable message is available or the caller couldn't send in SENDREC.
* Block the process trying to receive, unless the flags tell otherwise. * Block the process trying to receive, unless the flags tell otherwise.
*/ */
if ( ! (flags & NON_BLOCKING)) { if ( ! (flags & NON_BLOCKING)) {
caller_ptr->p_getfrom = src; caller_ptr->p_getfrom = src;
caller_ptr->p_messbuf = m_ptr; caller_ptr->p_messbuf = m_ptr;
if (caller_ptr->p_flags == 0) unready(caller_ptr); if (caller_ptr->p_flags == 0) unready(caller_ptr);
caller_ptr->p_flags |= RECEIVING; caller_ptr->p_flags |= RECEIVING;
return(OK); return(OK);
} else { } else {
return(ENOTREADY); return(ENOTREADY);
@ -374,7 +388,7 @@ message *m_ptr; /* pointer to message buffer */
} }
/* Destination is not ready. Add the notification to the pending queue. /* Destination is not ready. Add the notification to the pending queue.
* Get pointer to notification message. Don't copy for the kernel. * Get pointer to notification message. Don't copy if already in kernel.
*/ */
if (! iskernelp(caller_ptr)) { if (! iskernelp(caller_ptr)) {
CopyMess(proc_nr(caller_ptr), caller_ptr, m_ptr, CopyMess(proc_nr(caller_ptr), caller_ptr, m_ptr,
@ -401,13 +415,13 @@ message *m_ptr; /* pointer to message buffer */
/* Add to end of queue (found above). Get a free notification buffer. */ /* Add to end of queue (found above). Get a free notification buffer. */
if ((ntf_index = alloc_bit(notify_bitmap, NR_NOTIFY_BUFS)) < 0) if ((ntf_index = alloc_bit(notify_bitmap, NR_NOTIFY_BUFS)) < 0)
return(ENOSPC); return(ENOSPC);
ntf_p = &notify_buffer[ntf_index]; ntf_p = &notify_buffer[ntf_index]; /* get pointer to buffer */
*ntf_q_pp = ntf_p; /* add to end of queue */ ntf_p->n_source = proc_nr(caller_ptr);/* store notification data */
ntf_p->n_next = NULL; /* mark new end of queue */
ntf_p->n_source = proc_nr(caller_ptr);
ntf_p->n_type = m_ptr->NOTIFY_TYPE; ntf_p->n_type = m_ptr->NOTIFY_TYPE;
ntf_p->n_flags = m_ptr->NOTIFY_FLAGS; ntf_p->n_flags = m_ptr->NOTIFY_FLAGS;
ntf_p->n_arg = m_ptr->NOTIFY_ARG; ntf_p->n_arg = m_ptr->NOTIFY_ARG;
*ntf_q_pp = ntf_p; /* add to end of queue */
ntf_p->n_next = NULL; /* mark new end of queue */
return(OK); return(OK);
} }
@ -419,7 +433,7 @@ int dst; /* to whom is message being sent? */
message *m_ptr; /* pointer to message buffer */ message *m_ptr; /* pointer to message buffer */
{ {
/* Safe gateway to mini_notify() for tasks and interrupt handlers. This /* Safe gateway to mini_notify() for tasks and interrupt handlers. This
* function checks if it is called from an interrupt handler and makes sure * function checks if it is called from an interrupt handler and ensures
* that the correct message source is put on the notification. * that the correct message source is put on the notification.
*/ */
int result; int result;
@ -466,8 +480,8 @@ PRIVATE void ready(rp)
register struct proc *rp; /* this process is now runnable */ register struct proc *rp; /* this process is now runnable */
{ {
/* Add 'rp' to one of the queues of runnable processes. */ /* Add 'rp' to one of the queues of runnable processes. */
register int q = rp->p_priority; /* scheduling queue to use */ register int q = rp->p_priority; /* scheduling queue to use */
register struct proc **xpp; /* iterate over queue */ register struct proc **xpp; /* iterate over queue */
#if ENABLE_K_DEBUGGING #if ENABLE_K_DEBUGGING
if(rp->p_ready) { if(rp->p_ready) {
@ -481,15 +495,15 @@ register struct proc *rp; /* this process is now runnable */
* fairer to I/O bound processes. * fairer to I/O bound processes.
*/ */
#if NEW_SCHED_Q #if NEW_SCHED_Q
if (isuserp(rp)) { /* add to front of queue */ if (isuserp(rp)) { /* add to front of queue */
rp->p_nextready = rdy_head[q]; /* chain current front */ rp->p_nextready = rdy_head[q]; /* chain current front */
rdy_head[q] = rp; /* ready process becomes front */ rdy_head[q] = rp; /* rp becomes new front */
} }
else { /* add to end of queue */ else { /* add to end of queue */
xpp = &rdy_head[q]; /* find pointer to end of queue */ xpp = &rdy_head[q]; /* find pointer to end */
while (*xpp != NIL_PROC) xpp = &(*xpp)->p_nextready; while (*xpp != NIL_PROC) xpp = &(*xpp)->p_nextready;
*xpp = rp; /* replace end with ready process */ *xpp = rp; /* replace end with rp */
rp->p_nextready = NIL_PROC; /* mark end of queue */ rp->p_nextready = NIL_PROC; /* mark end of queue */
} }
#else #else
if (isuserp(rp)) { /* add to front of queue */ if (isuserp(rp)) { /* add to front of queue */
@ -524,11 +538,11 @@ register struct proc *rp; /* this process is no longer runnable */
{ {
/* A process has blocked. See ready for a description of the queues. */ /* A process has blocked. See ready for a description of the queues. */
register int q = rp->p_priority; /* queue to use */ register int q = rp->p_priority; /* queue to use */
#if NEW_SCHED_Q #if NEW_SCHED_Q
register struct proc **xpp; /* iterate over queue */ register struct proc **xpp; /* iterate over queue */
#else #else
register struct proc **qtail; /* queue's rdy_tail */ register struct proc **qtail; /* queue's rdy_tail */
register struct proc *xp; register struct proc *xp;
#endif #endif
@ -600,12 +614,12 @@ int queue;
/* One or more user processes queued. */ /* One or more user processes queued. */
#if NEW_SCHED_Q #if NEW_SCHED_Q
xp = rdy_head[queue]; /* save expired process */ xp = rdy_head[queue]; /* save expired process */
rdy_head[queue] = xp->p_nextready; /* advance to next process */ rdy_head[queue] = xp->p_nextready; /* advance to next process */
xpp = &rdy_head[queue]; /* find end of queue */ xpp = &rdy_head[queue]; /* find end of queue */
while (*xpp != NIL_PROC) xpp = &(*xpp)->p_nextready; while (*xpp != NIL_PROC) xpp = &(*xpp)->p_nextready;
*xpp = xp; /* add expired to end */ *xpp = xp; /* add expired to end */
xp->p_nextready = NIL_PROC; /* mark end of queue */ xp->p_nextready = NIL_PROC; /* mark new end of queue */
#else #else
rdy_tail[queue]->p_nextready = rdy_head[queue]; rdy_tail[queue]->p_nextready = rdy_head[queue];

View file

@ -186,6 +186,7 @@ int proc_nr; /* slot of process to clean up */
#else #else
register struct proc **xpp; /* iterate over caller queue */ register struct proc **xpp; /* iterate over caller queue */
#endif #endif
int i;
/* Get a pointer to the process that exited. */ /* Get a pointer to the process that exited. */
rc = proc_addr(proc_nr); rc = proc_addr(proc_nr);
@ -238,6 +239,19 @@ int proc_nr; /* slot of process to clean up */
} }
} }
/* Check the table with IRQ hooks to see if hooks should be released. */
for (i=0; i < NR_IRQ_HOOKS; i++) {
if (irq_hooks[i].proc_nr == proc_nr)
irq_hooks[i].proc_nr = NONE;
}
/* Check if there are pending notifications. Release the buffers. */
while (rc->p_ntf_q != NULL) {
i = (int) (rc->p_ntf_q - &notify_buffer[0]);
free_bit(i, notify_bitmap, NR_NOTIFY_BUFS);
rc->p_ntf_q = rc->p_ntf_q->n_next;
}
/* Now clean up the process table entry. Reset to defaults. */ /* Now clean up the process table entry. Reset to defaults. */
kstrncpy(rc->p_name, "<noname>", PROC_NAME_LEN); /* unset name */ kstrncpy(rc->p_name, "<noname>", PROC_NAME_LEN); /* unset name */
sigemptyset(&rc->p_pending); /* remove pending signals */ sigemptyset(&rc->p_pending); /* remove pending signals */