Blame source/vdo/base/superBlock.c

Packit Service d40955
/*
Packit Service d40955
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service d40955
 *
Packit Service d40955
 * This program is free software; you can redistribute it and/or
Packit Service d40955
 * modify it under the terms of the GNU General Public License
Packit Service d40955
 * as published by the Free Software Foundation; either version 2
Packit Service d40955
 * of the License, or (at your option) any later version.
Packit Service d40955
 * 
Packit Service d40955
 * This program is distributed in the hope that it will be useful,
Packit Service d40955
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d40955
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service d40955
 * GNU General Public License for more details.
Packit Service d40955
 * 
Packit Service d40955
 * You should have received a copy of the GNU General Public License
Packit Service d40955
 * along with this program; if not, write to the Free Software
Packit Service d40955
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service d40955
 * 02110-1301, USA. 
Packit Service d40955
 *
Packit Service d40955
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/superBlock.c#5 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "superBlock.h"
Packit Service d40955
Packit Service d40955
#include "buffer.h"
Packit Service d40955
#include "logger.h"
Packit Service d40955
#include "memoryAlloc.h"
Packit Service d40955
#include "permassert.h"
Packit Service d40955
Packit Service d40955
#include "completion.h"
Packit Service d40955
#include "constants.h"
Packit Service d40955
#include "header.h"
Packit Service d40955
#include "releaseVersions.h"
Packit Service d40955
#include "statusCodes.h"
Packit Service d40955
#include "types.h"
Packit Service d40955
#include "vio.h"
Packit Service d40955
Packit Service d40955
struct superBlock {
Packit Service d40955
  /** The parent for asynchronous load and save operations */
Packit Service d40955
  VDOCompletion        *parent;
Packit Service d40955
  /** The VIO for reading and writing the super block to disk */
Packit Service d40955
  VIO                  *vio;
Packit Service d40955
  /** The buffer for encoding and decoding component data */
Packit Service d40955
  Buffer               *componentBuffer;
Packit Service d40955
  /**
Packit Service d40955
   * A sector-sized buffer wrapping the first sector of encodedSuperBlock, for
Packit Service d40955
   * encoding and decoding the entire super block.
Packit Service d40955
   **/
Packit Service d40955
  Buffer               *blockBuffer;
Packit Service d40955
  /** A 1-block buffer holding the encoded on-disk super block */
Packit Service d40955
  byte                 *encodedSuperBlock;
Packit Service d40955
  /** The release version number loaded from the volume */
Packit Service d40955
  ReleaseVersionNumber  loadedReleaseVersion;
Packit Service d40955
  /** Whether this super block may not be written */
