Coherent4.2.10/coh.386/proc.c
/* $Header: /ker/coh.386/RCS/proc.c,v 2.6 93/10/29 00:55:30 nigel Exp Locker: nigel $ */
/* (lgl-
* The information contained herein is a trade secret of Mark Williams
* Company, and is confidential information. It is provided under a
* license agreement, and may be copied or disclosed only under the
* terms of that agreement. Any reproduction or disclosure of this
* material without the express written authorization of Mark Williams
* Company or persuant to the license agreement is unlawful.
*
* COHERENT Version 3.x, 4.x
* Copyright (c) 1982, 1993.
* An unpublished work by Mark Williams Company, Chicago.
* All rights reserved.
-lgl) */
/*
* Process handling and scheduling.
*/
#define _DDI_DKI 1
#define _DDI_DKI_IMPL 1
#define _SYSV3 1
#include <sys/coherent.h>
#include <common/_wait.h>
#include <sys/types.h>
#include <kernel/proc_lib.h>
#include <kernel/sig_lib.h>
#include <sys/cmn_err.h>
#include <sys/ksynch.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <stddef.h>
#include <signal.h>
#include <limits.h>
#define _KERNEL 1
#include <kernel/_timers.h>
#include <kernel/ddi_cpu.h>
#include <kernel/trace.h>
#include <kernel/reg.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/mmu.h>
#include <sys/acct.h>
#include <sys/inode.h>
#include <sys/ptrace.h>
#include <coh/proc.h>
/*
* Number of entries in sleep/wakeup queue.
*/
#define NHPLINK 97U
#define CLEAR_SIGN_BIT(foo) ((foo) & ~0x80000000)
/*
* Sleep/wakeup queues.
*/
struct plink {
PROC * p_lforw; /* Working forward pointer */
PROC * p_lback; /* Working backward pointer */
};
__LOCAL__ struct plink linkq [NHPLINK]; /* Sleep/wakeup hash queue */
#if _USE_IDLE_PROCESS
/*
* Pointer to idle process. For now, we still allocate a PROC *, but it
* exists only to satisfy the stupid circular-list scheme for process
* queueing; in actual fact, this process is never ever run.
*/
PROC * iprocp;
#endif
/*
* Function to hash a wakeup channel. To disappear ASAP along with the whole
* uniprocessor sleep mechanism.
*/
#define hash(e) ((unsigned)(e) % NHPLINK)
/*
* Initialization.
* Set up the hash table queues.
*/
void
pcsinit ()
{
struct plink * lp;
/*
* Explicitly initialize everything in the first process. We use the
* kernel initialize routine with SELF set to NULL so that the first
* process gets started out with default values for fields that are
* normally inherited.
*/
procq.p_nforw = & procq;
procq.p_nback = & procq;
procq.p_lforw = & procq;
procq.p_lback = & procq;
/* Segments are initialized in mchinit () and eveinit (). */
/* procq is static, so p_shmsr [] initializes to nulls. */
(void) new_process_init (& procq);
for (lp = linkq ; lp < linkq + NHPLINK ; lp ++) {
lp->p_lforw = lp;
lp->p_lback = lp;
}
/*
* After we have set things up, we can have a current process.
*/
SELF = & procq;
}
/*
* Initiate a process.
*/
static PROC *
process ()
{
PROC * pp1;
PROC * pp;
static pid_t next_pid;
if ((pp = new_process_init (NULL)) == NULL)
return NULL;
__GLOBAL_LOCK_PROCESS_TABLE ("process ()");
next:
/*
* Pick the next process id.
*/
if (++ next_pid >= PID_MAX)
next_pid = SYSPID_MAX + 1;
pp->p_pid = next_pid;
/*
* Make sure that process id is not in use.
*/
pp1 = & procq;
while ((pp1 = pp1->p_nforw) != & procq) {
if (pp1->p_pid < pp->p_pid)
break;
if (pp1->p_pid == pp->p_pid)
goto next;
}
/*
* We've got a valid pid, so let's put this process into
* the process table.
*/
pp->p_nback = pp1->p_nback;
pp1->p_nback->p_nforw = pp;
pp->p_nforw = pp1;
pp1->p_nback = pp;
__GLOBAL_UNLOCK_PROCESS_TABLE ();
return pp;
}
/*
* Remove a process from the next queue and release and space.
*/
void
relproc (pp)
PROC * pp;
{
SEG * sp;
/*
* Child process still has a user-area.
*/
if ((sp = pp->p_segl [SIUSERP].sr_segp) != NULL) {
/*
* Detach user-area from child process.
*/
pp->p_segl [SIUSERP].sr_segp = NULL;
/*
* Child process is swapped out.
*/
if (pp->p_flags & PFSWAP)
sp->s_lrefc ++;
/*
* Release child's user-area.
*/
sfree (sp);
}
/*
* Remove process from doubly-linked list of all processes.
* Release space allocated for proc structure.
*/
__GLOBAL_LOCK_PROCESS_TABLE ("relproc ()");
pp->p_nback->p_nforw = pp->p_nforw;
pp->p_nforw->p_nback = pp->p_nback;
__GLOBAL_UNLOCK_PROCESS_TABLE ();
proc_destroy (pp);
}
/*
* Create a clone of ourselves.
* N.B. - consave (& mcon) returns twice; anything not initialized
* in automatic storage before the call to segadup () will not be
* initialized when the second return from consave () commences.
*/
int
pfork ()
{
PROC * cpp;
int s;
struct __mcon mcon;
if ((cpp = process ()) == NULL) {
SET_U_ERROR (EAGAIN, "no more process table entries");
return -1;
}
s = sphi ();
/*
* Create an initial register context for the child process; the most
* obvious way to do this is to clone our context.
*/
if (consave (cpp->p_sysconp = & mcon) != 0) {
/*
* Child process.
*/
/*
* WARNING!!! Parent returns first from consave() call
* above. The lines below are executed only after the
* first context switch to the new child process.
*
* There is a brief interval between these times during
* which the process queue is fair game, e.g. for /dev/ps.
* So, be sure that changes here do not really belong
* in process() or something it calls.
*/
spl (s);
u.u_btime = timer.t_time;
u.u_flag = AFORK;
#if UPROC_VERSION
u.u_version = UPROC_VERSION;
#endif /* UPROC_VERSION */
sproto (0);
segload ();
return 0;
}
spl (s);
if (segadup (cpp) == 0) {
SET_U_ERROR (EAGAIN, "can not duplicate segments");
relproc (cpp);
return -1;
}
shmDup (cpp); /* copy shared memory info & update ref counts */
if (u.u_rdir)
u.u_rdir->i_refc ++;
if (u.u_cdir)
u.u_cdir->i_refc ++;
fd_dup_all ();
s = sphi ();
process_set_runnable (cpp);
spl (s);
return cpp->p_pid;
}
PROC *
get_next_process()
{
PROC *cur_proc;
PROC *ret_proc;
int max_prior;
/*
* This assumes that the process releasing context is queued
* at the **end** of the queue.
*/
ret_proc = cur_proc = iprocp->p_lback;
/* See if system is idle. */
if (cur_proc == iprocp)
return cur_proc;
/* Determine max priority by adding priority with anti-starvation. */
max_prior = cur_proc->p_schedPri + cur_proc->p_foodstamp;
/* Select the process with the **highest** priority value */
while (cur_proc != iprocp && cur_proc != NULL) {
long cur_con;
cur_con = CLEAR_SIGN_BIT(cur_proc->p_schedPri + cur_proc->p_foodstamp);
if (cur_con > max_prior) {
max_prior = cur_con;
ret_proc = cur_proc;
} else { /* Fend off starvation */
if (cur_proc != SELF) {
cur_proc->p_foodstamp++;
cur_proc->p_foodstamp = CLEAR_SIGN_BIT(cur_proc->p_foodstamp);
}
}
cur_proc = cur_proc->p_lback;
}
ASSERT(ret_proc != NULL);
/* Starvation is not a concern for him who eats. */
ret_proc->p_foodstamp = 0;
return ret_proc;
}
/*
* Select next process for execution, working backward from iprocp.
* If it is not the idle process, delete it from the run queue.
* If it is not the current process, consave the current process and
* conrest the selected process.
*/
void
dispatch ()
{
PROC *pp;
int s;
int old_quantum;
LOCK_DISPATCH();
s = sphi();
while ((pp = get_next_process()) == iprocp) {
pl_t prev_pl;
/*
* The system is idle. The logical thing to do now would be to
* go to ring 0 and halt. Instead, we'll enable interrupts and
* wait for the process to change.
*/
__MARK_CPU_IDLE();
prev_pl = splbase();
splo();
while (iprocp->p_lback == iprocp)
check_clock();
/*
* Re-enable interrupts, and restore normal programming.
*/
(void) sphi();
(void) splx(prev_pl);
__CLEAR_CPU_IDLE();
}
pp->p_lforw->p_lback = pp->p_lback;
pp->p_lback->p_lforw = pp->p_lforw;
spl(s);
old_quantum = quantum;
if (pp != iprocp) {
int newpri;
int divisor;
int new_usage;
/*
* The actual equation used to compute the priority is this:
* p(t) = a * (DEFAULT_QUANTUM / (DEFAULT_QUANTUM - old_quantum))
* + (1 - a) * p(t - 1)
*
* Currently, a == 0.5 and the equation is optimized for this.
* For p(0), look in ~/coh.386/lib/proc_init.c
*/
divisor = (DEFAULT_QUANTUM == old_quantum) ? 1 :
(DEFAULT_QUANTUM - old_quantum);
new_usage = (DEFAULT_QUANTUM * SCHED_SCALE) / divisor;
newpri = (new_usage + SELF->p_schedPri) >> 1;
SELF->p_schedPri = CLEAR_SIGN_BIT(newpri);
#if 0
printf("(%d) - 0x%x\n", SELF->p_pid, SELF->p_schedPri);
#endif
}
quantum = DEFAULT_QUANTUM;
disflag = 0;
if (pp != SELF) {
struct __mcon syscon;
/*
* Consave () returns twice.
* 1st time is after our context is saved in syscon,
* whereupon we should restore other proc's context.
* 2nd time is after our context is restored by another proc.
* Conrest () forces a context switch to a new process.
*/
s = sphi ();
if (consave (SELF->p_sysconp = & syscon) == 0) {
SELF = pp;
conrest (* SELF->p_segl [SIUSERP].sr_segp->s_vmem,
SELF->p_sysconp);
}
if (SELF->p_pid) { /* idle process is special! */
ndpConRest ();
segload ();
}
SELF->p_sysconp = NULL;
spl (s);
}
}
/*
* Die.
*/
void
pexit (s)
int s;
{
PROC * pp1;
SEG * sp;
int n;
PROC * parent = NULL;
T_PIGGY (0x1, printf ("%s:pexit (%x)", SELF->p_comm, s));
ndpEndProc ();
/*
* Cancel alarm and poll timers [if any].
*/
timeout (& SELF->p_alrmtim, 0, NULL, 0);
timeout (& SELF->p_polltim, 0, NULL, 0);
/*
* Write out accounting directory and close all files associated with
* this process.
*/
setacct ();
if (u.u_rdir)
ldetach (u.u_rdir);
if (u.u_cdir)
ldetach (u.u_cdir);
/*
* Block all signals before entering forced close sequence.
*/
___SIGSET_SET (SELF->p_signal_mask, -1UL);
fd_close_all ();
/*
* Free all segments in reverse order, except for user-area.
*/
for (n = NUSEG ; -- n > 0 ;) {
if ((sp = SELF->p_segl [n].sr_segp) != NULL) {
SELF->p_segl [n].sr_segp = NULL;
sfree (sp);
}
}
/* Detach remaining shared memory segments. */
shmAllDt ();
/* Adjust (undo) all semaphores. */
semAllAdjust (SELF);
/*
* Wakeup our parent. If we have any children, init will become the
* new parent. If there are any children we are tracing who are
* waiting for us, we wake them up.
*/
pp1 = & procq;
/* pp1 runs through the list of all processes */
while ((pp1 = pp1->p_nforw) != & procq) {
/* if pp1 points to parent of the current process...*/
if (pp1->p_pid == SELF->p_ppid) {
parent = pp1; /* Remember our parent. */
if (ASLEEP (pp1) && pp1->p_event == (char *) pp1)
wakeup ((char *) pp1);
}
/* if pp1 points to child of the current process...*/
if (pp1->p_ppid == SELF->p_pid) {
pp1->p_ppid = 1;
if (pp1->p_state == PSDEAD)
wakeup ((char *) eprocp);
if (pp1->p_flags & PFTRAC)
ptrace_cleanup ();
}
}
/*
* Mark us as dead and give up the processor forever.
*/
SELF->p_exit = s;
SELF->p_state = PSDEAD;
/*
* If this is a process group leader, inform all members of the group
* of the recent death with a HUP signal.
*/
if (SELF->p_group == SELF->p_pid)
ukill (- SELF->p_pid, SIGHUP);
/*
* If the parent is ignoring SIGCLD,
* remove the zombie right away.
*/
if (parent == NULL)
panic ("%d (@ %x), status %d has no parent!\n", SELF->p_pid,
SELF, s);
if ((proc_signal_misc (parent) & __SF_NOCLDWAIT) != 0) {
/*
* The parent has requested that no zombie processes be
* created out of childen.
*/
parent->p_cutime += SELF->p_utime + SELF->p_cutime;
parent->p_cstime += SELF->p_stime + SELF->p_cstime;
relproc (SELF);
} else {
/*
* Send parent a notification of our demise.
*/
__siginfo_t sigchld;
sigchld.__si_signo = SIGCHLD;
sigchld.__si_errno = 0;
if (__WIFEXITED (s)) {
sigchld.__si_code = __CLD_EXITED;
sigchld.__si_status = __WEXITSTATUS (s);
} else {
sigchld.__si_code = __WCOREDUMP (s) ? __CLD_DUMPED :
__CLD_KILLED;
sigchld.__si_status = __WTERMSIG (s);
}
sigchld.__si_pid = SELF->p_pid;
proc_send_signal (parent, & sigchld);
}
dispatch ();
}
/*
* x_sleep ()
*
* Surrender CPU while awaiting some event or resource.
*
* Arguments:
* event: key value; so wakeup () can find this sleep
* schedPri: prilo/primed/prihi/pritape/pritty/pridisk/prinet
* just copied into proc struct for scheduler to use.
* sleepPri: slpriNoSig - signals may not interrupt sleep
* slpriSigLjmp - signals cause longjmp (EINTR)
* slpriSigCatch - signals are caught
* reason: up to 10 chars of text for ps command "event"
*
* Return values:
* PROCESS_NORMAL_WAKE wakeup received
* PROCESS_SIGNALLED signal (other than SIGSTOP/SIGCONT) received
* PROCESS_CONTINUED SIGSTOP / SIGCONT (unimplemented now)
*
* If longjmp occurs, won't return from x_sleep!
*/
#if __USE_PROTO__
__sleep_t x_sleep (char * event, int schedPri, int sleepPri, char * reason)
#else
__sleep_t
x_sleep (event, schedPri, sleepPri, reason)
char * event;
int schedPri;
int sleepPri;
char * reason;
#endif
{
PROC *bp;
PROC *fp;
int s;
/*
* Before we queue ourselves on the event list, test for the presence
* of signals if the users wants to know about them. Older versions of
* the kernel tested after queueing, which meant that they left
* the lists in an invalid state.
*/
s = sphi();
if (sleepPri != slpriNoSig && curr_signal_pending() != 0) {
spl(s);
if (sleepPri == slpriSigCatch)
return PROCESS_SIGNALLED;
/* Do longjmp to beginning of system call. */
T_HAL (8, printf ("[%d]Ljmps ", SELF->p_pid));
(void) sphi();
envrest(u.u_sigenvp);
}
SELF->p_sleep = reason;
/*
* Get ready to go to sleep and do so.
*/
SELF->p_state = sleepPri == slpriNoSig ? PSSLEEP : PSSLSIG;
/* Scale priority by 2^n and mask off sign bit */
SELF->p_foodstamp += (schedPri << 3) + 7;
SELF->p_foodstamp = CLEAR_SIGN_BIT(SELF->p_foodstamp);
SELF->p_event = event;
fp = linkq + hash(event);
bp = fp->p_lback;
SELF->p_lforw = fp;
fp->p_lback = SELF;
SELF->p_lback = bp;
bp->p_lforw = SELF;
spl(s);
/*
* From here on we count on someone else dequeuing us.
*/
dispatch ();
SELF->p_sleep = NULL;
if (sleepPri == slpriNoSig || curr_signal_pending () == 0)
return PROCESS_NORMAL_WAKE;
/* The process has been interrupted from sleep by a signal. */
if (sleepPri == slpriSigCatch)
return PROCESS_SIGNALLED;
/* Do longjmp to beginning of system call. */
T_HAL (8, printf ("[%d]Ljmps ", SELF->p_pid));
(void) sphi ();
envrest (u.u_sigenvp);
}
/*
* Defer function to wake up all processes sleeping on the event `e'.
*/
void
wakeup (event)
char * event;
{
#if 0
void dwakeup ();
#ifdef TRACER
/*
* In diagnostic kernel, keep return addr on queue as well.
*/
int * r = (int *) (& e);
defer0 (dwakeup, e, * (r - 1));
#else
defer (dwakeup, e);
#endif
}
/*
* Wake up all processes sleeping on "event".
*/
void
dwakeup (event)
char * event;
{
#endif
PROC * pp;
PROC * pp1;
int s;
/*
* Identify event queue to check.
* Disable interrupts.
*/
pp1 = linkq + hash (event);
pp = pp1;
s = sphi ();
/*
* Traverse doubly-linked circular event-queue.
*/
while ((pp = pp->p_lforw) != pp1) {
/*
* Process is waiting on event 'event'.
*/
if (pp->p_event == event) {
/*
* Remove process from event queue.
* Update process priority.
* Insert process into run queue.
*/
pp->p_lback->p_lforw = pp->p_lforw;
pp->p_lforw->p_lback = pp->p_lback;
process_set_runnable (pp);
/*
* Enable interrupts.
* Restart search at start of event queue.
* Disable interrupts.
*/
spl (s);
pp = pp1;
s = sphi ();
}
}
spl (s);
}
/*
* Wait for the gate `g' to unlock, and then lock it.
*/
void
__GATE_LOCK (g, reason)
__DUMB_GATE_P g;
__CONST__ char * reason;
{
int s;
ASSERT (g != NULL);
s = sphi ();
while (g->_lock [0]) {
#if TRACER
if (g->_lock [0] != 1)
cmn_err (CE_PANIC, "Uninitialised gate");
if (__GATE_LOCK_OWNER (g) == (char *) SELF) {
cmn_err (CE_WARN, "Recursive lock attempt by pid %d"
SELF->p_pid);
backtrace (0);
break;
}
#endif
g->_lock [1] = 1;
x_sleep ((char *) g, primed, slpriNoSig, reason);
/* Waiting for a gate to unlock. */
}
g->_lock [0] = 1;
g->_lastlock = (char *) SELF;
if ((__GATE_FLAGS (g) & __GATE_COUNT) != 0)
u.u_lock_cnt ++;
spl (s);
}
/*
* Unlock the gate `g'.
*/
void
__GATE_UNLOCK (g)
__DUMB_GATE_P g;
{
ASSERT (g != NULL);
ASSERT (g->_lock [0] != 0);
g->_lock [0] = 0;
if (g->_lock [1]) {
ASSERT (g->_lock [1] == 1);
g->_lock [1] = 0;
disflag = 1;
wakeup ((char *) g);
}
if ((__GATE_FLAGS (g) & __GATE_COUNT) != 0) {
ASSERT (u.u_lock_cnt != 0);
if (g->_lastlock != (char *) & procq)
u.u_lock_cnt --;
}
}
/*
* Mark the given gate as being "owned" by an interrupt context.
*/
void
__GATE_TO_INTERRUPT (g)
__DUMB_GATE_P g;
{
ASSERT (g != NULL);
ASSERT (g->_lastlock != (char *) & procq);
if ((__GATE_FLAGS (g) & __GATE_COUNT) != 0) {
ASSERT (u.u_lock_cnt != 0);
u.u_lock_cnt --;
}
g->_lastlock = (char *) & procq;
}