xref: /netbsd-src/external/bsd/am-utils/dist/amd/sched.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
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
ins_que(qelem * elem,qelem * pred)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
rem_que(qelem * elem)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 *
sched_job(cb_fun * cf,opaque_t ca)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
run_task(task_fun * tf,opaque_t ta,cb_fun * cf,opaque_t ca)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
sched_task(cb_fun * cf,opaque_t ca,wchan_t wchan)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
wakeupjob(pjob * p)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
wakeup(wchan_t wchan)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
wakeup_task(int rc,int term,wchan_t wchan)201 wakeup_task(int rc, int term, wchan_t wchan)
202 {
203   wakeup(wchan);
204 }
205 
206 
207 wchan_t
get_mntfs_wchan(mntfs * mf)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
do_task_notify(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
sigchld(int sig)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