xref: /csrg-svn/sys/kern/kern_synch.c (revision 7698)
1 /*	kern_synch.c	4.18	82/08/10	*/
2 
3 #include "../h/param.h"
4 #include "../h/systm.h"
5 #include "../h/dir.h"
6 #include "../h/user.h"
7 #include "../h/proc.h"
8 #include "../h/file.h"
9 #include "../h/inode.h"
10 #include "../h/vm.h"
11 #include "../h/pte.h"
12 #include "../h/inline.h"
13 #include "../h/mtpr.h"
14 #include "../h/quota.h"
15 
16 #define SQSIZE 0100	/* Must be power of 2 */
17 #define HASH(x)	(( (int) x >> 5) & (SQSIZE-1))
18 struct proc *slpque[SQSIZE];
19 
20 /*
21  * Give up the processor till a wakeup occurs
22  * on chan, at which time the process
23  * enters the scheduling queue at priority pri.
24  * The most important effect of pri is that when
25  * pri<=PZERO a signal cannot disturb the sleep;
26  * if pri>PZERO signals will be processed.
27  * Callers of this routine must be prepared for
28  * premature return, and check that the reason for
29  * sleeping has gone away.
30  */
31 sleep(chan, pri)
32 caddr_t chan;
33 {
34 	register struct proc *rp, **hp;
35 	register s;
36 
37 	rp = u.u_procp;
38 	s = spl6();
39 	if (chan==0 || rp->p_stat != SRUN || rp->p_rlink)
40 		panic("sleep");
41 	rp->p_wchan = chan;
42 	rp->p_slptime = 0;
43 	rp->p_pri = pri;
44 	hp = &slpque[HASH(chan)];
45 	rp->p_link = *hp;
46 	*hp = rp;
47 	if (pri > PZERO) {
48 		if (ISSIG(rp)) {
49 			if (rp->p_wchan)
50 				unsleep(rp);
51 			rp->p_stat = SRUN;
52 			(void) spl0();
53 			goto psig;
54 		}
55 		if (rp->p_wchan == 0)
56 			goto out;
57 		rp->p_stat = SSLEEP;
58 		(void) spl0();
59 		swtch();
60 		if (ISSIG(rp))
61 			goto psig;
62 	} else {
63 		rp->p_stat = SSLEEP;
64 		(void) spl0();
65 		swtch();
66 	}
67 out:
68 	splx(s);
69 	return;
70 
71 	/*
72 	 * If priority was low (>PZERO) and
73 	 * there has been a signal, execute non-local goto through
74 	 * u.u_qsav, aborting the system call in progress (see trap.c)
75 	 * (or finishing a tsleep, see below)
76 	 */
77 psig:
78 	longjmp(u.u_qsav);
79 	/*NOTREACHED*/
80 }
81 
82 /*
83  * Sleep on chan at pri.
84  * Return in no more than the indicated number of seconds.
85  * (If seconds==0, no timeout implied)
86  * Return	TS_OK if chan was awakened normally
87  *		TS_TIME if timeout occurred
88  *		TS_SIG if asynchronous signal occurred
89  *
90  * SHOULD HAVE OPTION TO SLEEP TO ABSOLUTE TIME OR AN
91  * INCREMENT IN MILLISECONDS!
92  */
93 tsleep(chan, pri, seconds)
94 	caddr_t chan;
95 	int pri, seconds;
96 {
97 	label_t lqsav;
98 	register struct proc *pp;
99 	register sec, n, rval, sov;
100 
101 	pp = u.u_procp;
102 	n = spl7();
103 	sec = 0;
104 	rval = 0;
105 	sov = 0;
106 	if (pp->p_clktim && pp->p_clktim<seconds)
107 		if (pri > PZERO)
108 			seconds = 0;
109 		else
110 			sov++;
111 	if (seconds) {
112 		pp->p_flag |= STIMO;
113 		sec = pp->p_clktim-seconds;
114 		pp->p_clktim = seconds;
115 	}
116 	bcopy((caddr_t)u.u_qsav, (caddr_t)lqsav, sizeof (label_t));
117 	if (setjmp(u.u_qsav))
118 		rval = TS_SIG;
119 	else {
120 		sleep(chan, pri);
121 		if ((pp->p_flag&STIMO)==0 && seconds)
122 			rval = TS_TIME;
123 		else
124 			rval = TS_OK;
125 	}
126 	pp->p_flag &= ~STIMO;
127 	bcopy((caddr_t)lqsav, (caddr_t)u.u_qsav, sizeof (label_t));
128 	if (sec > 0)
129 		pp->p_clktim += sec;
130 	else if (sov) {
131 		if ((pp->p_clktim += sec) <= 0) {
132 			pp->p_clktim = 0;
133 			psignal(pp, SIGALRM);
134 		}
135 	} else
136 		pp->p_clktim = 0;
137 	splx(n);
138 	return (rval);
139 }
140 
141 /*
142  * Remove a process from its wait queue
143  */
144 unsleep(p)
145 	register struct proc *p;
146 {
147 	register struct proc **hp;
148 	register s;
149 
150 	s = spl6();
151 	if (p->p_wchan) {
152 		hp = &slpque[HASH(p->p_wchan)];
153 		while (*hp != p)
154 			hp = &(*hp)->p_link;
155 		*hp = p->p_link;
156 		p->p_wchan = 0;
157 	}
158 	splx(s);
159 }
160 
161 /*
162  * Wake up all processes sleeping on chan.
163  */
164 wakeup(chan)
165 	register caddr_t chan;
166 {
167 	register struct proc *p, **q, **h;
168 	int s;
169 
170 	s = spl6();
171 	h = &slpque[HASH(chan)];
172 restart:
173 	for (q = h; p = *q; ) {
174 		if (p->p_rlink || p->p_stat != SSLEEP && p->p_stat != SSTOP)
175 			panic("wakeup");
176 		if (p->p_wchan==chan) {
177 			p->p_wchan = 0;
178 			*q = p->p_link;
179 			p->p_slptime = 0;
180 			if (p->p_stat == SSLEEP) {
181 				/* OPTIMIZED INLINE EXPANSION OF setrun(p) */
182 				p->p_stat = SRUN;
183 				if (p->p_flag & SLOAD)
184 					setrq(p);
185 				if (p->p_pri < curpri) {
186 					runrun++;
187 					aston();
188 				}
189 				if ((p->p_flag&SLOAD) == 0) {
190 					if (runout != 0) {
191 						runout = 0;
192 						wakeup((caddr_t)&runout);
193 					}
194 					wantin++;
195 				}
196 				/* END INLINE EXPANSION */
197 				goto restart;
198 			}
199 		} else
200 			q = &p->p_link;
201 	}
202 	splx(s);
203 }
204 
205 /*
206  * Initialize the (doubly-linked) run queues
207  * to be empty.
208  */
209 rqinit()
210 {
211 	register int i;
212 
213 	for (i = 0; i < NQS; i++)
214 		qs[i].ph_link = qs[i].ph_rlink = (struct proc *)&qs[i];
215 }
216 
217 /*
218  * Set the process running;
219  * arrange for it to be swapped in if necessary.
220  */
221 setrun(p)
222 	register struct proc *p;
223 {
224 	register int s;
225 
226 	s = spl6();
227 	switch (p->p_stat) {
228 
229 	case 0:
230 	case SWAIT:
231 	case SRUN:
232 	case SZOMB:
233 	default:
234 		panic("setrun");
235 
236 	case SSTOP:
237 	case SSLEEP:
238 		unsleep(p);		/* e.g. when sending signals */
239 		break;
240 
241 	case SIDL:
242 		break;
243 	}
244 	p->p_stat = SRUN;
245 	if (p->p_flag & SLOAD)
246 		setrq(p);
247 	splx(s);
248 	if (p->p_pri < curpri) {
249 		runrun++;
250 		aston();
251 	}
252 	if ((p->p_flag&SLOAD) == 0) {
253 		if (runout != 0) {
254 			runout = 0;
255 			wakeup((caddr_t)&runout);
256 		}
257 		wantin++;
258 	}
259 }
260 
261 /*
262  * Set user priority.
263  * The rescheduling flag (runrun)
264  * is set if the priority is better
265  * than the currently running process.
266  */
267 setpri(pp)
268 	register struct proc *pp;
269 {
270 	register int p;
271 
272 	p = (pp->p_cpu & 0377)/4;
273 	p += PUSER + 2*(pp->p_nice - NZERO);
274 	if (pp->p_rssize > pp->p_maxrss && freemem < desfree)
275 		p += 2*4;	/* effectively, nice(4) */
276 	if (p > 127)
277 		p = 127;
278 	if (p < curpri) {
279 		runrun++;
280 		aston();
281 	}
282 	pp->p_usrpri = p;
283 	return (p);
284 }
285 
286 /*
287  * Create a new process-- the internal version of
288  * sys fork.
289  * It returns 1 in the new process, 0 in the old.
290  */
291 newproc(isvfork)
292 	int isvfork;
293 {
294 	register struct proc *p;
295 	register struct proc *rpp, *rip;
296 	register int n;
297 	register struct file *fp;
298 
299 	p = NULL;
300 	/*
301 	 * First, just locate a slot for a process
302 	 * and copy the useful info from this process into it.
303 	 * The panic "cannot happen" because fork has already
304 	 * checked for the existence of a slot.
305 	 */
306 retry:
307 	mpid++;
308 	if (mpid >= 30000) {
309 		mpid = 0;
310 		goto retry;
311 	}
312 	for (rpp = proc; rpp < procNPROC; rpp++) {
313 		if (rpp->p_stat == NULL && p==NULL)
314 			p = rpp;
315 		if (rpp->p_pid==mpid || rpp->p_pgrp==mpid)
316 			goto retry;
317 	}
318 	if ((rpp = p) == NULL)
319 		panic("no procs");
320 
321 	/*
322 	 * Make a proc table entry for the new process.
323 	 */
324 	rip = u.u_procp;
325 #ifdef QUOTA
326 	(rpp->p_quota = rip->p_quota)->q_cnt++;
327 #endif
328 	rpp->p_stat = SIDL;
329 	rpp->p_clktim = 0;
330 	rpp->p_flag = SLOAD | (rip->p_flag & (SPAGI|SNUSIG));
331 	if (isvfork) {
332 		rpp->p_flag |= SVFORK;
333 		rpp->p_ndx = rip->p_ndx;
334 	} else
335 		rpp->p_ndx = rpp - proc;
336 	rpp->p_uid = rip->p_uid;
337 	rpp->p_pgrp = rip->p_pgrp;
338 	rpp->p_nice = rip->p_nice;
339 	rpp->p_textp = isvfork ? 0 : rip->p_textp;
340 	rpp->p_pid = mpid;
341 	rpp->p_ppid = rip->p_pid;
342 	rpp->p_pptr = rip;
343 	rpp->p_osptr = rip->p_cptr;
344 	if (rip->p_cptr)
345 		rip->p_cptr->p_ysptr = rpp;
346 	rpp->p_ysptr = NULL;
347 	rpp->p_cptr = NULL;
348 	rip->p_cptr = rpp;
349 	rpp->p_time = 0;
350 	rpp->p_cpu = 0;
351 	rpp->p_siga0 = rip->p_siga0;
352 	rpp->p_siga1 = rip->p_siga1;
353 	/* take along any pending signals, like stops? */
354 	if (isvfork) {
355 		rpp->p_tsize = rpp->p_dsize = rpp->p_ssize = 0;
356 		rpp->p_szpt = clrnd(ctopt(UPAGES));
357 		forkstat.cntvfork++;
358 		forkstat.sizvfork += rip->p_dsize + rip->p_ssize;
359 	} else {
360 		rpp->p_tsize = rip->p_tsize;
361 		rpp->p_dsize = rip->p_dsize;
362 		rpp->p_ssize = rip->p_ssize;
363 		rpp->p_szpt = rip->p_szpt;
364 		forkstat.cntfork++;
365 		forkstat.sizfork += rip->p_dsize + rip->p_ssize;
366 	}
367 	rpp->p_rssize = 0;
368 	rpp->p_maxrss = rip->p_maxrss;
369 	rpp->p_wchan = 0;
370 	rpp->p_slptime = 0;
371 	rpp->p_pctcpu = 0;
372 	rpp->p_cpticks = 0;
373 	n = PIDHASH(rpp->p_pid);
374 	p->p_idhash = pidhash[n];
375 	pidhash[n] = rpp - proc;
376 	multprog++;
377 
378 	/*
379 	 * Increase reference counts on shared objects.
380 	 */
381 	for (n = 0; n < NOFILE; n++) {
382 		fp = u.u_ofile[n];
383 		if (fp == NULL)
384 			continue;
385 		fp->f_count++;
386 		if (u.u_pofile[n]&RDLOCK)
387 			fp->f_inode->i_rdlockc++;
388 		if (u.u_pofile[n]&WRLOCK)
389 			fp->f_inode->i_wrlockc++;
390 	}
391 	u.u_cdir->i_count++;
392 	if (u.u_rdir)
393 		u.u_rdir->i_count++;
394 
395 	/*
396 	 * Partially simulate the environment
397 	 * of the new process so that when it is actually
398 	 * created (by copying) it will look right.
399 	 * This begins the section where we must prevent the parent
400 	 * from being swapped.
401 	 */
402 	rip->p_flag |= SKEEP;
403 	if (procdup(rpp, isvfork))
404 		return (1);
405 
406 	/*
407 	 * Make child runnable and add to run queue.
408 	 */
409 	(void) spl6();
410 	rpp->p_stat = SRUN;
411 	setrq(rpp);
412 	(void) spl0();
413 
414 	/*
415 	 * Cause child to take a non-local goto as soon as it runs.
416 	 * On older systems this was done with SSWAP bit in proc
417 	 * table; on VAX we use u.u_pcb.pcb_sswap so don't need
418 	 * to do rpp->p_flag |= SSWAP.  Actually do nothing here.
419 	 */
420 	/* rpp->p_flag |= SSWAP; */
421 
422 	/*
423 	 * Now can be swapped.
424 	 */
425 	rip->p_flag &= ~SKEEP;
426 
427 	/*
428 	 * If vfork make chain from parent process to child
429 	 * (where virtal memory is temporarily).  Wait for
430 	 * child to finish, steal virtual memory back,
431 	 * and wakeup child to let it die.
432 	 */
433 	if (isvfork) {
434 		u.u_procp->p_xlink = rpp;
435 		u.u_procp->p_flag |= SNOVM;
436 		while (rpp->p_flag & SVFORK)
437 			sleep((caddr_t)rpp, PZERO - 1);
438 		if ((rpp->p_flag & SLOAD) == 0)
439 			panic("newproc vfork");
440 		uaccess(rpp, Vfmap, &vfutl);
441 		u.u_procp->p_xlink = 0;
442 		vpassvm(rpp, u.u_procp, &vfutl, &u, Vfmap);
443 		u.u_procp->p_flag &= ~SNOVM;
444 		rpp->p_ndx = rpp - proc;
445 		rpp->p_flag |= SVFDONE;
446 		wakeup((caddr_t)rpp);
447 	}
448 
449 	/*
450 	 * 0 return means parent.
451 	 */
452 	return (0);
453 }
454