Blame source/vdo/base/slabSummary.c

Packit Service d40955
/*
Packit Service d40955
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service d40955
 *
Packit Service d40955
 * This program is free software; you can redistribute it and/or
Packit Service d40955
 * modify it under the terms of the GNU General Public License
Packit Service d40955
 * as published by the Free Software Foundation; either version 2
Packit Service d40955
 * of the License, or (at your option) any later version.
Packit Service d40955
 * 
Packit Service d40955
 * This program is distributed in the hope that it will be useful,
Packit Service d40955
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d40955
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service d40955
 * GNU General Public License for more details.
Packit Service d40955
 * 
Packit Service d40955
 * You should have received a copy of the GNU General Public License
Packit Service d40955
 * along with this program; if not, write to the Free Software
Packit Service d40955
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service d40955
 * 02110-1301, USA. 
Packit Service d40955
 *
Packit Service d40955
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/slabSummary.c#7 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "slabSummary.h"
Packit Service d40955
Packit Service d40955
#include "memoryAlloc.h"
Packit Service d40955
Packit Service d40955
#include "adminState.h"
Packit Service d40955
#include "constants.h"
Packit Service d40955
#include "extent.h"
Packit Service d40955
#include "readOnlyNotifier.h"
Packit Service d40955
#include "slabSummaryInternals.h"
Packit Service d40955
#include "threadConfig.h"
Packit Service d40955
#include "types.h"
Packit Service d40955
Packit Service d40955
// SIZING
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static BlockCount getSlabSummaryZoneSize(BlockSize blockSize)
Packit Service d40955
{
Packit Service d40955
  SlabCount entriesPerBlock = blockSize / sizeof(SlabSummaryEntry);
Packit Service d40955
  BlockCount blocksNeeded   = MAX_SLABS / entriesPerBlock;
Packit Service d40955
  return blocksNeeded;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
BlockCount getSlabSummarySize(BlockSize blockSize)
Packit Service d40955
{
Packit Service d40955
  return getSlabSummaryZoneSize(blockSize) * MAX_PHYSICAL_ZONES;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
// FULLNESS HINT COMPUTATION
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Translate a slab's free block count into a 'fullness hint' that can be
Packit Service d40955
 * stored in a SlabSummaryEntry's 7 bits that are dedicated to its free count.
Packit Service d40955
 *
Packit Service d40955
 * Note: the number of free blocks must be strictly less than 2^23 blocks,
Packit Service d40955
 * even though theoretically slabs could contain precisely 2^23 blocks; there
Packit Service d40955
 * is an assumption that at least one block is used by metadata. This
Packit Service d40955
 * assumption is necessary; otherwise, the fullness hint might overflow.
Packit Service d40955
 * The fullness hint formula is roughly (fullness >> 16) & 0x7f, but
Packit Service d40955
 * ((1 << 23) >> 16) & 0x7f is the same as (0 >> 16) & 0x7f, namely 0, which
Packit Service d40955
 * is clearly a bad hint if it could indicate both 2^23 free blocks or 0 free
Packit Service d40955
 * blocks.
Packit Service d40955
 *
Packit Service d40955
 * @param summary     The summary which is being updated
Packit Service d40955
 * @param freeBlocks  The number of free blocks
Packit Service d40955
 *
Packit Service d40955
 * @return A fullness hint, which can be stored in 7 bits.
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static uint8_t computeFullnessHint(SlabSummary *summary, BlockCount freeBlocks)
Packit Service d40955
{
Packit Service d40955
  ASSERT_LOG_ONLY((freeBlocks < (1 << 23)),
Packit Service d40955
                  "free blocks must be less than 2^23");
Packit Service d40955
Packit Service d40955
  if (freeBlocks == 0) {
Packit Service d40955
    return 0;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  BlockCount hint = freeBlocks >> summary->hintShift;
Packit Service d40955
  return ((hint == 0) ? 1 : hint);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Translate a slab's free block hint into an approximate count, such that
Packit Service d40955
 * computeFullnessHint() is the inverse function of getApproximateFreeBlocks()
Packit Service d40955
 * (i.e. computeFullnessHint(getApproximateFreeBlocks(x)) == x).
Packit Service d40955
 *
Packit Service d40955
 * @param  summary        The summary from which the hint was obtained
Packit Service d40955
 * @param  freeBlockHint  The hint read from the summary
Packit Service d40955
 *
Packit Service d40955
 * @return An approximation to the free block count
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static BlockCount getApproximateFreeBlocks(SlabSummary *summary,
Packit Service d40955
                                           uint8_t      freeBlockHint)
Packit Service d40955
{
Packit Service d40955
  return ((BlockCount) freeBlockHint) << summary->hintShift;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
// MAKE/FREE FUNCTIONS
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void launchWrite(SlabSummaryBlock *summaryBlock);
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Initialize a SlabSummaryBlock.
Packit Service d40955
 *
Packit Service d40955
 * @param layer             The backing layer
Packit Service d40955
 * @param summaryZone       The parent SlabSummaryZone
Packit Service d40955
 * @param threadID          The ID of the thread of physical zone of this block
Packit Service d40955
 * @param entries           The entries this block manages
Packit Service d40955
 * @param index             The index of this block in its zone's summary
Packit Service d40955
 * @param slabSummaryBlock  The block to intialize
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
static int initializeSlabSummaryBlock(PhysicalLayer    *layer,
Packit Service d40955
                                      SlabSummaryZone  *summaryZone,
Packit Service d40955
                                      ThreadID          threadID,
Packit Service d40955
                                      SlabSummaryEntry *entries,
Packit Service d40955
                                      BlockCount        index,
Packit Service d40955
                                      SlabSummaryBlock *slabSummaryBlock)
Packit Service d40955
{
Packit Service d40955
  int result = ALLOCATE(VDO_BLOCK_SIZE, char, __func__,
Packit Service d40955
                        &slabSummaryBlock->outgoingEntries);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = createVIO(layer, VIO_TYPE_SLAB_SUMMARY, VIO_PRIORITY_METADATA,
Packit Service d40955
                     slabSummaryBlock, slabSummaryBlock->outgoingEntries,
Packit Service d40955
                     &slabSummaryBlock->vio);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  slabSummaryBlock->vio->completion.callbackThreadID = threadID;
Packit Service d40955
  slabSummaryBlock->zone                             = summaryZone;
Packit Service d40955
  slabSummaryBlock->entries                          = entries;
Packit Service d40955
  slabSummaryBlock->index                            = index;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Create a new, empty SlabSummaryZone object.
Packit Service d40955
 *
Packit Service d40955
 * @param summary     The summary to which the new zone will belong
Packit Service d40955
 * @param layer       The layer
Packit Service d40955
 * @param zoneNumber  The zone this is
Packit Service d40955
 * @param threadID    The ID of the thread for this zone
Packit Service d40955
 * @param entries     The buffer to hold the entries in this zone
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
static int makeSlabSummaryZone(SlabSummary      *summary,
Packit Service d40955
                               PhysicalLayer    *layer,
Packit Service d40955
                               ZoneCount         zoneNumber,
Packit Service d40955
                               ThreadID          threadID,
Packit Service d40955
                               SlabSummaryEntry *entries)
Packit Service d40955
{
Packit Service d40955
  int result = ALLOCATE_EXTENDED(SlabSummaryZone, summary->blocksPerZone,
Packit Service d40955
                                 SlabSummaryBlock, __func__,
Packit Service d40955
                                 &summary->zones[zoneNumber]);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  SlabSummaryZone *summaryZone = summary->zones[zoneNumber];
Packit Service d40955
  summaryZone->summary         = summary;
Packit Service d40955
  summaryZone->zoneNumber      = zoneNumber;
Packit Service d40955
  summaryZone->entries         = entries;
Packit Service d40955
Packit Service d40955
  if (layer->createMetadataVIO == NULL) {
Packit Service d40955
    // Blocks are only used for writing, and without a createVIO() call,
Packit Service d40955
    // we'll never be writing anything.
Packit Service d40955
    return VDO_SUCCESS;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Initialize each block.
Packit Service d40955
  for (BlockCount i = 0; i < summary->blocksPerZone; i++) {
Packit Service d40955
    result = initializeSlabSummaryBlock(layer, summaryZone, threadID, entries,
Packit Service d40955
                                        i, &summaryZone->summaryBlocks[i]);
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      return result;
Packit Service d40955
    }
Packit Service d40955
    entries += summary->entriesPerBlock;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int makeSlabSummary(PhysicalLayer       *layer,
Packit Service d40955
                    Partition           *partition,
Packit Service d40955
                    const ThreadConfig  *threadConfig,
Packit Service d40955
                    unsigned int         slabSizeShift,
Packit Service d40955
                    BlockCount           maximumFreeBlocksPerSlab,
Packit Service d40955
                    ReadOnlyNotifier    *readOnlyNotifier,
Packit Service d40955
                    SlabSummary        **slabSummaryPtr)
Packit Service d40955
{
Packit Service d40955
  BlockCount blocksPerZone   = getSlabSummaryZoneSize(VDO_BLOCK_SIZE);
Packit Service d40955
  SlabCount  entriesPerBlock = MAX_SLABS / blocksPerZone;
Packit Service d40955
  int result = ASSERT((entriesPerBlock * blocksPerZone) == MAX_SLABS,
Packit Service d40955
                      "block size must be a multiple of entry size");
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (partition == NULL) {
Packit Service d40955
    // Don't make a slab summary for the formatter since it doesn't need it.
Packit Service d40955
    return VDO_SUCCESS;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  SlabSummary *summary;
Packit Service d40955
  result = ALLOCATE_EXTENDED(SlabSummary, threadConfig->physicalZoneCount,
Packit Service d40955
                             SlabSummaryZone *, __func__, &summary);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  summary->zoneCount       = threadConfig->physicalZoneCount;
Packit Service d40955
  summary->readOnlyNotifier = readOnlyNotifier;
Packit Service d40955
  summary->hintShift       = (slabSizeShift > 6) ? (slabSizeShift - 6) : 0;
Packit Service d40955
  summary->blocksPerZone   = blocksPerZone;
Packit Service d40955
  summary->entriesPerBlock = entriesPerBlock;
Packit Service d40955
Packit Service d40955
  size_t totalEntries = MAX_SLABS * MAX_PHYSICAL_ZONES;
Packit Service d40955
  size_t entryBytes = totalEntries * sizeof(SlabSummaryEntry);
Packit Service d40955
  result = layer->allocateIOBuffer(layer, entryBytes, "summary entries",
Packit Service d40955
                                   (char **) &summary->entries);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeSlabSummary(&summary);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Initialize all the entries.
Packit Service d40955
  uint8_t hint = computeFullnessHint(summary, maximumFreeBlocksPerSlab);
Packit Service d40955
  for (size_t i = 0; i < totalEntries; i++) {
Packit Service d40955
    // This default tail block offset must be reflected in
Packit Service d40955
    // slabJournal.c::readSlabJournalTail().
Packit Service d40955
    summary->entries[i] = (SlabSummaryEntry) {
Packit Service d40955
      .tailBlockOffset = 0,
Packit Service d40955
      .fullnessHint    = hint,
Packit Service d40955
      .loadRefCounts   = false,
Packit Service d40955
      .isDirty         = false,
Packit Service d40955
    };
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  setSlabSummaryOrigin(summary, partition);
Packit Service d40955
  for (ZoneCount zone = 0; zone < summary->zoneCount; zone++) {
Packit Service d40955
    result = makeSlabSummaryZone(summary, layer, zone,
Packit Service d40955
                                 getPhysicalZoneThread(threadConfig, zone),
Packit Service d40955
                                 summary->entries + (MAX_SLABS * zone));
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      freeSlabSummary(&summary);
Packit Service d40955
      return result;
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  *slabSummaryPtr = summary;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void freeSlabSummary(SlabSummary **slabSummaryPtr)
Packit Service d40955
{
Packit Service d40955
  if (*slabSummaryPtr == NULL) {
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  SlabSummary *summary = *slabSummaryPtr;
Packit Service d40955
  for (ZoneCount zone = 0; zone < summary->zoneCount; zone++) {
Packit Service d40955
    SlabSummaryZone *summaryZone = summary->zones[zone];
Packit Service d40955
    if (summaryZone != NULL) {
Packit Service d40955
      for (BlockCount i = 0; i < summary->blocksPerZone; i++) {
Packit Service d40955
        freeVIO(&summaryZone->summaryBlocks[i].vio);
Packit Service d40955
        FREE(summaryZone->summaryBlocks[i].outgoingEntries);
Packit Service d40955
      }
Packit Service d40955
      FREE(summaryZone);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  FREE(summary->entries);
Packit Service d40955
  FREE(summary);
Packit Service d40955
  *slabSummaryPtr = NULL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
SlabSummaryZone *getSummaryForZone(SlabSummary *summary, ZoneCount zone)
Packit Service d40955
{
Packit Service d40955
  return summary->zones[zone];
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
// WRITING FUNCTIONALITY
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Check whether a summary zone has finished draining.
Packit Service d40955
 *
Packit Service d40955
 * @param summaryZone  The zone to check
Packit Service d40955
 **/
