|
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/blockMapTree.c#21 $
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "blockMapTree.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "logger.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 "blockMapTreeInternals.h"
|
|
Packit Service |
310c69 |
#include "constants.h"
|
|
Packit Service |
310c69 |
#include "dataVIO.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 "referenceOperation.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 "vdoPageCache.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 __attribute__((packed)) {
|
|
Packit Service |
310c69 |
RootCount rootIndex;
|
|
Packit Service |
310c69 |
Height height;
|
|
Packit Service |
310c69 |
PageNumber pageIndex;
|
|
Packit Service |
310c69 |
SlotNumber slot;
|
|
Packit Service |
310c69 |
} PageDescriptor;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef union {
|
|
Packit Service |
310c69 |
PageDescriptor descriptor;
|
|
Packit Service |
310c69 |
uint64_t key;
|
|
Packit Service |
310c69 |
} PageKey;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
typedef struct {
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone;
|
|
Packit Service |
310c69 |
uint8_t generation;
|
|
Packit Service |
310c69 |
} WriteIfNotDirtiedContext;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* An invalid PBN used to indicate that the page holding the location of a
|
|
Packit Service |
310c69 |
* tree root has been "loaded".
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
const PhysicalBlockNumber INVALID_PBN = 0xFFFFFFFFFFFFFFFF;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Convert a RingNode to a TreePage.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param ringNode The RingNode to convert
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return The TreePage which owns the RingNode
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static inline TreePage *treePageFromRingNode(RingNode *ringNode)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return (TreePage *) ((byte *) ringNode - offsetof(TreePage, node));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void writeDirtyPagesCallback(RingNode *expired, void *context);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Make VIOs for reading, writing, and allocating the arboreal block map.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Implements VIOConstructor.
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
__attribute__((warn_unused_result))
|
|
Packit Service |
310c69 |
static int makeBlockMapVIOs(PhysicalLayer *layer,
|
|
Packit Service |
310c69 |
void *parent,
|
|
Packit Service |
310c69 |
void *buffer,
|
|
Packit Service |
310c69 |
VIO **vioPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return createVIO(layer, VIO_TYPE_BLOCK_MAP_INTERIOR, VIO_PRIORITY_METADATA,
|
|
Packit Service |
310c69 |
parent, buffer, vioPtr);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int initializeTreeZone(BlockMapZone *zone,
|
|
Packit Service |
310c69 |
PhysicalLayer *layer,
|
|
Packit Service |
310c69 |
BlockCount eraLength)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
STATIC_ASSERT_SIZEOF(PageDescriptor, sizeof(uint64_t));
|
|
Packit Service |
310c69 |
BlockMapTreeZone *treeZone = &zone->treeZone;
|
|
Packit Service |
310c69 |
treeZone->mapZone = zone;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = makeDirtyLists(eraLength, writeDirtyPagesCallback, treeZone,
|
|
Packit Service |
310c69 |
&treeZone->dirtyLists);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = makeIntMap(LOCK_MAP_CAPACITY, 0, &treeZone->loadingPages);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return makeVIOPool(layer, BLOCK_MAP_VIO_POOL_SIZE, zone->threadID,
|
|
Packit Service |
310c69 |
makeBlockMapVIOs, treeZone, &treeZone->vioPool);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int replaceTreeZoneVIOPool(BlockMapTreeZone *zone,
|
|
Packit Service |
310c69 |
PhysicalLayer *layer,
|
|
Packit Service |
310c69 |
size_t poolSize)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
freeVIOPool(&zone->vioPool);
|
|
Packit Service |
310c69 |
return makeVIOPool(layer, poolSize, zone->mapZone->threadID,
|
|
Packit Service |
310c69 |
makeBlockMapVIOs, zone, &zone->vioPool);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void uninitializeBlockMapTreeZone(BlockMapTreeZone *treeZone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
freeDirtyLists(&treeZone->dirtyLists);
|
|
Packit Service |
310c69 |
freeVIOPool(&treeZone->vioPool);
|
|
Packit Service |
310c69 |
freeIntMap(&treeZone->loadingPages);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void setTreeZoneInitialPeriod(BlockMapTreeZone *treeZone,
|
|
Packit Service |
310c69 |
SequenceNumber period)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
setCurrentPeriod(treeZone->dirtyLists, period);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Get the BlockMapTreeZone in which a DataVIO is operating.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return The BlockMapTreeZone
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
__attribute__((warn_unused_result))
|
|
Packit Service |
310c69 |
static inline BlockMapTreeZone *getBlockMapTreeZone(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return &(getBlockMapForZone(dataVIO->logical.zone)->treeZone);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Get the TreePage for a given lock. This will be the page referred to by the
|
|
Packit Service |
310c69 |
* lock's tree slot for the lock's current height.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The tree zone of the tree
|
|
Packit Service |
310c69 |
* @param lock The lock describing the page to get
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return The requested page
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static inline TreePage *getTreePage(const BlockMapTreeZone *zone,
|
|
Packit Service |
310c69 |
const TreeLock *lock)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return getTreePageByIndex(zone->mapZone->blockMap->forest,
|
|
Packit Service |
310c69 |
lock->rootIndex,
|
|
Packit Service |
310c69 |
lock->height,
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height].pageIndex);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
bool copyValidPage(char *buffer,
|
|
Packit Service |
310c69 |
Nonce nonce,
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn,
|
|
Packit Service |
310c69 |
BlockMapPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
BlockMapPage *loaded = (BlockMapPage *) buffer;
|
|
Packit Service |
310c69 |
BlockMapPageValidity validity = validateBlockMapPage(loaded, nonce, pbn);
|
|
Packit Service |
310c69 |
if (validity == BLOCK_MAP_PAGE_VALID) {
|
|
Packit Service |
310c69 |
memcpy(page, loaded, VDO_BLOCK_SIZE);
|
|
Packit Service |
310c69 |
return true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (validity == BLOCK_MAP_PAGE_BAD) {
|
|
Packit Service |
310c69 |
logErrorWithStringError(VDO_BAD_PAGE,
|
|
Packit Service |
310c69 |
"Expected page %" PRIu64
|
|
Packit Service |
310c69 |
" but got page %llu instead",
|
|
Packit Service |
310c69 |
pbn, getBlockMapPagePBN(loaded));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
bool isTreeZoneActive(BlockMapTreeZone *zone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return ((zone->activeLookups != 0)
|
|
Packit Service |
310c69 |
|| hasWaiters(&zone->flushWaiters)
|
|
Packit Service |
310c69 |
|| isVIOPoolBusy(zone->vioPool));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Put the VDO in read-only mode and wake any VIOs waiting for a flush.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone
|
|
Packit Service |
310c69 |
* @param result The error which is causing read-only mode
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void enterZoneReadOnlyMode(BlockMapTreeZone *zone, int result)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
enterReadOnlyMode(zone->mapZone->readOnlyNotifier, result);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// We are in read-only mode, so we won't ever write any page out. Just take
|
|
Packit Service |
310c69 |
// all waiters off the queue so the tree zone can be closed.
|
|
Packit Service |
310c69 |
while (hasWaiters(&zone->flushWaiters)) {
|
|
Packit Service |
310c69 |
dequeueNextWaiter(&zone->flushWaiters);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
checkForDrainComplete(zone->mapZone);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Check whether a generation is strictly older than some other generation in
|
|
Packit Service |
310c69 |
* the context of a zone's current generation range.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone in which to do the comparison
|
|
Packit Service |
310c69 |
* @param a The generation in question
|
|
Packit Service |
310c69 |
* @param b The generation to compare to
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return true if generation a is not strictly older than
|
|
Packit Service |
310c69 |
* generation b in the context of the zone
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
__attribute__((warn_unused_result))
|
|
Packit Service |
310c69 |
static bool isNotOlder(BlockMapTreeZone *zone, uint8_t a, uint8_t b)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = ASSERT((inCyclicRange(zone->oldestGeneration, a,
|
|
Packit Service |
310c69 |
zone->generation, 1 << 8)
|
|
Packit Service |
310c69 |
&& inCyclicRange(zone->oldestGeneration, b,
|
|
Packit Service |
310c69 |
zone->generation, 1 << 8)),
|
|
Packit Service |
310c69 |
"generation(s) %u, %u are out of range [%u, %u]",
|
|
Packit Service |
310c69 |
a, b, zone->oldestGeneration, zone->generation);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
return true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return inCyclicRange(b, a, zone->generation, 1 << 8);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Decrement the count for a generation and roll the oldest generation if there
|
|
Packit Service |
310c69 |
* are no longer any active pages in it.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone
|
|
Packit Service |
310c69 |
* @param generation The generation to release
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void releaseGeneration(BlockMapTreeZone *zone, uint8_t generation)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = ASSERT((zone->dirtyPageCounts[generation] > 0),
|
|
Packit Service |
310c69 |
"dirty page count underflow for generation %u",
|
|
Packit Service |
310c69 |
generation);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
zone->dirtyPageCounts[generation]--;
|
|
Packit Service |
310c69 |
while ((zone->dirtyPageCounts[zone->oldestGeneration] == 0)
|
|
Packit Service |
310c69 |
&& (zone->oldestGeneration != zone->generation)) {
|
|
Packit Service |
310c69 |
zone->oldestGeneration++;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Set the generation of a page and update the dirty page count in the zone.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone which owns the page
|
|
Packit Service |
310c69 |
* @param page The page
|
|
Packit Service |
310c69 |
* @param newGeneration The generation to set
|
|
Packit Service |
310c69 |
* @param decrementOld Whether to decrement the count of the page's old
|
|
Packit Service |
310c69 |
* generation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void setGeneration(BlockMapTreeZone *zone,
|
|
Packit Service |
310c69 |
TreePage *page,
|
|
Packit Service |
310c69 |
uint8_t newGeneration,
|
|
Packit Service |
310c69 |
bool decrementOld)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
uint8_t oldGeneration = page->generation;
|
|
Packit Service |
310c69 |
if (decrementOld && (oldGeneration == newGeneration)) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
page->generation = newGeneration;
|
|
Packit Service |
310c69 |
uint32_t newCount = ++zone->dirtyPageCounts[newGeneration];
|
|
Packit Service |
310c69 |
int result = ASSERT((newCount != 0),
|
|
Packit Service |
310c69 |
"dirty page count overflow for generation %u",
|
|
Packit Service |
310c69 |
newGeneration);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (decrementOld) {
|
|
Packit Service |
310c69 |
releaseGeneration(zone, oldGeneration);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void writePage(TreePage *treePage, VIOPoolEntry *entry);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Write out a dirty page if it is still covered by the most recent flush
|
|
Packit Service |
310c69 |
* or if it is the flusher.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Implements WaiterCallback
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The page to write
|
|
Packit Service |
310c69 |
* @param context The VIOPoolEntry with which to do the write
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void writePageCallback(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
STATIC_ASSERT(offsetof(TreePage, waiter) == 0);
|
|
Packit Service |
310c69 |
writePage((TreePage *) waiter, (VIOPoolEntry *) context);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Acquire a VIO for writing a dirty page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The page which needs a VIO
|
|
Packit Service |
310c69 |
* @param zone The zone
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void acquireVIO(Waiter *waiter, BlockMapTreeZone *zone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
waiter->callback = writePageCallback;
|
|
Packit Service |
310c69 |
int result = acquireVIOFromPool(zone->vioPool, waiter);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Attempt to increment the generation.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone whose generation is to be incremented
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return true if all possible generations were not already
|
|
Packit Service |
310c69 |
* active
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static bool attemptIncrement(BlockMapTreeZone *zone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
uint8_t generation = zone->generation + 1;
|
|
Packit Service |
310c69 |
if (zone->oldestGeneration == generation) {
|
|
Packit Service |
310c69 |
return false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
zone->generation = generation;
|
|
Packit Service |
310c69 |
return true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Enqueue a page to either launch a flush or wait for the current flush which
|
|
Packit Service |
310c69 |
* is already in progress.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param page The page to enqueue
|
|
Packit Service |
310c69 |
* @param zone The zone
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void enqueuePage(TreePage *page, BlockMapTreeZone *zone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if ((zone->flusher == NULL) && attemptIncrement(zone)) {
|
|
Packit Service |
310c69 |
zone->flusher = page;
|
|
Packit Service |
310c69 |
acquireVIO(&page->waiter, zone);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = enqueueWaiter(&zone->flushWaiters, &page->waiter);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Write pages which were waiting for a flush and have not been redirtied.
|
|
Packit Service |
310c69 |
* Requeue those pages which were redirtied.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Implements WaiterCallback.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The dirty page
|
|
Packit Service |
310c69 |
* @param context The zone and generation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void writePageIfNotDirtied(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
STATIC_ASSERT(offsetof(TreePage, waiter) == 0);
|
|
Packit Service |
310c69 |
TreePage *page = (TreePage *) waiter;
|
|
Packit Service |
310c69 |
WriteIfNotDirtiedContext *writeContext = context;
|
|
Packit Service |
310c69 |
if (page->generation == writeContext->generation) {
|
|
Packit Service |
310c69 |
acquireVIO(waiter, writeContext->zone);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
enqueuePage(page, writeContext->zone);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Return a VIO to the zone's pool.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone which owns the pool
|
|
Packit Service |
310c69 |
* @param entry The pool entry to return
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void returnToPool(BlockMapTreeZone *zone, VIOPoolEntry *entry)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
returnVIOToPool(zone->vioPool, entry);
|
|
Packit Service |
310c69 |
checkForDrainComplete(zone->mapZone);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Handle the successful write of a tree page. This callback is registered in
|
|
Packit Service |
310c69 |
* writeInitializedPage().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO doing the write
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void finishPageWrite(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = completion->parent;
|
|
Packit Service |
310c69 |
TreePage *page = entry->parent;
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = entry->context;
|
|
Packit Service |
310c69 |
releaseRecoveryJournalBlockReference(zone->mapZone->blockMap->journal,
|
|
Packit Service |
310c69 |
page->writingRecoveryLock,
|
|
Packit Service |
310c69 |
ZONE_TYPE_LOGICAL,
|
|
Packit Service |
310c69 |
zone->mapZone->zoneNumber);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
bool dirty = (page->writingGeneration != page->generation);
|
|
Packit Service |
310c69 |
releaseGeneration(zone, page->writingGeneration);
|
|
Packit Service |
310c69 |
page->writing = false;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (zone->flusher == page) {
|
|
Packit Service |
310c69 |
WriteIfNotDirtiedContext context = {
|
|
Packit Service |
310c69 |
.zone = zone,
|
|
Packit Service |
310c69 |
.generation = page->writingGeneration,
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
notifyAllWaiters(&zone->flushWaiters, writePageIfNotDirtied, &context);
|
|
Packit Service |
310c69 |
if (dirty && attemptIncrement(zone)) {
|
|
Packit Service |
310c69 |
writePage(page, entry);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
zone->flusher = NULL;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dirty) {
|
|
Packit Service |
310c69 |
enqueuePage(page, zone);
|
|
Packit Service |
310c69 |
} else if ((zone->flusher == NULL)
|
|
Packit Service |
310c69 |
&& hasWaiters(&zone->flushWaiters)
|
|
Packit Service |
310c69 |
&& attemptIncrement(zone)) {
|
|
Packit Service |
310c69 |
zone->flusher = (TreePage *) dequeueNextWaiter(&zone->flushWaiters);
|
|
Packit Service |
310c69 |
writePage(zone->flusher, entry);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
returnToPool(zone, entry);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Handle an error writing a tree page. This error handler is registered in
|
|
Packit Service |
310c69 |
* writePage() and writeInitializedPage().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO doing the write
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void handleWriteError(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = completion->result;
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = completion->parent;
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = entry->context;
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
returnToPool(zone, entry);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Write a page which has been written at least once. This callback is
|
|
Packit Service |
310c69 |
* registered in (or called directly from) writePage().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO which will do the write
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void writeInitializedPage(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = completion->parent;
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = (BlockMapTreeZone *) entry->context;
|
|
Packit Service |
310c69 |
TreePage *treePage = (TreePage *) entry->parent;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Set the initialized field of the copy of the page we are writing to true.
|
|
Packit Service |
310c69 |
* We don't want to set it true on the real page in memory until after this
|
|
Packit Service |
310c69 |
* write succeeds.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
BlockMapPage *page = (BlockMapPage *) entry->buffer;
|
|
Packit Service |
310c69 |
markBlockMapPageInitialized(page, true);
|
|
Packit Service |
310c69 |
launchWriteMetadataVIOWithFlush(entry->vio, getBlockMapPagePBN(page),
|
|
Packit Service |
310c69 |
finishPageWrite, handleWriteError,
|
|
Packit Service |
310c69 |
(zone->flusher == treePage), false);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Write a dirty tree page now that we have a VIO with which to write it.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param treePage The page to write
|
|
Packit Service |
310c69 |
* @param entry The VIOPoolEntry with which to write
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void writePage(TreePage *treePage, VIOPoolEntry *entry)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = (BlockMapTreeZone *) entry->context;
|
|
Packit Service |
310c69 |
if ((zone->flusher != treePage)
|
|
Packit Service |
310c69 |
&& (isNotOlder(zone, treePage->generation, zone->generation))) {
|
|
Packit Service |
310c69 |
// This page was re-dirtied after the last flush was issued, hence we need
|
|
Packit Service |
310c69 |
// to do another flush.
|
|
Packit Service |
310c69 |
enqueuePage(treePage, zone);
|
|
Packit Service |
310c69 |
returnToPool(zone, entry);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
entry->parent = treePage;
|
|
Packit Service |
310c69 |
memcpy(entry->buffer, treePage->pageBuffer, VDO_BLOCK_SIZE);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
VDOCompletion *completion = vioAsCompletion(entry->vio);
|
|
Packit Service |
310c69 |
completion->callbackThreadID = zone->mapZone->threadID;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
treePage->writing = true;
|
|
Packit Service |
310c69 |
treePage->writingGeneration = treePage->generation;
|
|
Packit Service |
310c69 |
treePage->writingRecoveryLock = treePage->recoveryLock;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Clear this now so that we know this page is not on any dirty list.
|
|
Packit Service |
310c69 |
treePage->recoveryLock = 0;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BlockMapPage *page = asBlockMapPage(treePage);
|
|
Packit Service |
310c69 |
if (!markBlockMapPageInitialized(page, true)) {
|
|
Packit Service |
310c69 |
writeInitializedPage(completion);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
launchWriteMetadataVIO(entry->vio, getBlockMapPagePBN(page),
|
|
Packit Service |
310c69 |
writeInitializedPage, handleWriteError);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Schedule a batch of dirty pages for writing.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Implements DirtyListsCallback.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param expired The pages to write
|
|
Packit Service |
310c69 |
* @param context The zone
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void writeDirtyPagesCallback(RingNode *expired, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = (BlockMapTreeZone *) context;
|
|
Packit Service |
310c69 |
uint8_t generation = zone->generation;
|
|
Packit Service |
310c69 |
while (!isRingEmpty(expired)) {
|
|
Packit Service |
310c69 |
TreePage *page = treePageFromRingNode(chopRingNode(expired));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ASSERT(!isWaiting(&page->waiter),
|
|
Packit Service |
310c69 |
"Newly expired page not already waiting to write");
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(zone, result);
|
|
Packit Service |
310c69 |
continue;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
setGeneration(zone, page, generation, false);
|
|
Packit Service |
310c69 |
if (!page->writing) {
|
|
Packit Service |
310c69 |
enqueuePage(page, zone);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void advanceZoneTreePeriod(BlockMapTreeZone *zone, SequenceNumber period)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
advancePeriod(zone->dirtyLists, period);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void drainZoneTrees(BlockMapTreeZone *zone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY((zone->activeLookups == 0),
|
|
Packit Service |
310c69 |
"drainZoneTrees() called with no active lookups");
|
|
Packit Service |
310c69 |
if (!isSuspending(&zone->mapZone->state)) {
|
|
Packit Service |
310c69 |
flushDirtyLists(zone->dirtyLists);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Release a lock on a page which was being loaded or allocated.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO releasing the page lock
|
|
Packit Service |
310c69 |
* @param what What the DataVIO was doing (for logging)
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void releasePageLock(DataVIO *dataVIO, char *what)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(lock->locked,
|
|
Packit Service |
310c69 |
"release of unlocked block map page %s for key %" PRIu64
|
|
Packit Service |
310c69 |
" in tree %u",
|
|
Packit Service |
310c69 |
what, lock->key, lock->rootIndex);
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = getBlockMapTreeZone(dataVIO);
|
|
Packit Service |
310c69 |
TreeLock *lockHolder = intMapRemove(zone->loadingPages, lock->key);
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY((lockHolder == lock),
|
|
Packit Service |
310c69 |
"block map page %s mismatch for key %llu in tree %u",
|
|
Packit Service |
310c69 |
what, lock->key, lock->rootIndex);
|
|
Packit Service |
310c69 |
lock->locked = false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue a DataVIO now that the lookup is complete.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO
|
|
Packit Service |
310c69 |
* @param result The result of the lookup
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void finishLookup(DataVIO *dataVIO, int result)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIO->treeLock.height = 0;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = getBlockMapTreeZone(dataVIO);
|
|
Packit Service |
310c69 |
--zone->activeLookups;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
VDOCompletion *completion = dataVIOAsCompletion(dataVIO);
|
|
Packit Service |
310c69 |
setCompletionResult(completion, result);
|
|
Packit Service |
310c69 |
launchCallback(completion, dataVIO->treeLock.callback,
|
|
Packit Service |
310c69 |
dataVIO->treeLock.threadID);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Abort a block map PBN lookup due to an error in the load or allocation on
|
|
Packit Service |
310c69 |
* which we were waiting.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The DataVIO which was waiting for a page load or allocation
|
|
Packit Service |
310c69 |
* @param context The error which caused the abort
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void abortLookupForWaiter(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = waiterAsDataVIO(waiter);
|
|
Packit Service |
310c69 |
int result = *((int *) context);
|
|
Packit Service |
310c69 |
if (isReadDataVIO(dataVIO)) {
|
|
Packit Service |
310c69 |
if (result == VDO_NO_SPACE) {
|
|
Packit Service |
310c69 |
result = VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
} else if (result != VDO_NO_SPACE) {
|
|
Packit Service |
310c69 |
result = VDO_READ_ONLY;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Abort a block map PBN lookup due to an error loading or allocating a page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO which was loading or allocating a page
|
|
Packit Service |
310c69 |
* @param result The error code
|
|
Packit Service |
310c69 |
* @param what What the DataVIO was doing (for logging)
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void abortLookup(DataVIO *dataVIO, int result, char *what)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (result != VDO_NO_SPACE) {
|
|
Packit Service |
310c69 |
enterZoneReadOnlyMode(getBlockMapTreeZone(dataVIO), result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dataVIO->treeLock.locked) {
|
|
Packit Service |
310c69 |
releasePageLock(dataVIO, what);
|
|
Packit Service |
310c69 |
notifyAllWaiters(&dataVIO->treeLock.waiters, abortLookupForWaiter,
|
|
Packit Service |
310c69 |
&result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Abort a block map PBN lookup due to an error loading a page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO doing the page load
|
|
Packit Service |
310c69 |
* @param result The error code
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void abortLoad(DataVIO *dataVIO, int result)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
abortLookup(dataVIO, result, "load");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Determine if a location represents a valid mapping for a tree page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param vdo The VDO
|
|
Packit Service |
310c69 |
* @param mapping The DataLocation to check
|
|
Packit Service |
310c69 |
* @param height The height of the entry in the tree
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return true if the entry represents a invalid page mapping
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
__attribute__((warn_unused_result))
|
|
Packit Service |
310c69 |
static bool isInvalidTreeEntry(const VDO *vdo,
|
|
Packit Service |
310c69 |
const DataLocation *mapping,
|
|
Packit Service |
310c69 |
Height height)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (!isValidLocation(mapping)
|
|
Packit Service |
310c69 |
|| isCompressed(mapping->state)
|
|
Packit Service |
310c69 |
|| (isMappedLocation(mapping) && (mapping->pbn == ZERO_BLOCK))) {
|
|
Packit Service |
310c69 |
return true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Roots aren't physical data blocks, so we can't check their PBNs.
|
|
Packit Service |
310c69 |
if (height == BLOCK_MAP_TREE_HEIGHT) {
|
|
Packit Service |
310c69 |
return false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return !isPhysicalDataBlock(vdo->depot, mapping->pbn);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void loadBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO);
|
|
Packit Service |
310c69 |
static void allocateBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue a block map PBN lookup now that a page has been loaded by
|
|
Packit Service |
310c69 |
* descending one level in the tree.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO doing the lookup
|
|
Packit Service |
310c69 |
* @param page The page which was just loaded
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void continueWithLoadedPage(DataVIO *dataVIO, BlockMapPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
BlockMapTreeSlot slot = lock->treeSlots[lock->height];
|
|
Packit Service |
310c69 |
DataLocation mapping
|
|
Packit Service |
310c69 |
= unpackBlockMapEntry(&page->entries[slot.blockMapSlot.slot]);
|
|
Packit Service |
310c69 |
if (isInvalidTreeEntry(getVDOFromDataVIO(dataVIO), &mapping, lock->height)) {
|
|
Packit Service |
310c69 |
logErrorWithStringError(VDO_BAD_MAPPING,
|
|
Packit Service |
310c69 |
"Invalid block map tree PBN: %llu with "
|
|
Packit Service |
310c69 |
"state %u for page index %u at height %u",
|
|
Packit Service |
310c69 |
mapping.pbn, mapping.state,
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height - 1].pageIndex,
|
|
Packit Service |
310c69 |
lock->height - 1);
|
|
Packit Service |
310c69 |
abortLoad(dataVIO, VDO_BAD_MAPPING);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (!isMappedLocation(&mapping)) {
|
|
Packit Service |
310c69 |
// The page we need is unallocated
|
|
Packit Service |
310c69 |
allocateBlockMapPage(getBlockMapTreeZone(dataVIO), dataVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height - 1].blockMapSlot.pbn = mapping.pbn;
|
|
Packit Service |
310c69 |
if (lock->height == 1) {
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// We know what page we need to load next
|
|
Packit Service |
310c69 |
loadBlockMapPage(getBlockMapTreeZone(dataVIO), dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue a block map PBN lookup now that the page load we were waiting on
|
|
Packit Service |
310c69 |
* has finished.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The DataVIO waiting for a page to be loaded
|
|
Packit Service |
310c69 |
* @param context The page which was just loaded
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void continueLoadForWaiter(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = waiterAsDataVIO(waiter);
|
|
Packit Service |
310c69 |
dataVIO->treeLock.height--;
|
|
Packit Service |
310c69 |
continueWithLoadedPage(dataVIO, (BlockMapPage *) context);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Finish loading a page now that it has been read in from disk. This callback
|
|
Packit Service |
310c69 |
* is registered in loadPage().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO doing the page read
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void finishBlockMapPageLoad(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = completion->parent;
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = entry->parent;
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = (BlockMapTreeZone *) entry->context;
|
|
Packit Service |
310c69 |
TreeLock *treeLock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
treeLock->height--;
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn
|
|
Packit Service |
310c69 |
= treeLock->treeSlots[treeLock->height].blockMapSlot.pbn;
|
|
Packit Service |
310c69 |
TreePage *treePage = getTreePage(zone, treeLock);
|
|
Packit Service |
310c69 |
BlockMapPage *page = (BlockMapPage *) treePage->pageBuffer;
|
|
Packit Service |
310c69 |
Nonce nonce = zone->mapZone->blockMap->nonce;
|
|
Packit Service |
310c69 |
if (!copyValidPage(entry->buffer, nonce, pbn, page)) {
|
|
Packit Service |
310c69 |
formatBlockMapPage(page, nonce, pbn, false);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
returnVIOToPool(zone->vioPool, entry);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Release our claim to the load and wake any waiters
|
|
Packit Service |
310c69 |
releasePageLock(dataVIO, "load");
|
|
Packit Service |
310c69 |
notifyAllWaiters(&treeLock->waiters, continueLoadForWaiter, page);
|
|
Packit Service |
310c69 |
continueWithLoadedPage(dataVIO, page);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Handle an error loading a tree page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The VIO doing the page read
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void handleIOError(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = completion->result;
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = completion->parent;
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = entry->parent;
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = (BlockMapTreeZone *) entry->context;
|
|
Packit Service |
310c69 |
returnVIOToPool(zone->vioPool, entry);
|
|
Packit Service |
310c69 |
abortLoad(dataVIO, result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Read a tree page from disk now that we've gotten a VIO with which to do the
|
|
Packit Service |
310c69 |
* read. This WaiterCallback is registered in loadBlockMapPage().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The DataVIO which requires a page load
|
|
Packit Service |
310c69 |
* @param context The VIOPool entry with which to do the read
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void loadPage(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
VIOPoolEntry *entry = context;
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = waiterAsDataVIO(waiter);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
entry->parent = dataVIO;
|
|
Packit Service |
310c69 |
entry->vio->completion.callbackThreadID
|
|
Packit Service |
310c69 |
= getBlockMapForZone(dataVIO->logical.zone)->threadID;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
launchReadMetadataVIO(entry->vio,
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height - 1].blockMapSlot.pbn,
|
|
Packit Service |
310c69 |
finishBlockMapPageLoad, handleIOError);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Attempt to acquire a lock on a page in the block map tree. If the page is
|
|
Packit Service |
310c69 |
* already locked, queue up to wait for the lock to be released. If the lock is
|
|
Packit Service |
310c69 |
* acquired, the DataVIO's treeLock.locked field will be set to true.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The BlockMapTreeZone in which the DataVIO operates
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO which desires a page lock
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return VDO_SUCCESS or an error
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static int attemptPageLock(BlockMapTreeZone *zone, DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
Height height = lock->height;
|
|
Packit Service |
310c69 |
BlockMapTreeSlot treeSlot = lock->treeSlots[height];
|
|
Packit Service |
310c69 |
PageKey key;
|
|
Packit Service |
310c69 |
key.descriptor = (PageDescriptor) {
|
|
Packit Service |
310c69 |
.rootIndex = lock->rootIndex,
|
|
Packit Service |
310c69 |
.height = height,
|
|
Packit Service |
310c69 |
.pageIndex = treeSlot.pageIndex,
|
|
Packit Service |
310c69 |
.slot = treeSlot.blockMapSlot.slot,
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
lock->key = key.key;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreeLock *lockHolder;
|
|
Packit Service |
310c69 |
int result = intMapPut(zone->loadingPages, lock->key, lock, false,
|
|
Packit Service |
310c69 |
(void **) &lockHolder);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (lockHolder == NULL) {
|
|
Packit Service |
310c69 |
// We got the lock
|
|
Packit Service |
310c69 |
dataVIO->treeLock.locked = true;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Someone else is loading or allocating the page we need
|
|
Packit Service |
310c69 |
return enqueueDataVIO(&lockHolder->waiters, dataVIO,
|
|
Packit Service |
310c69 |
THIS_LOCATION("$F;cb=blockMapTreePage"));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Load a block map tree page from disk.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The BlockMapTreeZone in which the DataVIO operates
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO which requires a page to be loaded
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void loadBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = attemptPageLock(zone, dataVIO);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
abortLoad(dataVIO, result);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dataVIO->treeLock.locked) {
|
|
Packit Service |
310c69 |
Waiter *waiter = dataVIOAsWaiter(dataVIO);
|
|
Packit Service |
310c69 |
waiter->callback = loadPage;
|
|
Packit Service |
310c69 |
result = acquireVIOFromPool(zone->vioPool, waiter);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
abortLoad(dataVIO, result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Set the callback of a DataVIO after it has allocated a block map page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void setPostAllocationCallback(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
setCallback(dataVIOAsCompletion(dataVIO), dataVIO->treeLock.callback,
|
|
Packit Service |
310c69 |
dataVIO->treeLock.threadID);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Abort a block map PBN lookup due to an error allocating a page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO doing the page allocation
|
|
Packit Service |
310c69 |
* @param result The error code
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void abortAllocation(DataVIO *dataVIO, int result)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
setPostAllocationCallback(dataVIO);
|
|
Packit Service |
310c69 |
abortLookup(dataVIO, result, "allocation");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Callback to handle an error while attempting to allocate a page. This
|
|
Packit Service |
310c69 |
* callback is used to transfer back to the logical zone along the block map
|
|
Packit Service |
310c69 |
* page allocation path.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The DataVIO doing the allocation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void allocationFailure(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = asDataVIO(completion);
|
|
Packit Service |
310c69 |
assertInLogicalZone(dataVIO);
|
|
Packit Service |
310c69 |
abortAllocation(dataVIO, completion->result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue with page allocations now that a parent page has been allocated.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param waiter The DataVIO which was waiting for a page to be allocated
|
|
Packit Service |
310c69 |
* @param context The physical block number of the page which was just
|
|
Packit Service |
310c69 |
* allocated
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void continueAllocationForWaiter(Waiter *waiter, void *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = waiterAsDataVIO(waiter);
|
|
Packit Service |
310c69 |
TreeLock *treeLock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn = *((PhysicalBlockNumber *) context);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
treeLock->height--;
|
|
Packit Service |
310c69 |
dataVIO->treeLock.treeSlots[treeLock->height].blockMapSlot.pbn = pbn;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (treeLock->height == 0) {
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
allocateBlockMapPage(getBlockMapTreeZone(dataVIO), dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Finish the page allocation process by recording the allocation in the tree
|
|
Packit Service |
310c69 |
* and waking any waiters now that the write lock has been released. This
|
|
Packit Service |
310c69 |
* callback is registered in releaseBlockMapWriteLock().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The DataVIO doing the allocation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void finishBlockMapAllocation(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = asDataVIO(completion);
|
|
Packit Service |
310c69 |
assertInLogicalZone(dataVIO);
|
|
Packit Service |
310c69 |
if (completion->result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
allocationFailure(completion);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = getBlockMapTreeZone(dataVIO);
|
|
Packit Service |
310c69 |
TreeLock *treeLock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
TreePage *treePage = getTreePage(zone, treeLock);
|
|
Packit Service |
310c69 |
Height height = treeLock->height;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn = treeLock->treeSlots[height - 1].blockMapSlot.pbn;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Record the allocation.
|
|
Packit Service |
310c69 |
BlockMapPage *page = (BlockMapPage *) treePage->pageBuffer;
|
|
Packit Service |
310c69 |
SequenceNumber oldLock = treePage->recoveryLock;
|
|
Packit Service |
310c69 |
updateBlockMapPage(page, dataVIO, pbn, MAPPING_STATE_UNCOMPRESSED,
|
|
Packit Service |
310c69 |
&treePage->recoveryLock);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (isWaiting(&treePage->waiter)) {
|
|
Packit Service |
310c69 |
// This page is waiting to be written out.
|
|
Packit Service |
310c69 |
if (zone->flusher != treePage) {
|
|
Packit Service |
310c69 |
// The outstanding flush won't cover the update we just made, so mark
|
|
Packit Service |
310c69 |
// the page as needing another flush.
|
|
Packit Service |
310c69 |
setGeneration(zone, treePage, zone->generation, true);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
// Put the page on a dirty list
|
|
Packit Service |
310c69 |
if (oldLock == 0) {
|
|
Packit Service |
310c69 |
initializeRing(&treePage->node);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
addToDirtyLists(zone->dirtyLists, &treePage->node, oldLock,
|
|
Packit Service |
310c69 |
treePage->recoveryLock);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
treeLock->height--;
|
|
Packit Service |
310c69 |
if (height > 1) {
|
|
Packit Service |
310c69 |
// Format the interior node we just allocated (in memory).
|
|
Packit Service |
310c69 |
treePage = getTreePage(zone, treeLock);
|
|
Packit Service |
310c69 |
formatBlockMapPage(treePage->pageBuffer, zone->mapZone->blockMap->nonce,
|
|
Packit Service |
310c69 |
pbn, false);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Release our claim to the allocation and wake any waiters
|
|
Packit Service |
310c69 |
releasePageLock(dataVIO, "allocation");
|
|
Packit Service |
310c69 |
notifyAllWaiters(&treeLock->waiters, continueAllocationForWaiter, &pbn);
|
|
Packit Service |
310c69 |
if (treeLock->height == 0) {
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
allocateBlockMapPage(zone, dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Release the write lock on a newly allocated block map page now that we
|
|
Packit Service |
310c69 |
* have made its journal entries and reference count updates. This callback
|
|
Packit Service |
310c69 |
* is registered in setBlockMapPageReferenceCount().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The DataVIO doing the allocation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void releaseBlockMapWriteLock(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = asDataVIO(completion);
|
|
Packit Service |
310c69 |
AllocatingVIO *allocatingVIO = dataVIOAsAllocatingVIO(dataVIO);
|
|
Packit Service |
310c69 |
assertInAllocatedZone(dataVIO);
|
|
Packit Service |
310c69 |
if (completion->result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
launchLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
releaseAllocationLock(allocatingVIO);
|
|
Packit Service |
310c69 |
resetAllocation(allocatingVIO);
|
|
Packit Service |
310c69 |
launchLogicalCallback(dataVIO, finishBlockMapAllocation,
|
|
Packit Service |
310c69 |
THIS_LOCATION("$F;cb=finishBlockMapAllocation"));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Set the reference count of a newly allocated block map page to
|
|
Packit Service |
310c69 |
* MAXIMUM_REFERENCES now that we have made a recovery journal entry for it.
|
|
Packit Service |
310c69 |
* MAXIMUM_REFERENCES is used to prevent deduplication against the block after
|
|
Packit Service |
310c69 |
* we release the write lock on it, but before we write out the page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The DataVIO doing the allocation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void setBlockMapPageReferenceCount(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = asDataVIO(completion);
|
|
Packit Service |
310c69 |
assertInAllocatedZone(dataVIO);
|
|
Packit Service |
310c69 |
if (completion->result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
launchLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn = lock->treeSlots[lock->height - 1].blockMapSlot.pbn;
|
|
Packit Service |
310c69 |
completion->callback = releaseBlockMapWriteLock;
|
|
Packit Service |
310c69 |
addSlabJournalEntry(getSlabJournal(getVDOFromDataVIO(dataVIO)->depot, pbn),
|
|
Packit Service |
310c69 |
dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Make a recovery journal entry for a newly allocated block map page.
|
|
Packit Service |
310c69 |
* This callback is registered in continueBlockMapPageAllocation().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The DataVIO doing the allocation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void journalBlockMapAllocation(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = asDataVIO(completion);
|
|
Packit Service |
310c69 |
assertInJournalZone(dataVIO);
|
|
Packit Service |
310c69 |
if (completion->result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
launchLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
setAllocatedZoneCallback(dataVIO, setBlockMapPageReferenceCount,
|
|
Packit Service |
310c69 |
THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
addRecoveryJournalEntry(getVDOFromDataVIO(dataVIO)->recoveryJournal,
|
|
Packit Service |
310c69 |
dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue the process of allocating a block map page now that the
|
|
Packit Service |
310c69 |
* BlockAllocator has given us a block. This method is supplied as the callback
|
|
Packit Service |
310c69 |
* to allocateDataBlock() by allocateBlockMapPage().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param allocatingVIO The DataVIO which is doing the allocation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void continueBlockMapPageAllocation(AllocatingVIO *allocatingVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = allocatingVIOAsDataVIO(allocatingVIO);
|
|
Packit Service |
310c69 |
if (!hasAllocation(dataVIO)) {
|
|
Packit Service |
310c69 |
setLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
continueDataVIO(dataVIO, VDO_NO_SPACE);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn = allocatingVIO->allocation;
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height - 1].blockMapSlot.pbn = pbn;
|
|
Packit Service |
310c69 |
setUpReferenceOperationWithLock(BLOCK_MAP_INCREMENT, pbn,
|
|
Packit Service |
310c69 |
MAPPING_STATE_UNCOMPRESSED,
|
|
Packit Service |
310c69 |
allocatingVIO->allocationLock,
|
|
Packit Service |
310c69 |
&dataVIO->operation);
|
|
Packit Service |
310c69 |
launchJournalCallback(dataVIO, journalBlockMapAllocation,
|
|
Packit Service |
310c69 |
THIS_LOCATION("$F;cb=journalBlockMapAllocation"));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Allocate a block map page.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param zone The zone in which the DataVIO is operating
|
|
Packit Service |
310c69 |
* @param dataVIO The DataVIO which needs to allocate a page
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void allocateBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (!isWriteDataVIO(dataVIO) || isTrimDataVIO(dataVIO)) {
|
|
Packit Service |
310c69 |
// This is a pure read, the read phase of a read-modify-write, or a trim,
|
|
Packit Service |
310c69 |
// so there's nothing left to do here.
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = attemptPageLock(zone, dataVIO);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
abortAllocation(dataVIO, result);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (!dataVIO->treeLock.locked) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
allocateDataBlock(dataVIOAsAllocatingVIO(dataVIO),
|
|
Packit Service |
310c69 |
getAllocationSelector(dataVIO->logical.zone),
|
|
Packit Service |
310c69 |
VIO_BLOCK_MAP_WRITE_LOCK,
|
|
Packit Service |
310c69 |
continueBlockMapPageAllocation);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void lookupBlockMapPBN(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
BlockMapTreeZone *zone = getBlockMapTreeZone(dataVIO);
|
|
Packit Service |
310c69 |
zone->activeLookups++;
|
|
Packit Service |
310c69 |
if (isDraining(&zone->mapZone->state)) {
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, VDO_SHUTTING_DOWN);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreeLock *lock = &dataVIO->treeLock;
|
|
Packit Service |
310c69 |
PageNumber pageIndex
|
|
Packit Service |
310c69 |
= ((lock->treeSlots[0].pageIndex - zone->mapZone->blockMap->flatPageCount)
|
|
Packit Service |
310c69 |
/ zone->mapZone->blockMap->rootCount);
|
|
Packit Service |
310c69 |
BlockMapTreeSlot treeSlot = {
|
|
Packit Service |
310c69 |
.pageIndex = pageIndex / BLOCK_MAP_ENTRIES_PER_PAGE,
|
|
Packit Service |
310c69 |
.blockMapSlot = {
|
|
Packit Service |
310c69 |
.pbn = 0,
|
|
Packit Service |
310c69 |
.slot = pageIndex % BLOCK_MAP_ENTRIES_PER_PAGE,
|
|
Packit Service |
310c69 |
},
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BlockMapPage *page = NULL;
|
|
Packit Service |
310c69 |
for (lock->height = 1; lock->height <= BLOCK_MAP_TREE_HEIGHT;
|
|
Packit Service |
310c69 |
lock->height++) {
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height] = treeSlot;
|
|
Packit Service |
310c69 |
page = (BlockMapPage *) (getTreePage(zone, lock)->pageBuffer);
|
|
Packit Service |
310c69 |
PhysicalBlockNumber pbn = getBlockMapPagePBN(page);
|
|
Packit Service |
310c69 |
if (pbn != ZERO_BLOCK) {
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height].blockMapSlot.pbn = pbn;
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Calculate the index and slot for the next level.
|
|
Packit Service |
310c69 |
treeSlot.blockMapSlot.slot
|
|
Packit Service |
310c69 |
= treeSlot.pageIndex % BLOCK_MAP_ENTRIES_PER_PAGE;
|
|
Packit Service |
310c69 |
treeSlot.pageIndex
|
|
Packit Service |
310c69 |
= treeSlot.pageIndex / BLOCK_MAP_ENTRIES_PER_PAGE;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// The page at this height has been allocated and loaded.
|
|
Packit Service |
310c69 |
DataLocation mapping
|
|
Packit Service |
310c69 |
= unpackBlockMapEntry(&page->entries[treeSlot.blockMapSlot.slot]);
|
|
Packit Service |
310c69 |
if (isInvalidTreeEntry(getVDOFromDataVIO(dataVIO), &mapping, lock->height)) {
|
|
Packit Service |
310c69 |
logErrorWithStringError(VDO_BAD_MAPPING,
|
|
Packit Service |
310c69 |
"Invalid block map tree PBN: %llu with "
|
|
Packit Service |
310c69 |
"state %u for page index %u at height %u",
|
|
Packit Service |
310c69 |
mapping.pbn, mapping.state,
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height - 1].pageIndex,
|
|
Packit Service |
310c69 |
lock->height - 1);
|
|
Packit Service |
310c69 |
abortLoad(dataVIO, VDO_BAD_MAPPING);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (!isMappedLocation(&mapping)) {
|
|
Packit Service |
310c69 |
// The page we want one level down has not been allocated, so allocate it.
|
|
Packit Service |
310c69 |
allocateBlockMapPage(zone, dataVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
lock->treeSlots[lock->height - 1].blockMapSlot.pbn = mapping.pbn;
|
|
Packit Service |
310c69 |
if (lock->height == 1) {
|
|
Packit Service |
310c69 |
// This is the ultimate block map page, so we're done
|
|
Packit Service |
310c69 |
finishLookup(dataVIO, VDO_SUCCESS);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// We know what page we need to load.
|
|
Packit Service |
310c69 |
loadBlockMapPage(zone, dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
PhysicalBlockNumber findBlockMapPagePBN(BlockMap *map, PageNumber pageNumber)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (pageNumber < map->flatPageCount) {
|
|
Packit Service |
310c69 |
return (BLOCK_MAP_FLAT_PAGE_ORIGIN + pageNumber);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
RootCount rootIndex = pageNumber % map->rootCount;
|
|
Packit Service |
310c69 |
PageNumber pageIndex = ((pageNumber - map->flatPageCount) / map->rootCount);
|
|
Packit Service |
310c69 |
SlotNumber slot = pageIndex % BLOCK_MAP_ENTRIES_PER_PAGE;
|
|
Packit Service |
310c69 |
pageIndex /= BLOCK_MAP_ENTRIES_PER_PAGE;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
TreePage *treePage
|
|
Packit Service |
310c69 |
= getTreePageByIndex(map->forest, rootIndex, 1, pageIndex);
|
|
Packit Service |
310c69 |
BlockMapPage *page = (BlockMapPage *) treePage->pageBuffer;
|
|
Packit Service |
310c69 |
if (!isBlockMapPageInitialized(page)) {
|
|
Packit Service |
310c69 |
return ZERO_BLOCK;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataLocation mapping = unpackBlockMapEntry(&page->entries[slot]);
|
|
Packit Service |
310c69 |
if (!isValidLocation(&mapping) || isCompressed(mapping.state)) {
|
|
Packit Service |
310c69 |
return ZERO_BLOCK;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
return mapping.pbn;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void writeTreePage(TreePage *page, BlockMapTreeZone *zone)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
bool waiting = isWaiting(&page->waiter);
|
|
Packit Service |
310c69 |
if (waiting && (zone->flusher == page)) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
setGeneration(zone, page, zone->generation, waiting);
|
|
Packit Service |
310c69 |
if (waiting || page->writing) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
enqueuePage(page, zone);
|
|
Packit Service |
310c69 |
}
|