Blame source/vdo/base/lockCounter.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/lockCounter.c#3 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
#include "lockCounter.h"
Packit Service 310c69
Packit Service 310c69
#include "atomic.h"
Packit Service 310c69
#include "memoryAlloc.h"
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * LockCounter is intended to keep all of the locks for the blocks in the
Packit Service 310c69
 * recovery journal. The per-zone counters are all kept in a single array which
Packit Service 310c69
 * is arranged by zone (i.e. zone 0's lock 0 is at index 0, zone 0's lock 1 is
Packit Service 310c69
 * at index 1, and zone 1's lock 0 is at index 'locks'.  This arrangement is
Packit Service 310c69
 * intended to minimize cache-line contention for counters from different
Packit Service 310c69
 * zones.
Packit Service 310c69
 *
Packit Service 310c69
 * The locks are implemented as a single object instead of as a lock counter
Packit Service 310c69
 * per lock both to afford this opportunity to reduce cache line contention and
Packit Service 310c69
 * also to eliminate the need to have a completion per lock.
Packit Service 310c69
 *
Packit Service 310c69
 * Lock sets are laid out with the set for recovery journal first, followed by
Packit Service 310c69
 * the logical zones, and then the physical zones.
Packit Service 310c69
 **/
Packit Service 310c69
typedef enum lockCounterState {
Packit Service 310c69
  LOCK_COUNTER_STATE_NOT_NOTIFYING = 0,
Packit Service 310c69
  LOCK_COUNTER_STATE_NOTIFYING,
Packit Service 310c69
  LOCK_COUNTER_STATE_SUSPENDED,
Packit Service 310c69
} LockCounterState;
Packit Service 310c69
Packit Service 310c69
struct lockCounter {
Packit Service 310c69
  /** The completion for notifying the owner of a lock release */
Packit Service 310c69
  VDOCompletion  completion;
Packit Service 310c69
  /** The number of logical zones which may hold locks */
Packit Service 310c69
  ZoneCount      logicalZones;
Packit Service 310c69
  /** The number of physical zones which may hold locks */
Packit Service 310c69
  ZoneCount      physicalZones;
Packit Service 310c69
  /** The number of locks */
Packit Service 310c69
  BlockCount     locks;
Packit Service 310c69
  /** Whether the lock release notification is in flight */
Packit Service 310c69
  Atomic32       state;
Packit Service 310c69
  /** The number of logical zones which hold each lock */
Packit Service 310c69
  Atomic32      *logicalZoneCounts;
Packit Service 310c69
  /** The number of physical zones which hold each lock */
Packit Service 310c69
  Atomic32      *physicalZoneCounts;
Packit Service 310c69
  /** The per-zone, per-lock counts for the journal zone */
Packit Service 310c69
  uint16_t      *journalCounters;
Packit Service 310c69
  /** The per-zone, per-lock decrement counts for the journal zone */
Packit Service 310c69
  Atomic32      *journalDecrementCounts;
Packit Service 310c69
  /** The per-zone, per-lock reference counts for logical zones */
Packit Service 310c69
  uint16_t      *logicalCounters;
Packit Service 310c69
  /** The per-zone, per-lock reference counts for physical zones */
Packit Service 310c69
  uint16_t      *physicalCounters;
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int makeLockCounter(PhysicalLayer  *layer,
Packit Service 310c69
                    void           *parent,
Packit Service 310c69
                    VDOAction       callback,
Packit Service 310c69
                    ThreadID        threadID,
Packit Service 310c69
                    ZoneCount       logicalZones,
Packit Service 310c69
                    ZoneCount       physicalZones,
Packit Service 310c69
                    BlockCount      locks,
Packit Service 310c69
                    LockCounter   **lockCounterPtr)
Packit Service 310c69
{
Packit Service 310c69
  LockCounter *lockCounter;
Packit Service 310c69
Packit Service 310c69
  int result = ALLOCATE(1, LockCounter, __func__, &lockCounter);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(locks, uint16_t, __func__, &lockCounter->journalCounters);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(locks, Atomic32, __func__,
Packit Service 310c69
                    &lockCounter->journalDecrementCounts);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(locks * logicalZones, uint16_t, __func__,
Packit Service 310c69
                    &lockCounter->logicalCounters);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(locks, Atomic32, __func__,
Packit Service 310c69
                    &lockCounter->logicalZoneCounts);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(locks * physicalZones, uint16_t, __func__,
Packit Service 310c69
                    &lockCounter->physicalCounters);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = ALLOCATE(locks, Atomic32, __func__,
Packit Service 310c69
                    &lockCounter->physicalZoneCounts);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  result = initializeEnqueueableCompletion(&lockCounter->completion,
Packit Service 310c69
                                           LOCK_COUNTER_COMPLETION, layer);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeLockCounter(&lockCounter);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  setCallbackWithParent(&lockCounter->completion, callback, threadID, parent);
Packit Service 310c69
  lockCounter->logicalZones  = logicalZones;
Packit Service 310c69
  lockCounter->physicalZones = physicalZones;
Packit Service 310c69
  lockCounter->locks         = locks;
Packit Service 310c69
  *lockCounterPtr            = lockCounter;
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void freeLockCounter(LockCounter **lockCounterPtr)
Packit Service 310c69
{
Packit Service 310c69
  if (*lockCounterPtr == NULL) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  LockCounter *lockCounter = *lockCounterPtr;
Packit Service 310c69
  destroyEnqueueable(&lockCounter->completion);
Packit Service 310c69
  freeVolatile(lockCounter->physicalZoneCounts);
Packit Service 310c69
  freeVolatile(lockCounter->logicalZoneCounts);
Packit Service 310c69
  freeVolatile(lockCounter->journalDecrementCounts);
Packit Service 310c69
  FREE(lockCounter->journalCounters);
Packit Service 310c69
  FREE(lockCounter->logicalCounters);
Packit Service 310c69
  FREE(lockCounter->physicalCounters);
Packit Service 310c69
  FREE(lockCounter);
Packit Service 310c69
  *lockCounterPtr = NULL;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Get a pointer to the zone count for a given lock on a given zone.
Packit Service 310c69
 *
Packit Service 310c69
 * @param counter     The lock counter
Packit Service 310c69
 * @param lockNumber  The lock to get
Packit Service 310c69
 * @param zoneType    The zone type whose count is desired
Packit Service 310c69
 *
Packit Service 310c69
 * @return A pointer to the zone count for the given lock and zone
Packit Service 310c69
 **/
Packit Service 310c69
static inline Atomic32 *getZoneCountPtr(LockCounter *counter,
Packit Service 310c69
                                        BlockCount   lockNumber,
Packit Service 310c69
                                        ZoneType     zoneType)
Packit Service 310c69
{
Packit Service 310c69
  return ((zoneType == ZONE_TYPE_LOGICAL)
Packit Service 310c69
          ? &counter->logicalZoneCounts[lockNumber]
Packit Service 310c69
          : &counter->physicalZoneCounts[lockNumber]);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Get the zone counter for a given lock on a given zone.
Packit Service 310c69
 *
Packit Service 310c69
 * @param counter     The lock counter
Packit Service 310c69
 * @param lockNumber  The lock to get
Packit Service 310c69
 * @param zoneType    The zone type whose count is desired
Packit Service 310c69
 * @param zoneID      The zone index whose count is desired
Packit Service 310c69
 *
Packit Service 310c69
 * @return The counter for the given lock and zone
Packit Service 310c69
 **/
Packit Service 310c69
static inline uint16_t *getCounter(LockCounter *counter,
Packit Service 310c69
                                   BlockCount   lockNumber,
Packit Service 310c69
                                   ZoneType     zoneType,
Packit Service 310c69
                                   ZoneCount    zoneID)
Packit Service 310c69
{
Packit Service 310c69
  BlockCount zoneCounter = (counter->locks * zoneID) + lockNumber;
Packit Service 310c69
  if (zoneType == ZONE_TYPE_JOURNAL) {
Packit Service 310c69
    return &counter->journalCounters[zoneCounter];
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (zoneType == ZONE_TYPE_LOGICAL) {
Packit Service 310c69
    return &counter->logicalCounters[zoneCounter];
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return &counter->physicalCounters[zoneCounter];
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Check whether the journal zone is locked for a given lock.
Packit Service 310c69
 *
Packit Service 310c69
 * @param counter     The LockCounter
Packit Service 310c69
 * @param lockNumber  The lock to check
Packit Service 310c69
 *
Packit Service 310c69
 * @return true if the journal zone is locked
Packit Service 310c69
 **/
Packit Service 310c69
static bool isJournalZoneLocked(LockCounter *counter, BlockCount lockNumber)
Packit Service 310c69
{
Packit Service 310c69
  uint16_t journalValue
Packit Service 310c69
    = *(getCounter(counter, lockNumber, ZONE_TYPE_JOURNAL, 0));
Packit Service 310c69
  uint32_t decrements
Packit Service 310c69
    = atomicLoad32(&(counter->journalDecrementCounts[lockNumber]));
Packit Service 310c69
  ASSERT_LOG_ONLY((decrements <= journalValue),
Packit Service 310c69
                  "journal zone lock counter must not underflow");
Packit Service 310c69
Packit Service 310c69
  return (journalValue != decrements);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool isLocked(LockCounter *lockCounter,
Packit Service 310c69
              BlockCount   lockNumber,
Packit Service 310c69
              ZoneType     zoneType)
Packit Service 310c69
{
Packit Service 310c69
  ASSERT_LOG_ONLY((zoneType != ZONE_TYPE_JOURNAL),
Packit Service 310c69
                  "isLocked() called for non-journal zone");
Packit Service 310c69
  return (isJournalZoneLocked(lockCounter, lockNumber)
Packit Service 310c69
          || (atomicLoad32(getZoneCountPtr(lockCounter, lockNumber, zoneType))
Packit Service 310c69
              != 0));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Check that we are on the journal thread.
Packit Service 310c69
 *
Packit Service 310c69
 * @param counter  The LockCounter
Packit Service 310c69
 * @param caller   The name of the caller (for logging)
Packit Service 310c69
 **/
Packit Service 310c69
static void assertOnJournalThread(LockCounter *counter, const char *caller)
Packit Service 310c69
{
Packit Service 310c69
  ASSERT_LOG_ONLY((getCallbackThreadID()
Packit Service 310c69
                   == counter->completion.callbackThreadID),
Packit Service 310c69
                  "%s() called from journal zone", caller);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void initializeLockCount(LockCounter *counter,
Packit Service 310c69
                         BlockCount   lockNumber,
Packit Service 310c69
                         uint16_t     value)
Packit Service 310c69
{
Packit Service 310c69
  assertOnJournalThread(counter, __func__);
Packit Service 310c69
  uint16_t *journalValue   = getCounter(counter, lockNumber, ZONE_TYPE_JOURNAL,
Packit Service 310c69
                                        0);
Packit Service 310c69
  Atomic32 *decrementCount = &(counter->journalDecrementCounts[lockNumber]);
Packit Service 310c69
  ASSERT_LOG_ONLY((*journalValue == atomicLoad32(decrementCount)),
Packit Service 310c69
                  "count to be initialized not in use");
Packit Service 310c69
Packit Service 310c69
  *journalValue = value;
Packit Service 310c69
  atomicStore32(decrementCount, 0);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void acquireLockCountReference(LockCounter *counter,
Packit Service 310c69
                               BlockCount   lockNumber,
Packit Service 310c69
                               ZoneType     zoneType,
Packit Service 310c69
                               ZoneCount    zoneID)
Packit Service 310c69
{
Packit Service 310c69
  ASSERT_LOG_ONLY((zoneType != ZONE_TYPE_JOURNAL),
Packit Service 310c69
                  "invalid lock count increment from journal zone");
Packit Service 310c69
Packit Service 310c69
  uint16_t *currentValue = getCounter(counter, lockNumber, zoneType, zoneID);
Packit Service 310c69
  ASSERT_LOG_ONLY(*currentValue < UINT16_MAX,
Packit Service 310c69
                  "increment of lock counter must not overflow");
Packit Service 310c69
Packit Service 310c69
  if (*currentValue == 0) {
Packit Service 310c69
    // This zone is acquiring this lock for the first time.
Packit Service 310c69
    atomicAdd32(getZoneCountPtr(counter, lockNumber, zoneType), 1);
Packit Service 310c69
  }
Packit Service 310c69
  *currentValue += 1;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Decrement a non-atomic counter.
Packit Service 310c69
 *
Packit Service 310c69
 * @param counter     The LockCounter
Packit Service 310c69
 * @param lockNumber  Which lock to decrement
Packit Service 310c69
 * @param zoneType    The type of the zone releasing the reference
Packit Service 310c69
 * @param zoneID      The ID of the zone releasing the reference
Packit Service 310c69
 *
Packit Service 310c69
 * @return The new value of the counter
Packit Service 310c69
 **/
Packit Service 310c69
static uint16_t releaseReference(LockCounter *counter,
Packit Service 310c69
                                 BlockCount   lockNumber,
Packit Service 310c69
                                 ZoneType     zoneType,
Packit Service 310c69
                                 ZoneCount    zoneID)
Packit Service 310c69
{
Packit Service 310c69
  uint16_t *currentValue = getCounter(counter, lockNumber, zoneType, zoneID);
Packit Service 310c69
  ASSERT_LOG_ONLY((*currentValue >= 1),
Packit Service 310c69
                  "decrement of lock counter must not underflow");
Packit Service 310c69
Packit Service 310c69
  *currentValue -= 1;
Packit Service 310c69
  return *currentValue;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Attempt to notify the owner of this LockCounter that some lock has been
Packit Service 310c69
 * released for some zone type. Will do nothing if another notification is
Packit Service 310c69
 * already in progress.
Packit Service 310c69
 *
Packit Service 310c69
 * @param counter  The LockCounter
Packit Service 310c69
 **/
Packit Service 310c69
static void attemptNotification(LockCounter *counter)
Packit Service 310c69
{
Packit Service 310c69
  if (compareAndSwap32(&counter->state,
Packit Service 310c69
                       LOCK_COUNTER_STATE_NOT_NOTIFYING,
Packit Service 310c69
                       LOCK_COUNTER_STATE_NOTIFYING)) {
Packit Service 310c69
    resetCompletion(&counter->completion);
Packit Service 310c69
    invokeCallback(&counter->completion);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void releaseLockCountReference(LockCounter *counter,
Packit Service 310c69
                               BlockCount   lockNumber,
Packit Service 310c69
                               ZoneType     zoneType,
Packit Service 310c69
                               ZoneCount    zoneID)
Packit Service 310c69
{
Packit Service 310c69
  ASSERT_LOG_ONLY((zoneType != ZONE_TYPE_JOURNAL),
Packit Service 310c69
                  "invalid lock count decrement from journal zone");
Packit Service 310c69
  if (releaseReference(counter, lockNumber, zoneType, zoneID) != 0) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (atomicAdd32(getZoneCountPtr(counter, lockNumber, zoneType), -1) == 0) {
Packit Service 310c69
    // This zone was the last lock holder of its type, so try to notify the
Packit Service 310c69
    // owner.
Packit Service 310c69
    attemptNotification(counter);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void releaseJournalZoneReference(LockCounter *counter, BlockCount lockNumber)
Packit Service 310c69
{
Packit Service 310c69
  assertOnJournalThread(counter, __func__);
Packit Service 310c69
  releaseReference(counter, lockNumber, ZONE_TYPE_JOURNAL, 0);
Packit Service 310c69
  if (!isJournalZoneLocked(counter, lockNumber)) {
Packit Service 310c69
    // The journal zone is not locked, so try to notify the owner.
Packit Service 310c69
    attemptNotification(counter);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void releaseJournalZoneReferenceFromOtherZone(LockCounter *counter,
Packit Service 310c69
                                              BlockCount   lockNumber)
Packit Service 310c69
{
Packit Service 310c69
  atomicAdd32(&(counter->journalDecrementCounts[lockNumber]), 1);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void acknowledgeUnlock(LockCounter *counter)
Packit Service 310c69
{
Packit Service 310c69
  atomicStore32(&counter->state, LOCK_COUNTER_STATE_NOT_NOTIFYING);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool suspendLockCounter(LockCounter *counter)
Packit Service 310c69
{
Packit Service 310c69
  assertOnJournalThread(counter, __func__);
Packit Service 310c69
  return ((atomicLoad32(&counter->state) == LOCK_COUNTER_STATE_SUSPENDED)
Packit Service 310c69
          || compareAndSwap32(&counter->state,
Packit Service 310c69
                              LOCK_COUNTER_STATE_NOT_NOTIFYING,
Packit Service 310c69
                              LOCK_COUNTER_STATE_SUSPENDED));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool resumeLockCounter(LockCounter *counter)
Packit Service 310c69
{
Packit Service 310c69
  assertOnJournalThread(counter, __func__);
Packit Service 310c69
  return compareAndSwap32(&counter->state,
Packit Service 310c69
                          LOCK_COUNTER_STATE_SUSPENDED,
Packit Service 310c69
                          LOCK_COUNTER_STATE_NOT_NOTIFYING);
Packit Service 310c69
}