|
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 |
}
|