/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/allocatingVIO.h#4 $
*/
#ifndef ALLOCATING_VIO_H
#define ALLOCATING_VIO_H
#include "atomic.h"
#include "pbnLock.h"
#include "physicalZone.h"
#include "types.h"
#include "vio.h"
#include "waitQueue.h"
typedef void AllocationCallback(AllocatingVIO *allocationVIO);
/**
* A VIO which can receive an allocation from the block allocator. Currently,
* these are used both for servicing external data requests and for compressed
* block writes.
**/
struct allocatingVIO {
/** The underlying VIO */
VIO vio;
/** The WaitQueue entry structure */
Waiter waiter;
/** The physical zone in which to allocate a physical block */
PhysicalZone *zone;
/** The block allocated to this VIO */
PhysicalBlockNumber allocation;
/**
* If non-NULL, the pooled PBN lock held on the allocated block. Must be a
* write lock until the block has been written, after which it will become a
* read lock.
**/
PBNLock *allocationLock;
/** The type of write lock to obtain on the allocated block */
PBNLockType writeLockType;
/** The number of zones in which this VIO has attempted an allocation */
ZoneCount allocationAttempts;
/** Whether this VIO should wait for a clean slab */
bool waitForCleanSlab;
/** The function to call once allocation is complete */
AllocationCallback *allocationCallback;
};
/**
* Convert a VIO to an AllocatingVIO.
*
* @param vio The VIO to convert
*
* @return The VIO as an AllocatingVIO
**/
static inline AllocatingVIO *vioAsAllocatingVIO(VIO *vio)
{
STATIC_ASSERT(offsetof(AllocatingVIO, vio) == 0);
ASSERT_LOG_ONLY(((vio->type == VIO_TYPE_DATA)
|| (vio->type == VIO_TYPE_COMPRESSED_BLOCK)),
"VIO is an AllocatingVIO");
return (AllocatingVIO *) vio;
}
/**
* Convert an AllocatingVIO to a VIO.
*
* @param allocatingVIO The AllocatingVIO to convert
*
* @return The AllocatingVIO as a VIO
**/
static inline VIO *allocatingVIOAsVIO(AllocatingVIO *allocatingVIO)
{
return &allocatingVIO->vio;
}
/**
* Convert a generic VDOCompletion to an AllocatingVIO.
*
* @param completion The completion to convert
*
* @return The completion as an AllocatingVIO
**/
static inline AllocatingVIO *asAllocatingVIO(VDOCompletion *completion)
{
return vioAsAllocatingVIO(asVIO(completion));
}
/**
* Convert an AllocatingVIO to a generic completion.
*
* @param allocatingVIO The AllocatingVIO to convert
*
* @return The AllocatingVIO as a completion
**/
static inline
VDOCompletion *allocatingVIOAsCompletion(AllocatingVIO *allocatingVIO)
{
return vioAsCompletion(allocatingVIOAsVIO(allocatingVIO));
}
/**
* Convert an AllocatingVIO to a generic wait queue entry.
*
* @param allocatingVIO The AllocatingVIO to convert
*
* @return The AllocatingVIO as a wait queue entry
**/
static inline Waiter *allocatingVIOAsWaiter(AllocatingVIO *allocatingVIO)
{
return &allocatingVIO->waiter;
}
/**
* Convert an AllocatingVIO's generic wait queue entry back to the
* AllocatingVIO.
*
* @param waiter The wait queue entry to convert
*
* @return The wait queue entry as an AllocatingVIO
**/
static inline AllocatingVIO *waiterAsAllocatingVIO(Waiter *waiter)
{
if (waiter == NULL) {
return NULL;
}
return
(AllocatingVIO *) ((uintptr_t) waiter - offsetof(AllocatingVIO, waiter));
}
/**
* Check whether an AllocatingVIO is a compressed block write.
*
* @param allocatingVIO The AllocatingVIO to check
*
* @return <code>true</code> if the AllocatingVIO is a compressed block write
**/
static inline bool isCompressedWriteAllocatingVIO(AllocatingVIO *allocatingVIO)
{
return isCompressedWriteVIO(allocatingVIOAsVIO(allocatingVIO));
}
/**
* Add a trace record for the current source location.
*
* @param allocatingVIO The AllocatingVIO structure to be updated
* @param location The source-location descriptor to be recorded
**/
static inline void allocatingVIOAddTraceRecord(AllocatingVIO *allocatingVIO,
TraceLocation location)
{
vioAddTraceRecord(allocatingVIOAsVIO(allocatingVIO), location);
}
/**
* Get the VDO from an AllocatingVIO.
*
* @param allocatingVIO The AllocatingVIO from which to get the VDO
*
* @return The VDO to which an AllocatingVIO belongs
**/
static inline VDO *getVDOFromAllocatingVIO(AllocatingVIO *allocatingVIO)
{
return allocatingVIOAsVIO(allocatingVIO)->vdo;
}
/**
* Check that an AllocatingVIO is running on the physical zone thread in
* which it did its allocation.
*
* @param allocatingVIO The AllocatingVIO in question
**/
static inline void assertInPhysicalZone(AllocatingVIO *allocatingVIO)
{
ThreadID expected = getPhysicalZoneThreadID(allocatingVIO->zone);
ThreadID threadID = getCallbackThreadID();
ASSERT_LOG_ONLY((expected == threadID),
"AllocatingVIO for allocated physical block %" PRIu64
" on thread %u, should be on thread %u",
allocatingVIO->allocation, threadID, expected);
}
/**
* Set a callback as a physical block operation in an AllocatingVIO's allocated
* zone.
*
* @param allocatingVIO The AllocatingVIO
* @param callback The callback to set
* @param location The tracing info for the call site
**/
static inline void setPhysicalZoneCallback(AllocatingVIO *allocatingVIO,
VDOAction *callback,
TraceLocation location)
{
setCallback(allocatingVIOAsCompletion(allocatingVIO), callback,
getPhysicalZoneThreadID(allocatingVIO->zone));
allocatingVIOAddTraceRecord(allocatingVIO, location);
}
/**
* Set a callback as a physical block operation in an AllocatingVIO's allocated
* zone and invoke it immediately.
*
* @param allocatingVIO The AllocatingVIO
* @param callback The callback to invoke
* @param location The tracing info for the call site
**/
static inline void launchPhysicalZoneCallback(AllocatingVIO *allocatingVIO,
VDOAction *callback,
TraceLocation location)
{
setPhysicalZoneCallback(allocatingVIO, callback, location);
invokeCallback(allocatingVIOAsCompletion(allocatingVIO));
}
/**
* Allocate a data block to an AllocatingVIO.
*
* @param allocatingVIO The AllocatingVIO which needs an allocation
* @param selector The allocation selector for deciding which physical
* zone to allocate from
* @param writeLockType The type of write lock to obtain on the block
* @param callback The function to call once the allocation is complete
**/
void allocateDataBlock(AllocatingVIO *allocatingVIO,
AllocationSelector *selector,
PBNLockType writeLockType,
AllocationCallback *callback);
/**
* Release the PBN lock on the allocated block. If the reference to the locked
* block is still provisional, it will be released as well.
*
* @param allocatingVIO The lock holder
**/
void releaseAllocationLock(AllocatingVIO *allocatingVIO);
/**
* Reset an AllocatingVIO after it has done an allocation.
*
* @param allocatingVIO The AllocatingVIO
**/
void resetAllocation(AllocatingVIO *allocatingVIO);
#endif // ALLOCATING_VIO_H