]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sysproc.c
kernel: sysrfork abortion
[plan9front.git] / sys / src / 9 / port / sysproc.c
1 #include        "u.h"
2 #include        "tos.h"
3 #include        "../port/lib.h"
4 #include        "mem.h"
5 #include        "dat.h"
6 #include        "fns.h"
7 #include        "../port/error.h"
8 #include        "edf.h"
9
10 #include        <a.out.h>
11
12 int     shargs(char*, int, char**);
13
14 extern void checkpages(void);
15 extern void checkpagerefs(void);
16
17 long
18 sysr1(ulong*)
19 {
20         checkpagerefs();
21         return 0;
22 }
23
24 static void
25 abortion(void*)
26 {
27         pexit("fork aborted", 1);
28 }
29
30 long
31 sysrfork(ulong *arg)
32 {
33         Proc *p;
34         int n, i;
35         Fgrp *ofg;
36         Pgrp *opg;
37         Rgrp *org;
38         Egrp *oeg;
39         ulong pid, flag;
40         Mach *wm;
41
42         flag = arg[0];
43         /* Check flags before we commit */
44         if((flag & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG))
45                 error(Ebadarg);
46         if((flag & (RFNAMEG|RFCNAMEG)) == (RFNAMEG|RFCNAMEG))
47                 error(Ebadarg);
48         if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG))
49                 error(Ebadarg);
50
51         if((flag&RFPROC) == 0) {
52                 if(flag & (RFMEM|RFNOWAIT))
53                         error(Ebadarg);
54                 if(flag & (RFFDG|RFCFDG)) {
55                         ofg = up->fgrp;
56                         if(flag & RFFDG)
57                                 up->fgrp = dupfgrp(ofg);
58                         else
59                                 up->fgrp = dupfgrp(nil);
60                         closefgrp(ofg);
61                 }
62                 if(flag & (RFNAMEG|RFCNAMEG)) {
63                         opg = up->pgrp;
64                         up->pgrp = newpgrp();
65                         if(flag & RFNAMEG)
66                                 pgrpcpy(up->pgrp, opg);
67                         /* inherit noattach */
68                         up->pgrp->noattach = opg->noattach;
69                         closepgrp(opg);
70                 }
71                 if(flag & RFNOMNT)
72                         up->pgrp->noattach = 1;
73                 if(flag & RFREND) {
74                         org = up->rgrp;
75                         up->rgrp = newrgrp();
76                         closergrp(org);
77                 }
78                 if(flag & (RFENVG|RFCENVG)) {
79                         oeg = up->egrp;
80                         up->egrp = smalloc(sizeof(Egrp));
81                         up->egrp->ref = 1;
82                         if(flag & RFENVG)
83                                 envcpy(up->egrp, oeg);
84                         closeegrp(oeg);
85                 }
86                 if(flag & RFNOTEG)
87                         up->noteid = pidalloc(0);
88                 return 0;
89         }
90
91         p = newproc();
92
93         p->scallnr = up->scallnr;
94         p->s = up->s;
95         p->nerrlab = 0;
96         p->slash = up->slash;
97         p->dot = up->dot;
98         incref(p->dot);
99
100         memmove(p->note, up->note, sizeof(p->note));
101         p->privatemem = up->privatemem;
102         p->noswap = up->noswap;
103         p->nnote = up->nnote;
104         p->notified = 0;
105         p->lastnote = up->lastnote;
106         p->notify = up->notify;
107         p->ureg = up->ureg;
108         p->dbgreg = 0;
109
110         /* Abort the child process on error */
111         if(waserror()){
112                 p->kp = 1;
113                 kprocchild(p, abortion, 0);
114                 ready(p);
115                 nexterror();
116         }
117
118         /* Make a new set of memory segments */
119         n = flag & RFMEM;
120         qlock(&p->seglock);
121         if(waserror()){
122                 qunlock(&p->seglock);
123                 nexterror();
124         }
125         for(i = 0; i < NSEG; i++)
126                 if(up->seg[i])
127                         p->seg[i] = dupseg(up->seg, i, n);
128         qunlock(&p->seglock);
129         poperror();
130
131         /* File descriptors */
132         if(flag & (RFFDG|RFCFDG)) {
133                 if(flag & RFFDG)
134                         p->fgrp = dupfgrp(up->fgrp);
135                 else
136                         p->fgrp = dupfgrp(nil);
137         }
138         else {
139                 p->fgrp = up->fgrp;
140                 incref(p->fgrp);
141         }
142
143         /* Process groups */
144         if(flag & (RFNAMEG|RFCNAMEG)) {
145                 p->pgrp = newpgrp();
146                 if(flag & RFNAMEG)
147                         pgrpcpy(p->pgrp, up->pgrp);
148                 /* inherit noattach */
149                 p->pgrp->noattach = up->pgrp->noattach;
150         }
151         else {
152                 p->pgrp = up->pgrp;
153                 incref(p->pgrp);
154         }
155         if(flag & RFNOMNT)
156                 p->pgrp->noattach = 1;
157
158         if(flag & RFREND)
159                 p->rgrp = newrgrp();
160         else {
161                 incref(up->rgrp);
162                 p->rgrp = up->rgrp;
163         }
164
165         /* Environment group */
166         if(flag & (RFENVG|RFCENVG)) {
167                 p->egrp = smalloc(sizeof(Egrp));
168                 p->egrp->ref = 1;
169                 if(flag & RFENVG)
170                         envcpy(p->egrp, up->egrp);
171         }
172         else {
173                 p->egrp = up->egrp;
174                 incref(p->egrp);
175         }
176         p->hang = up->hang;
177         p->procmode = up->procmode;
178         if(up->procctl == Proc_tracesyscall)
179                 p->procctl = Proc_tracesyscall;
180
181         poperror();     /* abortion */
182
183         /* Craft a return frame which will cause the child to pop out of
184          * the scheduler in user mode with the return register zero
185          */
186         forkchild(p, up->dbgreg);
187
188         p->parent = up;
189         if((flag&RFNOWAIT) == 0){
190                 p->parentpid = up->pid;
191                 lock(&up->exl);
192                 up->nchild++;
193                 unlock(&up->exl);
194         }
195         if((flag&RFNOTEG) == 0)
196                 p->noteid = up->noteid;
197
198         pid = p->pid;
199         memset(p->time, 0, sizeof(p->time));
200         p->time[TReal] = MACHP(0)->ticks;
201
202         kstrdup(&p->text, up->text);
203         kstrdup(&p->user, up->user);
204
205         procfork(p);
206
207         /*
208          *  since the bss/data segments are now shareable,
209          *  any mmu info about this process is now stale
210          *  (i.e. has bad properties) and has to be discarded.
211          */
212         flushmmu();
213         p->basepri = up->basepri;
214         p->priority = up->basepri;
215         p->fixedpri = up->fixedpri;
216         p->mp = up->mp;
217         wm = up->wired;
218         if(wm)
219                 procwired(p, wm->machno);
220         ready(p);
221         sched();
222         return pid;
223 }
224
225 static ulong
226 l2be(long l)
227 {
228         uchar *cp;
229
230         cp = (uchar*)&l;
231         return (cp[0]<<24) | (cp[1]<<16) | (cp[2]<<8) | cp[3];
232 }
233
234 long
235 sysexec(ulong *arg)
236 {
237         Segment *s, *ts;
238         ulong t, d, b;
239         int i;
240         Chan *tc;
241         char **argv, **argp;
242         char *a, *charp, *args, *file, *file0;
243         char *progarg[sizeof(Exec)/2+1], *elem, progelem[64];
244         ulong ssize, tstk, nargs, nbytes, n, bssend;
245         int indir;
246         Exec exec;
247         char line[sizeof(Exec)];
248         Fgrp *f;
249         Image *img;
250         ulong magic, text, entry, data, bss;
251         Tos *tos;
252
253         indir = 0;
254         elem = nil;
255         validaddr(arg[0], 1, 0);
256         file0 = validnamedup((char*)arg[0], 1);
257         if(waserror()){
258                 free(file0);
259                 if(elem != up->text)
260                         free(elem);
261                 /* Disaster after commit */
262                 if(!up->seg[SSEG])
263                         pexit(up->errstr, 1);
264                 nexterror();
265         }
266         file = file0;
267         for(;;){
268                 tc = namec(file, Aopen, OEXEC, 0);
269                 if(waserror()){
270                         cclose(tc);
271                         nexterror();
272                 }
273                 if(!indir)
274                         kstrdup(&elem, up->genbuf);
275
276                 n = devtab[tc->type]->read(tc, &exec, sizeof(Exec), 0);
277                 if(n < 2)
278                         error(Ebadexec);
279                 magic = l2be(exec.magic);
280                 text = l2be(exec.text);
281                 entry = l2be(exec.entry);
282                 if(n==sizeof(Exec) && (magic == AOUT_MAGIC)){
283                         if(text >= USTKTOP-UTZERO
284                         || entry < UTZERO+sizeof(Exec)
285                         || entry >= UTZERO+sizeof(Exec)+text)
286                                 error(Ebadexec);
287                         break; /* for binary */
288                 }
289
290                 /*
291                  * Process #! /bin/sh args ...
292                  */
293                 memmove(line, &exec, sizeof(Exec));
294                 if(indir || line[0]!='#' || line[1]!='!')
295                         error(Ebadexec);
296                 n = shargs(line, n, progarg);
297                 if(n == 0)
298                         error(Ebadexec);
299                 indir = 1;
300                 /*
301                  * First arg becomes complete file name
302                  */
303                 progarg[n++] = file;
304                 progarg[n] = 0;
305                 validaddr(arg[1], BY2WD, 1);
306                 arg[1] += BY2WD;
307                 file = progarg[0];
308                 if(strlen(elem) >= sizeof progelem)
309                         error(Ebadexec);
310                 strcpy(progelem, elem);
311                 progarg[0] = progelem;
312                 poperror();
313                 cclose(tc);
314         }
315
316         data = l2be(exec.data);
317         bss = l2be(exec.bss);
318         t = (UTZERO+sizeof(Exec)+text+(BY2PG-1)) & ~(BY2PG-1);
319         d = (t + data + (BY2PG-1)) & ~(BY2PG-1);
320         bssend = t + data + bss;
321         b = (bssend + (BY2PG-1)) & ~(BY2PG-1);
322         if(t >= KZERO || d >= KZERO || b >= KZERO)
323                 error(Ebadexec);
324
325         /*
326          * Args: pass 1: count
327          */
328         nbytes = sizeof(Tos);           /* hole for profiling clock at top of stack (and more) */
329         nargs = 0;
330         if(indir){
331                 argp = progarg;
332                 while(*argp){
333                         a = *argp++;
334                         nbytes += strlen(a) + 1;
335                         nargs++;
336                 }
337         }
338         evenaddr(arg[1]);
339         argp = (char**)arg[1];
340         validaddr((ulong)argp, BY2WD, 0);
341         while(*argp){
342                 a = *argp++;
343                 if(((ulong)argp&(BY2PG-1)) < BY2WD)
344                         validaddr((ulong)argp, BY2WD, 0);
345                 validaddr((ulong)a, 1, 0);
346                 nbytes += ((char*)vmemchr(a, 0, 0x7FFFFFFF) - a) + 1;
347                 nargs++;
348         }
349         ssize = BY2WD*(nargs+1) + ((nbytes+(BY2WD-1)) & ~(BY2WD-1));
350
351         /*
352          * 8-byte align SP for those (e.g. sparc) that need it.
353          * execregs() will subtract another 4 bytes for argc.
354          */
355         if((ssize+4) & 7)
356                 ssize += 4;
357
358         if(PGROUND(ssize) >= USTKSIZE)
359                 error(Enovmem);
360
361         /*
362          * Build the stack segment, putting it in kernel virtual for the moment
363          */
364         qlock(&up->seglock);
365         if(waserror()){
366                 qunlock(&up->seglock);
367                 nexterror();
368         }
369
370         s = up->seg[SSEG];
371         do {
372                 tstk = s->base;
373                 if(tstk <= USTKSIZE)
374                         error(Enovmem);
375         } while((s = isoverlap(up, tstk-USTKSIZE, USTKSIZE)) != nil);
376         up->seg[ESEG] = newseg(SG_STACK, tstk-USTKSIZE, USTKSIZE/BY2PG);
377
378         /*
379          * Args: pass 2: assemble; the pages will be faulted in
380          */
381         tos = (Tos*)(tstk - sizeof(Tos));
382         tos->cyclefreq = m->cyclefreq;
383         tos->kcycles = 0;
384         tos->pcycles = 0;
385         tos->clock = 0;
386
387         argv = (char**)(tstk - ssize);
388         charp = (char*)(tstk - nbytes);
389         args = charp;
390         if(indir)
391                 argp = progarg;
392         else
393                 argp = (char**)arg[1];
394
395         for(i=0; i<nargs; i++){
396                 if(indir && *argp==0) {
397                         indir = 0;
398                         argp = (char**)arg[1];
399                 }
400                 *argv++ = charp + (USTKTOP-tstk);
401                 n = strlen(*argp) + 1;
402                 memmove(charp, *argp++, n);
403                 charp += n;
404         }
405
406         free(up->text);
407         up->text = elem;
408
409         /* copy args; easiest from new process's stack */
410         n = charp - args;
411         if(n > 128)     /* don't waste too much space on huge arg lists */
412                 n = 128;
413         a = up->args;
414         up->args = nil;
415         free(a);
416         up->args = smalloc(n);
417         memmove(up->args, args, n);
418         if(n>0 && up->args[n-1]!='\0'){
419                 /* make sure last arg is NUL-terminated */
420                 /* put NUL at UTF-8 character boundary */
421                 for(i=n-1; i>0; --i)
422                         if(fullrune(up->args+i, n-i))
423                                 break;
424                 up->args[i] = 0;
425                 n = i+1;
426         }
427         up->nargs = n;
428
429         /*
430          * Committed.
431          * Free old memory.
432          * Special segments are maintained across exec
433          */
434         for(i = SSEG; i <= BSEG; i++) {
435                 putseg(up->seg[i]);
436                 /* prevent a second free if we have an error */
437                 up->seg[i] = 0;
438         }
439         for(i = ESEG+1; i < NSEG; i++) {
440                 s = up->seg[i];
441                 if(s != 0 && (s->type&SG_CEXEC) != 0) {
442                         putseg(s);
443                         up->seg[i] = 0;
444                 }
445         }
446
447         /*
448          * Close on exec
449          */
450         if((f = up->fgrp) != nil){
451                 for(i=0; i<=f->maxfd; i++)
452                         fdclose(i, CCEXEC);
453         }
454
455         /* Text.  Shared. Attaches to cache image if possible */
456         /* attachimage returns a locked cache image */
457         img = attachimage(SG_TEXT|SG_RONLY, tc, UTZERO, (t-UTZERO)>>PGSHIFT);
458         ts = img->s;
459         up->seg[TSEG] = ts;
460         ts->flushme = 1;
461         ts->fstart = 0;
462         ts->flen = sizeof(Exec)+text;
463         unlock(img);
464
465         /* Data. Shared. */
466         s = newseg(SG_DATA, t, (d-t)>>PGSHIFT);
467         up->seg[DSEG] = s;
468
469         /* Attached by hand */
470         incref(img);
471         s->image = img;
472         s->fstart = ts->fstart+ts->flen;
473         s->flen = data;
474
475         /* BSS. Zero fill on demand */
476         up->seg[BSEG] = newseg(SG_BSS, d, (b-d)>>PGSHIFT);
477
478         /*
479          * Move the stack
480          */
481         s = up->seg[ESEG];
482         up->seg[ESEG] = 0;
483         s->base = USTKTOP-USTKSIZE;
484         s->top = USTKTOP;
485         relocateseg(s, USTKTOP-tstk);
486         up->seg[SSEG] = s;
487         qunlock(&up->seglock);
488         poperror();     /* seglock */
489
490         /*
491          *  '/' processes are higher priority (hack to make /ip more responsive).
492          */
493         if(devtab[tc->type]->dc == L'/')
494                 up->basepri = PriRoot;
495         up->priority = up->basepri;
496         poperror();     /* tc */
497         cclose(tc);
498         poperror();     /* file0 */
499         free(file0);
500
501         qlock(&up->debug);
502         up->nnote = 0;
503         up->notify = 0;
504         up->notified = 0;
505         up->privatemem = 0;
506         procsetup(up);
507         qunlock(&up->debug);
508
509         /*
510          *  At this point, the mmu contains info about the old address
511          *  space and needs to be flushed
512          */
513         flushmmu();
514
515         if(up->hang)
516                 up->procctl = Proc_stopme;
517         return execregs(entry, ssize, nargs);
518 }
519
520 int
521 shargs(char *s, int n, char **ap)
522 {
523         int i;
524
525         s += 2;
526         n -= 2;         /* skip #! */
527         for(i=0; s[i]!='\n'; i++)
528                 if(i == n-1)
529                         return 0;
530         s[i] = 0;
531         *ap = 0;
532         i = 0;
533         for(;;) {
534                 while(*s==' ' || *s=='\t')
535                         s++;
536                 if(*s == 0)
537                         break;
538                 i++;
539                 *ap++ = s;
540                 *ap = 0;
541                 while(*s && *s!=' ' && *s!='\t')
542                         s++;
543                 if(*s == 0)
544                         break;
545                 else
546                         *s++ = 0;
547         }
548         return i;
549 }
550
551 int
552 return0(void*)
553 {
554         return 0;
555 }
556
557 long
558 syssleep(ulong *arg)
559 {
560
561         int n;
562
563         n = arg[0];
564         if(n <= 0) {
565                 if (up->edf && (up->edf->flags & Admitted))
566                         edfyield();
567                 else
568                         yield();
569                 return 0;
570         }
571         if(n < TK2MS(1))
572                 n = TK2MS(1);
573         tsleep(&up->sleep, return0, 0, n);
574         return 0;
575 }
576
577 long
578 sysalarm(ulong *arg)
579 {
580         return procalarm(arg[0]);
581 }
582
583 long
584 sysexits(ulong *arg)
585 {
586         char *status;
587         char *inval = "invalid exit string";
588         char buf[ERRMAX];
589
590         status = (char*)arg[0];
591         if(status){
592                 if(waserror())
593                         status = inval;
594                 else{
595                         validaddr((ulong)status, 1, 0);
596                         if(vmemchr(status, 0, ERRMAX) == 0){
597                                 memmove(buf, status, ERRMAX);
598                                 buf[ERRMAX-1] = 0;
599                                 status = buf;
600                         }
601                         poperror();
602                 }
603
604         }
605         pexit(status, 1);
606         return 0;               /* not reached */
607 }
608
609 long
610 sys_wait(ulong *arg)
611 {
612         int pid;
613         Waitmsg w;
614         OWaitmsg *ow;
615
616         if(arg[0] == 0)
617                 return pwait(nil);
618
619         validaddr(arg[0], sizeof(OWaitmsg), 1);
620         evenaddr(arg[0]);
621         pid = pwait(&w);
622         if(pid >= 0){
623                 ow = (OWaitmsg*)arg[0];
624                 readnum(0, ow->pid, NUMSIZE, w.pid, NUMSIZE);
625                 readnum(0, ow->time+TUser*NUMSIZE, NUMSIZE, w.time[TUser], NUMSIZE);
626                 readnum(0, ow->time+TSys*NUMSIZE, NUMSIZE, w.time[TSys], NUMSIZE);
627                 readnum(0, ow->time+TReal*NUMSIZE, NUMSIZE, w.time[TReal], NUMSIZE);
628                 strncpy(ow->msg, w.msg, sizeof(ow->msg)-1);
629                 ow->msg[sizeof(ow->msg)-1] = '\0';
630         }
631         return pid;
632 }
633
634 long
635 sysawait(ulong *arg)
636 {
637         int i;
638         int pid;
639         Waitmsg w;
640         ulong n;
641
642         n = arg[1];
643         validaddr(arg[0], n, 1);
644         pid = pwait(&w);
645         if(pid < 0)
646                 return -1;
647         i = snprint((char*)arg[0], n, "%d %lud %lud %lud %q",
648                 w.pid,
649                 w.time[TUser], w.time[TSys], w.time[TReal],
650                 w.msg);
651
652         return i;
653 }
654
655 void
656 werrstr(char *fmt, ...)
657 {
658         va_list va;
659
660         if(up == nil)
661                 return;
662
663         va_start(va, fmt);
664         vseprint(up->syserrstr, up->syserrstr+ERRMAX, fmt, va);
665         va_end(va);
666 }
667
668 static long
669 generrstr(char *buf, uint nbuf)
670 {
671         char tmp[ERRMAX];
672
673         if(nbuf == 0)
674                 error(Ebadarg);
675         validaddr((ulong)buf, nbuf, 1);
676         if(nbuf > sizeof tmp)
677                 nbuf = sizeof tmp;
678         memmove(tmp, buf, nbuf);
679
680         /* make sure it's NUL-terminated */
681         tmp[nbuf-1] = '\0';
682         memmove(buf, up->syserrstr, nbuf);
683         buf[nbuf-1] = '\0';
684         memmove(up->syserrstr, tmp, nbuf);
685         return 0;
686 }
687
688 long
689 syserrstr(ulong *arg)
690 {
691         return generrstr((char*)arg[0], arg[1]);
692 }
693
694 /* compatibility for old binaries */
695 long
696 sys_errstr(ulong *arg)
697 {
698         return generrstr((char*)arg[0], 64);
699 }
700
701 long
702 sysnotify(ulong *arg)
703 {
704         if(arg[0] != 0)
705                 validaddr(arg[0], sizeof(ulong), 0);
706         up->notify = (int(*)(void*, char*))(arg[0]);
707         return 0;
708 }
709
710 long
711 sysnoted(ulong *arg)
712 {
713         if(arg[0]!=NRSTR && !up->notified)
714                 error(Egreg);
715         return 0;
716 }
717
718 long
719 syssegbrk(ulong *arg)
720 {
721         int i;
722         ulong addr;
723         Segment *s;
724
725         addr = arg[0];
726         for(i = 0; i < NSEG; i++) {
727                 s = up->seg[i];
728                 if(s == 0 || addr < s->base || addr >= s->top)
729                         continue;
730                 switch(s->type&SG_TYPE) {
731                 case SG_TEXT:
732                 case SG_DATA:
733                 case SG_STACK:
734                         error(Ebadarg);
735                 default:
736                         return ibrk(arg[1], i);
737                 }
738         }
739
740         error(Ebadarg);
741         return 0;               /* not reached */
742 }
743
744 long
745 syssegattach(ulong *arg)
746 {
747         return segattach(up, arg[0], (char*)arg[1], arg[2], arg[3]);
748 }
749
750 long
751 syssegdetach(ulong *arg)
752 {
753         int i;
754         ulong addr;
755         Segment *s;
756
757         qlock(&up->seglock);
758         if(waserror()){
759                 qunlock(&up->seglock);
760                 nexterror();
761         }
762
763         s = 0;
764         addr = arg[0];
765         for(i = 0; i < NSEG; i++)
766                 if(s = up->seg[i]) {
767                         qlock(&s->lk);
768                         if((addr >= s->base && addr < s->top) ||
769                            (s->top == s->base && addr == s->base))
770                                 goto found;
771                         qunlock(&s->lk);
772                 }
773
774         error(Ebadarg);
775
776 found:
777         /*
778          * Check we are not detaching the initial stack segment.
779          */
780         if(s == up->seg[SSEG]){
781                 qunlock(&s->lk);
782                 error(Ebadarg);
783         }
784         up->seg[i] = 0;
785         qunlock(&s->lk);
786         putseg(s);
787         qunlock(&up->seglock);
788         poperror();
789
790         /* Ensure we flush any entries from the lost segment */
791         flushmmu();
792         return 0;
793 }
794
795 long
796 syssegfree(ulong *arg)
797 {
798         Segment *s;
799         ulong from, to;
800
801         from = arg[0];
802         s = seg(up, from, 1);
803         if(s == nil)
804                 error(Ebadarg);
805         to = (from + arg[1]) & ~(BY2PG-1);
806         from = PGROUND(from);
807
808         if(to > s->top) {
809                 qunlock(&s->lk);
810                 error(Ebadarg);
811         }
812
813         mfreeseg(s, from, (to - from) / BY2PG);
814         qunlock(&s->lk);
815         flushmmu();
816
817         return 0;
818 }
819
820 /* For binary compatibility */
821 long
822 sysbrk_(ulong *arg)
823 {
824         return ibrk(arg[0], BSEG);
825 }
826
827 long
828 sysrendezvous(ulong *arg)
829 {
830         uintptr tag, val;
831         Proc *p, **l;
832
833         tag = arg[0];
834         l = &REND(up->rgrp, tag);
835
836         lock(up->rgrp);
837         for(p = *l; p; p = p->rendhash) {
838                 if(p->rendtag == tag) {
839                         *l = p->rendhash;
840                         val = p->rendval;
841                         p->rendval = arg[1];
842                         unlock(up->rgrp);
843
844                         ready(p);
845
846                         return val;
847                 }
848                 l = &p->rendhash;
849         }
850
851         /* Going to sleep here */
852         up->rendtag = tag;
853         up->rendval = arg[1];
854         up->rendhash = *l;
855         *l = up;
856         up->state = Rendezvous;
857         unlock(up->rgrp);
858
859         sched();
860
861         return up->rendval;
862 }
863
864 /*
865  * The implementation of semaphores is complicated by needing
866  * to avoid rescheduling in syssemrelease, so that it is safe
867  * to call from real-time processes.  This means syssemrelease
868  * cannot acquire any qlocks, only spin locks.
869  * 
870  * Semacquire and semrelease must both manipulate the semaphore
871  * wait list.  Lock-free linked lists only exist in theory, not
872  * in practice, so the wait list is protected by a spin lock.
873  * 
874  * The semaphore value *addr is stored in user memory, so it
875  * cannot be read or written while holding spin locks.
876  * 
877  * Thus, we can access the list only when holding the lock, and
878  * we can access the semaphore only when not holding the lock.
879  * This makes things interesting.  Note that sleep's condition function
880  * is called while holding two locks - r and up->rlock - so it cannot
881  * access the semaphore value either.
882  * 
883  * An acquirer announces its intention to try for the semaphore
884  * by putting a Sema structure onto the wait list and then
885  * setting Sema.waiting.  After one last check of semaphore,
886  * the acquirer sleeps until Sema.waiting==0.  A releaser of n
887  * must wake up n acquirers who have Sema.waiting set.  It does
888  * this by clearing Sema.waiting and then calling wakeup.
889  * 
890  * There are three interesting races here.  
891  
892  * The first is that in this particular sleep/wakeup usage, a single
893  * wakeup can rouse a process from two consecutive sleeps!  
894  * The ordering is:
895  * 
896  *      (a) set Sema.waiting = 1
897  *      (a) call sleep
898  *      (b) set Sema.waiting = 0
899  *      (a) check Sema.waiting inside sleep, return w/o sleeping
900  *      (a) try for semaphore, fail
901  *      (a) set Sema.waiting = 1
902  *      (a) call sleep
903  *      (b) call wakeup(a)
904  *      (a) wake up again
905  * 
906  * This is okay - semacquire will just go around the loop
907  * again.  It does mean that at the top of the for(;;) loop in
908  * semacquire, phore.waiting might already be set to 1.
909  * 
910  * The second is that a releaser might wake an acquirer who is
911  * interrupted before he can acquire the lock.  Since
912  * release(n) issues only n wakeup calls -- only n can be used
913  * anyway -- if the interrupted process is not going to use his
914  * wakeup call he must pass it on to another acquirer.
915  * 
916  * The third race is similar to the second but more subtle.  An
917  * acquirer sets waiting=1 and then does a final canacquire()
918  * before going to sleep.  The opposite order would result in
919  * missing wakeups that happen between canacquire and
920  * waiting=1.  (In fact, the whole point of Sema.waiting is to
921  * avoid missing wakeups between canacquire() and sleep().) But
922  * there can be spurious wakeups between a successful
923  * canacquire() and the following semdequeue().  This wakeup is
924  * not useful to the acquirer, since he has already acquired
925  * the semaphore.  Like in the previous case, though, the
926  * acquirer must pass the wakeup call along.
927  * 
928  * This is all rather subtle.  The code below has been verified
929  * with the spin model /sys/src/9/port/semaphore.p.  The
930  * original code anticipated the second race but not the first
931  * or third, which were caught only with spin.  The first race
932  * is mentioned in /sys/doc/sleep.ps, but I'd forgotten about it.
933  * It was lucky that my abstract model of sleep/wakeup still managed
934  * to preserve that behavior.
935  *
936  * I remain slightly concerned about memory coherence
937  * outside of locks.  The spin model does not take 
938  * queued processor writes into account so we have to
939  * think hard.  The only variables accessed outside locks
940  * are the semaphore value itself and the boolean flag
941  * Sema.waiting.  The value is only accessed with cmpswap,
942  * whose job description includes doing the right thing as
943  * far as memory coherence across processors.  That leaves
944  * Sema.waiting.  To handle it, we call coherence() before each
945  * read and after each write.           - rsc
946  */
947
948 /* Add semaphore p with addr a to list in seg. */
949 static void
950 semqueue(Segment *s, long *a, Sema *p)
951 {
952         memset(p, 0, sizeof *p);
953         p->addr = a;
954         lock(&s->sema); /* uses s->sema.Rendez.Lock, but no one else is */
955         p->next = &s->sema;
956         p->prev = s->sema.prev;
957         p->next->prev = p;
958         p->prev->next = p;
959         unlock(&s->sema);
960 }
961
962 /* Remove semaphore p from list in seg. */
963 static void
964 semdequeue(Segment *s, Sema *p)
965 {
966         lock(&s->sema);
967         p->next->prev = p->prev;
968         p->prev->next = p->next;
969         unlock(&s->sema);
970 }
971
972 /* Wake up n waiters with addr a on list in seg. */
973 static void
974 semwakeup(Segment *s, long *a, long n)
975 {
976         Sema *p;
977         
978         lock(&s->sema);
979         for(p=s->sema.next; p!=&s->sema && n>0; p=p->next){
980                 if(p->addr == a && p->waiting){
981                         p->waiting = 0;
982                         coherence();
983                         wakeup(p);
984                         n--;
985                 }
986         }
987         unlock(&s->sema);
988 }
989
990 /* Add delta to semaphore and wake up waiters as appropriate. */
991 static long
992 semrelease(Segment *s, long *addr, long delta)
993 {
994         long value;
995
996         do
997                 value = *addr;
998         while(!cmpswap(addr, value, value+delta));
999         semwakeup(s, addr, delta);
1000         return value+delta;
1001 }
1002
1003 /* Try to acquire semaphore using compare-and-swap */
1004 static int
1005 canacquire(long *addr)
1006 {
1007         long value;
1008         
1009         while((value=*addr) > 0)
1010                 if(cmpswap(addr, value, value-1))
1011                         return 1;
1012         return 0;
1013 }               
1014
1015 /* Should we wake up? */
1016 static int
1017 semawoke(void *p)
1018 {
1019         coherence();
1020         return !((Sema*)p)->waiting;
1021 }
1022
1023 /* Acquire semaphore (subtract 1). */
1024 static int
1025 semacquire(Segment *s, long *addr, int block)
1026 {
1027         int acquired;
1028         Sema phore;
1029
1030         if(canacquire(addr))
1031                 return 1;
1032         if(!block)
1033                 return 0;
1034
1035         acquired = 0;
1036         semqueue(s, addr, &phore);
1037         for(;;){
1038                 phore.waiting = 1;
1039                 coherence();
1040                 if(canacquire(addr)){
1041                         acquired = 1;
1042                         break;
1043                 }
1044                 if(waserror())
1045                         break;
1046                 sleep(&phore, semawoke, &phore);
1047                 poperror();
1048         }
1049         semdequeue(s, &phore);
1050         coherence();    /* not strictly necessary due to lock in semdequeue */
1051         if(!phore.waiting)
1052                 semwakeup(s, addr, 1);
1053         if(!acquired)
1054                 nexterror();
1055         return 1;
1056 }
1057
1058 /* Acquire semaphore or time-out */
1059 static int
1060 tsemacquire(Segment *s, long *addr, ulong ms)
1061 {
1062         int acquired, timedout;
1063         ulong t, elms;
1064         Sema phore;
1065
1066         if(canacquire(addr))
1067                 return 1;
1068         if(ms == 0)
1069                 return 0;
1070         acquired = timedout = 0;
1071         semqueue(s, addr, &phore);
1072         for(;;){
1073                 phore.waiting = 1;
1074                 coherence();
1075                 if(canacquire(addr)){
1076                         acquired = 1;
1077                         break;
1078                 }
1079                 if(waserror())
1080                         break;
1081                 t = m->ticks;
1082                 tsleep(&phore, semawoke, &phore, ms);
1083                 elms = TK2MS(m->ticks - t);
1084                 poperror();
1085                 if(elms >= ms){
1086                         timedout = 1;
1087                         break;
1088                 }
1089                 ms -= elms;
1090         }
1091         semdequeue(s, &phore);
1092         coherence();    /* not strictly necessary due to lock in semdequeue */
1093         if(!phore.waiting)
1094                 semwakeup(s, addr, 1);
1095         if(timedout)
1096                 return 0;
1097         if(!acquired)
1098                 nexterror();
1099         return 1;
1100 }
1101
1102 long
1103 syssemacquire(ulong *arg)
1104 {
1105         int block;
1106         long *addr;
1107         Segment *s;
1108
1109         validaddr(arg[0], sizeof(long), 1);
1110         evenaddr(arg[0]);
1111         addr = (long*)arg[0];
1112         block = arg[1];
1113         
1114         if((s = seg(up, (ulong)addr, 0)) == nil)
1115                 error(Ebadarg);
1116         if(*addr < 0)
1117                 error(Ebadarg);
1118         return semacquire(s, addr, block);
1119 }
1120
1121 long
1122 systsemacquire(ulong *arg)
1123 {
1124         long *addr;
1125         ulong ms;
1126         Segment *s;
1127
1128         validaddr(arg[0], sizeof(long), 1);
1129         evenaddr(arg[0]);
1130         addr = (long*)arg[0];
1131         ms = arg[1];
1132
1133         if((s = seg(up, (ulong)addr, 0)) == nil)
1134                 error(Ebadarg);
1135         if(*addr < 0)
1136                 error(Ebadarg);
1137         return tsemacquire(s, addr, ms);
1138 }
1139
1140 long
1141 syssemrelease(ulong *arg)
1142 {
1143         long *addr, delta;
1144         Segment *s;
1145
1146         validaddr(arg[0], sizeof(long), 1);
1147         evenaddr(arg[0]);
1148         addr = (long*)arg[0];
1149         delta = arg[1];
1150
1151         if((s = seg(up, (ulong)addr, 0)) == nil)
1152                 error(Ebadarg);
1153         /* delta == 0 is a no-op, not a release */
1154         if(delta < 0 || *addr < 0)
1155                 error(Ebadarg);
1156         return semrelease(s, addr, delta);
1157 }