]> 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 int imagereclaim(int);
9
10 /*
11  * Attachable segment types
12  */
13 static Physseg physseg[10] = {
14         { SG_SHARED,    "shared",       0,      SEGMAXSIZE      },
15         { SG_BSS,       "memory",       0,      SEGMAXSIZE      },
16         { 0,            0,              0,      0               },
17 };
18
19 static Lock physseglock;
20
21 #define IHASHSIZE       64
22 #define ihash(s)        imagealloc.hash[s%IHASHSIZE]
23 static struct Imagealloc
24 {
25         Lock;
26         Image   *list;
27         Image   *free;
28         Image   *hash[IHASHSIZE];
29         QLock   ireclaim;       /* mutex on reclaiming free images */
30 }imagealloc;
31
32 Segment* (*_globalsegattach)(Proc*, char*);
33
34 void
35 initseg(void)
36 {
37         Image *i, *ie;
38
39         imagealloc.list = xalloc(conf.nimage*sizeof(Image));
40         if(imagealloc.list == nil)
41                 panic("initseg: no memory for Image");
42         ie = &imagealloc.list[conf.nimage-1];
43         for(i = imagealloc.list; i < ie; i++)
44                 i->next = i+1;
45         i->next = nil;
46         imagealloc.free = imagealloc.list;
47 }
48
49 Segment *
50 newseg(int type, uintptr base, ulong size)
51 {
52         Segment *s;
53         int mapsize;
54
55         if(size > (SEGMAPSIZE*PTEPERTAB))
56                 error(Enovmem);
57
58         s = malloc(sizeof(Segment));
59         if(s == nil)
60                 error(Enomem);
61         s->ref = 1;
62         s->type = type;
63         s->base = base;
64         s->top = base+(size*BY2PG);
65         s->size = size;
66         s->sema.prev = &s->sema;
67         s->sema.next = &s->sema;
68
69         mapsize = ROUND(size, PTEPERTAB)/PTEPERTAB;
70         if(mapsize > nelem(s->ssegmap)){
71                 s->map = malloc(mapsize*sizeof(Pte*));
72                 if(s->map == nil){
73                         free(s);
74                         error(Enomem);
75                 }
76                 s->mapsize = mapsize;
77         }
78         else{
79                 s->map = s->ssegmap;
80                 s->mapsize = nelem(s->ssegmap);
81         }
82
83         return s;
84 }
85
86 void
87 putseg(Segment *s)
88 {
89         Pte **pte, **emap;
90         Image *i;
91
92         if(s == nil)
93                 return;
94
95         i = s->image;
96         if(i != nil) {
97                 lock(i);
98                 if(decref(s) != 0){
99                         unlock(i);
100                         return;
101                 }
102                 if(i->s == s)
103                         i->s = nil;
104                 unlock(i);
105                 putimage(i);
106         } else if(decref(s) != 0)
107                 return;
108
109         emap = &s->map[s->mapsize];
110         for(pte = s->map; pte < emap; pte++)
111                 if(*pte != nil)
112                         freepte(s, *pte);
113
114         if(s->map != s->ssegmap)
115                 free(s->map);
116         if(s->profile != nil)
117                 free(s->profile);
118
119         free(s);
120 }
121
122 void
123 relocateseg(Segment *s, uintptr offset)
124 {
125         Pte **pte, **emap;
126         Page **pg, **pe;
127
128         emap = &s->map[s->mapsize];
129         for(pte = s->map; pte < emap; pte++) {
130                 if(*pte == nil)
131                         continue;
132                 pe = (*pte)->last;
133                 for(pg = (*pte)->first; pg <= pe; pg++) {
134                         if(!pagedout(*pg))
135                                 (*pg)->va += offset;
136                 }
137         }
138 }
139
140 Segment*
141 dupseg(Segment **seg, int segno, int share)
142 {
143         int i, size;
144         Pte *pte;
145         Segment *n, *s;
146
147         SET(n);
148         s = seg[segno];
149
150         qlock(s);
151         if(waserror()){
152                 qunlock(s);
153                 nexterror();
154         }
155         switch(s->type&SG_TYPE) {
156         case SG_TEXT:           /* New segment shares pte set */
157         case SG_SHARED:
158         case SG_PHYSICAL:
159         case SG_FIXED:
160                 goto sameseg;
161
162         case SG_STACK:
163                 n = newseg(s->type, s->base, s->size);
164                 break;
165
166         case SG_BSS:            /* Just copy on write */
167                 if(share)
168                         goto sameseg;
169                 n = newseg(s->type, s->base, s->size);
170                 break;
171
172         case SG_DATA:           /* Copy on write plus demand load info */
173                 if(segno == TSEG){
174                         n = data2txt(s);
175                         poperror();
176                         qunlock(s);
177                         return n;
178                 }
179
180                 if(share)
181                         goto sameseg;
182                 n = newseg(s->type, s->base, s->size);
183
184                 incref(s->image);
185                 n->image = s->image;
186                 n->fstart = s->fstart;
187                 n->flen = s->flen;
188                 break;
189         }
190         size = s->mapsize;
191         for(i = 0; i < size; i++)
192                 if((pte = s->map[i]) != nil)
193                         n->map[i] = ptecpy(pte);
194
195         n->flushme = s->flushme;
196         if(s->ref > 1)
197                 procflushseg(s);
198         poperror();
199         qunlock(s);
200         return n;
201
202 sameseg:
203         incref(s);
204         poperror();
205         qunlock(s);
206         return s;
207 }
208
209 void
210 segpage(Segment *s, Page *p)
211 {
212         Pte **pte, *etp;
213         uintptr soff;
214         Page **pg;
215
216         if(p->va < s->base || p->va >= s->top)
217                 panic("segpage");
218
219         soff = p->va - s->base;
220         pte = &s->map[soff/PTEMAPMEM];
221         if((etp = *pte) == nil)
222                 *pte = etp = ptealloc();
223
224         pg = &etp->pages[(soff&(PTEMAPMEM-1))/BY2PG];
225         *pg = p;
226         if(pg < etp->first)
227                 etp->first = pg;
228         if(pg > etp->last)
229                 etp->last = pg;
230 }
231
232 Image*
233 attachimage(int type, Chan *c, uintptr base, ulong len)
234 {
235         Image *i, **l;
236
237         lock(&imagealloc);
238
239         /*
240          * Search the image cache for remains of the text from a previous
241          * or currently running incarnation
242          */
243         for(i = ihash(c->qid.path); i; i = i->hash) {
244                 if(c->qid.path == i->qid.path) {
245                         lock(i);
246                         if(eqchantdqid(c, i->type, i->dev, i->qid, 0) && c->qid.type == i->qid.type)
247                                 goto found;
248                         unlock(i);
249                 }
250         }
251
252         /* dump pages of inactive images to free image structures */
253         while((i = imagealloc.free) == nil) {
254                 unlock(&imagealloc);
255                 if(imagereclaim(1000) == 0 && imagealloc.free == nil){
256                         freebroken();           /* can use the memory */
257                         resrcwait("no image after reclaim");
258                 }
259                 lock(&imagealloc);
260         }
261
262         imagealloc.free = i->next;
263
264         lock(i);
265         i->type = c->type;
266         i->dev = c->dev;
267         i->qid = c->qid;
268
269         l = &ihash(c->qid.path);
270         i->hash = *l;
271         *l = i;
272
273 found:
274         unlock(&imagealloc);
275         if(i->c == nil){
276                 i->c = c;
277                 c->flag &= ~CCACHE;
278                 incref(c);
279         }
280
281         if(i->s == nil) {
282                 incref(i);
283                 if(waserror()) {
284                         unlock(i);
285                         putimage(i);
286                         nexterror();
287                 }
288                 i->s = newseg(type, base, len);
289                 i->s->image = i;
290                 poperror();
291         }
292         else
293                 incref(i->s);
294
295         return i;
296 }
297
298 extern int pagereclaim(Image*, int);    /* page.c */
299
300 int
301 imagereclaim(int min)
302 {
303         static Image *i, *ie;
304         int j, n;
305
306         eqlock(&imagealloc.ireclaim);
307         if(i == nil){
308                 i = imagealloc.list;
309                 ie = &imagealloc.list[conf.nimage];
310         }
311         n = 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                         n += pagereclaim(i, min - n);
323                         if(n >= min)
324                                 break;
325                 }
326         }
327         qunlock(&imagealloc.ireclaim);
328
329         return n;
330 }
331
332 void
333 putimage(Image *i)
334 {
335         Image *f, **l;
336         Chan *c;
337         int 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 = smalloc(mapsize*sizeof(Pte*));
441                 memmove(map, s->map, s->mapsize*sizeof(Pte*));
442                 if(s->map != s->ssegmap)
443                         free(s->map);
444                 s->map = map;
445                 s->mapsize = mapsize;
446         }
447
448         s->top = newtop;
449         s->size = newsize;
450         qunlock(s);
451         return 0;
452 }
453
454 /*
455  *  called with s locked
456  */
457 ulong
458 mcountseg(Segment *s)
459 {
460         Pte **pte, **emap;
461         Page **pg, **pe;
462         ulong pages;
463
464         if((s->type&SG_TYPE) == SG_PHYSICAL)
465                 return 0;
466
467         pages = 0;
468         emap = &s->map[s->mapsize];
469         for(pte = s->map; pte < emap; pte++){
470                 if(*pte == nil)
471                         continue;
472                 pe = (*pte)->last;
473                 for(pg = (*pte)->first; pg <= pe; pg++)
474                         if(!pagedout(*pg))
475                                 pages++;
476         }
477         return pages;
478 }
479
480 /*
481  *  called with s locked
482  */
483 void
484 mfreeseg(Segment *s, uintptr start, ulong pages)
485 {
486         uintptr off;
487         Pte **pte, **emap;
488         Page **pg, **pe;
489
490         if(pages == 0)
491                 return;
492
493         switch(s->type&SG_TYPE){
494         case SG_PHYSICAL:
495         case SG_FIXED:
496                 return;
497         }
498
499         /*
500          * we have to make sure other processors flush the
501          * entry from their TLBs before the page is freed.
502          */
503         if(s->ref > 1)
504                 procflushseg(s);
505
506         off = start-s->base;
507         pte = &s->map[off/PTEMAPMEM];
508         off = (off&(PTEMAPMEM-1))/BY2PG;
509         for(emap = &s->map[s->mapsize]; pte < emap; pte++, off = 0) {
510                 if(*pte == nil) {
511                         off = PTEPERTAB - off;
512                         if(off >= pages)
513                                 return;
514                         pages -= off;
515                         continue;
516                 }
517                 pg = &(*pte)->pages[off];
518                 for(pe = &(*pte)->pages[PTEPERTAB]; pg < pe; pg++) {
519                         if(*pg != nil){
520                                 putpage(*pg);
521                                 *pg = nil;
522                         }
523                         if(--pages == 0)
524                                 return;
525                 }
526         }
527 }
528
529 Segment*
530 isoverlap(Proc *p, uintptr va, uintptr len)
531 {
532         int i;
533         Segment *ns;
534         uintptr newtop;
535
536         newtop = va+len;
537         for(i = 0; i < NSEG; i++) {
538                 ns = p->seg[i];
539                 if(ns == nil)
540                         continue;
541                 if((newtop > ns->base && newtop <= ns->top) ||
542                    (va >= ns->base && va < ns->top))
543                         return ns;
544         }
545         return nil;
546 }
547
548 int
549 addphysseg(Physseg* new)
550 {
551         Physseg *ps;
552
553         /*
554          * Check not already entered and there is room
555          * for a new entry and the terminating null entry.
556          */
557         lock(&physseglock);
558         for(ps = physseg; ps->name; ps++){
559                 if(strcmp(ps->name, new->name) == 0){
560                         unlock(&physseglock);
561                         return -1;
562                 }
563         }
564         if(ps-physseg >= nelem(physseg)-2){
565                 unlock(&physseglock);
566                 return -1;
567         }
568         *ps = *new;
569         unlock(&physseglock);
570
571         return 0;
572 }
573
574 int
575 isphysseg(char *name)
576 {
577         Physseg *ps;
578         int rv = 0;
579
580         lock(&physseglock);
581         for(ps = physseg; ps->name; ps++){
582                 if(strcmp(ps->name, name) == 0){
583                         rv = 1;
584                         break;
585                 }
586         }
587         unlock(&physseglock);
588         return rv;
589 }
590
591 uintptr
592 segattach(Proc *p, ulong attr, char *name, uintptr va, uintptr len)
593 {
594         int sno;
595         Segment *s, *os;
596         Physseg *ps;
597
598         if(va != 0 && va >= USTKTOP)
599                 error(Ebadarg);
600
601         validaddr((uintptr)name, 1, 0);
602         vmemchr(name, 0, ~0);
603
604         for(sno = 0; sno < NSEG; sno++)
605                 if(p->seg[sno] == nil && sno != ESEG)
606                         break;
607
608         if(sno == NSEG)
609                 error(Enovmem);
610
611         /*
612          *  first look for a global segment with the
613          *  same name
614          */
615         if(_globalsegattach != nil){
616                 s = (*_globalsegattach)(p, name);
617                 if(s != nil){
618                         p->seg[sno] = s;
619                         return s->base;
620                 }
621         }
622
623         /* round up va+len */
624         len += va & (BY2PG-1);
625         len = PGROUND(len);
626
627         if(len == 0)
628                 error(Ebadarg);
629
630         /*
631          * Find a hole in the address space.
632          * Starting at the lowest possible stack address - len,
633          * check for an overlapping segment, and repeat at the
634          * base of that segment - len until either a hole is found
635          * or the address space is exhausted.  Ensure that we don't
636          * map the zero page.
637          */
638         if(va == 0) {
639                 for (os = p->seg[SSEG]; os != nil; os = isoverlap(p, va, len)) {
640                         va = os->base;
641                         if(len >= va)
642                                 error(Enovmem);
643                         va -= len;
644                 }
645         }
646
647         va &= ~(BY2PG-1);
648         if(va == 0 || (va+len) > USTKTOP || (va+len) < va)
649                 error(Ebadarg);
650
651         if(isoverlap(p, va, len) != nil)
652                 error(Esoverlap);
653
654         for(ps = physseg; ps->name; ps++)
655                 if(strcmp(name, ps->name) == 0)
656                         goto found;
657
658         error(Ebadarg);
659 found:
660         if(len > ps->size)
661                 error(Enovmem);
662
663         attr &= ~SG_TYPE;               /* Turn off what is not allowed */
664         attr |= ps->attr;               /* Copy in defaults */
665
666         s = newseg(attr, va, len/BY2PG);
667         s->pseg = ps;
668         p->seg[sno] = s;
669
670         return va;
671 }
672
673 uintptr
674 syssegflush(va_list list)
675 {
676         uintptr from, to, off, len;
677         Segment *s;
678         Pte *pte;
679         Page **pg, **pe;
680
681         from = va_arg(list, uintptr);
682         to = va_arg(list, ulong);
683         to += from;
684
685         to = PGROUND(to);
686         from &= ~(BY2PG-1);
687         if(to < from)
688                 error(Ebadarg);
689
690         while(from < to) {
691                 s = seg(up, from, 1);
692                 if(s == nil)
693                         error(Ebadarg);
694
695                 s->flushme = 1;
696         more:
697                 len = (s->top < to ? s->top : to) - from;
698                 off = from-s->base;
699                 pte = s->map[off/PTEMAPMEM];
700                 off &= PTEMAPMEM-1;
701                 if(off+len > PTEMAPMEM)
702                         len = PTEMAPMEM-off;
703
704                 if(pte != nil) {
705                         pg = &pte->pages[off/BY2PG];
706                         pe = pg + len/BY2PG;
707                         while(pg < pe) {
708                                 if(!pagedout(*pg))
709                                         (*pg)->txtflush = ~0;
710                                 pg++;
711                         }
712                 }
713
714                 from += len;
715                 if(from < to && from < s->top)
716                         goto more;
717
718                 qunlock(s);
719         }
720         flushmmu();
721         return 0;
722 }
723
724 void
725 segclock(uintptr pc)
726 {
727         Segment *s;
728
729         s = up->seg[TSEG];
730         if(s == nil || s->profile == nil)
731                 return;
732
733         s->profile[0] += TK2MS(1);
734         if(pc >= s->base && pc < s->top) {
735                 pc -= s->base;
736                 s->profile[pc>>LRESPROF] += TK2MS(1);
737         }
738 }
739
740 Segment*
741 txt2data(Segment *s)
742 {
743         Segment *ps;
744
745         ps = newseg(SG_DATA, s->base, s->size);
746         ps->image = s->image;
747         incref(ps->image);
748         ps->fstart = s->fstart;
749         ps->flen = s->flen;
750         ps->flushme = 1;
751         qunlock(s);
752         putseg(s);
753         qlock(ps);
754         return ps;
755 }
756
757 Segment*
758 data2txt(Segment *s)
759 {
760         Segment *ps;
761
762         ps = newseg(SG_TEXT, s->base, s->size);
763         ps->image = s->image;
764         incref(ps->image);
765         ps->fstart = s->fstart;
766         ps->flen = s->flen;
767         ps->flushme = 1;
768         return ps;
769 }