/* * 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; }