Blame source/vdo/base/recoveryJournalBlock.c

Packit Service 75d76b
/*
Packit Service 75d76b
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 75d76b
 *
Packit Service 75d76b
 * This program is free software; you can redistribute it and/or
Packit Service 75d76b
 * modify it under the terms of the GNU General Public License
Packit Service 75d76b
 * as published by the Free Software Foundation; either version 2
Packit Service 75d76b
 * of the License, or (at your option) any later version.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * This program is distributed in the hope that it will be useful,
Packit Service 75d76b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 75d76b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 75d76b
 * GNU General Public License for more details.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * You should have received a copy of the GNU General Public License
Packit Service 75d76b
 * along with this program; if not, write to the Free Software
Packit Service 75d76b
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 75d76b
 * 02110-1301, USA. 
Packit Service 75d76b
 *
Packit Service 75d76b
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/recoveryJournalBlock.c#13 $
Packit Service 75d76b
 */
Packit Service 75d76b
Packit Service 75d76b
#include "recoveryJournalBlock.h"
Packit Service 75d76b
Packit Service 75d76b
#include "logger.h"
Packit Service 75d76b
#include "memoryAlloc.h"
Packit Service 75d76b
Packit Service 75d76b
#include "dataVIO.h"
Packit Service 75d76b
#include "fixedLayout.h"
Packit Service 75d76b
#include "packedRecoveryJournalBlock.h"
Packit Service 75d76b
#include "recoveryJournalEntry.h"
Packit Service 75d76b
#include "recoveryJournalInternals.h"
Packit Service 75d76b
#include "ringNode.h"
Packit Service 75d76b
#include "vio.h"
Packit Service 75d76b
#include "waitQueue.h"
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int makeRecoveryBlock(PhysicalLayer         *layer,
Packit Service 75d76b
                      RecoveryJournal       *journal,
Packit Service 75d76b
                      RecoveryJournalBlock **blockPtr)
