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