📄 ck_checkpoint.cxx
字号:
/* * Copyright (C) 1998, 1999, 2001, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <disk/DiskNode.hxx>#include <disk/PagePot.hxx>#include <kerninc/kernel.hxx>#include <kerninc/MsgLog.hxx>#include <kerninc/Check.hxx>#include <kerninc/Checkpoint.hxx>#include <kerninc/Persist.hxx>#include <kerninc/ObjectCache.hxx>#include <kerninc/Thread.hxx>#include <kerninc/CpuReserve.hxx>#include <kerninc/Depend.hxx>#include <kerninc/BlockDev.hxx>#include <kerninc/Check.hxx>#include <kerninc/Invocation.hxx>#include <kerninc/Machine.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/Console.hxx>#include <arch-kerninc/PTE.hxx>#define dbg_ckpt 0x1 /* steps in taking snapshot */#define dbg_ckage 0x2 /* migration state machine */#define dbg_mig 0x4 /* migration state machine */#define dbg_ckdir 0x8 /* steps in ckdir reconstruction */#define dbg_reservation 0x10 /* reservation logic */#define dbg_wrckdir 0x20 /* writing ckdir */#define dbg_dirent 0x40 /* dirent manipulation */#define dbg_stabilize 0x80 /* stabilization */#define dbg_mignode 0x100 /* actual object migration */#define dbg_migpage 0x200 /* actual object migration */#define dbg_migfinish 0x400 /* actual object migration *//* Following should be an OR of some of the above */#define dbg_flags ( dbg_migfinish | 0u )#define DBCOND(x) (dbg_##x & dbg_flags)#define DEBUG(x) if DBCOND(x)#define DEBUG2(x,y) if ((dbg_##x|dbg_##y) & dbg_flags)bool Checkpoint::enabled = false;CoreGeneration Checkpoint::coreGeneration[nGeneration];DiskCheckpoint *Checkpoint::lastCkptHdr = 0;ObjectHeader *Checkpoint::lastCkptObHdr = 0;lid_t Checkpoint::lastCkptHdrLid = 0;DiskCheckpoint *Checkpoint::nextCkptHdr = 0;ObjectHeader *Checkpoint::nextCkptObHdr = 0;lid_t Checkpoint::nextCkptHdrLid = 0;ObjectHeader *Checkpoint::diskDirPageHdr = 0;ObjectHeader *Checkpoint::threadDirHdr[nThreadDirPage];ThreadDirPage *Checkpoint::threadDirPg[nThreadDirPage];lid_t Checkpoint::lastThreadDirLid[nThreadDirPage];lid_t Checkpoint::nextThreadDirLid[nThreadDirPage];ObjectHeader *Checkpoint::reserveDirHdr[nReserveDirPage];ReserveDirPage *Checkpoint::reserveDirPg[nReserveDirPage];lid_t Checkpoint::lastReserveDirLid[nReserveDirPage];lid_t Checkpoint::nextReserveDirLid[nReserveDirPage];ObjectHeader *Checkpoint::nextDirPageHdr;CkptDirPage *Checkpoint::nextDirPage;AllocMap Checkpoint::allocMap;logframe_t Checkpoint::LastAllocatedFrame = 0;uint32_t Checkpoint::totLogFrame = 0;uint32_t Checkpoint::nAvailLogFrame = 0;uint32_t Checkpoint::nReservedLogFrame = 0;uint32_t Checkpoint::nAllocatedLogFrame = 0;uint32_t Checkpoint::nMasterPage = 0;Checkpoint::MigrationStatus Checkpoint::migrationStatus;uint32_t Checkpoint::nCheckpointsCompleted = 0;uint32_t Checkpoint::ckGenLimit;voidCheckpoint::SwapCheckpointHeaders(){ ObjectHeader *oldLastCkptObHdr = lastCkptObHdr; DiskCheckpoint *oldLastCkptHdr = lastCkptHdr; lid_t oldLastCkptHdrLid = lastCkptHdrLid; lastCkptObHdr = nextCkptObHdr; lastCkptHdr = nextCkptHdr; lastCkptHdrLid = nextCkptHdrLid; nextCkptObHdr = oldLastCkptObHdr; nextCkptHdr = oldLastCkptHdr; nextCkptHdrLid = oldLastCkptHdrLid;}voidCheckpoint::SwapThreadDirs(){ for (uint32_t i = 0; i < nThreadDirPage; i++) { lid_t oldLid = lastThreadDirLid[i]; lastThreadDirLid[i] = nextThreadDirLid[i]; nextThreadDirLid[i] = oldLid; } for (uint32_t i = 0; i < nReserveDirPage; i++) { lid_t oldLid = lastReserveDirLid[i]; lastReserveDirLid[i] = nextReserveDirLid[i]; nextReserveDirLid[i] = oldLid; }}/* Unpreparing the dirty nodes will have the effect of flushing all of * the context caches. */voidCheckpoint::MarkDirtyNodesForCkpt(){ for (uint32_t nd = 0; nd < ObjectCache::TotalNodes(); nd++) { Node *pNode = ObjectCache::GetCoreNodeFrame(nd); if (pNode->IsDirty()) { assert (pNode->GetFlags(OFLG_CKPT) == false); for (unsigned i = 0; i < EROS_NODE_SIZE; i++) if (pNode->slot[i].IsRdHazard()) pNode->ClearHazard(i); pNode->SetFlags(OFLG_CKPT); } }}/* Mark dirty pages for COW and ckpt, and disable write permissions on * all outstanding PTE's to such pages. */voidCheckpoint::MarkDirtyPagesForCkpt(){#ifdef OPTION_SMALL_SPACES Process::WriteDisableSmallSpaces();#endif for (uint32_t pg = 0; pg < ObjectCache::TotalPages(); pg++) { ObjectHeader *pObj = ObjectCache::GetCorePageFrame(pg); switch(pObj->obType) { case ObType::PtDataPage: if (pObj->GetFlags(OFLG_REDIRTY) && !pObj->GetFlags(OFLG_DIRTY)) MsgLog::dprintf(true, "Checkpointing redirtied page!!\n"); if (pObj->IsDirty()) { assert (pObj->GetFlags(OFLG_CKPT) == 0); pObj->SetFlags(OFLG_CKPT); } break;#ifdef USES_MAPPING_PAGES case ObType::PtMappingPage: Depend_WriteDisableProduct(pObj); break;#endif } }#ifdef DBG_WILD_PTR if (dbg_wild_ptr) Check::Consistency("After marking pgs for ckpt");#endif}/* Initialize all fields of the soon-to-be-written checkpoint header * that we can. We will update nDirPage, nThreadPage, and maxLogLoc * as we construct and write out the various directories. */ voidCheckpoint::InitNextCkptHdr(){ nextCkptHdr->nThreadPage = 0; nextCkptHdr->nRsrvPage = 0; nextCkptHdr->nDirPage = 0; nextCkptHdr->hasMigrated = false; nextCkptHdr->sequenceNumber = lastCkptHdr->sequenceNumber + 1; nextCkptHdr->maxLogLid = 2 * EROS_OBJECTS_PER_FRAME; nextCkptObHdr->SetDirtyFlag();}voidCheckpoint::SnapshotReserves(){ uint32_t r = 0; coreGeneration[current].nReserve = 0; while (r < MAX_CPU_RESERVE) { ReserveDirPage* rdp = reserveDirPg[r / ReserveDirPage::maxDirEnt]; reserveDirHdr[r / ReserveDirPage::maxDirEnt]->SetDirtyFlag(); uint32_t count = 0; for (; count < ReserveDirPage::maxDirEnt && r < MAX_CPU_RESERVE; count++, r++) { CpuReserve& rsrv = CpuReserve::CpuReserveTable[r]; CpuReserveInfo& ckrsrv = rdp->entry[count]; ckrsrv.index = r; ckrsrv.quanta = rsrv.quanta; ckrsrv.duration = rsrv.duration; ckrsrv.period = rsrv.period; ckrsrv.start = rsrv.start; ckrsrv.normPrio = rsrv.normPrio; ckrsrv.rsrvPrio = rsrv.rsrvPrio; coreGeneration[current].nReserve++; } rdp->nDirent = count; assert(nextReserveDirLid[nextCkptHdr->nRsrvPage]); nextCkptHdr->dirPage[nextCkptHdr->nRsrvPage] = nextReserveDirLid[nextCkptHdr->nRsrvPage]; nextCkptHdr->nRsrvPage++; }}voidCheckpoint::SnapshotThreads(){ uint32_t t = 0; coreGeneration[current].nThread = 0; while (t < KTUNE_NTHREAD) { ThreadDirPage* tdp = threadDirPg[t / ThreadDirPage::maxDirEnt]; threadDirHdr[t / ThreadDirPage::maxDirEnt]->SetDirtyFlag(); uint32_t count = 0; for (; count < ThreadDirPage::maxDirEnt && t < KTUNE_NTHREAD; t++) { Thread& thrd = Thread::ThreadTable[t]; thrd.pageWriteCount = 0; if (thrd.state == Thread::Free) continue; if (thrd.state != Thread::Free && thrd.processKey.IsObjectKey() == false) { /* This thread contained a key which was rescinded. It has * been scheduled to run, and will die at that time. Do not * bother to copy it. */ assert (thrd.state == Thread::Ready); assert (thrd.processKey.IsVoidKey());#if 0 MsgLog::dprintf(true, "Non-free thread 0x%08x with non-object key!\n", &thrd);#endif continue; } #if 0 /* Now that we are allowing mapping tables to persist in * read-only form, the thread no longer gets deprepared. */ /* Thread must be in deprepared form: */ assert (thrd.context == 0);#endif#if 0 /* Can the embodied domain key be prepared? -- I think probably so. */ assert (thrd.domainKey.IsPrepared() == false);#endif tdp->entry[count].oid = thrd.processKey.GetKeyOid(); tdp->entry[count].allocCount = thrd.processKey.GetAllocCount(); tdp->entry[count].schedNdx = thrd.cpuReserve - CpuReserve::CpuReserveTable; coreGeneration[current].nThread++; count++; DEBUG(ckdir) MsgLog::dprintf(false, "Add snap thrd 0x%08x%08x ac=0x%08x\n", (uint32_t) (tdp->entry[count].oid >> 32), (uint32_t) tdp->entry[count].oid, tdp->entry[count].allocCount); } tdp->nDirent = count; assert(nextThreadDirLid[nextCkptHdr->nThreadPage]); nextCkptHdr->dirPage[nextCkptHdr->nThreadPage + nextCkptHdr->nRsrvPage] = nextThreadDirLid[nextCkptHdr->nThreadPage]; nextCkptHdr->nThreadPage++; }}voidCheckpoint::AgeGenerations(){ DEBUG(ckage) MsgLog::dprintf(true, "Enter AgeGenerations\n"); /* First, forget the oldest generation present. */ CoreGeneration *cg = &coreGeneration[nGeneration-1]; assert (cg->canReclaim); DEBUG(ckage) MsgLog::dprintf(true, "Removing object entries\n"); cg->Reclaim(); assert(cg->alloc.nFrames == cg->rsrv.nFrames); assert(cg->rsrv.nDirent == cg->alloc.nCoreDirent); for (int g = last_ckpt; g < nGeneration; g++) coreGeneration[g].CheckConsistency(true); DEBUG(ckage) MsgLog::dprintf(true, "Shift Generations\n"); /* Next, age everything remaining one generation. */ for (int g = nGeneration - 2; g >= 0; g--) { bcopy(&coreGeneration[g], &coreGeneration[g+1], sizeof(CoreGeneration)); coreGeneration[g+1].index = g+1; } coreGeneration[current].Init(); DEBUG(ckage) MsgLog::dprintf(true, "Exit AgeGenerations\n");}voidCheckpoint::TakeCheckpoint(){#ifdef DBG_WILD_PTR if (dbg_wild_ptr) Check::Consistency("Top TakeCheckpoint()");#endif if (migrationStatus != mg_Idle) { ProcessMigration(); Thread::Current()->Yield(); } MsgLog::dprintf(false,"!"); DEBUG(ckpt) MsgLog::dprintf(true, "Enter TakeCheckpoint()\n"); MsgLog::dprintf(true, "Checkpoint is declared\n"); MarkDirtyNodesForCkpt(); MarkDirtyPagesForCkpt(); DEBUG(ckpt) MsgLog::dprintf(true, "Everything is now marked for checkpoint\n"); /* Ensure that all the invalidated PTE's stay that way: */ Machine::FlushTLB(); DEBUG(ckpt) MsgLog::dprintf(true, "TLB is flushed\n"); InitNextCkptHdr(); DEBUG(ckpt) MsgLog::dprintf(true, "Next ckpt hdr valid\n"); SnapshotReserves(); DEBUG(ckpt) MsgLog::dprintf(true, "Rsrvs are snapshotted\n"); SnapshotThreads(); DEBUG(ckpt) MsgLog::dprintf(true, "Threads are snapshotted\n"); AgeGenerations(); DEBUG(ckpt) MsgLog::dprintf(true, "Generations have been aged\n");#ifdef DBG_WILD_PTR if (dbg_wild_ptr) Check::Consistency("Bottom TakeCheckpoint()");#endif DEBUG2(ckpt,migfinish) { MsgLog::dprintf(false, "About to start migration()\n"); /* Check::Pages(); */ } StartMigration(true); /* Must yield, since current thread has been deprepared by the fact * of having taken a checkpoint! */ Thread::Current()->Yield();}/* Variables associated with stabilization and migration: */static uint32_t mg_nextCorePage;static uint32_t mg_nextCoreNode;static CoreDirent *mg_nextDirent;static uint32_t mg_nextThreadDirPg;static uint32_t mg_nextReserveDirPg;static OID mg_objectPotOID;static OID mg_tagPotOID;#ifndef NDEBUGstatic bool mg_SanityChecked;#endifconst char *Checkpoint::MigStateName(){ static char *names[Checkpoint::mg_nState] = { "Idle", "StartCheckpoint", "StabilizeNodes", "StabilizePages", "WriteDir", "DrainCkpt", "WriteHeader", "StartMigration", "MigrateObjects", "DrainMigration", "UpdateRangeHeaders", }; return names[migrationStatus];} charCheckpoint::MigStateAbbrev(){ static char letters[Checkpoint::mg_nState] = { ' ', /* idle shows blank */ 'C', /* start checkpoitn */ 'N', /* stabilize nodes */ 'P', /* stabilize pages */ 'D', /* writing directory */ 'W', /* drain wait */ 'H', /* write header */ 'm', /* start migration */ 'o', /* migrate objects */ 'w', /* migrate drain wait */ 'r', /* update range headers */ }; return letters[migrationStatus];}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -