Blame source/uds/sparseCache.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/uds-releases/jasper/src/uds/sparseCache.c#3 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * The sparse chapter index cache is implemented as a simple array of cache
Packit Service 310c69
 * entries. Since the cache is small (seven chapters by default), searching
Packit Service 310c69
 * for a specific virtual chapter is implemented as a linear search. The cache
Packit Service 310c69
 * replacement policy is least-recently-used (LRU). Again, size of the cache
Packit Service 310c69
 * allows the LRU order to be maintained by shifting entries in an array list.
Packit Service 310c69
 *
Packit Service 310c69
 * The most important property of this cache is the absence of synchronization
Packit Service 310c69
 * for read operations. Safe concurrent access to the cache by the zone
Packit Service 310c69
 * threads is controlled by the triage queue and the barrier requests it
Packit Service 310c69
 * issues to the zone queues. The set of cached chapters does not and must not
Packit Service 310c69
 * change between the carefully coordinated calls to updateSparseCache() from
Packit Service 310c69
 * the zone threads.
Packit Service 310c69
 *
Packit Service 310c69
 * The critical invariant for that coordination is the cache membership must
Packit Service 310c69
 * not change between those updates; the calls to sparseCacheContains() from
Packit Service 310c69
 * the zone threads must all receive the same results for any virtual chapter
Packit Service 310c69
 * number. To ensure that critical invariant, state changes such as "that
Packit Service 310c69
 * virtual chapter is no longer in the volume" and "skip searching that
Packit Service 310c69
 * chapter because it has had too many cache misses" are represented
Packit Service 310c69
 * separately from the cache membership information (the virtual chapter
Packit Service 310c69
 * number).
Packit Service 310c69
 *
Packit Service 310c69
 * As a result of this invariant, we have the guarantee that every zone thread
Packit Service 310c69
 * will call updateSparseCache() once and exactly once to request a chapter
Packit Service 310c69
 * that is not in the cache, and the serialization of the barrier requests
Packit Service 310c69
 * from the triage queue ensures they will all request the same chapter
Packit Service 310c69
 * number. This means the only synchronization we need can be provided by a
Packit Service 310c69
 * pair of thread barriers used only in the updateSparseCache() call,
Packit Service 310c69
 * providing a critical section where a single zone thread can drive the cache
Packit Service 310c69
 * update while all the other zone threads are known to be blocked, waiting in
Packit Service 310c69
 * the second barrier. Outside that critical section, all the zone threads
Packit Service 310c69
 * implicitly hold a shared lock. Inside it, the "captain" (the thread that
Packit Service 310c69
 * was uniquely flagged when passing through the first barrier) holds an
Packit Service 310c69
 * exclusive lock. No other threads may access or modify the cache, except for
Packit Service 310c69
 * accessing cache statistics and similar queries.
Packit Service 310c69
 *
Packit Service 310c69
 * Cache statistics must only be modified by a single thread, conventionally
Packit Service 310c69
 * the zone zero thread. All fields that might be frequently updated by that
Packit Service 310c69
 * thread are kept in separate cache-aligned structures so they will not cause
Packit Service 310c69
 * cache contention via "false sharing" with the fields that are frequently
Packit Service 310c69
 * accessed by all of the zone threads.
Packit Service 310c69
 *
Packit Service 310c69
 * LRU order is kept independently by each zone thread, and each zone uses its
Packit Service 310c69
 * own list for searching and cache membership queries. The zone zero list is
Packit Service 310c69
 * used to decide which chapter to evict when the cache is updated, and its
Packit Service 310c69
 * search list is copied to the other threads at that time.
Packit Service 310c69
 *
Packit Service 310c69
 * The virtual chapter number field of the cache entry is the single field
Packit Service 310c69
 * indicating whether a chapter is a member of the cache or not. The value
Packit Service 310c69
 * UINT64_MAX is used to represent a null, undefined, or wildcard
Packit Service 310c69
 * chapter number. When present in the virtual chapter number field
