Blame source/vdo/base/pbnLock.c

Packit Service 75d76b
/*
Packit Service 75d76b
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 75d76b
 *
Packit Service 75d76b
 * This program is free software; you can redistribute it and/or
Packit Service 75d76b
 * modify it under the terms of the GNU General Public License
Packit Service 75d76b
 * as published by the Free Software Foundation; either version 2
Packit Service 75d76b
 * of the License, or (at your option) any later version.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * This program is distributed in the hope that it will be useful,
Packit Service 75d76b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 75d76b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 75d76b
 * GNU General Public License for more details.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * You should have received a copy of the GNU General Public License
Packit Service 75d76b
 * along with this program; if not, write to the Free Software
Packit Service 75d76b
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 75d76b
 * 02110-1301, USA. 
Packit Service 75d76b
 *
Packit Service 75d76b
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/pbnLock.c#3 $
Packit Service 75d76b
 */
Packit Service 75d76b
Packit Service 75d76b
#include "pbnLock.h"
Packit Service 75d76b
Packit Service 75d76b
#include "logger.h"
Packit Service 75d76b
Packit Service 75d76b
#include "blockAllocator.h"
Packit Service 75d76b
#include "referenceBlock.h"
Packit Service 75d76b
Packit Service 75d76b
struct pbnLockImplementation {
Packit Service 75d76b
  PBNLockType  type;
Packit Service 75d76b
  const char  *name;
Packit Service 75d76b
  const char  *releaseReason;
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * This array must have an entry for every PBNLockType value.
Packit Service 75d76b
 **/
Packit Service 75d76b
static const PBNLockImplementation LOCK_IMPLEMENTATIONS[] = {
Packit Service 75d76b
  [VIO_READ_LOCK] = {
Packit Service 75d76b
    .type          = VIO_READ_LOCK,
Packit Service 75d76b
    .name          = "read",
Packit Service 75d76b
    .releaseReason = "candidate duplicate",
Packit Service 75d76b
  },
Packit Service 75d76b
  [VIO_WRITE_LOCK] = {
Packit Service 75d76b
    .type          = VIO_WRITE_LOCK,
Packit Service 75d76b
    .name          = "write",
Packit Service 75d76b
    .releaseReason = "newly allocated",
Packit Service 75d76b
  },
Packit Service 75d76b
  [VIO_COMPRESSED_WRITE_LOCK] = {
Packit Service 75d76b
    .type          = VIO_COMPRESSED_WRITE_LOCK,
Packit Service 75d76b
    .name          = "compressed write",
Packit Service 75d76b
    .releaseReason = "failed compression",
Packit Service 75d76b
  },
Packit Service 75d76b
  [VIO_BLOCK_MAP_WRITE_LOCK] = {
Packit Service 75d76b
    .type          = VIO_BLOCK_MAP_WRITE_LOCK,
Packit Service 75d76b
    .name          = "block map write",
Packit Service 75d76b
    .releaseReason = "block map write",
Packit Service 75d76b
  },
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
static inline bool hasLockType(const PBNLock *lock, PBNLockType type)
Packit Service 75d76b
{
Packit Service 75d76b
  return (lock->implementation == &LOCK_IMPLEMENTATIONS[type]);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
bool isPBNReadLock(const PBNLock *lock)
Packit Service 75d76b
{
Packit Service 75d76b
  return hasLockType(lock, VIO_READ_LOCK);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
static inline void setPBNLockType(PBNLock *lock, PBNLockType type)
Packit Service 75d76b
{
Packit Service 75d76b
  lock->implementation = &LOCK_IMPLEMENTATIONS[type];
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void initializePBNLock(PBNLock *lock, PBNLockType type)
Packit Service 75d76b
{
Packit Service 75d76b
  lock->holderCount = 0;
Packit Service 75d76b
  setPBNLockType(lock, type);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void downgradePBNWriteLock(PBNLock *lock)
Packit Service 75d76b
{
Packit Service 75d76b
  ASSERT_LOG_ONLY(!isPBNReadLock(lock),
Packit Service 75d76b
                  "PBN lock must not already have been downgraded");
Packit Service 75d76b
  ASSERT_LOG_ONLY(!hasLockType(lock, VIO_BLOCK_MAP_WRITE_LOCK),
Packit Service 75d76b
                  "must not downgrade block map write locks");
Packit Service 75d76b
  ASSERT_LOG_ONLY(lock->holderCount == 1,
Packit Service 75d76b
                  "PBN write lock should have one holder but has %u",
Packit Service 75d76b
                  lock->holderCount);
Packit Service 75d76b
  if (hasLockType(lock, VIO_WRITE_LOCK)) {
Packit Service 75d76b
    // DataVIO write locks are downgraded in place--the writer retains the
Packit Service 75d76b
    // hold on the lock. They've already had a single incRef journaled.
Packit Service 75d76b
    lock->incrementLimit = MAXIMUM_REFERENCE_COUNT - 1;
Packit Service 75d76b
  } else {
Packit Service 75d76b
    // Compressed block write locks are downgraded when they are shared with
Packit Service 75d76b
    // all their hash locks. The writer is releasing its hold on the lock.
Packit Service 75d76b
    lock->holderCount = 0;
Packit Service 75d76b
    lock->incrementLimit = MAXIMUM_REFERENCE_COUNT;
Packit Service 75d76b
  }
Packit Service 75d76b
  setPBNLockType(lock, VIO_READ_LOCK);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
bool claimPBNLockIncrement(PBNLock *lock)
Packit Service 75d76b
{
Packit Service 75d76b
  /*
Packit Service 75d76b
   * Claim the next free reference atomically since hash locks from multiple
Packit Service 75d76b
   * hash zone threads might be concurrently deduplicating against a single
Packit Service 75d76b
   * PBN lock on compressed block. As long as hitting the increment limit will
Packit Service 75d76b
   * lead to the PBN lock being released in a sane time-frame, we won't
Packit Service 75d76b
   * overflow a 32-bit claim counter, allowing a simple add instead of a
Packit Service 75d76b
   * compare-and-swap.
Packit Service 75d76b
   */
Packit Service 75d76b
  uint32_t claimNumber = atomicAdd32(&lock->incrementsClaimed, 1);
Packit Service 75d76b
  return (claimNumber <= lock->incrementLimit);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void assignProvisionalReference(PBNLock *lock)
Packit Service 75d76b
{
Packit Service 75d76b
  ASSERT_LOG_ONLY(!lock->hasProvisionalReference,
Packit Service 75d76b
                  "lock does not have a provisional reference");
Packit Service 75d76b
  lock->hasProvisionalReference = true;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void unassignProvisionalReference(PBNLock *lock)
Packit Service 75d76b
{
Packit Service 75d76b
  lock->hasProvisionalReference = false;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void releaseProvisionalReference(PBNLock             *lock,
Packit Service 75d76b
                                 PhysicalBlockNumber  lockedPBN,
Packit Service 75d76b
                                 BlockAllocator      *allocator)
Packit Service 75d76b
{
Packit Service 75d76b
  if (hasProvisionalReference(lock)) {
Packit Service 75d76b
    releaseBlockReference(allocator, lockedPBN,
Packit Service 75d76b
                          lock->implementation->releaseReason);
Packit Service 75d76b
    unassignProvisionalReference(lock);
Packit Service 75d76b
  }
Packit Service 75d76b
}