xref: /openbsd/usr.sbin/amd/amd/sched.c (revision 26d0c865)
1 /*	$OpenBSD: sched.c,v 1.18 2014/10/26 03:28:41 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 1990 Jan-Simon Pendry
5  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6  * Copyright (c) 1990, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Jan-Simon Pendry at Imperial College, London.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	from: @(#)sched.c	8.1 (Berkeley) 6/6/93
37  *	$Id: sched.c,v 1.18 2014/10/26 03:28:41 guenther Exp $
38  */
39 
40 /*
41  * Process scheduler
42  */
43 
44 #include "am.h"
45 #include <signal.h>
46 #include <sys/wait.h>
47 #include <setjmp.h>
48 extern jmp_buf select_intr;
49 extern int select_intr_valid;
50 
51 typedef struct pjob pjob;
52 struct pjob {
53 	qelem hdr;			/* Linked list */
54 	pid_t pid;			/* Process ID of job */
55 	cb_fun cb_fun;			/* Callback function */
56 	void *cb_closure;		/* Closure for callback */
57 	int w;				/* Status filled in by sigchld */
58 	void *wchan;			/* Wait channel */
59 };
60 
61 extern qelem proc_list_head;
62 qelem proc_list_head = { &proc_list_head, &proc_list_head };
63 extern qelem proc_wait_list;
64 qelem proc_wait_list = { &proc_wait_list, &proc_wait_list };
65 
66 int task_notify_todo;
67 
68 void
ins_que(qelem * elem,qelem * pred)69 ins_que(qelem *elem, qelem *pred)
70 {
71 	qelem *p = pred->q_forw;
72 	elem->q_back = pred;
73 	elem->q_forw = p;
74 	pred->q_forw = elem;
75 	p->q_back = elem;
76 }
77 
78 void
rem_que(qelem * elem)79 rem_que(qelem *elem)
80 {
81 	qelem *p = elem->q_forw;
82 	qelem *p2 = elem->q_back;
83 	p2->q_forw = p;
84 	p->q_back = p2;
85 }
86 
87 static pjob *
sched_job(cb_fun cf,void * ca)88 sched_job(cb_fun cf, void *ca)
89 {
90 	pjob *p = ALLOC(pjob);
91 
92 	p->cb_fun = cf;
93 	p->cb_closure = ca;
94 
95 	/*
96 	 * Now place on wait queue
97 	 */
98 	ins_que(&p->hdr, &proc_wait_list);
99 
100 	return p;
101 }
102 
103 void
run_task(task_fun tf,void * ta,cb_fun cf,void * ca)104 run_task(task_fun tf, void *ta, cb_fun cf, void *ca)
105 {
106 	pjob *p = sched_job(cf, ca);
107 	sigset_t mask, omask;
108 
109 	p->wchan = p;
110 
111 	sigemptyset(&mask);
112 	sigaddset(&mask, SIGCHLD);
113 	sigprocmask(SIG_BLOCK, &mask, &omask);
114 
115 	if ((p->pid = background())) {
116 		sigprocmask(SIG_SETMASK, &omask, NULL);
117 		return;
118 	}
119 
120 	exit((*tf)(ta));
121 	/* firewall... */
122 	abort();
123 }
124 
125 /*
126  * Schedule a task to be run when woken up
127  */
128 void
sched_task(cb_fun cf,void * ca,void * wchan)129 sched_task(cb_fun cf, void *ca, void *wchan)
130 {
131 	/*
132 	 * Allocate a new task
133 	 */
134 	pjob *p = sched_job(cf, ca);
135 #ifdef DEBUG_SLEEP
136 	dlog("SLEEP on %#x", wchan);
137 #endif
138 	p->wchan = wchan;
139 	p->pid = 0;
140 	bzero(&p->w, sizeof(p->w));
141 }
142 
143 static void
wakeupjob(pjob * p)144 wakeupjob(pjob *p)
145 {
146 	rem_que(&p->hdr);
147 	ins_que(&p->hdr, &proc_list_head);
148 	task_notify_todo++;
149 }
150 
151 void
wakeup(void * wchan)152 wakeup(void *wchan)
153 {
154 	pjob *p, *p2;
155 #ifdef DEBUG_SLEEP
156 	int done = 0;
157 #endif
158 	if (!foreground)
159 		return;
160 
161 #ifdef DEBUG_SLEEP
162 	/*dlog("wakeup(%#x)", wchan);*/
163 #endif
164 	/*
165 	 * Can't user ITER() here because
166 	 * wakeupjob() juggles the list.
167 	 */
168 	for (p = FIRST(pjob, &proc_wait_list);
169 			p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
170 			p = p2) {
171 		if (p->wchan == wchan) {
172 #ifdef DEBUG_SLEEP
173 			done = 1;
174 #endif
175 			wakeupjob(p);
176 		}
177 	}
178 
179 #ifdef DEBUG_SLEEP
180 	if (!done)
181 		dlog("Nothing SLEEPing on %#x", wchan);
182 #endif
183 }
184 
185 void
wakeup_task(int rc,int term,void * cl)186 wakeup_task(int rc, int term, void *cl)
187 {
188 	wakeup(cl);
189 }
190 
191 
192 void
sigchld(int sig)193 sigchld(int sig)
194 {
195 	int w;
196 	int save_errno = errno;
197 	pid_t pid;
198 
199 	while ((pid = waitpid((pid_t)-1, &w, WNOHANG)) > 0) {
200 		pjob *p, *p2;
201 
202 		if (WIFSIGNALED(w))
203 			plog(XLOG_ERROR, "Process %ld exited with signal %d",
204 				(long)pid, WTERMSIG(w));
205 #ifdef DEBUG
206 		else
207 			dlog("Process %ld exited with status %d",
208 				(long)pid, WEXITSTATUS(w));
209 #endif /* DEBUG */
210 
211 		for (p = FIRST(pjob, &proc_wait_list);
212 		     p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
213 		     p = p2) {
214 			if (p->pid == pid) {
215 				p->w = w;
216 				wakeupjob(p);
217 				break;
218 			}
219 		}
220 
221 #ifdef DEBUG
222 		if (p == NULL)
223 			dlog("can't locate task block for pid %ld", (long)pid);
224 #endif /* DEBUG */
225 	}
226 
227 	if (select_intr_valid)
228 		longjmp(select_intr, sig);
229 	errno = save_errno;
230 }
231 
232 /*
233  * Run any pending tasks.
234  * This must be called with SIGCHLD disabled
235  */
236 void
do_task_notify(void)237 do_task_notify(void)
238 {
239 	/*
240 	 * Keep taking the first item off the list and processing it.
241 	 *
242 	 * Done this way because the callback can, quite reasonably,
243 	 * queue a new task, so no local reference into the list can be
244 	 * held here.
245 	 */
246 	while (FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
247 		pjob *p = FIRST(pjob, &proc_list_head);
248 		rem_que(&p->hdr);
249 		/*
250 		 * This job has completed
251 		 */
252 		--task_notify_todo;
253 
254 		/*
255 		 * Do callback if it exists
256 		 */
257 		if (p->cb_fun)
258 			(*p->cb_fun)(WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0,
259 				WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0,
260 				p->cb_closure);
261 
262 		free(p);
263 	}
264 }
265