fix broken image reclaim. additional improvments to follow - rewrite imagereclaim. call pagereclaim with no arguments to prevent recursively acquiring locks. - rewrite pagereclaim to free lru pages only without regard to images, and to visit all page sizes. - remove the private channel free queue. use ccloseq instead. it's not often that essentially removing code fixes things, so this is a real treat. Reference: /n/atom/patch/applied/imagereclaimfix Date: Thu Jun 12 00:54:00 CES 2014 Signed-off-by: quanstro@quanstro.net --- /sys/src/nix/port/cache.c Thu Jun 12 00:53:31 2014 +++ /sys/src/nix/port/cache.c Thu Jun 12 00:53:32 2014 @@ -354,7 +354,7 @@ if(e == 0) break; - p = auxpage(BIGPGSZ); + p = auxpage(BIGPGSZ, NOCOLOR); if(p == 0) { extentfree(e); break; --- /sys/src/nix/port/portdat.h Thu Jun 12 00:53:36 2014 +++ /sys/src/nix/port/portdat.h Thu Jun 12 00:53:37 2014 @@ -581,7 +581,6 @@ struct Pgalloc { Lock; - int userinit; /* working in user init mode */ Pgsza pgsza[NPGSZ]; /* allocs for m->npgsz page sizes */ Page* hash[PGHSIZE]; /* only used for user pages */ Lock hashlock; --- /sys/src/nix/port/portfns.h Thu Jun 12 00:53:39 2014 +++ /sys/src/nix/port/portfns.h Thu Jun 12 00:53:41 2014 @@ -16,11 +16,10 @@ int anyready(void); void _assert(char*); Image* attachimage(int, Chan*, int, uintptr, uintptr); -Page* auxpage(usize); +Page* auxpage(usize, int); Block* bl2mem(uchar*, Block*, int); int blocklen(Block*); void bootlinks(void); -void cachedel(Image*, ulong); void cachepage(Page*, Image*); void callwithureg(void (*)(Ureg*)); int canlock(Lock*); @@ -219,7 +218,7 @@ void pagechainhead(Page*); void pageinit(void); ulong pagenumber(Page*); -uvlong pagereclaim(Image*); +uint pagereclaim(void); void pagersummary(void); void pageunchain(Page*); void panic(char*, ...); @@ -314,7 +313,6 @@ void rebootcmd(int, char**); void relocateseg(Segment*, uintptr); void renameuser(char*, char*); -void resched(char*); void resrcwait(char*); int return0(void*); void rlock(RWlock*); --- /sys/src/nix/port/page.c Thu Jun 12 00:53:43 2014 +++ /sys/src/nix/port/page.c Thu Jun 12 00:53:44 2014 @@ -47,7 +47,6 @@ int si, i, color; Page *pg; - pga.userinit = 1; DBG("pageinit: npgsz = %d\n", m->npgsz); /* * Don't pre-allocate 4K pages, we are not using them anymore. @@ -71,8 +70,6 @@ unlock(&pga); } } - - pga.userinit = 0; } int @@ -329,13 +326,15 @@ * The interface must specify page size. */ Page* -auxpage(usize size) +auxpage(usize size, int color) { Page *p; Pgsza *pa; int si; si = getpgszi(size); + if(si == -1) + panic("auxpage: getpgszi %lux %d %#p", size, color, getcallerpc(&size)); lock(&pga); pa = &pga.pgsza[si]; p = pa->head; @@ -466,7 +465,7 @@ { Page **l, *f; - if(p->image == 0) + if(p->image == nil) return; lock(&pga.hashlock); @@ -506,30 +505,6 @@ unlock(&pga.hashlock); } -void -cachedel(Image *i, ulong daddr) -{ - Page *f, **l; - - lock(&pga.hashlock); - l = &pghash(daddr); - for(f = *l; f; f = f->hash){ - if(f->image == i && f->daddr == daddr){ - lock(f); - if(f->image == i && f->daddr == daddr){ - *l = f->hash; - putimage(f->image); - f->image = nil; - f->daddr = 0; - } - unlock(f); - break; - } - l = &f->hash; - } - unlock(&pga.hashlock); -} - Page * lookpage(Image *i, ulong daddr) { @@ -560,37 +535,33 @@ return nil; } -/* - * Called from imagereclaim, to try to release Images. - * The argument shows the preferred image to release pages from. - * All images will be tried, from lru to mru. - */ -uvlong -pagereclaim(Image *i) +/* Called from imagereclaim, to try to release Images */ +uint +pagereclaim(void) { + int lg, n; + usize sz; Page *p; - uvlong ticks; lock(&pga); - ticks = fastticks(nil); - - /* - * All the pages with images backing them are at the - * end of the list (see putpage) so start there and work - * backward. - */ - for(p = pga.pgsza[0].tail; p && p->image == i; p = p->prev){ - if(p->ref == 0 && canlock(p)){ - if(p->ref == 0) { - uncachepage(p); + sz = 0; + n = 0; + for(lg = 0; lg < m->npgsz; lg++){ + for(p = pga.pgsza[lg].tail; p != nil; p = p->prev){ + if(p->image != nil && p->ref == 0 && canlock(p)){ + if(p->ref == 0) { + n++; + sz += 1<pgszlg2[lg]; + uncachepage(p); + } + unlock(p); } - unlock(p); + if(sz >= 20*MiB && n>5) + break; } } - ticks = fastticks(nil) - ticks; unlock(&pga); - - return ticks; + return sz; } Pte* --- /sys/src/nix/port/image.c Thu Jun 12 00:53:46 2014 +++ /sys/src/nix/port/image.c Thu Jun 12 00:53:47 2014 @@ -5,8 +5,7 @@ #include "fns.h" #include "../port/error.h" -#define NFREECHAN 64 -#define IHASHSIZE 64 +#define IHASHSIZE 67 #define ihash(s) imagealloc.hash[s%IHASHSIZE] static struct Imagealloc @@ -16,33 +15,14 @@ Image *lru; /* tail of LRU list */ 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; 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 */ - int noluck; /* # of times we couldn't get one */ - int nolock; /* # of times we couldn't get the lock */ } irstats; -static void -dumplru(void) -{ - Image *i; - - print("lru:"); - for(i = imagealloc.mru; i != nil; i = i->next) - print(" %p(c%p,r%d)", i, i->c, i->ref); - print("\n"); -} - /* * imagealloc and i must be locked. */ @@ -93,10 +73,6 @@ return nil; } -/* - * On clu, set sys->nimages = 10 to exercise reclaiming. - * It won't be able to get through all of cpurc, but will reclaim. - */ void initimage(void) { @@ -116,89 +92,33 @@ imagealloc.mru[0].prev = nil; imagealloc.mru[sys->nimage-1].next = nil; imagealloc.lru = &imagealloc.mru[sys->nimage-1]; - imagealloc.freechan = malloc(NFREECHAN * sizeof(Chan*)); - imagealloc.szfreechan = NFREECHAN; - } -static void +/* + * images may hang around because they have stale pages referring + * to them. call pagerreclaim() to do the dirty work. + */ +static uint imagereclaim(void) { - Image *i; + uint sz; uvlong ticks0, ticks; irstats.calls++; /* Somebody is already cleaning the page cache */ if(!canqlock(&imagealloc.ireclaim)) - return; - DBG("imagereclaim maxt %ulld noluck %d nolock %d\n", - irstats.maxt, irstats.noluck, irstats.nolock); + return 0; ticks0 = fastticks(nil); - if(!canlock(&imagealloc)){ - /* never happen in the experiments I made */ - qunlock(&imagealloc.ireclaim); - return; - } - for(i = imagealloc.lru; i != nil; i = i->prev){ - if(canlock(i)){ - i->ref++; /* make sure it does not go away */ - unlock(i); - pagereclaim(i); - lock(i); - DBG("imagereclaim: image %p(c%p, r%d)\n", i, i->c, i->ref); - if(i->ref == 1){ /* no pages referring to it, it's ours */ - unlock(i); - unlock(&imagealloc); - putimage(i); - break; - }else - --i->ref; - unlock(i); - } - } + sz = pagereclaim(); - if(i == nil){ - irstats.noluck++; - unlock(&imagealloc); - } - irstats.loops++; ticks = fastticks(nil) - ticks0; irstats.ticks += ticks; if(ticks > irstats.maxt) irstats.maxt = ticks; - //print("T%llud+", ticks); + DBG("imagereclaim %lludµs\n", fastticks2us(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); + return sz; } Image* @@ -206,10 +126,6 @@ { Image *i, **l; - /* reclaim any free channels from reclaimed segments */ - if(imagealloc.nfreechan) - imagechanreclaim(); - lock(&imagealloc); /* @@ -266,7 +182,7 @@ i->s = newseg(type, base, top); i->s->image = i; i->s->color = color; - i->ref++; + incref(i); poperror(); } else @@ -278,14 +194,14 @@ void putimage(Image *i) { - Chan *c, **cp; + Chan *c; Image *f, **l; if(i->notext) return; - lock(i); - if(--i->ref == 0) { + if(decref(i) == 0){ + lock(i); l = &ihash(i->qid.path); mkqid(&i->qid, ~0, ~0, QTFILE); unlock(i); @@ -299,22 +215,9 @@ } l = &f->hash; } - - /* 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; i->c = nil; /* flag as unused in lru list */ unlock(&imagealloc); - return; + ccloseq(c); /* won't block */ } - unlock(i); }