home *** CD-ROM | disk | FTP | other *** search
- /* Non pre-empting synchronization kernel, using LWP on SunOS
- * Portions Copyright 1991 Phil Karn, KA9Q
- */
- #include <stdio.h>
- #include <lwp/lwp.h>
- #include <lwp/lwpmachdep.h>
- #include <lwp/lwperror.h>
- #include <memory.h>
- #include <signal.h>
- #include "global.h"
- #include "mbuf.h"
- #include "proc.h"
- #include "timer.h"
- #include "socket.h"
- #include "daemon.h"
-
-
- #define KERNELLOG /* keep a nice log of major kernel events */
- #define STACKDEBUG /* silently test all processes for stack overrun */
- #undef TRACEDEBUG /* one-line message for each major routine */
- #undef THREADDEBUG /* Gobs of ps-like output constantly */
-
-
- struct proc *Curproc; /* Currently running process */
- struct proc *Rdytab; /* Processes ready to run (not including curproc) */
- struct proc *Waittab[PHASH]; /* Waiting process list */
- static struct mbuf *Killq;
-
- static void addproc __ARGS((struct proc *entry));
- static void delproc __ARGS((struct proc *entry));
- static void startproc __ARGS((int pp, int pc));
-
- #ifdef KERNELLOG
- #define KLOG_SIZE 32
- void kernellog();
- void kernel_segv();
- int klog_i;
- struct {
- char *routine; /* "psignal" */
- char *detail; /* "enter" */
- char proc_name[32]; /* "display" */
- int i; /* interrupts */
- } klog[KLOG_SIZE];
- #define KL(qa, qb, qc) kernellog(qa, qb, qc)
- #else
- #define KL(qa, qb, qc)
- #endif
-
- #define LWP_STACKSIZE 8192 /* requested stack size for all processes */
-
- /* This is the macro that tests if a given process has run off the stack */
- #define LWP_TEST(q,t) {if(((q)->stksize)>0){if(*((q)->stackbottom)!=STACKBOT){printf("%s: LWP stack overflow!\n",t);abort();}}}
-
- /* Create a process descriptor for the main function. Must be actually
- * called from the main function!
- */
- struct proc *
- mainproc(name)
- char *name;
- {
- register struct proc *pp;
-
- /* Create process descriptor */
- pp = (struct proc *)callocw(1,sizeof(struct proc));
-
- /* Find our own thread ID */
- if(lwp_self(&pp->tid) < 0){
- lwp_perror("lwp_self");
- abort();
- }
- /* Multiplex the libc stuff (errno) */
- if(lwp_libcset(pp->tid) < 0){
- lwp_perror("lwp_libcset");
- abort();
- }
- /* Create name */
- pp->name = strdup(name);
- pp->stksize = 0;
-
- /* Task initially runs with interrupts on */
- pp->i_state = 1;
-
- /* Make current */
- pp->state = READY;
- Curproc = pp;
-
- return pp;
- }
- /* Create a new, ready process and return pointer to descriptor.
- * The general registers are not initialized, but optional args are pushed
- * on the stack so they can be seen by a C function.
- */
- struct proc *
- newproc(name,stksize,pc,iarg,parg1,parg2,freeargs)
- char *name; /* Arbitrary user-assigned name string */
- unsigned int stksize; /* Stack size in words to allocate */
- void (*pc)(); /* Initial execution address */
- int iarg; /* Integer argument (argc) */
- void *parg1; /* Generic pointer argument #1 (argv) */
- void *parg2; /* Generic pointer argument #2 (session ptr) */
- int freeargs; /* If set, free arg list on parg1 at termination */
- {
- register struct proc *pp;
- int i, j, *w;
-
- /* Create process descriptor */
- pp = (struct proc *)callocw(1,sizeof(struct proc));
-
- /* Create name */
- pp->name = strdup(name);
-
- pp->stksize = LWP_STACKSIZE;
- pp->stackbottom = (int *)malloc((pp->stksize) * sizeof(int));
- pp->stack = (pp->stackbottom) + (pp->stksize)- 4;
- pp->stack = pp->stack & 0xfffffff8;
-
- /* Task initially runs with interrupts on */
- pp->i_state = 1;
- pp->freeargs = freeargs;
- pp->iarg = iarg;
- pp->parg1 = parg1;
- pp->parg2 = parg2;
-
- /* Inherit creator's input and output sockets */
- usesock(Curproc->input);
- pp->input = Curproc->input;
- usesock(Curproc->output);
- pp->output = Curproc->output;
-
- /* Clear out the rest of the fields */
- pp->prev = pp->next = NULLPROC;
- pp->outbuf = (struct mbuf *)NULL;
-
- /* Add to ready process table */
- pp->state = READY;
-
- /* Initialize the stack (for paranoia purposes!) */
- for(w=(pp->stackbottom); w<(pp->stack); w++) *w = STACKPAT;
-
- /* Initialize stack for high-water check */
- *(pp->stackbottom) = STACKBOT; /* test this each switch! */
-
- addproc(pp);
-
- if(lwp_create(&pp->tid, startproc, MINPRIO, 0, pp->stack, 2,
- (int)pp,(int)pc) < 0) {
- lwp_perror("lwp_create");
- delproc(pp);
- free(pp->name);
- free((char *)pp->stackbottom);
- free((char *)pp);
- return NULLPROC;
- }
-
- /* Multiplex the libc stuff (errno) */
- if(lwp_libcset(pp->tid) < 0){
- lwp_perror("lwp_libcset");
- abort();
- }
-
- #ifdef TRACEDEBUG
- printf("Added Process %s\n",pp->name);
- #endif
- #ifdef THREADDEBUG
- debug_lwp();
- #endif
- #ifdef STACKDEBUG
- lwp_stackscan();
- #endif
- KL("addproc", "", pp);
-
- return pp;
- }
-
- static void
- startproc(pp, pc)
- struct proc *pp;
- int pc;
- {
- void (*func)();
- func = (void (*)()) pc;
- Curproc = (struct proc *) pp;
- delproc (Curproc);
- #ifdef TRACEDEBUG
- printf("Started Process %s\n",Curproc->name);
- #endif
- #ifdef THREADDEBUG
- debug_lwp();
- #endif
- #ifdef STACKDEBUG
- lwp_stackscan();
- #endif
- KL("startproc","begin",pp);
- (*func)(Curproc->iarg,Curproc->parg1,Curproc->parg2);
- KL("startproc","end",pp);
- killself();
- }
-
- /* Free resources allocated to specified process. If a process wants to kill
- * itself, the reaper is called to do the dirty work. This avoids some
- * messy situations that would otherwise occur, like freeing your own stack.
- */
- void
- killproc(pp)
- register struct proc *pp;
- {
- int i;
- char **argv;
-
- if(pp == NULLPROC)
- return;
- /* Don't check the stack here! Will cause infinite recursion if
- * called from a stack error
- */
-
- if(pp == Curproc)
- killself(); /* Doesn't return */
-
- /* Close any open sockets */
- freesock(pp);
-
- close_s(pp->input);
- close_s(pp->output);
-
- /* Stop alarm clock in case it's running */
- stop_timer(&pp->alarm);
-
- /* Alert everyone waiting for this proc to die */
- psignal(pp,0);
-
- /* Remove from appropriate table */
- delproc(pp);
-
- #ifdef TRACEDEBUG
- printf("Killed Process %s\n",pp->name);
- #endif
- KL("killproc","",pp);
- if(lwp_destroy(pp->tid) < 0){
- lwp_perror("killproc: lwp_destroy:");
- abort();
- }
-
- /* Free allocated memory resources */
- if(pp->freeargs){
- argv = pp->parg1;
- while(pp->iarg-- != 0){
- free(*argv);
- argv++;
- }
- free(pp->parg1);
- }
- free(pp->name);
- free(pp->outbuf);
- free((char *)pp->stackbottom);
- free((char *)pp);
- }
-
- /* Terminate current process by sending a request to the killer process.
- * Automatically called when a process function returns. Does not return.
- */
- void
- killself()
- {
- register struct mbuf *bp;
- bp = pushdown(NULLBUF,sizeof(Curproc));
- memcpy(bp->data,(char *)&Curproc,sizeof(Curproc));
- enqueue(&Killq,bp);
- /* "Wait for me; I will be merciful and quick." */
- for(;;)
- lwp_suspend(SELF);
- }
-
- /* Process used by processes that want to kill themselves */
- void
- killer(i,v1,v2)
- int i;
- void *v1;
- void *v2;
- {
- struct proc *pp;
- struct mbuf *bp;
-
- for(;;){
- while(Killq == NULLBUF)
- pwait(&Killq);
- bp = dequeue(&Killq);
- pullup(&bp,(char *)&pp,sizeof(pp));
- free_p(bp);
- if(pp != Curproc) /* We're immortal */
- killproc(pp);
- }
- }
-
- /* Inhibit a process from running */
- void
- suspend(pp)
- struct proc *pp;
- {
- if(pp == NULLPROC)
- return;
- #ifdef TRACEDEBUG
- printf("Suspended Process %s\n",pp->name);
- #endif
- KL("suspend","",pp);
- if(pp != Curproc)
- delproc(pp); /* Running process isn't on any list */
- pp->state |= SUSPEND;
- if(pp != Curproc)
- addproc(pp); /* pwait will do it for us */
- else
- if(lwp_suspend(SELF) < 0){
- lwp_perror("suspend: lwp_suspend");
- abort();
- }
- }
- /* Restart suspended process */
- void
- resume(pp)
- struct proc *pp;
- {
- if(pp == NULLPROC)
- return;
- #ifdef TRACEDEBUG
- printf("Resumed Process %s\n",pp->name);
- #endif
- KL("resume","",pp);
- delproc(pp); /* Can't be Curproc! */
- pp->state &= ~SUSPEND;
- addproc(pp);
- }
-
- /* Wakeup waiting process, regardless of event it's waiting for. The process
- * will see a return value of "val" from its pwait() call.
- */
- void
- alert(pp,val)
- struct proc *pp;
- int val;
- {
- if(pp == NULLPROC)
- return;
- #ifdef notdef
- if((pp->state & WAITING) == 0)
- return;
- #endif
- #ifdef TRACEDEBUG
- printf("Alerted Process %s\n",pp->name);
- #endif
- KL("alert","",pp);
- if(pp != Curproc)
- delproc(pp);
- pp->state &= ~WAITING;
- pp->retval = val;
- pp->event = 0;
- if(pp != Curproc)
- addproc(pp);
- if(lwp_resume(pp->tid) < 0){
- lwp_perror("alert: lwp_resume");
- abort();
- }
- }
-
- /* Post a wait on a specified event and give up the CPU until it happens. The
- * null event is special: it means "I don't want to block on an event, but let
- * somebody else run for a while". It can also mean that the present process
- * is terminating; in this case the wait never returns.
- *
- * Pwait() returns 0 if the event was signaled; otherwise it returns the
- * arg in an alert() call. Pwait must not be called from interrupt level.
- *
- */
- int
- pwait(event)
- void *event;
- {
- register struct proc *oldproc;
- int tmp;
- struct proc *pp;
-
- #ifdef TRACEDEBUG
- if(event){
- printf("PWAIT: (%s) event=%x\n",Curproc->name,event);
- } else {
- printf("PWAIT: (%s) NULL\n",Curproc->name);
- }
- #endif
- #ifdef THREADDEBUG
- debug_lwp();
- #endif
- #ifdef STACKDEBUG
- lwp_stackscan();
- #endif
- pp = Curproc;
- KL("pwait","enter",pp);
- pp->i_state = istate();
- Curproc->event = event;
- if(event != NULL) {
- Curproc->state = WAITING; /* Post wait for specified event */
- }
- addproc(Curproc);
- restore(1);
- LWP_TEST(Curproc,"pwait");
- if(Curproc->event == NULL){
- /* Special case; just give up the processor. */
- if(lwp_yield(THREADNULL) < 0){
- lwp_perror("pwait: lwp_yield");
- abort();
- }
- } else {
- if(lwp_suspend(SELF) < 0){
- lwp_perror("pwait: lwp_suspend");
- abort();
- }
- }
- Curproc = pp;
- LWP_TEST(Curproc,"pwait");
- KL("pwait","exit",pp);
- delproc(Curproc);
- /* At this point, we're running in the newly dispatched task */
- tmp = Curproc->retval;
- Curproc->retval = 0;
- dirps();
- restore(Curproc->i_state);
- return tmp;
- }
-
- /* Make ready the first 'n' processes waiting for a given event. The ready
- * processes will see a return value of 0 from pwait(). Note that they don't
- * actually get control until we explicitly give up the CPU ourselves through
- * a pwait(). Psignal may be called from interrupt level. It returns the
- * number of processes that were woken up.
- */
- int
- psignal(event,n)
- void *event; /* Event to signal */
- int n; /* Max number of processes to wake up */
- {
- register struct proc *pp;
- struct proc *pnext;
- int i_state;
- unsigned int hashval;
- int cnt = 0;
-
- #ifdef TRACEDEBUG
- if(event){
- printf("PSIGNAL: (%s) event=%x; n=%d\n",Curproc->name, event, n);
- } else {
- printf("PSIGNAL: (%s) NULL\n",Curproc->name);
- }
- #endif
- #ifdef STACKDEBUG
- lwp_stackscan();
- #endif
- if(event == NULL)
- return 0; /* Null events are invalid */
-
- /* n = 0 means "signal everybody waiting for this event" */
- if(n == 0)
- n = 65535;
-
- hashval = phash(event);
- i_state = dirps();
- for(pp = Waittab[hashval];n != 0 && pp != NULLPROC;pp = pnext){
- pnext = pp->next;
- if(pp->event == event){
- delproc(pp);
- pp->state &= ~WAITING;
- pp->retval = 0;
- pp->event = NULL;
- addproc(pp);
- if(lwp_resume(pp->tid) < 0){
- lwp_perror("psignal: lwp_resume");
- abort();
- }
- LWP_TEST(pp,"psignal");
- KL("psignal","signaled",pp);
- n--;
- cnt++;
- }
- }
- restore(i_state);
- return cnt;
- }
-
- /* Rename a process */
- void
- chname(pp,newname)
- struct proc *pp;
- char *newname;
- {
- free(pp->name);
- pp->name = strdup(newname);
- #ifdef TRACEDEBUG
- printf("Renamed a process to %s\n",pp->name);
- #endif
- KL("chname","",pp);
- }
- /* Remove a process entry from the appropriate table */
- static void
- delproc(entry)
- register struct proc *entry; /* Pointer to entry */
- {
- int i_state;
-
- if(entry == NULLPROC){
- #ifdef TRACEDEBUG
- printf("Warning: attempt to delete a null process!\n");
- #endif
- return;
- }
-
- i_state = dirps();
- if(entry->next != NULLPROC)
- entry->next->prev = entry->prev;
- if(entry->prev != NULLPROC){
- entry->prev->next = entry->next;
- } else {
- switch(entry->state){
- case READY:
- Rdytab = entry->next;
- break;
- case WAITING:
- Waittab[phash(entry->event)] = entry->next;
- break;
- }
- }
- entry->next = NULLPROC;
- entry->prev = NULLPROC;
- restore(i_state);
- }
- /* Append proc entry to end of appropriate list */
- static void
- addproc(entry)
- register struct proc *entry; /* Pointer to entry */
- {
- register struct proc *pp;
- struct proc **head;
- int i_state;
-
- if(entry == NULLPROC){
- #ifdef TRACEDEBUG
- printf("Warning: attempt to add a null process!\n");
- #endif
- return;
- }
-
- i_state = dirps();
- switch(entry->state){
- case READY:
- head = &Rdytab;
- break;
- case WAITING:
- head = &Waittab[phash(entry->event)];
- break;
- }
- entry->next = NULLPROC;
- if(*head == NULLPROC){
- /* Empty list, stick at beginning */
- entry->prev = NULLPROC;
- *head = entry;
- } else {
- /* Find last entry on list */
- for(pp = *head;pp->next != NULLPROC;pp = pp->next)
- ;
- pp->next = entry;
- entry->prev = pp;
- }
- restore(i_state);
- }
-
- /* ps() will show strange values for the stack usage of the main process.
- This is beacuse we do not fill the stack space of the main process
- with STACKPAT. */
-
-
- /* Print process table info
- * Since things can change while ps is running, the ready proceses are
- * displayed last. This is because an interrupt can make a process ready,
- * but a ready process won't spontaneously become unready. Therefore a
- * process that changes during ps may show up twice, but this is better
- * than not having it showing up at all.
- */
- int
- ps(argc,argv,pppp)
- int argc;
- char **argv;
- void *pppp;
- {
- register struct proc *pp;
- int i, used, *p;
-
- tprintf("PID SP stksize maxstk event fl in out name\n");
- for(i=0;i<PHASH;i++){
- for(pp = Waittab[i];pp != NULLPROC;pp = pp->next){
- if(pp->stksize > 0){
- for(p=pp->stackbottom+1;*p==STACKPAT;p++);
- used = pp->stack - p;
- } else used = 0;
- if(tprintf("%-10lx%-10lx%-10u%-10u%-10lx%c%c%c %2d %2d %s\n",
- ptol(pp),ptol(pp->stack),pp->stksize,used,
- ptol(pp->event),
- pp->i_state ? 'I' : ' ',
- (pp->state & WAITING) ? 'W' : ' ',
- (pp->state & SUSPEND) ? 'S' : ' ',
- pp->input,pp->output,
- pp->name) == EOF)
- return 0;
- }
- }
- for(pp = Rdytab;pp != NULLPROC;pp = pp->next){
- if(pp->stksize > 0){
- for(p=pp->stackbottom+1;*p==STACKPAT;p++);
- used = pp->stack - p;
- } else used = 0;
- if(tprintf("%-10lx%-10lx%-10u%-10u %c%c%c %2d %2d %s\n",
- ptol(pp),ptol(pp->stack),pp->stksize,used,
- pp->i_state ? 'I' : ' ',
- (pp->state & WAITING) ? 'W' : ' ',
- (pp->state & SUSPEND) ? 'S' : ' ',
- pp->input,pp->output,
- pp->name) == EOF)
- return 0;
- }
- if(Curproc != NULLPROC){
- if(Curproc->stksize > 0){
- for(p=Curproc->stackbottom+1;*p==STACKPAT;p++);
- used = Curproc->stack - p;
- } else used = 0;
- tprintf("%-10lx%-10lx%-10u%-10u %c %2d %2d %s\n",
- ptol(Curproc),ptol(Curproc->stack),Curproc->stksize,used,
- istate() ? 'I' : ' ',
- Curproc->input,Curproc->output,
- Curproc->name);
- }
- return 0;
- }
-
- void kinit()
- {
- if(pod_setmaxpri(MINPRIO + 2) < 0){
- lwp_perror("kinit: pod_setmaxpri");
- abort();
- }
-
- if(lwp_setpri(SELF, MINPRIO) < 0){
- lwp_perror("kinit: lwp_setpri");
- abort();
- }
-
- #ifdef KERNELLOG
- for(klog_i=0;klog_i<KLOG_SIZE;klog_i++)klog[klog_i].routine=NULL;
- klog_i = 0;
- (void) signal(SIGSEGV, kernel_segv);
- (void) signal(SIGUSR1, kernel_segv); /* for debugging :-) */
- #endif
- }
-
-
-
- phash(event)
- void *event;
- {
- register unsigned x;
-
- x = (unsigned) event;
-
- /* If PHASH is a power of two, this will simply mask off the
- * higher order bits
- */
- return x % PHASH;
- }
-
-
- #ifdef KERNELLOG
- void
- kernellog(r, d, pp)
- char *r;
- char *d;
- struct proc *pp;
- {
- if(r==NULL)return;
-
- klog[klog_i].routine = r;
-
- if(d)klog[klog_i].detail = d;
- else klog[klog_i].detail = "";
-
- if(pp)strcpy(klog[klog_i].proc_name, pp->name);
- else strcpy(klog[klog_i].proc_name, "?");
-
- klog[klog_i].i = istate();
-
- klog_i++;
- if(klog_i>=KLOG_SIZE)klog_i = 0;
- }
-
- void
- kernel_segv()
- {
- int i;
-
- fprintf(stderr,"Signal: SEGV\n");
- fprintf(stderr,"attempting to dump kernel log:\n");
- for(i=0;i<KLOG_SIZE;i++){
- fprintf(stderr,"%2d: %s:%s (%s) interrupts:%d\n",i,
- klog[klog_i].routine,
- klog[klog_i].detail,
- klog[klog_i].proc_name,
- klog[klog_i].i);
- klog_i++;
- if(klog_i>=KLOG_SIZE)klog_i = 0;
- }
- fprintf(stderr,"Waiting for dbx attach; my pid is %d\n",getpid());
- fflush(stderr);
- for(;;)sleep(1);
- }
-
- #endif
-
- #ifdef THREADDEBUG
- int
- debug_lwp()
- {
- int num_threads, i, j;
- thread_t vec[256], me;
- statvec_t statvec;
-
- num_threads = lwp_enumerate(vec, 256);
-
- if(lwp_self(&me)< 0){
- lwp_perror("debug_lwp: lwp_self");
- }
-
- printf("debug_lwp:\n");
- for(i=0;i<num_threads;i++){
- if(lwp_getstate(vec[i], &statvec) < 0){
- lwp_perror("debug_lwp: lwp_getstate");
- } else {
- printf("(%3d) ",i);
- if (SAMETHREAD(me, vec[i])) printf("(me) ");
- else printf(" ");
- printf("%8x/%02d %d ",vec[i].thread_id,
- vec[i].thread_key, statvec.stat_prio);
- printf("\n");
- }
-
- }
- return 0;
- }
- #endif
-
- #ifdef STACKDEBUG
- int
- lwp_stackscan()
- {
- register struct proc *pp;
- int i, *j;
-
- for(i=0;i<PHASH;i++){
- for(pp = Waittab[i];pp != NULLPROC;pp = pp->next)
- LWP_TEST(pp,"lwp_stackscan (waitlist)");
- }
-
- for(pp = Rdytab;pp != NULLPROC;pp = pp->next)
- LWP_TEST(pp,"lwp_stackscan (readylist)");
-
- if(Curproc != NULLPROC)LWP_TEST(Curproc,"lwp_stackscan (run)");
-
- return 0;
- }
- #endif
-