Blame source/vdo/base/blockMapTree.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/blockMapTree.c#21 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
#include "blockMapTree.h"
Packit Service 310c69
Packit Service 310c69
#include "logger.h"
Packit Service 310c69
Packit Service 310c69
#include "blockMap.h"
Packit Service 310c69
#include "blockMapInternals.h"
Packit Service 310c69
#include "blockMapPage.h"
Packit Service 310c69
#include "blockMapTreeInternals.h"
Packit Service 310c69
#include "constants.h"
Packit Service 310c69
#include "dataVIO.h"
Packit Service 310c69
#include "dirtyLists.h"
Packit Service 310c69
#include "forest.h"
Packit Service 310c69
#include "numUtils.h"
Packit Service 310c69
#include "recoveryJournal.h"
Packit Service 310c69
#include "referenceOperation.h"
Packit Service 310c69
#include "slabDepot.h"
Packit Service 310c69
#include "slabJournal.h"
Packit Service 310c69
#include "types.h"
Packit Service 310c69
#include "vdoInternal.h"
Packit Service 310c69
#include "vdoPageCache.h"
Packit Service 310c69
#include "vioPool.h"
Packit Service 310c69
Packit Service 310c69
enum {
Packit Service 310c69
  BLOCK_MAP_VIO_POOL_SIZE = 64,
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
typedef struct __attribute__((packed)) {
Packit Service 310c69
  RootCount  rootIndex;
Packit Service 310c69
  Height     height;
Packit Service 310c69
  PageNumber pageIndex;
Packit Service 310c69
  SlotNumber slot;
Packit Service 310c69
} PageDescriptor;
Packit Service 310c69
Packit Service 310c69
typedef union {
Packit Service 310c69
  PageDescriptor descriptor;
Packit Service 310c69
  uint64_t       key;
Packit Service 310c69
} PageKey;
Packit Service 310c69
Packit Service 310c69
typedef struct {
Packit Service 310c69
  BlockMapTreeZone *zone;
Packit Service 310c69
  uint8_t           generation;
Packit Service 310c69
} WriteIfNotDirtiedContext;
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * An invalid PBN used to indicate that the page holding the location of a
Packit Service 310c69
 * tree root has been "loaded".
Packit Service 310c69
 **/
Packit Service 310c69
const PhysicalBlockNumber INVALID_PBN = 0xFFFFFFFFFFFFFFFF;
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Convert a RingNode to a TreePage.
Packit Service 310c69
 *
Packit Service 310c69
 * @param ringNode The RingNode to convert
Packit Service 310c69
 *
Packit Service 310c69
 * @return The TreePage which owns the RingNode
Packit Service 310c69
 **/
Packit Service 310c69
static inline TreePage *treePageFromRingNode(RingNode *ringNode)
Packit Service 310c69
{
Packit Service 310c69
  return (TreePage *) ((byte *) ringNode - offsetof(TreePage, node));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
static void writeDirtyPagesCallback(RingNode *expired, void *context);
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Make VIOs for reading, writing, and allocating the arboreal block map.
Packit Service 310c69
 *
Packit Service 310c69
 * Implements VIOConstructor.
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static int makeBlockMapVIOs(PhysicalLayer  *layer,
Packit Service 310c69
                            void           *parent,
Packit Service 310c69
                            void           *buffer,
Packit Service 310c69
                            VIO           **vioPtr)
Packit Service 310c69
{
Packit Service 310c69
  return createVIO(layer, VIO_TYPE_BLOCK_MAP_INTERIOR, VIO_PRIORITY_METADATA,
Packit Service 310c69
                   parent, buffer, vioPtr);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int initializeTreeZone(BlockMapZone  *zone,
Packit Service 310c69
                       PhysicalLayer *layer,
Packit Service 310c69
                       BlockCount     eraLength)
Packit Service 310c69
{
Packit Service 310c69
  STATIC_ASSERT_SIZEOF(PageDescriptor, sizeof(uint64_t));
Packit Service 310c69
  BlockMapTreeZone *treeZone = &zone->treeZone;
Packit Service 310c69
  treeZone->mapZone          = zone;
Packit Service 310c69
Packit Service 310c69
  int result = makeDirtyLists(eraLength, writeDirtyPagesCallback, treeZone,
Packit Service 310c69
                              &treeZone->dirtyLists);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = makeIntMap(LOCK_MAP_CAPACITY, 0, &treeZone->loadingPages);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return makeVIOPool(layer, BLOCK_MAP_VIO_POOL_SIZE, zone->threadID,
Packit Service 310c69
                     makeBlockMapVIOs, treeZone, &treeZone->vioPool);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int replaceTreeZoneVIOPool(BlockMapTreeZone *zone,
Packit Service 310c69
                           PhysicalLayer    *layer,
Packit Service 310c69
                           size_t            poolSize)
Packit Service 310c69
{
Packit Service 310c69
  freeVIOPool(&zone->vioPool);
Packit Service 310c69
  return makeVIOPool(layer, poolSize, zone->mapZone->threadID,
Packit Service 310c69
                     makeBlockMapVIOs, zone, &zone->vioPool);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void uninitializeBlockMapTreeZone(BlockMapTreeZone *treeZone)
Packit Service 310c69
{
Packit Service 310c69
  freeDirtyLists(&treeZone->dirtyLists);
Packit Service 310c69
  freeVIOPool(&treeZone->vioPool);
Packit Service 310c69
  freeIntMap(&treeZone->loadingPages);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void setTreeZoneInitialPeriod(BlockMapTreeZone *treeZone,
Packit Service 310c69
                              SequenceNumber    period)
Packit Service 310c69
{
Packit Service 310c69
  setCurrentPeriod(treeZone->dirtyLists, period);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Get the BlockMapTreeZone in which a DataVIO is operating.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO
Packit Service 310c69
 *
Packit Service 310c69
 * @return The BlockMapTreeZone
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static inline BlockMapTreeZone *getBlockMapTreeZone(DataVIO *dataVIO)
Packit Service 310c69
{
Packit Service 310c69
  return &(getBlockMapForZone(dataVIO->logical.zone)->treeZone);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Get the TreePage for a given lock. This will be the page referred to by the
Packit Service 310c69
 * lock's tree slot for the lock's current height.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone  The tree zone of the tree
Packit Service 310c69
 * @param lock  The lock describing the page to get
Packit Service 310c69
 *
Packit Service 310c69
 * @return The requested page
Packit Service 310c69
 **/
Packit Service 310c69
static inline TreePage *getTreePage(const BlockMapTreeZone *zone,
Packit Service 310c69
                                    const TreeLock         *lock)
Packit Service 310c69
{
Packit Service 310c69
  return getTreePageByIndex(zone->mapZone->blockMap->forest,
Packit Service 310c69
                            lock->rootIndex,
Packit Service 310c69
                            lock->height,
Packit Service 310c69
                            lock->treeSlots[lock->height].pageIndex);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool copyValidPage(char                *buffer,
Packit Service 310c69
                   Nonce                nonce,
Packit Service 310c69
                   PhysicalBlockNumber  pbn,
Packit Service 310c69
                   BlockMapPage        *page)
Packit Service 310c69
{
Packit Service 310c69
  BlockMapPage         *loaded   = (BlockMapPage *) buffer;
Packit Service 310c69
  BlockMapPageValidity  validity = validateBlockMapPage(loaded, nonce, pbn);
Packit Service 310c69
  if (validity == BLOCK_MAP_PAGE_VALID) {
Packit Service 310c69
    memcpy(page, loaded, VDO_BLOCK_SIZE);
Packit Service 310c69
    return true;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (validity == BLOCK_MAP_PAGE_BAD) {
Packit Service 310c69
    logErrorWithStringError(VDO_BAD_PAGE,
Packit Service 310c69
                            "Expected page %" PRIu64
Packit Service 310c69
                            " but got page %llu instead",
Packit Service 310c69
                            pbn, getBlockMapPagePBN(loaded));
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool isTreeZoneActive(BlockMapTreeZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  return ((zone->activeLookups != 0)
Packit Service 310c69
          || hasWaiters(&zone->flushWaiters)
Packit Service 310c69
          || isVIOPoolBusy(zone->vioPool));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Put the VDO in read-only mode and wake any VIOs waiting for a flush.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone    The zone
Packit Service 310c69
 * @param result  The error which is causing read-only mode
Packit Service 310c69
 **/
Packit Service 310c69
static void enterZoneReadOnlyMode(BlockMapTreeZone *zone, int result)
Packit Service 310c69
{
Packit Service 310c69
  enterReadOnlyMode(zone->mapZone->readOnlyNotifier, result);
Packit Service 310c69
Packit Service 310c69
  // We are in read-only mode, so we won't ever write any page out. Just take
Packit Service 310c69
  // all waiters off the queue so the tree zone can be closed.
Packit Service 310c69
  while (hasWaiters(&zone->flushWaiters)) {
Packit Service 310c69
    dequeueNextWaiter(&zone->flushWaiters);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  checkForDrainComplete(zone->mapZone);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Check whether a generation is strictly older than some other generation in
Packit Service 310c69
 * the context of a zone's current generation range.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone  The zone in which to do the comparison
Packit Service 310c69
 * @param a     The generation in question
Packit Service 310c69
 * @param b     The generation to compare to
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if generation a is not strictly older than
Packit Service 310c69
 *         generation b in the context of the zone
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static bool isNotOlder(BlockMapTreeZone *zone, uint8_t a, uint8_t b)
Packit Service 310c69
{
Packit Service 310c69
  int result = ASSERT((inCyclicRange(zone->oldestGeneration, a,
Packit Service 310c69
                                     zone->generation, 1 << 8)
Packit Service 310c69
                       && inCyclicRange(zone->oldestGeneration, b,
Packit Service 310c69
                                        zone->generation, 1 << 8)),
Packit Service 310c69
                      "generation(s) %u, %u are out of range [%u, %u]",
Packit Service 310c69
                      a, b, zone->oldestGeneration, zone->generation);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
    return true;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return inCyclicRange(b, a, zone->generation, 1 << 8);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Decrement the count for a generation and roll the oldest generation if there
Packit Service 310c69
 * are no longer any active pages in it.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone        The zone
Packit Service 310c69
 * @param generation  The generation to release
Packit Service 310c69
 **/
Packit Service 310c69
static void releaseGeneration(BlockMapTreeZone *zone, uint8_t generation)
Packit Service 310c69
{
Packit Service 310c69
  int result = ASSERT((zone->dirtyPageCounts[generation] > 0),
Packit Service 310c69
                      "dirty page count underflow for generation %u",
Packit Service 310c69
                      generation);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  zone->dirtyPageCounts[generation]--;
Packit Service 310c69
  while ((zone->dirtyPageCounts[zone->oldestGeneration] == 0)
Packit Service 310c69
         && (zone->oldestGeneration != zone->generation)) {
Packit Service 310c69
    zone->oldestGeneration++;
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the generation of a page and update the dirty page count in the zone.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone           The zone which owns the page
Packit Service 310c69
 * @param page           The page
Packit Service 310c69
 * @param newGeneration  The generation to set
Packit Service 310c69
 * @param decrementOld   Whether to decrement the count of the page's old
Packit Service 310c69
 *                       generation
Packit Service 310c69
 **/
Packit Service 310c69
static void setGeneration(BlockMapTreeZone *zone,
Packit Service 310c69
                          TreePage         *page,
Packit Service 310c69
                          uint8_t           newGeneration,
Packit Service 310c69
                          bool              decrementOld)
Packit Service 310c69
{
Packit Service 310c69
  uint8_t oldGeneration = page->generation;
Packit Service 310c69
  if (decrementOld && (oldGeneration == newGeneration)) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  page->generation = newGeneration;
Packit Service 310c69
  uint32_t newCount = ++zone->dirtyPageCounts[newGeneration];
Packit Service 310c69
  int result = ASSERT((newCount != 0),
Packit Service 310c69
                      "dirty page count overflow for generation %u",
Packit Service 310c69
                      newGeneration);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (decrementOld) {
Packit Service 310c69
    releaseGeneration(zone, oldGeneration);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
static void writePage(TreePage *treePage, VIOPoolEntry *entry);
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Write out a dirty page if it is still covered by the most recent flush
Packit Service 310c69
 * or if it is the flusher.
Packit Service 310c69
 *
Packit Service 310c69
 * 

Implements WaiterCallback

Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter   The page to write
Packit Service 310c69
 * @param context  The VIOPoolEntry with which to do the write
Packit Service 310c69
 **/
Packit Service 310c69
static void writePageCallback(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  STATIC_ASSERT(offsetof(TreePage, waiter) == 0);
Packit Service 310c69
  writePage((TreePage *) waiter, (VIOPoolEntry *) context);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Acquire a VIO for writing a dirty page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter  The page which needs a VIO
Packit Service 310c69
 * @param zone    The zone
Packit Service 310c69
 **/
Packit Service 310c69
static void acquireVIO(Waiter *waiter, BlockMapTreeZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  waiter->callback = writePageCallback;
Packit Service 310c69
  int result = acquireVIOFromPool(zone->vioPool, waiter);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Attempt to increment the generation.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone  The zone whose generation is to be incremented
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if all possible generations were not already
Packit Service 310c69
 *         active
Packit Service 310c69
 **/
Packit Service 310c69
static bool attemptIncrement(BlockMapTreeZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  uint8_t generation = zone->generation + 1;
Packit Service 310c69
  if (zone->oldestGeneration == generation) {
Packit Service 310c69
    return false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  zone->generation = generation;
Packit Service 310c69
  return true;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Enqueue a page to either launch a flush or wait for the current flush which
Packit Service 310c69
 * is already in progress.
Packit Service 310c69
 *
Packit Service 310c69
 * @param page  The page to enqueue
Packit Service 310c69
 * @param zone  The zone
Packit Service 310c69
 **/
Packit Service 310c69
static void enqueuePage(TreePage *page, BlockMapTreeZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  if ((zone->flusher == NULL) && attemptIncrement(zone)) {
Packit Service 310c69
    zone->flusher = page;
Packit Service 310c69
    acquireVIO(&page->waiter, zone);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  int result = enqueueWaiter(&zone->flushWaiters, &page->waiter);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Write pages which were waiting for a flush and have not been redirtied.
Packit Service 310c69
 * Requeue those pages which were redirtied.
Packit Service 310c69
 *
Packit Service 310c69
 * 

Implements WaiterCallback.

Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter   The dirty page
Packit Service 310c69
 * @param context  The zone and generation
Packit Service 310c69
 **/
Packit Service 310c69
static void writePageIfNotDirtied(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  STATIC_ASSERT(offsetof(TreePage, waiter) == 0);
Packit Service 310c69
  TreePage *page = (TreePage *) waiter;
Packit Service 310c69
  WriteIfNotDirtiedContext *writeContext = context;
Packit Service 310c69
  if (page->generation == writeContext->generation) {
Packit Service 310c69
    acquireVIO(waiter, writeContext->zone);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  enqueuePage(page, writeContext->zone);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Return a VIO to the zone's pool.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone   The zone which owns the pool
Packit Service 310c69
 * @param entry  The pool entry to return
Packit Service 310c69
 **/
Packit Service 310c69
static void returnToPool(BlockMapTreeZone *zone, VIOPoolEntry *entry)
Packit Service 310c69
{
Packit Service 310c69
  returnVIOToPool(zone->vioPool, entry);
Packit Service 310c69
  checkForDrainComplete(zone->mapZone);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Handle the successful write of a tree page. This callback is registered in
Packit Service 310c69
 * writeInitializedPage().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO doing the write
Packit Service 310c69
 **/
Packit Service 310c69
static void finishPageWrite(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry     *entry = completion->parent;
Packit Service 310c69
  TreePage         *page  = entry->parent;
Packit Service 310c69
  BlockMapTreeZone *zone  = entry->context;
Packit Service 310c69
  releaseRecoveryJournalBlockReference(zone->mapZone->blockMap->journal,
Packit Service 310c69
                                       page->writingRecoveryLock,
Packit Service 310c69
                                       ZONE_TYPE_LOGICAL,
Packit Service 310c69
                                       zone->mapZone->zoneNumber);
Packit Service 310c69
Packit Service 310c69
  bool dirty    = (page->writingGeneration != page->generation);
Packit Service 310c69
  releaseGeneration(zone, page->writingGeneration);
Packit Service 310c69
  page->writing = false;
Packit Service 310c69
Packit Service 310c69
  if (zone->flusher == page) {
Packit Service 310c69
    WriteIfNotDirtiedContext context = {
Packit Service 310c69
      .zone       = zone,
Packit Service 310c69
      .generation = page->writingGeneration,
Packit Service 310c69
    };
Packit Service 310c69
    notifyAllWaiters(&zone->flushWaiters, writePageIfNotDirtied, &context);
Packit Service 310c69
    if (dirty && attemptIncrement(zone)) {
Packit Service 310c69
      writePage(page, entry);
Packit Service 310c69
      return;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    zone->flusher = NULL;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (dirty) {
Packit Service 310c69
    enqueuePage(page, zone);
Packit Service 310c69
  } else if ((zone->flusher == NULL)
Packit Service 310c69
             && hasWaiters(&zone->flushWaiters)
Packit Service 310c69
             && attemptIncrement(zone)) {
Packit Service 310c69
    zone->flusher = (TreePage *) dequeueNextWaiter(&zone->flushWaiters);
Packit Service 310c69
    writePage(zone->flusher, entry);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  returnToPool(zone, entry);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Handle an error writing a tree page. This error handler is registered in
Packit Service 310c69
 * writePage() and writeInitializedPage().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO doing the write
Packit Service 310c69
 **/
Packit Service 310c69
static void handleWriteError(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  int               result = completion->result;
Packit Service 310c69
  VIOPoolEntry     *entry  = completion->parent;
Packit Service 310c69
  BlockMapTreeZone *zone   = entry->context;
Packit Service 310c69
  enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
  returnToPool(zone, entry);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Write a page which has been written at least once. This callback is
Packit Service 310c69
 * registered in (or called directly from) writePage().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO which will do the write
Packit Service 310c69
 **/
Packit Service 310c69
static void writeInitializedPage(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry     *entry    = completion->parent;
Packit Service 310c69
  BlockMapTreeZone *zone     = (BlockMapTreeZone *) entry->context;
Packit Service 310c69
  TreePage         *treePage = (TreePage *) entry->parent;
Packit Service 310c69
Packit Service 310c69
  /*
Packit Service 310c69
   * Set the initialized field of the copy of the page we are writing to true.
Packit Service 310c69
   * We don't want to set it true on the real page in memory until after this
Packit Service 310c69
   * write succeeds.
Packit Service 310c69
   */
Packit Service 310c69
  BlockMapPage *page = (BlockMapPage *) entry->buffer;
Packit Service 310c69
  markBlockMapPageInitialized(page, true);
Packit Service 310c69
  launchWriteMetadataVIOWithFlush(entry->vio, getBlockMapPagePBN(page),
Packit Service 310c69
                                  finishPageWrite, handleWriteError,
Packit Service 310c69
                                  (zone->flusher == treePage), false);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Write a dirty tree page now that we have a VIO with which to write it.
Packit Service 310c69
 *
Packit Service 310c69
 * @param treePage  The page to write
Packit Service 310c69
 * @param entry     The VIOPoolEntry with which to write
Packit Service 310c69
 **/
Packit Service 310c69
static void writePage(TreePage *treePage, VIOPoolEntry *entry)
Packit Service 310c69
{
Packit Service 310c69
  BlockMapTreeZone *zone = (BlockMapTreeZone *) entry->context;
Packit Service 310c69
  if ((zone->flusher != treePage)
Packit Service 310c69
      && (isNotOlder(zone, treePage->generation, zone->generation))) {
Packit Service 310c69
    // This page was re-dirtied after the last flush was  issued, hence we need
Packit Service 310c69
    // to do another flush.
Packit Service 310c69
    enqueuePage(treePage, zone);
Packit Service 310c69
    returnToPool(zone, entry);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  entry->parent = treePage;
Packit Service 310c69
  memcpy(entry->buffer, treePage->pageBuffer, VDO_BLOCK_SIZE);
Packit Service 310c69
Packit Service 310c69
  VDOCompletion *completion    = vioAsCompletion(entry->vio);
Packit Service 310c69
  completion->callbackThreadID = zone->mapZone->threadID;
Packit Service 310c69
Packit Service 310c69
  treePage->writing             = true;
Packit Service 310c69
  treePage->writingGeneration   = treePage->generation;
Packit Service 310c69
  treePage->writingRecoveryLock = treePage->recoveryLock;
Packit Service 310c69
Packit Service 310c69
  // Clear this now so that we know this page is not on any dirty list.
Packit Service 310c69
  treePage->recoveryLock = 0;
Packit Service 310c69
Packit Service 310c69
  BlockMapPage *page = asBlockMapPage(treePage);
Packit Service 310c69
  if (!markBlockMapPageInitialized(page, true)) {
Packit Service 310c69
    writeInitializedPage(completion);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  launchWriteMetadataVIO(entry->vio, getBlockMapPagePBN(page),
Packit Service 310c69
                         writeInitializedPage, handleWriteError);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Schedule a batch of dirty pages for writing.
Packit Service 310c69
 *
Packit Service 310c69
 * 

Implements DirtyListsCallback.

Packit Service 310c69
 *
Packit Service 310c69
 * @param expired  The pages to write
Packit Service 310c69
 * @param context  The zone
Packit Service 310c69
 **/
Packit Service 310c69
static void writeDirtyPagesCallback(RingNode *expired, void *context)
Packit Service 310c69
{
Packit Service 310c69
  BlockMapTreeZone *zone       = (BlockMapTreeZone *) context;
Packit Service 310c69
  uint8_t           generation = zone->generation;
Packit Service 310c69
  while (!isRingEmpty(expired)) {
Packit Service 310c69
    TreePage       *page       = treePageFromRingNode(chopRingNode(expired));
Packit Service 310c69
Packit Service 310c69
    int result = ASSERT(!isWaiting(&page->waiter),
Packit Service 310c69
                        "Newly expired page not already waiting to write");
Packit Service 310c69
    if (result != VDO_SUCCESS) {
Packit Service 310c69
      enterZoneReadOnlyMode(zone, result);
Packit Service 310c69
      continue;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    setGeneration(zone, page, generation, false);
Packit Service 310c69
    if (!page->writing) {
Packit Service 310c69
      enqueuePage(page, zone);
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void advanceZoneTreePeriod(BlockMapTreeZone *zone, SequenceNumber period)
Packit Service 310c69
{
Packit Service 310c69
  advancePeriod(zone->dirtyLists, period);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void drainZoneTrees(BlockMapTreeZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  ASSERT_LOG_ONLY((zone->activeLookups == 0),
Packit Service 310c69
                  "drainZoneTrees() called with no active lookups");
Packit Service 310c69
  if (!isSuspending(&zone->mapZone->state)) {
Packit Service 310c69
    flushDirtyLists(zone->dirtyLists);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Release a lock on a page which was being loaded or allocated.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO releasing the page lock
Packit Service 310c69
 * @param what     What the DataVIO was doing (for logging)
Packit Service 310c69
 **/
Packit Service 310c69
static void releasePageLock(DataVIO *dataVIO, char *what)
Packit Service 310c69
{
Packit Service 310c69
  TreeLock *lock = &dataVIO->treeLock;
Packit Service 310c69
  ASSERT_LOG_ONLY(lock->locked,
Packit Service 310c69
                  "release of unlocked block map page %s for key %" PRIu64
Packit Service 310c69
                  " in tree %u",
Packit Service 310c69
                  what, lock->key, lock->rootIndex);
Packit Service 310c69
  BlockMapTreeZone *zone       = getBlockMapTreeZone(dataVIO);
Packit Service 310c69
  TreeLock         *lockHolder = intMapRemove(zone->loadingPages, lock->key);
Packit Service 310c69
  ASSERT_LOG_ONLY((lockHolder == lock),
Packit Service 310c69
                  "block map page %s mismatch for key %llu in tree %u",
Packit Service 310c69
                  what, lock->key, lock->rootIndex);
Packit Service 310c69
  lock->locked = false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Continue a DataVIO now that the lookup is complete.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO
Packit Service 310c69
 * @param result   The result of the lookup
Packit Service 310c69
 **/
Packit Service 310c69
static void finishLookup(DataVIO *dataVIO, int result)
Packit Service 310c69
{
Packit Service 310c69
  dataVIO->treeLock.height = 0;
Packit Service 310c69
Packit Service 310c69
  BlockMapTreeZone *zone = getBlockMapTreeZone(dataVIO);
Packit Service 310c69
  --zone->activeLookups;
Packit Service 310c69
Packit Service 310c69
  VDOCompletion *completion = dataVIOAsCompletion(dataVIO);
Packit Service 310c69
  setCompletionResult(completion, result);
Packit Service 310c69
  launchCallback(completion, dataVIO->treeLock.callback,
Packit Service 310c69
                 dataVIO->treeLock.threadID);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Abort a block map PBN lookup due to an error in the load or allocation on
Packit Service 310c69
 * which we were waiting.
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter   The DataVIO which was waiting for a page load or allocation
Packit Service 310c69
 * @param context  The error which caused the abort
Packit Service 310c69
 **/
Packit Service 310c69
static void abortLookupForWaiter(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = waiterAsDataVIO(waiter);
Packit Service 310c69
  int      result  = *((int *) context);
Packit Service 310c69
  if (isReadDataVIO(dataVIO)) {
Packit Service 310c69
    if (result == VDO_NO_SPACE) {
Packit Service 310c69
      result = VDO_SUCCESS;
Packit Service 310c69
    }
Packit Service 310c69
  } else if (result != VDO_NO_SPACE) {
Packit Service 310c69
    result = VDO_READ_ONLY;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  finishLookup(dataVIO, result);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Abort a block map PBN lookup due to an error loading or allocating a page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO which was loading or allocating a page
Packit Service 310c69
 * @param result   The error code
Packit Service 310c69
 * @param what     What the DataVIO was doing (for logging)
Packit Service 310c69
 **/
Packit Service 310c69
static void abortLookup(DataVIO *dataVIO, int result, char *what)
Packit Service 310c69
{
Packit Service 310c69
  if (result != VDO_NO_SPACE) {
Packit Service 310c69
    enterZoneReadOnlyMode(getBlockMapTreeZone(dataVIO), result);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (dataVIO->treeLock.locked) {
Packit Service 310c69
    releasePageLock(dataVIO, what);
Packit Service 310c69
    notifyAllWaiters(&dataVIO->treeLock.waiters, abortLookupForWaiter,
Packit Service 310c69
                     &result);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  finishLookup(dataVIO, result);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Abort a block map PBN lookup due to an error loading a page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO doing the page load
Packit Service 310c69
 * @param result   The error code
Packit Service 310c69
 **/
Packit Service 310c69
static void abortLoad(DataVIO *dataVIO, int result)
Packit Service 310c69
{
Packit Service 310c69
  abortLookup(dataVIO, result, "load");
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Determine if a location represents a valid mapping for a tree page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param vdo      The VDO
Packit Service 310c69
 * @param mapping  The DataLocation to check
Packit Service 310c69
 * @param height   The height of the entry in the tree
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if the entry represents a invalid page mapping
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static bool isInvalidTreeEntry(const VDO          *vdo,
Packit Service 310c69
                               const DataLocation *mapping,
Packit Service 310c69
                               Height              height)
Packit Service 310c69
{
Packit Service 310c69
  if (!isValidLocation(mapping)
Packit Service 310c69
      || isCompressed(mapping->state)
Packit Service 310c69
      || (isMappedLocation(mapping) && (mapping->pbn == ZERO_BLOCK))) {
Packit Service 310c69
    return true;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Roots aren't physical data blocks, so we can't check their PBNs.
Packit Service 310c69
  if (height == BLOCK_MAP_TREE_HEIGHT) {
Packit Service 310c69
    return false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return !isPhysicalDataBlock(vdo->depot, mapping->pbn);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
static void loadBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO);
Packit Service 310c69
static void allocateBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO);
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Continue a block map PBN lookup now that a page has been loaded by
Packit Service 310c69
 * descending one level in the tree.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO doing the lookup
Packit Service 310c69
 * @param page     The page which was just loaded
Packit Service 310c69
 **/
Packit Service 310c69
static void continueWithLoadedPage(DataVIO *dataVIO, BlockMapPage *page)
Packit Service 310c69
{
Packit Service 310c69
  TreeLock         *lock = &dataVIO->treeLock;
Packit Service 310c69
  BlockMapTreeSlot  slot = lock->treeSlots[lock->height];
Packit Service 310c69
  DataLocation mapping
Packit Service 310c69
    = unpackBlockMapEntry(&page->entries[slot.blockMapSlot.slot]);
Packit Service 310c69
  if (isInvalidTreeEntry(getVDOFromDataVIO(dataVIO), &mapping, lock->height)) {
Packit Service 310c69
    logErrorWithStringError(VDO_BAD_MAPPING,
Packit Service 310c69
                            "Invalid block map tree PBN: %llu with "
Packit Service 310c69
                            "state %u for page index %u at height %u",
Packit Service 310c69
                            mapping.pbn, mapping.state,
Packit Service 310c69
                            lock->treeSlots[lock->height - 1].pageIndex,
Packit Service 310c69
                            lock->height - 1);
Packit Service 310c69
    abortLoad(dataVIO, VDO_BAD_MAPPING);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (!isMappedLocation(&mapping)) {
Packit Service 310c69
    // The page we need is unallocated
Packit Service 310c69
    allocateBlockMapPage(getBlockMapTreeZone(dataVIO), dataVIO);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  lock->treeSlots[lock->height - 1].blockMapSlot.pbn = mapping.pbn;
Packit Service 310c69
  if (lock->height == 1) {
Packit Service 310c69
    finishLookup(dataVIO, VDO_SUCCESS);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // We know what page we need to load next
Packit Service 310c69
  loadBlockMapPage(getBlockMapTreeZone(dataVIO), dataVIO);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Continue a block map PBN lookup now that the page load we were waiting on
Packit Service 310c69
 * has finished.
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter   The DataVIO waiting for a page to be loaded
Packit Service 310c69
 * @param context  The page which was just loaded
Packit Service 310c69
 **/
Packit Service 310c69
static void continueLoadForWaiter(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = waiterAsDataVIO(waiter);
Packit Service 310c69
  dataVIO->treeLock.height--;
Packit Service 310c69
  continueWithLoadedPage(dataVIO, (BlockMapPage *) context);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Finish loading a page now that it has been read in from disk. This callback
Packit Service 310c69
 * is registered in loadPage().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO doing the page read
Packit Service 310c69
 **/
Packit Service 310c69
static void finishBlockMapPageLoad(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry     *entry    = completion->parent;
Packit Service 310c69
  DataVIO          *dataVIO  = entry->parent;
Packit Service 310c69
  BlockMapTreeZone *zone     = (BlockMapTreeZone *) entry->context;
Packit Service 310c69
  TreeLock         *treeLock = &dataVIO->treeLock;
Packit Service 310c69
Packit Service 310c69
  treeLock->height--;
Packit Service 310c69
  PhysicalBlockNumber pbn
Packit Service 310c69
    = treeLock->treeSlots[treeLock->height].blockMapSlot.pbn;
Packit Service 310c69
  TreePage     *treePage = getTreePage(zone, treeLock);
Packit Service 310c69
  BlockMapPage *page     = (BlockMapPage *) treePage->pageBuffer;
Packit Service 310c69
  Nonce         nonce    = zone->mapZone->blockMap->nonce;
Packit Service 310c69
  if (!copyValidPage(entry->buffer, nonce, pbn, page)) {
Packit Service 310c69
    formatBlockMapPage(page, nonce, pbn, false);
Packit Service 310c69
  }
Packit Service 310c69
  returnVIOToPool(zone->vioPool, entry);
Packit Service 310c69
Packit Service 310c69
  // Release our claim to the load and wake any waiters
Packit Service 310c69
  releasePageLock(dataVIO, "load");
Packit Service 310c69
  notifyAllWaiters(&treeLock->waiters, continueLoadForWaiter, page);
Packit Service 310c69
  continueWithLoadedPage(dataVIO, page);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Handle an error loading a tree page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The VIO doing the page read
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
  DataVIO          *dataVIO = entry->parent;
Packit Service 310c69
  BlockMapTreeZone *zone    = (BlockMapTreeZone *) entry->context;
Packit Service 310c69
  returnVIOToPool(zone->vioPool, entry);
Packit Service 310c69
  abortLoad(dataVIO, result);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Read a tree page from disk now that we've gotten a VIO with which to do the
Packit Service 310c69
 * read. This WaiterCallback is registered in loadBlockMapPage().
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter   The DataVIO which requires a page load
Packit Service 310c69
 * @param context  The VIOPool entry with which to do the read
Packit Service 310c69
 **/
Packit Service 310c69
static void loadPage(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  VIOPoolEntry *entry   = context;
Packit Service 310c69
  DataVIO      *dataVIO = waiterAsDataVIO(waiter);
Packit Service 310c69
Packit Service 310c69
  entry->parent = dataVIO;
Packit Service 310c69
  entry->vio->completion.callbackThreadID
Packit Service 310c69
    = getBlockMapForZone(dataVIO->logical.zone)->threadID;
Packit Service 310c69
Packit Service 310c69
  TreeLock *lock = &dataVIO->treeLock;
Packit Service 310c69
  launchReadMetadataVIO(entry->vio,
Packit Service 310c69
                        lock->treeSlots[lock->height - 1].blockMapSlot.pbn,
Packit Service 310c69
                        finishBlockMapPageLoad, handleIOError);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Attempt to acquire a lock on a page in the block map tree. If the page is
Packit Service 310c69
 * already locked, queue up to wait for the lock to be released. If the lock is
Packit Service 310c69
 * acquired, the DataVIO's treeLock.locked field will be set to true.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone     The BlockMapTreeZone in which the DataVIO operates
Packit Service 310c69
 * @param dataVIO  The DataVIO which desires a page lock
Packit Service 310c69
 *
Packit Service 310c69
 * @return VDO_SUCCESS or an error
Packit Service 310c69
 **/
Packit Service 310c69
static int attemptPageLock(BlockMapTreeZone *zone, DataVIO *dataVIO)
Packit Service 310c69
{
Packit Service 310c69
  TreeLock         *lock     = &dataVIO->treeLock;
Packit Service 310c69
  Height            height   = lock->height;
Packit Service 310c69
  BlockMapTreeSlot  treeSlot = lock->treeSlots[height];
Packit Service 310c69
  PageKey           key;
Packit Service 310c69
  key.descriptor = (PageDescriptor) {
Packit Service 310c69
    .rootIndex = lock->rootIndex,
Packit Service 310c69
    .height    = height,
Packit Service 310c69
    .pageIndex = treeSlot.pageIndex,
Packit Service 310c69
    .slot      = treeSlot.blockMapSlot.slot,
Packit Service 310c69
  };
Packit Service 310c69
  lock->key = key.key;
Packit Service 310c69
Packit Service 310c69
  TreeLock *lockHolder;
Packit Service 310c69
  int result = intMapPut(zone->loadingPages, lock->key, lock, false,
Packit Service 310c69
                         (void **) &lockHolder);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (lockHolder == NULL) {
Packit Service 310c69
    // We got the lock
Packit Service 310c69
    dataVIO->treeLock.locked = true;
Packit Service 310c69
    return VDO_SUCCESS;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Someone else is loading or allocating the page we need
Packit Service 310c69
  return enqueueDataVIO(&lockHolder->waiters, dataVIO,
Packit Service 310c69
                        THIS_LOCATION("$F;cb=blockMapTreePage"));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Load a block map tree page from disk.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone     The BlockMapTreeZone in which the DataVIO operates
Packit Service 310c69
 * @param dataVIO  The DataVIO which requires a page to be loaded
Packit Service 310c69
 **/
Packit Service 310c69
static void loadBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO)
Packit Service 310c69
{
Packit Service 310c69
  int result = attemptPageLock(zone, dataVIO);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    abortLoad(dataVIO, result);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (dataVIO->treeLock.locked) {
Packit Service 310c69
    Waiter *waiter   = dataVIOAsWaiter(dataVIO);
Packit Service 310c69
    waiter->callback = loadPage;
Packit Service 310c69
    result = acquireVIOFromPool(zone->vioPool, waiter);
Packit Service 310c69
    if (result != VDO_SUCCESS) {
Packit Service 310c69
      abortLoad(dataVIO, result);
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the callback of a DataVIO after it has allocated a block map page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO
Packit Service 310c69
 **/
Packit Service 310c69
static void setPostAllocationCallback(DataVIO *dataVIO)
Packit Service 310c69
{
Packit Service 310c69
  setCallback(dataVIOAsCompletion(dataVIO), dataVIO->treeLock.callback,
Packit Service 310c69
              dataVIO->treeLock.threadID);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Abort a block map PBN lookup due to an error allocating a page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param dataVIO  The DataVIO doing the page allocation
Packit Service 310c69
 * @param result   The error code
Packit Service 310c69
 **/
Packit Service 310c69
static void abortAllocation(DataVIO *dataVIO, int result)
Packit Service 310c69
{
Packit Service 310c69
  setPostAllocationCallback(dataVIO);
Packit Service 310c69
  abortLookup(dataVIO, result, "allocation");
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Callback to handle an error while attempting to allocate a page. This
Packit Service 310c69
 * callback is used to transfer back to the logical zone along the block map
Packit Service 310c69
 * page allocation path.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The DataVIO doing the allocation
Packit Service 310c69
 **/
Packit Service 310c69
static void allocationFailure(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = asDataVIO(completion);
Packit Service 310c69
  assertInLogicalZone(dataVIO);
Packit Service 310c69
  abortAllocation(dataVIO, completion->result);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Continue with page allocations now that a parent page has been allocated.
Packit Service 310c69
 *
Packit Service 310c69
 * @param waiter   The DataVIO which was waiting for a page to be allocated
Packit Service 310c69
 * @param context  The physical block number of the page which was just
Packit Service 310c69
 *                 allocated
Packit Service 310c69
 **/
Packit Service 310c69
static void continueAllocationForWaiter(Waiter *waiter, void *context)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO             *dataVIO  = waiterAsDataVIO(waiter);
Packit Service 310c69
  TreeLock            *treeLock = &dataVIO->treeLock;
Packit Service 310c69
  PhysicalBlockNumber  pbn      = *((PhysicalBlockNumber *) context);
Packit Service 310c69
Packit Service 310c69
  treeLock->height--;
Packit Service 310c69
  dataVIO->treeLock.treeSlots[treeLock->height].blockMapSlot.pbn = pbn;
Packit Service 310c69
Packit Service 310c69
  if (treeLock->height == 0) {
Packit Service 310c69
    finishLookup(dataVIO, VDO_SUCCESS);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  allocateBlockMapPage(getBlockMapTreeZone(dataVIO), dataVIO);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Finish the page allocation process by recording the allocation in the tree
Packit Service 310c69
 * and waking any waiters now that the write lock has been released. This
Packit Service 310c69
 * callback is registered in releaseBlockMapWriteLock().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The DataVIO doing the allocation
Packit Service 310c69
 **/
Packit Service 310c69
static void finishBlockMapAllocation(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = asDataVIO(completion);
Packit Service 310c69
  assertInLogicalZone(dataVIO);
Packit Service 310c69
  if (completion->result != VDO_SUCCESS) {
Packit Service 310c69
    allocationFailure(completion);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  BlockMapTreeZone *zone     = getBlockMapTreeZone(dataVIO);
Packit Service 310c69
  TreeLock         *treeLock = &dataVIO->treeLock;
Packit Service 310c69
  TreePage         *treePage = getTreePage(zone, treeLock);
Packit Service 310c69
  Height            height   = treeLock->height;
Packit Service 310c69
Packit Service 310c69
  PhysicalBlockNumber pbn = treeLock->treeSlots[height - 1].blockMapSlot.pbn;
Packit Service 310c69
Packit Service 310c69
  // Record the allocation.
Packit Service 310c69
  BlockMapPage   *page    = (BlockMapPage *) treePage->pageBuffer;
Packit Service 310c69
  SequenceNumber  oldLock = treePage->recoveryLock;
Packit Service 310c69
  updateBlockMapPage(page, dataVIO, pbn, MAPPING_STATE_UNCOMPRESSED,
Packit Service 310c69
                     &treePage->recoveryLock);
Packit Service 310c69
Packit Service 310c69
  if (isWaiting(&treePage->waiter)) {
Packit Service 310c69
    // This page is waiting to be written out.
Packit Service 310c69
    if (zone->flusher != treePage) {
Packit Service 310c69
      // The outstanding flush won't cover the update we just made, so mark
Packit Service 310c69
      // the page as needing another flush.
Packit Service 310c69
      setGeneration(zone, treePage, zone->generation, true);
Packit Service 310c69
    }
Packit Service 310c69
  } else {
Packit Service 310c69
    // Put the page on a dirty list
Packit Service 310c69
    if (oldLock == 0) {
Packit Service 310c69
      initializeRing(&treePage->node);
Packit Service 310c69
    }
Packit Service 310c69
    addToDirtyLists(zone->dirtyLists, &treePage->node, oldLock,
Packit Service 310c69
                    treePage->recoveryLock);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  treeLock->height--;
Packit Service 310c69
  if (height > 1) {
Packit Service 310c69
    // Format the interior node we just allocated (in memory).
Packit Service 310c69
    treePage = getTreePage(zone, treeLock);
Packit Service 310c69
    formatBlockMapPage(treePage->pageBuffer, zone->mapZone->blockMap->nonce,
Packit Service 310c69
                       pbn, false);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Release our claim to the allocation and wake any waiters
Packit Service 310c69
  releasePageLock(dataVIO, "allocation");
Packit Service 310c69
  notifyAllWaiters(&treeLock->waiters, continueAllocationForWaiter, &pbn);
Packit Service 310c69
  if (treeLock->height == 0) {
Packit Service 310c69
    finishLookup(dataVIO, VDO_SUCCESS);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  allocateBlockMapPage(zone, dataVIO);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Release the write lock on a newly allocated block map page now that we
Packit Service 310c69
 * have made its journal entries and reference count updates. This callback
Packit Service 310c69
 * is registered in setBlockMapPageReferenceCount().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The DataVIO doing the allocation
Packit Service 310c69
 **/
Packit Service 310c69
static void releaseBlockMapWriteLock(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = asDataVIO(completion);
Packit Service 310c69
  AllocatingVIO *allocatingVIO = dataVIOAsAllocatingVIO(dataVIO);
Packit Service 310c69
  assertInAllocatedZone(dataVIO);
Packit Service 310c69
  if (completion->result != VDO_SUCCESS) {
Packit Service 310c69
    launchLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  releaseAllocationLock(allocatingVIO);
Packit Service 310c69
  resetAllocation(allocatingVIO);
Packit Service 310c69
  launchLogicalCallback(dataVIO, finishBlockMapAllocation,
Packit Service 310c69
                        THIS_LOCATION("$F;cb=finishBlockMapAllocation"));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Set the reference count of a newly allocated block map page to
Packit Service 310c69
 * MAXIMUM_REFERENCES now that we have made a recovery journal entry for it.
Packit Service 310c69
 * MAXIMUM_REFERENCES is used to prevent deduplication against the block after
Packit Service 310c69
 * we release the write lock on it, but before we write out the page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The DataVIO doing the allocation
Packit Service 310c69
 **/
Packit Service 310c69
static void setBlockMapPageReferenceCount(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = asDataVIO(completion);
Packit Service 310c69
  assertInAllocatedZone(dataVIO);
Packit Service 310c69
  if (completion->result != VDO_SUCCESS) {
Packit Service 310c69
    launchLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  TreeLock *lock = &dataVIO->treeLock;
Packit Service 310c69
  PhysicalBlockNumber pbn = lock->treeSlots[lock->height - 1].blockMapSlot.pbn;
Packit Service 310c69
  completion->callback = releaseBlockMapWriteLock;
Packit Service 310c69
  addSlabJournalEntry(getSlabJournal(getVDOFromDataVIO(dataVIO)->depot, pbn),
Packit Service 310c69
                      dataVIO);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Make a recovery journal entry for a newly allocated block map page.
Packit Service 310c69
 * This callback is registered in continueBlockMapPageAllocation().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The DataVIO doing the allocation
Packit Service 310c69
 **/
Packit Service 310c69
static void journalBlockMapAllocation(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = asDataVIO(completion);
Packit Service 310c69
  assertInJournalZone(dataVIO);
Packit Service 310c69
  if (completion->result != VDO_SUCCESS) {
Packit Service 310c69
    launchLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  setAllocatedZoneCallback(dataVIO, setBlockMapPageReferenceCount,
Packit Service 310c69
                           THIS_LOCATION(NULL));
Packit Service 310c69
  addRecoveryJournalEntry(getVDOFromDataVIO(dataVIO)->recoveryJournal,
Packit Service 310c69
                          dataVIO);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Continue the process of allocating a block map page now that the
Packit Service 310c69
 * BlockAllocator has given us a block. This method is supplied as the callback
Packit Service 310c69
 * to allocateDataBlock() by allocateBlockMapPage().
Packit Service 310c69
 *
Packit Service 310c69
 * @param allocatingVIO  The DataVIO which is doing the allocation
Packit Service 310c69
 **/
Packit Service 310c69
static void continueBlockMapPageAllocation(AllocatingVIO *allocatingVIO)
Packit Service 310c69
{
Packit Service 310c69
  DataVIO *dataVIO = allocatingVIOAsDataVIO(allocatingVIO);
Packit Service 310c69
  if (!hasAllocation(dataVIO)) {
Packit Service 310c69
    setLogicalCallback(dataVIO, allocationFailure, THIS_LOCATION(NULL));
Packit Service 310c69
    continueDataVIO(dataVIO, VDO_NO_SPACE);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  PhysicalBlockNumber  pbn  = allocatingVIO->allocation;
Packit Service 310c69
  TreeLock            *lock = &dataVIO->treeLock;
Packit Service 310c69
  lock->treeSlots[lock->height - 1].blockMapSlot.pbn = pbn;
Packit Service 310c69
  setUpReferenceOperationWithLock(BLOCK_MAP_INCREMENT, pbn,
Packit Service 310c69
                                  MAPPING_STATE_UNCOMPRESSED,
Packit Service 310c69
                                  allocatingVIO->allocationLock,
Packit Service 310c69
                                  &dataVIO->operation);
Packit Service 310c69
  launchJournalCallback(dataVIO, journalBlockMapAllocation,
Packit Service 310c69
                        THIS_LOCATION("$F;cb=journalBlockMapAllocation"));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Allocate a block map page.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone     The zone in which the DataVIO is operating
Packit Service 310c69
 * @param dataVIO  The DataVIO which needs to allocate a page
Packit Service 310c69
 **/
Packit Service 310c69
static void allocateBlockMapPage(BlockMapTreeZone *zone, DataVIO *dataVIO)
Packit Service 310c69
{
Packit Service 310c69
  if (!isWriteDataVIO(dataVIO) || isTrimDataVIO(dataVIO)) {
Packit Service 310c69
    // This is a pure read, the read phase of a read-modify-write, or a trim,
Packit Service 310c69
    // so there's nothing left to do here.
Packit Service 310c69
    finishLookup(dataVIO, VDO_SUCCESS);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  int result = attemptPageLock(zone, dataVIO);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    abortAllocation(dataVIO, result);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (!dataVIO->treeLock.locked) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  allocateDataBlock(dataVIOAsAllocatingVIO(dataVIO),
Packit Service 310c69
                    getAllocationSelector(dataVIO->logical.zone),
Packit Service 310c69
                    VIO_BLOCK_MAP_WRITE_LOCK,
Packit Service 310c69
                    continueBlockMapPageAllocation);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void lookupBlockMapPBN(DataVIO *dataVIO)
Packit Service 310c69
{
Packit Service 310c69
  BlockMapTreeZone *zone = getBlockMapTreeZone(dataVIO);
Packit Service 310c69
  zone->activeLookups++;
Packit Service 310c69
  if (isDraining(&zone->mapZone->state)) {
Packit Service 310c69
    finishLookup(dataVIO, VDO_SHUTTING_DOWN);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  TreeLock *lock = &dataVIO->treeLock;
Packit Service 310c69
  PageNumber pageIndex
Packit Service 310c69
    = ((lock->treeSlots[0].pageIndex - zone->mapZone->blockMap->flatPageCount)
Packit Service 310c69
       / zone->mapZone->blockMap->rootCount);
Packit Service 310c69
  BlockMapTreeSlot treeSlot = {
Packit Service 310c69
    .pageIndex = pageIndex / BLOCK_MAP_ENTRIES_PER_PAGE,
Packit Service 310c69
    .blockMapSlot = {
Packit Service 310c69
      .pbn  = 0,
Packit Service 310c69
      .slot = pageIndex % BLOCK_MAP_ENTRIES_PER_PAGE,
Packit Service 310c69
    },
Packit Service 310c69
  };
Packit Service 310c69
Packit Service 310c69
  BlockMapPage *page = NULL;
Packit Service 310c69
  for (lock->height = 1; lock->height <= BLOCK_MAP_TREE_HEIGHT;
Packit Service 310c69
       lock->height++) {
Packit Service 310c69
    lock->treeSlots[lock->height] = treeSlot;
Packit Service 310c69
    page = (BlockMapPage *) (getTreePage(zone, lock)->pageBuffer);
Packit Service 310c69
    PhysicalBlockNumber pbn = getBlockMapPagePBN(page);
Packit Service 310c69
    if (pbn != ZERO_BLOCK) {
Packit Service 310c69
      lock->treeSlots[lock->height].blockMapSlot.pbn = pbn;
Packit Service 310c69
      break;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    // Calculate the index and slot for the next level.
Packit Service 310c69
    treeSlot.blockMapSlot.slot
Packit Service 310c69
      = treeSlot.pageIndex % BLOCK_MAP_ENTRIES_PER_PAGE;
Packit Service 310c69
    treeSlot.pageIndex
Packit Service 310c69
      = treeSlot.pageIndex / BLOCK_MAP_ENTRIES_PER_PAGE;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // The page at this height has been allocated and loaded.
Packit Service 310c69
  DataLocation mapping
Packit Service 310c69
    = unpackBlockMapEntry(&page->entries[treeSlot.blockMapSlot.slot]);
Packit Service 310c69
  if (isInvalidTreeEntry(getVDOFromDataVIO(dataVIO), &mapping, lock->height)) {
Packit Service 310c69
    logErrorWithStringError(VDO_BAD_MAPPING,
Packit Service 310c69
                            "Invalid block map tree PBN: %llu with "
Packit Service 310c69
                            "state %u for page index %u at height %u",
Packit Service 310c69
                            mapping.pbn, mapping.state,
Packit Service 310c69
                            lock->treeSlots[lock->height - 1].pageIndex,
Packit Service 310c69
                            lock->height - 1);
Packit Service 310c69
    abortLoad(dataVIO, VDO_BAD_MAPPING);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (!isMappedLocation(&mapping)) {
Packit Service 310c69
    // The page we want one level down has not been allocated, so allocate it.
Packit Service 310c69
    allocateBlockMapPage(zone, dataVIO);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  lock->treeSlots[lock->height - 1].blockMapSlot.pbn = mapping.pbn;
Packit Service 310c69
  if (lock->height == 1) {
Packit Service 310c69
    // This is the ultimate block map page, so we're done
Packit Service 310c69
    finishLookup(dataVIO, VDO_SUCCESS);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // We know what page we need to load.
Packit Service 310c69
  loadBlockMapPage(zone, dataVIO);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
PhysicalBlockNumber findBlockMapPagePBN(BlockMap *map, PageNumber pageNumber)
Packit Service 310c69
{
Packit Service 310c69
  if (pageNumber < map->flatPageCount) {
Packit Service 310c69
    return (BLOCK_MAP_FLAT_PAGE_ORIGIN + pageNumber);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  RootCount  rootIndex = pageNumber % map->rootCount;
Packit Service 310c69
  PageNumber pageIndex = ((pageNumber - map->flatPageCount) / map->rootCount);
Packit Service 310c69
  SlotNumber slot      = pageIndex % BLOCK_MAP_ENTRIES_PER_PAGE;
Packit Service 310c69
  pageIndex /= BLOCK_MAP_ENTRIES_PER_PAGE;
Packit Service 310c69
Packit Service 310c69
  TreePage *treePage
Packit Service 310c69
    = getTreePageByIndex(map->forest, rootIndex, 1, pageIndex);
Packit Service 310c69
  BlockMapPage *page = (BlockMapPage *) treePage->pageBuffer;
Packit Service 310c69
  if (!isBlockMapPageInitialized(page)) {
Packit Service 310c69
    return ZERO_BLOCK;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  DataLocation mapping = unpackBlockMapEntry(&page->entries[slot]);
Packit Service 310c69
  if (!isValidLocation(&mapping) || isCompressed(mapping.state)) {
Packit Service 310c69
    return ZERO_BLOCK;
Packit Service 310c69
  }
Packit Service 310c69
  return mapping.pbn;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void writeTreePage(TreePage *page, BlockMapTreeZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  bool waiting = isWaiting(&page->waiter);
Packit Service 310c69
  if (waiting && (zone->flusher == page)) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  setGeneration(zone, page, zone->generation, waiting);
Packit Service 310c69
  if (waiting || page->writing) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  enqueuePage(page, zone);
Packit Service 310c69
}