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