Blame source/vdo/base/pbnLock.c

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