Packit Service d40955
static void checkForDrainComplete(SlabSummaryZone *summaryZone)
Packit Service d40955
{
Packit Service d40955
  if (!isDraining(&summaryZone->state) || (summaryZone->writeCount > 0)) {
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  finishOperationWithResult(&summaryZone->state,
Packit Service d40955
                            (isReadOnly(summaryZone->summary->readOnlyNotifier)
Packit Service d40955
                             ? VDO_READ_ONLY : VDO_SUCCESS));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Wake all the waiters in a given queue. If the VDO is in read-only mode they
Packit Service d40955
 * will be given a VDO_READ_ONLY error code as their context, otherwise they
Packit Service d40955
 * will be given VDO_SUCCESS.
Packit Service d40955
 *
Packit Service d40955
 * @param summaryZone  The slab summary which owns the queue
Packit Service d40955
 * @param queue        The queue to notify
Packit Service d40955
 **/
Packit Service d40955
static void notifyWaiters(SlabSummaryZone *summaryZone, WaitQueue *queue)
Packit Service d40955
{
Packit Service d40955
  int result = (isReadOnly(summaryZone->summary->readOnlyNotifier)
Packit Service d40955
                ? VDO_READ_ONLY : VDO_SUCCESS);
Packit Service d40955
  notifyAllWaiters(queue, NULL, &result);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Finish processing a block which attempted to write, whether or not the
Packit Service d40955
 * attempt succeeded.
Packit Service d40955
 *
Packit Service d40955
 * @param block  The block
Packit Service d40955
 **/
Packit Service d40955
static void finishUpdatingSlabSummaryBlock(SlabSummaryBlock *block)
Packit Service d40955
{
Packit Service d40955
  notifyWaiters(block->zone, &block->currentUpdateWaiters);
Packit Service d40955
  block->writing = false;
Packit Service d40955
  block->zone->writeCount--;
Packit Service d40955
  if (hasWaiters(&block->nextUpdateWaiters)) {
Packit Service d40955
    launchWrite(block);
Packit Service d40955
  } else {
Packit Service d40955
    checkForDrainComplete(block->zone);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * This is the callback for a successful block write.
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The write VIO
Packit Service d40955
 **/
Packit Service d40955
static void finishUpdate(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  SlabSummaryBlock *block = completion->parent;
Packit Service d40955
  atomicAdd64(&block->zone->summary->statistics.blocksWritten, 1);
Packit Service d40955
  finishUpdatingSlabSummaryBlock(block);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Handle an error writing a slab summary block.
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The write VIO
Packit Service d40955
 **/
Packit Service d40955
static void handleWriteError(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  SlabSummaryBlock *block = completion->parent;
Packit Service d40955
  enterReadOnlyMode(block->zone->summary->readOnlyNotifier,
Packit Service d40955
                    completion->result);
Packit Service d40955
  finishUpdatingSlabSummaryBlock(block);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Write a slab summary block unless it is currently out for writing.
Packit Service d40955
 *
Packit Service d40955
 * @param [in] block  The block that needs to be committed
Packit Service d40955
 **/
Packit Service d40955
static void launchWrite(SlabSummaryBlock *block)
Packit Service d40955
{
Packit Service d40955
  if (block->writing) {
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  SlabSummaryZone *zone = block->zone;
Packit Service d40955
  zone->writeCount++;
Packit Service d40955
  transferAllWaiters(&block->nextUpdateWaiters, &block->currentUpdateWaiters);
Packit Service d40955
  block->writing = true;
Packit Service d40955
Packit Service d40955
  SlabSummary *summary = zone->summary;
Packit Service d40955
  if (isReadOnly(summary->readOnlyNotifier)) {
Packit Service d40955
    finishUpdatingSlabSummaryBlock(block);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  memcpy(block->outgoingEntries, block->entries,
Packit Service d40955
         sizeof(SlabSummaryEntry) * summary->entriesPerBlock);
Packit Service d40955
Packit Service d40955
  // Flush before writing to ensure that the slab journal tail blocks and
Packit Service d40955
  // reference updates covered by this summary update are stable (VDO-2332).
Packit Service d40955
  PhysicalBlockNumber pbn = (summary->origin
Packit Service d40955
                             + (summary->blocksPerZone * zone->zoneNumber)
Packit Service d40955
                             + block->index);
Packit Service d40955
  launchWriteMetadataVIOWithFlush(block->vio, pbn, finishUpdate,
Packit Service d40955
                                  handleWriteError, true, false);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Initiate a drain.
Packit Service d40955
 *
Packit Service d40955
 * Implements AdminInitiator.
Packit Service d40955
 **/
Packit Service d40955
static void initiateDrain(AdminState *state)
Packit Service d40955
{
Packit Service d40955
  checkForDrainComplete(container_of(state, SlabSummaryZone, state));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void drainSlabSummaryZone(SlabSummaryZone *summaryZone,
Packit Service d40955
                          AdminStateCode   operation,
Packit Service d40955
                          VDOCompletion   *parent)
Packit Service d40955
{
Packit Service d40955
  startDraining(&summaryZone->state, operation, parent, initiateDrain);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void resumeSlabSummaryZone(SlabSummaryZone *summaryZone, VDOCompletion *parent)
Packit Service d40955
{
Packit Service d40955
  finishCompletion(parent, resumeIfQuiescent(&summaryZone->state));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
// READ/UPDATE FUNCTIONS
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get the summary block, and offset into it, for storing the summary for a
Packit Service d40955
 * slab.
Packit Service d40955
 *
Packit Service d40955
 * @param summaryZone    The SlabSummaryZone being queried
Packit Service d40955
 * @param slabNumber     The slab whose summary location is sought
Packit Service d40955
 *
Packit Service d40955
 * @return A pointer to the SlabSummaryEntryBlock containing this
Packit Service d40955
 *         SlabSummaryEntry
Packit Service d40955
 **/
Packit Service d40955
static SlabSummaryBlock *getSummaryBlockForSlab(SlabSummaryZone *summaryZone,
Packit Service d40955
                                                SlabCount        slabNumber)
Packit Service d40955
{
Packit Service d40955
  SlabCount entriesPerBlock = summaryZone->summary->entriesPerBlock;
Packit Service d40955
  return &summaryZone->summaryBlocks[slabNumber / entriesPerBlock];
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void updateSlabSummaryEntry(SlabSummaryZone *summaryZone,
Packit Service d40955
                            Waiter          *waiter,
Packit Service d40955
                            SlabCount        slabNumber,
Packit Service d40955
                            TailBlockOffset  tailBlockOffset,
Packit Service d40955
                            bool             loadRefCounts,
Packit Service d40955
                            bool             isClean,
Packit Service d40955
                            BlockCount       freeBlocks)
Packit Service d40955
{
Packit Service d40955
  SlabSummaryBlock *block = getSummaryBlockForSlab(summaryZone, slabNumber);
Packit Service d40955
  int               result;
Packit Service d40955
  if (isReadOnly(summaryZone->summary->readOnlyNotifier)) {
Packit Service d40955
    result = VDO_READ_ONLY;
Packit Service d40955
  } else if (isDraining(&summaryZone->state)
Packit Service d40955
             || isQuiescent(&summaryZone->state)) {
Packit Service d40955
    result = VDO_INVALID_ADMIN_STATE;
Packit Service d40955
  } else {
Packit Service d40955
    uint8_t hint = computeFullnessHint(summaryZone->summary, freeBlocks);
Packit Service d40955
    SlabSummaryEntry *entry = &summaryZone->entries[slabNumber];
Packit Service d40955
    *entry = (SlabSummaryEntry) {
Packit Service d40955
      .tailBlockOffset = tailBlockOffset,
Packit Service d40955
      .loadRefCounts   = (entry->loadRefCounts || loadRefCounts),
Packit Service d40955
      .isDirty         = !isClean,
Packit Service d40955
      .fullnessHint    = hint,
Packit Service d40955
    };
Packit Service d40955
    result = enqueueWaiter(&block->nextUpdateWaiters, waiter);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    waiter->callback(waiter, &result);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  launchWrite(block);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
TailBlockOffset getSummarizedTailBlockOffset(SlabSummaryZone *summaryZone,
Packit Service d40955
                                             SlabCount        slabNumber)
Packit Service d40955
{
Packit Service d40955
  return summaryZone->entries[slabNumber].tailBlockOffset;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
bool mustLoadRefCounts(SlabSummaryZone *summaryZone, SlabCount slabNumber)
Packit Service d40955
{
Packit Service d40955
  return summaryZone->entries[slabNumber].loadRefCounts;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
bool getSummarizedCleanliness(SlabSummaryZone *summaryZone,
Packit Service d40955
                              SlabCount        slabNumber)
Packit Service d40955
{
Packit Service d40955
  return !summaryZone->entries[slabNumber].isDirty;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
BlockCount getSummarizedFreeBlockCount(SlabSummaryZone *summaryZone,
Packit Service d40955
                                       SlabCount        slabNumber)
Packit Service d40955
{
Packit Service d40955
  SlabSummaryEntry *entry = &summaryZone->entries[slabNumber];
Packit Service d40955
  return getApproximateFreeBlocks(summaryZone->summary, entry->fullnessHint);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void getSummarizedRefCountsState(SlabSummaryZone *summaryZone,
Packit Service d40955
                                 SlabCount        slabNumber,
Packit Service d40955
                                 size_t          *freeBlockHint,
Packit Service d40955
                                 bool            *isClean)
Packit Service d40955
{
Packit Service d40955
  SlabSummaryEntry *entry = &summaryZone->entries[slabNumber];
Packit Service d40955
  *freeBlockHint          = entry->fullnessHint;
Packit Service d40955
  *isClean                = !entry->isDirty;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void getSummarizedSlabStatuses(SlabSummaryZone *summaryZone,
Packit Service d40955
                               SlabCount        slabCount,
Packit Service d40955
                               SlabStatus      *statuses)
Packit Service d40955
{
Packit Service d40955
  for (SlabCount i = 0; i < slabCount; i++) {
Packit Service d40955
    statuses[i] = (SlabStatus) {
Packit Service d40955
      .slabNumber = i,
Packit Service d40955
      .isClean    = !summaryZone->entries[i].isDirty,
Packit Service d40955
      .emptiness  = summaryZone->entries[i].fullnessHint
Packit Service d40955
    };
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
// RESIZE FUNCTIONS
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void setSlabSummaryOrigin(SlabSummary *summary, Partition *partition)
Packit Service d40955
{
Packit Service d40955
  summary->origin = getFixedLayoutPartitionOffset(partition);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
// COMBINING FUNCTIONS (LOAD)
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Clean up after saving out the combined slab summary. This callback is
Packit Service d40955
 * registered in finishLoadingSummary() and loadSlabSummary().
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The extent which was used to write the summary data
Packit Service d40955
 **/
Packit Service d40955
static void finishCombiningZones(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  SlabSummary *summary = completion->parent;
Packit Service d40955
  int          result  = completion->result;
Packit Service d40955
  VDOExtent   *extent  = asVDOExtent(completion);
Packit Service d40955
  freeExtent(&extent);
Packit Service d40955
  finishLoadingWithResult(&summary->zones[0]->state, result);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void combineZones(SlabSummary *summary)
Packit Service d40955
{
Packit Service d40955
  // Combine all the old summary data into the portion of the buffer
Packit Service d40955
  // corresponding to the first zone.
Packit Service d40955
  ZoneCount zone = 0;
Packit Service d40955
  if (summary->zonesToCombine > 1) {
Packit Service d40955
    for (SlabCount entryNumber = 0; entryNumber < MAX_SLABS; entryNumber++) {
Packit Service d40955
      if (zone != 0) {
Packit Service d40955
        memcpy(summary->entries + entryNumber,
Packit Service d40955
               summary->entries + (zone * MAX_SLABS) + entryNumber,
Packit Service d40955
               sizeof(SlabSummaryEntry));
Packit Service d40955
      }
Packit Service d40955
      zone++;
Packit Service d40955
      if (zone == summary->zonesToCombine) {
Packit Service d40955
        zone = 0;
Packit Service d40955
      }
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Copy the combined data to each zones's region of the buffer.
Packit Service d40955
  for (zone = 1; zone < MAX_PHYSICAL_ZONES; zone++) {
Packit Service d40955
    memcpy(summary->entries + (zone * MAX_SLABS), summary->entries,
Packit Service d40955
           MAX_SLABS * sizeof(SlabSummaryEntry));
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Combine the slab summary data from all the previously written zones
Packit Service d40955
 * and copy the combined summary to each partition's data region. Then write
Packit Service d40955
 * the combined summary back out to disk. This callback is registered in
Packit Service d40955
 * loadSlabSummary().
Packit Service d40955
 *
Packit Service d40955
 * @param completion  The extent which was used to read the summary data
Packit Service d40955
 **/
Packit Service d40955
static void finishLoadingSummary(VDOCompletion *completion)
Packit Service d40955
{
Packit Service d40955
  SlabSummary *summary = completion->parent;
Packit Service d40955
  VDOExtent   *extent  = asVDOExtent(completion);
Packit Service d40955
Packit Service d40955
  // Combine the zones so each zone is correct for all slabs.
Packit Service d40955
  combineZones(summary);
Packit Service d40955
Packit Service d40955
  // Write the combined summary back out.
Packit Service d40955
  extent->completion.callback = finishCombiningZones;
Packit Service d40955
  writeMetadataExtent(extent, summary->origin);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void loadSlabSummary(SlabSummary    *summary,
Packit Service d40955
                     AdminStateCode  operation,
Packit Service d40955
                     ZoneCount       zonesToCombine,
Packit Service d40955
                     VDOCompletion  *parent)
Packit Service d40955
{
Packit Service d40955
  SlabSummaryZone *zone = summary->zones[0];
Packit Service d40955
  if (!startLoading(&zone->state, operation, parent, NULL)) {
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  VDOExtent *extent;
Packit Service d40955
  BlockCount blocks = summary->blocksPerZone * MAX_PHYSICAL_ZONES;
Packit Service d40955
  int        result = createExtent(parent->layer, VIO_TYPE_SLAB_SUMMARY,
Packit Service d40955
                                   VIO_PRIORITY_METADATA, blocks,
Packit Service d40955
                                   (char *) summary->entries, &extent);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    finishLoadingWithResult(&zone->state, result);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if ((operation == ADMIN_STATE_FORMATTING)
Packit Service d40955
      || (operation == ADMIN_STATE_LOADING_FOR_REBUILD)) {
Packit Service d40955
    prepareCompletion(&extent->completion, finishCombiningZones,
Packit Service d40955
                      finishCombiningZones, 0, summary);
Packit Service d40955
    writeMetadataExtent(extent, summary->origin);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  summary->zonesToCombine = zonesToCombine;
Packit Service d40955
  prepareCompletion(&extent->completion, finishLoadingSummary,
Packit Service d40955
                    finishCombiningZones, 0, summary);
Packit Service d40955
  readMetadataExtent(extent, summary->origin);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
SlabSummaryStatistics getSlabSummaryStatistics(const SlabSummary *summary)
Packit Service d40955
{
Packit Service d40955
  const AtomicSlabSummaryStatistics *atoms = &summary->statistics;
Packit Service d40955
  return (SlabSummaryStatistics) {
Packit Service d40955
    .blocksWritten = atomicLoad64(&atoms->blocksWritten),
Packit Service d40955
  };
Packit Service d40955
}