Packit Service 75d76b
{
Packit Service 75d76b
  // Ensure that a block is large enough to store
Packit Service 75d76b
  // RECOVERY_JOURNAL_ENTRIES_PER_BLOCK entries.
Packit Service 75d76b
  STATIC_ASSERT(RECOVERY_JOURNAL_ENTRIES_PER_BLOCK
Packit Service 75d76b
                <= ((VDO_BLOCK_SIZE - sizeof(PackedJournalHeader))
Packit Service 75d76b
                    / sizeof(PackedRecoveryJournalEntry)));
Packit Service 75d76b
Packit Service 75d76b
  RecoveryJournalBlock *block;
Packit Service 75d76b
  int result = ALLOCATE(1, RecoveryJournalBlock, __func__, &block);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  // Allocate a full block for the journal block even though not all of the
Packit Service 75d76b
  // space is used since the VIO needs to write a full disk block.
Packit Service 75d76b
  result = ALLOCATE(VDO_BLOCK_SIZE, char, "PackedJournalBlock", &block->block);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    freeRecoveryBlock(&block);
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  result = createVIO(layer, VIO_TYPE_RECOVERY_JOURNAL, VIO_PRIORITY_HIGH,
Packit Service 75d76b
                     block, block->block, &block->vio);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    freeRecoveryBlock(&block);
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  block->vio->completion.callbackThreadID = journal->threadID;
Packit Service 75d76b
  initializeRing(&block->ringNode);
Packit Service 75d76b
  block->journal = journal;
Packit Service 75d76b
Packit Service 75d76b
  *blockPtr = block;
Packit Service 75d76b
  return VDO_SUCCESS;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void freeRecoveryBlock(RecoveryJournalBlock **blockPtr)
Packit Service 75d76b
{
Packit Service 75d76b
  RecoveryJournalBlock *block = *blockPtr;
Packit Service 75d76b
  if (block == NULL) {
Packit Service 75d76b
    return;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  FREE(block->block);
Packit Service 75d76b
  freeVIO(&block->vio);
Packit Service 75d76b
  FREE(block);
Packit Service 75d76b
  *blockPtr = NULL;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Get a pointer to the packed journal block header in the block buffer.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param block  The recovery block
Packit Service 75d76b
 *
Packit Service 75d76b
 * @return The block's header
Packit Service 75d76b
 **/
Packit Service 75d76b
static inline
Packit Service 75d76b
PackedJournalHeader *getBlockHeader(const RecoveryJournalBlock *block)
Packit Service 75d76b
{
Packit Service 75d76b
  return (PackedJournalHeader *) block->block;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Set the current sector of the current block and initialize it.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param block  The block to update
Packit Service 75d76b
 * @param sector A pointer to the first byte of the new sector
Packit Service 75d76b
 **/
Packit Service 75d76b
static void setActiveSector(RecoveryJournalBlock *block, void *sector)
Packit Service 75d76b
{
Packit Service 75d76b
  block->sector                = (PackedJournalSector *) sector;
Packit Service 75d76b
  block->sector->checkByte     = getBlockHeader(block)->fields.checkByte;
Packit Service 75d76b
  block->sector->recoveryCount = block->journal->recoveryCount;
Packit Service 75d76b
  block->sector->entryCount    = 0;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void initializeRecoveryBlock(RecoveryJournalBlock *block)
Packit Service 75d76b
{
Packit Service 75d76b
  memset(block->block, 0x0, VDO_BLOCK_SIZE);
Packit Service 75d76b
Packit Service 75d76b
  RecoveryJournal *journal     = block->journal;
Packit Service 75d76b
  block->sequenceNumber        = journal->tail;
Packit Service 75d76b
  block->entryCount            = 0;
Packit Service 75d76b
  block->uncommittedEntryCount = 0;
Packit Service 75d76b
Packit Service 75d76b
  block->blockNumber = getRecoveryJournalBlockNumber(journal, journal->tail);
Packit Service 75d76b
Packit Service 75d76b
  RecoveryBlockHeader unpacked = {
Packit Service 75d76b
    .metadataType       = VDO_METADATA_RECOVERY_JOURNAL,
Packit Service 75d76b
    .blockMapDataBlocks = journal->blockMapDataBlocks,
Packit Service 75d76b
    .logicalBlocksUsed  = journal->logicalBlocksUsed,
Packit Service 75d76b
    .nonce              = journal->nonce,
Packit Service 75d76b
    .recoveryCount      = journal->recoveryCount,
Packit Service 75d76b
    .sequenceNumber     = journal->tail,
Packit Service 75d76b
    .checkByte          = computeRecoveryCheckByte(journal, journal->tail),
Packit Service 75d76b
  };
Packit Service 75d76b
  PackedJournalHeader *header = getBlockHeader(block);
Packit Service 75d76b
  packRecoveryBlockHeader(&unpacked, header);
Packit Service 75d76b
Packit Service 75d76b
  setActiveSector(block, getJournalBlockSector(header, 1));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int enqueueRecoveryBlockEntry(RecoveryJournalBlock *block, DataVIO *dataVIO)
Packit Service 75d76b
{
Packit Service 75d76b
  // First queued entry indicates this is a journal block we've just opened
Packit Service 75d76b
  // or a committing block we're extending and will have to write again.
Packit Service 75d76b
  bool newBatch = !hasWaiters(&block->entryWaiters);
Packit Service 75d76b
Packit Service 75d76b
  // Enqueue the DataVIO to wait for its entry to commit.
Packit Service 75d76b
  int result = enqueueDataVIO(&block->entryWaiters, dataVIO,
Packit Service 75d76b
                              THIS_LOCATION("$F($j-$js)"));
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  block->entryCount++;
Packit Service 75d76b
  block->uncommittedEntryCount++;
Packit Service 75d76b
Packit Service 75d76b
  // Update stats to reflect the journal entry we're going to write.
Packit Service 75d76b
  if (newBatch) {
Packit Service 75d76b
    block->journal->events.blocks.started++;
Packit Service 75d76b
  }
Packit Service 75d76b
  block->journal->events.entries.started++;
Packit Service 75d76b
Packit Service 75d76b
  return VDO_SUCCESS;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Check whether the current sector of a block is full.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param block  The block to check
Packit Service 75d76b
 *
Packit Service 75d76b
 * @return true if the sector is full
Packit Service 75d76b
 **/
Packit Service 75d76b
__attribute__((warn_unused_result))
Packit Service 75d76b
static bool isSectorFull(const RecoveryJournalBlock *block)
Packit Service 75d76b
{
Packit Service 75d76b
  return (block->sector->entryCount == RECOVERY_JOURNAL_ENTRIES_PER_SECTOR);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Actually add entries from the queue to the given block.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param block  The journal block
Packit Service 75d76b
 *
Packit Service 75d76b
 * @return VDO_SUCCESS or an error code
Packit Service 75d76b
 **/
Packit Service 75d76b
__attribute__((warn_unused_result))
Packit Service 75d76b
static int addQueuedRecoveryEntries(RecoveryJournalBlock *block)
Packit Service 75d76b
{
Packit Service 75d76b
  while (hasWaiters(&block->entryWaiters)) {
Packit Service 75d76b
    DataVIO *dataVIO
Packit Service 75d76b
      = waiterAsDataVIO(dequeueNextWaiter(&block->entryWaiters));
Packit Service 75d76b
    if (dataVIO->operation.type == DATA_INCREMENT) {
Packit Service 75d76b
      // In order to not lose committed sectors of this partial write, we must
Packit Service 75d76b
      // flush before the partial write entries are committed.
Packit Service 75d76b
      block->hasPartialWriteEntry = (block->hasPartialWriteEntry
Packit Service 75d76b
                                     || dataVIO->isPartialWrite);
Packit Service 75d76b
      /*
Packit Service 75d76b
       * In order to not lose acknowledged writes with the FUA flag set, we
Packit Service 75d76b
       * must issue a flush to cover the data write and also all previous
Packit Service 75d76b
       * journal writes, and we must issue a FUA on the journal write.
Packit Service 75d76b
       */
Packit Service 75d76b
      block->hasFUAEntry = (block->hasFUAEntry
Packit Service 75d76b
                            || vioRequiresFlushAfter(dataVIOAsVIO(dataVIO)));
Packit Service 75d76b
    }
Packit Service 75d76b
Packit Service 75d76b
    // Compose and encode the entry.
Packit Service 75d76b
    PackedRecoveryJournalEntry *packedEntry
Packit Service 75d76b
      = &block->sector->entries[block->sector->entryCount++];
Packit Service 75d76b
    TreeLock *lock = &dataVIO->treeLock;
Packit Service 75d76b
    RecoveryJournalEntry newEntry = {
Packit Service 75d76b
      .mapping   = {
Packit Service 75d76b
        .pbn     = dataVIO->operation.pbn,
Packit Service 75d76b
        .state   = dataVIO->operation.state,
Packit Service 75d76b
      },
Packit Service 75d76b
      .operation = dataVIO->operation.type,
Packit Service 75d76b
      .slot      = lock->treeSlots[lock->height].blockMapSlot,
Packit Service 75d76b
    };
Packit Service 75d76b
    *packedEntry = packRecoveryJournalEntry(&newEntry);
Packit Service 75d76b
Packit Service 75d76b
    if (isIncrementOperation(dataVIO->operation.type)) {
Packit Service 75d76b
      dataVIO->recoverySequenceNumber = block->sequenceNumber;
Packit Service 75d76b
    }
Packit Service 75d76b
Packit Service 75d76b
    // Enqueue the DataVIO to wait for its entry to commit.
Packit Service 75d76b
    int result = enqueueDataVIO(&block->commitWaiters, dataVIO,
Packit Service 75d76b
                                THIS_LOCATION("$F($j-$js)"));
Packit Service 75d76b
    if (result != VDO_SUCCESS) {
Packit Service 75d76b
      continueDataVIO(dataVIO, result);
Packit Service 75d76b
      return result;
Packit Service 75d76b
    }
Packit Service 75d76b
Packit Service 75d76b
    if (isSectorFull(block)) {
Packit Service 75d76b
      setActiveSector(block, (char *) block->sector + VDO_SECTOR_SIZE);
Packit Service 75d76b
    }
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  return VDO_SUCCESS;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
__attribute__((warn_unused_result))
Packit Service 75d76b
static int getRecoveryBlockPBN(RecoveryJournalBlock *block,
Packit Service 75d76b
                               PhysicalBlockNumber  *pbnPtr)
Packit Service 75d76b
{
Packit Service 75d76b
  RecoveryJournal *journal = block->journal;
Packit Service 75d76b
  int result = translateToPBN(journal->partition, block->blockNumber, pbnPtr);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    logErrorWithStringError(result,
Packit Service 75d76b
                            "Error translating recovery journal block "
Packit Service 75d76b
                            "number %llu", block->blockNumber);
Packit Service 75d76b
  }
Packit Service 75d76b
  return result;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
bool canCommitRecoveryBlock(RecoveryJournalBlock *block)
Packit Service 75d76b
{
Packit Service 75d76b
  // Cannot commit in read-only mode, if already committing the block, or
Packit Service 75d76b
  // if there are no entries to commit.
Packit Service 75d76b
  return ((block != NULL)
Packit Service 75d76b
           && !block->committing
Packit Service 75d76b
           && hasWaiters(&block->entryWaiters)
Packit Service 75d76b
           && !isReadOnly(block->journal->readOnlyNotifier));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int commitRecoveryBlock(RecoveryJournalBlock *block,
Packit Service 75d76b
                        VDOAction            *callback,
Packit Service 75d76b
                        VDOAction            *errorHandler)
Packit Service 75d76b
{
Packit Service 75d76b
  int result = ASSERT(canCommitRecoveryBlock(block), "should never call %s"
Packit Service 75d76b
		      " when the block can't be committed", __func__);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  PhysicalBlockNumber blockPBN;
Packit Service 75d76b
  result = getRecoveryBlockPBN(block, &blockPBN);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  block->entriesInCommit = countWaiters(&block->entryWaiters);
Packit Service 75d76b
  result = addQueuedRecoveryEntries(block);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  RecoveryJournal     *journal = block->journal;
Packit Service 75d76b
  PackedJournalHeader *header  = getBlockHeader(block);
Packit Service 75d76b
Packit Service 75d76b
  // Update stats to reflect the block and entries we're about to write.
Packit Service 75d76b
  journal->pendingWriteCount      += 1;
Packit Service 75d76b
  journal->events.blocks.written  += 1;
Packit Service 75d76b
  journal->events.entries.written += block->entriesInCommit;
Packit Service 75d76b
Packit Service 75d76b
  storeUInt64LE(header->fields.blockMapHead,    journal->blockMapHead);
Packit Service 75d76b
  storeUInt64LE(header->fields.slabJournalHead, journal->slabJournalHead);
Packit Service 75d76b
  storeUInt16LE(header->fields.entryCount,      block->entryCount);
Packit Service 75d76b
Packit Service 75d76b
  block->committing = true;
Packit Service 75d76b
Packit Service 75d76b
  /*
Packit Service 75d76b
   * In sync or async mode, when we are writing an increment entry for a
Packit Service 75d76b
   * request with FUA, or when making the increment entry for a partial
Packit Service 75d76b
   * write, we need to make sure all the data being mapped to by this block
Packit Service 75d76b
   * is stable on disk and also that the recovery journal is stable up to
Packit Service 75d76b
   * the current block, so we must flush before writing.
Packit Service 75d76b
   *
Packit Service 75d76b
   * In sync mode, and for FUA, we also need to make sure that the write we
Packit Service 75d76b
   * are doing is stable, so we issue the write with FUA.
Packit Service 75d76b
   */
Packit Service 75d76b
  PhysicalLayer *layer        = vioAsCompletion(block->vio)->layer;
Packit Service 75d76b
  bool fua = (block->hasFUAEntry
Packit Service 75d76b
              || (layer->getWritePolicy(layer) == WRITE_POLICY_SYNC));
Packit Service 75d76b
  bool flush = (block->hasFUAEntry
Packit Service 75d76b
                || (layer->getWritePolicy(layer) != WRITE_POLICY_ASYNC_UNSAFE)
Packit Service 75d76b
		|| block->hasPartialWriteEntry);
Packit Service 75d76b
  block->hasFUAEntry          = false;
Packit Service 75d76b
  block->hasPartialWriteEntry = false;
Packit Service 75d76b
  launchWriteMetadataVIOWithFlush(block->vio, blockPBN, callback, errorHandler,
Packit Service 75d76b
                                  flush, fua);
Packit Service 75d76b
Packit Service 75d76b
  return VDO_SUCCESS;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void dumpRecoveryBlock(const RecoveryJournalBlock *block)
Packit Service 75d76b
{
Packit Service 75d76b
  logInfo("    sequence number %llu; entries %" PRIu16
Packit Service 75d76b
          "; %s; %zu entry waiters; %zu commit waiters",
Packit Service 75d76b
          block->sequenceNumber,
Packit Service 75d76b
          block->entryCount,
Packit Service 75d76b
          (block->committing ? "committing" : "waiting"),
Packit Service 75d76b
          countWaiters(&block->entryWaiters),
Packit Service 75d76b
          countWaiters(&block->commitWaiters));
Packit Service 75d76b
}