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