1*28942eadSforsyth #include "logfsos.h"
237da2899SCharles.Forsyth #include "logfs.h"
337da2899SCharles.Forsyth #include "local.h"
437da2899SCharles.Forsyth
5*28942eadSforsyth typedef struct AllocState AllocState;
6*28942eadSforsyth struct AllocState {
737da2899SCharles.Forsyth long oldblock;
837da2899SCharles.Forsyth int markbad;
9*28942eadSforsyth };
1037da2899SCharles.Forsyth
11*28942eadSforsyth Pageset
logfsdatapagemask(int pages,int base)1237da2899SCharles.Forsyth logfsdatapagemask(int pages, int base)
1337da2899SCharles.Forsyth {
14*28942eadSforsyth if(pages == BITSPERSET)
15*28942eadSforsyth return ~(Pageset)0;
16*28942eadSforsyth return (((Pageset)1 << pages) - 1) << (BITSPERSET - base - pages);
1737da2899SCharles.Forsyth }
1837da2899SCharles.Forsyth
19*28942eadSforsyth static Pageset
fastgap(Pageset w,uint n)20*28942eadSforsyth fastgap(Pageset w, uint n)
2137da2899SCharles.Forsyth {
22*28942eadSforsyth Pageset s;
2337da2899SCharles.Forsyth //print("fastgap(0x%.8ux, %d)\n", w, n);
24*28942eadSforsyth if(w == 0 || n < 1 || n > BITSPERSET)
2537da2899SCharles.Forsyth return 0;
2637da2899SCharles.Forsyth /*
2737da2899SCharles.Forsyth # unroll the following loop 5 times:
2837da2899SCharles.Forsyth # while(n > 1){
2937da2899SCharles.Forsyth # s := n >> 1;
3037da2899SCharles.Forsyth # w &= w<<s;
3137da2899SCharles.Forsyth # n -= s;
3237da2899SCharles.Forsyth # }
3337da2899SCharles.Forsyth */
3437da2899SCharles.Forsyth s = n >> 1;
3537da2899SCharles.Forsyth w &= w << s;
3637da2899SCharles.Forsyth n -= s;
3737da2899SCharles.Forsyth s = n >> 1;
3837da2899SCharles.Forsyth w &= w << s;
3937da2899SCharles.Forsyth n -= s;
4037da2899SCharles.Forsyth s = n >> 1;
4137da2899SCharles.Forsyth w &= w << s;
4237da2899SCharles.Forsyth n -= s;
4337da2899SCharles.Forsyth s = n >> 1;
4437da2899SCharles.Forsyth w &= w << s;
4537da2899SCharles.Forsyth n -= s;
4637da2899SCharles.Forsyth s = n >> 1;
47*28942eadSforsyth if(BITSPERSET == 64){ /* extra time if 64 bits */
48*28942eadSforsyth w &= w << s;
49*28942eadSforsyth n -= s;
50*28942eadSforsyth s = n >> 1;
51*28942eadSforsyth }
5237da2899SCharles.Forsyth return w & (w << s);
5337da2899SCharles.Forsyth }
5437da2899SCharles.Forsyth
55*28942eadSforsyth static int
nlz(Pageset x)56*28942eadSforsyth nlz(Pageset x)
5737da2899SCharles.Forsyth {
5837da2899SCharles.Forsyth int n, c;
59*28942eadSforsyth
6037da2899SCharles.Forsyth if(x == 0)
61*28942eadSforsyth return BITSPERSET;
62*28942eadSforsyth if(x & PAGETOP)
63*28942eadSforsyth return 0;
64*28942eadSforsyth n = BITSPERSET;
65*28942eadSforsyth c = BITSPERSET/2;
6637da2899SCharles.Forsyth do {
67*28942eadSforsyth Pageset y;
6837da2899SCharles.Forsyth y = x >> c;
6937da2899SCharles.Forsyth if(y != 0) {
7037da2899SCharles.Forsyth n -= c;
7137da2899SCharles.Forsyth x = y;
7237da2899SCharles.Forsyth }
7337da2899SCharles.Forsyth } while((c >>= 1) != 0);
7437da2899SCharles.Forsyth return n - x;
7537da2899SCharles.Forsyth }
7637da2899SCharles.Forsyth
77*28942eadSforsyth static Pageset
findgap(Pageset w,uint n)78*28942eadSforsyth findgap(Pageset w, uint n)
7937da2899SCharles.Forsyth {
80*28942eadSforsyth Pageset m;
81*28942eadSforsyth
8237da2899SCharles.Forsyth do {
8337da2899SCharles.Forsyth m = fastgap(w, n);
8437da2899SCharles.Forsyth if(m)
8537da2899SCharles.Forsyth break;
8637da2899SCharles.Forsyth n--;
8737da2899SCharles.Forsyth } while(n);
8837da2899SCharles.Forsyth if(n == 0)
8937da2899SCharles.Forsyth return 0;
9037da2899SCharles.Forsyth return logfsdatapagemask(n, nlz(m));
9137da2899SCharles.Forsyth }
9237da2899SCharles.Forsyth
9337da2899SCharles.Forsyth static int
bitcount(Pageset mask)94*28942eadSforsyth bitcount(Pageset mask)
9537da2899SCharles.Forsyth {
96*28942eadSforsyth Pageset m;
9737da2899SCharles.Forsyth int rv;
98*28942eadSforsyth
99*28942eadSforsyth rv = 0;
100*28942eadSforsyth for(m = PAGETOP; m != 0; m >>= 1)
10137da2899SCharles.Forsyth if(mask & m)
10237da2899SCharles.Forsyth rv++;
10337da2899SCharles.Forsyth return rv;
10437da2899SCharles.Forsyth }
10537da2899SCharles.Forsyth
10637da2899SCharles.Forsyth static char *
allocdatapages(LogfsServer * server,u32int count,int * countp,long * blockindexp,int * pagep,u32int * flashaddr,AllocState * state)10737da2899SCharles.Forsyth allocdatapages(LogfsServer *server, u32int count, int *countp, long *blockindexp, int *pagep, u32int *flashaddr, AllocState *state)
10837da2899SCharles.Forsyth {
10937da2899SCharles.Forsyth LogfsLowLevel *ll = server->ll;
11037da2899SCharles.Forsyth long b, blockindex;
11137da2899SCharles.Forsyth DataBlock *db;
11237da2899SCharles.Forsyth int pagebase;
11337da2899SCharles.Forsyth u32int pages = (count + (1 << ll->l2pagesize) - 1) >> ll->l2pagesize;
11437da2899SCharles.Forsyth u32int gapmask;
11537da2899SCharles.Forsyth long bestfreeblockindex;
11637da2899SCharles.Forsyth int bestfree;
11737da2899SCharles.Forsyth int pagesperblock = 1 << ll->l2pagesperblock;
11837da2899SCharles.Forsyth int apages;
11937da2899SCharles.Forsyth char *errmsg;
12037da2899SCharles.Forsyth int didsomething;
12137da2899SCharles.Forsyth
12237da2899SCharles.Forsyth state->oldblock = -1;
12337da2899SCharles.Forsyth state->markbad = 0;
12437da2899SCharles.Forsyth if(pages > pagesperblock)
12537da2899SCharles.Forsyth pages = pagesperblock;
12637da2899SCharles.Forsyth /*
12737da2899SCharles.Forsyth * fill in gaps first
12837da2899SCharles.Forsyth */
12937da2899SCharles.Forsyth bestfreeblockindex = -1;
13037da2899SCharles.Forsyth bestfree = 0;
13137da2899SCharles.Forsyth for(blockindex = 0; blockindex < server->ndatablocks; blockindex++) {
13237da2899SCharles.Forsyth db = server->datablock + blockindex;
13337da2899SCharles.Forsyth if(db->block < 0)
13437da2899SCharles.Forsyth continue;
13537da2899SCharles.Forsyth gapmask = findgap(db->free & ~db->dirty, pages);
13637da2899SCharles.Forsyth //print("blockindex %ld free 0x%.8ux dirty 0x%.8ux gapmask %.8ux\n", blockindex, db->free, db->dirty, gapmask);
13737da2899SCharles.Forsyth if(gapmask != 0) {
13837da2899SCharles.Forsyth /*
13937da2899SCharles.Forsyth * this is free and !dirty
14037da2899SCharles.Forsyth */
14137da2899SCharles.Forsyth b = db->block;
14237da2899SCharles.Forsyth USED(b);
14337da2899SCharles.Forsyth goto done;
14437da2899SCharles.Forsyth }
14537da2899SCharles.Forsyth else {
14637da2899SCharles.Forsyth int free = bitcount(db->free & logfsdatapagemask(pagesperblock, 0));
14737da2899SCharles.Forsyth if(free > 0 && (bestfreeblockindex < 0 || free > bestfree)) {
14837da2899SCharles.Forsyth bestfreeblockindex = blockindex;
14937da2899SCharles.Forsyth bestfree = free;
15037da2899SCharles.Forsyth }
15137da2899SCharles.Forsyth }
15237da2899SCharles.Forsyth }
15337da2899SCharles.Forsyth //print("out of space - need to clean up a data block\n");
15437da2899SCharles.Forsyth if(bestfreeblockindex >= 0) {
15537da2899SCharles.Forsyth //print("best block index %ld (%ld) %d bits\n", bestfreeblockindex, server->datablock[bestfreeblockindex].block, bestfree);
15637da2899SCharles.Forsyth /*
15737da2899SCharles.Forsyth * clean up data block
15837da2899SCharles.Forsyth */
15937da2899SCharles.Forsyth b = logfsfindfreeblock(ll, AllocReasonTransfer);
16037da2899SCharles.Forsyth while(b >= 0) {
16137da2899SCharles.Forsyth char *errmsg;
16237da2899SCharles.Forsyth LogfsLowLevelReadResult llrr;
16337da2899SCharles.Forsyth long oldblock;
16437da2899SCharles.Forsyth int markedbad;
16537da2899SCharles.Forsyth
16637da2899SCharles.Forsyth db = server->datablock + bestfreeblockindex;
16737da2899SCharles.Forsyth oldblock = db->block;
16837da2899SCharles.Forsyth errmsg = logfsservercopyactivedata(server, b, bestfreeblockindex, 0, &llrr, &markedbad);
16937da2899SCharles.Forsyth if(errmsg) {
17037da2899SCharles.Forsyth if(!markedbad)
17137da2899SCharles.Forsyth return errmsg;
17237da2899SCharles.Forsyth b = logfsfindfreeblock(ll, AllocReasonTransfer);
17337da2899SCharles.Forsyth }
17437da2899SCharles.Forsyth else {
175*28942eadSforsyth Pageset available;
17637da2899SCharles.Forsyth /*
17737da2899SCharles.Forsyth * if page0 is free, then we must ensure that we use it otherwise
17837da2899SCharles.Forsyth * in tagged storage such as nand, the block tag is not written
17937da2899SCharles.Forsyth * in all cases, it is safer to erase the block afterwards to
18037da2899SCharles.Forsyth * preserve the data for as long as possible (we could choose
18137da2899SCharles.Forsyth * to erase the old block now if page0 has already been copied)
18237da2899SCharles.Forsyth */
18337da2899SCharles.Forsyth blockindex = bestfreeblockindex;
18437da2899SCharles.Forsyth state->oldblock = oldblock;
18537da2899SCharles.Forsyth state->markbad = llrr != LogfsLowLevelReadResultOk;
18637da2899SCharles.Forsyth available = db->free & ~db->dirty;
187*28942eadSforsyth if(available & PAGETOP)
188*28942eadSforsyth available = logfsdatapagemask(nlz(~available), 0);
18937da2899SCharles.Forsyth gapmask = findgap(available, pages);
19037da2899SCharles.Forsyth goto done;
19137da2899SCharles.Forsyth }
19237da2899SCharles.Forsyth }
19337da2899SCharles.Forsyth }
19437da2899SCharles.Forsyth /*
19537da2899SCharles.Forsyth * use already erased blocks, so long as there are a few free
19637da2899SCharles.Forsyth */
19737da2899SCharles.Forsyth b = logfsfindfreeblock(ll, AllocReasonDataExtend);
19837da2899SCharles.Forsyth if(b >= 0) {
19937da2899SCharles.Forsyth useerased:
20037da2899SCharles.Forsyth for(blockindex = 0, db = server->datablock; blockindex < server->ndatablocks; blockindex++, db++)
20137da2899SCharles.Forsyth if(db->block < 0)
20237da2899SCharles.Forsyth break;
20337da2899SCharles.Forsyth if(blockindex == server->ndatablocks)
20437da2899SCharles.Forsyth server->ndatablocks++;
20537da2899SCharles.Forsyth db->path = mkdatapath(blockindex, 0);
20637da2899SCharles.Forsyth db->block = b;
20737da2899SCharles.Forsyth (*ll->setblocktag)(ll, b, LogfsTdata);
20837da2899SCharles.Forsyth (*ll->setblockpath)(ll, b, db->path);
20937da2899SCharles.Forsyth db->free = logfsdatapagemask(pagesperblock, 0);
21037da2899SCharles.Forsyth db->dirty = 0;
21137da2899SCharles.Forsyth gapmask = db->free;
21237da2899SCharles.Forsyth goto done;
21337da2899SCharles.Forsyth }
21437da2899SCharles.Forsyth /*
21537da2899SCharles.Forsyth * last resort; try to steal from log
21637da2899SCharles.Forsyth */
21737da2899SCharles.Forsyth //print("last resort\n");
21837da2899SCharles.Forsyth errmsg = logfsserverlogsweep(server, 0, &didsomething);
21937da2899SCharles.Forsyth if(errmsg)
22037da2899SCharles.Forsyth return errmsg;
22137da2899SCharles.Forsyth if(didsomething) {
22237da2899SCharles.Forsyth /*
22337da2899SCharles.Forsyth * this can only create whole free blocks, so...
22437da2899SCharles.Forsyth */
22537da2899SCharles.Forsyth //print("findfree after last resort\n");
22637da2899SCharles.Forsyth b = logfsfindfreeblock(ll, AllocReasonDataExtend);
22737da2899SCharles.Forsyth if(b >= 0) {
22837da2899SCharles.Forsyth //print("*********************************************************\n");
22937da2899SCharles.Forsyth goto useerased;
23037da2899SCharles.Forsyth }
23137da2899SCharles.Forsyth }
23237da2899SCharles.Forsyth *countp = 0;
23337da2899SCharles.Forsyth return nil;
23437da2899SCharles.Forsyth done:
23537da2899SCharles.Forsyth /*
23637da2899SCharles.Forsyth * common finish - needs gapmask, blockindex, db
23737da2899SCharles.Forsyth */
23837da2899SCharles.Forsyth apages = bitcount(gapmask);
23937da2899SCharles.Forsyth pagebase = nlz(gapmask);
24037da2899SCharles.Forsyth if(apages > pages)
24137da2899SCharles.Forsyth apages = pages;
24237da2899SCharles.Forsyth gapmask = logfsdatapagemask(apages, pagebase);
24337da2899SCharles.Forsyth if(server->trace > 1)
24437da2899SCharles.Forsyth print("allocdatapages: block %ld(%ld) pages %d mask 0x%.8ux pagebase %d apages %d\n",
24537da2899SCharles.Forsyth blockindex, db->block, pages, gapmask, pagebase, apages);
246*28942eadSforsyth db->free &= ~gapmask;
247*28942eadSforsyth db->dirty |= gapmask;
24837da2899SCharles.Forsyth *pagep = pagebase;
24937da2899SCharles.Forsyth *blockindexp = blockindex;
25037da2899SCharles.Forsyth *flashaddr = logfsspo2flashaddr(server, blockindex, pagebase, 0);
25137da2899SCharles.Forsyth *countp = apages << ll->l2pagesize;
25237da2899SCharles.Forsyth if(*countp > count)
25337da2899SCharles.Forsyth *countp = count;
25437da2899SCharles.Forsyth return nil;
25537da2899SCharles.Forsyth }
25637da2899SCharles.Forsyth
25737da2899SCharles.Forsyth typedef struct Page {
25837da2899SCharles.Forsyth u32int pageaddr;
25937da2899SCharles.Forsyth int ref;
26037da2899SCharles.Forsyth } Page;
26137da2899SCharles.Forsyth
26237da2899SCharles.Forsyth typedef struct DataStructure {
26337da2899SCharles.Forsyth LogfsServer *server;
26437da2899SCharles.Forsyth int nentries;
26537da2899SCharles.Forsyth int maxentries;
26637da2899SCharles.Forsyth Page *array;
26737da2899SCharles.Forsyth } DataStructure;
26837da2899SCharles.Forsyth
26937da2899SCharles.Forsyth static int
deltapage(DataStructure * ds,u32int pageaddr,int add,int delta)27037da2899SCharles.Forsyth deltapage(DataStructure *ds, u32int pageaddr, int add, int delta)
27137da2899SCharles.Forsyth {
27237da2899SCharles.Forsyth int i;
27337da2899SCharles.Forsyth for(i = 0; i < ds->nentries; i++)
27437da2899SCharles.Forsyth if(ds->array[i].pageaddr == pageaddr) {
27537da2899SCharles.Forsyth ds->array[i].ref += delta;
27637da2899SCharles.Forsyth return 1;
27737da2899SCharles.Forsyth }
27837da2899SCharles.Forsyth if(!add)
27937da2899SCharles.Forsyth return 1;
28037da2899SCharles.Forsyth if(ds->maxentries == 0) {
28137da2899SCharles.Forsyth ds->array = logfsrealloc(nil, sizeof(Page) * 100);
28237da2899SCharles.Forsyth if(ds->array == nil)
28337da2899SCharles.Forsyth return 0;
28437da2899SCharles.Forsyth ds->maxentries = 100;
28537da2899SCharles.Forsyth }
28637da2899SCharles.Forsyth else if(ds->nentries >= ds->maxentries) {
28737da2899SCharles.Forsyth void *a = logfsrealloc(ds->array, ds->maxentries * 2 * sizeof(Page));
28837da2899SCharles.Forsyth if(a == nil)
28937da2899SCharles.Forsyth return 0;
29037da2899SCharles.Forsyth ds->array = a;
29137da2899SCharles.Forsyth ds->maxentries *= 2;
29237da2899SCharles.Forsyth }
29337da2899SCharles.Forsyth ds->array[ds->nentries].pageaddr = pageaddr;
29437da2899SCharles.Forsyth ds->array[ds->nentries++].ref = delta;
29537da2899SCharles.Forsyth return 1;
29637da2899SCharles.Forsyth }
29737da2899SCharles.Forsyth
29837da2899SCharles.Forsyth /*
29937da2899SCharles.Forsyth * only called for data addresses
30037da2899SCharles.Forsyth */
30137da2899SCharles.Forsyth static int
deltapages(DataStructure * ds,LogfsLowLevel * ll,u32int baseflashaddr,int range,int add,int delta)30237da2899SCharles.Forsyth deltapages(DataStructure *ds, LogfsLowLevel *ll, u32int baseflashaddr, int range, int add, int delta)
30337da2899SCharles.Forsyth {
30437da2899SCharles.Forsyth long seq;
30537da2899SCharles.Forsyth int page, offset;
30637da2899SCharles.Forsyth int pages;
30737da2899SCharles.Forsyth u32int pageaddr;
30837da2899SCharles.Forsyth int x;
30937da2899SCharles.Forsyth
31037da2899SCharles.Forsyth //print("deltapages(%ud, %ud, %d, %d)\n", baseflashaddr, limitflashaddr, add, delta);
31137da2899SCharles.Forsyth logfsflashaddr2spo(ds->server, baseflashaddr, &seq, &page, &offset);
312*28942eadSforsyth pages = (offset + range + (1 << ll->l2pagesize) - 1) >> ll->l2pagesize;
31337da2899SCharles.Forsyth pageaddr = (seq << ll->l2pagesperblock) + page;
31437da2899SCharles.Forsyth for(x = 0; x < pages; x++, pageaddr++)
31537da2899SCharles.Forsyth if(!deltapage(ds, pageaddr, add, delta))
31637da2899SCharles.Forsyth return 0;
31737da2899SCharles.Forsyth return 1;
31837da2899SCharles.Forsyth }
31937da2899SCharles.Forsyth
32037da2899SCharles.Forsyth static int
findpageset(void * magic,u32int baseoffset,u32int limitoffset,Extent * e,u32int extentoffset)32137da2899SCharles.Forsyth findpageset(void *magic, u32int baseoffset, u32int limitoffset, Extent *e, u32int extentoffset)
32237da2899SCharles.Forsyth {
32337da2899SCharles.Forsyth DataStructure *ds = magic;
32437da2899SCharles.Forsyth LogfsLowLevel *ll;
32537da2899SCharles.Forsyth u32int flashaddr;
32637da2899SCharles.Forsyth u32int range;
32737da2899SCharles.Forsyth u32int residue;
32837da2899SCharles.Forsyth
32937da2899SCharles.Forsyth if(e == nil || (e->flashaddr & LogAddr) != 0)
33037da2899SCharles.Forsyth return 1;
33137da2899SCharles.Forsyth ll = ds->server->ll;
33237da2899SCharles.Forsyth //print("baseoffset %ud limitoffset %ud min %ud max %ud\n", baseoffset, limitoffset, e->min, e->max);
33337da2899SCharles.Forsyth flashaddr = e->flashaddr;
33437da2899SCharles.Forsyth if(extentoffset)
33537da2899SCharles.Forsyth if(!deltapages(ds, ll, flashaddr, extentoffset, 1, 1))
33637da2899SCharles.Forsyth return -1;
33737da2899SCharles.Forsyth flashaddr += extentoffset;
33837da2899SCharles.Forsyth range = limitoffset - baseoffset;
33937da2899SCharles.Forsyth if(!deltapages(ds, ll, flashaddr, range, 1, -1))
34037da2899SCharles.Forsyth return -1;
34137da2899SCharles.Forsyth flashaddr += range;
34237da2899SCharles.Forsyth residue = e->max - e->min - (extentoffset + range);
34337da2899SCharles.Forsyth if(residue)
34437da2899SCharles.Forsyth if(!deltapages(ds, ll, flashaddr, residue, 1, 1))
34537da2899SCharles.Forsyth return -1;
34637da2899SCharles.Forsyth return 1;
34737da2899SCharles.Forsyth }
34837da2899SCharles.Forsyth
34937da2899SCharles.Forsyth static int
addpagereferences(void * magic,Extent * e,int hole)35037da2899SCharles.Forsyth addpagereferences(void *magic, Extent *e, int hole)
35137da2899SCharles.Forsyth {
35237da2899SCharles.Forsyth DataStructure *ds = magic;
35337da2899SCharles.Forsyth
35437da2899SCharles.Forsyth if(hole || (e->flashaddr & LogAddr) != 0)
35537da2899SCharles.Forsyth return 1;
35637da2899SCharles.Forsyth return deltapages(ds, ds->server->ll, e->flashaddr, e->max - e->min, 0, 1) ? 1 : -1;
35737da2899SCharles.Forsyth }
35837da2899SCharles.Forsyth
35937da2899SCharles.Forsyth static char *
zappages(LogfsServer * server,Entry * e,u32int min,u32int max)36037da2899SCharles.Forsyth zappages(LogfsServer *server, Entry *e, u32int min, u32int max)
36137da2899SCharles.Forsyth {
36237da2899SCharles.Forsyth DataStructure ds;
363*28942eadSforsyth long seq;
364*28942eadSforsyth int x, rv, page;
365*28942eadSforsyth Page *p;
36637da2899SCharles.Forsyth
36737da2899SCharles.Forsyth if(min >= e->u.file.length)
368*28942eadSforsyth return nil; /* no checks necessary */
36937da2899SCharles.Forsyth if(min == 0 && max >= e->u.file.length) {
37037da2899SCharles.Forsyth /* replacing entire file */
37137da2899SCharles.Forsyth logfsextentlistwalk(e->u.file.extent, logfsunconditionallymarkfreeanddirty, server);
37237da2899SCharles.Forsyth return nil;
37337da2899SCharles.Forsyth }
37437da2899SCharles.Forsyth /* hard after that - this will need to be improved */
37537da2899SCharles.Forsyth /*
37637da2899SCharles.Forsyth * current algorithm
37737da2899SCharles.Forsyth * build a list of all pages referenced by the extents being removed, and count the
37837da2899SCharles.Forsyth * number of references
37937da2899SCharles.Forsyth * then subtract the number of references to each page in entire file
38037da2899SCharles.Forsyth * any pages with a reference count == 0 can be removed
38137da2899SCharles.Forsyth */
38237da2899SCharles.Forsyth ds.server = server;
38337da2899SCharles.Forsyth ds.nentries = 0;
38437da2899SCharles.Forsyth ds.maxentries = 0;
38537da2899SCharles.Forsyth ds.array = nil;
38637da2899SCharles.Forsyth rv = logfsextentlistwalkrange(e->u.file.extent, findpageset, &ds, min, max);
387*28942eadSforsyth if(rv < 0 || ds.nentries == 0)
388*28942eadSforsyth goto Out;
389*28942eadSforsyth if(server->trace > 1){
39037da2899SCharles.Forsyth print("pass 1\n");
391*28942eadSforsyth for(x = 0; x < ds.nentries; x++){
392*28942eadSforsyth p = &ds.array[x];
393*28942eadSforsyth seq = p->pageaddr >> server->ll->l2pagesperblock;
394*28942eadSforsyth page = p->pageaddr & ((1 << server->ll->l2pagesperblock) - 1);
395*28942eadSforsyth print("block %lud page %ud ref %d\n", seq, page, p->ref);
396*28942eadSforsyth }
397*28942eadSforsyth print("pass 2\n");
398*28942eadSforsyth }
39937da2899SCharles.Forsyth rv = logfsextentlistwalk(e->u.file.extent, addpagereferences, &ds);
400*28942eadSforsyth if(rv >= 0){
401*28942eadSforsyth for(x = 0; x < ds.nentries; x++){
402*28942eadSforsyth p = &ds.array[x];
403*28942eadSforsyth seq = p->pageaddr >> server->ll->l2pagesperblock;
404*28942eadSforsyth page = p->pageaddr & ((1 << server->ll->l2pagesperblock) - 1);
405*28942eadSforsyth if(server->trace > 1)
406*28942eadSforsyth print("block %lud page %ud ref %d\n", seq, page, p->ref);
407*28942eadSforsyth if(p->ref == 0)
408*28942eadSforsyth logfsfreedatapages(server, seq, logfsdatapagemask(1, page));
40937da2899SCharles.Forsyth }
41037da2899SCharles.Forsyth }
411*28942eadSforsyth Out:
41237da2899SCharles.Forsyth logfsfreemem(ds.array);
41337da2899SCharles.Forsyth return rv < 0 ? Enomem : nil;
41437da2899SCharles.Forsyth }
41537da2899SCharles.Forsyth
41637da2899SCharles.Forsyth static void
disposeofoldblock(LogfsServer * server,AllocState * state)41737da2899SCharles.Forsyth disposeofoldblock(LogfsServer *server, AllocState *state)
41837da2899SCharles.Forsyth {
41937da2899SCharles.Forsyth if(state->oldblock >= 0) {
42037da2899SCharles.Forsyth if(server->testflags & LogfsTestDontFettleDataBlock) {
42137da2899SCharles.Forsyth /* take the block out of commission */
42237da2899SCharles.Forsyth (*server->ll->setblocktag)(server->ll, state->oldblock, LogfsTworse);
42337da2899SCharles.Forsyth server->testflags &= ~LogfsTestDontFettleDataBlock;
42437da2899SCharles.Forsyth }
42537da2899SCharles.Forsyth else {
42637da2899SCharles.Forsyth if(state->markbad)
42737da2899SCharles.Forsyth (*server->ll->markblockbad)(server->ll, state->oldblock);
42837da2899SCharles.Forsyth else
42937da2899SCharles.Forsyth logfsbootfettleblock(server->lb, state->oldblock, LogfsTnone, ~0, nil);
43037da2899SCharles.Forsyth }
43137da2899SCharles.Forsyth state->oldblock = -1;
43237da2899SCharles.Forsyth }
43337da2899SCharles.Forsyth }
43437da2899SCharles.Forsyth
43537da2899SCharles.Forsyth char *
logfsserverwrite(LogfsServer * server,u32int fid,u32int offset,u32int count,uchar * buf,u32int * rcount)43637da2899SCharles.Forsyth logfsserverwrite(LogfsServer *server, u32int fid, u32int offset, u32int count, uchar *buf, u32int *rcount)
43737da2899SCharles.Forsyth {
43837da2899SCharles.Forsyth Fid *f;
43937da2899SCharles.Forsyth Entry *e;
44037da2899SCharles.Forsyth u32int now;
44137da2899SCharles.Forsyth char *muid;
44237da2899SCharles.Forsyth int muidlen;
44337da2899SCharles.Forsyth LogfsLowLevel *ll = server->ll;
44437da2899SCharles.Forsyth
44537da2899SCharles.Forsyth if(server->trace > 1)
44637da2899SCharles.Forsyth print("logfsserverwrite(%ud, %ud, %ud)\n", fid, offset, count);
44737da2899SCharles.Forsyth f = logfsfidmapfindentry(server->fidmap, fid);
44837da2899SCharles.Forsyth if(f == nil)
44937da2899SCharles.Forsyth return logfsebadfid;
45037da2899SCharles.Forsyth if(f->openmode < 0)
45137da2899SCharles.Forsyth return logfsefidnotopen;
45237da2899SCharles.Forsyth if((f->openmode & 3) == OREAD)
45337da2899SCharles.Forsyth return logfseaccess;
45437da2899SCharles.Forsyth e = f->entry;
45537da2899SCharles.Forsyth if(e->deadandgone)
45637da2899SCharles.Forsyth return Eio;
45737da2899SCharles.Forsyth if(e->qid.type & QTDIR)
45837da2899SCharles.Forsyth return Eperm;
45937da2899SCharles.Forsyth if(e->perm & DMAPPEND)
46037da2899SCharles.Forsyth offset = e->u.file.length;
46137da2899SCharles.Forsyth now = logfsnow();
46237da2899SCharles.Forsyth *rcount = count;
46337da2899SCharles.Forsyth muid = logfsisfindidfromname(server->is, f->uname);
46437da2899SCharles.Forsyth muidlen = strlen(muid);
46537da2899SCharles.Forsyth while(count) {
46637da2899SCharles.Forsyth Extent extent;
46737da2899SCharles.Forsyth int thistime;
46837da2899SCharles.Forsyth char *errmsg;
46937da2899SCharles.Forsyth thistime = lognicesizeforwrite(server, 1, count, muidlen);
47037da2899SCharles.Forsyth if(thistime == 0) {
47137da2899SCharles.Forsyth int p;
47237da2899SCharles.Forsyth u32int n;
47337da2899SCharles.Forsyth long blockindex;
47437da2899SCharles.Forsyth int pagebase;
47537da2899SCharles.Forsyth AllocState state;
47637da2899SCharles.Forsyth int pagesize = 1 << ll->l2pagesize;
47737da2899SCharles.Forsyth reallocate:
47837da2899SCharles.Forsyth errmsg = allocdatapages(server, count, &thistime, &blockindex, &pagebase, &extent.flashaddr, &state);
47937da2899SCharles.Forsyth if(errmsg)
48037da2899SCharles.Forsyth return errmsg;
48137da2899SCharles.Forsyth if(thistime == 0)
48237da2899SCharles.Forsyth return logfselogfull;
48337da2899SCharles.Forsyth for(p = pagebase, n = 0; n < thistime; p++, n += pagesize) {
48437da2899SCharles.Forsyth u32int mask;
48537da2899SCharles.Forsyth DataBlock *db = server->datablock + blockindex;
48637da2899SCharles.Forsyth errmsg = (*ll->writepage)(ll, buf + n, db->block, p);
48737da2899SCharles.Forsyth if(errmsg) {
48837da2899SCharles.Forsyth if(strcmp(errmsg, Eio) != 0) {
48937da2899SCharles.Forsyth /*
49037da2899SCharles.Forsyth * something horrid happened down below
49137da2899SCharles.Forsyth * recover without writing any more than we have to
49237da2899SCharles.Forsyth */
49337da2899SCharles.Forsyth if(p != 0) {
49437da2899SCharles.Forsyth /*
49537da2899SCharles.Forsyth * page 0 was either written already, or has been written in this loop
49637da2899SCharles.Forsyth * thus the block referenced is valid on the media. all we need to do
49737da2899SCharles.Forsyth * is lose the old block, mark the written pages as free (so they can
49837da2899SCharles.Forsyth * be scavenged), and don't bother with the log message
49937da2899SCharles.Forsyth */
50037da2899SCharles.Forsyth disposeofoldblock(server, &state);
50137da2899SCharles.Forsyth mask = logfsdatapagemask(p - pagebase - 1, pagebase);
50237da2899SCharles.Forsyth db->free |= mask;
50337da2899SCharles.Forsyth db->dirty |= mask;
50437da2899SCharles.Forsyth return errmsg;
50537da2899SCharles.Forsyth }
50637da2899SCharles.Forsyth /*
50737da2899SCharles.Forsyth * page 0 failed to write (so nothing written at all)
50837da2899SCharles.Forsyth * this is either an entirely free block (no erased block in savestate),
50937da2899SCharles.Forsyth * or a copy of a scavenged block (erased block in savestate)
51037da2899SCharles.Forsyth */
51137da2899SCharles.Forsyth if(state.oldblock < 0) {
51237da2899SCharles.Forsyth /*
51337da2899SCharles.Forsyth * newly selected erased block (blockindex == server->ndatablocks - 1)
51437da2899SCharles.Forsyth * mark it bad, lose it from the datablock table
51537da2899SCharles.Forsyth */
51637da2899SCharles.Forsyth (*ll->markblockbad)(ll, db->block);
51737da2899SCharles.Forsyth db->block = -1;
51837da2899SCharles.Forsyth if(blockindex == server->ndatablocks - 1)
51937da2899SCharles.Forsyth server->ndatablocks--;
52037da2899SCharles.Forsyth return errmsg;
52137da2899SCharles.Forsyth }
52237da2899SCharles.Forsyth /*
52337da2899SCharles.Forsyth * page 0 of a data scavenge copy
52437da2899SCharles.Forsyth * mark it bad, restore state (old block)
52537da2899SCharles.Forsyth */
52637da2899SCharles.Forsyth (*ll->markblockbad)(ll, db->block);
52737da2899SCharles.Forsyth db->block = state.oldblock;
52837da2899SCharles.Forsyth return errmsg;
52937da2899SCharles.Forsyth }
53037da2899SCharles.Forsyth /*
53137da2899SCharles.Forsyth * write error on target block
53237da2899SCharles.Forsyth *
53337da2899SCharles.Forsyth * if it is a replacement (state saved)
53437da2899SCharles.Forsyth * mark the new block bad, restore state and try again
53537da2899SCharles.Forsyth *
53637da2899SCharles.Forsyth * if it is not replaced (no state saved)
53737da2899SCharles.Forsyth * replace block, and try again
53837da2899SCharles.Forsyth */
53937da2899SCharles.Forsyth if(state.oldblock >= 0) {
54037da2899SCharles.Forsyth (*ll->markblockbad)(ll, db->block);
54137da2899SCharles.Forsyth db->block = state.oldblock;
54237da2899SCharles.Forsyth }
54337da2899SCharles.Forsyth else {
54437da2899SCharles.Forsyth errmsg = logfsserverreplacedatablock(server, blockindex);
54537da2899SCharles.Forsyth if(errmsg)
54637da2899SCharles.Forsyth return errmsg;
54737da2899SCharles.Forsyth }
54837da2899SCharles.Forsyth goto reallocate;
54937da2899SCharles.Forsyth }
55037da2899SCharles.Forsyth mask = logfsdatapagemask(1, p);
55137da2899SCharles.Forsyth db->free &= ~mask;
55237da2899SCharles.Forsyth db->dirty |= mask;
55337da2899SCharles.Forsyth }
55437da2899SCharles.Forsyth /* well, we managed to write the data out */
55537da2899SCharles.Forsyth errmsg = logfslogwrite(server, 1, e->qid.path, offset, thistime, now, e->u.file.cvers,
55637da2899SCharles.Forsyth muid, nil, &extent.flashaddr);
55737da2899SCharles.Forsyth /*
55837da2899SCharles.Forsyth * now we can dispose of the original data block, if any
55937da2899SCharles.Forsyth * this is regardless of whether we succeeded in writing a log message, as
56037da2899SCharles.Forsyth * if this block is not erased, there will be a duplicate
56137da2899SCharles.Forsyth */
56237da2899SCharles.Forsyth disposeofoldblock(server, &state);
56337da2899SCharles.Forsyth }
56437da2899SCharles.Forsyth else {
56537da2899SCharles.Forsyth if(thistime > count)
56637da2899SCharles.Forsyth thistime = count;
56737da2899SCharles.Forsyth errmsg = logfslogwrite(server, 1, e->qid.path, offset, thistime, now, e->u.file.cvers,
56837da2899SCharles.Forsyth muid, buf, &extent.flashaddr);
56937da2899SCharles.Forsyth }
57037da2899SCharles.Forsyth /*
57137da2899SCharles.Forsyth * here if we failed to write the log message
57237da2899SCharles.Forsyth */
57337da2899SCharles.Forsyth if(errmsg)
57437da2899SCharles.Forsyth return errmsg;
57537da2899SCharles.Forsyth if(server->trace > 1)
57637da2899SCharles.Forsyth print("logfsserverwrite: %d bytes at flashaddr 0x%.8ux\n", thistime, extent.flashaddr);
57737da2899SCharles.Forsyth extent.min = offset;
57837da2899SCharles.Forsyth extent.max = offset + thistime;
57937da2899SCharles.Forsyth errmsg = zappages(server, e, extent.min, extent.max);
58037da2899SCharles.Forsyth if(errmsg)
58137da2899SCharles.Forsyth return errmsg;
58237da2899SCharles.Forsyth errmsg = logfsextentlistinsert(e->u.file.extent, &extent, nil);
58337da2899SCharles.Forsyth if(errmsg)
58437da2899SCharles.Forsyth return errmsg;
58537da2899SCharles.Forsyth e->muid = muid;
58637da2899SCharles.Forsyth e->mtime = now;
58737da2899SCharles.Forsyth offset += thistime;
58837da2899SCharles.Forsyth if(e->u.file.length < offset)
58937da2899SCharles.Forsyth e->u.file.length = offset;
59037da2899SCharles.Forsyth count -= thistime;
59137da2899SCharles.Forsyth buf += thistime;
59237da2899SCharles.Forsyth e->qid.vers++;
59337da2899SCharles.Forsyth }
59437da2899SCharles.Forsyth return nil;
59537da2899SCharles.Forsyth }
596