Blame source/uds/pageCache.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/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