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