xref: /inferno-os/liblogfs/replace.c (revision 28942ead413418b56c5be78e8c4c400881fba72e)
1 #include "logfsos.h"
2 #include "logfs.h"
3 #include "local.h"
4 #include "fcall.h"
5 
6 static char *
copypages(LogfsServer * server,long newb,long oldb,Pageset copymask,LogfsLowLevelReadResult * llrrp,int * markedbadp)7 copypages(LogfsServer *server, long newb, long oldb, Pageset copymask, LogfsLowLevelReadResult *llrrp, int *markedbadp)
8 {
9 	char *errmsg;
10 	int page;
11 	LogfsLowLevel *ll;
12 	int ppb;
13 	int pagesize;
14 	uchar *buf;
15 
16 	if(copymask == 0)
17 		return nil;
18 
19 	ll = server->ll;
20 	ppb = 1 << ll->l2pagesperblock;
21 	pagesize = 1 << ll->l2pagesize;
22 	*markedbadp = 0;
23 	*llrrp = LogfsLowLevelReadResultOk;
24 	errmsg = nil;
25 
26 	buf = logfsrealloc(nil, 1 << ll->l2pagesize);
27 	if(buf == nil)
28 		return Enomem;
29 
30 	for(page = ppb - 1; page >= 0; page--) {
31 		Pageset m;
32 
33 		m = logfsdatapagemask(1, page);
34 
35 		if(copymask & m) {
36 			LogfsLowLevelReadResult llrr;
37 			if(server->trace > 1)
38 				print("copypages read page %d\n", page);
39 			errmsg = (*ll->readpagerange)(ll, buf, oldb, page, 0, pagesize, &llrr);
40 			if(errmsg != nil)
41 				break;
42 			if(llrr > *llrrp)
43 				*llrrp = llrr;
44 			if(server->trace > 1)
45 				print("copypages write page %d\n", page);
46 			errmsg = (*ll->writepage)(ll, buf, newb, page);
47 			if(errmsg) {
48 				if(strcmp(errmsg, Eio) == 0) {
49 					(*ll->markblockbad)(ll, newb);
50 					*markedbadp = 1;
51 				}
52 				break;
53 			}
54 			if(server->trace > 1)
55 				print("copypages end page %d\n", page);
56 		}
57 	}
58 	logfsfreemem(buf);
59 	return errmsg;
60 }
61 
62 char *
logfsservercopyactivedata(LogfsServer * server,long newb,long oldblockindex,int forcepage0,LogfsLowLevelReadResult * llrrp,int * markedbadp)63 logfsservercopyactivedata(LogfsServer *server, long newb, long oldblockindex, int forcepage0, LogfsLowLevelReadResult *llrrp, int *markedbadp)
64 {
65 	LogfsLowLevel *ll = server->ll;
66 	ulong newpath;
67 	DataBlock *ob;
68 	char *errmsg;
69 	Pageset copymask;
70 
71 	ob = server->datablock + oldblockindex;
72 	copymask = ~ob->free;
73 	if(forcepage0)
74 		copymask |= logfsdatapagemask(1, 0);
75 	if(server->trace > 1)
76 		print("copyactivedata %ld: (%ld -> %ld)\n", oldblockindex, ob->block, newb);
77 	newpath = mkdatapath(dataseqof(ob->path), copygensucc(copygenof(ob->path)));
78 	(*ll->setblocktag)(ll, newb, LogfsTdata);
79 	(*ll->setblockpath)(ll, newb, newpath);
80 	errmsg = copypages(server, newb, ob->block, copymask, llrrp, markedbadp);
81 	if(errmsg)
82 		return errmsg;
83 	/*
84 	 * anything not copied is now not dirty
85 	 */
86 	ob->dirty &= copymask;
87 	ob->block = newb;
88 	ob->path = newpath;
89 	return nil;
90 }
91 
92 /*
93  * unconditionally replace a datablock, and mark the old one bad
94  * NB: if page 0 is apparently unused, force it to be copied, and mark
95  * it free and dirty afterwards
96  */
97 char *
logfsserverreplacedatablock(LogfsServer * server,long index)98 logfsserverreplacedatablock(LogfsServer *server, long index)
99 {
100 	long newb;
101 	LogfsLowLevel *ll = server->ll;
102 
103 	newb = logfsfindfreeblock(ll, AllocReasonReplace);
104 	/* TODO - recover space by scavenging other blocks, or recycling the log */
105 	while(newb >= 0) {
106 		char *errmsg;
107 		LogfsLowLevelReadResult llrr;
108 		long oldblock;
109 		int markedbad;
110 		DataBlock *db;
111 
112 		db = server->datablock + index;
113 		oldblock = db->block;
114 		errmsg = logfsservercopyactivedata(server, newb, index, 1, &llrr, &markedbad);
115 		if(errmsg) {
116 			if(!markedbad)
117 				return errmsg;
118 			newb = logfsfindfreeblock(ll, AllocReasonReplace);
119 			continue;
120 		}
121 		(*ll->markblockbad)(ll, oldblock);
122 		return nil;
123 	}
124 	return logfsefullreplacing;
125 }
126 
127 char *
logfsserverreplacelogblock(LogfsServer * server,LogSegment * seg,long index)128 logfsserverreplacelogblock(LogfsServer *server, LogSegment *seg, long index)
129 {
130 	ulong opath;
131 	LogfsLowLevel *ll = server->ll;
132 	long oldb = seg->blockmap[index];
133 
134 	opath = (*ll->getblockpath)(ll, oldb);
135 
136 	for(;;) {
137 		long newb;
138 		int pages;
139 		char *errmsg;
140 		LogfsLowLevelReadResult llrr;
141 		int markedbad;
142 
143 		newb  = logfsfindfreeblock(ll, AllocReasonReplace);
144 		if(newb < 0)
145 			return "full replacing log block";
146 		/* TODO - scavenge data space for a spare block */
147 		(*ll->setblocktag)(ll, newb, LogfsTlog);
148 		(*ll->setblockpath)(ll, newb, mklogpath(seg->gen, index, copygensucc(copygenof(opath))));
149 		if(index == seg->curblockindex)
150 			pages = seg->curpage;
151 		else
152 			pages = 1 << server->ll->l2pagesperblock;
153 		errmsg = copypages(server, newb, oldb, logfsdatapagemask(pages, 0), &llrr, &markedbad);
154 		if(errmsg == nil) {
155 			(*ll->markblockbad)(ll, seg->blockmap[index]);
156 			seg->blockmap[index] = newb;
157 			return nil;
158 		}
159 		if(!markedbad)
160 			return errmsg;
161 	}
162 }
163 
164 char *
logfsserverreplaceblock(LogfsServer * server,LogSegment * seg,long index)165 logfsserverreplaceblock(LogfsServer *server, LogSegment *seg, long index)
166 {
167 	if(seg)
168 		return logfsserverreplacelogblock(server, seg, index);
169 	else
170 		return logfsserverreplacedatablock(server, index);
171 }
172