/* * 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/vdoLayout.c#2 $ */ #include "vdoLayout.h" #include "vdoLayoutInternals.h" #include "logger.h" #include "memoryAlloc.h" #include "blockMap.h" #include "partitionCopy.h" #include "slab.h" #include "slabSummary.h" #include "types.h" #include "vdoInternal.h" #include "statusCodes.h" static const PartitionID REQUIRED_PARTITIONS[] = { BLOCK_MAP_PARTITION, BLOCK_ALLOCATOR_PARTITION, RECOVERY_JOURNAL_PARTITION, SLAB_SUMMARY_PARTITION, }; static const uint8_t REQUIRED_PARTITION_COUNT = 4; /** * Make a fixed layout for a VDO. * * @param [in] physicalBlocks The number of physical blocks in the VDO * @param [in] startingOffset The starting offset of the layout * @param [in] blockMapBlocks The size of the block map partition * @param [in] journalBlocks The size of the journal partition * @param [in] summaryBlocks The size of the slab summary partition * @param [out] layoutPtr A pointer to hold the new FixedLayout * * @return VDO_SUCCESS or an error **/ __attribute__((warn_unused_result)) static int makeVDOFixedLayout(BlockCount physicalBlocks, PhysicalBlockNumber startingOffset, BlockCount blockMapBlocks, BlockCount journalBlocks, BlockCount summaryBlocks, FixedLayout **layoutPtr) { BlockCount necessarySize = (startingOffset + blockMapBlocks + journalBlocks + summaryBlocks); if (necessarySize > physicalBlocks) { return logErrorWithStringError(VDO_NO_SPACE, "Not enough space to" " make a VDO"); } FixedLayout *layout; int result = makeFixedLayout(physicalBlocks - startingOffset, startingOffset, &layout); if (result != VDO_SUCCESS) { return result; } result = makeFixedLayoutPartition(layout, BLOCK_MAP_PARTITION, blockMapBlocks, FROM_BEGINNING, 0); if (result != VDO_SUCCESS) { freeFixedLayout(&layout); return result; } result = makeFixedLayoutPartition(layout, SLAB_SUMMARY_PARTITION, summaryBlocks, FROM_END, 0); if (result != VDO_SUCCESS) { freeFixedLayout(&layout); return result; } result = makeFixedLayoutPartition(layout, RECOVERY_JOURNAL_PARTITION, journalBlocks, FROM_END, 0); if (result != VDO_SUCCESS) { freeFixedLayout(&layout); return result; } /* * The block allocator no longer traffics in relative PBNs so the offset * doesn't matter. We need to keep this partition around both for upgraded * systems, and because we decided that all of the usable space in the * volume, other than the super block, should be part of some partition. */ result = makeFixedLayoutPartition(layout, BLOCK_ALLOCATOR_PARTITION, ALL_FREE_BLOCKS, FROM_BEGINNING, blockMapBlocks); if (result != VDO_SUCCESS) { freeFixedLayout(&layout); return result; } *layoutPtr = layout; return VDO_SUCCESS; } /** * Get the offset of a given partition. * * @param layout The layout containing the partition * @param partitionID The ID of the partition whose offset is desired * * @return The offset of the partition (in blocks) **/ __attribute__((warn_unused_result)) static BlockCount getPartitionOffset(VDOLayout *layout, PartitionID partitionID) { return getFixedLayoutPartitionOffset(getVDOPartition(layout, partitionID)); } /**********************************************************************/ int makeVDOLayout(BlockCount physicalBlocks, PhysicalBlockNumber startingOffset, BlockCount blockMapBlocks, BlockCount journalBlocks, BlockCount summaryBlocks, VDOLayout **vdoLayoutPtr) { VDOLayout *vdoLayout; int result = ALLOCATE(1, VDOLayout, __func__, &vdoLayout); if (result != VDO_SUCCESS) { return result; } result = makeVDOFixedLayout(physicalBlocks, startingOffset, blockMapBlocks, journalBlocks, summaryBlocks, &vdoLayout->layout); if (result != VDO_SUCCESS) { freeVDOLayout(&vdoLayout); return result; } vdoLayout->startingOffset = startingOffset; *vdoLayoutPtr = vdoLayout; return VDO_SUCCESS; } /**********************************************************************/ int decodeVDOLayout(Buffer *buffer, VDOLayout **vdoLayoutPtr) { VDOLayout *vdoLayout; int result = ALLOCATE(1, VDOLayout, __func__, &vdoLayout); if (result != VDO_SUCCESS) { return result; } result = decodeFixedLayout(buffer, &vdoLayout->layout); if (result != VDO_SUCCESS) { freeVDOLayout(&vdoLayout); return result; } // Check that all the expected partitions exist Partition *partition; for (uint8_t i = 0; i < REQUIRED_PARTITION_COUNT; i++) { result = getPartition(vdoLayout->layout, REQUIRED_PARTITIONS[i], &partition); if (result != VDO_SUCCESS) { freeVDOLayout(&vdoLayout); return logErrorWithStringError(result, "VDO layout is missing required partition" " %u", REQUIRED_PARTITIONS[i]); } } // XXX Assert this is the same as where we loaded the super block. vdoLayout->startingOffset = getPartitionOffset(vdoLayout, BLOCK_MAP_PARTITION); *vdoLayoutPtr = vdoLayout; return VDO_SUCCESS; } /**********************************************************************/ void freeVDOLayout(VDOLayout **vdoLayoutPtr) { VDOLayout *vdoLayout = *vdoLayoutPtr; if (vdoLayout == NULL) { return; } freeCopyCompletion(&vdoLayout->copyCompletion); freeFixedLayout(&vdoLayout->nextLayout); freeFixedLayout(&vdoLayout->layout); freeFixedLayout(&vdoLayout->previousLayout); FREE(vdoLayout); *vdoLayoutPtr = NULL; } /** * Get a partition from a FixedLayout in conditions where we expect that it can * not fail. * * @param layout The FixedLayout from which to get the partition * @param id The ID of the partition to retrieve * * @return The desired partition **/ __attribute__((warn_unused_result)) static Partition *retrievePartition(FixedLayout *layout, PartitionID id) { Partition *partition; int result = getPartition(layout, id, &partition); ASSERT_LOG_ONLY(result == VDO_SUCCESS, "VDOLayout has expected partition"); return partition; } /**********************************************************************/ Partition *getVDOPartition(VDOLayout *vdoLayout, PartitionID id) { return retrievePartition(vdoLayout->layout, id); } /** * Get a partition from a VDOLayout's next FixedLayout. This method should * only be called when the VDOLayout is prepared to grow. * * @param vdoLayout The VDOLayout from which to get the partition * @param id The ID of the desired partition * * @return The requested partition **/ __attribute__((warn_unused_result)) static Partition *getPartitionFromNextLayout(VDOLayout *vdoLayout, PartitionID id) { ASSERT_LOG_ONLY(vdoLayout->nextLayout != NULL, "VDOLayout is prepared to grow"); return retrievePartition(vdoLayout->nextLayout, id); } /** * Get the size of a given partition. * * @param layout The layout containing the partition * @param partitionID The partition ID whose size to find * * @return The size of the partition (in blocks) **/ __attribute__((warn_unused_result)) static BlockCount getPartitionSize(VDOLayout *layout, PartitionID partitionID) { return getFixedLayoutPartitionSize(getVDOPartition(layout, partitionID)); } /**********************************************************************/ int prepareToGrowVDOLayout(VDOLayout *vdoLayout, BlockCount oldPhysicalBlocks, BlockCount newPhysicalBlocks, PhysicalLayer *layer) { if (getNextVDOLayoutSize(vdoLayout) == newPhysicalBlocks) { // We are already prepared to grow to the new size, so we're done. return VDO_SUCCESS; } // Make a copy completion if there isn't one if (vdoLayout->copyCompletion == NULL) { int result = makeCopyCompletion(layer, &vdoLayout->copyCompletion); if (result != VDO_SUCCESS) { return result; } } // Free any unused preparation. freeFixedLayout(&vdoLayout->nextLayout); // Make a new layout with the existing partition sizes for everything but the // block allocator partition. int result = makeVDOFixedLayout(newPhysicalBlocks, vdoLayout->startingOffset, getPartitionSize(vdoLayout, BLOCK_MAP_PARTITION), getPartitionSize(vdoLayout, RECOVERY_JOURNAL_PARTITION), getPartitionSize(vdoLayout, SLAB_SUMMARY_PARTITION), &vdoLayout->nextLayout); if (result != VDO_SUCCESS) { freeCopyCompletion(&vdoLayout->copyCompletion); return result; } // Ensure the new journal and summary are entirely within the added blocks. Partition *slabSummaryPartition = getPartitionFromNextLayout(vdoLayout, SLAB_SUMMARY_PARTITION); Partition *recoveryJournalPartition = getPartitionFromNextLayout(vdoLayout, RECOVERY_JOURNAL_PARTITION); BlockCount minNewSize = (oldPhysicalBlocks + getFixedLayoutPartitionSize(slabSummaryPartition) + getFixedLayoutPartitionSize(recoveryJournalPartition)); if (minNewSize > newPhysicalBlocks) { // Copying the journal and summary would destroy some old metadata. freeFixedLayout(&vdoLayout->nextLayout); freeCopyCompletion(&vdoLayout->copyCompletion); return VDO_INCREMENT_TOO_SMALL; } return VDO_SUCCESS; } /** * Get the size of a VDO from the specified FixedLayout and the * starting offset thereof. * * @param layout The fixed layout whose size to use * @param startingOffset The starting offset of the layout * * @return The total size of a VDO (in blocks) with the given layout **/ __attribute__((warn_unused_result)) static BlockCount getVDOSize(FixedLayout *layout, BlockCount startingOffset) { // The FixedLayout does not include the super block or any earlier // metadata; all that is captured in the VDOLayout's starting offset return getTotalFixedLayoutSize(layout) + startingOffset; } /**********************************************************************/ BlockCount getNextVDOLayoutSize(VDOLayout *vdoLayout) { return ((vdoLayout->nextLayout == NULL) ? 0 : getVDOSize(vdoLayout->nextLayout, vdoLayout->startingOffset)); } /**********************************************************************/ BlockCount getNextBlockAllocatorPartitionSize(VDOLayout *vdoLayout) { if (vdoLayout->nextLayout == NULL) { return 0; } Partition *partition = getPartitionFromNextLayout(vdoLayout, BLOCK_ALLOCATOR_PARTITION); return getFixedLayoutPartitionSize(partition); } /**********************************************************************/ BlockCount growVDOLayout(VDOLayout *vdoLayout) { ASSERT_LOG_ONLY(vdoLayout->nextLayout != NULL, "VDO prepared to grow physical"); vdoLayout->previousLayout = vdoLayout->layout; vdoLayout->layout = vdoLayout->nextLayout; vdoLayout->nextLayout = NULL; return getVDOSize(vdoLayout->layout, vdoLayout->startingOffset); } /**********************************************************************/ BlockCount revertVDOLayout(VDOLayout *vdoLayout) { if ((vdoLayout->previousLayout != NULL) && (vdoLayout->previousLayout != vdoLayout->layout)) { // Only revert if there's something to revert to. freeFixedLayout(&vdoLayout->layout); vdoLayout->layout = vdoLayout->previousLayout; vdoLayout->previousLayout = NULL; } return getVDOSize(vdoLayout->layout, vdoLayout->startingOffset); } /**********************************************************************/ void finishVDOLayoutGrowth(VDOLayout *vdoLayout) { if (vdoLayout->layout != vdoLayout->previousLayout) { freeFixedLayout(&vdoLayout->previousLayout); } if (vdoLayout->layout != vdoLayout->nextLayout) { freeFixedLayout(&vdoLayout->nextLayout); } freeCopyCompletion(&vdoLayout->copyCompletion); } /**********************************************************************/ void copyPartition(VDOLayout *layout, PartitionID partitionID, VDOCompletion *parent) { copyPartitionAsync(layout->copyCompletion, getVDOPartition(layout, partitionID), getPartitionFromNextLayout(layout, partitionID), parent); } /**********************************************************************/ size_t getVDOLayoutEncodedSize(const VDOLayout *vdoLayout) { return getFixedLayoutEncodedSize(vdoLayout->layout); } /**********************************************************************/ int encodeVDOLayout(const VDOLayout *vdoLayout, Buffer *buffer) { return encodeFixedLayout(vdoLayout->layout, buffer); }