Packit Service 310c69
 * CachedChapterIndex, it indicates that the cache entry is dead, and all
Packit Service 310c69
 * the other fields of that entry (other than immutable pointers to cache
Packit Service 310c69
 * memory) are undefined and irrelevant. Any cache entry that is not marked as
Packit Service 310c69
 * dead is fully defined and a member of the cache--sparseCacheContains()
Packit Service 310c69
 * must always return true for any virtual chapter number that appears in any
Packit Service 310c69
 * of the cache entries.
Packit Service 310c69
 *
Packit Service 310c69
 * A chapter index that is a member of the cache may be marked for different
Packit Service 310c69
 * treatment (disabling search) between calls to updateSparseCache() in two
Packit Service 310c69
 * different ways. When a chapter falls off the end of the volume, its virtual
Packit Service 310c69
 * chapter number will be less that the oldest virtual chapter number. Since
Packit Service 310c69
 * that chapter is no longer part of the volume, there's no point in continuing
Packit Service 310c69
 * to search that chapter index. Once invalidated, that virtual chapter will
Packit Service 310c69
 * still be considered a member of the cache, but it will no longer be searched
Packit Service 310c69
 * for matching chunk names.
Packit Service 310c69
 *
Packit Service 310c69
 * The second mechanism for disabling search is the heuristic based on keeping
Packit Service 310c69
 * track of the number of consecutive search misses in a given chapter index.
Packit Service 310c69
 * Once that count exceeds a threshold, the skipSearch flag will be set to
Packit Service 310c69
 * true, causing the chapter to be skipped in the fallback search of the
Packit Service 310c69
 * entire cache, but still allowing it to be found when searching for a hook
Packit Service 310c69
 * in that specific chapter. Finding a hook will clear the skipSearch flag,
Packit Service 310c69
 * once again allowing the non-hook searches to use the cache entry. Again,
Packit Service 310c69
 * regardless of the state of the skipSearch flag, the virtual chapter must
Packit Service 310c69
 * still considered to be a member of the cache for sparseCacheContains().
Packit Service 310c69
 *
Packit Service 310c69
 * Barrier requests and the sparse chapter index cache are also described in
Packit Service 310c69
 *
Packit Service 310c69
 * https://intranet.permabit.com/wiki/Chapter_Index_Cache_supports_concurrent_access
Packit Service 310c69
 *
Packit Service 310c69
 * and in a message to the albireo mailing list on 5/28/2011 titled "true
Packit Service 310c69
 * barriers with a hook resolution queue".
Packit Service 310c69
 **/
Packit Service 310c69
Packit Service 310c69
#include "sparseCache.h"
Packit Service 310c69
Packit Service 310c69
#include "cachedChapterIndex.h"
Packit Service 310c69
#include "chapterIndex.h"
Packit Service 310c69
#include "common.h"
Packit Service 310c69
#include "index.h"
Packit Service 310c69
#include "logger.h"
Packit Service 310c69
#include "memoryAlloc.h"
Packit Service 310c69
#include "permassert.h"
Packit Service 310c69
#include "searchList.h"
Packit Service 310c69
#include "threads.h"
Packit Service 310c69
#include "zone.h"
Packit Service 310c69
Packit Service 310c69
enum {
Packit Service 310c69
  /** The number of consecutive search misses that will disable searching */
Packit Service 310c69
  SKIP_SEARCH_THRESHOLD = 20000,
Packit Service 310c69
Packit Service 310c69
  /** a named constant to use when identifying zone zero */
Packit Service 310c69
  ZONE_ZERO = 0
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * These counter values are essentially fields of the SparseCache, but are
Packit Service 310c69
 * segregated into this structure because they are frequently modified. We
Packit Service 310c69
 * group them and align them to keep them on different cache lines from the
Packit Service 310c69
 * cache fields that are accessed far more often than they are updated.
Packit Service 310c69
 **/
Packit Service 310c69
typedef struct __attribute__((aligned(CACHE_LINE_BYTES))) sparseCacheCounters {
Packit Service 310c69
  /** the total number of virtual chapter probes that succeeded */
Packit Service 310c69
  uint64_t      chapterHits;
Packit Service 310c69
Packit Service 310c69
  /** the total number of virtual chapter probes that failed */
Packit Service 310c69
  uint64_t      chapterMisses;
Packit Service 310c69
Packit Service 310c69
  /** the total number of cache searches that found a possible match */
Packit Service 310c69
  uint64_t      searchHits;
Packit Service 310c69
Packit Service 310c69
  /** the total number of cache searches that found no matches */
Packit Service 310c69
  uint64_t      searchMisses;
Packit Service 310c69
Packit Service 310c69
  /** the number of cache entries that fell off the end of the volume */
Packit Service 310c69
  uint64_t      invalidations;
Packit Service 310c69
Packit Service 310c69
  /** the number of cache entries that were evicted while still valid */
Packit Service 310c69
  uint64_t      evictions;
Packit Service 310c69
} SparseCacheCounters;
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * This is the private structure definition of a SparseCache.
Packit Service 310c69
 **/
Packit Service 310c69
struct sparseCache {
Packit Service 310c69
  /** the number of cache entries, which is the size of the chapters array */
Packit Service 310c69
  unsigned int           capacity;
Packit Service 310c69
Packit Service 310c69
  /** the number of zone threads using the cache */
Packit Service 310c69
  unsigned int           zoneCount;
Packit Service 310c69
Packit Service 310c69
  /** the geometry governing the volume */
Packit Service 310c69
  const Geometry        *geometry;
Packit Service 310c69
Packit Service 310c69
  /** the number of search misses in zone zero that will disable searching */
Packit Service 310c69
  unsigned int           skipSearchThreshold;
Packit Service 310c69
Packit Service 310c69
  /** pointers to the cache-aligned chapter search order for each zone */
Packit Service 310c69
  SearchList            *searchLists[MAX_ZONES];
Packit Service 310c69
Packit Service 310c69
  /** the thread barriers used to synchronize the zone threads for update */
Packit Service 310c69
  Barrier                beginCacheUpdate;
Packit Service 310c69
  Barrier                endCacheUpdate;
Packit Service 310c69
Packit Service 310c69
  /** frequently-updated counter fields (cache-aligned) */
Packit Service 310c69
  SparseCacheCounters    counters;
Packit Service 310c69
Packit Service 310c69
  /** the counted array of chapter index cache entries (cache-aligned) */
Packit Service 310c69
  CachedChapterIndex     chapters[];
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Initialize a sparse chapter index cache.
Packit Service 310c69
 *
Packit Service 310c69
 * @param cache      the sparse cache to initialize
Packit Service 310c69
 * @param geometry   the geometry governing the volume
Packit Service 310c69
 * @param capacity   the number of chapters the cache will hold
Packit Service 310c69
 * @param zoneCount  the number of zone threads using the cache
Packit Service 310c69
 *
Packit Service 310c69
 * @return UDS_SUCCESS or an error code
Packit Service 310c69
 **/
Packit Service 310c69
__attribute__((warn_unused_result))
Packit Service 310c69
static int initializeSparseCache(SparseCache    *cache,
Packit Service 310c69
                                 const Geometry *geometry,
Packit Service 310c69
                                 unsigned int    capacity,
Packit Service 310c69
                                 unsigned int    zoneCount)
Packit Service 310c69
{
Packit Service 310c69
  cache->geometry  = geometry;
Packit Service 310c69
  cache->capacity  = capacity;
Packit Service 310c69
  cache->zoneCount = zoneCount;
Packit Service 310c69
Packit Service 310c69
  // Scale down the skip threshold by the number of zones since we count the
Packit Service 310c69
  // chapter search misses only in zone zero.
Packit Service 310c69
  cache->skipSearchThreshold = (SKIP_SEARCH_THRESHOLD / zoneCount);
Packit Service 310c69
Packit Service 310c69
  int result = initializeBarrier(&cache->beginCacheUpdate, zoneCount);
Packit Service 310c69
  if (result != UDS_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
  result = initializeBarrier(&cache->endCacheUpdate, zoneCount);
Packit Service 310c69
  if (result != UDS_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
  unsigned int i;
Packit Service 310c69
  for (i = 0; i < capacity; i++) {
Packit Service 310c69
    result = initializeCachedChapterIndex(&cache->chapters[i], geometry);
Packit Service 310c69
    if (result != UDS_SUCCESS) {
Packit Service 310c69
      return result;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Allocate each zone's independent LRU order.
Packit Service 310c69
  for (i = 0; i < zoneCount; i++) {
Packit Service 310c69
    result = makeSearchList(capacity, &cache->searchLists[i]);
Packit Service 310c69
    if (result != UDS_SUCCESS) {
Packit Service 310c69
      return result;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
  return UDS_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int makeSparseCache(const Geometry  *geometry,
Packit Service 310c69
                    unsigned int     capacity,
Packit Service 310c69
                    unsigned int     zoneCount,
Packit Service 310c69
                    SparseCache    **cachePtr)
Packit Service 310c69
{
Packit Service 310c69
  unsigned int bytes
Packit Service 310c69
    = (sizeof(SparseCache) + (capacity * sizeof(CachedChapterIndex)));
Packit Service 310c69
Packit Service 310c69
  SparseCache *cache;
Packit Service 310c69
  int result = allocateCacheAligned(bytes, "sparse cache", &cache);
Packit Service 310c69
  if (result != UDS_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = initializeSparseCache(cache, geometry, capacity, zoneCount);
Packit Service 310c69
  if (result != UDS_SUCCESS) {
Packit Service 310c69
    freeSparseCache(cache);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *cachePtr = cache;
Packit Service 310c69
  return UDS_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
size_t getSparseCacheMemorySize(const SparseCache *cache)
Packit Service 310c69
{
Packit Service 310c69
  // Count the DeltaIndexPage as cache memory, but ignore all other overhead.
Packit Service 310c69
  size_t pageSize = (sizeof(DeltaIndexPage) + cache->geometry->bytesPerPage);
Packit Service 310c69
  size_t chapterSize = (pageSize * cache->geometry->indexPagesPerChapter);
Packit Service 310c69
  return (cache->capacity * chapterSize);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Update counters to reflect a chapter access hit and clear the skipSearch
Packit Service 310c69
 * flag on the chapter, if set.
Packit Service 310c69
 *
Packit Service 310c69
 * @param cache      the cache to update
Packit Service 310c69
 * @param chapter    the cache entry to update
Packit Service 310c69
 **/
Packit Service 310c69
static void scoreChapterHit(SparseCache        *cache,
Packit Service 310c69
                            CachedChapterIndex *chapter)
Packit Service 310c69
{
Packit Service 310c69
  cache->counters.chapterHits += 1;
Packit Service 310c69
  setSkipSearch(chapter, false);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Update counters to reflect a chapter access miss.
Packit Service 310c69
 *
Packit Service 310c69
 * @param cache      the cache to update
Packit Service 310c69
 **/
Packit Service 310c69
static void scoreChapterMiss(SparseCache *cache)
Packit Service 310c69
{
Packit Service 310c69
  cache->counters.chapterMisses += 1;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Check if the cache entry that is about to be replaced is already dead, and
Packit Service 310c69
 * if it's not, add to tally of evicted or invalidated cache entries.
Packit Service 310c69
 *
Packit Service 310c69
 * @param zone       the zone used to find the oldest chapter
Packit Service 310c69
 * @param cache      the cache to update
Packit Service 310c69
 * @param chapter    the cache entry about to be replaced
Packit Service 310c69
 **/
Packit Service 310c69
static void scoreEviction(IndexZone          *zone,
Packit Service 310c69
                          SparseCache        *cache,
Packit Service 310c69
                          CachedChapterIndex *chapter)
Packit Service 310c69
{
Packit Service 310c69
  if (chapter->virtualChapter == UINT64_MAX) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
  if (chapter->virtualChapter < zone->oldestVirtualChapter) {
Packit Service 310c69
    cache->counters.invalidations += 1;
Packit Service 310c69
  } else {
Packit Service 310c69
    cache->counters.evictions += 1;
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Update counters to reflect a cache search hit. This bumps the hit
Packit Service 310c69
 * count, clears the miss count, and clears the skipSearch flag.
Packit Service 310c69
 *
Packit Service 310c69
 * @param cache      the cache to update
Packit Service 310c69
 * @param chapter    the cache entry to update
Packit Service 310c69
 **/
Packit Service 310c69
static void scoreSearchHit(SparseCache        *cache,
Packit Service 310c69
                           CachedChapterIndex *chapter)
Packit Service 310c69
{
Packit Service 310c69
  cache->counters.searchHits += 1;
Packit Service 310c69
  chapter->counters.searchHits += 1;
Packit Service 310c69
  chapter->counters.consecutiveMisses = 0;
Packit Service 310c69
  setSkipSearch(chapter, false);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Update counters to reflect a cache search miss. This bumps the consecutive
Packit Service 310c69
 * miss count, and if it goes over skipSearchThreshold, sets the skipSearch
Packit Service 310c69
 * flag on the chapter.
Packit Service 310c69
 *
Packit Service 310c69
 * @param cache      the cache to update
Packit Service 310c69
 * @param chapter    the cache entry to update
Packit Service 310c69
 **/
Packit Service 310c69
static void scoreSearchMiss(SparseCache        *cache,
Packit Service 310c69
                            CachedChapterIndex *chapter)
Packit Service 310c69
{
Packit Service 310c69
  cache->counters.searchMisses += 1;
Packit Service 310c69
  chapter->counters.searchMisses += 1;
Packit Service 310c69
  chapter->counters.consecutiveMisses += 1;
Packit Service 310c69
  if (chapter->counters.consecutiveMisses > cache->skipSearchThreshold) {
Packit Service 310c69
    setSkipSearch(chapter, true);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void freeSparseCache(SparseCache *cache)
Packit Service 310c69
{
Packit Service 310c69
  if (cache == NULL) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  unsigned int i;
Packit Service 310c69
  for (i = 0; i < cache->zoneCount; i++) {
Packit Service 310c69
    freeSearchList(&cache->searchLists[i]);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  for (i = 0; i < cache->capacity; i++) {
Packit Service 310c69
    CachedChapterIndex *chapter = &cache->chapters[i];
Packit Service 310c69
    destroyCachedChapterIndex(chapter);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  destroyBarrier(&cache->beginCacheUpdate);
Packit Service 310c69
  destroyBarrier(&cache->endCacheUpdate);
Packit Service 310c69
  FREE(cache);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool sparseCacheContains(SparseCache  *cache,
Packit Service 310c69
                         uint64_t      virtualChapter,
Packit Service 310c69
                         unsigned int  zoneNumber)
Packit Service 310c69
{
Packit Service 310c69
  /*
Packit Service 310c69
   * The correctness of the barriers depends on the invariant that between
Packit Service 310c69
   * calls to updateSparseCache(), the answers this function returns must
Packit Service 310c69
   * never vary--the result for a given chapter must be identical across
Packit Service 310c69
   * zones. That invariant must be maintained even if the chapter falls off
Packit Service 310c69
   * the end of the volume, or if searching it is disabled because of too many
Packit Service 310c69
   * search misses.
Packit Service 310c69
   */
Packit Service 310c69
Packit Service 310c69
  // Get the chapter search order for this zone thread.
Packit Service 310c69
  SearchListIterator iterator
Packit Service 310c69
    = iterateSearchList(cache->searchLists[zoneNumber], cache->chapters);
Packit Service 310c69
  while (hasNextChapter(&iterator)) {
Packit Service 310c69
    CachedChapterIndex *chapter = getNextChapter(&iterator);
Packit Service 310c69
    if (virtualChapter == chapter->virtualChapter) {
Packit Service 310c69
      if (zoneNumber == ZONE_ZERO) {
Packit Service 310c69
        scoreChapterHit(cache, chapter);
Packit Service 310c69
      }
Packit Service 310c69
Packit Service 310c69
      // Move the chapter to the front of the search list.
Packit Service 310c69
      rotateSearchList(iterator.list, iterator.nextEntry);
Packit Service 310c69
      return true;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // The specified virtual chapter isn't cached.
Packit Service 310c69
  if (zoneNumber == ZONE_ZERO) {
Packit Service 310c69
    scoreChapterMiss(cache);
Packit Service 310c69
  }
Packit Service 310c69
  return false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int updateSparseCache(IndexZone *zone, uint64_t virtualChapter)
Packit Service 310c69
{
Packit Service 310c69
  const Index *index = zone->index;
Packit Service 310c69
  SparseCache *cache = index->volume->sparseCache;
Packit Service 310c69
Packit Service 310c69
  // If the chapter is already in the cache, we don't need to do a thing
Packit Service 310c69
  // except update the search list order, which this check does.
Packit Service 310c69
  if (sparseCacheContains(cache, virtualChapter, zone->id)) {
Packit Service 310c69
    return UDS_SUCCESS;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Wait for every zone thread to have reached its corresponding barrier
Packit Service 310c69
  // request and invoked this function before starting to modify the cache.
Packit Service 310c69
  enterBarrier(&cache->beginCacheUpdate, NULL);
Packit Service 310c69
Packit Service 310c69
  /*
Packit Service 310c69
   * This is the start of the critical section: the zone zero thread is
Packit Service 310c69
   * captain, effectively holding an exclusive lock on the sparse cache. All
Packit Service 310c69
   * the other zone threads must do nothing between the two barriers. They
Packit Service 310c69
   * will wait at the endCacheUpdate barrier for the captain to finish the
Packit Service 310c69
   * update.
Packit Service 310c69
   */
Packit Service 310c69
Packit Service 310c69
  int result = UDS_SUCCESS;
Packit Service 310c69
  if (zone->id == ZONE_ZERO) {
Packit Service 310c69
    // Purge invalid chapters from the LRU search list.
Packit Service 310c69
    SearchList *zoneZeroList = cache->searchLists[ZONE_ZERO];
Packit Service 310c69
    purgeSearchList(zoneZeroList, cache->chapters, zone->oldestVirtualChapter);
Packit Service 310c69
Packit Service 310c69
    // First check that the desired chapter is still in the volume. If it's
Packit Service 310c69
    // not, the hook fell out of the index and there's nothing to do for it.
Packit Service 310c69
    if (virtualChapter >= index->oldestVirtualChapter) {
Packit Service 310c69
      // Evict the least recently used live chapter, or replace a dead cache
Packit Service 310c69
      // entry, all by rotating the the last list entry to the front.
Packit Service 310c69
      CachedChapterIndex *victim
Packit Service 310c69
        = &cache->chapters[rotateSearchList(zoneZeroList, cache->capacity)];
Packit Service 310c69
Packit Service 310c69
      // Check if the victim is already dead, and if it's not, add to the
Packit Service 310c69
      // tally of evicted or invalidated cache entries.
Packit Service 310c69
      scoreEviction(zone, cache, victim);
Packit Service 310c69
Packit Service 310c69
      // Read the index page bytes and initialize the page array.
Packit Service 310c69
      result = cacheChapterIndex(victim, virtualChapter, index->volume);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    // Copy the new search list state to all the other zone threads so they'll
Packit Service 310c69
    // get the result of pruning and see the new chapter.
Packit Service 310c69
    unsigned int z;
Packit Service 310c69
    for (z = 1; z < cache->zoneCount; z++) {
Packit Service 310c69
      copySearchList(zoneZeroList, cache->searchLists[z]);
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // This is the end of the critical section. All cache invariants must have
Packit Service 310c69
  // been restored--it will be shared/read-only again beyond the barrier.
Packit Service 310c69
Packit Service 310c69
  enterBarrier(&cache->endCacheUpdate, NULL);
Packit Service 310c69
  return result;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int searchSparseCache(IndexZone          *zone,
Packit Service 310c69
                      const UdsChunkName *name,
Packit Service 310c69
                      uint64_t           *virtualChapterPtr,
Packit Service 310c69
                      int                *recordPagePtr)
Packit Service 310c69
{
Packit Service 310c69
  Volume *volume = zone->index->volume;
Packit Service 310c69
  SparseCache *cache = volume->sparseCache;
Packit Service 310c69
  unsigned int zoneNumber = zone->id;
Packit Service 310c69
  // If the caller did not specify a virtual chapter, search the entire cache.
Packit Service 310c69
  bool searchAll = (*virtualChapterPtr == UINT64_MAX);
Packit Service 310c69
  unsigned int chaptersSearched = 0;
Packit Service 310c69
Packit Service 310c69
  // Get the chapter search order for this zone thread, searching the chapters
Packit Service 310c69
  // from most recently hit to least recently hit.
Packit Service 310c69
  SearchListIterator iterator
Packit Service 310c69
    = iterateSearchList(cache->searchLists[zoneNumber], cache->chapters);
Packit Service 310c69
  while (hasNextChapter(&iterator)) {
Packit Service 310c69
    CachedChapterIndex *chapter = getNextChapter(&iterator);
Packit Service 310c69
Packit Service 310c69
    // Skip chapters no longer cached, or that have too many search misses.
Packit Service 310c69
    if (shouldSkipChapterIndex(zone, chapter, *virtualChapterPtr)) {
Packit Service 310c69
      continue;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    int result = searchCachedChapterIndex(chapter, cache->geometry,
Packit Service 310c69
                                          volume->indexPageMap, name,
Packit Service 310c69
                                          recordPagePtr);
Packit Service 310c69
    if (result != UDS_SUCCESS) {
Packit Service 310c69
      return result;
Packit Service 310c69
    }
Packit Service 310c69
    chaptersSearched += 1;
Packit Service 310c69
Packit Service 310c69
    // Did we find an index entry for the name?
Packit Service 310c69
    if (*recordPagePtr != NO_CHAPTER_INDEX_ENTRY) {
Packit Service 310c69
      if (zoneNumber == ZONE_ZERO) {
Packit Service 310c69
        scoreSearchHit(cache, chapter);
Packit Service 310c69
      }
Packit Service 310c69
Packit Service 310c69
      // Move the chapter to the front of the search list.
Packit Service 310c69
      rotateSearchList(iterator.list, iterator.nextEntry);
Packit Service 310c69
Packit Service 310c69
      // Return a matching entry as soon as it is found. It might be a false
Packit Service 310c69
      // collision that has a true match in another chapter, but that's a very
Packit Service 310c69
      // rare case and not worth the extra search cost or complexity.
Packit Service 310c69
      *virtualChapterPtr = chapter->virtualChapter;
Packit Service 310c69
      return UDS_SUCCESS;
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    if (zoneNumber == ZONE_ZERO) {
Packit Service 310c69
      scoreSearchMiss(cache, chapter);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    if (!searchAll) {
Packit Service 310c69
      // We just searched the virtual chapter the caller specified and there
Packit Service 310c69
      // was no match, so we're done.
Packit Service 310c69
      break;
Packit Service 310c69
    }
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // The name was not found in the cache.
Packit Service 310c69
  *recordPagePtr = NO_CHAPTER_INDEX_ENTRY;
Packit Service 310c69
  return UDS_SUCCESS;
Packit Service 310c69
}