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