xref: /inferno-os/liblogfs/write.c (revision 28942ead413418b56c5be78e8c4c400881fba72e)
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