summaryrefslogtreecommitdiff
path: root/liblogfs/replace.c
diff options
context:
space:
mode:
Diffstat (limited to 'liblogfs/replace.c')
-rw-r--r--liblogfs/replace.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/liblogfs/replace.c b/liblogfs/replace.c
new file mode 100644
index 00000000..b29e4c28
--- /dev/null
+++ b/liblogfs/replace.c
@@ -0,0 +1,175 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "local.h"
+#include "fcall.h"
+
+static char *
+copypages(LogfsServer *server, long newb, long oldb, ulong copymask, LogfsLowLevelReadResult *llrrp, int *markedbadp)
+{
+ char *errmsg;
+ int page;
+ LogfsLowLevel *ll;
+ int ppb;
+ int pagesize;
+ uchar *buf;
+
+ if(copymask == 0)
+ return nil;
+
+ ll = server->ll;
+ ppb = 1 << ll->l2pagesperblock;
+ pagesize = 1 << ll->l2pagesize;
+ *markedbadp = 0;
+ *llrrp = LogfsLowLevelReadResultOk;
+ errmsg = nil;
+
+ buf = logfsrealloc(nil, 1 << ll->l2pagesize);
+ if(buf == nil)
+ return Enomem;
+
+ for(page = ppb - 1; page >= 0; page--) {
+ ulong m;
+
+ m = logfsdatapagemask(1, page);
+
+ if(copymask & m) {
+ LogfsLowLevelReadResult llrr;
+ if(server->trace > 1)
+ print("copypages read page %d\n", page);
+ errmsg = (*ll->readpagerange)(ll, buf, oldb, page, 0, pagesize, &llrr);
+ if(errmsg != nil)
+ break;
+ if(llrr > *llrrp)
+ *llrrp = llrr;
+ if(server->trace > 1)
+ print("copypages write page %d\n", page);
+ errmsg = (*ll->writepage)(ll, buf, newb, page);
+ if(errmsg) {
+ if(strcmp(errmsg, Eio) == 0) {
+ (*ll->markblockbad)(ll, newb);
+ *markedbadp = 1;
+ }
+ break;
+ }
+ if(server->trace > 1)
+ print("copypages end page %d\n", page);
+ }
+ }
+ logfsfreemem(buf);
+ return errmsg;
+}
+
+char *
+logfsservercopyactivedata(LogfsServer *server, long newb, long oldblockindex, int forcepage0, LogfsLowLevelReadResult *llrrp, int *markedbadp)
+{
+ LogfsLowLevel *ll = server->ll;
+ ulong newpath;
+ DataBlock *ob;
+ char *errmsg;
+
+ ob = server->datablock + oldblockindex;
+ if(forcepage0) {
+ u32int mask;
+ mask = logfsdatapagemask(1, 0);
+ if(ob->free & mask) {
+ ob->dirty |= mask;
+ ob->free |= mask;
+ }
+ }
+ if(server->trace > 1)
+ print("copyactivedata %ld: (%ld -> %ld)\n", oldblockindex, ob->block, newb);
+ newpath = mkdatapath(dataseqof(ob->path), copygensucc(copygenof(ob->path)));
+ (*ll->setblocktag)(ll, newb, LogfsTdata);
+ (*ll->setblockpath)(ll, newb, newpath);
+ errmsg = copypages(server, newb, ob->block, ~ob->free, llrrp, markedbadp);
+ if(errmsg)
+ return errmsg;
+ /*
+ * anything dirty and free is now not dirty and free
+ */
+ ob->dirty &= ~(ob->dirty & ob->free);
+ ob->block = newb;
+ ob->path = newpath;
+ return nil;
+}
+
+/*
+ * unconditionally replace a datablock, and mark the old one bad
+ * NB: if page 0 is apparently unused, force it to be copied, and mark
+ * it free and dirty afterwards
+ */
+char *
+logfsserverreplacedatablock(LogfsServer *server, long index)
+{
+ long newb;
+ LogfsLowLevel *ll = server->ll;
+
+ newb = logfsfindfreeblock(ll, AllocReasonReplace);
+ /* TODO - recover space by scavenging other blocks, or recycling the log */
+ while(newb >= 0) {
+ char *errmsg;
+ LogfsLowLevelReadResult llrr;
+ long oldblock;
+ int markedbad;
+ DataBlock *db;
+
+ db = server->datablock + index;
+ oldblock = db->block;
+ errmsg = logfsservercopyactivedata(server, newb, index, 1, &llrr, &markedbad);
+ if(errmsg) {
+ if(!markedbad)
+ return errmsg;
+ newb = logfsfindfreeblock(ll, AllocReasonReplace);
+ continue;
+ }
+ (*ll->markblockbad)(ll, oldblock);
+ return nil;
+ }
+ return logfsefullreplacing;
+}
+
+char *
+logfsserverreplacelogblock(LogfsServer *server, LogSegment *seg, long index)
+{
+ ulong opath;
+ LogfsLowLevel *ll = server->ll;
+ long oldb = seg->blockmap[index];
+
+ opath = (*ll->getblockpath)(ll, oldb);
+
+ for(;;) {
+ long newb;
+ int pages;
+ char *errmsg;
+ LogfsLowLevelReadResult llrr;
+ int markedbad;
+
+ newb = logfsfindfreeblock(ll, AllocReasonReplace);
+ if(newb < 0)
+ return "full replacing log block";
+ /* TODO - scavenge data space for a spare block */
+ (*ll->setblocktag)(ll, newb, LogfsTlog);
+ (*ll->setblockpath)(ll, newb, mklogpath(seg->gen, index, copygensucc(copygenof(opath))));
+ if(index == seg->curblockindex)
+ pages = seg->curpage;
+ else
+ pages = 1 << server->ll->l2pagesperblock;
+ errmsg = copypages(server, newb, oldb, logfsdatapagemask(pages, 0), &llrr, &markedbad);
+ if(errmsg == nil) {
+ (*ll->markblockbad)(ll, seg->blockmap[index]);
+ seg->blockmap[index] = newb;
+ return nil;
+ }
+ if(!markedbad)
+ return errmsg;
+ }
+}
+
+char *
+logfsserverreplaceblock(LogfsServer *server, LogSegment *seg, long index)
+{
+ if(seg)
+ return logfsserverreplacelogblock(server, seg, index);
+ else
+ return logfsserverreplacedatablock(server, index);
+}