Blame source/vdo/base/refCounts.c

Packit Service 310c69
/*
Packit Service 310c69
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 310c69
 *
Packit Service 310c69
 * This program is free software; you can redistribute it and/or
Packit Service 310c69
 * modify it under the terms of the GNU General Public License
Packit Service 310c69
 * as published by the Free Software Foundation; either version 2
Packit Service 310c69
 * of the License, or (at your option) any later version.
Packit Service 310c69
 * 
Packit Service 310c69
 * This program is distributed in the hope that it will be useful,
Packit Service 310c69
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 310c69
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 310c69
 * GNU General Public License for more details.
Packit Service 310c69
 * 
Packit Service 310c69
 * You should have received a copy of the GNU General Public License
Packit Service 310c69
 * along with this program; if not, write to the Free Software
Packit Service 310c69
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 310c69
 * 02110-1301, USA. 
Packit Service 310c69
 *
Packit Service 310c69
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/refCounts.c#9 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
#include "refCounts.h"
Packit Service 310c69
#include "refCountsInternals.h"
Packit Service 310c69
Packit Service 310c69
#include "logger.h"
Packit Service 310c69
#include "memoryAlloc.h"
Packit Service 310c69
#include "numeric.h"
Packit Service 310c69
#include "permassert.h"
Packit Service 310c69
Packit Service 310c69
#include "adminState.h"
Packit Service 310c69
#include "blockAllocatorInternals.h"
Packit Service 310c69
#include "completion.h"
Packit Service 310c69
#include "extent.h"
Packit Service 310c69
#include "header.h"
Packit Service 310c69
#include "journalPoint.h"
Packit Service 310c69
#include "numUtils.h"
Packit Service 310c69
#include "pbnLock.h"
Packit Service 310c69
#include "readOnlyNotifier.h"
Packit Service 310c69
#include "referenceBlock.h"
Packit Service 310c69
#include "referenceOperation.h"
Packit Service 310c69
#include "slab.h"
Packit Service 310c69
#include "slabJournal.h"
Packit Service 310c69
#include "slabJournalInternals.h"
Packit Service 310c69
#include "slabSummary.h"
Packit Service 310c69
#include "statusCodes.h"
Packit Service 310c69
#include "stringUtils.h"
Packit Service 310c69
#include "vdo.h"
Packit Service 310c69
#include "vioPool.h"
Packit Service 310c69
#include "waitQueue.h"
Packit Service 310c69
Packit Service 310c69
static const uint64_t BYTES_PER_WORD   = sizeof(uint64_t);
Packit Service 310c69
static const bool     NORMAL_OPERATION = true;
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Return the RefCounts from the RefCounts waiter.
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter  The waiter to convert
Packit Service 310c69
 *
Packit Service 310c69
 * @return  The RefCounts
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static inline RefCounts *refCountsFromWaiter(Waiter *waiter)
Packit Service 310c69
{
Packit Service 310c69
  if (waiter == NULL) {
Packit Service 310c69
    return NULL;
Packit Service 310c69
  }
Packit Service 310c69
  return (RefCounts *)
Packit Service 310c69
    ((uintptr_t) waiter - offsetof(RefCounts, slabSummaryWaiter));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Convert the index of a reference counter back to the block number of the
Packit Service 310c69
 * physical block for which it is counting references. The index is assumed to
Packit Service 310c69
 * be valid and in-range.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts  The reference counts object
Packit Service 310c69
 * @param index      The array index of the reference counter
Packit Service 310c69
 *
Packit Service 310c69
 * @return the physical block number corresponding to the index
Packit Service 310c69
 **/
Packit Service 310c69
static PhysicalBlockNumber indexToPBN(const RefCounts *refCounts,
Packit Service 310c69
                                      uint64_t         index)
