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