summaryrefslogtreecommitdiff
path: root/sys/src/9/port/segment.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/segment.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/segment.c')
-rwxr-xr-xsys/src/9/port/segment.c799
1 files changed, 799 insertions, 0 deletions
diff --git a/sys/src/9/port/segment.c b/sys/src/9/port/segment.c
new file mode 100755
index 000000000..94267ae85
--- /dev/null
+++ b/sys/src/9/port/segment.c
@@ -0,0 +1,799 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+static void imagereclaim(void);
+static void imagechanreclaim(void);
+
+#include "io.h"
+
+/*
+ * Attachable segment types
+ */
+static Physseg physseg[10] = {
+ { SG_SHARED, "shared", 0, SEGMAXSIZE, 0, 0 },
+ { SG_BSS, "memory", 0, SEGMAXSIZE, 0, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static Lock physseglock;
+
+#define NFREECHAN 64
+#define IHASHSIZE 64
+#define ihash(s) imagealloc.hash[s%IHASHSIZE]
+static struct Imagealloc
+{
+ Lock;
+ Image *free;
+ Image *hash[IHASHSIZE];
+ QLock ireclaim; /* mutex on reclaiming free images */
+
+ Chan **freechan; /* free image channels */
+ int nfreechan; /* number of free channels */
+ int szfreechan; /* size of freechan array */
+ QLock fcreclaim; /* mutex on reclaiming free channels */
+}imagealloc;
+
+Segment* (*_globalsegattach)(Proc*, char*);
+
+void
+initseg(void)
+{
+ Image *i, *ie;
+
+ imagealloc.free = xalloc(conf.nimage*sizeof(Image));
+ if (imagealloc.free == nil)
+ panic("initseg: no memory");
+ ie = &imagealloc.free[conf.nimage-1];
+ for(i = imagealloc.free; i < ie; i++)
+ i->next = i+1;
+ i->next = 0;
+ imagealloc.freechan = malloc(NFREECHAN * sizeof(Chan*));
+ imagealloc.szfreechan = NFREECHAN;
+}
+
+Segment *
+newseg(int type, ulong base, ulong size)
+{
+ Segment *s;
+ int mapsize;
+
+ if(size > (SEGMAPSIZE*PTEPERTAB))
+ error(Enovmem);
+
+ s = smalloc(sizeof(Segment));
+ s->ref = 1;
+ s->type = type;
+ s->base = base;
+ s->top = base+(size*BY2PG);
+ s->size = size;
+ s->sema.prev = &s->sema;
+ s->sema.next = &s->sema;
+
+ mapsize = ROUND(size, PTEPERTAB)/PTEPERTAB;
+ if(mapsize > nelem(s->ssegmap)){
+ mapsize *= 2;
+ if(mapsize > (SEGMAPSIZE*PTEPERTAB))
+ mapsize = (SEGMAPSIZE*PTEPERTAB);
+ s->map = smalloc(mapsize*sizeof(Pte*));
+ s->mapsize = mapsize;
+ }
+ else{
+ s->map = s->ssegmap;
+ s->mapsize = nelem(s->ssegmap);
+ }
+
+ return s;
+}
+
+void
+putseg(Segment *s)
+{
+ Pte **pp, **emap;
+ Image *i;
+
+ if(s == 0)
+ return;
+
+ i = s->image;
+ if(i != 0) {
+ lock(i);
+ lock(s);
+ if(i->s == s && s->ref == 1)
+ i->s = 0;
+ unlock(i);
+ }
+ else
+ lock(s);
+
+ s->ref--;
+ if(s->ref != 0) {
+ unlock(s);
+ return;
+ }
+ unlock(s);
+
+ qlock(&s->lk);
+ if(i)
+ putimage(i);
+
+ emap = &s->map[s->mapsize];
+ for(pp = s->map; pp < emap; pp++)
+ if(*pp)
+ freepte(s, *pp);
+
+ qunlock(&s->lk);
+ if(s->map != s->ssegmap)
+ free(s->map);
+ if(s->profile != 0)
+ free(s->profile);
+ free(s);
+}
+
+void
+relocateseg(Segment *s, ulong offset)
+{
+ Page **pg, *x;
+ Pte *pte, **p, **endpte;
+
+ endpte = &s->map[s->mapsize];
+ for(p = s->map; p < endpte; p++) {
+ if(*p == 0)
+ continue;
+ pte = *p;
+ for(pg = pte->first; pg <= pte->last; pg++) {
+ if(x = *pg)
+ x->va += offset;
+ }
+ }
+}
+
+Segment*
+dupseg(Segment **seg, int segno, int share)
+{
+ int i, size;
+ Pte *pte;
+ Segment *n, *s;
+
+ SET(n);
+ s = seg[segno];
+
+ qlock(&s->lk);
+ if(waserror()){
+ qunlock(&s->lk);
+ nexterror();
+ }
+ switch(s->type&SG_TYPE) {
+ case SG_TEXT: /* New segment shares pte set */
+ case SG_SHARED:
+ case SG_PHYSICAL:
+ goto sameseg;
+
+ case SG_STACK:
+ n = newseg(s->type, s->base, s->size);
+ break;
+
+ case SG_BSS: /* Just copy on write */
+ if(share)
+ goto sameseg;
+ n = newseg(s->type, s->base, s->size);
+ break;
+
+ case SG_DATA: /* Copy on write plus demand load info */
+ if(segno == TSEG){
+ poperror();
+ qunlock(&s->lk);
+ return data2txt(s);
+ }
+
+ if(share)
+ goto sameseg;
+ n = newseg(s->type, s->base, s->size);
+
+ incref(s->image);
+ n->image = s->image;
+ n->fstart = s->fstart;
+ n->flen = s->flen;
+ break;
+ }
+ size = s->mapsize;
+ for(i = 0; i < size; i++)
+ if(pte = s->map[i])
+ n->map[i] = ptecpy(pte);
+
+ n->flushme = s->flushme;
+ if(s->ref > 1)
+ procflushseg(s);
+ poperror();
+ qunlock(&s->lk);
+ return n;
+
+sameseg:
+ incref(s);
+ poperror();
+ qunlock(&s->lk);
+ return s;
+}
+
+void
+segpage(Segment *s, Page *p)
+{
+ Pte **pte;
+ ulong off;
+ Page **pg;
+
+ if(p->va < s->base || p->va >= s->top)
+ panic("segpage");
+
+ off = p->va - s->base;
+ pte = &s->map[off/PTEMAPMEM];
+ if(*pte == 0)
+ *pte = ptealloc();
+
+ pg = &(*pte)->pages[(off&(PTEMAPMEM-1))/BY2PG];
+ *pg = p;
+ if(pg < (*pte)->first)
+ (*pte)->first = pg;
+ if(pg > (*pte)->last)
+ (*pte)->last = pg;
+}
+
+Image*
+attachimage(int type, Chan *c, ulong base, ulong len)
+{
+ Image *i, **l;
+
+ /* reclaim any free channels from reclaimed segments */
+ if(imagealloc.nfreechan)
+ imagechanreclaim();
+
+ lock(&imagealloc);
+
+ /*
+ * Search the image cache for remains of the text from a previous
+ * or currently running incarnation
+ */
+ for(i = ihash(c->qid.path); i; i = i->hash) {
+ if(c->qid.path == i->qid.path) {
+ lock(i);
+ if(eqqid(c->qid, i->qid) &&
+ eqqid(c->mqid, i->mqid) &&
+ c->mchan == i->mchan &&
+ c->type == i->type) {
+ goto found;
+ }
+ unlock(i);
+ }
+ }
+
+ /*
+ * imagereclaim dumps pages from the free list which are cached by image
+ * structures. This should free some image structures.
+ */
+ while(!(i = imagealloc.free)) {
+ unlock(&imagealloc);
+ imagereclaim();
+ sched();
+ lock(&imagealloc);
+ }
+
+ imagealloc.free = i->next;
+
+ lock(i);
+ incref(c);
+ i->c = c;
+ i->type = c->type;
+ i->qid = c->qid;
+ i->mqid = c->mqid;
+ i->mchan = c->mchan;
+ l = &ihash(c->qid.path);
+ i->hash = *l;
+ *l = i;
+found:
+ unlock(&imagealloc);
+
+ if(i->s == 0) {
+ /* Disaster after commit in exec */
+ if(waserror()) {
+ unlock(i);
+ pexit(Enovmem, 1);
+ }
+ i->s = newseg(type, base, len);
+ i->s->image = i;
+ i->ref++;
+ poperror();
+ }
+ else
+ incref(i->s);
+
+ return i;
+}
+
+static struct {
+ int calls; /* times imagereclaim was called */
+ int loops; /* times the main loop was run */
+ uvlong ticks; /* total time in the main loop */
+ uvlong maxt; /* longest time in main loop */
+} irstats;
+
+static void
+imagereclaim(void)
+{
+ int n;
+ Page *p;
+ uvlong ticks;
+
+ irstats.calls++;
+ /* Somebody is already cleaning the page cache */
+ if(!canqlock(&imagealloc.ireclaim))
+ return;
+
+ lock(&palloc);
+ ticks = fastticks(nil);
+ n = 0;
+ /*
+ * All the pages with images backing them are at the
+ * end of the list (see putpage) so start there and work
+ * backward.
+ */
+ for(p = palloc.tail; p && p->image && n<1000; p = p->prev) {
+ if(p->ref == 0 && canlock(p)) {
+ if(p->ref == 0) {
+ n++;
+ uncachepage(p);
+ }
+ unlock(p);
+ }
+ }
+ ticks = fastticks(nil) - ticks;
+ unlock(&palloc);
+ irstats.loops++;
+ irstats.ticks += ticks;
+ if(ticks > irstats.maxt)
+ irstats.maxt = ticks;
+ //print("T%llud+", ticks);
+ qunlock(&imagealloc.ireclaim);
+}
+
+/*
+ * since close can block, this has to be called outside of
+ * spin locks.
+ */
+static void
+imagechanreclaim(void)
+{
+ Chan *c;
+
+ /* Somebody is already cleaning the image chans */
+ if(!canqlock(&imagealloc.fcreclaim))
+ return;
+
+ /*
+ * We don't have to recheck that nfreechan > 0 after we
+ * acquire the lock, because we're the only ones who decrement
+ * it (the other lock contender increments it), and there's only
+ * one of us thanks to the qlock above.
+ */
+ while(imagealloc.nfreechan > 0){
+ lock(&imagealloc);
+ imagealloc.nfreechan--;
+ c = imagealloc.freechan[imagealloc.nfreechan];
+ unlock(&imagealloc);
+ cclose(c);
+ }
+
+ qunlock(&imagealloc.fcreclaim);
+}
+
+void
+putimage(Image *i)
+{
+ Chan *c, **cp;
+ Image *f, **l;
+
+ if(i->notext)
+ return;
+
+ lock(i);
+ if(--i->ref == 0) {
+ l = &ihash(i->qid.path);
+ mkqid(&i->qid, ~0, ~0, QTFILE);
+ unlock(i);
+ c = i->c;
+
+ lock(&imagealloc);
+ for(f = *l; f; f = f->hash) {
+ if(f == i) {
+ *l = i->hash;
+ break;
+ }
+ l = &f->hash;
+ }
+
+ i->next = imagealloc.free;
+ imagealloc.free = i;
+
+ /* defer freeing channel till we're out of spin lock's */
+ if(imagealloc.nfreechan == imagealloc.szfreechan){
+ imagealloc.szfreechan += NFREECHAN;
+ cp = malloc(imagealloc.szfreechan*sizeof(Chan*));
+ if(cp == nil)
+ panic("putimage");
+ memmove(cp, imagealloc.freechan, imagealloc.nfreechan*sizeof(Chan*));
+ free(imagealloc.freechan);
+ imagealloc.freechan = cp;
+ }
+ imagealloc.freechan[imagealloc.nfreechan++] = c;
+ unlock(&imagealloc);
+
+ return;
+ }
+ unlock(i);
+}
+
+long
+ibrk(ulong addr, int seg)
+{
+ Segment *s, *ns;
+ ulong newtop, newsize;
+ int i, mapsize;
+ Pte **map;
+
+ s = up->seg[seg];
+ if(s == 0)
+ error(Ebadarg);
+
+ if(addr == 0)
+ return s->base;
+
+ qlock(&s->lk);
+
+ /* We may start with the bss overlapping the data */
+ if(addr < s->base) {
+ if(seg != BSEG || up->seg[DSEG] == 0 || addr < up->seg[DSEG]->base) {
+ qunlock(&s->lk);
+ error(Enovmem);
+ }
+ addr = s->base;
+ }
+
+ newtop = PGROUND(addr);
+ newsize = (newtop-s->base)/BY2PG;
+ if(newtop < s->top) {
+ /*
+ * do not shrink a segment shared with other procs, as the
+ * to-be-freed address space may have been passed to the kernel
+ * already by another proc and is past the validaddr stage.
+ */
+ if(s->ref > 1){
+ qunlock(&s->lk);
+ error(Einuse);
+ }
+ mfreeseg(s, newtop, (s->top-newtop)/BY2PG);
+ s->top = newtop;
+ s->size = newsize;
+ qunlock(&s->lk);
+ flushmmu();
+ return 0;
+ }
+
+ for(i = 0; i < NSEG; i++) {
+ ns = up->seg[i];
+ if(ns == 0 || ns == s)
+ continue;
+ if(newtop >= ns->base && newtop < ns->top) {
+ qunlock(&s->lk);
+ error(Esoverlap);
+ }
+ }
+
+ if(newsize > (SEGMAPSIZE*PTEPERTAB)) {
+ qunlock(&s->lk);
+ error(Enovmem);
+ }
+ mapsize = ROUND(newsize, PTEPERTAB)/PTEPERTAB;
+ if(mapsize > s->mapsize){
+ map = smalloc(mapsize*sizeof(Pte*));
+ memmove(map, s->map, s->mapsize*sizeof(Pte*));
+ if(s->map != s->ssegmap)
+ free(s->map);
+ s->map = map;
+ s->mapsize = mapsize;
+ }
+
+ s->top = newtop;
+ s->size = newsize;
+ qunlock(&s->lk);
+ return 0;
+}
+
+/*
+ * called with s->lk locked
+ */
+void
+mfreeseg(Segment *s, ulong start, int pages)
+{
+ int i, j, size;
+ ulong soff;
+ Page *pg;
+ Page *list;
+
+ soff = start-s->base;
+ j = (soff&(PTEMAPMEM-1))/BY2PG;
+
+ size = s->mapsize;
+ list = nil;
+ for(i = soff/PTEMAPMEM; i < size; i++) {
+ if(pages <= 0)
+ break;
+ if(s->map[i] == 0) {
+ pages -= PTEPERTAB-j;
+ j = 0;
+ continue;
+ }
+ while(j < PTEPERTAB) {
+ pg = s->map[i]->pages[j];
+ /*
+ * We want to zero s->map[i]->page[j] and putpage(pg),
+ * but we have to make sure other processors flush the
+ * entry from their TLBs before the page is freed.
+ * We construct a list of the pages to be freed, zero
+ * the entries, then (below) call procflushseg, and call
+ * putpage on the whole list.
+ *
+ * Swapped-out pages don't appear in TLBs, so it's okay
+ * to putswap those pages before procflushseg.
+ */
+ if(pg){
+ if(onswap(pg))
+ putswap(pg);
+ else{
+ pg->next = list;
+ list = pg;
+ }
+ s->map[i]->pages[j] = 0;
+ }
+ if(--pages == 0)
+ goto out;
+ j++;
+ }
+ j = 0;
+ }
+out:
+ /* flush this seg in all other processes */
+ if(s->ref > 1)
+ procflushseg(s);
+
+ /* free the pages */
+ for(pg = list; pg != nil; pg = list){
+ list = list->next;
+ putpage(pg);
+ }
+}
+
+Segment*
+isoverlap(Proc *p, ulong va, int len)
+{
+ int i;
+ Segment *ns;
+ ulong newtop;
+
+ newtop = va+len;
+ for(i = 0; i < NSEG; i++) {
+ ns = p->seg[i];
+ if(ns == 0)
+ continue;
+ if((newtop > ns->base && newtop <= ns->top) ||
+ (va >= ns->base && va < ns->top))
+ return ns;
+ }
+ return nil;
+}
+
+int
+addphysseg(Physseg* new)
+{
+ Physseg *ps;
+
+ /*
+ * Check not already entered and there is room
+ * for a new entry and the terminating null entry.
+ */
+ lock(&physseglock);
+ for(ps = physseg; ps->name; ps++){
+ if(strcmp(ps->name, new->name) == 0){
+ unlock(&physseglock);
+ return -1;
+ }
+ }
+ if(ps-physseg >= nelem(physseg)-2){
+ unlock(&physseglock);
+ return -1;
+ }
+
+ *ps = *new;
+ unlock(&physseglock);
+
+ return 0;
+}
+
+int
+isphysseg(char *name)
+{
+ Physseg *ps;
+ int rv = 0;
+
+ lock(&physseglock);
+ for(ps = physseg; ps->name; ps++){
+ if(strcmp(ps->name, name) == 0){
+ rv = 1;
+ break;
+ }
+ }
+ unlock(&physseglock);
+ return rv;
+}
+
+ulong
+segattach(Proc *p, ulong attr, char *name, ulong va, ulong len)
+{
+ int sno;
+ Segment *s, *os;
+ Physseg *ps;
+
+ if(va != 0 && va >= USTKTOP)
+ error(Ebadarg);
+
+ validaddr((ulong)name, 1, 0);
+ vmemchr(name, 0, ~0);
+
+ for(sno = 0; sno < NSEG; sno++)
+ if(p->seg[sno] == nil && sno != ESEG)
+ break;
+
+ if(sno == NSEG)
+ error(Enovmem);
+
+ /*
+ * first look for a global segment with the
+ * same name
+ */
+ if(_globalsegattach != nil){
+ s = (*_globalsegattach)(p, name);
+ if(s != nil){
+ p->seg[sno] = s;
+ return s->base;
+ }
+ }
+
+ len = PGROUND(len);
+ if(len == 0)
+ error(Ebadarg);
+
+ /*
+ * Find a hole in the address space.
+ * Starting at the lowest possible stack address - len,
+ * check for an overlapping segment, and repeat at the
+ * base of that segment - len until either a hole is found
+ * or the address space is exhausted. Ensure that we don't
+ * map the zero page.
+ */
+ if(va == 0) {
+ for (os = p->seg[SSEG]; os != nil; os = isoverlap(p, va, len)) {
+ va = os->base;
+ if(len >= va)
+ error(Enovmem);
+ va -= len;
+ }
+ va &= ~(BY2PG-1);
+ } else {
+ va &= ~(BY2PG-1);
+ if(va == 0 || va >= USTKTOP)
+ error(Ebadarg);
+ }
+
+ if(isoverlap(p, va, len) != nil)
+ error(Esoverlap);
+
+ for(ps = physseg; ps->name; ps++)
+ if(strcmp(name, ps->name) == 0)
+ goto found;
+
+ error(Ebadarg);
+found:
+ if(len > ps->size)
+ error(Enovmem);
+
+ attr &= ~SG_TYPE; /* Turn off what is not allowed */
+ attr |= ps->attr; /* Copy in defaults */
+
+ s = newseg(attr, va, len/BY2PG);
+ s->pseg = ps;
+ p->seg[sno] = s;
+
+ return va;
+}
+
+void
+pteflush(Pte *pte, int s, int e)
+{
+ int i;
+ Page *p;
+
+ for(i = s; i < e; i++) {
+ p = pte->pages[i];
+ if(pagedout(p) == 0)
+ memset(p->cachectl, PG_TXTFLUSH, sizeof(p->cachectl));
+ }
+}
+
+long
+syssegflush(ulong *arg)
+{
+ Segment *s;
+ ulong addr, l;
+ Pte *pte;
+ int chunk, ps, pe, len;
+
+ addr = arg[0];
+ len = arg[1];
+
+ while(len > 0) {
+ s = seg(up, addr, 1);
+ if(s == 0)
+ error(Ebadarg);
+
+ s->flushme = 1;
+ more:
+ l = len;
+ if(addr+l > s->top)
+ l = s->top - addr;
+
+ ps = addr-s->base;
+ pte = s->map[ps/PTEMAPMEM];
+ ps &= PTEMAPMEM-1;
+ pe = PTEMAPMEM;
+ if(pe-ps > l){
+ pe = ps + l;
+ pe = (pe+BY2PG-1)&~(BY2PG-1);
+ }
+ if(pe == ps) {
+ qunlock(&s->lk);
+ error(Ebadarg);
+ }
+
+ if(pte)
+ pteflush(pte, ps/BY2PG, pe/BY2PG);
+
+ chunk = pe-ps;
+ len -= chunk;
+ addr += chunk;
+
+ if(len > 0 && addr < s->top)
+ goto more;
+
+ qunlock(&s->lk);
+ }
+ flushmmu();
+ return 0;
+}
+
+void
+segclock(ulong pc)
+{
+ Segment *s;
+
+ s = up->seg[TSEG];
+ if(s == 0 || s->profile == 0)
+ return;
+
+ s->profile[0] += TK2MS(1);
+ if(pc >= s->base && pc < s->top) {
+ pc -= s->base;
+ s->profile[pc>>LRESPROF] += TK2MS(1);
+ }
+}
+