/*
* 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/upgrade.c#6 $
*/
#include "upgrade.h"
#include "logger.h"
#include "memoryAlloc.h"
#include "permassert.h"
#include "blockMap.h"
#include "readOnlyNotifier.h"
#include "recoveryJournal.h"
#include "releaseVersions.h"
#include "slabDepot.h"
#include "statusCodes.h"
#include "superBlock.h"
#include "vdoInternal.h"
#include "volumeGeometry.h"
/* The latest supported Sodium version */
/* Commented out because not currently used.
* static const VersionNumber SODIUM_MASTER_VERSION_67_0 = {
* .majorVersion = 67,
* .minorVersion = 0,
* };
*/
/* The component data version for current Sodium */
static const VersionNumber SODIUM_COMPONENT_DATA_41_0 = {
.majorVersion = 41,
.minorVersion = 0,
};
/**
* Current Sodium's configuration of the VDO component.
**/
typedef struct {
VDOState state;
uint64_t completeRecoveries;
uint64_t readOnlyRecoveries;
VDOConfig config;
Nonce nonce;
} __attribute__((packed)) SodiumComponent41_0;
/**
* Checks whether the release version loaded in the superblock is the
* current VDO version.
*
* @param vdo The VDO to validate
*
* @return true if the release version number is the current version
**/
static bool isCurrentReleaseVersion(VDO *vdo)
{
ReleaseVersionNumber loadedVersion
= getLoadedReleaseVersion(vdo->superBlock);
return (loadedVersion == CURRENT_RELEASE_VERSION_NUMBER);
}
/**
* Loads the VDO master version into the VDO and checks that the version
* can be understood by VDO.
*
* @param vdo The VDO to validate
*
* @return VDO_SUCCESS or an error if the loaded version is not supported
**/
static int validateSodiumVersion(VDO *vdo)
{
int result = decodeVDOVersion(vdo);
if (result != VDO_SUCCESS) {
return result;
}
if (isCurrentReleaseVersion(vdo)) {
return VDO_SUCCESS;
}
ReleaseVersionNumber loadedVersion
= getLoadedReleaseVersion(vdo->superBlock);
return logErrorWithStringError(VDO_UNSUPPORTED_VERSION,
"Release version %d, load version %d.%d"
" cannot be upgraded", loadedVersion,
vdo->loadVersion.majorVersion,
vdo->loadVersion.minorVersion);
}
/**
* Decode a SodiumComponent41_0.
*
* @param buffer The component data buffer
* @param component The component structure to decode into
*
* @return VDO_SUCCESS or an error code
**/
static int decodeSodium41_0Component(Buffer *buffer,
SodiumComponent41_0 *component)
{
return getBytesFromBuffer(buffer, sizeof(*component), component);
}
/**
* Decode the component data for the VDO itself from the component data
* buffer in the super block.
*
* @param vdo The VDO to decode
*
* @return VDO_SUCCESS or an error
**/
__attribute__((warn_unused_result))
static int decodeSodiumComponent(VDO *vdo)
{
Buffer *buffer = getComponentBuffer(vdo->superBlock);
VersionNumber version;
int result = decodeVersionNumber(buffer, &version);
if (result != VDO_SUCCESS) {
return result;
}
SodiumComponent41_0 component;
if (areSameVersion(SODIUM_COMPONENT_DATA_41_0, version)) {
result = decodeSodium41_0Component(buffer, &component);
} else {
return logErrorWithStringError(VDO_UNSUPPORTED_VERSION,
"VDO component data version mismatch,"
" expected 41.0, got %d.%d",
version.majorVersion,
version.minorVersion);
}
if (result != VDO_SUCCESS) {
return result;
}
// Copy the decoded component into the VDO structure.
vdo->state = component.state;
vdo->loadState = component.state;
vdo->completeRecoveries = component.completeRecoveries;
vdo->readOnlyRecoveries = component.readOnlyRecoveries;
vdo->config = component.config;
vdo->nonce = component.nonce;
logInfo("Converted VDO component data version %d.%d",
version.majorVersion, version.minorVersion);
return VDO_SUCCESS;
}
/**********************************************************************/
__attribute__((warn_unused_result))
static int finishSodiumDecode(VDO *vdo)
{
Buffer *buffer = getComponentBuffer(vdo->superBlock);
const ThreadConfig *threadConfig = getThreadConfig(vdo);
int result = makeRecoveryJournal(vdo->nonce, vdo->layer,
getVDOPartition(vdo->layout,
RECOVERY_JOURNAL_PARTITION),
vdo->completeRecoveries,
vdo->config.recoveryJournalSize,
RECOVERY_JOURNAL_TAIL_BUFFER_SIZE,
vdo->readOnlyNotifier, threadConfig,
&vdo->recoveryJournal);
if (result != VDO_SUCCESS) {
return result;
}
result = decodeSodiumRecoveryJournal(vdo->recoveryJournal, buffer);
if (result != VDO_SUCCESS) {
return result;
}
result = decodeSodiumSlabDepot(buffer, threadConfig, vdo->nonce, vdo->layer,
getVDOPartition(vdo->layout,
SLAB_SUMMARY_PARTITION),
vdo->readOnlyNotifier, vdo->recoveryJournal,
&vdo->depot);
if (result != VDO_SUCCESS) {
return result;
}
result = decodeSodiumBlockMap(buffer, vdo->config.logicalBlocks,
threadConfig, &vdo->blockMap);
if (result != VDO_SUCCESS) {
return result;
}
ASSERT_LOG_ONLY((contentLength(buffer) == 0),
"All decoded component data was used");
return VDO_SUCCESS;
}
/**********************************************************************/
int upgradePriorVDO(PhysicalLayer *layer)
{
VolumeGeometry geometry;
int result = loadVolumeGeometry(layer, &geometry);
if (result != VDO_SUCCESS) {
return result;
}
VDO *vdo;
result = makeVDO(layer, &vdo);
if (result != VDO_SUCCESS) {
return result;
}
result = loadSuperBlock(vdo->layer, getDataRegionOffset(geometry),
&vdo->superBlock);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return logErrorWithStringError(result, "Could not load VDO super block");
}
// Load the necessary pieces to save again.
result = validateSodiumVersion(vdo);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return result;
}
if (isCurrentReleaseVersion(vdo)) {
logInfo("VDO already up-to-date");
freeVDO(&vdo);
return VDO_SUCCESS;
}
result = decodeSodiumComponent(vdo);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return result;
}
if (requiresRebuild(vdo)) {
// Do not attempt to upgrade a dirty prior version.
freeVDO(&vdo);
return logErrorWithStringError(VDO_UNSUPPORTED_VERSION,
"Cannot upgrade a dirty VDO.");
}
result = decodeVDOLayout(getComponentBuffer(vdo->superBlock), &vdo->layout);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return result;
}
const ThreadConfig *threadConfig = getThreadConfig(vdo);
result = makeReadOnlyNotifier(inReadOnlyMode(vdo), threadConfig, vdo->layer,
&vdo->readOnlyNotifier);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return result;
}
result = finishSodiumDecode(vdo);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return result;
}
// Saving will automatically change the release version to current.
result = saveVDOComponents(vdo);
if (result != VDO_SUCCESS) {
freeVDO(&vdo);
return result;
}
logInfo("Successfully saved upgraded VDO");
freeVDO(&vdo);
return result;
}