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