|
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/pbnLock.c#3 $
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "pbnLock.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "logger.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "blockAllocator.h"
|
|
Packit Service |
310c69 |
#include "referenceBlock.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
struct pbnLockImplementation {
|
|
Packit Service |
310c69 |
PBNLockType type;
|
|
Packit Service |
310c69 |
const char *name;
|
|
Packit Service |
310c69 |
const char *releaseReason;
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* This array must have an entry for every PBNLockType value.
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static const PBNLockImplementation LOCK_IMPLEMENTATIONS[] = {
|
|
Packit Service |
310c69 |
[VIO_READ_LOCK] = {
|
|
Packit Service |
310c69 |
.type = VIO_READ_LOCK,
|
|
Packit Service |
310c69 |
.name = "read",
|
|
Packit Service |
310c69 |
.releaseReason = "candidate duplicate",
|
|
Packit Service |
310c69 |
},
|
|
Packit Service |
310c69 |
[VIO_WRITE_LOCK] = {
|
|
Packit Service |
310c69 |
.type = VIO_WRITE_LOCK,
|
|
Packit Service |
310c69 |
.name = "write",
|
|
Packit Service |
310c69 |
.releaseReason = "newly allocated",
|
|
Packit Service |
310c69 |
},
|
|
Packit Service |
310c69 |
[VIO_COMPRESSED_WRITE_LOCK] = {
|
|
Packit Service |
310c69 |
.type = VIO_COMPRESSED_WRITE_LOCK,
|
|
Packit Service |
310c69 |
.name = "compressed write",
|
|
Packit Service |
310c69 |
.releaseReason = "failed compression",
|
|
Packit Service |
310c69 |
},
|
|
Packit Service |
310c69 |
[VIO_BLOCK_MAP_WRITE_LOCK] = {
|
|
Packit Service |
310c69 |
.type = VIO_BLOCK_MAP_WRITE_LOCK,
|
|
Packit Service |
310c69 |
.name = "block map write",
|
|
Packit Service |
310c69 |
.releaseReason = "block map write",
|
|
Packit Service |
310c69 |
},
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static inline bool hasLockType(const PBNLock *lock, PBNLockType type)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return (lock->implementation == &LOCK_IMPLEMENTATIONS[type]);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
bool isPBNReadLock(const PBNLock *lock)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return hasLockType(lock, VIO_READ_LOCK);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static inline void setPBNLockType(PBNLock *lock, PBNLockType type)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
lock->implementation = &LOCK_IMPLEMENTATIONS[type];
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void initializePBNLock(PBNLock *lock, PBNLockType type)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
lock->holderCount = 0;
|
|
Packit Service |
310c69 |
setPBNLockType(lock, type);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void downgradePBNWriteLock(PBNLock *lock)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(!isPBNReadLock(lock),
|
|
Packit Service |
310c69 |
"PBN lock must not already have been downgraded");
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(!hasLockType(lock, VIO_BLOCK_MAP_WRITE_LOCK),
|
|
Packit Service |
310c69 |
"must not downgrade block map write locks");
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(lock->holderCount == 1,
|
|
Packit Service |
310c69 |
"PBN write lock should have one holder but has %u",
|
|
Packit Service |
310c69 |
lock->holderCount);
|
|
Packit Service |
310c69 |
if (hasLockType(lock, VIO_WRITE_LOCK)) {
|
|
Packit Service |
310c69 |
// DataVIO write locks are downgraded in place--the writer retains the
|
|
Packit Service |
310c69 |
// hold on the lock. They've already had a single incRef journaled.
|
|
Packit Service |
310c69 |
lock->incrementLimit = MAXIMUM_REFERENCE_COUNT - 1;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
// Compressed block write locks are downgraded when they are shared with
|
|
Packit Service |
310c69 |
// all their hash locks. The writer is releasing its hold on the lock.
|
|
Packit Service |
310c69 |
lock->holderCount = 0;
|
|
Packit Service |
310c69 |
lock->incrementLimit = MAXIMUM_REFERENCE_COUNT;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
setPBNLockType(lock, VIO_READ_LOCK);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
bool claimPBNLockIncrement(PBNLock *lock)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Claim the next free reference atomically since hash locks from multiple
|
|
Packit Service |
310c69 |
* hash zone threads might be concurrently deduplicating against a single
|
|
Packit Service |
310c69 |
* PBN lock on compressed block. As long as hitting the increment limit will
|
|
Packit Service |
310c69 |
* lead to the PBN lock being released in a sane time-frame, we won't
|
|
Packit Service |
310c69 |
* overflow a 32-bit claim counter, allowing a simple add instead of a
|
|
Packit Service |
310c69 |
* compare-and-swap.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
uint32_t claimNumber = atomicAdd32(&lock->incrementsClaimed, 1);
|
|
Packit Service |
310c69 |
return (claimNumber <= lock->incrementLimit);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void assignProvisionalReference(PBNLock *lock)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(!lock->hasProvisionalReference,
|
|
Packit Service |
310c69 |
"lock does not have a provisional reference");
|
|
Packit Service |
310c69 |
lock->hasProvisionalReference = true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void unassignProvisionalReference(PBNLock *lock)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
lock->hasProvisionalReference = false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void releaseProvisionalReference(PBNLock *lock,
|
|
Packit Service |
310c69 |
PhysicalBlockNumber lockedPBN,
|
|
Packit Service |
310c69 |
BlockAllocator *allocator)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (hasProvisionalReference(lock)) {
|
|
Packit Service |
310c69 |
releaseBlockReference(allocator, lockedPBN,
|
|
Packit Service |
310c69 |
lock->implementation->releaseReason);
|
|
Packit Service |
310c69 |
unassignProvisionalReference(lock);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|