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