/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/blockMapPage.c#8 $
*/
#include "blockMapPage.h"
#include "permassert.h"
#include "blockMap.h"
#include "blockMapInternals.h"
#include "blockMapTree.h"
#include "constants.h"
#include "dataVIO.h"
#include "recoveryJournal.h"
#include "statusCodes.h"
#include "types.h"
enum {
PAGE_HEADER_4_1_SIZE = 8 + 8 + 8 + 1 + 1 + 1 + 1,
};
static const VersionNumber BLOCK_MAP_4_1 = {
.majorVersion = 4,
.minorVersion = 1,
};
/**********************************************************************/
bool isCurrentBlockMapPage(const BlockMapPage *page)
{
return areSameVersion(BLOCK_MAP_4_1, unpackVersionNumber(page->version));
}
/**********************************************************************/
BlockMapPage *formatBlockMapPage(void *buffer,
Nonce nonce,
PhysicalBlockNumber pbn,
bool initialized)
{
memset(buffer, 0, VDO_BLOCK_SIZE);
BlockMapPage *page = (BlockMapPage *) buffer;
page->version = packVersionNumber(BLOCK_MAP_4_1);
storeUInt64LE(page->header.fields.nonce, nonce);
storeUInt64LE(page->header.fields.pbn, pbn);
page->header.fields.initialized = initialized;
return page;
}
/**********************************************************************/
BlockMapPageValidity validateBlockMapPage(BlockMapPage *page,
Nonce nonce,
PhysicalBlockNumber pbn)
{
// Make sure the page layout isn't accidentally changed by changing the
// length of the page header.
STATIC_ASSERT_SIZEOF(PageHeader, PAGE_HEADER_4_1_SIZE);
if (!areSameVersion(BLOCK_MAP_4_1, unpackVersionNumber(page->version))
|| !isBlockMapPageInitialized(page)
|| (nonce != getUInt64LE(page->header.fields.nonce))) {
return BLOCK_MAP_PAGE_INVALID;
}
if (pbn != getBlockMapPagePBN(page)) {
return BLOCK_MAP_PAGE_BAD;
}
return BLOCK_MAP_PAGE_VALID;
}
/**********************************************************************/
void updateBlockMapPage(BlockMapPage *page,
DataVIO *dataVIO,
PhysicalBlockNumber pbn,
BlockMappingState mappingState,
SequenceNumber *recoveryLock)
{
// Encode the new mapping.
TreeLock *treeLock = &dataVIO->treeLock;
SlotNumber slot = treeLock->treeSlots[treeLock->height].blockMapSlot.slot;
page->entries[slot] = packPBN(pbn, mappingState);
// Adjust references (locks) on the recovery journal blocks.
BlockMapZone *zone = getBlockMapForZone(dataVIO->logical.zone);
BlockMap *blockMap = zone->blockMap;
RecoveryJournal *journal = blockMap->journal;
SequenceNumber oldLocked = *recoveryLock;
SequenceNumber newLocked = dataVIO->recoverySequenceNumber;
if ((oldLocked == 0) || (oldLocked > newLocked)) {
// Acquire a lock on the newly referenced journal block.
acquireRecoveryJournalBlockReference(journal, newLocked, ZONE_TYPE_LOGICAL,
zone->zoneNumber);
// If the block originally held a newer lock, release it.
if (oldLocked > 0) {
releaseRecoveryJournalBlockReference(journal, oldLocked,
ZONE_TYPE_LOGICAL,
zone->zoneNumber);
}
*recoveryLock = newLocked;
}
// Release the transferred lock from the DataVIO.
releasePerEntryLockFromOtherZone(journal, newLocked);
dataVIO->recoverySequenceNumber = 0;
}