Home | History | Annotate | Line # | Download | only in amd
      1 /*	$NetBSD: sched.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2014 Erez Zadok
      5  * Copyright (c) 1990 Jan-Simon Pendry
      6  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
      7  * Copyright (c) 1990 The Regents of the University of California.
      8  * All rights reserved.
      9  *
     10  * This code is derived from software contributed to Berkeley by
     11  * Jan-Simon Pendry at Imperial College, London.
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     16  * 1. Redistributions of source code must retain the above copyright
     17  *    notice, this list of conditions and the following disclaimer.
     18  * 2. Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  * 3. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  *
     37  *
     38  * File: am-utils/amd/sched.c
     39  *
     40  */
     41 
     42 /*
     43  * Process scheduler
     44  */
     45 
     46 #ifdef HAVE_CONFIG_H
     47 # include <config.h>
     48 #endif /* HAVE_CONFIG_H */
     49 #include <am_defs.h>
     50 #include <amd.h>
     51 
     52 
     53 typedef struct pjob pjob;
     54 
     55 struct pjob {
     56   qelem hdr;		/* Linked list */
     57   int pid;		/* Process ID of job */
     58   cb_fun *cb_fun;	/* Callback function */
     59   opaque_t cb_arg;	/* Argument for callback */
     60   int w;		/* everyone these days uses int, not a "union wait" */
     61   wchan_t wchan;	/* Wait channel */
     62 };
     63 
     64 /* globals */
     65 qelem proc_list_head = {&proc_list_head, &proc_list_head};
     66 qelem proc_wait_list = {&proc_wait_list, &proc_wait_list};
     67 int task_notify_todo;
     68 
     69 
     70 void
     71 ins_que(qelem *elem, qelem *pred)
     72 {
     73   qelem *p = pred->q_forw;
     74 
     75   elem->q_back = pred;
     76   elem->q_forw = p;
     77   pred->q_forw = elem;
     78   p->q_back = elem;
     79 }
     80 
     81 
     82 void
     83 rem_que(qelem *elem)
     84 {
     85   qelem *p = elem->q_forw;
     86   qelem *p2 = elem->q_back;
     87 
     88   p2->q_forw = p;
     89   p->q_back = p2;
     90 }
     91 
     92 
     93 static pjob *
     94 sched_job(cb_fun *cf, opaque_t ca)
     95 {
     96   pjob *p = ALLOC(struct pjob);
     97 
     98   p->cb_fun = cf;
     99   p->cb_arg = ca;
    100 
    101   /*
    102    * Now place on wait queue
    103    */
    104   ins_que(&p->hdr, &proc_wait_list);
    105 
    106   return p;
    107 }
    108 
    109 
    110 /*
    111  * tf: The task to execute (ta is its arguments)
    112  * cf: Continuation function (ca is its arguments)
    113  */
    114 void
    115 run_task(task_fun *tf, opaque_t ta, cb_fun *cf, opaque_t ca)
    116 {
    117   pjob *p = sched_job(cf, ca);
    118 #ifdef HAVE_SIGACTION
    119   sigset_t new, mask;
    120 #else /* not HAVE_SIGACTION */
    121   int mask;
    122 #endif /* not HAVE_SIGACTION */
    123 
    124   p->wchan = (wchan_t) p;
    125 
    126 #ifdef HAVE_SIGACTION
    127   sigemptyset(&new);		/* initialize signal set we wish to block */
    128   sigaddset(&new, SIGCHLD);	/* only block on SIGCHLD */
    129   sigprocmask(SIG_BLOCK, &new, &mask);
    130 #else /* not HAVE_SIGACTION */
    131   mask = sigblock(sigmask(SIGCHLD));
    132 #endif /* not HAVE_SIGACTION */
    133 
    134   if ((p->pid = background())) {
    135 #ifdef HAVE_SIGACTION
    136     sigprocmask(SIG_SETMASK, &mask, NULL);
    137 #else /* not HAVE_SIGACTION */
    138     sigsetmask(mask);
    139 #endif /* not HAVE_SIGACTION */
    140     return;
    141   }
    142 
    143   /* child code runs here, parent has returned to caller */
    144 
    145   exit((*tf) (ta));
    146   /* firewall... */
    147   abort();
    148 }
    149 
    150 
    151 /*
    152  * Schedule a task to be run when woken up
    153  */
    154 void
    155 sched_task(cb_fun *cf, opaque_t ca, wchan_t wchan)
    156 {
    157   /*
    158    * Allocate a new task
    159    */
    160   pjob *p = sched_job(cf, ca);
    161 
    162   dlog("SLEEP on %p", wchan);
    163   p->wchan = wchan;
    164   p->pid = 0;
    165   p->w = 0;			/* was memset (when ->w was union) */
    166 }
    167 
    168 
    169 static void
    170 wakeupjob(pjob *p)
    171 {
    172   rem_que(&p->hdr);
    173   ins_que(&p->hdr, &proc_list_head);
    174   task_notify_todo++;
    175 }
    176 
    177 
    178 void
    179 wakeup(wchan_t wchan)
    180 {
    181   pjob *p, *p2;
    182 
    183   if (!foreground)
    184     return;
    185 
    186   /*
    187    * Can't use ITER() here because
    188    * wakeupjob() juggles the list.
    189    */
    190   for (p = AM_FIRST(pjob, &proc_wait_list);
    191        p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
    192        p = p2) {
    193     if (p->wchan == wchan) {
    194       wakeupjob(p);
    195     }
    196   }
    197 }
    198 
    199 
    200 void
    201 wakeup_task(int rc, int term, wchan_t wchan)
    202 {
    203   wakeup(wchan);
    204 }
    205 
    206 
    207 wchan_t
    208 get_mntfs_wchan(mntfs *mf)
    209 {
    210   if (mf &&
    211       mf->mf_ops &&
    212       mf->mf_ops->get_wchan)
    213     return mf->mf_ops->get_wchan(mf);
    214   return mf;
    215 }
    216 
    217 
    218 /*
    219  * Run any pending tasks.
    220  * This must be called with SIGCHLD disabled
    221  */
    222 void
    223 do_task_notify(void)
    224 {
    225   /*
    226    * Keep taking the first item off the list and processing it.
    227    *
    228    * Done this way because the callback can, quite reasonably,
    229    * queue a new task, so no local reference into the list can be
    230    * held here.
    231    */
    232   while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
    233     pjob *p = AM_FIRST(pjob, &proc_list_head);
    234     rem_que(&p->hdr);
    235     /*
    236      * This job has completed
    237      */
    238     --task_notify_todo;
    239 
    240     /*
    241      * Do callback if it exists
    242      */
    243     if (p->cb_fun) {
    244       /* these two trigraphs will ensure compatibility with strict POSIX.1 */
    245       p->cb_fun(WIFEXITED(p->w)   ? WEXITSTATUS(p->w) : 0,
    246 		WIFSIGNALED(p->w) ? WTERMSIG(p->w)    : 0,
    247 		p->cb_arg);
    248     }
    249     XFREE(p);
    250   }
    251 }
    252 
    253 
    254 RETSIGTYPE
    255 sigchld(int sig)
    256 {
    257   int w;	/* everyone these days uses int, not a "union wait" */
    258   int pid;
    259 
    260 #ifdef HAVE_WAITPID
    261   while ((pid = waitpid((pid_t) -1,  &w, WNOHANG)) > 0) {
    262 #else /* not HAVE_WAITPID */
    263   while ((pid = wait3( &w, WNOHANG, (struct rusage *) NULL)) > 0) {
    264 #endif /* not HAVE_WAITPID */
    265     pjob *p, *p2;
    266 
    267     if (WIFSIGNALED(w))
    268       plog(XLOG_ERROR, "Process %d exited with signal %d",
    269 	   pid, WTERMSIG(w));
    270     else
    271       dlog("Process %d exited with status %d",
    272 	   pid, WEXITSTATUS(w));
    273 
    274     for (p = AM_FIRST(pjob, &proc_wait_list);
    275 	 p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
    276 	 p = p2) {
    277       if (p->pid == pid) {
    278 	p->w = w;
    279 	wakeupjob(p);
    280 	break;
    281       }
    282     } /* end of for loop */
    283 
    284     if (p == HEAD(pjob, &proc_wait_list))
    285       dlog("can't locate task block for pid %d", pid);
    286 
    287     /*
    288      * Must count down children inside the while loop, otherwise we won't
    289      * count them all, and NumChildren (and later backoff) will be set
    290      * incorrectly. SH/RUNIT 940519.
    291      */
    292     if (--NumChildren < 0)
    293       NumChildren = 0;
    294   } /* end of "while wait..." loop */
    295 
    296 #ifdef REINSTALL_SIGNAL_HANDLER
    297   signal(sig, sigchld);
    298 #endif /* REINSTALL_SIGNAL_HANDLER */
    299 
    300   if (select_intr_valid)
    301     longjmp(select_intr, sig);
    302 }
    303