Blame source/vdo/base/hashZone.c

Packit Service 310c69
/*
Packit Service 310c69
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 310c69
 *
Packit Service 310c69
 * This program is free software; you can redistribute it and/or
Packit Service 310c69
 * modify it under the terms of the GNU General Public License
Packit Service 310c69
 * as published by the Free Software Foundation; either version 2
Packit Service 310c69
 * of the License, or (at your option) any later version.
Packit Service 310c69
 * 
Packit Service 310c69
 * This program is distributed in the hope that it will be useful,
Packit Service 310c69
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 310c69
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 310c69
 * GNU General Public License for more details.
Packit Service 310c69
 * 
Packit Service 310c69
 * You should have received a copy of the GNU General Public License
Packit Service 310c69
 * along with this program; if not, write to the Free Software
Packit Service 310c69
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 310c69
 * 02110-1301, USA. 
Packit Service 310c69
 *
Packit Service 310c69
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/hashZone.c#3 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
#include "hashZone.h"
Packit Service 310c69
Packit Service 310c69
#include "logger.h"
Packit Service 310c69
#include "memoryAlloc.h"
Packit Service 310c69
#include "numeric.h"
Packit Service 310c69
#include "permassert.h"
Packit Service 310c69
Packit Service 310c69
#include "constants.h"
Packit Service 310c69
#include "dataVIO.h"
Packit Service 310c69
#include "hashLock.h"
Packit Service 310c69
#include "hashLockInternals.h"
Packit Service 310c69
#include "pointerMap.h"
Packit Service 310c69
#include "ringNode.h"
Packit Service 310c69
#include "statistics.h"
Packit Service 310c69
#include "threadConfig.h"
Packit Service 310c69
#include "types.h"
Packit Service 310c69
#include "vdoInternal.h"
Packit Service 310c69
Packit Service 310c69
enum {
Packit Service 310c69
  LOCK_POOL_CAPACITY = MAXIMUM_USER_VIOS,
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * These fields are only modified by the locks sharing the hash zone thread,
Packit Service 310c69
 * but are queried by other threads.
Packit Service 310c69
 **/