Packit Service d40955
  bool                  unwriteable;
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
enum {
Packit Service d40955
  SUPER_BLOCK_FIXED_SIZE
Packit Service d40955
    = ENCODED_HEADER_SIZE + sizeof(ReleaseVersionNumber) + CHECKSUM_SIZE,
Packit Service d40955
  MAX_COMPONENT_DATA_SIZE = VDO_SECTOR_SIZE - SUPER_BLOCK_FIXED_SIZE,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
static const Header SUPER_BLOCK_HEADER_12_0 = {
Packit Service d40955
  .id = SUPER_BLOCK,
Packit Service d40955
  .version = {
Packit Service d40955
    .majorVersion = 12,
Packit Service d40955
    .minorVersion = 0,
Packit Service d40955
  },
Packit Service d40955
Packit Service d40955
  // This is the minimum size, if the super block contains no components.
Packit Service d40955
  .size = SUPER_BLOCK_FIXED_SIZE - ENCODED_HEADER_SIZE,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Allocate a super block. Callers must free the allocated super block even
Packit Service d40955
 * on error.
Packit Service d40955
 *
Packit Service d40955
 * @param layer  The physical layer which holds the super block on disk
Packit Service d40955
 * @param superBlockPtr  A pointer to hold the new super block
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int allocateSuperBlock(PhysicalLayer *layer, SuperBlock **superBlockPtr)
Packit Service d40955
{
Packit Service d40955
  int result = ALLOCATE(1, SuperBlock, __func__, superBlockPtr);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  SuperBlock *superBlock = *superBlockPtr;
Packit Service d40955
  result = makeBuffer(MAX_COMPONENT_DATA_SIZE, &superBlock->componentBuffer);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = layer->allocateIOBuffer(layer, VDO_BLOCK_SIZE,
Packit Service d40955
                                   "encoded super block",
Packit Service d40955
                                   (char **) &superBlock->encodedSuperBlock);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Even though the buffer is a full block, to avoid the potential corruption
Packit Service d40955
  // from a torn write, the entire encoding must fit in the first sector.
Packit Service d40955
  result = wrapBuffer(superBlock->encodedSuperBlock, VDO_SECTOR_SIZE, 0,
Packit Service d40955
                      &superBlock->blockBuffer);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (layer->createMetadataVIO == NULL) {
Packit Service d40955
    return VDO_SUCCESS;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return createVIO(layer, VIO_TYPE_SUPER_BLOCK, VIO_PRIORITY_METADATA,
Packit Service d40955
                   superBlock, (char *) superBlock->encodedSuperBlock,
Packit Service d40955
                   &superBlock->vio);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int makeSuperBlock(PhysicalLayer *layer, SuperBlock **superBlockPtr)
Packit Service d40955
{
Packit Service d40955
  SuperBlock *superBlock;
Packit Service d40955
  int         result = allocateSuperBlock(layer, &superBlock);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeSuperBlock(&superBlock);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // For a new super block, use the current release.
Packit Service d40955
  superBlock->loadedReleaseVersion = CURRENT_RELEASE_VERSION_NUMBER;
Packit Service d40955
  *superBlockPtr = superBlock;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void freeSuperBlock(SuperBlock **superBlockPtr)
Packit Service d40955
{
Packit Service d40955
  if (*superBlockPtr == NULL) {
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  SuperBlock *superBlock = *superBlockPtr;
Packit Service d40955
  freeBuffer(&superBlock->blockBuffer);
Packit Service d40955
  freeBuffer(&superBlock->componentBuffer);
Packit Service d40955
  freeVIO(&superBlock->vio);
Packit Service d40955
  FREE(superBlock->encodedSuperBlock);
Packit Service d40955
  FREE(superBlock);
Packit Service d40955
  *superBlockPtr = NULL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Encode a super block into its on-disk representation.
Packit Service d40955
 *
Packit Service d40955
 * @param layer       The physical layer which implements the checksum
Packit Service d40955
 * @param superBlock  The super block to encode
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int encodeSuperBlock(PhysicalLayer *layer, SuperBlock *superBlock)
Packit Service d40955
{
Packit Service d40955
  Buffer *buffer = superBlock->blockBuffer;
Packit Service d40955
  int     result = resetBufferEnd(buffer, 0);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  size_t componentDataSize = contentLength(superBlock->componentBuffer);
Packit Service d40955
Packit Service d40955
  // Encode the header.
Packit Service d40955
  Header header = SUPER_BLOCK_HEADER_12_0;
Packit Service d40955
  header.size += componentDataSize;
Packit Service d40955
  result = encodeHeader(&header, buffer);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Encode the loaded release version.
Packit Service d40955
  result = putUInt32LEIntoBuffer(buffer, superBlock->loadedReleaseVersion);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Copy the already-encoded component data.
Packit Service d40955
  result = putBytes(buffer, componentDataSize,
Packit Service d40955
                    getBufferContents(superBlock->componentBuffer));
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Compute and encode the checksum.
Packit Service d40955
  CRC32Checksum checksum = layer->updateCRC32(INITIAL_CHECKSUM,
Packit Service d40955
                                              superBlock->encodedSuperBlock,
Packit Service d40955
                                              contentLength(buffer));
Packit Service d40955
  result = putUInt32LEIntoBuffer(buffer, checksum);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return UDS_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int saveSuperBlock(PhysicalLayer       *layer,
Packit Service d40955
                   SuperBlock          *superBlock,
Packit Service d40955
                   PhysicalBlockNumber  superBlockOffset)
Packit Service d40955
{
Packit Service d40955
  int result = encodeSuperBlock(layer, superBlock);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return layer->writer(layer, superBlockOffset, 1,
Packit Service d40955
                       (char *) superBlock->encodedSuperBlock, NULL);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Finish the parent of a super block load or save operation. This
Packit Service d40955
 * callback is registered in saveSuperBlockAsync() and loadSuperBlockAsync.
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The super block VIO
Packit Service d40955
 **/
Packit Service d40955
static void finishSuperBlockParent(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  SuperBlock    *superBlock = completion->parent;
Packit Service d40955
  VDOCompletion *parent     = superBlock->parent;
Packit Service d40955
  superBlock->parent        = NULL;
Packit Service d40955
  finishCompletion(parent, completion->result);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Log a super block save error. This error handler is registered in
Packit Service d40955
 * saveSuperBlockAsync().
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The super block VIO
Packit Service d40955
 **/
Packit Service d40955
static void handleSaveError(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  logErrorWithStringError(completion->result, "super block save failed");
Packit Service d40955
  /*
Packit Service d40955
   * Mark the super block as unwritable so that we won't attempt to write it
Packit Service d40955
   * again. This avoids the case where a growth attempt fails writing the
Packit Service d40955
   * super block with the new size, but the subsequent attempt to write out
Packit Service d40955
   * the read-only state succeeds. In this case, writes which happened just
Packit Service d40955
   * before the suspend would not be visible if the VDO is restarted without
Packit Service d40955
   * rebuilding, but, after a read-only rebuild, the effects of those writes
Packit Service d40955
   * would reappear.
Packit Service d40955
   */
Packit Service d40955
  ((SuperBlock *) completion->parent)->unwriteable = true;
Packit Service d40955
  completion->callback(completion);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void saveSuperBlockAsync(SuperBlock          *superBlock,
Packit Service d40955
                         PhysicalBlockNumber  superBlockOffset,
Packit Service d40955
                         VDOCompletion       *parent)
Packit Service d40955
{
Packit Service d40955
  if (superBlock->unwriteable) {
Packit Service d40955
    finishCompletion(parent, VDO_READ_ONLY);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (superBlock->parent != NULL) {
Packit Service d40955
    finishCompletion(parent, VDO_COMPONENT_BUSY);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  PhysicalLayer *layer = parent->layer;
Packit Service d40955
  int result = encodeSuperBlock(layer, superBlock);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    finishCompletion(parent, result);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  superBlock->parent                           = parent;
Packit Service d40955
  superBlock->vio->completion.callbackThreadID = parent->callbackThreadID;
Packit Service d40955
  launchWriteMetadataVIOWithFlush(superBlock->vio, superBlockOffset,
Packit Service d40955
                                  finishSuperBlockParent, handleSaveError,
Packit Service d40955
                                  true, true);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Decode a super block from its on-disk representation.
Packit Service d40955
 *
Packit Service d40955
 * @param layer       The physical layer which implements the checksum
Packit Service d40955
 * @param superBlock  The super block to decode
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int decodeSuperBlock(PhysicalLayer *layer, SuperBlock *superBlock)
Packit Service d40955
{
Packit Service d40955
  // Reset the block buffer to start decoding the entire first sector.
Packit Service d40955
  Buffer *buffer = superBlock->blockBuffer;
Packit Service d40955
  clearBuffer(buffer);
Packit Service d40955
Packit Service d40955
  // Decode and validate the header.
Packit Service d40955
  Header header;
Packit Service d40955
  int result = decodeHeader(buffer, &header);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = validateHeader(&SUPER_BLOCK_HEADER_12_0, &header, false, __func__);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (header.size > contentLength(buffer)) {
Packit Service d40955
    // We can't check release version or checksum until we know the content
Packit Service d40955
    // size, so we have to assume a version mismatch on unexpected values.
Packit Service d40955
    return logErrorWithStringError(VDO_UNSUPPORTED_VERSION,
Packit Service d40955
                                   "super block contents too large: %zu",
Packit Service d40955
                                   header.size);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Restrict the buffer to the actual payload bytes that remain.
Packit Service d40955
  result = resetBufferEnd(buffer, uncompactedAmount(buffer) + header.size);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Decode and store the release version number. It will be checked when the
Packit Service d40955
  // VDO master version is decoded and validated.
Packit Service d40955
  result = getUInt32LEFromBuffer(buffer, &superBlock->loadedReleaseVersion);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // The component data is all the rest, except for the checksum.
Packit Service d40955
  size_t componentDataSize = contentLength(buffer) - sizeof(CRC32Checksum);
Packit Service d40955
  result = putBuffer(superBlock->componentBuffer, buffer, componentDataSize);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Checksum everything up to but not including the saved checksum itself.
Packit Service d40955
  CRC32Checksum checksum = layer->updateCRC32(INITIAL_CHECKSUM,
Packit Service d40955
                                              superBlock->encodedSuperBlock,
Packit Service d40955
                                              uncompactedAmount(buffer));
Packit Service d40955
Packit Service d40955
  // Decode and verify the saved checksum.
Packit Service d40955
  CRC32Checksum savedChecksum;
Packit Service d40955
  result = getUInt32LEFromBuffer(buffer, &savedChecksum);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = ASSERT(contentLength(buffer) == 0,
Packit Service d40955
                  "must have decoded entire superblock payload");
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return ((checksum != savedChecksum) ? VDO_CHECKSUM_MISMATCH : VDO_SUCCESS);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int loadSuperBlock(PhysicalLayer        *layer,
Packit Service d40955
                   PhysicalBlockNumber   superBlockOffset,
Packit Service d40955
                   SuperBlock          **superBlockPtr)
Packit Service d40955
{
Packit Service d40955
  SuperBlock *superBlock = NULL;
Packit Service d40955
  int         result     = allocateSuperBlock(layer, &superBlock);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeSuperBlock(&superBlock);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = layer->reader(layer, superBlockOffset, 1,
Packit Service d40955
                         (char *) superBlock->encodedSuperBlock, NULL);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeSuperBlock(&superBlock);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = decodeSuperBlock(layer, superBlock);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeSuperBlock(&superBlock);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  *superBlockPtr = superBlock;
Packit Service d40955
  return result;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Continue after loading the super block. This callback is registered
Packit Service d40955
 * in loadSuperBlockAsync().
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The super block VIO
Packit Service d40955
 **/
Packit Service d40955
static void finishReadingSuperBlock(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  SuperBlock    *superBlock = completion->parent;
Packit Service d40955
  VDOCompletion *parent     = superBlock->parent;
Packit Service d40955
  superBlock->parent        = NULL;
Packit Service d40955
  finishCompletion(parent, decodeSuperBlock(completion->layer, superBlock));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void loadSuperBlockAsync(VDOCompletion        *parent,
Packit Service d40955
                         PhysicalBlockNumber   superBlockOffset,
Packit Service d40955
                         SuperBlock          **superBlockPtr)
Packit Service d40955
{
Packit Service d40955
  PhysicalLayer *layer      = parent->layer;
Packit Service d40955
  SuperBlock    *superBlock = NULL;
Packit Service d40955
  int            result     = allocateSuperBlock(layer, &superBlock);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeSuperBlock(&superBlock);
Packit Service d40955
    finishCompletion(parent, result);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  *superBlockPtr = superBlock;
Packit Service d40955
Packit Service d40955
  superBlock->parent                           = parent;
Packit Service d40955
  superBlock->vio->completion.callbackThreadID = parent->callbackThreadID;
Packit Service d40955
  launchReadMetadataVIO(superBlock->vio, superBlockOffset,
Packit Service d40955
                        finishReadingSuperBlock, finishSuperBlockParent);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
Buffer *getComponentBuffer(SuperBlock *superBlock)
Packit Service d40955
{
Packit Service d40955
  return superBlock->componentBuffer;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
ReleaseVersionNumber getLoadedReleaseVersion(const SuperBlock *superBlock)
Packit Service d40955
{
Packit Service d40955
  return superBlock->loadedReleaseVersion;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
size_t getFixedSuperBlockSize(void)
Packit Service d40955
{
Packit Service d40955
  return SUPER_BLOCK_FIXED_SIZE;
Packit Service d40955
}