diff options
Diffstat (limited to 'liblogfs/replace.c')
| -rw-r--r-- | liblogfs/replace.c | 175 |
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); +} |
