|
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/pageCache.c#6 $
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "pageCache.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "atomicDefs.h"
|
|
Packit Service |
310c69 |
#include "cacheCounters.h"
|
|
Packit Service |
310c69 |
#include "chapterIndex.h"
|
|
Packit Service |
310c69 |
#include "compiler.h"
|
|
Packit Service |
310c69 |
#include "errors.h"
|
|
Packit Service |
310c69 |
#include "geometry.h"
|
|
Packit Service |
310c69 |
#include "hashUtils.h"
|
|
Packit Service |
310c69 |
#include "indexConfig.h"
|
|
Packit Service |
310c69 |
#include "logger.h"
|
|
Packit Service |
310c69 |
#include "memoryAlloc.h"
|
|
Packit Service |
310c69 |
#include "permassert.h"
|
|
Packit Service |
310c69 |
#include "recordPage.h"
|
|
Packit Service |
310c69 |
#include "stringUtils.h"
|
|
Packit Service |
310c69 |
#include "threads.h"
|
|
Packit Service |
310c69 |
#include "zone.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int assertPageInCache(PageCache *cache, CachedPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
int result = ASSERT((page->cp_physicalPage < cache->numIndexEntries),
|
|
Packit Service |
310c69 |
"physicalPage %u is valid (< %u)",
|
|
Packit Service |
310c69 |
page->cp_physicalPage, cache->numIndexEntries);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
uint16_t pageIndex = cache->index[page->cp_physicalPage];
|
|
Packit Service |
310c69 |
return ASSERT((pageIndex < cache->numCacheEntries)
|
|
Packit Service |
310c69 |
&& (&cache->cache[pageIndex] == page),
|
|
Packit Service |
310c69 |
"page is at expected location in cache");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Clear a cache page. Note: this does not clear readPending - a read could
|
|
Packit Service |
310c69 |
* still be pending and the read thread needs to be able to proceed and restart
|
|
Packit Service |
310c69 |
* the requests regardless. This page will still be marked invalid, but it
|
|
Packit Service |
310c69 |
* won't get reused (see getLeastRecentPage()) until the readPending flag
|
|
Packit Service |
310c69 |
* is cleared. This is a valid case, e.g. the chapter gets forgotten and
|
|
Packit Service |
310c69 |
* replaced with a new one in LRU. Restarting the requests will lead them to
|
|
Packit Service |
310c69 |
* not find the records in the MI.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cache the cache
|
|
Packit Service |
310c69 |
* @param page the cached page to clear
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void clearPage(PageCache *cache, CachedPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
page->cp_physicalPage = cache->numIndexEntries;
|
|
Packit Service |
310c69 |
WRITE_ONCE(page->cp_lastUsed, 0);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Get a page from the cache, but with no stats
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cache the cache
|
|
Packit Service |
310c69 |
* @param physicalPage the physical page to get
|
|
Packit Service |
310c69 |
* @param queueIndex the index of the page in the read queue if
|
|
Packit Service |
310c69 |
* queued, -1 otherwise
|
|
Packit Service |
310c69 |
* @param pagePtr a pointer to hold the page
|
|
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 getPageNoStats(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int physicalPage,
|
|
Packit Service |
310c69 |
int *queueIndex,
|
|
Packit Service |
310c69 |
CachedPage **pagePtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* ASSERTION: We are either a zone thread holding a searchPendingCounter,
|
|
Packit Service |
310c69 |
* or we are any thread holding the readThreadsMutex.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Holding only a searchPendingCounter is the most frequent case.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ASSERT((physicalPage < cache->numIndexEntries),
|
|
Packit Service |
310c69 |
"physical page %u is invalid", physicalPage);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* It would be unlikely that the compiler turns the usage of indexValue into
|
|
Packit Service |
310c69 |
* two reads of cache->index, but it would be possible and very bad if those
|
|
Packit Service |
310c69 |
* reads did not return the same bits.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
uint16_t indexValue = READ_ONCE(cache->index[physicalPage]);
|
|
Packit Service |
310c69 |
bool queued = (indexValue & VOLUME_CACHE_QUEUED_FLAG) != 0;
|
|
Packit Service |
310c69 |
uint16_t index = indexValue & ~VOLUME_CACHE_QUEUED_FLAG;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (!queued && (index < cache->numCacheEntries)) {
|
|
Packit Service |
310c69 |
*pagePtr = &cache->cache[index];
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* We have acquired access to the cached page, but unless we hold the
|
|
Packit Service |
310c69 |
* readThreadsMutex, we need a read memory barrier now. The corresponding
|
|
Packit Service |
310c69 |
* write memory barrier is in putPageInCache.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
smp_rmb();
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
*pagePtr = NULL;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (queueIndex != NULL) {
|
|
Packit Service |
310c69 |
*queueIndex = queued ? index : -1;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Wait for all pending searches on a page in the cache to complete
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cache the page cache
|
|
Packit Service |
310c69 |
* @param physicalPage the page to check searches on
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void waitForPendingSearches(PageCache *cache, unsigned int physicalPage)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* We hold the readThreadsMutex. We are waiting for threads that do not hold
|
|
Packit Service |
310c69 |
* the readThreadsMutex. Those threads have "locked" their targeted page by
|
|
Packit Service |
310c69 |
* setting the searchPendingCounter. The corresponding write memory barrier
|
|
Packit Service |
310c69 |
* is in beginPendingSearch.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
smp_mb();
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
InvalidateCounter initialCounters[MAX_ZONES];
|
|
Packit Service |
310c69 |
unsigned int i;
|
|
Packit Service |
310c69 |
for (i = 0; i < cache->zoneCount; i++) {
|
|
Packit Service |
310c69 |
initialCounters[i] = getInvalidateCounter(cache, i);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
for (i = 0; i < cache->zoneCount; i++) {
|
|
Packit Service |
310c69 |
if (searchPending(initialCounters[i])
|
|
Packit Service |
310c69 |
&& (pageBeingSearched(initialCounters[i]) == physicalPage)) {
|
|
Packit Service |
310c69 |
// There is an active search using the physical page.
|
|
Packit Service |
310c69 |
// We need to wait for the search to finish.
|
|
Packit Service |
310c69 |
while (initialCounters[i] == getInvalidateCounter(cache, i)) {
|
|
Packit Service |
310c69 |
yieldScheduler();
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Invalidate a cache page
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cache the cache
|
|
Packit Service |
310c69 |
* @param page the cached page
|
|
Packit Service |
310c69 |
* @param reason the reason for invalidation, for stats
|
|
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 invalidatePageInCache(PageCache *cache,
|
|
Packit Service |
310c69 |
CachedPage *page,
|
|
Packit Service |
310c69 |
InvalidationReason reason)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (page == NULL) {
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (page->cp_physicalPage != cache->numIndexEntries) {
|
|
Packit Service |
310c69 |
switch (reason) {
|
|
Packit Service |
310c69 |
case INVALIDATION_EVICT:
|
|
Packit Service |
310c69 |
cache->counters.evictions++;
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
case INVALIDATION_EXPIRE:
|
|
Packit Service |
310c69 |
cache->counters.expirations++;
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
default:
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (reason != INVALIDATION_ERROR) {
|
|
Packit Service |
310c69 |
int result = assertPageInCache(cache, page);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
WRITE_ONCE(cache->index[page->cp_physicalPage], cache->numCacheEntries);
|
|
Packit Service |
310c69 |
waitForPendingSearches(cache, page->cp_physicalPage);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
clearPage(cache, page);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int findInvalidateAndMakeLeastRecent(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int physicalPage,
|
|
Packit Service |
310c69 |
QueuedRead *readQueue,
|
|
Packit Service |
310c69 |
InvalidationReason reason,
|
|
Packit Service |
310c69 |
bool mustFind)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
CachedPage *page;
|
|
Packit Service |
310c69 |
int queuedIndex = -1;
|
|
Packit Service |
310c69 |
int result
|
|
Packit Service |
310c69 |
= getPageNoStats(cache, physicalPage,
|
|
Packit Service |
310c69 |
((readQueue != NULL) ? &queuedIndex : NULL), &page);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (page == NULL) {
|
|
Packit Service |
310c69 |
result = ASSERT(!mustFind, "found page");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (queuedIndex > -1) {
|
|
Packit Service |
310c69 |
logDebug("setting pending read to invalid");
|
|
Packit Service |
310c69 |
readQueue[queuedIndex].invalid = true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Invalidate the page and unmap it from the cache.
|
|
Packit Service |
310c69 |
result = invalidatePageInCache(cache, page, reason);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Move the cached page to the least recently used end of the list
|
|
Packit Service |
310c69 |
// so it will be replaced before any page with valid data.
|
|
Packit Service |
310c69 |
WRITE_ONCE(page->cp_lastUsed, 0);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
__attribute__((warn_unused_result))
|
|
Packit Service |
310c69 |
static int initializePageCache(PageCache *cache,
|
|
Packit Service |
310c69 |
const Geometry *geometry,
|
|
Packit Service |
310c69 |
unsigned int chaptersInCache,
|
|
Packit Service |
310c69 |
unsigned int readQueueMaxSize,
|
|
Packit Service |
310c69 |
unsigned int zoneCount)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
cache->geometry = geometry;
|
|
Packit Service |
310c69 |
cache->numIndexEntries = geometry->pagesPerVolume + 1;
|
|
Packit Service |
310c69 |
cache->numCacheEntries = chaptersInCache * geometry->recordPagesPerChapter;
|
|
Packit Service |
310c69 |
cache->readQueueMaxSize = readQueueMaxSize;
|
|
Packit Service |
310c69 |
cache->zoneCount = zoneCount;
|
|
Packit Service |
310c69 |
atomic64_set(&cache->clock, 1);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ALLOCATE(readQueueMaxSize, QueuedRead,
|
|
Packit Service |
310c69 |
"volume read queue", &cache->readQueue);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ALLOCATE(cache->zoneCount, SearchPendingCounter,
|
|
Packit Service |
310c69 |
"Volume Cache Zones", &cache->searchPendingCounters);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ASSERT((cache->numCacheEntries <= VOLUME_CACHE_MAX_ENTRIES),
|
|
Packit Service |
310c69 |
"requested cache size, %u, within limit %u",
|
|
Packit Service |
310c69 |
cache->numCacheEntries, VOLUME_CACHE_MAX_ENTRIES);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ALLOCATE(cache->numIndexEntries, uint16_t, "page cache index",
|
|
Packit Service |
310c69 |
&cache->index);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Initialize index values to invalid values.
|
|
Packit Service |
310c69 |
unsigned int i;
|
|
Packit Service |
310c69 |
for (i = 0; i < cache->numIndexEntries; i++) {
|
|
Packit Service |
310c69 |
cache->index[i] = cache->numCacheEntries;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ALLOCATE(cache->numCacheEntries, CachedPage,
|
|
Packit Service |
310c69 |
"page cache cache", &cache->cache);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
for (i = 0; i < cache->numCacheEntries; i++) {
|
|
Packit Service |
310c69 |
CachedPage *page = &cache->cache[i];
|
|
Packit Service |
310c69 |
result = initializeVolumePage(geometry, &page->cp_pageData);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
clearPage(cache, page);
|
|
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 makePageCache(const Geometry *geometry,
|
|
Packit Service |
310c69 |
unsigned int chaptersInCache,
|
|
Packit Service |
310c69 |
unsigned int readQueueMaxSize,
|
|
Packit Service |
310c69 |
unsigned int zoneCount,
|
|
Packit Service |
310c69 |
PageCache **cachePtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (chaptersInCache < 1) {
|
|
Packit Service |
310c69 |
return logWarningWithStringError(UDS_BAD_STATE,
|
|
Packit Service |
310c69 |
"cache size must be"
|
|
Packit Service |
310c69 |
" at least one chapter");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (readQueueMaxSize <= 0) {
|
|
Packit Service |
310c69 |
return logWarningWithStringError(UDS_INVALID_ARGUMENT,
|
|
Packit Service |
310c69 |
"read queue max size must be"
|
|
Packit Service |
310c69 |
" greater than 0");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (zoneCount < 1) {
|
|
Packit Service |
310c69 |
return logWarningWithStringError(UDS_INVALID_ARGUMENT,
|
|
Packit Service |
310c69 |
"cache must have at least one zone");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
PageCache *cache;
|
|
Packit Service |
310c69 |
int result = ALLOCATE(1, PageCache, "volume cache", &cache);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = initializePageCache(cache, geometry, chaptersInCache,
|
|
Packit Service |
310c69 |
readQueueMaxSize, zoneCount);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
freePageCache(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 |
void freePageCache(PageCache *cache)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (cache->cache != NULL) {
|
|
Packit Service |
310c69 |
unsigned int i;
|
|
Packit Service |
310c69 |
for (i = 0; i < cache->numCacheEntries; i++) {
|
|
Packit Service |
310c69 |
destroyVolumePage(&cache->cache[i].cp_pageData);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
FREE(cache->index);
|
|
Packit Service |
310c69 |
FREE(cache->cache);
|
|
Packit Service |
310c69 |
FREE(cache->searchPendingCounters);
|
|
Packit Service |
310c69 |
FREE(cache->readQueue);
|
|
Packit Service |
310c69 |
FREE(cache);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int invalidatePageCacheForChapter(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int chapter,
|
|
Packit Service |
310c69 |
unsigned int pagesPerChapter,
|
|
Packit Service |
310c69 |
InvalidationReason reason)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
if ((cache == NULL) || (cache->cache == NULL)) {
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result;
|
|
Packit Service |
310c69 |
unsigned int i;
|
|
Packit Service |
310c69 |
for (i = 0; i < pagesPerChapter; i++) {
|
|
Packit Service |
310c69 |
unsigned int physicalPage = 1 + (pagesPerChapter * chapter) + i;
|
|
Packit Service |
310c69 |
result = findInvalidateAndMakeLeastRecent(cache, physicalPage,
|
|
Packit Service |
310c69 |
cache->readQueue, reason, false);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*********************************************************************/
|
|
Packit Service |
310c69 |
void makePageMostRecent(PageCache *cache, CachedPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// ASSERTION: We are either a zone thread holding a searchPendingCounter,
|
|
Packit Service |
310c69 |
// or we are any thread holding the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (atomic64_read(&cache->clock) != READ_ONCE(page->cp_lastUsed)) {
|
|
Packit Service |
310c69 |
WRITE_ONCE(page->cp_lastUsed, atomic64_inc_return(&cache->clock));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Get the least recent valid page from the cache.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param cache the cache
|
|
Packit Service |
310c69 |
* @param pagePtr a pointer to hold the new page (will be set to NULL
|
|
Packit Service |
310c69 |
* if the page was not found)
|
|
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 getLeastRecentPage(PageCache *cache, CachedPage **pagePtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
int oldestIndex = 0;
|
|
Packit Service |
310c69 |
// Our first candidate is any page that does have a pending read. We ensure
|
|
Packit Service |
310c69 |
// above that there are more entries than read threads, so there must be one.
|
|
Packit Service |
310c69 |
unsigned int i;
|
|
Packit Service |
310c69 |
for (i = 0;; i++) {
|
|
Packit Service |
310c69 |
if (i >= cache->numCacheEntries) {
|
|
Packit Service |
310c69 |
// This should never happen.
|
|
Packit Service |
310c69 |
return ASSERT(false, "oldest page is not NULL");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (!cache->cache[i].cp_readPending) {
|
|
Packit Service |
310c69 |
oldestIndex = i;
|
|
Packit Service |
310c69 |
break;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
// Now find the least recently used page that does not have a pending read.
|
|
Packit Service |
310c69 |
for (i = 0; i < cache->numCacheEntries; i++) {
|
|
Packit Service |
310c69 |
if (!cache->cache[i].cp_readPending
|
|
Packit Service |
310c69 |
&& (READ_ONCE(cache->cache[i].cp_lastUsed)
|
|
Packit Service |
310c69 |
<= READ_ONCE(cache->cache[oldestIndex].cp_lastUsed))) {
|
|
Packit Service |
310c69 |
oldestIndex = i;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
*pagePtr = &cache->cache[oldestIndex];
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/***********************************************************************/
|
|
Packit Service |
310c69 |
int getPageFromCache(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int physicalPage,
|
|
Packit Service |
310c69 |
int probeType,
|
|
Packit Service |
310c69 |
CachedPage **pagePtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// ASSERTION: We are in a zone thread.
|
|
Packit Service |
310c69 |
// ASSERTION: We holding a searchPendingCounter or the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
return logWarningWithStringError(UDS_BAD_STATE,
|
|
Packit Service |
310c69 |
"cannot get page with NULL cache");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Get the cache page from the index
|
|
Packit Service |
310c69 |
CachedPage *page;
|
|
Packit Service |
310c69 |
int queueIndex = -1;
|
|
Packit Service |
310c69 |
int result = getPageNoStats(cache, physicalPage, &queueIndex, &page);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
CacheResultKind cacheResult = ((page != NULL)
|
|
Packit Service |
310c69 |
? CACHE_RESULT_HIT
|
|
Packit Service |
310c69 |
: ((queueIndex != -1)
|
|
Packit Service |
310c69 |
? CACHE_RESULT_QUEUED
|
|
Packit Service |
310c69 |
: CACHE_RESULT_MISS));
|
|
Packit Service |
310c69 |
incrementCacheCounter(&cache->counters, probeType, cacheResult);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (pagePtr != NULL) {
|
|
Packit Service |
310c69 |
*pagePtr = page;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/***********************************************************************/
|
|
Packit Service |
310c69 |
int enqueueRead(PageCache *cache, Request *request, unsigned int physicalPage)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
uint16_t first = cache->readQueueFirst;
|
|
Packit Service |
310c69 |
uint16_t last = cache->readQueueLast;
|
|
Packit Service |
310c69 |
uint16_t next = (last + 1) % cache->readQueueMaxSize;
|
|
Packit Service |
310c69 |
uint16_t readQueuePos;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if ((cache->index[physicalPage] & VOLUME_CACHE_QUEUED_FLAG) == 0) {
|
|
Packit Service |
310c69 |
/* Not seen before, add this to the read queue and mark it as queued */
|
|
Packit Service |
310c69 |
if (next == first) {
|
|
Packit Service |
310c69 |
/* queue is full */
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
/* fill the read queue entry */
|
|
Packit Service |
310c69 |
cache->readQueue[last].physicalPage = physicalPage;
|
|
Packit Service |
310c69 |
cache->readQueue[last].invalid = false;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/* point the cache index to it */
|
|
Packit Service |
310c69 |
readQueuePos = last;
|
|
Packit Service |
310c69 |
WRITE_ONCE(cache->index[physicalPage],
|
|
Packit Service |
310c69 |
readQueuePos | VOLUME_CACHE_QUEUED_FLAG);
|
|
Packit Service |
310c69 |
cache->readQueue[readQueuePos].requestList.first = NULL;
|
|
Packit Service |
310c69 |
cache->readQueue[readQueuePos].requestList.last = NULL;
|
|
Packit Service |
310c69 |
/* bump the last pointer */
|
|
Packit Service |
310c69 |
cache->readQueueLast = next;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
/* It's already queued, just add on to it */
|
|
Packit Service |
310c69 |
readQueuePos = cache->index[physicalPage] & ~VOLUME_CACHE_QUEUED_FLAG;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ASSERT((readQueuePos < cache->readQueueMaxSize),
|
|
Packit Service |
310c69 |
"queue is not overfull");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
request->nextRequest = NULL;
|
|
Packit Service |
310c69 |
if (cache->readQueue[readQueuePos].requestList.first == NULL) {
|
|
Packit Service |
310c69 |
cache->readQueue[readQueuePos].requestList.first = request;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
cache->readQueue[readQueuePos].requestList.last->nextRequest = request;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
cache->readQueue[readQueuePos].requestList.last = request;
|
|
Packit Service |
310c69 |
return UDS_QUEUED;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/***********************************************************************/
|
|
Packit Service |
310c69 |
bool reserveReadQueueEntry(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int *queuePos,
|
|
Packit Service |
310c69 |
Request **firstRequest,
|
|
Packit Service |
310c69 |
unsigned int *physicalPage,
|
|
Packit Service |
310c69 |
bool *invalid)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
uint16_t lastRead = cache->readQueueLastRead;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// No items to dequeue
|
|
Packit Service |
310c69 |
if (lastRead == cache->readQueueLast) {
|
|
Packit Service |
310c69 |
return false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
unsigned int pageNo = cache->readQueue[lastRead].physicalPage;
|
|
Packit Service |
310c69 |
bool isInvalid = cache->readQueue[lastRead].invalid;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
uint16_t indexValue = cache->index[pageNo];
|
|
Packit Service |
310c69 |
bool queued = (indexValue & VOLUME_CACHE_QUEUED_FLAG) != 0;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// ALB-1429 ... need to check to see if its still queued before resetting
|
|
Packit Service |
310c69 |
if (isInvalid && queued) {
|
|
Packit Service |
310c69 |
// invalidate cache index slot
|
|
Packit Service |
310c69 |
WRITE_ONCE(cache->index[pageNo], cache->numCacheEntries);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// If a sync read has taken this page, set invalid to true so we don't
|
|
Packit Service |
310c69 |
// overwrite, we simply just requeue requests.
|
|
Packit Service |
310c69 |
if (!queued) {
|
|
Packit Service |
310c69 |
isInvalid = true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
cache->readQueue[lastRead].reserved = true;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
*queuePos = lastRead;
|
|
Packit Service |
310c69 |
*firstRequest = cache->readQueue[lastRead].requestList.first;
|
|
Packit Service |
310c69 |
*physicalPage = pageNo;
|
|
Packit Service |
310c69 |
*invalid = isInvalid;
|
|
Packit Service |
310c69 |
cache->readQueueLastRead = (lastRead + 1) % cache->readQueueMaxSize;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/************************************************************************/
|
|
Packit Service |
310c69 |
void releaseReadQueueEntry(PageCache *cache, unsigned int queuePos)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
cache->readQueue[queuePos].reserved = false;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
uint16_t lastRead = cache->readQueueLastRead;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Move the readQueueFirst pointer along when we can
|
|
Packit Service |
310c69 |
while ((cache->readQueueFirst != lastRead)
|
|
Packit Service |
310c69 |
&& (!cache->readQueue[cache->readQueueFirst].reserved)) {
|
|
Packit Service |
310c69 |
cache->readQueueFirst =
|
|
Packit Service |
310c69 |
(cache->readQueueFirst + 1) % cache->readQueueMaxSize;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/***********************************************************************/
|
|
Packit Service |
310c69 |
int selectVictimInCache(PageCache *cache,
|
|
Packit Service |
310c69 |
CachedPage **pagePtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
return logWarningWithStringError(UDS_BAD_STATE,
|
|
Packit Service |
310c69 |
"cannot put page in NULL cache");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
CachedPage *page = NULL;
|
|
Packit Service |
310c69 |
int result = getLeastRecentPage(cache, &page);
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ASSERT((page != NULL), "least recent page was not NULL");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// If the page is currently being pointed to by the page map, clear
|
|
Packit Service |
310c69 |
// it from the page map, and update cache stats
|
|
Packit Service |
310c69 |
if (page->cp_physicalPage != cache->numIndexEntries) {
|
|
Packit Service |
310c69 |
cache->counters.evictions++;
|
|
Packit Service |
310c69 |
WRITE_ONCE(cache->index[page->cp_physicalPage], cache->numCacheEntries);
|
|
Packit Service |
310c69 |
waitForPendingSearches(cache, page->cp_physicalPage);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
page->cp_readPending = true;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
*pagePtr = page;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/***********************************************************************/
|
|
Packit Service |
310c69 |
int putPageInCache(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int physicalPage,
|
|
Packit Service |
310c69 |
CachedPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
return logWarningWithStringError(UDS_BAD_STATE,
|
|
Packit Service |
310c69 |
"cannot complete page in NULL cache");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ASSERT((page != NULL), "page to install exists");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ASSERT((page->cp_readPending),
|
|
Packit Service |
310c69 |
"page to install has a pending read");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
clearPage(cache, page);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
page->cp_physicalPage = physicalPage;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Figure out the index into the cache array using pointer arithmetic
|
|
Packit Service |
310c69 |
uint16_t value = page - cache->cache;
|
|
Packit Service |
310c69 |
result = ASSERT((value < cache->numCacheEntries), "cache index is valid");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
makePageMostRecent(cache, page);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
page->cp_readPending = false;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* We hold the readThreadsMutex, but we must have a write memory barrier
|
|
Packit Service |
310c69 |
* before making the CachedPage available to the readers that do not hold the
|
|
Packit Service |
310c69 |
* mutex. The corresponding read memory barrier is in getPageNoStats.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
smp_wmb();
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Point the page map to the new page. Will clear queued flag
|
|
Packit Service |
310c69 |
WRITE_ONCE(cache->index[physicalPage], value);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
return UDS_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/***********************************************************************/
|
|
Packit Service |
310c69 |
void cancelPageInCache(PageCache *cache,
|
|
Packit Service |
310c69 |
unsigned int physicalPage,
|
|
Packit Service |
310c69 |
CachedPage *page)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// We hold the readThreadsMutex.
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
logWarning("cannot cancel page in NULL cache");
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int result = ASSERT((page != NULL), "page to install exists");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = ASSERT((page->cp_readPending),
|
|
Packit Service |
310c69 |
"page to install has a pending read");
|
|
Packit Service |
310c69 |
if (result != UDS_SUCCESS) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
clearPage(cache, page);
|
|
Packit Service |
310c69 |
page->cp_readPending = false;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Clear the page map for the new page. Will clear queued flag
|
|
Packit Service |
310c69 |
WRITE_ONCE(cache->index[physicalPage], cache->numCacheEntries);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
size_t getPageCacheSize(PageCache *cache)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (cache == NULL) {
|
|
Packit Service |
310c69 |
return 0;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
return sizeof(DeltaIndexPage) * cache->numCacheEntries;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|