Packit Service 310c69
typedef struct atomicHashLockStatistics {
Packit Service 310c69
  /** Number of times the UDS advice proved correct */
Packit Service 310c69
  Atomic64 dedupeAdviceValid;
Packit Service 310c69
Packit Service 310c69
  /** Number of times the UDS advice proved incorrect */
Packit Service 310c69
  Atomic64 dedupeAdviceStale;
Packit Service 310c69
Packit Service 310c69
  /** Number of writes with the same data as another in-flight write */
Packit Service 310c69
  Atomic64 concurrentDataMatches;
Packit Service 310c69
Packit Service 310c69
  /** Number of writes whose hash collided with an in-flight write */
Packit Service 310c69
  Atomic64 concurrentHashCollisions;
Packit Service 310c69
} AtomicHashLockStatistics;
Packit Service 310c69
Packit Service 310c69
struct hashZone {
Packit Service 310c69
  /** Which hash zone this is */
Packit Service 310c69
  ZoneCount zoneNumber;
Packit Service 310c69
Packit Service 310c69
  /** The thread ID for this zone */
Packit Service 310c69
  ThreadID threadID;
Packit Service 310c69
Packit Service 310c69
  /** Mapping from chunkName fields to HashLocks */
Packit Service 310c69
  PointerMap *hashLockMap;
Packit Service 310c69
Packit Service 310c69
  /** Ring containing all unused HashLocks */
Packit Service 310c69
  RingNode lockPool;
Packit Service 310c69
Packit Service 310c69
  /** Statistics shared by all hash locks in this zone */
Packit Service 310c69
  AtomicHashLockStatistics statistics;
Packit Service 310c69
Packit Service 310c69
  /** Array of all HashLocks */
Packit Service 310c69
  HashLock *lockArray;
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Implements PointerKeyComparator.
Packit Service 310c69
 **/
Packit Service 310c69
static bool compareKeys(const void *thisKey, const void *thatKey)
Packit Service 310c69
{
Packit Service 310c69
  // Null keys are not supported.
Packit Service 310c69
  return (memcmp(thisKey, thatKey, sizeof(UdsChunkName)) == 0);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Implements PointerKeyComparator.
Packit Service 310c69
 **/
Packit Service 310c69
static uint32_t hashKey(const void *key)
Packit Service 310c69
{
Packit Service 310c69
  const UdsChunkName *name = key;
Packit Service 310c69
  /*
Packit Service 310c69
   * Use a fragment of the chunk name as a hash code. It must not overlap with
Packit Service 310c69
   * fragments used elsewhere to ensure uniform distributions.
Packit Service 310c69
   */
Packit Service 310c69
  // XXX pick an offset in the chunk name that isn't used elsewhere
Packit Service 310c69
  return getUInt32LE(&name->name[4]);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
static inline HashLock *asHashLock(RingNode *poolNode)
Packit Service 310c69
{
Packit Service 310c69
  STATIC_ASSERT(offsetof(HashLock, poolNode) == 0);
Packit Service 310c69
  return (HashLock *) poolNode;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int makeHashZone(VDO *vdo, ZoneCount zoneNumber, HashZone **zonePtr)
Packit Service 310c69
{
Packit Service 310c69
  HashZone *zone;
Packit Service 310c69
  int result = ALLOCATE(1, HashZone, __func__, &zone);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = makePointerMap(LOCK_MAP_CAPACITY, 0, compareKeys, hashKey,
Packit Service 310c69
                          &zone->hashLockMap);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeHashZone(&zone);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  zone->zoneNumber = zoneNumber;
Packit Service 310c69
  zone->threadID   = getHashZoneThread(getThreadConfig(vdo), zoneNumber);
Packit Service 310c69
  initializeRing(&zone->lockPool);
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(LOCK_POOL_CAPACITY, HashLock, "HashLock array",
Packit Service 310c69
                    &zone->lockArray);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeHashZone(&zone);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  for (VIOCount i = 0; i < LOCK_POOL_CAPACITY; i++) {
Packit Service 310c69
    HashLock *lock = &zone->lockArray[i];
Packit Service 310c69
    initializeHashLock(lock);
Packit Service 310c69
    pushRingNode(&zone->lockPool, &lock->poolNode);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *zonePtr = zone;
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void freeHashZone(HashZone **zonePtr)
Packit Service 310c69
{
Packit Service 310c69
  if (*zonePtr == NULL) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  HashZone *zone = *zonePtr;
Packit Service 310c69
  freePointerMap(&zone->hashLockMap);
Packit Service 310c69
  FREE(zone->lockArray);
Packit Service 310c69
  FREE(zone);
Packit Service 310c69
  *zonePtr = NULL;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
ZoneCount getHashZoneNumber(const HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  return zone->zoneNumber;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
ThreadID getHashZoneThreadID(const HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  return zone->threadID;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
HashLockStatistics getHashZoneStatistics(const HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  const AtomicHashLockStatistics *atoms = &zone->statistics;
Packit Service 310c69
  return (HashLockStatistics) {
Packit Service 310c69
    .dedupeAdviceValid     = relaxedLoad64(&atoms->dedupeAdviceValid),
Packit Service 310c69
    .dedupeAdviceStale     = relaxedLoad64(&atoms->dedupeAdviceStale),
Packit Service 310c69
    .concurrentDataMatches = relaxedLoad64(&atoms->concurrentDataMatches),
Packit Service 310c69
    .concurrentHashCollisions
Packit Service 310c69
      = relaxedLoad64(&atoms->concurrentHashCollisions),
Packit Service 310c69
  };
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Return a hash lock to the zone's pool and null out the reference to it.
Packit Service 310c69
 *
Packit Service 310c69
 * @param [in]     zone     The zone from which the lock was borrowed
Packit Service 310c69
 * @param [in,out] lockPtr  The last reference to the lock being returned
Packit Service 310c69
 **/
Packit Service 310c69
static void returnHashLockToPool(HashZone *zone, HashLock **lockPtr)
Packit Service 310c69
{
Packit Service 310c69
  HashLock *lock = *lockPtr;
Packit Service 310c69
  *lockPtr = NULL;
Packit Service 310c69
Packit Service 310c69
  memset(lock, 0, sizeof(*lock));
Packit Service 310c69
  initializeHashLock(lock);
Packit Service 310c69
  pushRingNode(&zone->lockPool, &lock->poolNode);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int acquireHashLockFromZone(HashZone            *zone,
Packit Service 310c69
                            const UdsChunkName  *hash,
Packit Service 310c69
                            HashLock            *replaceLock,
Packit Service 310c69
                            HashLock           **lockPtr)
Packit Service 310c69
{
Packit Service 310c69
  // Borrow and prepare a lock from the pool so we don't have to do two
Packit Service 310c69
  // PointerMap accesses in the common case of no lock contention.
Packit Service 310c69
  HashLock *newLock = asHashLock(popRingNode(&zone->lockPool));
Packit Service 310c69
  int result = ASSERT(newLock != NULL,
Packit Service 310c69
                      "never need to wait for a free hash lock");
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Fill in the hash of the new lock so we can map it, since we have to use
Packit Service 310c69
  // the hash as the map key.
Packit Service 310c69
  newLock->hash = *hash;
Packit Service 310c69
Packit Service 310c69
  HashLock *lock;
Packit Service 310c69
  result = pointerMapPut(zone->hashLockMap, &newLock->hash, newLock,
Packit Service 310c69
                         (replaceLock != NULL), (void **) &lock);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    returnHashLockToPool(zone, &newLock);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (replaceLock != NULL) {
Packit Service 310c69
    // XXX on mismatch put the old lock back and return a severe error
Packit Service 310c69
    ASSERT_LOG_ONLY(lock == replaceLock,
Packit Service 310c69
                    "old lock must have been in the lock map");
Packit Service 310c69
    // XXX check earlier and bail out?
Packit Service 310c69
    ASSERT_LOG_ONLY(replaceLock->registered,
Packit Service 310c69
                    "old lock must have been marked registered");
Packit Service 310c69
    replaceLock->registered = false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (lock == replaceLock) {
Packit Service 310c69
    lock = newLock;
Packit Service 310c69
    lock->registered = true;
Packit Service 310c69
  } else {
Packit Service 310c69
    // There's already a lock for the hash, so we don't need the borrowed lock.
Packit Service 310c69
    returnHashLockToPool(zone, &newLock);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *lockPtr = lock;
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void returnHashLockToZone(HashZone *zone, HashLock **lockPtr)
Packit Service 310c69
{
Packit Service 310c69
  HashLock *lock = *lockPtr;
Packit Service 310c69
  *lockPtr = NULL;
Packit Service 310c69
Packit Service 310c69
  if (lock->registered) {
Packit Service 310c69
    HashLock *removed = pointerMapRemove(zone->hashLockMap, &lock->hash);
Packit Service 310c69
    ASSERT_LOG_ONLY(lock == removed,
Packit Service 310c69
                    "hash lock being released must have been mapped");
Packit Service 310c69
  } else {
Packit Service 310c69
    ASSERT_LOG_ONLY(lock != pointerMapGet(zone->hashLockMap, &lock->hash),
Packit Service 310c69
                    "unregistered hash lock must not be in the lock map");
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  ASSERT_LOG_ONLY(!hasWaiters(&lock->waiters),
Packit Service 310c69
                  "hash lock returned to zone must have no waiters");
Packit Service 310c69
  ASSERT_LOG_ONLY((lock->duplicateLock == NULL),
Packit Service 310c69
                  "hash lock returned to zone must not reference a PBN lock");
Packit Service 310c69
  ASSERT_LOG_ONLY((lock->state == HASH_LOCK_DESTROYING),
Packit Service 310c69
                  "returned hash lock must not be in use with state %s",
Packit Service 310c69
                  getHashLockStateName(lock->state));
Packit Service 310c69
  ASSERT_LOG_ONLY(isRingEmpty(&lock->poolNode),
Packit Service 310c69
                  "hash lock returned to zone must not be in a pool ring");
Packit Service 310c69
  ASSERT_LOG_ONLY(isRingEmpty(&lock->duplicateRing),
Packit Service 310c69
                  "hash lock returned to zone must not reference DataVIOs");
Packit Service 310c69
Packit Service 310c69
  returnHashLockToPool(zone, &lock);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Dump a compact description of HashLock to the log if the lock is not on the
Packit Service 310c69
 * free list.
Packit Service 310c69
 *
Packit Service 310c69
 * @param lock  The hash lock to dump
Packit Service 310c69
 **/
Packit Service 310c69
static void dumpHashLock(const HashLock *lock)
Packit Service 310c69
{
Packit Service 310c69
  if (!isRingEmpty(&lock->poolNode)) {
Packit Service 310c69
    // This lock is on the free list.
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  // Necessarily cryptic since we can log a lot of these. First three chars of
Packit Service 310c69
  // state is unambiguous. 'U' indicates a lock not registered in the map.
Packit Service 310c69
  const char *state = getHashLockStateName(lock->state);
Packit Service 310c69
  logInfo("  hl %" PRIptr ": %3.3s %c%llu/%u rc=%u wc=%zu agt=%" PRIptr,
Packit Service 310c69
          (const void *) lock,
Packit Service 310c69
          state,
Packit Service 310c69
          (lock->registered ? 'D' : 'U'),
Packit Service 310c69
          lock->duplicate.pbn,
Packit Service 310c69
          lock->duplicate.state,
Packit Service 310c69
          lock->referenceCount,
Packit Service 310c69
          countWaiters(&lock->waiters),
Packit Service 310c69
          (void *) lock->agent);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void bumpHashZoneValidAdviceCount(HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  // Must only be mutated on the hash zone thread.
Packit Service 310c69
  relaxedAdd64(&zone->statistics.dedupeAdviceValid, 1);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void bumpHashZoneStaleAdviceCount(HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  // Must only be mutated on the hash zone thread.
Packit Service 310c69
  relaxedAdd64(&zone->statistics.dedupeAdviceStale, 1);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void bumpHashZoneDataMatchCount(HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  // Must only be mutated on the hash zone thread.
Packit Service 310c69
  relaxedAdd64(&zone->statistics.concurrentDataMatches, 1);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void bumpHashZoneCollisionCount(HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  // Must only be mutated on the hash zone thread.
Packit Service 310c69
  relaxedAdd64(&zone->statistics.concurrentHashCollisions, 1);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void dumpHashZone(const HashZone *zone)
Packit Service 310c69
{
Packit Service 310c69
  if (zone->hashLockMap == NULL) {
Packit Service 310c69
    logInfo("HashZone %u: NULL map", zone->zoneNumber);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  logInfo("HashZone %u: mapSize=%zu",
Packit Service 310c69
          zone->zoneNumber, pointerMapSize(zone->hashLockMap));
Packit Service 310c69
  for (VIOCount i = 0; i < LOCK_POOL_CAPACITY; i++) {
Packit Service 310c69
    dumpHashLock(&zone->lockArray[i]);
Packit Service 310c69
  }
Packit Service 310c69
}