]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/segment.c
26a62998e66b705f36f7903b4f91da7154c7472f
[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 static void     imagereclaim(void);
9 static void     imagechanreclaim(void);
10
11 #include "io.h"
12
13 /*
14  * Attachable segment types
15  */
16 static Physseg physseg[10] = {
17         { SG_SHARED,    "shared",       0,      SEGMAXSIZE,     0,      0 },
18         { SG_BSS,       "memory",       0,      SEGMAXSIZE,     0,      0 },
19         { 0,            0,              0,      0,              0,      0 },
20 };
21
22 static Lock physseglock;
23
24 #define NFREECHAN       64
25 #define IHASHSIZE       64
26 #define ihash(s)        imagealloc.hash[s%IHASHSIZE]
27 static struct Imagealloc
28 {
29         Lock;
30         Image   *free;
31         Image   *hash[IHASHSIZE];
32         QLock   ireclaim;       /* mutex on reclaiming free images */
33
34         Chan    **freechan;     /* free image channels */
35         int     nfreechan;      /* number of free channels */
36         int     szfreechan;     /* size of freechan array */
37         QLock   fcreclaim;      /* mutex on reclaiming free channels */
38 }imagealloc;
39
40 Segment* (*_globalsegattach)(Proc*, char*);
41
42 void
43 initseg(void)
44 {
45         Image *i, *ie;
46
47         imagealloc.free = xalloc(conf.nimage*sizeof(Image));
48         if (imagealloc.free == nil)
49                 panic("initseg: no memory");
50         ie = &imagealloc.free[conf.nimage-1];
51         for(i = imagealloc.free; i < ie; i++)
52                 i->next = i+1;
53         i->next = 0;
54         imagealloc.freechan = malloc(NFREECHAN * sizeof(Chan*));
55         imagealloc.szfreechan = NFREECHAN;
56 }
57
58 Segment *
59 newseg(int type, ulong base, ulong size)
60 {
61         Segment *s;
62         int mapsize;
63
64         if(size > (SEGMAPSIZE*PTEPERTAB))
65                 error(Enovmem);
66
67         s = smalloc(sizeof(Segment));
68         s->ref = 1;
69         s->type = type;
70         s->base = base;
71         s->top = base+(size*BY2PG);
72         s->size = size;
73         s->sema.prev = &s->sema;
74         s->sema.next = &s->sema;
75
76         mapsize = ROUND(size, PTEPERTAB)/PTEPERTAB;
77         if(mapsize > nelem(s->ssegmap)){
78                 mapsize *= 2;
79                 if(mapsize > (SEGMAPSIZE*PTEPERTAB))
80                         mapsize = (SEGMAPSIZE*PTEPERTAB);
81                 s->map = smalloc(mapsize*sizeof(Pte*));
82                 s->mapsize = mapsize;
83         }
84         else{
85                 s->map = s->ssegmap;
86                 s->mapsize = nelem(s->ssegmap);
87         }
88
89         return s;
90 }
91
92 void
93 putseg(Segment *s)
94 {
95         Pte **pp, **emap;
96         Image *i;
97
98         if(s == 0)
99                 return;
100
101         i = s->image;
102         if(i != 0) {
103                 lock(i);
104                 lock(s);
105                 if(i->s == s && s->ref == 1)
106                         i->s = 0;
107                 unlock(i);
108         }
109         else
110                 lock(s);
111
112         s->ref--;
113         if(s->ref != 0) {
114                 unlock(s);
115                 return;
116         }
117         unlock(s);
118
119         qlock(&s->lk);
120         if(i)
121                 putimage(i);
122
123         emap = &s->map[s->mapsize];
124         for(pp = s->map; pp < emap; pp++)
125                 if(*pp)
126                         freepte(s, *pp);
127
128         qunlock(&s->lk);
129         if(s->map != s->ssegmap)
130                 free(s->map);
131         if(s->profile != 0)
132                 free(s->profile);
133         free(s);
134 }
135
136 void
137 relocateseg(Segment *s, ulong offset)
138 {
139         Page **pg, *x;
140         Pte *pte, **p, **endpte;
141
142         endpte = &s->map[s->mapsize];
143         for(p = s->map; p < endpte; p++) {
144                 if(*p == 0)
145                         continue;
146                 pte = *p;
147                 for(pg = pte->first; pg <= pte->last; pg++) {
148                         if(x = *pg)
149                                 x->va += offset;
150                 }
151         }
152 }
153
154 Segment*
155 dupseg(Segment **seg, int segno, int share)
156 {
157         int i, size;
158         Pte *pte;
159         Segment *n, *s;
160
161         SET(n);
162         s = seg[segno];
163
164         qlock(&s->lk);
165         if(waserror()){
166                 qunlock(&s->lk);
167                 nexterror();
168         }
169         switch(s->type&SG_TYPE) {
170         case SG_TEXT:           /* New segment shares pte set */
171         case SG_SHARED:
172         case SG_PHYSICAL:
173                 goto sameseg;
174
175         case SG_STACK:
176                 n = newseg(s->type, s->base, s->size);
177                 break;
178
179         case SG_BSS:            /* Just copy on write */
180                 if(share)
181                         goto sameseg;
182                 n = newseg(s->type, s->base, s->size);
183                 break;
184
185         case SG_DATA:           /* Copy on write plus demand load info */
186                 if(segno == TSEG){
187                         poperror();
188                         qunlock(&s->lk);
189                         return data2txt(s);
190                 }
191
192                 if(share)
193                         goto sameseg;
194                 n = newseg(s->type, s->base, s->size);
195
196                 incref(s->image);
197                 n->image = s->image;
198                 n->fstart = s->fstart;
199                 n->flen = s->flen;
200                 break;
201         }
202         size = s->mapsize;
203         for(i = 0; i < size; i++)
204                 if(pte = s->map[i])
205                         n->map[i] = ptecpy(pte);
206
207         n->flushme = s->flushme;
208         if(s->ref > 1)
209                 procflushseg(s);
210         poperror();
211         qunlock(&s->lk);
212         return n;
213
214 sameseg:
215         incref(s);
216         poperror();
217         qunlock(&s->lk);
218         return s;
219 }
220
221 void
222 segpage(Segment *s, Page *p)
223 {
224         Pte **pte;
225         ulong off;
226         Page **pg;
227
228         if(p->va < s->base || p->va >= s->top)
229                 panic("segpage");
230
231         off = p->va - s->base;
232         pte = &s->map[off/PTEMAPMEM];
233         if(*pte == 0)
234                 *pte = ptealloc();
235
236         pg = &(*pte)->pages[(off&(PTEMAPMEM-1))/BY2PG];
237         *pg = p;
238         if(pg < (*pte)->first)
239                 (*pte)->first = pg;
240         if(pg > (*pte)->last)
241                 (*pte)->last = pg;
242 }
243
244 Image*
245 attachimage(int type, Chan *c, ulong base, ulong len)
246 {
247         Image *i, **l;
248
249         /* reclaim any free channels from reclaimed segments */
250         if(imagealloc.nfreechan)
251                 imagechanreclaim();
252
253         lock(&imagealloc);
254
255         /*
256          * Search the image cache for remains of the text from a previous
257          * or currently running incarnation
258          */
259         for(i = ihash(c->qid.path); i; i = i->hash) {
260                 if(c->qid.path == i->qid.path) {
261                         lock(i);
262                         if(eqqid(c->qid, i->qid) &&
263                            eqqid(c->mqid, i->mqid) &&
264                            c->mchan == i->mchan &&
265                            c->type == i->type) {
266                                 goto found;
267                         }
268                         unlock(i);
269                 }
270         }
271
272         /*
273          * imagereclaim dumps pages from the free list which are cached by image
274          * structures. This should free some image structures.
275          */
276         while(!(i = imagealloc.free)) {
277                 unlock(&imagealloc);
278                 imagereclaim();
279                 sched();
280                 lock(&imagealloc);
281         }
282
283         imagealloc.free = i->next;
284
285         lock(i);
286         incref(c);
287         i->c = c;
288         i->type = c->type;
289         i->qid = c->qid;
290         i->mqid = c->mqid;
291         i->mchan = c->mchan;
292         l = &ihash(c->qid.path);
293         i->hash = *l;
294         *l = i;
295 found:
296         unlock(&imagealloc);
297
298         if(i->s == 0) {
299                 /* Disaster after commit in exec */
300                 if(waserror()) {
301                         unlock(i);
302                         pexit(Enovmem, 1);
303                 }
304                 i->s = newseg(type, base, len);
305                 i->s->image = i;
306                 i->ref++;
307                 poperror();
308         }
309         else
310                 incref(i->s);
311
312         return i;
313 }
314
315 static struct {
316         int     calls;                  /* times imagereclaim was called */
317         int     loops;                  /* times the main loop was run */
318         uvlong  ticks;                  /* total time in the main loop */
319         uvlong  maxt;                   /* longest time in main loop */
320 } irstats;
321
322 static void
323 imagereclaim(void)
324 {
325         int n;
326         Page *p;
327         uvlong ticks;
328
329         irstats.calls++;
330         /* Somebody is already cleaning the page cache */
331         if(!canqlock(&imagealloc.ireclaim))
332                 return;
333
334         lock(&palloc);
335         ticks = fastticks(nil);
336         n = 0;
337         /*
338          * All the pages with images backing them are at the
339          * end of the list (see putpage) so start there and work
340          * backward.
341          */
342         for(p = palloc.tail; p && p->image && n<1000; p = p->prev) {
343                 if(p->ref == 0 && canlock(p)) {
344                         if(p->ref == 0) {
345                                 n++;
346                                 uncachepage(p);
347                         }
348                         unlock(p);
349                 }
350         }
351         ticks = fastticks(nil) - ticks;
352         unlock(&palloc);
353         irstats.loops++;
354         irstats.ticks += ticks;
355         if(ticks > irstats.maxt)
356                 irstats.maxt = ticks;
357         //print("T%llud+", ticks);
358         qunlock(&imagealloc.ireclaim);
359 }
360
361 /*
362  *  since close can block, this has to be called outside of
363  *  spin locks.
364  */
365 static void
366 imagechanreclaim(void)
367 {
368         Chan *c;
369
370         /* Somebody is already cleaning the image chans */
371         if(!canqlock(&imagealloc.fcreclaim))
372                 return;
373
374         /*
375          * We don't have to recheck that nfreechan > 0 after we
376          * acquire the lock, because we're the only ones who decrement 
377          * it (the other lock contender increments it), and there's only
378          * one of us thanks to the qlock above.
379          */
380         while(imagealloc.nfreechan > 0){
381                 lock(&imagealloc);
382                 imagealloc.nfreechan--;
383                 c = imagealloc.freechan[imagealloc.nfreechan];
384                 unlock(&imagealloc);
385                 cclose(c);
386         }
387
388         qunlock(&imagealloc.fcreclaim);
389 }
390
391 void
392 putimage(Image *i)
393 {
394         Chan *c, **cp;
395         Image *f, **l;
396
397         if(i->notext)
398                 return;
399
400         lock(i);
401         if(--i->ref == 0) {
402                 l = &ihash(i->qid.path);
403                 mkqid(&i->qid, ~0, ~0, QTFILE);
404                 unlock(i);
405                 c = i->c;
406
407                 lock(&imagealloc);
408                 for(f = *l; f; f = f->hash) {
409                         if(f == i) {
410                                 *l = i->hash;
411                                 break;
412                         }
413                         l = &f->hash;
414                 }
415
416                 i->next = imagealloc.free;
417                 imagealloc.free = i;
418
419                 /* defer freeing channel till we're out of spin lock's */
420                 if(imagealloc.nfreechan == imagealloc.szfreechan){
421                         imagealloc.szfreechan += NFREECHAN;
422                         cp = malloc(imagealloc.szfreechan*sizeof(Chan*));
423                         if(cp == nil)
424                                 panic("putimage");
425                         memmove(cp, imagealloc.freechan, imagealloc.nfreechan*sizeof(Chan*));
426                         free(imagealloc.freechan);
427                         imagealloc.freechan = cp;
428                 }
429                 imagealloc.freechan[imagealloc.nfreechan++] = c;
430                 unlock(&imagealloc);
431
432                 return;
433         }
434         unlock(i);
435 }
436
437 long
438 ibrk(ulong addr, int seg)
439 {
440         Segment *s, *ns;
441         ulong newtop, newsize;
442         int i, mapsize;
443         Pte **map;
444
445         s = up->seg[seg];
446         if(s == 0)
447                 error(Ebadarg);
448
449         if(addr == 0)
450                 return s->base;
451
452         qlock(&s->lk);
453
454         /* We may start with the bss overlapping the data */
455         if(addr < s->base) {
456                 if(seg != BSEG || up->seg[DSEG] == 0 || addr < up->seg[DSEG]->base) {
457                         qunlock(&s->lk);
458                         error(Enovmem);
459                 }
460                 addr = s->base;
461         }
462
463         newtop = PGROUND(addr);
464         newsize = (newtop-s->base)/BY2PG;
465         if(newtop < s->top) {
466                 /*
467                  * do not shrink a segment shared with other procs, as the
468                  * to-be-freed address space may have been passed to the kernel
469                  * already by another proc and is past the validaddr stage.
470                  */
471                 if(s->ref > 1){
472                         qunlock(&s->lk);
473                         error(Einuse);
474                 }
475                 mfreeseg(s, newtop, (s->top-newtop)/BY2PG);
476                 s->top = newtop;
477                 s->size = newsize;
478                 qunlock(&s->lk);
479                 flushmmu();
480                 return 0;
481         }
482
483         for(i = 0; i < NSEG; i++) {
484                 ns = up->seg[i];
485                 if(ns == 0 || ns == s)
486                         continue;
487                 if(newtop >= ns->base && newtop < ns->top) {
488                         qunlock(&s->lk);
489                         error(Esoverlap);
490                 }
491         }
492
493         if(newsize > (SEGMAPSIZE*PTEPERTAB)) {
494                 qunlock(&s->lk);
495                 error(Enovmem);
496         }
497         mapsize = ROUND(newsize, PTEPERTAB)/PTEPERTAB;
498         if(mapsize > s->mapsize){
499                 map = smalloc(mapsize*sizeof(Pte*));
500                 memmove(map, s->map, s->mapsize*sizeof(Pte*));
501                 if(s->map != s->ssegmap)
502                         free(s->map);
503                 s->map = map;
504                 s->mapsize = mapsize;
505         }
506
507         s->top = newtop;
508         s->size = newsize;
509         qunlock(&s->lk);
510         return 0;
511 }
512
513 /*
514  *  called with s->lk locked
515  */
516 int
517 mcountseg(Segment *s)
518 {
519         int i, j, pages;
520         Page **map;
521
522         pages = 0;
523         for(i = 0; i < s->mapsize; i++){
524                 if(s->map[i] == 0)
525                         continue;
526                 map = s->map[i]->pages;
527                 for(j = 0; j < PTEPERTAB; j++)
528                         if(map[j])
529                                 pages++;
530         }
531         return pages;
532 }
533
534 /*
535  *  called with s->lk locked
536  */
537 void
538 mfreeseg(Segment *s, ulong start, int pages)
539 {
540         int i, j, size;
541         ulong soff;
542         Page *pg;
543         Page *list;
544
545         soff = start-s->base;
546         j = (soff&(PTEMAPMEM-1))/BY2PG;
547
548         size = s->mapsize;
549         list = nil;
550         for(i = soff/PTEMAPMEM; i < size; i++) {
551                 if(pages <= 0)
552                         break;
553                 if(s->map[i] == 0) {
554                         pages -= PTEPERTAB-j;
555                         j = 0;
556                         continue;
557                 }
558                 while(j < PTEPERTAB) {
559                         pg = s->map[i]->pages[j];
560                         /*
561                          * We want to zero s->map[i]->page[j] and putpage(pg),
562                          * but we have to make sure other processors flush the
563                          * entry from their TLBs before the page is freed.
564                          * We construct a list of the pages to be freed, zero
565                          * the entries, then (below) call procflushseg, and call
566                          * putpage on the whole list.
567                          *
568                          * Swapped-out pages don't appear in TLBs, so it's okay
569                          * to putswap those pages before procflushseg.
570                          */
571                         if(pg){
572                                 if(onswap(pg))
573                                         putswap(pg);
574                                 else{
575                                         pg->next = list;
576                                         list = pg;
577                                 }
578                                 s->map[i]->pages[j] = 0;
579                         }
580                         if(--pages == 0)
581                                 goto out;
582                         j++;
583                 }
584                 j = 0;
585         }
586 out:
587         /* flush this seg in all other processes */
588         if(s->ref > 1)
589                 procflushseg(s);
590
591         /* free the pages */
592         for(pg = list; pg != nil; pg = list){
593                 list = list->next;
594                 putpage(pg);
595         }
596 }
597
598 Segment*
599 isoverlap(Proc *p, ulong va, int len)
600 {
601         int i;
602         Segment *ns;
603         ulong newtop;
604
605         newtop = va+len;
606         for(i = 0; i < NSEG; i++) {
607                 ns = p->seg[i];
608                 if(ns == 0)
609                         continue;
610                 if((newtop > ns->base && newtop <= ns->top) ||
611                    (va >= ns->base && va < ns->top))
612                         return ns;
613         }
614         return nil;
615 }
616
617 int
618 addphysseg(Physseg* new)
619 {
620         Physseg *ps;
621
622         /*
623          * Check not already entered and there is room
624          * for a new entry and the terminating null entry.
625          */
626         lock(&physseglock);
627         for(ps = physseg; ps->name; ps++){
628                 if(strcmp(ps->name, new->name) == 0){
629                         unlock(&physseglock);
630                         return -1;
631                 }
632         }
633         if(ps-physseg >= nelem(physseg)-2){
634                 unlock(&physseglock);
635                 return -1;
636         }
637
638         *ps = *new;
639         unlock(&physseglock);
640
641         return 0;
642 }
643
644 int
645 isphysseg(char *name)
646 {
647         Physseg *ps;
648         int rv = 0;
649
650         lock(&physseglock);
651         for(ps = physseg; ps->name; ps++){
652                 if(strcmp(ps->name, name) == 0){
653                         rv = 1;
654                         break;
655                 }
656         }
657         unlock(&physseglock);
658         return rv;
659 }
660
661 ulong
662 segattach(Proc *p, ulong attr, char *name, ulong va, ulong len)
663 {
664         int sno;
665         Segment *s, *os;
666         Physseg *ps;
667
668         if(va != 0 && va >= USTKTOP)
669                 error(Ebadarg);
670
671         validaddr((ulong)name, 1, 0);
672         vmemchr(name, 0, ~0);
673
674         for(sno = 0; sno < NSEG; sno++)
675                 if(p->seg[sno] == nil && sno != ESEG)
676                         break;
677
678         if(sno == NSEG)
679                 error(Enovmem);
680
681         /*
682          *  first look for a global segment with the
683          *  same name
684          */
685         if(_globalsegattach != nil){
686                 s = (*_globalsegattach)(p, name);
687                 if(s != nil){
688                         p->seg[sno] = s;
689                         return s->base;
690                 }
691         }
692
693         len = PGROUND(len);
694         if(len == 0)
695                 error(Ebadarg);
696
697         /*
698          * Find a hole in the address space.
699          * Starting at the lowest possible stack address - len,
700          * check for an overlapping segment, and repeat at the
701          * base of that segment - len until either a hole is found
702          * or the address space is exhausted.  Ensure that we don't
703          * map the zero page.
704          */
705         if(va == 0) {
706                 for (os = p->seg[SSEG]; os != nil; os = isoverlap(p, va, len)) {
707                         va = os->base;
708                         if(len >= va)
709                                 error(Enovmem);
710                         va -= len;
711                 }
712                 va &= ~(BY2PG-1);
713         } else {
714                 va &= ~(BY2PG-1);
715                 if(va == 0 || va >= USTKTOP)
716                         error(Ebadarg);
717         }
718
719         if(isoverlap(p, va, len) != nil)
720                 error(Esoverlap);
721
722         for(ps = physseg; ps->name; ps++)
723                 if(strcmp(name, ps->name) == 0)
724                         goto found;
725
726         error(Ebadarg);
727 found:
728         if(len > ps->size)
729                 error(Enovmem);
730
731         attr &= ~SG_TYPE;               /* Turn off what is not allowed */
732         attr |= ps->attr;               /* Copy in defaults */
733
734         s = newseg(attr, va, len/BY2PG);
735         s->pseg = ps;
736         p->seg[sno] = s;
737
738         return va;
739 }
740
741 void
742 pteflush(Pte *pte, int s, int e)
743 {
744         int i;
745         Page *p;
746
747         for(i = s; i < e; i++) {
748                 p = pte->pages[i];
749                 if(pagedout(p) == 0)
750                         memset(p->cachectl, PG_TXTFLUSH, sizeof(p->cachectl));
751         }
752 }
753
754 long
755 syssegflush(ulong *arg)
756 {
757         Segment *s;
758         ulong addr, l;
759         Pte *pte;
760         int chunk, ps, pe, len;
761
762         addr = arg[0];
763         len = arg[1];
764
765         while(len > 0) {
766                 s = seg(up, addr, 1);
767                 if(s == 0)
768                         error(Ebadarg);
769
770                 s->flushme = 1;
771         more:
772                 l = len;
773                 if(addr+l > s->top)
774                         l = s->top - addr;
775
776                 ps = addr-s->base;
777                 pte = s->map[ps/PTEMAPMEM];
778                 ps &= PTEMAPMEM-1;
779                 pe = PTEMAPMEM;
780                 if(pe-ps > l){
781                         pe = ps + l;
782                         pe = (pe+BY2PG-1)&~(BY2PG-1);
783                 }
784                 if(pe == ps) {
785                         qunlock(&s->lk);
786                         error(Ebadarg);
787                 }
788
789                 if(pte)
790                         pteflush(pte, ps/BY2PG, pe/BY2PG);
791
792                 chunk = pe-ps;
793                 len -= chunk;
794                 addr += chunk;
795
796                 if(len > 0 && addr < s->top)
797                         goto more;
798
799                 qunlock(&s->lk);
800         }
801         flushmmu();
802         return 0;
803 }
804
805 void
806 segclock(ulong pc)
807 {
808         Segment *s;
809
810         s = up->seg[TSEG];
811         if(s == 0 || s->profile == 0)
812                 return;
813
814         s->profile[0] += TK2MS(1);
815         if(pc >= s->base && pc < s->top) {
816                 pc -= s->base;
817                 s->profile[pc>>LRESPROF] += TK2MS(1);
818         }
819 }
820