|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
310c69 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
310c69 |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
310c69 |
* of the License, or (at your option) any later version.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
310c69 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
310c69 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
310c69 |
* GNU General Public License for more details.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
310c69 |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
310c69 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
310c69 |
* 02110-1301, USA.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/forest.c#8 $
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "forest.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "logger.h"
|
|
Packit Service |
310c69 |
#include "memoryAlloc.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "blockMap.h"
|
|
Packit Service |
310c69 |
#include "blockMapInternals.h"
|
|
Packit Service |
310c69 |
#include "blockMapPage.h"
|
|
Packit Service |
310c69 |
#include "blockMapTree.h"
|
|
Packit Service |
310c69 |
#include "blockMapTreeInternals.h"
|
|
Packit Service |
310c69 |
#include "constants.h"
|
|
Packit Service |
310c69 |
#include "dirtyLists.h"
|
|
Packit Service |
310c69 |
#include "forest.h"
|
|
Packit Service |
310c69 |
#include "numUtils.h"
|
|
Packit Service |
310c69 |
#include "recoveryJournal.h"
|
|
Packit Service |
310c69 |
#include "slabDepot.h"
|
|
Packit Service |
310c69 |
#include "slabJournal.h"
|
|
Packit Service |
310c69 |
#include "types.h"
|
|
Packit Service |
310c69 |
#include "vdoInternal.h"
|
|
Packit Service |
310c69 |
#include "vio.h"
|
|
Packit Service |
310c69 |
#include "vioPool.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
enum {
|
|
Packit Service |
310c69 |
BLOCK_MAP_VIO_POOL_SIZE = 64,
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef struct {
|
|
Packit Service |
310c69 |
TreePage *levels[BLOCK_MAP_TREE_HEIGHT];
|
|
Packit Service |
310c69 |
} BlockMapTreeSegment;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef struct blockMapTree {
|
|
Packit Service |
310c69 |
BlockMapTreeSegment *segments;
|
|
Packit Service |
310c69 |
} BlockMapTree;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
struct forest {
|
|
Packit Service |
310c69 |
BlockMap *map;
|
|
Packit Service |
310c69 |
size_t segments;
|
|
Packit Service |
310c69 |
Boundary *boundaries;
|
|
Packit Service |
310c69 |
TreePage **pages;
|
|
Packit Service |
310c69 |
BlockMapTree trees[];
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef struct {
|
|
Packit Service |
310c69 |
PageNumber pageIndex;
|
|
Packit Service |
310c69 |
SlotNumber slot;
|
|
Packit Service |
310c69 |
} CursorLevel;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef struct cursors Cursors;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef struct {
|
|
Packit Service |
310c69 |
Waiter waiter;
|
|
Packit Service |
310c69 |
BlockMapTree *tree;
|
|
Packit Service |
310c69 |
Height height;
|
|
Packit Service |
310c69 |
Cursors *parent;
|
|
Packit Service |
310c69 |
Boundary boundary;
|
|
Packit Service |
310c69 |
CursorLevel levels[BLOCK_MAP_TREE_HEIGHT];
|
|
Packit Service |
310c69 |
VIOPoolEntry *vioPoolEntry;
|
|
Packit Service |
310c69 |
} Cursor;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
struct cursors {
|
|
Packit Service |
310c69 |
BlockMap *map;
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone;
|
|
Packit Service |
310c69 |
VIOPool *pool;
|
|
Packit Service |
310c69 |
EntryCallback *entryCallback;
|
|
Packit Service |
310c69 |
VDOCompletion *parent;
|
|
Packit Service |
310c69 |
RootCount activeRoots;
|
|
Packit Service |
310c69 |
Cursor cursors[];
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
TreePage *getTreePageByIndex(Forest *forest,
|
|
Packit Service |
310c69 |
RootCount rootIndex,
|
|
Packit Service |
310c69 |
Height height,
|
|
Packit Service |
310c69 |
PageNumber pageIndex)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
PageNumber offset = 0;
|
|
Packit Service |
310c69 |
for (size_t segment = 0; segment < forest->segments; segment++) {
|
|
Packit Service |
310c69 |
PageNumber border = forest->boundaries[segment].levels[height - 1];
|
|
Packit Service |
310c69 |
if (pageIndex < border) {
|
|
Packit Service |
310c69 |
BlockMapTree *tree = &forest->trees[rootIndex];
|
|
Packit Service |
310c69 |
return &(tree->segments[segment].levels[height - 1][pageIndex - offset]);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
offset = border;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return NULL;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Compute the number of pages which must be allocated at each level in order
|
|
Packit Service |
310c69 |
* to grow the forest to a new number of entries.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param [in] rootCount The number of roots
|
|
Packit Service |
310c69 |
* @param [in] flatPageCount The number of flat block map pages
|
|
Packit Service |
310c69 |
* @param [in] oldSizes The current size of the forest at each level
|
|
Packit Service |
310c69 |
* @param [in] entries The new number of entries the block map must
|
|
Packit Service |
310c69 |
* address
|
|
Packit Service |
310c69 |
* @param [out] newSizes The new size of the forest at each level
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return The total number of non-leaf pages required
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static BlockCount computeNewPages(RootCount rootCount,
|
|
Packit Service |
310c69 |
BlockCount flatPageCount,
|
|
Packit Service |
310c69 |
Boundary *oldSizes,
|
|
Packit Service |
310c69 |
BlockCount entries,
|
|
Packit Service |
310c69 |
Boundary *newSizes)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
PageCount leafPages
|
|
Packit Service |
310c69 |
= maxPageCount(computeBlockMapPageCount(entries) - flatPageCount, 1);
|
|
Packit Service |
310c69 |
PageCount levelSize = computeBucketCount(leafPages, rootCount);
|
|
Packit Service |
310c69 |
BlockCount totalPages = 0;
|
|
Packit Service |
310c69 |
for (Height height = 0; height < BLOCK_MAP_TREE_HEIGHT; height++) {
|
|
Packit Service |
310c69 |
levelSize = computeBucketCount(levelSize, BLOCK_MAP_ENTRIES_PER_PAGE);
|
|
Packit Service |
310c69 |
newSizes->levels[height] = levelSize;
|
|
Packit Service |
310c69 |
BlockCount newPages = levelSize;
|
|
Packit Service |
310c69 |
if (oldSizes != NULL) {
|
|
Packit Service |
310c69 |
newPages -= oldSizes->levels[height];
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
totalPages += (newPages * rootCount);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return totalPages;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static int makeSegment(Forest *oldForest,
|
|
Packit Service |
310c69 |
BlockCount newPages,
|
|
Packit Service |
310c69 |
Boundary *newBoundary,
|
|
Packit Service |
310c69 |
Forest *forest)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
size_t index = (oldForest == NULL) ? 0 : oldForest->segments;
|
|
Packit Service |
310c69 |
forest->segments = index + 1;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ALLOCATE(forest->segments, Boundary, "forest boundary array",
|
|
Packit Service |
310c69 |
&forest->boundaries);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ALLOCATE(forest->segments, TreePage *, "forest page pointers",
|
|
Packit Service |
310c69 |
&forest->pages);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ALLOCATE(newPages, TreePage, "new forest pages",
|
|
Packit Service |
310c69 |
&forest->pages[index]);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (index > 0) {
|
|
Packit Service |
310c69 |
memcpy(forest->boundaries, oldForest->boundaries,
|
|
Packit Service |
310c69 |
index * sizeof(Boundary));
|
|
Packit Service |
310c69 |
memcpy(forest->pages, oldForest->pages, index * sizeof(TreePage *));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
memcpy(&(forest->boundaries[index]), newBoundary, sizeof(Boundary));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
PageCount segmentSizes[BLOCK_MAP_TREE_HEIGHT];
|
|
Packit Service |
310c69 |
for (Height height = 0; height < BLOCK_MAP_TREE_HEIGHT; height++) {
|
|
Packit Service |
310c69 |
segmentSizes[height] = newBoundary->levels[height];
|
|
Packit Service |
310c69 |
if (index > 0) {
|
|
Packit Service |
310c69 |
segmentSizes[height] -= oldForest->boundaries[index - 1].levels[height];
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreePage *pagePtr = forest->pages[index];
|
|
Packit Service |
310c69 |
for (RootCount root = 0; root < forest->map->rootCount; root++) {
|
|
Packit Service |
310c69 |
BlockMapTree *tree = &(forest->trees[root]);
|
|
Packit Service |
310c69 |
int result = ALLOCATE(forest->segments, BlockMapTreeSegment,
|
|
Packit Service |
310c69 |
"tree root segments", &tree->segments);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (index > 0) {
|
|
Packit Service |
310c69 |
memcpy(tree->segments, oldForest->trees[root].segments,
|
|
Packit Service |
310c69 |
index * sizeof(BlockMapTreeSegment));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BlockMapTreeSegment *segment = &(tree->segments[index]);
|
|
Packit Service |
310c69 |
for (Height height = 0; height < BLOCK_MAP_TREE_HEIGHT; height++) {
|
|
Packit Service |
310c69 |
if (segmentSizes[height] == 0) {
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
segment->levels[height] = pagePtr;
|
|
Packit Service |
310c69 |
if (height == (BLOCK_MAP_TREE_HEIGHT - 1)) {
|
|
Packit Service |
310c69 |
// Record the root.
|
|
Packit Service |
310c69 |
BlockMapPage *page = formatBlockMapPage(pagePtr->pageBuffer,
|
|
Packit Service |
310c69 |
forest->map->nonce,
|
|
Packit Service |
310c69 |
INVALID_PBN, true);
|
|
Packit Service |
310c69 |
page->entries[0] = packPBN(forest->map->rootOrigin + root,
|
|
Packit Service |
310c69 |
MAPPING_STATE_UNCOMPRESSED);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
pagePtr += segmentSizes[height];
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void deforest(Forest *forest, size_t firstPageSegment)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (forest->pages != NULL) {
|
|
Packit Service |
310c69 |
for (size_t segment = firstPageSegment; segment < forest->segments;
|
|
Packit Service |
310c69 |
segment++) {
|
|
Packit Service |
310c69 |
FREE(forest->pages[segment]);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
FREE(forest->pages);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
for (RootCount root = 0; root < forest->map->rootCount; root++) {
|
|
Packit Service |
310c69 |
BlockMapTree *tree = &(forest->trees[root]);
|
|
Packit Service |
310c69 |
FREE(tree->segments);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
FREE(forest->boundaries);
|
|
Packit Service |
310c69 |
FREE(forest);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int makeForest(BlockMap *map, BlockCount entries)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
STATIC_ASSERT(offsetof(TreePage, waiter) == 0);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
Forest *oldForest = map->forest;
|
|
Packit Service |
310c69 |
Boundary *oldBoundary = NULL;
|
|
Packit Service |
310c69 |
if (oldForest != NULL) {
|
|
Packit Service |
310c69 |
oldBoundary = &(oldForest->boundaries[oldForest->segments - 1]);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
Boundary newBoundary;
|
|
Packit Service |
310c69 |
BlockCount newPages = computeNewPages(map->rootCount, map->flatPageCount,
|
|
Packit Service |
310c69 |
oldBoundary, entries, &newBoundary);
|
|
Packit Service |
310c69 |
if (newPages == 0) {
|
|
Packit Service |
310c69 |
map->nextEntryCount = entries;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
Forest *forest;
|
|
Packit Service |
310c69 |
int result = ALLOCATE_EXTENDED(Forest, map->rootCount, BlockMapTree,
|
|
Packit Service |
310c69 |
__func__, &forest);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
forest->map = map;
|
|
Packit Service |
310c69 |
result = makeSegment(oldForest, newPages, &newBoundary, forest);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
deforest(forest, forest->segments - 1);
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
map->nextForest = forest;
|
|
Packit Service |
310c69 |
map->nextEntryCount = entries;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void freeForest(Forest **forestPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
Forest *forest = *forestPtr;
|
|
Packit Service |
310c69 |
if (forest == NULL) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
deforest(forest, 0);
|
|
Packit Service |
310c69 |
*forestPtr = NULL;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void abandonForest(BlockMap *map)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
Forest *forest = map->nextForest;
|
|
Packit Service |
310c69 |
map->nextForest = NULL;
|
|
Packit Service |
310c69 |
if (forest != NULL) {
|
|
Packit Service |
310c69 |
deforest(forest, forest->segments - 1);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
map->nextEntryCount = 0;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void replaceForest(BlockMap *map)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (map->nextForest != NULL) {
|
|
Packit Service |
310c69 |
if (map->forest != NULL) {
|
|
Packit Service |
310c69 |
deforest(map->forest, map->forest->segments);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
map->forest = map->nextForest;
|
|
Packit Service |
310c69 |
map->nextForest = NULL;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
map->entryCount = map->nextEntryCount;
|
|
Packit Service |
310c69 |
map->nextEntryCount = 0;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Finish the traversal of a single tree. If it was the last cursor, finish
|
|
Packit Service |
310c69 |
* the traversal.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cursor The cursor doing the traversal
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void finishCursor(Cursor *cursor)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
Cursors *cursors = cursor->parent;
|
|
Packit Service |
310c69 |
returnVIOToPool(cursors->pool, cursor->vioPoolEntry);
|
|
Packit Service |
310c69 |
if (--cursors->activeRoots > 0) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
VDOCompletion *parent = cursors->parent;
|
|
Packit Service |
310c69 |
FREE(cursors);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
finishCompletion(parent, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void traverse(Cursor *cursor);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue traversing a block map tree.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO doing a read or write
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void continueTraversal(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
VIOPoolEntry *poolEntry = completion->parent;
|
|
Packit Service |
310c69 |
Cursor *cursor = poolEntry->parent;
|
|
Packit Service |
310c69 |
traverse(cursor);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue traversing a block map tree now that a page has been loaded.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO doing the read
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void finishTraversalLoad(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = completion->parent;
|
|
Packit Service |
310c69 |
Cursor *cursor = entry->parent;
|
|
Packit Service |
310c69 |
Height height = cursor->height;
|
|
Packit Service |
310c69 |
CursorLevel *level = &cursor->levels[height];
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreePage *treePage
|
|
Packit Service |
310c69 |
= &(cursor->tree->segments[0].levels[height][level->pageIndex]);
|
|
Packit Service |
310c69 |
BlockMapPage *page = (BlockMapPage *) treePage->pageBuffer;
|
|
Packit Service |
310c69 |
copyValidPage(entry->buffer, cursor->parent->map->nonce,
|
|
Packit Service |
310c69 |
entry->vio->physical, page);
|
|
Packit Service |
310c69 |
traverse(cursor);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Traverse a single block map tree. This is the recursive heart of the
|
|
Packit Service |
310c69 |
* traversal process.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cursor The cursor doing the traversal
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void traverse(Cursor *cursor)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
for (; cursor->height < BLOCK_MAP_TREE_HEIGHT; cursor->height++) {
|
|
Packit Service |
310c69 |
Height height = cursor->height;
|
|
Packit Service |
310c69 |
CursorLevel *level = &cursor->levels[height];
|
|
Packit Service |
310c69 |
TreePage *treePage
|
|
Packit Service |
310c69 |
= &(cursor->tree->segments[0].levels[height][level->pageIndex]);
|
|
Packit Service |
310c69 |
BlockMapPage *page = (BlockMapPage *) treePage->pageBuffer;
|
|
Packit Service |
310c69 |
if (!isBlockMapPageInitialized(page)) {
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
for (; level->slot < BLOCK_MAP_ENTRIES_PER_PAGE; level->slot++) {
|
|
Packit Service |
310c69 |
DataLocation location = unpackBlockMapEntry(&page->entries[level->slot]);
|
|
Packit Service |
310c69 |
if (!isValidLocation(&location)) {
|
|
Packit Service |
310c69 |
// This entry is invalid, so remove it from the page.
|
|
Packit Service |
310c69 |
page->entries[level->slot]
|
|
Packit Service |
310c69 |
= packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
310c69 |
writeTreePage(treePage, cursor->parent->zone);
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (!isMappedLocation(&location)) {
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
PageNumber entryIndex
|
|
Packit Service |
310c69 |
= (BLOCK_MAP_ENTRIES_PER_PAGE * level->pageIndex) + level->slot;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Erase mapped entries past the end of the logical space.
|
|
Packit Service |
310c69 |
if (entryIndex >= cursor->boundary.levels[height]) {
|
|
Packit Service |
310c69 |
page->entries[level->slot]
|
|
Packit Service |
310c69 |
= packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
310c69 |
writeTreePage(treePage, cursor->parent->zone);
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (cursor->height < BLOCK_MAP_TREE_HEIGHT - 1) {
|
|
Packit Service |
310c69 |
int result = cursor->parent->entryCallback(location.pbn,
|
|
Packit Service |
310c69 |
cursor->parent->parent);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
page->entries[level->slot]
|
|
Packit Service |
310c69 |
= packPBN(ZERO_BLOCK, MAPPING_STATE_UNMAPPED);
|
|
Packit Service |
310c69 |
writeTreePage(treePage, cursor->parent->zone);
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (cursor->height == 0) {
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
cursor->height--;
|
|
Packit Service |
310c69 |
CursorLevel *nextLevel = &cursor->levels[cursor->height];
|
|
Packit Service |
310c69 |
nextLevel->pageIndex = entryIndex;
|
|
Packit Service |
310c69 |
nextLevel->slot = 0;
|
|
Packit Service |
310c69 |
level->slot++;
|
|
Packit Service |
310c69 |
launchReadMetadataVIO(cursor->vioPoolEntry->vio, location.pbn,
|
|
Packit Service |
310c69 |
finishTraversalLoad, continueTraversal);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
finishCursor(cursor);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Start traversing a single block map tree now that the Cursor has a VIO with
|
|
Packit Service |
310c69 |
* which to load pages.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Implements WaiterCallback.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The Cursor
|
|
Packit Service |
310c69 |
* @param context The VIOPoolEntry just acquired
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void launchCursor(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
STATIC_ASSERT(offsetof(Cursor, waiter) == 0);
|
|
Packit Service |
310c69 |
Cursor *cursor = (Cursor *) waiter;
|
|
Packit Service |
310c69 |
cursor->vioPoolEntry = (VIOPoolEntry *) context;
|
|
Packit Service |
310c69 |
cursor->vioPoolEntry->parent = cursor;
|
|
Packit Service |
310c69 |
vioAsCompletion(cursor->vioPoolEntry->vio)->callbackThreadID
|
|
Packit Service |
310c69 |
= cursor->parent->zone->mapZone->threadID;
|
|
Packit Service |
310c69 |
traverse(cursor);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Compute the number of pages used at each level of the given root's tree.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param map The block map
|
|
Packit Service |
310c69 |
* @param rootIndex The index of the root to measure
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return The list of page counts as a Boundary
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static Boundary computeBoundary(BlockMap *map, RootCount rootIndex)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
PageCount leafPages = computeBlockMapPageCount(map->entryCount);
|
|
Packit Service |
310c69 |
PageCount treeLeafPages = leafPages - map->flatPageCount;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Compute the leaf pages for this root. If the number of leaf pages does
|
|
Packit Service |
310c69 |
* not distribute evenly, we must determine if this root gets an extra page.
|
|
Packit Service |
310c69 |
* Extra pages are assigned to roots starting at firstTreeRoot and going up.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
PageCount firstTreeRoot = map->flatPageCount % map->rootCount;
|
|
Packit Service |
310c69 |
PageCount lastTreeRoot = (leafPages - 1) % map->rootCount;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
PageCount levelPages = treeLeafPages / map->rootCount;
|
|
Packit Service |
310c69 |
if (inCyclicRange(firstTreeRoot, rootIndex, lastTreeRoot, map->rootCount)) {
|
|
Packit Service |
310c69 |
levelPages++;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
Boundary boundary;
|
|
Packit Service |
310c69 |
for (Height height = 0; height < BLOCK_MAP_TREE_HEIGHT - 1; height++) {
|
|
Packit Service |
310c69 |
boundary.levels[height] = levelPages;
|
|
Packit Service |
310c69 |
levelPages = computeBucketCount(levelPages, BLOCK_MAP_ENTRIES_PER_PAGE);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// The root node always exists, even if the root is otherwise unused.
|
|
Packit Service |
310c69 |
boundary.levels[BLOCK_MAP_TREE_HEIGHT - 1] = 1;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return boundary;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void traverseForest(BlockMap *map,
|
|
Packit Service |
310c69 |
EntryCallback *entryCallback,
|
|
Packit Service |
310c69 |
VDOCompletion *parent)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (computeBlockMapPageCount(map->entryCount) <= map->flatPageCount) {
|
|
Packit Service |
310c69 |
// There are no tree pages, so there's nothing to do.
|
|
Packit Service |
310c69 |
finishCompletion(parent, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
Cursors *cursors;
|
|
Packit Service |
310c69 |
int result = ALLOCATE_EXTENDED(Cursors, map->rootCount, Cursor, __func__,
|
|
Packit Service |
310c69 |
&cursors);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
finishCompletion(parent, result);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
cursors->map = map;
|
|
Packit Service |
310c69 |
cursors->zone = &(getBlockMapZone(map, 0)->treeZone);
|
|
Packit Service |
310c69 |
cursors->pool = cursors->zone->vioPool;
|
|
Packit Service |
310c69 |
cursors->entryCallback = entryCallback;
|
|
Packit Service |
310c69 |
cursors->parent = parent;
|
|
Packit Service |
310c69 |
cursors->activeRoots = map->rootCount;
|
|
Packit Service |
310c69 |
for (RootCount root = 0; root < map->rootCount; root++) {
|
|
Packit Service |
310c69 |
Cursor *cursor = &cursors->cursors[root];
|
|
Packit Service |
310c69 |
*cursor = (Cursor) {
|
|
Packit Service |
310c69 |
.tree = &map->forest->trees[root],
|
|
Packit Service |
310c69 |
.height = BLOCK_MAP_TREE_HEIGHT - 1,
|
|
Packit Service |
310c69 |
.parent = cursors,
|
|
Packit Service |
310c69 |
.boundary = computeBoundary(map, root),
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
cursor->waiter.callback = launchCursor;
|
|
Packit Service |
310c69 |
acquireVIOFromPool(cursors->pool, &cursor->waiter);
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
BlockCount computeForestSize(BlockCount logicalBlocks, RootCount rootCount)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
Boundary newSizes;
|
|
Packit Service |
310c69 |
BlockCount approximateNonLeaves
|
|
Packit Service |
310c69 |
= computeNewPages(rootCount, 0, NULL, logicalBlocks, &newSizes);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Exclude the tree roots since those aren't allocated from slabs,
|
|
Packit Service |
310c69 |
// and also exclude the super-roots, which only exist in memory.
|
|
Packit Service |
310c69 |
approximateNonLeaves
|
|
Packit Service |
310c69 |
-= rootCount * (newSizes.levels[BLOCK_MAP_TREE_HEIGHT - 2]
|
|
Packit Service |
310c69 |
+ newSizes.levels[BLOCK_MAP_TREE_HEIGHT - 1]);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BlockCount approximateLeaves
|
|
Packit Service |
310c69 |
= computeBlockMapPageCount(logicalBlocks - approximateNonLeaves);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// This can be a slight over-estimate since the tree will never have to
|
|
Packit Service |
310c69 |
// address these blocks, so it might be a tiny bit smaller.
|
|
Packit Service |
310c69 |
return (approximateNonLeaves + approximateLeaves);
|
|
Packit Service |
310c69 |
}
|