Packit Service 310c69
{
Packit Service 310c69
  return (refCounts->slab->start + index);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Convert a block number to the index of a reference counter for that block.
Packit Service 310c69
 * Out of range values are pinned to the beginning or one past the end of the
Packit Service 310c69
 * array.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts  The reference counts object
Packit Service 310c69
 * @param pbn        The physical block number
Packit Service 310c69
 *
Packit Service 310c69
 * @return the index corresponding to the physical block number
Packit Service 310c69
 **/
Packit Service 310c69
static uint64_t pbnToIndex(const RefCounts *refCounts, PhysicalBlockNumber pbn)
Packit Service 310c69
{
Packit Service 310c69
  if (pbn < refCounts->slab->start) {
Packit Service 310c69
    return 0;
Packit Service 310c69
  }
Packit Service 310c69
  uint64_t index = (pbn - refCounts->slab->start);
Packit Service 310c69
  return minBlock(index, refCounts->blockCount);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
ReferenceStatus referenceCountToStatus(ReferenceCount count)
Packit Service 310c69
{
Packit Service 310c69
  if (count == EMPTY_REFERENCE_COUNT) {
Packit Service 310c69
    return RS_FREE;
Packit Service 310c69
  } else if (count == 1) {
Packit Service 310c69
    return RS_SINGLE;
Packit Service 310c69
  } else if (count == PROVISIONAL_REFERENCE_COUNT) {
Packit Service 310c69
    return RS_PROVISIONAL;
Packit Service 310c69
  } else {
Packit Service 310c69
    return RS_SHARED;
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Reset the free block search back to the first reference counter
Packit Service 310c69
 * in the first reference block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts  The RefCounts object containing the search cursor
Packit Service 310c69
 **/
Packit Service 310c69
static void resetSearchCursor(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  SearchCursor *cursor = &refCounts->searchCursor;
Packit Service 310c69
Packit Service 310c69
  cursor->block    = cursor->firstBlock;
Packit Service 310c69
  cursor->index    = 0;
Packit Service 310c69
  // Unit tests have slabs with only one reference block (and it's a runt).
Packit Service 310c69
  cursor->endIndex = minBlock(COUNTS_PER_BLOCK, refCounts->blockCount);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Advance the search cursor to the start of the next reference block,
Packit Service 310c69
 * wrapping around to the first reference block if the current block is the
Packit Service 310c69
 * last reference block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts  The RefCounts object containing the search cursor
Packit Service 310c69
 *
Packit Service 310c69
 * @return true unless the cursor was at the last reference block
Packit Service 310c69
 **/
Packit Service 310c69
static bool advanceSearchCursor(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  SearchCursor *cursor = &refCounts->searchCursor;
Packit Service 310c69
Packit Service 310c69
  // If we just finished searching the last reference block, then wrap back
Packit Service 310c69
  // around to the start of the array.
Packit Service 310c69
  if (cursor->block == cursor->lastBlock) {
Packit Service 310c69
    resetSearchCursor(refCounts);
Packit Service 310c69
    return false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // We're not already at the end, so advance to cursor to the next block.
Packit Service 310c69
  cursor->block++;
Packit Service 310c69
  cursor->index = cursor->endIndex;
Packit Service 310c69
Packit Service 310c69
  if (cursor->block == cursor->lastBlock) {
Packit Service 310c69
    // The last reference block will usually be a runt.
Packit Service 310c69
    cursor->endIndex = refCounts->blockCount;
Packit Service 310c69
  } else {
Packit Service 310c69
    cursor->endIndex += COUNTS_PER_BLOCK;
Packit Service 310c69
  }
Packit Service 310c69
  return true;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int makeRefCounts(BlockCount            blockCount,
Packit Service 310c69
                  Slab                 *slab,
Packit Service 310c69
                  PhysicalBlockNumber   origin,
Packit Service 310c69
                  ReadOnlyNotifier     *readOnlyNotifier,
Packit Service 310c69
                  RefCounts           **refCountsPtr)
Packit Service 310c69
{
Packit Service 310c69
  BlockCount  refBlockCount = getSavedReferenceCountSize(blockCount);
Packit Service 310c69
  RefCounts  *refCounts;
Packit Service 310c69
  int result = ALLOCATE_EXTENDED(RefCounts, refBlockCount, ReferenceBlock,
Packit Service 310c69
                                 "ref counts structure", &refCounts);
Packit Service 310c69
  if (result != UDS_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Allocate such that the runt slab has a full-length memory array,
Packit Service 310c69
  // plus a little padding so we can word-search even at the very end.
Packit Service 310c69
  size_t bytes = ((refBlockCount * COUNTS_PER_BLOCK) + (2 * BYTES_PER_WORD));
Packit Service 310c69
  result = ALLOCATE(bytes, ReferenceCount, "ref counts array",
Packit Service 310c69
                    &refCounts->counters);
Packit Service 310c69
  if (result != UDS_SUCCESS) {
Packit Service 310c69
    freeRefCounts(&refCounts);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  refCounts->slab                    = slab;
Packit Service 310c69
  refCounts->blockCount              = blockCount;
Packit Service 310c69
  refCounts->freeBlocks              = blockCount;
Packit Service 310c69
  refCounts->origin                  = origin;
Packit Service 310c69
  refCounts->referenceBlockCount     = refBlockCount;
Packit Service 310c69
  refCounts->readOnlyNotifier        = readOnlyNotifier;
Packit Service 310c69
  refCounts->statistics              = &slab->allocator->refCountStatistics;
Packit Service 310c69
  refCounts->searchCursor.firstBlock = &refCounts->blocks[0];
Packit Service 310c69
  refCounts->searchCursor.lastBlock  = &refCounts->blocks[refBlockCount - 1];
Packit Service 310c69
  resetSearchCursor(refCounts);
Packit Service 310c69
Packit Service 310c69
  for (size_t index = 0; index < refBlockCount; index++) {
Packit Service 310c69
    refCounts->blocks[index] = (ReferenceBlock) {
Packit Service 310c69
      .refCounts = refCounts,
Packit Service 310c69
    };
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *refCountsPtr = refCounts;
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void freeRefCounts(RefCounts **refCountsPtr)
Packit Service 310c69
{
Packit Service 310c69
  RefCounts *refCounts = *refCountsPtr;
Packit Service 310c69
  if (refCounts == NULL) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  FREE(refCounts->counters);
Packit Service 310c69
  FREE(refCounts);
Packit Service 310c69
  *refCountsPtr = NULL;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Check whether a RefCounts has active I/O.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts  The RefCounts to check
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if there is reference block I/O or a summary
Packit Service 310c69
 *         update in progress
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static bool hasActiveIO(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  return ((refCounts->activeCount > 0) || refCounts->updatingSlabSummary);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool areRefCountsActive(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  if (hasActiveIO(refCounts)) {
Packit Service 310c69
    return true;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
    // When not suspending or recovering, the refCounts must be clean.
Packit Service 310c69
  AdminStateCode code = refCounts->slab->state.state;
Packit Service 310c69
  return (hasWaiters(&refCounts->dirtyBlocks)
Packit Service 310c69
          && (code != ADMIN_STATE_SUSPENDING)
Packit Service 310c69
          && (code != ADMIN_STATE_RECOVERING));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
static void enterRefCountsReadOnlyMode(RefCounts *refCounts, int result)
Packit Service 310c69
{
Packit Service 310c69
  enterReadOnlyMode(refCounts->readOnlyNotifier, result);
Packit Service 310c69
  checkIfSlabDrained(refCounts->slab);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Enqueue a block on the dirty queue.
Packit Service 310c69
 *
Packit Service 310c69
 * @param block  The block to enqueue
Packit Service 310c69
 **/
Packit Service 310c69
static void enqueueDirtyBlock(ReferenceBlock *block)
Packit Service 310c69
{
Packit Service 310c69
  int result = enqueueWaiter(&block->refCounts->dirtyBlocks, &block->waiter);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    // This should never happen.
Packit Service 310c69
    enterRefCountsReadOnlyMode(block->refCounts, result);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Mark a reference count block as dirty, potentially adding it to the dirty
Packit Service 310c69
 * queue if it wasn't already dirty.
Packit Service 310c69
 *
Packit Service 310c69
 * @param block  The reference block to mark as dirty
Packit Service 310c69
 **/
Packit Service 310c69
static void dirtyBlock(ReferenceBlock *block)
Packit Service 310c69
{
Packit Service 310c69
  if (block->isDirty) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  block->isDirty = true;
Packit Service 310c69
  if (block->isWriting) {
Packit Service 310c69
    // The conclusion of the current write will enqueue the block again.
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  enqueueDirtyBlock(block);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
BlockCount getUnreferencedBlockCount(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  return refCounts->freeBlocks;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
ReferenceBlock *getReferenceBlock(RefCounts *refCounts, SlabBlockNumber index)
Packit Service 310c69
{
Packit Service 310c69
  return &refCounts->blocks[index / COUNTS_PER_BLOCK];
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Get the reference counter that covers the given physical block number.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]  refCounts       The refcounts object
Packit Service 310c69
 * @param [in]  pbn             The physical block number
Packit Service 310c69
 * @param [out] counterPtr      A pointer to the reference counter
Packit Service 310c69
Packit Service 310c69
 **/
Packit Service 310c69
static int getReferenceCounter(RefCounts            *refCounts,
Packit Service 310c69
                               PhysicalBlockNumber   pbn,
Packit Service 310c69
                               ReferenceCount      **counterPtr)
Packit Service 310c69
{
Packit Service 310c69
  SlabBlockNumber index;
Packit Service 310c69
  int result = slabBlockNumberFromPBN(refCounts->slab, pbn, &index);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *counterPtr = &refCounts->counters[index];
Packit Service 310c69
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
uint8_t getAvailableReferences(RefCounts *refCounts, PhysicalBlockNumber pbn)
Packit Service 310c69
{
Packit Service 310c69
  ReferenceCount *counterPtr = NULL;
Packit Service 310c69
  int result = getReferenceCounter(refCounts, pbn, &counterPtr);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return 0;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (*counterPtr == PROVISIONAL_REFERENCE_COUNT) {
Packit Service 310c69
    return (MAXIMUM_REFERENCE_COUNT - 1);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return (MAXIMUM_REFERENCE_COUNT - *counterPtr);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Increment the reference count for a data block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]     refCounts          The refCounts responsible for the block
Packit Service 310c69
 * @param [in]     block              The reference block which contains the
Packit Service 310c69
 *                                    block being updated
Packit Service 310c69
 * @param [in]     slabBlockNumber    The block to update
Packit Service 310c69
 * @param [in]     oldStatus          The reference status of the data block
Packit Service 310c69
 *                                    before this increment
Packit Service 310c69
 * @param [in]     lock               The PBNLock associated with this
Packit Service 310c69
 *                                    increment (may be NULL)
Packit Service 310c69
 * @param [in,out] counterPtr         A pointer to the count for the data block
Packit Service 310c69
 * @param [out]    freeStatusChanged  A pointer which will be set to true if
Packit Service 310c69
 *                                    this update changed the free status of
Packit Service 310c69
 *                                    the block
Packit Service 310c69
 *
Packit Service 310c69
 * @return VDO_SUCCESS or an error
Packit Service 310c69
 **/
Packit Service 310c69
static int incrementForData(RefCounts       *refCounts,
Packit Service 310c69
                            ReferenceBlock  *block,
Packit Service 310c69
                            SlabBlockNumber  slabBlockNumber,
Packit Service 310c69
                            ReferenceStatus  oldStatus,
Packit Service 310c69
                            PBNLock         *lock,
Packit Service 310c69
                            ReferenceCount  *counterPtr,
Packit Service 310c69
                            bool            *freeStatusChanged)
Packit Service 310c69
{
Packit Service 310c69
  switch (oldStatus) {
Packit Service 310c69
  case RS_FREE:
Packit Service 310c69
    *counterPtr = 1;
Packit Service 310c69
    block->allocatedCount++;
Packit Service 310c69
    refCounts->freeBlocks--;
Packit Service 310c69
    *freeStatusChanged = true;
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case RS_PROVISIONAL:
Packit Service 310c69
    *counterPtr        = 1;
Packit Service 310c69
    *freeStatusChanged = false;
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  default:
Packit Service 310c69
    // Single or shared
Packit Service 310c69
    if (*counterPtr >= MAXIMUM_REFERENCE_COUNT) {
Packit Service 310c69
      return logErrorWithStringError(VDO_REF_COUNT_INVALID,
Packit Service 310c69
                                     "Incrementing a block already having"
Packit Service 310c69
                                     " 254 references (slab %u, offset %"
Packit Service 310c69
                                     PRIu32 ")",
Packit Service 310c69
                                     refCounts->slab->slabNumber,
Packit Service 310c69
                                     slabBlockNumber);
Packit Service 310c69
    }
Packit Service 310c69
    (*counterPtr)++;
Packit Service 310c69
    *freeStatusChanged = false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (lock != NULL) {
Packit Service 310c69
    unassignProvisionalReference(lock);
Packit Service 310c69
  }
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Decrement the reference count for a data block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]     refCounts          The refCounts responsible for the block
Packit Service 310c69
 * @param [in]     block              The reference block which contains the
Packit Service 310c69
 *                                    block being updated
Packit Service 310c69
 * @param [in]     slabBlockNumber    The block to update
Packit Service 310c69
 * @param [in]     oldStatus          The reference status of the data block
Packit Service 310c69
 *                                    before this decrement
Packit Service 310c69
 * @param [in]     lock               The PBNLock associated with the block
Packit Service 310c69
 *                                    being decremented (may be NULL)
Packit Service 310c69
 * @param [in,out] counterPtr         A pointer to the count for the data block
Packit Service 310c69
 * @param [out]    freeStatusChanged  A pointer which will be set to true if
Packit Service 310c69
 *                                    this update changed the free status of
Packit Service 310c69
 *                                    the block
Packit Service 310c69
 *
Packit Service 310c69
 * @return VDO_SUCCESS or an error
Packit Service 310c69
 **/
Packit Service 310c69
static int decrementForData(RefCounts       *refCounts,
Packit Service 310c69
                            ReferenceBlock  *block,
Packit Service 310c69
                            SlabBlockNumber  slabBlockNumber,
Packit Service 310c69
                            ReferenceStatus  oldStatus,
Packit Service 310c69
                            PBNLock         *lock,
Packit Service 310c69
                            ReferenceCount  *counterPtr,
Packit Service 310c69
                            bool            *freeStatusChanged)
Packit Service 310c69
{
Packit Service 310c69
  switch (oldStatus) {
Packit Service 310c69
  case RS_FREE:
Packit Service 310c69
    return logErrorWithStringError(VDO_REF_COUNT_INVALID,
Packit Service 310c69
                                   "Decrementing free block at offset %"
Packit Service 310c69
                                   PRIu32 " in slab %u", slabBlockNumber,
Packit Service 310c69
                                   refCounts->slab->slabNumber);
Packit Service 310c69
Packit Service 310c69
  case RS_PROVISIONAL:
Packit Service 310c69
  case RS_SINGLE:
Packit Service 310c69
    if (lock != NULL) {
Packit Service 310c69
      // There is a read lock on this block, so the block must not become
Packit Service 310c69
      // unreferenced.
Packit Service 310c69
      *counterPtr        = PROVISIONAL_REFERENCE_COUNT;
Packit Service 310c69
      *freeStatusChanged = false;
Packit Service 310c69
      assignProvisionalReference(lock);
Packit Service 310c69
    } else {
Packit Service 310c69
      *counterPtr = EMPTY_REFERENCE_COUNT;
Packit Service 310c69
      block->allocatedCount--;
Packit Service 310c69
      refCounts->freeBlocks++;
Packit Service 310c69
      *freeStatusChanged = true;
Packit Service 310c69
    }
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  default:
Packit Service 310c69
    // Shared
Packit Service 310c69
    (*counterPtr)--;
Packit Service 310c69
    *freeStatusChanged = false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Increment the reference count for a block map page. All block map increments
Packit Service 310c69
 * should be from provisional to MAXIMUM_REFERENCE_COUNT. Since block map blocks
Packit Service 310c69
 * never dedupe they should never be adjusted from any other state. The
Packit Service 310c69
 * adjustment always results in MAXIMUM_REFERENCE_COUNT as this value is used to
Packit Service 310c69
 * prevent dedupe against block map blocks.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]     refCounts          The refCounts responsible for the block
Packit Service 310c69
 * @param [in]     block              The reference block which contains the
Packit Service 310c69
 *                                    block being updated
Packit Service 310c69
 * @param [in]     slabBlockNumber    The block to update
Packit Service 310c69
 * @param [in]     oldStatus          The reference status of the block
Packit Service 310c69
 *                                    before this increment
Packit Service 310c69
 * @param [in]     lock               The PBNLock associated with this
Packit Service 310c69
 *                                    increment (may be NULL)
Packit Service 310c69
 * @param [in]     normalOperation    Whether we are in normal operation vs.
Packit Service 310c69
 *                                    recovery or rebuild
Packit Service 310c69
 * @param [in,out] counterPtr         A pointer to the count for the block
Packit Service 310c69
 * @param [out]    freeStatusChanged  A pointer which will be set to true if
Packit Service 310c69
 *                                    this update changed the free status of the
Packit Service 310c69
 *                                    block
Packit Service 310c69
 *
Packit Service 310c69
 * @return VDO_SUCCESS or an error
Packit Service 310c69
 **/
Packit Service 310c69
static int incrementForBlockMap(RefCounts       *refCounts,
Packit Service 310c69
                                ReferenceBlock  *block,
Packit Service 310c69
                                SlabBlockNumber  slabBlockNumber,
Packit Service 310c69
                                ReferenceStatus  oldStatus,
Packit Service 310c69
                                PBNLock         *lock,
Packit Service 310c69
                                bool             normalOperation,
Packit Service 310c69
                                ReferenceCount  *counterPtr,
Packit Service 310c69
                                bool            *freeStatusChanged)
Packit Service 310c69
{
Packit Service 310c69
  switch (oldStatus) {
Packit Service 310c69
  case RS_FREE:
Packit Service 310c69
    if (normalOperation) {
Packit Service 310c69
      return logErrorWithStringError(VDO_REF_COUNT_INVALID,
Packit Service 310c69
                                     "Incrementing unallocated block map block"
Packit Service 310c69
                                     " (slab %u, offset %" PRIu32 ")",
Packit Service 310c69
                                     refCounts->slab->slabNumber,
Packit Service 310c69
                                     slabBlockNumber);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    *counterPtr = MAXIMUM_REFERENCE_COUNT;
Packit Service 310c69
    block->allocatedCount++;
Packit Service 310c69
    refCounts->freeBlocks--;
Packit Service 310c69
    *freeStatusChanged = true;
Packit Service 310c69
    return VDO_SUCCESS;
Packit Service 310c69
Packit Service 310c69
  case RS_PROVISIONAL:
Packit Service 310c69
    if (!normalOperation) {
Packit Service 310c69
      return logErrorWithStringError(VDO_REF_COUNT_INVALID,
Packit Service 310c69
                                     "Block map block had provisional "
Packit Service 310c69
                                     "reference during replay"
Packit Service 310c69
                                     " (slab %u, offset %" PRIu32 ")",
Packit Service 310c69
                                     refCounts->slab->slabNumber,
Packit Service 310c69
                                     slabBlockNumber);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    *counterPtr        = MAXIMUM_REFERENCE_COUNT;
Packit Service 310c69
    *freeStatusChanged = false;
Packit Service 310c69
    if (lock != NULL) {
Packit Service 310c69
      unassignProvisionalReference(lock);
Packit Service 310c69
    }
Packit Service 310c69
    return VDO_SUCCESS;
Packit Service 310c69
Packit Service 310c69
  default:
Packit Service 310c69
    return logErrorWithStringError(VDO_REF_COUNT_INVALID,
Packit Service 310c69
                                   "Incrementing a block map block which is "
Packit Service 310c69
                                   "already referenced %u times (slab %u, "
Packit Service 310c69
                                   "offset %" PRIu32 ")",
Packit Service 310c69
                                   *counterPtr,
Packit Service 310c69
                                   refCounts->slab->slabNumber,
Packit Service 310c69
                                   slabBlockNumber);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Update the reference count of a block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]  refCounts                The refCounts responsible for the
Packit Service 310c69
 *                                       block
Packit Service 310c69
 * @param [in]  block                    The reference block which contains the
Packit Service 310c69
 *                                       block being updated
Packit Service 310c69
 * @param [in]  slabBlockNumber          The block to update
Packit Service 310c69
 * @param [in]  slabJournalPoint         The slab journal point at which this
Packit Service 310c69
 *                                       update is journaled
Packit Service 310c69
 * @param [in]  operation                How to update the count
Packit Service 310c69
 * @param [in]  normalOperation          Whether we are in normal operation vs.
Packit Service 310c69
 *                                       recovery or rebuild
Packit Service 310c69
 * @param [out] freeStatusChanged        A pointer which will be set to true if
Packit Service 310c69
 *                                       this update changed the free status of
Packit Service 310c69
 *                                       the block
Packit Service 310c69
 * @param [out] provisionalDecrementPtr  A pointer which will be set to true if
Packit Service 310c69
 *                                       this update was a decrement of a
Packit Service 310c69
 *                                       provisional reference
Packit Service 310c69
 *
Packit Service 310c69
 * @return VDO_SUCCESS or an error
Packit Service 310c69
 **/
Packit Service 310c69
static int updateReferenceCount(RefCounts          *refCounts,
Packit Service 310c69
                                ReferenceBlock     *block,
Packit Service 310c69
                                SlabBlockNumber     slabBlockNumber,
Packit Service 310c69
                                const JournalPoint *slabJournalPoint,
Packit Service 310c69
                                ReferenceOperation  operation,
Packit Service 310c69
                                bool                normalOperation,
Packit Service 310c69
                                bool               *freeStatusChanged,
Packit Service 310c69
                                bool               *provisionalDecrementPtr)
Packit Service 310c69
{
Packit Service 310c69
  ReferenceCount  *counterPtr = &refCounts->counters[slabBlockNumber];
Packit Service 310c69
  ReferenceStatus  oldStatus  = referenceCountToStatus(*counterPtr);
Packit Service 310c69
  PBNLock         *lock       = getReferenceOperationPBNLock(operation);
Packit Service 310c69
  int result;
Packit Service 310c69
Packit Service 310c69
  switch (operation.type) {
Packit Service 310c69
  case DATA_INCREMENT:
Packit Service 310c69
    result = incrementForData(refCounts, block, slabBlockNumber, oldStatus,
Packit Service 310c69
                              lock, counterPtr, freeStatusChanged);
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case DATA_DECREMENT:
Packit Service 310c69
    result = decrementForData(refCounts, block, slabBlockNumber, oldStatus,
Packit Service 310c69
                              lock, counterPtr, freeStatusChanged);
Packit Service 310c69
    if ((result == VDO_SUCCESS) && (oldStatus == RS_PROVISIONAL)) {
Packit Service 310c69
      if (provisionalDecrementPtr != NULL) {
Packit Service 310c69
        *provisionalDecrementPtr = true;
Packit Service 310c69
      }
Packit Service 310c69
      return VDO_SUCCESS;
Packit Service 310c69
    }
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case BLOCK_MAP_INCREMENT:
Packit Service 310c69
    result = incrementForBlockMap(refCounts, block, slabBlockNumber, oldStatus,
Packit Service 310c69
                                  lock, normalOperation, counterPtr,
Packit Service 310c69
                                  freeStatusChanged);
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  default:
Packit Service 310c69
    logError("Unknown reference count operation: %u", operation.type);
Packit Service 310c69
    enterRefCountsReadOnlyMode(refCounts, VDO_NOT_IMPLEMENTED);
Packit Service 310c69
    result = VDO_NOT_IMPLEMENTED;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (isValidJournalPoint(slabJournalPoint)) {
Packit Service 310c69
    refCounts->slabJournalPoint = *slabJournalPoint;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int adjustReferenceCount(RefCounts          *refCounts,
Packit Service 310c69
                         ReferenceOperation  operation,
Packit Service 310c69
                         const JournalPoint *slabJournalPoint,
Packit Service 310c69
                         bool               *freeStatusChanged)
Packit Service 310c69
{
Packit Service 310c69
  if (!isSlabOpen(refCounts->slab)) {
Packit Service 310c69
    return VDO_INVALID_ADMIN_STATE;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  SlabBlockNumber slabBlockNumber;
Packit Service 310c69
  int result = slabBlockNumberFromPBN(refCounts->slab, operation.pbn,
Packit Service 310c69
                                      &slabBlockNumber);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  ReferenceBlock *block = getReferenceBlock(refCounts, slabBlockNumber);
Packit Service 310c69
  bool provisionalDecrement = false;
Packit Service 310c69
  result = updateReferenceCount(refCounts, block, slabBlockNumber,
Packit Service 310c69
                                slabJournalPoint, operation,
Packit Service 310c69
                                NORMAL_OPERATION, freeStatusChanged,
Packit Service 310c69
                                &provisionalDecrement);
Packit Service 310c69
  if ((result != VDO_SUCCESS) || provisionalDecrement) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (block->isDirty && (block->slabJournalLock > 0)) {
Packit Service 310c69
    /*
Packit Service 310c69
     * This block is already dirty and a slab journal entry has been made
Packit Service 310c69
     * for it since the last time it was clean. We must release the per-entry
Packit Service 310c69
     * slab journal lock for the entry associated with the update we are now
Packit Service 310c69
     * doing.
Packit Service 310c69
     */
Packit Service 310c69
    result = ASSERT(isValidJournalPoint(slabJournalPoint),
Packit Service 310c69
                    "Reference count adjustments need slab journal points.");
Packit Service 310c69
    if (result != VDO_SUCCESS) {
Packit Service 310c69
      return result;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    SequenceNumber entryLock = slabJournalPoint->sequenceNumber;
Packit Service 310c69
    adjustSlabJournalBlockReference(refCounts->slab->journal, entryLock, -1);
Packit Service 310c69
    return VDO_SUCCESS;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  /*
Packit Service 310c69
   * This may be the first time we are applying an update for which there
Packit Service 310c69
   * is a slab journal entry to this block since the block was
Packit Service 310c69
   * cleaned. Therefore, we convert the per-entry slab journal lock to an
Packit Service 310c69
   * uncommitted reference block lock, if there is a per-entry lock.
Packit Service 310c69
   */
Packit Service 310c69
  if (isValidJournalPoint(slabJournalPoint)) {
Packit Service 310c69
    block->slabJournalLock = slabJournalPoint->sequenceNumber;
Packit Service 310c69
  } else {
Packit Service 310c69
    block->slabJournalLock = 0;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  dirtyBlock(block);
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int adjustReferenceCountForRebuild(RefCounts           *refCounts,
Packit Service 310c69
                                   PhysicalBlockNumber  pbn,
Packit Service 310c69
                                   JournalOperation     operation)
Packit Service 310c69
{
Packit Service 310c69
  SlabBlockNumber slabBlockNumber;
Packit Service 310c69
  int result = slabBlockNumberFromPBN(refCounts->slab, pbn, &slabBlockNumber);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  ReferenceBlock *block = getReferenceBlock(refCounts, slabBlockNumber);
Packit Service 310c69
  bool unusedFreeStatus;
Packit Service 310c69
  ReferenceOperation physicalOperation = {
Packit Service 310c69
    .type = operation,
Packit Service 310c69
  };
Packit Service 310c69
  result = updateReferenceCount(refCounts, block, slabBlockNumber, NULL,
Packit Service 310c69
                                physicalOperation, !NORMAL_OPERATION,
Packit Service 310c69
                                &unusedFreeStatus, NULL);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  dirtyBlock(block);
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int replayReferenceCountChange(RefCounts          *refCounts,
Packit Service 310c69
                               const JournalPoint *entryPoint,
Packit Service 310c69
                               SlabJournalEntry    entry)
Packit Service 310c69
{
Packit Service 310c69
  ReferenceBlock *block = getReferenceBlock(refCounts, entry.sbn);
Packit Service 310c69
  SectorCount sector
Packit Service 310c69
    = (entry.sbn % COUNTS_PER_BLOCK) / COUNTS_PER_SECTOR;
Packit Service 310c69
  if (!beforeJournalPoint(&block->commitPoints[sector], entryPoint)) {
Packit Service 310c69
    // This entry is already reflected in the existing counts, so do nothing.
Packit Service 310c69
    return VDO_SUCCESS;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // This entry is not yet counted in the reference counts.
Packit Service 310c69
  bool unusedFreeStatus;
Packit Service 310c69
  ReferenceOperation operation = {
Packit Service 310c69
    .type = entry.operation
Packit Service 310c69
  };
Packit Service 310c69
  int result = updateReferenceCount(refCounts, block, entry.sbn,
Packit Service 310c69
                                    entryPoint, operation, !NORMAL_OPERATION,
Packit Service 310c69
                                    &unusedFreeStatus, NULL);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  dirtyBlock(block);
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int getReferenceStatus(RefCounts           *refCounts,
Packit Service 310c69
                       PhysicalBlockNumber  pbn,
Packit Service 310c69
                       ReferenceStatus     *statusPtr)
Packit Service 310c69
{
Packit Service 310c69
  ReferenceCount *counterPtr = NULL;
Packit Service 310c69
  int result = getReferenceCounter(refCounts, pbn, &counterPtr);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *statusPtr = referenceCountToStatus(*counterPtr);
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool areEquivalentReferenceCounters(RefCounts *counterA, RefCounts *counterB)
Packit Service 310c69
{
Packit Service 310c69
  if ((counterA->blockCount             != counterB->blockCount)
Packit Service 310c69
      || (counterA->freeBlocks          != counterB->freeBlocks)
Packit Service 310c69
      || (counterA->referenceBlockCount != counterB->referenceBlockCount)) {
Packit Service 310c69
    return false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  for (size_t i = 0; i < counterA->referenceBlockCount; i++) {
Packit Service 310c69
    ReferenceBlock *blockA = &counterA->blocks[i];
Packit Service 310c69
    ReferenceBlock *blockB = &counterB->blocks[i];
Packit Service 310c69
    if (blockA->allocatedCount != blockB->allocatedCount) {
Packit Service 310c69
      return false;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return (memcmp(counterA->counters, counterB->counters,
Packit Service 310c69
                 sizeof(ReferenceCount) * counterA->blockCount) == 0);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Find the array index of the first zero byte in word-sized range of
Packit Service 310c69
 * reference counters. The search does no bounds checking; the function relies
Packit Service 310c69
 * on the array being sufficiently padded.
Packit Service 310c69
 *
Packit Service 310c69
 * @param wordPtr     A pointer to the eight counter bytes to check
Packit Service 310c69
 * @param startIndex  The array index corresponding to wordPtr[0]
Packit Service 310c69
 * @param failIndex   The array index to return if no zero byte is found
Packit Service 310c69
Packit Service 310c69
 * @return the array index of the first zero byte in the word, or
Packit Service 310c69
 *         the value passed as failIndex if no zero byte was found
Packit Service 310c69
 **/
Packit Service 310c69
static inline SlabBlockNumber findZeroByteInWord(const byte      *wordPtr,
Packit Service 310c69
                                                 SlabBlockNumber  startIndex,
Packit Service 310c69
                                                 SlabBlockNumber  failIndex)
Packit Service 310c69
{
Packit Service 310c69
  uint64_t word = getUInt64LE(wordPtr);
Packit Service 310c69
Packit Service 310c69
  // This looks like a loop, but GCC will unroll the eight iterations for us.
Packit Service 310c69
  for (unsigned int offset = 0; offset < BYTES_PER_WORD; offset++) {
Packit Service 310c69
    // Assumes little-endian byte order, which we have on X86.
Packit Service 310c69
    if ((word & 0xFF) == 0) {
Packit Service 310c69
      return (startIndex + offset);
Packit Service 310c69
    }
Packit Service 310c69
    word >>= 8;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return failIndex;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool findFreeBlock(const RefCounts *refCounts,
Packit Service 310c69
                   SlabBlockNumber  startIndex,
Packit Service 310c69
                   SlabBlockNumber  endIndex,
Packit Service 310c69
                   SlabBlockNumber *indexPtr)
Packit Service 310c69
{
Packit Service 310c69
  SlabBlockNumber  zeroIndex;
Packit Service 310c69
  SlabBlockNumber  nextIndex   = startIndex;
Packit Service 310c69
  byte            *nextCounter = &refCounts->counters[nextIndex];
Packit Service 310c69
  byte            *endCounter  = &refCounts->counters[endIndex];
Packit Service 310c69
Packit Service 310c69
  // Search every byte of the first unaligned word. (Array is padded so
Packit Service 310c69
  // reading past end is safe.)
Packit Service 310c69
  zeroIndex = findZeroByteInWord(nextCounter, nextIndex, endIndex);
Packit Service 310c69
  if (zeroIndex < endIndex) {
Packit Service 310c69
    *indexPtr = zeroIndex;
Packit Service 310c69
    return true;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // On architectures where unaligned word access is expensive, this
Packit Service 310c69
  // would be a good place to advance to an alignment boundary.
Packit Service 310c69
  nextIndex   += BYTES_PER_WORD;
Packit Service 310c69
  nextCounter += BYTES_PER_WORD;
Packit Service 310c69
Packit Service 310c69
  // Now we're word-aligned; check an word at a time until we find a word
Packit Service 310c69
  // containing a zero. (Array is padded so reading past end is safe.)
Packit Service 310c69
  while (nextCounter < endCounter) {
Packit Service 310c69
    /*
Packit Service 310c69
     * The following code is currently an exact copy of the code preceding the
Packit Service 310c69
     * loop, but if you try to merge them by using a do loop, it runs slower
Packit Service 310c69
     * because a jump instruction gets added at the start of the iteration.
Packit Service 310c69
     */
Packit Service 310c69
    zeroIndex = findZeroByteInWord(nextCounter, nextIndex, endIndex);
Packit Service 310c69
    if (zeroIndex < endIndex) {
Packit Service 310c69
      *indexPtr = zeroIndex;
Packit Service 310c69
      return true;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    nextIndex   += BYTES_PER_WORD;
Packit Service 310c69
    nextCounter += BYTES_PER_WORD;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Search the reference block currently saved in the search cursor for a
Packit Service 310c69
 * reference count of zero, starting at the saved counter index.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]  refCounts     The RefCounts object to search
Packit Service 310c69
 * @param [out] freeIndexPtr  A pointer to receive the array index of the
Packit Service 310c69
 *                            zero reference count
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if an unreferenced counter was found
Packit Service 310c69
 **/
Packit Service 310c69
static bool searchCurrentReferenceBlock(const RefCounts *refCounts,
Packit Service 310c69
                                        SlabBlockNumber *freeIndexPtr)
Packit Service 310c69
{
Packit Service 310c69
  // Don't bother searching if the current block is known to be full.
Packit Service 310c69
  return ((refCounts->searchCursor.block->allocatedCount < COUNTS_PER_BLOCK)
Packit Service 310c69
          && findFreeBlock(refCounts, refCounts->searchCursor.index,
Packit Service 310c69
                           refCounts->searchCursor.endIndex, freeIndexPtr));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Search each reference block for a reference count of zero, starting at the
Packit Service 310c69
 * reference block and counter index saved in the search cursor and searching
Packit Service 310c69
 * up to the end of the last reference block. The search does not wrap.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]  refCounts     The RefCounts object to search
Packit Service 310c69
 * @param [out] freeIndexPtr  A pointer to receive the array index of the
Packit Service 310c69
 *                            zero reference count
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if an unreferenced counter was found
Packit Service 310c69
 **/
Packit Service 310c69
static bool searchReferenceBlocks(RefCounts       *refCounts,
Packit Service 310c69
                                  SlabBlockNumber *freeIndexPtr)
Packit Service 310c69
{
Packit Service 310c69
  // Start searching at the saved search position in the current block.
Packit Service 310c69
  if (searchCurrentReferenceBlock(refCounts, freeIndexPtr)) {
Packit Service 310c69
    return true;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Search each reference block up to the end of the slab.
Packit Service 310c69
  while (advanceSearchCursor(refCounts)) {
Packit Service 310c69
    if (searchCurrentReferenceBlock(refCounts, freeIndexPtr)) {
Packit Service 310c69
      return true;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Do the bookkeeping for making a provisional reference.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts        The RefCounts
Packit Service 310c69
 * @param slabBlockNumber  The block to reference
Packit Service 310c69
 **/
Packit Service 310c69
static void makeProvisionalReference(RefCounts       *refCounts,
Packit Service 310c69
                                     SlabBlockNumber  slabBlockNumber)
Packit Service 310c69
{
Packit Service 310c69
  // Make the initial transition from an unreferenced block to a provisionally
Packit Service 310c69
  // allocated block.
Packit Service 310c69
  refCounts->counters[slabBlockNumber] = PROVISIONAL_REFERENCE_COUNT;
Packit Service 310c69
Packit Service 310c69
  // Account for the allocation.
Packit Service 310c69
  ReferenceBlock *block = getReferenceBlock(refCounts, slabBlockNumber);
Packit Service 310c69
  block->allocatedCount++;
Packit Service 310c69
  refCounts->freeBlocks--;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int allocateUnreferencedBlock(RefCounts           *refCounts,
Packit Service 310c69
                              PhysicalBlockNumber *allocatedPtr)
Packit Service 310c69
{
Packit Service 310c69
  if (!isSlabOpen(refCounts->slab)) {
Packit Service 310c69
    return VDO_INVALID_ADMIN_STATE;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  SlabBlockNumber freeIndex;
Packit Service 310c69
  if (!searchReferenceBlocks(refCounts, &freeIndex)) {
Packit Service 310c69
    return VDO_NO_SPACE;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  ASSERT_LOG_ONLY((refCounts->counters[freeIndex] == EMPTY_REFERENCE_COUNT),
Packit Service 310c69
                  "free block must have refCount of zero");
Packit Service 310c69
  makeProvisionalReference(refCounts, freeIndex);
Packit Service 310c69
Packit Service 310c69
  // Update the search hint so the next search will start at the array
Packit Service 310c69
  // index just past the free block we just found.
Packit Service 310c69
  refCounts->searchCursor.index = (freeIndex + 1);
Packit Service 310c69
Packit Service 310c69
  *allocatedPtr = indexToPBN(refCounts, freeIndex);
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int provisionallyReferenceBlock(RefCounts           *refCounts,
Packit Service 310c69
                                PhysicalBlockNumber  pbn,
Packit Service 310c69
                                PBNLock             *lock)
Packit Service 310c69
{
Packit Service 310c69
  if (!isSlabOpen(refCounts->slab)) {
Packit Service 310c69
    return VDO_INVALID_ADMIN_STATE;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  SlabBlockNumber slabBlockNumber;
Packit Service 310c69
  int result = slabBlockNumberFromPBN(refCounts->slab, pbn, &slabBlockNumber);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (refCounts->counters[slabBlockNumber] == EMPTY_REFERENCE_COUNT) {
Packit Service 310c69
    makeProvisionalReference(refCounts, slabBlockNumber);
Packit Service 310c69
    if (lock != NULL) {
Packit Service 310c69
      assignProvisionalReference(lock);
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
BlockCount countUnreferencedBlocks(RefCounts           *refCounts,
Packit Service 310c69
                                   PhysicalBlockNumber  startPBN,
Packit Service 310c69
                                   PhysicalBlockNumber  endPBN)
Packit Service 310c69
{
Packit Service 310c69
  BlockCount freeBlocks = 0;
Packit Service 310c69
  SlabBlockNumber   startIndex = pbnToIndex(refCounts, startPBN);
Packit Service 310c69
  SlabBlockNumber   endIndex   = pbnToIndex(refCounts, endPBN);
Packit Service 310c69
  for (SlabBlockNumber index = startIndex; index < endIndex; index++) {
Packit Service 310c69
    if (refCounts->counters[index] == EMPTY_REFERENCE_COUNT) {
Packit Service 310c69
      freeBlocks++;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return freeBlocks;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Convert a ReferenceBlock's generic wait queue entry back into the
Packit Service 310c69
 * ReferenceBlock.
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter        The wait queue entry to convert
Packit Service 310c69
 *
Packit Service 310c69
 * @return  The wrapping ReferenceBlock
Packit Service 310c69
 **/
Packit Service 310c69
static inline ReferenceBlock *waiterAsReferenceBlock(Waiter *waiter)
Packit Service 310c69
{
Packit Service 310c69
  STATIC_ASSERT(offsetof(ReferenceBlock, waiter) == 0);
Packit Service 310c69
  return (ReferenceBlock *) waiter;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * WaitCallback to clean dirty reference blocks when resetting.
Packit Service 310c69
 *
Packit Service 310c69
 * @param blockWaiter  The dirty block
Packit Service 310c69
 * @param context      Unused
Packit Service 310c69
 **/
Packit Service 310c69
static void
Packit Service 310c69
clearDirtyReferenceBlocks(Waiter *blockWaiter,
Packit Service 310c69
                          void   *context __attribute__((unused)))
Packit Service 310c69
{
Packit Service 310c69
  waiterAsReferenceBlock(blockWaiter)->isDirty = false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void resetReferenceCounts(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  // We can just use memset() since each ReferenceCount is exactly one byte.
Packit Service 310c69
  STATIC_ASSERT(sizeof(ReferenceCount) == 1);
Packit Service 310c69
  memset(refCounts->counters, 0, refCounts->blockCount);
Packit Service 310c69
  refCounts->freeBlocks       = refCounts->blockCount;
Packit Service 310c69
  refCounts->slabJournalPoint = (JournalPoint) {
Packit Service 310c69
    .sequenceNumber = 0,
Packit Service 310c69
    .entryCount     = 0,
Packit Service 310c69
  };
Packit Service 310c69
Packit Service 310c69
  for (size_t i = 0; i < refCounts->referenceBlockCount; i++) {
Packit Service 310c69
    refCounts->blocks[i].allocatedCount = 0;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  notifyAllWaiters(&refCounts->dirtyBlocks, clearDirtyReferenceBlocks, NULL);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
BlockCount getSavedReferenceCountSize(BlockCount blockCount)
Packit Service 310c69
{
Packit Service 310c69
  return computeBucketCount(blockCount, COUNTS_PER_BLOCK);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * A waiter callback that resets the writing state of refCounts.
Packit Service 310c69
 **/
Packit Service 310c69
static void finishSummaryUpdate(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  RefCounts *refCounts           = refCountsFromWaiter(waiter);
Packit Service 310c69
  refCounts->updatingSlabSummary = false;
Packit Service 310c69
Packit Service 310c69
  int result = *((int *) context);
Packit Service 310c69
  if ((result == VDO_SUCCESS) || (result == VDO_READ_ONLY)) {
Packit Service 310c69
    checkIfSlabDrained(refCounts->slab);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  logErrorWithStringError(result, "failed to update slab summary");
Packit Service 310c69
  enterRefCountsReadOnlyMode(refCounts, result);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Update slab summary that the RefCounts is clean.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts    The RefCounts object that is being written
Packit Service 310c69
 **/
Packit Service 310c69
static void updateSlabSummaryAsClean(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  SlabSummaryZone *summary = getSlabSummaryZone(refCounts->slab->allocator);
Packit Service 310c69
  if (summary == NULL) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Update the slab summary to indicate this refCounts is clean.
Packit Service 310c69
  TailBlockOffset offset
Packit Service 310c69
    = getSummarizedTailBlockOffset(summary, refCounts->slab->slabNumber);
Packit Service 310c69
  refCounts->updatingSlabSummary        = true;
Packit Service 310c69
  refCounts->slabSummaryWaiter.callback = finishSummaryUpdate;
Packit Service 310c69
  updateSlabSummaryEntry(summary, &refCounts->slabSummaryWaiter,
Packit Service 310c69
                         refCounts->slab->slabNumber, offset, true, true,
Packit Service 310c69
                         getSlabFreeBlockCount(refCounts->slab));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Handle an I/O error reading or writing a reference count block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO doing the I/O as a completion
Packit Service 310c69
 **/
Packit Service 310c69
static void handleIOError(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  int           result    = completion->result;
Packit Service 310c69
  VIOPoolEntry *entry     = completion->parent;
Packit Service 310c69
  RefCounts    *refCounts = ((ReferenceBlock *) entry->parent)->refCounts;
Packit Service 310c69
  returnVIO(refCounts->slab->allocator, entry);
Packit Service 310c69
  refCounts->activeCount--;
Packit Service 310c69
  enterRefCountsReadOnlyMode(refCounts, result);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * After a reference block has written, clean it, release its locks, and return
Packit Service 310c69
 * its VIO to the pool.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO that just finished writing
Packit Service 310c69
 **/
Packit Service 310c69
static void finishReferenceBlockWrite(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry   *entry     = completion->parent;
Packit Service 310c69
  ReferenceBlock *block     = entry->parent;
Packit Service 310c69
  RefCounts      *refCounts = block->refCounts;
Packit Service 310c69
  refCounts->activeCount--;
Packit Service 310c69
Packit Service 310c69
  // Release the slab journal lock.
Packit Service 310c69
  adjustSlabJournalBlockReference(refCounts->slab->journal,
Packit Service 310c69
                                  block->slabJournalLockToRelease, -1);
Packit Service 310c69
  returnVIO(refCounts->slab->allocator, entry);
Packit Service 310c69
Packit Service 310c69
  /*
Packit Service 310c69
   * We can't clear the isWriting flag earlier as releasing the slab journal
Packit Service 310c69
   * lock may cause us to be dirtied again, but we don't want to double
Packit Service 310c69
   * enqueue.
Packit Service 310c69
   */
Packit Service 310c69
  block->isWriting = false;
Packit Service 310c69
Packit Service 310c69
  if (isReadOnly(refCounts->readOnlyNotifier)) {
Packit Service 310c69
    checkIfSlabDrained(refCounts->slab);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Re-queue the block if it was re-dirtied while it was writing.
Packit Service 310c69
  if (block->isDirty) {
Packit Service 310c69
    enqueueDirtyBlock(block);
Packit Service 310c69
    if (isSlabDraining(refCounts->slab)) {
Packit Service 310c69
      // We must be saving, and this block will otherwise not be relaunched.
Packit Service 310c69
      saveDirtyReferenceBlocks(refCounts);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Mark the RefCounts as clean in the slab summary if there are no dirty
Packit Service 310c69
  // or writing blocks and no summary update in progress.
Packit Service 310c69
  if (!hasActiveIO(refCounts) && !hasWaiters(&refCounts->dirtyBlocks)) {
Packit Service 310c69
    updateSlabSummaryAsClean(refCounts);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
ReferenceCount *getReferenceCountersForBlock(ReferenceBlock *block)
Packit Service 310c69
{
Packit Service 310c69
  size_t blockIndex = block - block->refCounts->blocks;
Packit Service 310c69
  return &block->refCounts->counters[blockIndex * COUNTS_PER_BLOCK];
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void packReferenceBlock(ReferenceBlock *block, void *buffer)
Packit Service 310c69
{
Packit Service 310c69
  PackedJournalPoint commitPoint;
Packit Service 310c69
  packJournalPoint(&block->refCounts->slabJournalPoint, &commitPoint);
Packit Service 310c69
Packit Service 310c69
  PackedReferenceBlock *packed   = buffer;
Packit Service 310c69
  ReferenceCount       *counters = getReferenceCountersForBlock(block);
Packit Service 310c69
  for (SectorCount i = 0; i < SECTORS_PER_BLOCK; i++) {
Packit Service 310c69
    packed->sectors[i].commitPoint = commitPoint;
Packit Service 310c69
    memcpy(packed->sectors[i].counts, counters + (i * COUNTS_PER_SECTOR),
Packit Service 310c69
           (sizeof(ReferenceCount) * COUNTS_PER_SECTOR));
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * After a dirty block waiter has gotten a VIO from the VIO pool, copy its
Packit Service 310c69
 * counters and associated data into the VIO, and launch the write.
Packit Service 310c69
 *
Packit Service 310c69
 * @param blockWaiter  The waiter of the dirty block
Packit Service 310c69
 * @param vioContext   The VIO returned by the pool
Packit Service 310c69
 **/
Packit Service 310c69
static void writeReferenceBlock(Waiter *blockWaiter, void *vioContext)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry   *entry = vioContext;
Packit Service 310c69
  ReferenceBlock *block = waiterAsReferenceBlock(blockWaiter);
Packit Service 310c69
  packReferenceBlock(block, entry->buffer);
Packit Service 310c69
Packit Service 310c69
  size_t              blockOffset = (block - block->refCounts->blocks);
Packit Service 310c69
  PhysicalBlockNumber pbn         = (block->refCounts->origin + blockOffset);
Packit Service 310c69
  block->slabJournalLockToRelease = block->slabJournalLock;
Packit Service 310c69
  entry->parent                   = block;
Packit Service 310c69
Packit Service 310c69
  /*
Packit Service 310c69
   * Mark the block as clean, since we won't be committing any updates that
Packit Service 310c69
   * happen after this moment. As long as VIO order is preserved, two
Packit Service 310c69
   * VIOs updating this block at once will not cause complications.
Packit Service 310c69
   */
Packit Service 310c69
  block->isDirty = false;
Packit Service 310c69
Packit Service 310c69
  // Flush before writing to ensure that the recovery journal and slab journal
Packit Service 310c69
  // entries which cover this reference update are stable (VDO-2331).
Packit Service 310c69
  relaxedAdd64(&block->refCounts->statistics->blocksWritten, 1);
Packit Service 310c69
  entry->vio->completion.callbackThreadID
Packit Service 310c69
    = block->refCounts->slab->allocator->threadID;
Packit Service 310c69
  launchWriteMetadataVIOWithFlush(entry->vio, pbn, finishReferenceBlockWrite,
Packit Service 310c69
                                  handleIOError, true, false);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Launch the write of a dirty reference block by first acquiring a VIO for it
Packit Service 310c69
 * from the pool. This can be asynchronous since the writer will have to wait
Packit Service 310c69
 * if all VIOs in the pool are currently in use.
Packit Service 310c69
 *
Packit Service 310c69
 * @param blockWaiter  The waiter of the block which is starting to write
Packit Service 310c69
 * @param context      The parent refCounts of the block
Packit Service 310c69
 **/
Packit Service 310c69
static void launchReferenceBlockWrite(Waiter *blockWaiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  RefCounts *refCounts = context;
Packit Service 310c69
  if (isReadOnly(refCounts->readOnlyNotifier)) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  refCounts->activeCount++;
Packit Service 310c69
  ReferenceBlock *block = waiterAsReferenceBlock(blockWaiter);
Packit Service 310c69
  block->isWriting      = true;
Packit Service 310c69
  blockWaiter->callback = writeReferenceBlock;
Packit Service 310c69
  int result = acquireVIO(refCounts->slab->allocator, blockWaiter);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    // This should never happen.
Packit Service 310c69
    refCounts->activeCount--;
Packit Service 310c69
    enterRefCountsReadOnlyMode(refCounts, result);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void saveOldestReferenceBlock(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  notifyNextWaiter(&refCounts->dirtyBlocks, launchReferenceBlockWrite,
Packit Service 310c69
                   refCounts);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void saveSeveralReferenceBlocks(RefCounts *refCounts, size_t flushDivisor)
Packit Service 310c69
{
Packit Service 310c69
  BlockCount dirtyBlockCount = countWaiters(&refCounts->dirtyBlocks);
Packit Service 310c69
  if (dirtyBlockCount == 0) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  BlockCount blocksToWrite = dirtyBlockCount / flushDivisor;
Packit Service 310c69
  // Always save at least one block.
Packit Service 310c69
  if (blocksToWrite == 0) {
Packit Service 310c69
    blocksToWrite = 1;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  for (BlockCount written = 0; written < blocksToWrite; written++) {
Packit Service 310c69
    saveOldestReferenceBlock(refCounts);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void saveDirtyReferenceBlocks(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  notifyAllWaiters(&refCounts->dirtyBlocks, launchReferenceBlockWrite,
Packit Service 310c69
                   refCounts);
Packit Service 310c69
  checkIfSlabDrained(refCounts->slab);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void dirtyAllReferenceBlocks(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  for (BlockCount i = 0; i < refCounts->referenceBlockCount; i++) {
Packit Service 310c69
    dirtyBlock(&refCounts->blocks[i]);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Clear the provisional reference counts from a reference block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param block  The block to clear
Packit Service 310c69
 **/
Packit Service 310c69
static void clearProvisionalReferences(ReferenceBlock *block)
Packit Service 310c69
{
Packit Service 310c69
  ReferenceCount *counters = getReferenceCountersForBlock(block);
Packit Service 310c69
  for (BlockCount j = 0; j < COUNTS_PER_BLOCK; j++) {
Packit Service 310c69
    if (counters[j] == PROVISIONAL_REFERENCE_COUNT) {
Packit Service 310c69
      counters[j] = EMPTY_REFERENCE_COUNT;
Packit Service 310c69
      block->allocatedCount--;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Unpack reference counts blocks into the internal memory structure.
Packit Service 310c69
 *
Packit Service 310c69
 * @param packed  The written reference block to be unpacked
Packit Service 310c69
 * @param block   The internal reference block to be loaded
Packit Service 310c69
 **/
Packit Service 310c69
static void unpackReferenceBlock(PackedReferenceBlock *packed,
Packit Service 310c69
                                 ReferenceBlock       *block)
Packit Service 310c69
{
Packit Service 310c69
  RefCounts      *refCounts    = block->refCounts;
Packit Service 310c69
  ReferenceCount *counters     = getReferenceCountersForBlock(block);
Packit Service 310c69
  for (SectorCount i = 0; i < SECTORS_PER_BLOCK; i++) {
Packit Service 310c69
    PackedReferenceSector *sector = &packed->sectors[i];
Packit Service 310c69
    unpackJournalPoint(&sector->commitPoint, &block->commitPoints[i]);
Packit Service 310c69
    memcpy(counters + (i * COUNTS_PER_SECTOR), sector->counts,
Packit Service 310c69
           (sizeof(ReferenceCount) * COUNTS_PER_SECTOR));
Packit Service 310c69
    // The slabJournalPoint must be the latest point found in any sector.
Packit Service 310c69
    if (beforeJournalPoint(&refCounts->slabJournalPoint,
Packit Service 310c69
                           &block->commitPoints[i])) {
Packit Service 310c69
      refCounts->slabJournalPoint = block->commitPoints[i];
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    if ((i > 0) && !areEquivalentJournalPoints(&block->commitPoints[0],
Packit Service 310c69
                                               &block->commitPoints[i])) {
Packit Service 310c69
      size_t blockIndex = block - block->refCounts->blocks;
Packit Service 310c69
      logWarning("Torn write detected in sector %u of reference block"
Packit Service 310c69
                 " %zu of slab %" PRIu16,
Packit Service 310c69
                 i, blockIndex, block->refCounts->slab->slabNumber);
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  block->allocatedCount = 0;
Packit Service 310c69
  for (BlockCount i = 0; i < COUNTS_PER_BLOCK; i++) {
Packit Service 310c69
    if (counters[i] != EMPTY_REFERENCE_COUNT) {
Packit Service 310c69
      block->allocatedCount++;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * After a reference block has been read, unpack it.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO that just finished reading
Packit Service 310c69
 **/
Packit Service 310c69
static void finishReferenceBlockLoad(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry   *entry = completion->parent;
Packit Service 310c69
  ReferenceBlock *block = entry->parent;
Packit Service 310c69
  unpackReferenceBlock((PackedReferenceBlock *) entry->buffer, block);
Packit Service 310c69
Packit Service 310c69
  RefCounts *refCounts = block->refCounts;
Packit Service 310c69
  returnVIO(refCounts->slab->allocator, entry);
Packit Service 310c69
  refCounts->activeCount--;
Packit Service 310c69
  clearProvisionalReferences(block);
Packit Service 310c69
Packit Service 310c69
  refCounts->freeBlocks -= block->allocatedCount;
Packit Service 310c69
  checkIfSlabDrained(block->refCounts->slab);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * After a block waiter has gotten a VIO from the VIO pool, load the block.
Packit Service 310c69
 *
Packit Service 310c69
 * @param blockWaiter  The waiter of the block to load
Packit Service 310c69
 * @param vioContext   The VIO returned by the pool
Packit Service 310c69
 **/
Packit Service 310c69
static void loadReferenceBlock(Waiter *blockWaiter, void *vioContext)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry        *entry       = vioContext;
Packit Service 310c69
  ReferenceBlock      *block       = waiterAsReferenceBlock(blockWaiter);
Packit Service 310c69
  size_t               blockOffset = (block - block->refCounts->blocks);
Packit Service 310c69
  PhysicalBlockNumber  pbn         = (block->refCounts->origin + blockOffset);
Packit Service 310c69
  entry->parent                    = block;
Packit Service 310c69
Packit Service 310c69
  entry->vio->completion.callbackThreadID
Packit Service 310c69
    = block->refCounts->slab->allocator->threadID;
Packit Service 310c69
  launchReadMetadataVIO(entry->vio, pbn, finishReferenceBlockLoad,
Packit Service 310c69
                        handleIOError);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Load reference blocks from the underlying storage into a pre-allocated
Packit Service 310c69
 * reference counter.
Packit Service 310c69
 *
Packit Service 310c69
 * @param refCounts  The reference counter to be loaded
Packit Service 310c69
 **/
Packit Service 310c69
static void loadReferenceBlocks(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  refCounts->freeBlocks  = refCounts->blockCount;
Packit Service 310c69
  refCounts->activeCount = refCounts->referenceBlockCount;
Packit Service 310c69
  for (BlockCount i = 0; i < refCounts->referenceBlockCount; i++) {
Packit Service 310c69
    Waiter *blockWaiter = &refCounts->blocks[i].waiter;
Packit Service 310c69
    blockWaiter->callback = loadReferenceBlock;
Packit Service 310c69
    int result = acquireVIO(refCounts->slab->allocator, blockWaiter);
Packit Service 310c69
    if (result != VDO_SUCCESS) {
Packit Service 310c69
      // This should never happen.
Packit Service 310c69
      refCounts->activeCount -= (refCounts->referenceBlockCount - i);
Packit Service 310c69
      enterRefCountsReadOnlyMode(refCounts, result);
Packit Service 310c69
      return;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void drainRefCounts(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  Slab *slab = refCounts->slab;
Packit Service 310c69
  bool  save = false;
Packit Service 310c69
  switch (slab->state.state) {
Packit Service 310c69
  case ADMIN_STATE_SCRUBBING:
Packit Service 310c69
    if (mustLoadRefCounts(slab->allocator->summary, slab->slabNumber)) {
Packit Service 310c69
      loadReferenceBlocks(refCounts);
Packit Service 310c69
      return;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case ADMIN_STATE_SAVE_FOR_SCRUBBING:
Packit Service 310c69
    if (!mustLoadRefCounts(slab->allocator->summary, slab->slabNumber)) {
Packit Service 310c69
      // These reference counts were never written, so mark them all dirty.
Packit Service 310c69
      dirtyAllReferenceBlocks(refCounts);
Packit Service 310c69
    }
Packit Service 310c69
    save = true;
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case ADMIN_STATE_REBUILDING:
Packit Service 310c69
    if (shouldSaveFullyBuiltSlab(slab)) {
Packit Service 310c69
      dirtyAllReferenceBlocks(refCounts);
Packit Service 310c69
      save = true;
Packit Service 310c69
    }
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case ADMIN_STATE_SAVING:
Packit Service 310c69
    save = !isUnrecoveredSlab(slab);
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  case ADMIN_STATE_RECOVERING:
Packit Service 310c69
  case ADMIN_STATE_SUSPENDING:
Packit Service 310c69
    break;
Packit Service 310c69
Packit Service 310c69
  default:
Packit Service 310c69
    notifyRefCountsAreDrained(slab, VDO_SUCCESS);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (save) {
Packit Service 310c69
    saveDirtyReferenceBlocks(refCounts);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void acquireDirtyBlockLocks(RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  dirtyAllReferenceBlocks(refCounts);
Packit Service 310c69
  for (BlockCount i = 0; i < refCounts->referenceBlockCount; i++) {
Packit Service 310c69
    refCounts->blocks[i].slabJournalLock = 1;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  adjustSlabJournalBlockReference(refCounts->slab->journal, 1,
Packit Service 310c69
                                  refCounts->referenceBlockCount);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void dumpRefCounts(const RefCounts *refCounts)
Packit Service 310c69
{
Packit Service 310c69
  // Terse because there are a lot of slabs to dump and syslog is lossy.
Packit Service 310c69
  logInfo("  refCounts: free=%" PRIu32 "/%" PRIu32 " blocks=%" PRIu32
Packit Service 310c69
          " dirty=%zu active=%zu journal@(%llu,%" PRIu16 ")%s",
Packit Service 310c69
          refCounts->freeBlocks, refCounts->blockCount,
Packit Service 310c69
          refCounts->referenceBlockCount,
Packit Service 310c69
          countWaiters(&refCounts->dirtyBlocks),
Packit Service 310c69
          refCounts->activeCount,
Packit Service 310c69
          refCounts->slabJournalPoint.sequenceNumber,
Packit Service 310c69
          refCounts->slabJournalPoint.entryCount,
Packit Service 310c69
          (refCounts->updatingSlabSummary ? " updating" : ""));
Packit Service 310c69
}