]> 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(eqqid(c->qid, i->qid) &&
251                            eqqid(c->mqid, i->mqid) &&
252                            c->mchan == i->mchan &&
253                            c->type == i->type) {
254                                 goto found;
255                         }
256                         unlock(i);
257                 }
258         }
259
260         /*
261          * imagereclaim dumps pages from the free list which are cached by image
262          * structures. This should free some image structures.
263          */
264         while(!(i = imagealloc.free)) {
265                 unlock(&imagealloc);
266                 imagereclaim();
267                 if(!imagealloc.free){
268                         freebroken();           /* can use the memory */
269                         resrcwait("no image after reclaim");
270                 }
271                 lock(&imagealloc);
272         }
273
274         imagealloc.free = i->next;
275
276         lock(i);
277         incref(c);
278         c->flag &= ~CCACHE;
279         i->c = c;
280         i->type = c->type;
281         i->qid = c->qid;
282         i->mqid = c->mqid;
283         i->mchan = c->mchan;
284         l = &ihash(c->qid.path);
285         i->hash = *l;
286         *l = i;
287 found:
288         unlock(&imagealloc);
289
290         if(i->s == 0) {
291                 i->ref++;
292                 if(waserror()) {
293                         unlock(i);
294                         putimage(i);
295                         nexterror();
296                 }
297                 i->s = newseg(type, base, len);
298                 i->s->image = i;
299                 poperror();
300         }
301         else
302                 incref(i->s);
303
304         return i;
305 }
306
307 static struct {
308         int     calls;                  /* times imagereclaim was called */
309         int     loops;                  /* times the main loop was run */
310         uvlong  ticks;                  /* total time in the main loop */
311         uvlong  maxt;                   /* longest time in main loop */
312 } irstats;
313
314 static void
315 imagereclaim(void)
316 {
317         int n;
318         Page *p;
319         uvlong ticks;
320
321         irstats.calls++;
322         /* Somebody is already cleaning the page cache */
323         if(!canqlock(&imagealloc.ireclaim))
324                 return;
325
326         lock(&palloc);
327         ticks = fastticks(nil);
328         n = 0;
329         /*
330          * All the pages with images backing them are at the
331          * end of the list (see putpage) so start there and work
332          * backward.
333          */
334         for(p = palloc.tail; p && p->image && (n<1000 || !imagealloc.free); p = p->prev) {
335                 if(p->ref == 0 && canlock(p)) {
336                         if(p->ref == 0 && p->image && !p->image->notext) {
337                                 n++;
338                                 uncachepage(p);
339                         }
340                         unlock(p);
341                 }
342         }
343         ticks = fastticks(nil) - ticks;
344         unlock(&palloc);
345         irstats.loops++;
346         irstats.ticks += ticks;
347         if(ticks > irstats.maxt)
348                 irstats.maxt = ticks;
349         //print("T%llud+", ticks);
350         qunlock(&imagealloc.ireclaim);
351 }
352
353 void
354 putimage(Image *i)
355 {
356         Image *f, **l;
357         Chan *c;
358
359         if(i->notext)
360                 return;
361
362         lock(i);
363         if(--i->ref == 0) {
364                 l = &ihash(i->qid.path);
365                 mkqid(&i->qid, ~0, ~0, QTFILE);
366                 unlock(i);
367                 c = i->c;
368
369                 lock(&imagealloc);
370                 for(f = *l; f; f = f->hash) {
371                         if(f == i) {
372                                 *l = i->hash;
373                                 break;
374                         }
375                         l = &f->hash;
376                 }
377
378                 i->next = imagealloc.free;
379                 imagealloc.free = i;
380                 unlock(&imagealloc);
381
382                 ccloseq(c);     /* does not block */
383                 return;
384         }
385         unlock(i);
386 }
387
388 long
389 ibrk(ulong addr, int seg)
390 {
391         Segment *s, *ns;
392         ulong newtop, newsize;
393         int i, mapsize;
394         Pte **map;
395
396         s = up->seg[seg];
397         if(s == 0)
398                 error(Ebadarg);
399
400         if(addr == 0)
401                 return s->base;
402
403         qlock(&s->lk);
404
405         /* We may start with the bss overlapping the data */
406         if(addr < s->base) {
407                 if(seg != BSEG || up->seg[DSEG] == 0 || addr < up->seg[DSEG]->base) {
408                         qunlock(&s->lk);
409                         error(Enovmem);
410                 }
411                 addr = s->base;
412         }
413
414         newtop = PGROUND(addr);
415         newsize = (newtop-s->base)/BY2PG;
416         if(newtop < s->top) {
417                 /*
418                  * do not shrink a segment shared with other procs, as the
419                  * to-be-freed address space may have been passed to the kernel
420                  * already by another proc and is past the validaddr stage.
421                  */
422                 if(s->ref > 1){
423                         qunlock(&s->lk);
424                         error(Einuse);
425                 }
426                 mfreeseg(s, newtop, (s->top-newtop)/BY2PG);
427                 s->top = newtop;
428                 s->size = newsize;
429                 qunlock(&s->lk);
430                 flushmmu();
431                 return 0;
432         }
433
434         for(i = 0; i < NSEG; i++) {
435                 ns = up->seg[i];
436                 if(ns == 0 || ns == s)
437                         continue;
438                 if(newtop >= ns->base && newtop < ns->top) {
439                         qunlock(&s->lk);
440                         error(Esoverlap);
441                 }
442         }
443
444         if(newsize > (SEGMAPSIZE*PTEPERTAB)) {
445                 qunlock(&s->lk);
446                 error(Enovmem);
447         }
448         mapsize = ROUND(newsize, PTEPERTAB)/PTEPERTAB;
449         if(mapsize > s->mapsize){
450                 map = smalloc(mapsize*sizeof(Pte*));
451                 memmove(map, s->map, s->mapsize*sizeof(Pte*));
452                 if(s->map != s->ssegmap)
453                         free(s->map);
454                 s->map = map;
455                 s->mapsize = mapsize;
456         }
457
458         s->top = newtop;
459         s->size = newsize;
460         qunlock(&s->lk);
461         return 0;
462 }
463
464 /*
465  *  called with s->lk locked
466  */
467 int
468 mcountseg(Segment *s)
469 {
470         int i, j, pages;
471         Page **map;
472
473         pages = 0;
474         for(i = 0; i < s->mapsize; i++){
475                 if(s->map[i] == 0)
476                         continue;
477                 map = s->map[i]->pages;
478                 for(j = 0; j < PTEPERTAB; j++)
479                         if(map[j])
480                                 pages++;
481         }
482         return pages;
483 }
484
485 /*
486  *  called with s->lk locked
487  */
488 void
489 mfreeseg(Segment *s, ulong start, int pages)
490 {
491         int i, j, size;
492         ulong soff;
493         Page *pg;
494         Page *list;
495
496         soff = start-s->base;
497         j = (soff&(PTEMAPMEM-1))/BY2PG;
498
499         size = s->mapsize;
500         list = nil;
501         for(i = soff/PTEMAPMEM; i < size; i++) {
502                 if(pages <= 0)
503                         break;
504                 if(s->map[i] == 0) {
505                         pages -= PTEPERTAB-j;
506                         j = 0;
507                         continue;
508                 }
509                 while(j < PTEPERTAB) {
510                         pg = s->map[i]->pages[j];
511                         /*
512                          * We want to zero s->map[i]->page[j] and putpage(pg),
513                          * but we have to make sure other processors flush the
514                          * entry from their TLBs before the page is freed.
515                          * We construct a list of the pages to be freed, zero
516                          * the entries, then (below) call procflushseg, and call
517                          * putpage on the whole list.
518                          *
519                          * Swapped-out pages don't appear in TLBs, so it's okay
520                          * to putswap those pages before procflushseg.
521                          */
522                         if(pg){
523                                 if(onswap(pg))
524                                         putswap(pg);
525                                 else{
526                                         pg->next = list;
527                                         list = pg;
528                                 }
529                                 s->map[i]->pages[j] = 0;
530                         }
531                         if(--pages == 0)
532                                 goto out;
533                         j++;
534                 }
535                 j = 0;
536         }
537 out:
538         /* flush this seg in all other processes */
539         if(s->ref > 1)
540                 procflushseg(s);
541
542         /* free the pages */
543         for(pg = list; pg != nil; pg = list){
544                 list = list->next;
545                 putpage(pg);
546         }
547 }
548
549 Segment*
550 isoverlap(Proc *p, ulong va, int len)
551 {
552         int i;
553         Segment *ns;
554         ulong newtop;
555
556         newtop = va+len;
557         for(i = 0; i < NSEG; i++) {
558                 ns = p->seg[i];
559                 if(ns == 0)
560                         continue;
561                 if((newtop > ns->base && newtop <= ns->top) ||
562                    (va >= ns->base && va < ns->top))
563                         return ns;
564         }
565         return nil;
566 }
567
568 int
569 addphysseg(Physseg* new)
570 {
571         Physseg *ps;
572
573         /*
574          * Check not already entered and there is room
575          * for a new entry and the terminating null entry.
576          */
577         lock(&physseglock);
578         for(ps = physseg; ps->name; ps++){
579                 if(strcmp(ps->name, new->name) == 0){
580                         unlock(&physseglock);
581                         return -1;
582                 }
583         }
584         if(ps-physseg >= nelem(physseg)-2){
585                 unlock(&physseglock);
586                 return -1;
587         }
588
589         *ps = *new;
590         unlock(&physseglock);
591
592         return 0;
593 }
594
595 int
596 isphysseg(char *name)
597 {
598         Physseg *ps;
599         int rv = 0;
600
601         lock(&physseglock);
602         for(ps = physseg; ps->name; ps++){
603                 if(strcmp(ps->name, name) == 0){
604                         rv = 1;
605                         break;
606                 }
607         }
608         unlock(&physseglock);
609         return rv;
610 }
611
612 ulong
613 segattach(Proc *p, ulong attr, char *name, ulong va, ulong len)
614 {
615         int sno;
616         Segment *s, *os;
617         Physseg *ps;
618
619         if(va != 0 && va >= USTKTOP)
620                 error(Ebadarg);
621
622         validaddr((ulong)name, 1, 0);
623         vmemchr(name, 0, ~0);
624
625         for(sno = 0; sno < NSEG; sno++)
626                 if(p->seg[sno] == nil && sno != ESEG)
627                         break;
628
629         if(sno == NSEG)
630                 error(Enovmem);
631
632         /*
633          *  first look for a global segment with the
634          *  same name
635          */
636         if(_globalsegattach != nil){
637                 s = (*_globalsegattach)(p, name);
638                 if(s != nil){
639                         p->seg[sno] = s;
640                         return s->base;
641                 }
642         }
643
644         len = PGROUND(len);
645         if(len == 0)
646                 error(Ebadarg);
647
648         /*
649          * Find a hole in the address space.
650          * Starting at the lowest possible stack address - len,
651          * check for an overlapping segment, and repeat at the
652          * base of that segment - len until either a hole is found
653          * or the address space is exhausted.  Ensure that we don't
654          * map the zero page.
655          */
656         if(va == 0) {
657                 for (os = p->seg[SSEG]; os != nil; os = isoverlap(p, va, len)) {
658                         va = os->base;
659                         if(len >= va)
660                                 error(Enovmem);
661                         va -= len;
662                 }
663                 va &= ~(BY2PG-1);
664         } else {
665                 va &= ~(BY2PG-1);
666                 if(va == 0 || va >= USTKTOP)
667                         error(Ebadarg);
668         }
669
670         if(isoverlap(p, va, len) != nil)
671                 error(Esoverlap);
672
673         for(ps = physseg; ps->name; ps++)
674                 if(strcmp(name, ps->name) == 0)
675                         goto found;
676
677         error(Ebadarg);
678 found:
679         if(len > ps->size)
680                 error(Enovmem);
681
682         attr &= ~SG_TYPE;               /* Turn off what is not allowed */
683         attr |= ps->attr;               /* Copy in defaults */
684
685         s = newseg(attr, va, len/BY2PG);
686         s->pseg = ps;
687         p->seg[sno] = s;
688
689         return va;
690 }
691
692 void
693 pteflush(Pte *pte, int s, int e)
694 {
695         int i;
696         Page *p;
697
698         for(i = s; i < e; i++) {
699                 p = pte->pages[i];
700                 if(pagedout(p) == 0)
701                         memset(p->cachectl, PG_TXTFLUSH, sizeof(p->cachectl));
702         }
703 }
704
705 long
706 syssegflush(ulong *arg)
707 {
708         Segment *s;
709         ulong addr, l;
710         Pte *pte;
711         int chunk, ps, pe, len;
712
713         addr = arg[0];
714         len = arg[1];
715
716         while(len > 0) {
717                 s = seg(up, addr, 1);
718                 if(s == 0)
719                         error(Ebadarg);
720
721                 s->flushme = 1;
722         more:
723                 l = len;
724                 if(addr+l > s->top)
725                         l = s->top - addr;
726
727                 ps = addr-s->base;
728                 pte = s->map[ps/PTEMAPMEM];
729                 ps &= PTEMAPMEM-1;
730                 pe = PTEMAPMEM;
731                 if(pe-ps > l){
732                         pe = ps + l;
733                         pe = (pe+BY2PG-1)&~(BY2PG-1);
734                 }
735                 if(pe == ps) {
736                         qunlock(&s->lk);
737                         error(Ebadarg);
738                 }
739
740                 if(pte)
741                         pteflush(pte, ps/BY2PG, pe/BY2PG);
742
743                 chunk = pe-ps;
744                 len -= chunk;
745                 addr += chunk;
746
747                 if(len > 0 && addr < s->top)
748                         goto more;
749
750                 qunlock(&s->lk);
751         }
752         flushmmu();
753         return 0;
754 }
755
756 void
757 segclock(ulong pc)
758 {
759         Segment *s;
760
761         s = up->seg[TSEG];
762         if(s == 0 || s->profile == 0)
763                 return;
764
765         s->profile[0] += TK2MS(1);
766         if(pc >= s->base && pc < s->top) {
767                 pc -= s->base;
768                 s->profile[pc>>LRESPROF] += TK2MS(1);
769         }
770 }
771