|
Packit Service |
d40955 |
/*
|
|
Packit Service |
d40955 |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
d40955 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
d40955 |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
d40955 |
* of the License, or (at your option) any later version.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
d40955 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
d40955 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
d40955 |
* GNU General Public License for more details.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
d40955 |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
d40955 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
d40955 |
* 02110-1301, USA.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/referenceCountRebuild.c#6 $
|
|
Packit Service |
d40955 |
*/
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
#include "referenceCountRebuild.h"
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
#include "logger.h"
|
|
Packit Service |
d40955 |
#include "memoryAlloc.h"
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
#include "blockMap.h"
|
|
Packit Service |
d40955 |
#include "blockMapInternals.h"
|
|
Packit Service |
d40955 |
#include "blockMapPage.h"
|
|
Packit Service |
d40955 |
#include "forest.h"
|
|
Packit Service |
d40955 |
#include "constants.h"
|
|
Packit Service |
d40955 |
#include "numUtils.h"
|
|
Packit Service |
d40955 |
#include "refCounts.h"
|
|
Packit Service |
d40955 |
#include "slabDepot.h"
|
|
Packit Service |
d40955 |
#include "vdoInternal.h"
|
|
Packit Service |
d40955 |
#include "vdoPageCache.h"
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* A reference count rebuild completion.
|
|
Packit Service |
d40955 |
* Note that the page completions kept in this structure are not immediately
|
|
Packit Service |
d40955 |
* freed, so the corresponding pages will be locked down in the page cache
|
|
Packit Service |
d40955 |
* until the rebuild frees them.
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
typedef struct {
|
|
Packit Service |
d40955 |
/** completion header */
|
|
Packit Service |
d40955 |
VDOCompletion completion;
|
|
Packit Service |
d40955 |
/** the completion for flushing the block map */
|
|
Packit Service |
d40955 |
VDOCompletion subTaskCompletion;
|
|
Packit Service |
d40955 |
/** the thread on which all block map operations must be done */
|
|
Packit Service |
d40955 |
ThreadID logicalThreadID;
|
|
Packit Service |
d40955 |
/** the admin thread */
|
|
Packit Service |
d40955 |
ThreadID adminThreadID;
|
|
Packit Service |
d40955 |
/** the block map */
|
|
Packit Service |
d40955 |
BlockMap *blockMap;
|
|
Packit Service |
d40955 |
/** the slab depot */
|
|
Packit Service |
d40955 |
SlabDepot *depot;
|
|
Packit Service |
d40955 |
/** whether this recovery has been aborted */
|
|
Packit Service |
d40955 |
bool aborted;
|
|
Packit Service |
d40955 |
/** whether we are currently launching the initial round of requests */
|
|
Packit Service |
d40955 |
bool launching;
|
|
Packit Service |
d40955 |
/** The number of logical blocks observed used */
|
|
Packit Service |
d40955 |
BlockCount *logicalBlocksUsed;
|
|
Packit Service |
d40955 |
/** The number of block map data blocks */
|
|
Packit Service |
d40955 |
BlockCount *blockMapDataBlocks;
|
|
Packit Service |
d40955 |
/** the next page to fetch */
|
|
Packit Service |
d40955 |
PageCount pageToFetch;
|
|
Packit Service |
d40955 |
/** the number of leaf pages in the block map */
|
|
Packit Service |
d40955 |
PageCount leafPages;
|
|
Packit Service |
d40955 |
/** the last slot of the block map */
|
|
Packit Service |
d40955 |
BlockMapSlot lastSlot;
|
|
Packit Service |
d40955 |
/** number of pending (non-ready) requests*/
|
|
Packit Service |
d40955 |
PageCount outstanding;
|
|
Packit Service |
d40955 |
/** number of page completions */
|
|
Packit Service |
d40955 |
PageCount pageCount;
|
|
Packit Service |
d40955 |
/** array of requested, potentially ready page completions */
|
|
Packit Service |
d40955 |
VDOPageCompletion pageCompletions[];
|
|
Packit Service |
d40955 |
} RebuildCompletion;
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Convert a VDOCompletion to a RebuildCompletion.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completion The completion to convert
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @return The completion as a RebuildCompletion
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
__attribute__((warn_unused_result))
|
|
Packit Service |
d40955 |
static inline RebuildCompletion *asRebuildCompletion(VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
STATIC_ASSERT(offsetof(RebuildCompletion, completion) == 0);
|
|
Packit Service |
d40955 |
assertCompletionType(completion->type, REFERENCE_COUNT_REBUILD_COMPLETION);
|
|
Packit Service |
d40955 |
return (RebuildCompletion *) completion;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Free a RebuildCompletion and null out the reference to it.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completionPtr a pointer to the completion to free
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void freeRebuildCompletion(VDOCompletion **completionPtr)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
VDOCompletion *completion = *completionPtr;
|
|
Packit Service |
d40955 |
if (completion == NULL) {
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild = asRebuildCompletion(completion);
|
|
Packit Service |
d40955 |
destroyEnqueueable(&rebuild->subTaskCompletion);
|
|
Packit Service |
d40955 |
destroyEnqueueable(completion);
|
|
Packit Service |
d40955 |
FREE(rebuild);
|
|
Packit Service |
d40955 |
*completionPtr = NULL;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Free the RebuildCompletion and notify the parent that the block map
|
|
Packit Service |
d40955 |
* rebuild is done. This callback is registered in rebuildBlockMap().
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completion The RebuildCompletion
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void finishRebuild(VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
int result = completion->result;
|
|
Packit Service |
d40955 |
VDOCompletion *parent = completion->parent;
|
|
Packit Service |
d40955 |
freeRebuildCompletion(&completion);
|
|
Packit Service |
d40955 |
finishCompletion(parent, result);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Make a new rebuild completion.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param [in] vdo The VDO
|
|
Packit Service |
d40955 |
* @param [in] logicalBlocksUsed A pointer to hold the logical blocks used
|
|
Packit Service |
d40955 |
* @param [in] blockMapDataBlocks A pointer to hold the number of block map
|
|
Packit Service |
d40955 |
* data blocks
|
|
Packit Service |
d40955 |
* @param [in] parent The parent of the rebuild completion
|
|
Packit Service |
d40955 |
* @param [out] rebuildPtr The new block map rebuild completion
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @return a success or error code
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static int makeRebuildCompletion(VDO *vdo,
|
|
Packit Service |
d40955 |
BlockCount *logicalBlocksUsed,
|
|
Packit Service |
d40955 |
BlockCount *blockMapDataBlocks,
|
|
Packit Service |
d40955 |
VDOCompletion *parent,
|
|
Packit Service |
d40955 |
RebuildCompletion **rebuildPtr)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
BlockMap *blockMap = getBlockMap(vdo);
|
|
Packit Service |
d40955 |
PageCount pageCount
|
|
Packit Service |
d40955 |
= minPageCount(getConfiguredCacheSize(vdo) >> 1,
|
|
Packit Service |
d40955 |
MAXIMUM_SIMULTANEOUS_BLOCK_MAP_RESTORATION_READS);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild;
|
|
Packit Service |
d40955 |
int result = ALLOCATE_EXTENDED(RebuildCompletion, pageCount,
|
|
Packit Service |
d40955 |
VDOPageCompletion, __func__, &rebuild);
|
|
Packit Service |
d40955 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
d40955 |
return result;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
result = initializeEnqueueableCompletion(&rebuild->completion,
|
|
Packit Service |
d40955 |
REFERENCE_COUNT_REBUILD_COMPLETION,
|
|
Packit Service |
d40955 |
vdo->layer);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
VDOCompletion *completion = &rebuild->completion;
|
|
Packit Service |
d40955 |
freeRebuildCompletion(&completion);
|
|
Packit Service |
d40955 |
return result;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
result = initializeEnqueueableCompletion(&rebuild->subTaskCompletion,
|
|
Packit Service |
d40955 |
SUB_TASK_COMPLETION, vdo->layer);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
VDOCompletion *completion = &rebuild->completion;
|
|
Packit Service |
d40955 |
freeRebuildCompletion(&completion);
|
|
Packit Service |
d40955 |
return result;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
rebuild->blockMap = blockMap;
|
|
Packit Service |
d40955 |
rebuild->depot = vdo->depot;
|
|
Packit Service |
d40955 |
rebuild->logicalBlocksUsed = logicalBlocksUsed;
|
|
Packit Service |
d40955 |
rebuild->blockMapDataBlocks = blockMapDataBlocks;
|
|
Packit Service |
d40955 |
rebuild->pageCount = pageCount;
|
|
Packit Service |
d40955 |
rebuild->leafPages = computeBlockMapPageCount(blockMap->entryCount);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
const ThreadConfig *threadConfig = getThreadConfig(vdo);
|
|
Packit Service |
d40955 |
rebuild->logicalThreadID = getLogicalZoneThread(threadConfig, 0);
|
|
Packit Service |
d40955 |
rebuild->adminThreadID = getAdminThread(threadConfig);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
ASSERT_LOG_ONLY((getCallbackThreadID() == rebuild->logicalThreadID),
|
|
Packit Service |
d40955 |
"%s must be called on logical thread %u (not %u)", __func__,
|
|
Packit Service |
d40955 |
rebuild->logicalThreadID, getCallbackThreadID());
|
|
Packit Service |
d40955 |
prepareCompletion(&rebuild->completion, finishRebuild, finishRebuild,
|
|
Packit Service |
d40955 |
rebuild->logicalThreadID, parent);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
*rebuildPtr = rebuild;
|
|
Packit Service |
d40955 |
return VDO_SUCCESS;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Flush the block map now that all the reference counts are rebuilt. This
|
|
Packit Service |
d40955 |
* callback is registered in finishIfDone().
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completion The sub-task completion
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void flushBlockMapUpdates(VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
logInfo("Flushing block map changes");
|
|
Packit Service |
d40955 |
prepareToFinishParent(completion, completion->parent);
|
|
Packit Service |
d40955 |
drainBlockMap(asRebuildCompletion(completion->parent)->blockMap,
|
|
Packit Service |
d40955 |
ADMIN_STATE_RECOVERING, completion);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Check whether the rebuild is done. If it succeeded, continue by flushing the
|
|
Packit Service |
d40955 |
* block map.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param rebuild The rebuild completion
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @return true if the rebuild is complete
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static bool finishIfDone(RebuildCompletion *rebuild)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
if (rebuild->launching || (rebuild->outstanding > 0)) {
|
|
Packit Service |
d40955 |
return false;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if (rebuild->aborted) {
|
|
Packit Service |
d40955 |
completeCompletion(&rebuild->completion);
|
|
Packit Service |
d40955 |
return true;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if (rebuild->pageToFetch < rebuild->leafPages) {
|
|
Packit Service |
d40955 |
return false;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
prepareCompletion(&rebuild->subTaskCompletion, flushBlockMapUpdates,
|
|
Packit Service |
d40955 |
finishParentCallback, rebuild->adminThreadID, rebuild);
|
|
Packit Service |
d40955 |
invokeCallback(&rebuild->subTaskCompletion);
|
|
Packit Service |
d40955 |
return true;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Record that there has been an error during the rebuild.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param rebuild The rebuild completion
|
|
Packit Service |
d40955 |
* @param result The error result to use, if one is not already saved
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void abortRebuild(RebuildCompletion *rebuild, int result)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
rebuild->aborted = true;
|
|
Packit Service |
d40955 |
setCompletionResult(&rebuild->completion, result);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Handle an error loading a page.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completion The VDOPageCompletion
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void handlePageLoadError(VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild = asRebuildCompletion(completion->parent);
|
|
Packit Service |
d40955 |
rebuild->outstanding--;
|
|
Packit Service |
d40955 |
abortRebuild(rebuild, completion->result);
|
|
Packit Service |
d40955 |
releaseVDOPageCompletion(completion);
|
|
Packit Service |
d40955 |
finishIfDone(rebuild);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Rebuild reference counts from a block map page.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param rebuild The rebuild completion
|
|
Packit Service |
d40955 |
* @param completion The page completion holding the page
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @return VDO_SUCCESS or an error
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static int rebuildReferenceCountsFromPage(RebuildCompletion *rebuild,
|
|
Packit Service |
d40955 |
VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
BlockMapPage *page = dereferenceWritableVDOPage(completion);
|
|
Packit Service |
d40955 |
int result = ASSERT(page != NULL, "page available");
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
return result;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if (!isBlockMapPageInitialized(page)) {
|
|
Packit Service |
d40955 |
return VDO_SUCCESS;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Remove any bogus entries which exist beyond the end of the logical space.
|
|
Packit Service |
d40955 |
if (getBlockMapPagePBN(page) == rebuild->lastSlot.pbn) {
|
|
Packit Service |
d40955 |
for (SlotNumber slot = rebuild->lastSlot.slot;
|
|
Packit Service |
d40955 |
slot < BLOCK_MAP_ENTRIES_PER_PAGE; slot++) {
|
|
Packit Service |
d40955 |
DataLocation mapping = unpackBlockMapEntry(&page->entries[slot]);
|
|
Packit Service |
d40955 |
if (isMappedLocation(&mapping)) {
|
|
Packit Service |
d40955 |
page->entries[slot] = packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
d40955 |
requestVDOPageWrite(completion);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Inform the slab depot of all entries on this page.
|
|
Packit Service |
d40955 |
for (SlotNumber slot = 0; slot < BLOCK_MAP_ENTRIES_PER_PAGE; slot++) {
|
|
Packit Service |
d40955 |
DataLocation mapping = unpackBlockMapEntry(&page->entries[slot]);
|
|
Packit Service |
d40955 |
if (!isValidLocation(&mapping)) {
|
|
Packit Service |
d40955 |
// This entry is invalid, so remove it from the page.
|
|
Packit Service |
d40955 |
page->entries[slot] = packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
d40955 |
requestVDOPageWrite(completion);
|
|
Packit Service |
d40955 |
continue;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if (!isMappedLocation(&mapping)) {
|
|
Packit Service |
d40955 |
continue;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
(*rebuild->logicalBlocksUsed)++;
|
|
Packit Service |
d40955 |
if (mapping.pbn == ZERO_BLOCK) {
|
|
Packit Service |
d40955 |
continue;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if (!isPhysicalDataBlock(rebuild->depot, mapping.pbn)) {
|
|
Packit Service |
d40955 |
// This is a nonsense mapping. Remove it from the map so we're at least
|
|
Packit Service |
d40955 |
// consistent and mark the page dirty.
|
|
Packit Service |
d40955 |
page->entries[slot] = packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
d40955 |
requestVDOPageWrite(completion);
|
|
Packit Service |
d40955 |
continue;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
Slab *slab = getSlab(rebuild->depot, mapping.pbn);
|
|
Packit Service |
d40955 |
int result = adjustReferenceCountForRebuild(slab->referenceCounts,
|
|
Packit Service |
d40955 |
mapping.pbn, DATA_INCREMENT);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
logErrorWithStringError(result,
|
|
Packit Service |
d40955 |
"Could not adjust reference count for PBN"
|
|
Packit Service |
d40955 |
" %llu, slot %u mapped to PBN %llu",
|
|
Packit Service |
d40955 |
getBlockMapPagePBN(page), slot, mapping.pbn);
|
|
Packit Service |
d40955 |
page->entries[slot] = packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
d40955 |
requestVDOPageWrite(completion);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
return VDO_SUCCESS;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
static void fetchPage(RebuildCompletion *rebuild, VDOCompletion *completion);
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Process a page which has just been loaded. This callback is registered by
|
|
Packit Service |
d40955 |
* fetchPage().
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completion The VDOPageCompletion for the fetched page
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void pageLoaded(VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild = asRebuildCompletion(completion->parent);
|
|
Packit Service |
d40955 |
rebuild->outstanding--;
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
int result = rebuildReferenceCountsFromPage(rebuild, completion);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
abortRebuild(rebuild, result);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
releaseVDOPageCompletion(completion);
|
|
Packit Service |
d40955 |
if (finishIfDone(rebuild)) {
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Advance progress to the next page, and fetch the next page we
|
|
Packit Service |
d40955 |
// haven't yet requested.
|
|
Packit Service |
d40955 |
fetchPage(rebuild, completion);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Fetch a page from the block map.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param rebuild the RebuildCompletion
|
|
Packit Service |
d40955 |
* @param completion the page completion to use
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void fetchPage(RebuildCompletion *rebuild, VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
while (rebuild->pageToFetch < rebuild->leafPages) {
|
|
Packit Service |
d40955 |
PhysicalBlockNumber pbn = findBlockMapPagePBN(rebuild->blockMap,
|
|
Packit Service |
d40955 |
rebuild->pageToFetch++);
|
|
Packit Service |
d40955 |
if (pbn == ZERO_BLOCK) {
|
|
Packit Service |
d40955 |
continue;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
if (!isPhysicalDataBlock(rebuild->depot, pbn)) {
|
|
Packit Service |
d40955 |
abortRebuild(rebuild, VDO_BAD_MAPPING);
|
|
Packit Service |
d40955 |
if (finishIfDone(rebuild)) {
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
continue;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
initVDOPageCompletion(((VDOPageCompletion *) completion),
|
|
Packit Service |
d40955 |
rebuild->blockMap->zones[0].pageCache,
|
|
Packit Service |
d40955 |
pbn, true, &rebuild->completion,
|
|
Packit Service |
d40955 |
pageLoaded, handlePageLoadError);
|
|
Packit Service |
d40955 |
rebuild->outstanding++;
|
|
Packit Service |
d40955 |
getVDOPageAsync(completion);
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Rebuild reference counts from the leaf block map pages now that reference
|
|
Packit Service |
d40955 |
* counts have been rebuilt from the interior tree pages (which have been
|
|
Packit Service |
d40955 |
* loaded in the process). This callback is registered in
|
|
Packit Service |
d40955 |
* rebuildReferenceCounts().
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param completion The sub-task completion
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static void rebuildFromLeaves(VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild = asRebuildCompletion(completion->parent);
|
|
Packit Service |
d40955 |
*rebuild->logicalBlocksUsed = 0;
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// The PBN calculation doesn't work until the tree pages have been loaded,
|
|
Packit Service |
d40955 |
// so we can't set this value at the start of rebuild.
|
|
Packit Service |
d40955 |
rebuild->lastSlot = (BlockMapSlot) {
|
|
Packit Service |
d40955 |
.slot = rebuild->blockMap->entryCount % BLOCK_MAP_ENTRIES_PER_PAGE,
|
|
Packit Service |
d40955 |
.pbn = findBlockMapPagePBN(rebuild->blockMap, rebuild->leafPages - 1),
|
|
Packit Service |
d40955 |
};
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Prevent any page from being processed until all pages have been launched.
|
|
Packit Service |
d40955 |
rebuild->launching = true;
|
|
Packit Service |
d40955 |
for (PageCount i = 0; i < rebuild->pageCount; i++) {
|
|
Packit Service |
d40955 |
fetchPage(rebuild, &rebuild->pageCompletions[i].completion);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
rebuild->launching = false;
|
|
Packit Service |
d40955 |
finishIfDone(rebuild);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**
|
|
Packit Service |
d40955 |
* Process a single entry from the block map tree.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* Implements EntryCallback.
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @param pbn A pbn which holds a block map tree page
|
|
Packit Service |
d40955 |
* @param completion The parent completion of the traversal
|
|
Packit Service |
d40955 |
*
|
|
Packit Service |
d40955 |
* @return VDO_SUCCESS or an error
|
|
Packit Service |
d40955 |
**/
|
|
Packit Service |
d40955 |
static int processEntry(PhysicalBlockNumber pbn, VDOCompletion *completion)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild = asRebuildCompletion(completion->parent);
|
|
Packit Service |
d40955 |
if ((pbn == ZERO_BLOCK) || !isPhysicalDataBlock(rebuild->depot, pbn)) {
|
|
Packit Service |
d40955 |
return logErrorWithStringError(VDO_BAD_CONFIGURATION,
|
|
Packit Service |
d40955 |
"PBN %llu out of range",
|
|
Packit Service |
d40955 |
pbn);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
Slab *slab = getSlab(rebuild->depot, pbn);
|
|
Packit Service |
d40955 |
int result = adjustReferenceCountForRebuild(slab->referenceCounts, pbn,
|
|
Packit Service |
d40955 |
BLOCK_MAP_INCREMENT);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
return logErrorWithStringError(result,
|
|
Packit Service |
d40955 |
"Could not adjust reference count for "
|
|
Packit Service |
d40955 |
"block map tree PBN %llu",
|
|
Packit Service |
d40955 |
pbn);
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
(*rebuild->blockMapDataBlocks)++;
|
|
Packit Service |
d40955 |
return VDO_SUCCESS;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
/**********************************************************************/
|
|
Packit Service |
d40955 |
void rebuildReferenceCounts(VDO *vdo,
|
|
Packit Service |
d40955 |
VDOCompletion *parent,
|
|
Packit Service |
d40955 |
BlockCount *logicalBlocksUsed,
|
|
Packit Service |
d40955 |
BlockCount *blockMapDataBlocks)
|
|
Packit Service |
d40955 |
{
|
|
Packit Service |
d40955 |
RebuildCompletion *rebuild;
|
|
Packit Service |
d40955 |
int result = makeRebuildCompletion(vdo, logicalBlocksUsed,
|
|
Packit Service |
d40955 |
blockMapDataBlocks, parent, &rebuild);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
finishCompletion(parent, result);
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// Completion chaining from page cache hits can lead to stack overflow
|
|
Packit Service |
d40955 |
// during the rebuild, so clear out the cache before this rebuild phase.
|
|
Packit Service |
d40955 |
result = invalidateVDOPageCache(rebuild->blockMap->zones[0].pageCache);
|
|
Packit Service |
d40955 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
d40955 |
finishCompletion(parent, result);
|
|
Packit Service |
d40955 |
return;
|
|
Packit Service |
d40955 |
}
|
|
Packit Service |
d40955 |
|
|
Packit Service |
d40955 |
// First traverse the block map trees.
|
|
Packit Service |
d40955 |
*rebuild->blockMapDataBlocks = 0;
|
|
Packit Service |
d40955 |
VDOCompletion *completion = &rebuild->subTaskCompletion;
|
|
Packit Service |
d40955 |
prepareCompletion(completion, rebuildFromLeaves, finishParentCallback,
|
|
Packit Service |
d40955 |
rebuild->logicalThreadID, rebuild);
|
|
Packit Service |
d40955 |
traverseForest(rebuild->blockMap, processEntry, completion);
|
|
Packit Service |
d40955 |
}
|