]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/segment.c
987f984cc7cb60beb36ad2a13535e01c6505cb72
[plan9front.git] / sys / src / 9 / port / segment.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 /*
9  * Attachable segment types
10  */
11 static Physseg physseg[10] = {
12         { SG_SHARED,    "shared",       0,      SEGMAXSIZE      },
13         { SG_BSS,       "memory",       0,      SEGMAXSIZE      },
14         { 0,            0,              0,      0               },
15 };
16
17 static Lock physseglock;
18
19 #define IHASHSIZE       64
20 #define ihash(s)        imagealloc.hash[s%IHASHSIZE]
21 static struct Imagealloc
22 {
23         Lock;
24         Image   *list;
25         Image   *free;
26         Image   *hash[IHASHSIZE];
27         QLock   ireclaim;       /* mutex on reclaiming free images */
28 }imagealloc;
29
30 Segment* (*_globalsegattach)(Proc*, char*);
31
32 void
33 initseg(void)
34 {
35         Image *i, *ie;
36
37         imagealloc.list = xalloc(conf.nimage*sizeof(Image));
38         if(imagealloc.list == nil)
39                 panic("initseg: no memory for Image");
40         ie = &imagealloc.list[conf.nimage-1];
41         for(i = imagealloc.list; i < ie; i++)
42                 i->next = i+1;
43         i->next = nil;
44         imagealloc.free = imagealloc.list;
45 }
46
47 Segment *
48 newseg(int type, uintptr base, ulong size)
49 {
50         Segment *s;
51         int mapsize;
52
53         if(size > (SEGMAPSIZE*PTEPERTAB))
54                 error(Enovmem);
55
56         s = malloc(sizeof(Segment));
57         if(s == nil)
58                 error(Enomem);
59         s->ref = 1;
60         s->type = type;
61         s->base = base;
62         s->top = base+(size*BY2PG);
63         s->size = size;
64         s->sema.prev = &s->sema;
65         s->sema.next = &s->sema;
66
67         mapsize = ROUND(size, PTEPERTAB)/PTEPERTAB;
68         if(mapsize > nelem(s->ssegmap)){
69                 s->map = malloc(mapsize*sizeof(Pte*));
70                 if(s->map == nil){
71                         free(s);
72                         error(Enomem);
73                 }
74                 s->mapsize = mapsize;
75         }
76         else{
77                 s->map = s->ssegmap;
78                 s->mapsize = nelem(s->ssegmap);
79         }
80
81         return s;
82 }
83
84 void
85 putseg(Segment *s)
86 {
87         Pte **pte, **emap;
88         Image *i;
89
90         if(s == nil)
91                 return;
92
93         i = s->image;
94         if(i != nil) {
95                 lock(i);
96                 if(decref(s) != 0){
97                         unlock(i);
98                         return;
99                 }
100                 if(i->s == s)
101                         i->s = nil;
102                 unlock(i);
103                 putimage(i);
104         } else if(decref(s) != 0)
105                 return;
106
107         emap = &s->map[s->mapsize];
108         for(pte = s->map; pte < emap; pte++)
109                 if(*pte != nil)
110                         freepte(s, *pte);
111
112         if(s->map != s->ssegmap)
113                 free(s->map);
114         if(s->profile != nil)
115                 free(s->profile);
116
117         free(s);
118 }
119
120 void
121 relocateseg(Segment *s, uintptr offset)
122 {
123         Pte **pte, **emap;
124         Page **pg, **pe;
125
126         emap = &s->map[s->mapsize];
127         for(pte = s->map; pte < emap; pte++) {
128                 if(*pte == nil)
129                         continue;
130                 pe = (*pte)->last;
131                 for(pg = (*pte)->first; pg <= pe; pg++) {
132                         if(!pagedout(*pg))
133                                 (*pg)->va += offset;
134                 }
135         }
136 }
137
138 Segment*
139 dupseg(Segment **seg, int segno, int share)
140 {
141         int i, size;
142         Pte *pte;
143         Segment *n, *s;
144
145         SET(n);
146         s = seg[segno];
147
148         qlock(s);
149         if(waserror()){
150                 qunlock(s);
151                 nexterror();
152         }
153         switch(s->type&SG_TYPE) {
154         case SG_TEXT:           /* New segment shares pte set */
155         case SG_SHARED:
156         case SG_PHYSICAL:
157         case SG_FIXED:
158                 goto sameseg;
159
160         case SG_STACK:
161                 n = newseg(s->type, s->base, s->size);
162                 break;
163
164         case SG_BSS:            /* Just copy on write */
165                 if(share)
166                         goto sameseg;
167                 n = newseg(s->type, s->base, s->size);
168                 break;
169
170         case SG_DATA:           /* Copy on write plus demand load info */
171                 if(segno == TSEG){
172                         n = data2txt(s);
173                         poperror();
174                         qunlock(s);
175                         return n;
176                 }
177
178                 if(share)
179                         goto sameseg;
180                 n = newseg(s->type, s->base, s->size);
181
182                 incref(s->image);
183                 n->image = s->image;
184                 n->fstart = s->fstart;
185                 n->flen = s->flen;
186                 break;
187         }
188         size = s->mapsize;
189         for(i = 0; i < size; i++)
190                 if((pte = s->map[i]) != nil)
191                         n->map[i] = ptecpy(pte);
192
193         n->flushme = s->flushme;
194         if(s->ref > 1)
195                 procflushseg(s);
196         poperror();
197         qunlock(s);
198         return n;
199
200 sameseg:
201         incref(s);
202         poperror();
203         qunlock(s);
204         return s;
205 }
206
207 void
208 segpage(Segment *s, Page *p)
209 {
210         Pte **pte, *etp;
211         uintptr soff;
212         Page **pg;
213
214         if(p->va < s->base || p->va >= s->top)
215                 panic("segpage");
216
217         soff = p->va - s->base;
218         pte = &s->map[soff/PTEMAPMEM];
219         if((etp = *pte) == nil)
220                 *pte = etp = ptealloc();
221
222         pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
223         *pg = p;
224         if(pg < etp->first)
225                 etp->first = pg;
226         if(pg > etp->last)
227                 etp->last = pg;
228 }
229
230 Image*
231 attachimage(int type, Chan *c, uintptr base, ulong len)
232 {
233         Image *i, **l;
234
235         lock(&imagealloc);
236
237         /*
238          * Search the image cache for remains of the text from a previous
239          * or currently running incarnation
240          */
241         for(i = ihash(c->qid.path); i; i = i->hash) {
242                 if(c->qid.path == i->qid.path) {
243                         lock(i);
244                         if(eqchantdqid(c, i->type, i->dev, i->qid, 0) && c->qid.type == i->qid.type)
245                                 goto found;
246                         unlock(i);
247                 }
248         }
249
250         /* dump pages of inactive images to free image structures */
251         while((i = imagealloc.free) == nil) {
252                 unlock(&imagealloc);
253                 if(imagereclaim(1000) == 0 && imagealloc.free == nil){
254                         freebroken();           /* can use the memory */
255                         resrcwait("no image after reclaim");
256                 }
257                 lock(&imagealloc);
258         }
259
260         imagealloc.free = i->next;
261
262         lock(i);
263         i->type = c->type;
264         i->dev = c->dev;
265         i->qid = c->qid;
266
267         l = &ihash(c->qid.path);
268         i->hash = *l;
269         *l = i;
270
271 found:
272         unlock(&imagealloc);
273         if(i->c == nil){
274                 i->c = c;
275                 c->flag &= ~CCACHE;
276                 incref(c);
277         }
278
279         if(i->s == nil) {
280                 incref(i);
281                 if(waserror()) {
282                         unlock(i);
283                         putimage(i);
284                         nexterror();
285                 }
286                 i->s = newseg(type, base, len);
287                 i->s->image = i;
288                 poperror();
289         }
290         else
291                 incref(i->s);
292
293         return i;
294 }
295
296 ulong
297 imagereclaim(ulong pages)
298 {
299         static Image *i, *ie;
300         ulong np;
301         int j;
302
303         if(pages == 0)
304                 return 0;
305
306         eqlock(&imagealloc.ireclaim);
307         if(i == nil){
308                 i = imagealloc.list;
309                 ie = &imagealloc.list[conf.nimage];
310         }
311         np = 0;
312         for(j = 0; j < conf.nimage; j++, i++){
313                 if(i >= ie)
314                         i = imagealloc.list;
315                 if(i->ref == 0)
316                         continue;
317                 /*
318                  * if there are no free image structures, only
319                  * reclaim pages from inactive images.
320                  */
321                 if(imagealloc.free != nil || i->ref == i->pgref){
322                         np += pagereclaim(i, pages - np);
323                         if(np >= pages)
324                                 break;
325                 }
326         }
327         qunlock(&imagealloc.ireclaim);
328
329         return np;
330 }
331
332 void
333 putimage(Image *i)
334 {
335         Image *f, **l;
336         Chan *c;
337         long r;
338
339         if(i->notext){
340                 decref(i);
341                 return;
342         }
343
344         c = nil;
345         lock(i);
346         r = decref(i);
347         if(r == i->pgref){
348                 /*
349                  * all remaining references to this image are from the
350                  * page cache, so close the chan.
351                  */
352                 c = i->c;
353                 i->c = nil;
354         }
355         if(r == 0){
356                 l = &ihash(i->qid.path);
357                 mkqid(&i->qid, ~0, ~0, QTFILE);
358                 unlock(i);
359
360                 lock(&imagealloc);
361                 for(f = *l; f != nil; f = f->hash) {
362                         if(f == i) {
363                                 *l = i->hash;
364                                 break;
365                         }
366                         l = &f->hash;
367                 }
368                 i->next = imagealloc.free;
369                 imagealloc.free = i;
370                 unlock(&imagealloc);
371         } else
372                 unlock(i);
373         if(c != nil)
374                 ccloseq(c);     /* does not block */
375 }
376
377 long
378 ibrk(uintptr addr, int seg)
379 {
380         Segment *s, *ns;
381         uintptr newtop;
382         ulong newsize;
383         int i, mapsize;
384         Pte **map;
385
386         s = up->seg[seg];
387         if(s == nil)
388                 error(Ebadarg);
389
390         if(addr == 0)
391                 return s->base;
392
393         qlock(s);
394
395         /* We may start with the bss overlapping the data */
396         if(addr < s->base) {
397                 if(seg != BSEG || up->seg[DSEG] == nil || addr < up->seg[DSEG]->base) {
398                         qunlock(s);
399                         error(Enovmem);
400                 }
401                 addr = s->base;
402         }
403
404         newtop = PGROUND(addr);
405         newsize = (newtop-s->base)/BY2PG;
406         if(newtop < s->top) {
407                 /*
408                  * do not shrink a segment shared with other procs, as the
409                  * to-be-freed address space may have been passed to the kernel
410                  * already by another proc and is past the validaddr stage.
411                  */
412                 if(s->ref > 1){
413                         qunlock(s);
414                         error(Einuse);
415                 }
416                 mfreeseg(s, newtop, (s->top-newtop)/BY2PG);
417                 s->top = newtop;
418                 s->size = newsize;
419                 qunlock(s);
420                 flushmmu();
421                 return 0;
422         }
423
424         for(i = 0; i < NSEG; i++) {
425                 ns = up->seg[i];
426                 if(ns == nil || ns == s)
427                         continue;
428                 if(newtop >= ns->base && newtop < ns->top) {
429                         qunlock(s);
430                         error(Esoverlap);
431                 }
432         }
433
434         if(newsize > (SEGMAPSIZE*PTEPERTAB)) {
435                 qunlock(s);
436                 error(Enovmem);
437         }
438         mapsize = ROUND(newsize, PTEPERTAB)/PTEPERTAB;
439         if(mapsize > s->mapsize){
440                 map = malloc(mapsize*sizeof(Pte*));
441                 if(map == nil){
442                         qunlock(s);
443                         error(Enomem);
444                 }
445                 memmove(map, s->map, s->mapsize*sizeof(Pte*));
446                 if(s->map != s->ssegmap)
447                         free(s->map);
448                 s->map = map;
449                 s->mapsize = mapsize;
450         }
451
452         s->top = newtop;
453         s->size = newsize;
454         qunlock(s);
455         return 0;
456 }
457
458 /*
459  *  called with s locked
460  */
461 ulong
462 mcountseg(Segment *s)
463 {
464         Pte **pte, **emap;
465         Page **pg, **pe;
466         ulong pages;
467
468         if((s->type&SG_TYPE) == SG_PHYSICAL)
469                 return 0;
470
471         pages = 0;
472         emap = &s->map[s->mapsize];
473         for(pte = s->map; pte < emap; pte++){
474                 if(*pte == nil)
475                         continue;
476                 pe = (*pte)->last;
477                 for(pg = (*pte)->first; pg <= pe; pg++)
478                         if(!pagedout(*pg))
479                                 pages++;
480         }
481         return pages;
482 }
483
484 /*
485  *  called with s locked
486  */
487 void
488 mfreeseg(Segment *s, uintptr start, ulong pages)
489 {
490         uintptr off;
491         Pte **pte, **emap;
492         Page **pg, **pe;
493
494         if(pages == 0)
495                 return;
496
497         switch(s->type&SG_TYPE){
498         case SG_PHYSICAL:
499         case SG_FIXED:
500                 return;
501         }
502
503         /*
504          * we have to make sure other processors flush the
505          * entry from their TLBs before the page is freed.
506          */
507         if(s->ref > 1)
508                 procflushseg(s);
509
510         off = start-s->base;
511         pte = &s->map[off/PTEMAPMEM];
512         off = (off&(PTEMAPMEM-1))/BY2PG;
513         for(emap = &s->map[s->mapsize]; pte < emap; pte++, off = 0) {
514                 if(*pte == nil) {
515                         off = PTEPERTAB - off;
516                         if(off >= pages)
517                                 return;
518                         pages -= off;
519                         continue;
520                 }
521                 pg = &(*pte)->pages[off];
522                 for(pe = &(*pte)->pages[PTEPERTAB]; pg < pe; pg++) {
523                         if(*pg != nil){
524                                 putpage(*pg);
525                                 *pg = nil;
526                         }
527                         if(--pages == 0)
528                                 return;
529                 }
530         }
531 }
532
533 Segment*
534 isoverlap(Proc *p, uintptr va, uintptr len)
535 {
536         int i;
537         Segment *ns;
538         uintptr newtop;
539
540         newtop = va+len;
541         for(i = 0; i < NSEG; i++) {
542                 ns = p->seg[i];
543                 if(ns == nil)
544                         continue;
545                 if((newtop > ns->base && newtop <= ns->top) ||
546                    (va >= ns->base && va < ns->top))
547                         return ns;
548         }
549         return nil;
550 }
551
552 int
553 addphysseg(Physseg* new)
554 {
555         Physseg *ps;
556
557         /*
558          * Check not already entered and there is room
559          * for a new entry and the terminating null entry.
560          */
561         lock(&physseglock);
562         for(ps = physseg; ps->name; ps++){
563                 if(strcmp(ps->name, new->name) == 0){
564                         unlock(&physseglock);
565                         return -1;
566                 }
567         }
568         if(ps-physseg >= nelem(physseg)-2){
569                 unlock(&physseglock);
570                 return -1;
571         }
572         *ps = *new;
573         unlock(&physseglock);
574
575         return 0;
576 }
577
578 int
579 isphysseg(char *name)
580 {
581         Physseg *ps;
582         int rv = 0;
583
584         lock(&physseglock);
585         for(ps = physseg; ps->name; ps++){
586                 if(strcmp(ps->name, name) == 0){
587                         rv = 1;
588                         break;
589                 }
590         }
591         unlock(&physseglock);
592         return rv;
593 }
594
595 uintptr
596 segattach(Proc *p, ulong attr, char *name, uintptr va, uintptr len)
597 {
598         int sno;
599         Segment *s, *os;
600         Physseg *ps;
601
602         if(va != 0 && va >= USTKTOP)
603                 error(Ebadarg);
604
605         validaddr((uintptr)name, 1, 0);
606         vmemchr(name, 0, ~0);
607
608         for(sno = 0; sno < NSEG; sno++)
609                 if(p->seg[sno] == nil && sno != ESEG)
610                         break;
611
612         if(sno == NSEG)
613                 error(Enovmem);
614
615         /*
616          *  first look for a global segment with the
617          *  same name
618          */
619         if(_globalsegattach != nil){
620                 s = (*_globalsegattach)(p, name);
621                 if(s != nil){
622                         p->seg[sno] = s;
623                         return s->base;
624                 }
625         }
626
627         /* round up va+len */
628         len += va & (BY2PG-1);
629         len = PGROUND(len);
630
631         if(len == 0)
632                 error(Ebadarg);
633
634         /*
635          * Find a hole in the address space.
636          * Starting at the lowest possible stack address - len,
637          * check for an overlapping segment, and repeat at the
638          * base of that segment - len until either a hole is found
639          * or the address space is exhausted.  Ensure that we don't
640          * map the zero page.
641          */
642         if(va == 0) {
643                 for (os = p->seg[SSEG]; os != nil; os = isoverlap(p, va, len)) {
644                         va = os->base;
645                         if(len >= va)
646                                 error(Enovmem);
647                         va -= len;
648                 }
649         }
650
651         va &= ~(BY2PG-1);
652         if(va == 0 || (va+len) > USTKTOP || (va+len) < va)
653                 error(Ebadarg);
654
655         if(isoverlap(p, va, len) != nil)
656                 error(Esoverlap);
657
658         for(ps = physseg; ps->name; ps++)
659                 if(strcmp(name, ps->name) == 0)
660                         goto found;
661
662         error(Ebadarg);
663 found:
664         if(len > ps->size)
665                 error(Enovmem);
666
667         attr &= ~SG_TYPE;               /* Turn off what is not allowed */
668         attr |= ps->attr;               /* Copy in defaults */
669
670         s = newseg(attr, va, len/BY2PG);
671         s->pseg = ps;
672         p->seg[sno] = s;
673
674         return va;
675 }
676
677 static void
678 segflush(void *va, uintptr len)
679 {
680         uintptr from, to, off;
681         Segment *s;
682         Pte *pte;
683         Page **pg, **pe;
684
685         from = (uintptr)va;
686         to = from + len;
687         to = PGROUND(to);
688         from &= ~(BY2PG-1);
689         if(to < from)
690                 error(Ebadarg);
691
692         while(from < to) {
693                 s = seg(up, from, 1);
694                 if(s == nil)
695                         error(Ebadarg);
696
697                 s->flushme = 1;
698         more:
699                 len = (s->top < to ? s->top : to) - from;
700                 off = from-s->base;
701                 pte = s->map[off/PTEMAPMEM];
702                 off &= PTEMAPMEM-1;
703                 if(off+len > PTEMAPMEM)
704                         len = PTEMAPMEM-off;
705
706                 if(pte != nil) {
707                         pg = &pte->pages[off/BY2PG];
708                         pe = pg + len/BY2PG;
709                         while(pg < pe) {
710                                 if(!pagedout(*pg))
711                                         (*pg)->txtflush = ~0;
712                                 pg++;
713                         }
714                 }
715
716                 from += len;
717                 if(from < to && from < s->top)
718                         goto more;
719
720                 qunlock(s);
721         }
722 }
723
724 uintptr
725 syssegflush(va_list list)
726 {
727         void *va;
728         ulong len;
729
730         va = va_arg(list, void*);
731         len = va_arg(list, ulong);
732         segflush(va, len);
733         flushmmu();
734         return 0;
735 }
736
737 void
738 segclock(uintptr pc)
739 {
740         Segment *s;
741
742         s = up->seg[TSEG];
743         if(s == nil || s->profile == nil)
744                 return;
745
746         s->profile[0] += TK2MS(1);
747         if(pc >= s->base && pc < s->top) {
748                 pc -= s->base;
749                 s->profile[pc>>LRESPROF] += TK2MS(1);
750         }
751 }
752
753 Segment*
754 txt2data(Segment *s)
755 {
756         Segment *ps;
757
758         ps = newseg(SG_DATA, s->base, s->size);
759         ps->image = s->image;
760         incref(ps->image);
761         ps->fstart = s->fstart;
762         ps->flen = s->flen;
763         ps->flushme = 1;
764         qunlock(s);
765         putseg(s);
766         qlock(ps);
767         return ps;
768 }
769
770 Segment*
771 data2txt(Segment *s)
772 {
773         Segment *ps;
774
775         ps = newseg(SG_TEXT, s->base, s->size);
776         ps->image = s->image;
777         incref(ps->image);
778         ps->fstart = s->fstart;
779         ps->flen = s->flen;
780         ps->flushme = 1;
781         return ps;
782 }
783
784
785 enum {
786         /* commands to segmentioproc */
787         Cnone=0,
788         Cread,
789         Cwrite,
790         Cdie,
791 };
792
793 static int
794 cmddone(void *arg)
795 {
796         Segio *sio = arg;
797
798         return sio->cmd == Cnone;
799 }
800
801 static void
802 docmd(Segio *sio, int cmd)
803 {
804         sio->err = nil;
805         sio->cmd = cmd;
806         while(waserror())
807                 ;
808         wakeup(&sio->cmdwait);
809         sleep(&sio->replywait, cmddone, sio);
810         poperror();
811         if(sio->err != nil)
812                 error(sio->err);
813 }
814
815 static int
816 cmdready(void *arg)
817 {
818         Segio *sio = arg;
819
820         return sio->cmd != Cnone;
821 }
822
823 static void
824 segmentioproc(void *arg)
825 {
826         Segio *sio = arg;
827         int done;
828         int sno;
829
830         for(sno = 0; sno < NSEG; sno++)
831                 if(up->seg[sno] == nil && sno != ESEG)
832                         break;
833         if(sno == NSEG)
834                 panic("segmentkproc");
835
836         sio->p = up;
837         incref(sio->s);
838         up->seg[sno] = sio->s;
839
840         while(waserror())
841                 ;
842         for(done = 0; !done;){
843                 sleep(&sio->cmdwait, cmdready, sio);
844                 if(waserror())
845                         sio->err = up->errstr;
846                 else {
847                         if(sio->s != nil && up->seg[sno] != sio->s){
848                                 putseg(up->seg[sno]);
849                                 incref(sio->s);
850                                 up->seg[sno] = sio->s;
851                                 flushmmu();
852                         }
853                         switch(sio->cmd){
854                         case Cread:
855                                 memmove(sio->data, sio->addr, sio->dlen);
856                                 break;
857                         case Cwrite:
858                                 memmove(sio->addr, sio->data, sio->dlen);
859                                 if(sio->s->flushme)
860                                         segflush(sio->addr, sio->dlen);
861                                 break;
862                         case Cdie:
863                                 done = 1;
864                                 break;
865                         }
866                         poperror();
867                 }
868                 sio->cmd = Cnone;
869                 wakeup(&sio->replywait);
870         }
871
872         pexit("done", 1);
873 }
874
875 long
876 segio(Segio *sio, Segment *s, void *a, long n, vlong off, int read)
877 {
878         uintptr m;
879         void *b;
880
881         b = a;
882         if(s != nil){
883                 m = s->top - s->base;
884                 if(off < 0 || off >= m){
885                         if(!read)
886                                 error(Ebadarg);
887                         return 0;
888                 }
889                 if(off+n > m){
890                         if(!read)
891                                 error(Ebadarg); 
892                         n = m - off;
893                 }
894
895                 if((uintptr)a < KZERO) {
896                         b = smalloc(n);
897                         if(waserror()){
898                                 free(b);
899                                 nexterror();
900                         }
901                         if(!read)
902                                 memmove(b, a, n);
903                 }
904         }
905
906         eqlock(sio);
907         if(waserror()){
908                 qunlock(sio);
909                 nexterror();
910         }
911         sio->s = s;
912         if(s == nil){
913                 if(sio->p != nil){
914                         docmd(sio, Cdie);
915                         sio->p = nil;
916                 }
917                 qunlock(sio);
918                 poperror();
919                 return 0;
920         }
921         if(sio->p == nil){
922                 sio->cmd = Cnone;
923                 kproc("segmentio", segmentioproc, sio);
924         }
925         sio->addr = (char*)s->base + off;
926         sio->data = b;
927         sio->dlen = n;
928         docmd(sio, read ? Cread : Cwrite);
929         qunlock(sio);
930         poperror();
931
932         if(a != b){
933                 if(read)
934                         memmove(a, b, n);
935                 free(b);
936                 poperror();
937         }
938         return n;
939 }