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