|
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/kernel/dataKVIO.c#18 $
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "dataKVIO.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "logger.h"
|
|
Packit Service |
310c69 |
#include "memoryAlloc.h"
|
|
Packit Service |
310c69 |
#include "murmur/MurmurHash3.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "dataVIO.h"
|
|
Packit Service |
310c69 |
#include "compressedBlock.h"
|
|
Packit Service |
310c69 |
#include "hashLock.h"
|
|
Packit Service |
310c69 |
#include "lz4.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#include "bio.h"
|
|
Packit Service |
310c69 |
#include "dedupeIndex.h"
|
|
Packit Service |
310c69 |
#include "kvdoFlush.h"
|
|
Packit Service |
310c69 |
#include "kvio.h"
|
|
Packit Service |
310c69 |
#include "ioSubmitter.h"
|
|
Packit Service |
310c69 |
#include "vdoCommon.h"
|
|
Packit Service |
310c69 |
#include "verify.h"
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
static void dumpPooledDataKVIO(void *poolData, void *data);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
enum {
|
|
Packit Service |
310c69 |
WRITE_PROTECT_FREE_POOL = 0,
|
|
Packit Service |
310c69 |
WP_DATA_KVIO_SIZE = (sizeof(DataKVIO) + PAGE_SIZE - 1
|
|
Packit Service |
310c69 |
- ((sizeof(DataKVIO) + PAGE_SIZE - 1)
|
|
Packit Service |
310c69 |
% PAGE_SIZE))
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Alter the write-access permission to a page of memory, so that
|
|
Packit Service |
310c69 |
* objects in the free pool may no longer be modified.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* To do: Deny read access as well.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param address The starting address to protect, which must be on a
|
|
Packit Service |
310c69 |
* page boundary
|
|
Packit Service |
310c69 |
* @param byteCount The number of bytes to protect, which must be a multiple
|
|
Packit Service |
310c69 |
* of the page size
|
|
Packit Service |
310c69 |
* @param mode The write protection mode (true means read-only)
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static __always_inline void
|
|
Packit Service |
310c69 |
setWriteProtect(void *address,
|
|
Packit Service |
310c69 |
size_t byteCount,
|
|
Packit Service |
310c69 |
bool mode __attribute__((unused)))
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
BUG_ON((((long) address) % PAGE_SIZE) != 0);
|
|
Packit Service |
310c69 |
BUG_ON((byteCount % PAGE_SIZE) != 0);
|
|
Packit Service |
310c69 |
BUG(); // only works in internal code, sorry
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void maybeLogDataKVIOTrace(DataKVIO *dataKVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (dataKVIO->kvio.layer->traceLogging) {
|
|
Packit Service |
310c69 |
logKvioTrace(&dataKVIO->kvio);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* First tracing hook for VIO completion.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* If the SystemTap script vdotrace.stp is in use, it does stage 1 of
|
|
Packit Service |
310c69 |
* its processing here. We must not call addTraceRecord between the
|
|
Packit Service |
310c69 |
* two tap functions.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataKVIO The VIO we're finishing up
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void kvioCompletionTap1(DataKVIO *dataKVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Ensure that dataKVIO doesn't get optimized out, even under inline
|
|
Packit Service |
310c69 |
* expansion. Also, make sure the compiler has to emit debug info
|
|
Packit Service |
310c69 |
* for baseTraceLocation, which some of our SystemTap scripts will
|
|
Packit Service |
310c69 |
* use here.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* First, make it look as though all memory could be clobbered; then
|
|
Packit Service |
310c69 |
* require that a value be read into a register. That'll force at
|
|
Packit Service |
310c69 |
* least one instruction to exist (so SystemTap can hook in) where
|
|
Packit Service |
310c69 |
* dataKVIO is live. We use a field that the caller would've
|
|
Packit Service |
310c69 |
* accessed recently anyway, so it may be cached.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
barrier();
|
|
Packit Service |
310c69 |
__asm__ __volatile__(""
|
|
Packit Service |
310c69 |
:
|
|
Packit Service |
310c69 |
: "g" (dataKVIO), "g" (baseTraceLocation),
|
|
Packit Service |
310c69 |
"r" (dataKVIO->kvio.layer));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Second tracing hook for VIO completion.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* The SystemTap script vdotrace.stp splits its VIO-completion work
|
|
Packit Service |
310c69 |
* into two stages, to reduce lock contention for script variables.
|
|
Packit Service |
310c69 |
* Hence, it needs two hooks in the code.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataKVIO The VIO we're finishing up
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void kvioCompletionTap2(DataKVIO *dataKVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
// Hack to ensure variable doesn't get optimized out.
|
|
Packit Service |
310c69 |
barrier();
|
|
Packit Service |
310c69 |
__asm__ __volatile__("" : : "g" (dataKVIO), "r" (dataKVIO->kvio.layer));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void kvdoAcknowledgeDataKVIO(DataKVIO *dataKVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
KernelLayer *layer = dataKVIO->kvio.layer;
|
|
Packit Service |
310c69 |
ExternalIORequest *externalIORequest = &dataKVIO->externalIORequest;
|
|
Packit Service |
310c69 |
BIO *bio = externalIORequest->bio;
|
|
Packit Service |
310c69 |
if (bio == NULL) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
externalIORequest->bio = NULL;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int error
|
|
Packit Service |
310c69 |
= mapToSystemError(dataVIOAsCompletion(&dataKVIO->dataVIO)->result);
|
|
Packit Service |
310c69 |
bio->bi_end_io = externalIORequest->endIO;
|
|
Packit Service |
310c69 |
bio->bi_private = externalIORequest->private;
|
|
Packit Service |
310c69 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
|
|
Packit Service |
310c69 |
bio->bi_opf = externalIORequest->rw;
|
|
Packit Service |
310c69 |
#else
|
|
Packit Service |
310c69 |
bio->bi_rw = externalIORequest->rw;
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
countBios(&layer->biosAcknowledged, bio);
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial) {
|
|
Packit Service |
310c69 |
countBios(&layer->biosAcknowledgedPartial, bio);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
completeBio(bio, error);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static noinline void cleanDataKVIO(DataKVIO *dataKVIO, FreeBufferPointers *fbp)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
kvdoAcknowledgeDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
KVIO *kvio = dataKVIOAsKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
kvio->bio = NULL;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (unlikely(kvio->vio->trace != NULL)) {
|
|
Packit Service |
310c69 |
maybeLogDataKVIOTrace(dataKVIO);
|
|
Packit Service |
310c69 |
kvioCompletionTap1(dataKVIO);
|
|
Packit Service |
310c69 |
kvioCompletionTap2(dataKVIO);
|
|
Packit Service |
310c69 |
freeTraceToPool(kvio->layer, kvio->vio->trace);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
addFreeBufferPointer(fbp, dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void returnDataKVIOBatchToPool(BatchProcessor *batch, void *closure)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
KernelLayer *layer = closure;
|
|
Packit Service |
310c69 |
uint32_t count = 0;
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(batch != NULL, "batch not null");
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(layer != NULL, "layer not null");
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
FreeBufferPointers fbp;
|
|
Packit Service |
310c69 |
initFreeBufferPointers(&fbp, layer->dataKVIOPool);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
KvdoWorkItem *item;
|
|
Packit Service |
310c69 |
while ((item = nextBatchItem(batch)) != NULL) {
|
|
Packit Service |
310c69 |
cleanDataKVIO(workItemAsDataKVIO(item), &fbp);
|
|
Packit Service |
310c69 |
condReschedBatchProcessor(batch);
|
|
Packit Service |
310c69 |
count++;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (fbp.index > 0) {
|
|
Packit Service |
310c69 |
freeBufferPointers(&fbp);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
completeManyRequests(layer, count);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void kvdoAcknowledgeThenCompleteDataKVIO(KvdoWorkItem *item)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = workItemAsDataKVIO(item);
|
|
Packit Service |
310c69 |
kvdoAcknowledgeDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
addToBatchProcessor(dataKVIO->kvio.layer->dataKVIOReleaser, item);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoCompleteDataKVIO(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(asDataVIO(completion));
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
KernelLayer *layer = getLayerFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
if (useBioAckQueue(layer) && USE_BIO_ACK_QUEUE_FOR_READ
|
|
Packit Service |
310c69 |
&& (dataKVIO->externalIORequest.bio != NULL)) {
|
|
Packit Service |
310c69 |
launchDataKVIOOnBIOAckQueue(dataKVIO, kvdoAcknowledgeThenCompleteDataKVIO,
|
|
Packit Service |
310c69 |
NULL, BIO_ACK_Q_ACTION_ACK);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
addToBatchProcessor(layer->dataKVIOReleaser,
|
|
Packit Service |
310c69 |
workItemFromDataKVIO(dataKVIO));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Copy the uncompressed data from a compressed block read into the user
|
|
Packit Service |
310c69 |
* bio which requested the read.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param workItem The DataKVIO which requested the read
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void copyReadBlockData(KvdoWorkItem *workItem)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = workItemAsDataKVIO(workItem);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// For a read-modify-write, copy the data into the dataBlock buffer so it
|
|
Packit Service |
310c69 |
// will be set up for the write phase.
|
|
Packit Service |
310c69 |
if (isReadModifyWriteVIO(dataKVIO->kvio.vio)) {
|
|
Packit Service |
310c69 |
bioCopyDataOut(getBIOFromDataKVIO(dataKVIO), dataKVIO->readBlock.data);
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// For a partial read, the callback will copy the requested data from the
|
|
Packit Service |
310c69 |
// read block.
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial) {
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// For a full block read, copy the data to the bio and acknowledge.
|
|
Packit Service |
310c69 |
bioCopyDataOut(getBIOFromDataKVIO(dataKVIO), dataKVIO->readBlock.data);
|
|
Packit Service |
310c69 |
kvdoAcknowledgeDataVIO(&dataKVIO->dataVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Finish reading data for a compressed block.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataKVIO The DataKVIO which requested the read
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void readDataKVIOReadBlockCallback(DataKVIO *dataKVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (dataKVIO->readBlock.status != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
setCompletionResult(dataVIOAsCompletion(&dataKVIO->dataVIO),
|
|
Packit Service |
310c69 |
dataKVIO->readBlock.status);
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
launchDataKVIOOnCPUQueue(dataKVIO, copyReadBlockData, NULL,
|
|
Packit Service |
310c69 |
CPU_Q_ACTION_COMPRESS_BLOCK);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Complete and reset a bio that was supplied by the user and then used for a
|
|
Packit Service |
310c69 |
* read (so that we can complete it with the user's callback).
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param bio The bio to complete
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void resetUserBio(BIO *bio)
|
|
Packit Service |
310c69 |
#else
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Complete and reset a bio that was supplied by the user and then used for a
|
|
Packit Service |
310c69 |
* read (so that we can complete it with the user's callback).
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param bio The bio to complete
|
|
Packit Service |
310c69 |
* @param error Possible error from underlying block device
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void resetUserBio(BIO *bio, int error)
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) \
|
|
Packit Service |
310c69 |
&& (LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)))
|
|
Packit Service |
310c69 |
// This is a user bio, and the device just called bio_endio() on it, so
|
|
Packit Service |
310c69 |
// we need to re-increment bi_remaining so we too can call bio_endio().
|
|
Packit Service |
310c69 |
atomic_inc(&bio->bi_remaining);
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
|
|
Packit Service |
310c69 |
completeAsyncBio(bio);
|
|
Packit Service |
310c69 |
#else
|
|
Packit Service |
310c69 |
completeAsyncBio(bio, error);
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Uncompress the data that's just been read and then call back the requesting
|
|
Packit Service |
310c69 |
* DataKVIO.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param workItem The DataKVIO requesting the data
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void uncompressReadBlock(KvdoWorkItem *workItem)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = workItemAsDataKVIO(workItem);
|
|
Packit Service |
310c69 |
ReadBlock *readBlock = &dataKVIO->readBlock;
|
|
Packit Service |
310c69 |
BlockSize blockSize = VDO_BLOCK_SIZE;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// The DataKVIO's scratch block will be used to contain the
|
|
Packit Service |
310c69 |
// uncompressed data.
|
|
Packit Service |
310c69 |
uint16_t fragmentOffset, fragmentSize;
|
|
Packit Service |
310c69 |
char *compressedData = readBlock->data;
|
|
Packit Service |
310c69 |
int result = getCompressedBlockFragment(readBlock->mappingState,
|
|
Packit Service |
310c69 |
compressedData, blockSize,
|
|
Packit Service |
310c69 |
&fragmentOffset,
|
|
Packit Service |
310c69 |
&fragmentSize);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
logDebug("%s: frag err %d", __func__, result);
|
|
Packit Service |
310c69 |
readBlock->status = result;
|
|
Packit Service |
310c69 |
readBlock->callback(dataKVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
char *fragment = compressedData + fragmentOffset;
|
|
Packit Service |
310c69 |
int size = LZ4_uncompress_unknownOutputSize(fragment, dataKVIO->scratchBlock,
|
|
Packit Service |
310c69 |
fragmentSize, blockSize);
|
|
Packit Service |
310c69 |
if (size == blockSize) {
|
|
Packit Service |
310c69 |
readBlock->data = dataKVIO->scratchBlock;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
logDebug("%s: lz4 error", __func__);
|
|
Packit Service |
310c69 |
readBlock->status = VDO_INVALID_FRAGMENT;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
readBlock->callback(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Now that we have gotten the data from storage, uncompress the data if
|
|
Packit Service |
310c69 |
* necessary and then call back the requesting DataKVIO.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataKVIO The DataKVIO requesting the data
|
|
Packit Service |
310c69 |
* @param result The result of the read operation
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void completeRead(DataKVIO *dataKVIO, int result)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ReadBlock *readBlock = &dataKVIO->readBlock;
|
|
Packit Service |
310c69 |
readBlock->status = result;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if ((result == VDO_SUCCESS) && isCompressed(readBlock->mappingState)) {
|
|
Packit Service |
310c69 |
launchDataKVIOOnCPUQueue(dataKVIO, uncompressReadBlock, NULL,
|
|
Packit Service |
310c69 |
CPU_Q_ACTION_COMPRESS_BLOCK);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
readBlock->callback(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Callback for a bio doing a read.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param bio The bio
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
static void readBioCallback(BIO *bio)
|
|
Packit Service |
310c69 |
#else
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Callback for a bio doing a read.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param bio The bio
|
|
Packit Service |
310c69 |
* @param result The result of the read operation
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
static void readBioCallback(BIO *bio, int result)
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
KVIO *kvio = (KVIO *) bio->bi_private;
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = kvioAsDataKVIO(kvio);
|
|
Packit Service |
310c69 |
dataKVIO->readBlock.data = dataKVIO->readBlock.buffer;
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
countCompletedBios(bio);
|
|
Packit Service |
310c69 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
|
|
Packit Service |
310c69 |
completeRead(dataKVIO, getBioResult(bio));
|
|
Packit Service |
310c69 |
#else
|
|
Packit Service |
310c69 |
completeRead(dataKVIO, result);
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoReadBlock(DataVIO *dataVIO,
|
|
Packit Service |
310c69 |
PhysicalBlockNumber location,
|
|
Packit Service |
310c69 |
BlockMappingState mappingState,
|
|
Packit Service |
310c69 |
BioQAction action,
|
|
Packit Service |
310c69 |
DataKVIOCallback callback)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(dataVIO);
|
|
Packit Service |
310c69 |
ReadBlock *readBlock = &dataKVIO->readBlock;
|
|
Packit Service |
310c69 |
KernelLayer *layer = getLayerFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
readBlock->callback = callback;
|
|
Packit Service |
310c69 |
readBlock->status = VDO_SUCCESS;
|
|
Packit Service |
310c69 |
readBlock->mappingState = mappingState;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BUG_ON(getBIOFromDataKVIO(dataKVIO)->bi_private != &dataKVIO->kvio);
|
|
Packit Service |
310c69 |
// Read the data directly from the device using the read bio.
|
|
Packit Service |
310c69 |
BIO *bio = readBlock->bio;
|
|
Packit Service |
310c69 |
resetBio(bio, layer);
|
|
Packit Service |
310c69 |
setBioSector(bio, blockToSector(layer, location));
|
|
Packit Service |
310c69 |
setBioOperationRead(bio);
|
|
Packit Service |
310c69 |
bio->bi_end_io = readBioCallback;
|
|
Packit Service |
310c69 |
submitBio(bio, action);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoReadDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(!isWriteVIO(dataVIOAsVIO(dataVIO)),
|
|
Packit Service |
310c69 |
"operation set correctly for data read");
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION("$F;io=readData"));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (isCompressed(dataVIO->mapped.state)) {
|
|
Packit Service |
310c69 |
kvdoReadBlock(dataVIO, dataVIO->mapped.pbn, dataVIO->mapped.state,
|
|
Packit Service |
310c69 |
BIO_Q_ACTION_COMPRESSED_DATA, readDataKVIOReadBlockCallback);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
KVIO *kvio = dataVIOAsKVIO(dataVIO);
|
|
Packit Service |
310c69 |
BIO *bio = kvio->bio;
|
|
Packit Service |
310c69 |
bio->bi_end_io = resetUserBio;
|
|
Packit Service |
310c69 |
setBioSector(bio, blockToSector(kvio->layer, dataVIO->mapped.pbn));
|
|
Packit Service |
310c69 |
submitBio(bio, BIO_Q_ACTION_DATA);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void kvdoAcknowledgeDataKVIOThenContinue(KvdoWorkItem *item)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = workItemAsDataKVIO(item);
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
kvdoAcknowledgeDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
// Even if we're not using bio-ack threads, we may be in the wrong
|
|
Packit Service |
310c69 |
// base-code thread.
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoAcknowledgeDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(dataVIO);
|
|
Packit Service |
310c69 |
KernelLayer *layer = getLayerFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// If the remaining discard work is not completely processed by this VIO,
|
|
Packit Service |
310c69 |
// don't acknowledge it yet.
|
|
Packit Service |
310c69 |
if (isDiscardBio(dataKVIO->externalIORequest.bio)
|
|
Packit Service |
310c69 |
&& (dataKVIO->remainingDiscard
|
|
Packit Service |
310c69 |
> (VDO_BLOCK_SIZE - dataKVIO->offset))) {
|
|
Packit Service |
310c69 |
invokeCallback(dataVIOAsCompletion(dataVIO));
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// We've finished with the KVIO; acknowledge completion of the bio to the
|
|
Packit Service |
310c69 |
// kernel.
|
|
Packit Service |
310c69 |
if (useBioAckQueue(layer)) {
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
launchDataKVIOOnBIOAckQueue(dataKVIO, kvdoAcknowledgeDataKVIOThenContinue,
|
|
Packit Service |
310c69 |
NULL, BIO_ACK_Q_ACTION_ACK);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
kvdoAcknowledgeDataKVIOThenContinue(workItemFromDataKVIO(dataKVIO));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoWriteDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(isWriteVIO(dataVIOAsVIO(dataVIO)),
|
|
Packit Service |
310c69 |
"kvdoWriteDataVIO() called on write DataVIO");
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION("$F;io=writeData;j=normal"));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
KVIO *kvio = dataVIOAsKVIO(dataVIO);
|
|
Packit Service |
310c69 |
BIO *bio = kvio->bio;
|
|
Packit Service |
310c69 |
setBioOperationWrite(bio);
|
|
Packit Service |
310c69 |
setBioSector(bio, blockToSector(kvio->layer, dataVIO->newMapped.pbn));
|
|
Packit Service |
310c69 |
submitBio(bio, BIO_Q_ACTION_DATA);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoModifyWriteDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(dataVIO);
|
|
Packit Service |
310c69 |
BIO *bio = dataKVIO->externalIORequest.bio;
|
|
Packit Service |
310c69 |
KernelLayer *layer = getLayerFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
resetBio(dataKVIO->dataBlockBio, layer);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (!isDiscardBio(bio)) {
|
|
Packit Service |
310c69 |
bioCopyDataIn(bio, dataKVIO->dataBlock + dataKVIO->offset);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
memset(dataKVIO->dataBlock + dataKVIO->offset, '\0',
|
|
Packit Service |
310c69 |
min(dataKVIO->remainingDiscard,
|
|
Packit Service |
310c69 |
(DiscardSize) (VDO_BLOCK_SIZE - dataKVIO->offset)));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
dataVIO->isZeroBlock = bioIsZeroData(dataKVIO->dataBlockBio);
|
|
Packit Service |
310c69 |
dataKVIO->dataBlockBio->bi_private = &dataKVIO->kvio;
|
|
Packit Service |
310c69 |
copyBioOperationAndFlags(dataKVIO->dataBlockBio, bio);
|
|
Packit Service |
310c69 |
// Make the bio a write, not (potentially) a discard.
|
|
Packit Service |
310c69 |
setBioOperationWrite(dataKVIO->dataBlockBio);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoZeroDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION("zeroDataVIO;io=readData"));
|
|
Packit Service |
310c69 |
bioZeroData(dataVIOAsKVIO(dataVIO)->bio);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoCopyDataVIO(DataVIO *source, DataVIO *destination)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(destination, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
bioCopyDataOut(dataVIOAsKVIO(destination)->bio,
|
|
Packit Service |
310c69 |
dataVIOAsDataKVIO(source)->dataBlock);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void kvdoCompressWork(KvdoWorkItem *item)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = workItemAsDataKVIO(item);
|
|
Packit Service |
310c69 |
KernelLayer *layer = getLayerFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
char *context = getWorkQueuePrivateData();
|
|
Packit Service |
310c69 |
if (unlikely(context == NULL)) {
|
|
Packit Service |
310c69 |
uint32_t index = atomicAdd32(&layer->compressionContextIndex, 1) - 1;
|
|
Packit Service |
310c69 |
BUG_ON(index >= layer->deviceConfig->threadCounts.cpuThreads);
|
|
Packit Service |
310c69 |
context = layer->compressionContext[index];
|
|
Packit Service |
310c69 |
setWorkQueuePrivateData(context);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
int size = LZ4_compress_ctx_limitedOutput(context, dataKVIO->dataBlock,
|
|
Packit Service |
310c69 |
dataKVIO->scratchBlock,
|
|
Packit Service |
310c69 |
VDO_BLOCK_SIZE,
|
|
Packit Service |
310c69 |
VDO_BLOCK_SIZE);
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = &dataKVIO->dataVIO;
|
|
Packit Service |
310c69 |
if (size > 0) {
|
|
Packit Service |
310c69 |
// The scratch block will be used to contain the compressed data.
|
|
Packit Service |
310c69 |
dataVIO->compression.data = dataKVIO->scratchBlock;
|
|
Packit Service |
310c69 |
dataVIO->compression.size = size;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
// Use block size plus one as an indicator for uncompressible data.
|
|
Packit Service |
310c69 |
dataVIO->compression.size = VDO_BLOCK_SIZE + 1;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoCompressDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO,
|
|
Packit Service |
310c69 |
THIS_LOCATION("compressDataVIO;"
|
|
Packit Service |
310c69 |
"io=compress;cb=compress"));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* If the orignal bio was a discard, but we got this far because the discard
|
|
Packit Service |
310c69 |
* was a partial one (r/m/w), and it is part of a larger discard, we cannot
|
|
Packit Service |
310c69 |
* compress this VIO. We need to make sure the VIO completes ASAP.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(dataVIO);
|
|
Packit Service |
310c69 |
if (isDiscardBio(dataKVIO->externalIORequest.bio)
|
|
Packit Service |
310c69 |
&& (dataKVIO->remainingDiscard > 0)) {
|
|
Packit Service |
310c69 |
dataVIO->compression.size = VDO_BLOCK_SIZE + 1;
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
launchDataKVIOOnCPUQueue(dataKVIO, kvdoCompressWork, NULL,
|
|
Packit Service |
310c69 |
CPU_Q_ACTION_COMPRESS_BLOCK);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Construct a DataKVIO.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param [in] layer The physical layer
|
|
Packit Service |
310c69 |
* @param [in] bio The bio to associate with this DataKVIO
|
|
Packit Service |
310c69 |
* @param [out] dataKVIOPtr A pointer to hold the new DataKVIO
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return VDO_SUCCESS or an error
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
__attribute__((warn_unused_result))
|
|
Packit Service |
310c69 |
static int makeDataKVIO(KernelLayer *layer, BIO *bio, DataKVIO **dataKVIOPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO;
|
|
Packit Service |
310c69 |
int result = allocBufferFromPool(layer->dataKVIOPool, (void **) &dataKVIO);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result, "data kvio allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (WRITE_PROTECT_FREE_POOL) {
|
|
Packit Service |
310c69 |
setWriteProtect(dataKVIO, WP_DATA_KVIO_SIZE, false);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
KVIO *kvio = &dataKVIO->kvio;
|
|
Packit Service |
310c69 |
kvio->vio = dataVIOAsVIO(&dataKVIO->dataVIO);
|
|
Packit Service |
310c69 |
memset(&kvio->enqueueable, 0, sizeof(KvdoEnqueueable));
|
|
Packit Service |
310c69 |
memset(&dataKVIO->dedupeContext.pendingList, 0, sizeof(struct list_head));
|
|
Packit Service |
310c69 |
memset(&dataKVIO->dataVIO, 0, sizeof(DataVIO));
|
|
Packit Service |
310c69 |
kvio->bioToSubmit = NULL;
|
|
Packit Service |
310c69 |
bio_list_init(&kvio->biosMerged);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// The dataBlock is only needed for writes and some partial reads.
|
|
Packit Service |
310c69 |
if (isWriteBio(bio) || (getBioSize(bio) < VDO_BLOCK_SIZE)) {
|
|
Packit Service |
310c69 |
resetBio(dataKVIO->dataBlockBio, layer);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
initializeKVIO(kvio, layer, VIO_TYPE_DATA, VIO_PRIORITY_DATA, NULL, bio);
|
|
Packit Service |
310c69 |
*dataKVIOPtr = dataKVIO;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Creates a new DataVIO structure. A DataVIO represents a single logical
|
|
Packit Service |
310c69 |
* block of data. It is what most VDO operations work with. This function also
|
|
Packit Service |
310c69 |
* creates a wrapping DataKVIO structure that is used when we want to
|
|
Packit Service |
310c69 |
* physically read or write the data associated with the DataVIO.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param [in] layer The physical layer
|
|
Packit Service |
310c69 |
* @param [in] bio The BIO from the request the new DataKVIO will
|
|
Packit Service |
310c69 |
* service
|
|
Packit Service |
310c69 |
* @param [in] arrivalTime The arrival time of the BIO
|
|
Packit Service |
310c69 |
* @param [out] dataKVIOPtr A pointer to hold the new DataKVIO
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return VDO_SUCCESS or an error
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static int kvdoCreateKVIOFromBio(KernelLayer *layer,
|
|
Packit Service |
310c69 |
BIO *bio,
|
|
Packit Service |
310c69 |
Jiffies arrivalTime,
|
|
Packit Service |
310c69 |
DataKVIO **dataKVIOPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
ExternalIORequest externalIORequest = {
|
|
Packit Service |
310c69 |
.bio = bio,
|
|
Packit Service |
310c69 |
.private = bio->bi_private,
|
|
Packit Service |
310c69 |
.endIO = bio->bi_end_io,
|
|
Packit Service |
310c69 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
|
|
Packit Service |
310c69 |
.rw = bio->bi_opf,
|
|
Packit Service |
310c69 |
#else
|
|
Packit Service |
310c69 |
.rw = bio->bi_rw,
|
|
Packit Service |
310c69 |
#endif
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// We will handle FUA at the end of the request (after we restore the
|
|
Packit Service |
310c69 |
// bi_rw field from externalIORequest.rw).
|
|
Packit Service |
310c69 |
clearBioOperationFlagFua(bio);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = NULL;
|
|
Packit Service |
310c69 |
int result = makeDataKVIO(layer, bio, &dataKVIO);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
dataKVIO->externalIORequest = externalIORequest;
|
|
Packit Service |
310c69 |
dataKVIO->offset = sectorToBlockOffset(layer, getBioSector(bio));
|
|
Packit Service |
310c69 |
dataKVIO->isPartial = ((getBioSize(bio) < VDO_BLOCK_SIZE)
|
|
Packit Service |
310c69 |
|| (dataKVIO->offset != 0));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial) {
|
|
Packit Service |
310c69 |
countBios(&layer->biosInPartial, bio);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Note that we unconditionally fill in the dataBlock array for
|
|
Packit Service |
310c69 |
* non-read operations. There are places like kvdoCopyVIO that may
|
|
Packit Service |
310c69 |
* look at kvio->dataBlock for a zero block (and maybe for
|
|
Packit Service |
310c69 |
* discards?). We could skip filling in dataBlock for such cases,
|
|
Packit Service |
310c69 |
* but only once we're sure all such places are fixed to check the
|
|
Packit Service |
310c69 |
* isZeroBlock flag first.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
if (isDiscardBio(bio)) {
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* This is a discard/trim operation. This is treated much like the zero
|
|
Packit Service |
310c69 |
* block, but we keep different stats and distinguish it in the block
|
|
Packit Service |
310c69 |
* map.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
memset(dataKVIO->dataBlock, 0, VDO_BLOCK_SIZE);
|
|
Packit Service |
310c69 |
} else if (bio_data_dir(bio) == WRITE) {
|
|
Packit Service |
310c69 |
dataKVIO->dataVIO.isZeroBlock = bioIsZeroData(bio);
|
|
Packit Service |
310c69 |
// Copy the bio data to a char array so that we can continue to use
|
|
Packit Service |
310c69 |
// the data after we acknowledge the bio.
|
|
Packit Service |
310c69 |
bioCopyDataIn(bio, dataKVIO->dataBlock);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial || isWriteBio(bio)) {
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* dataKVIO->bio will point at kvio->dataBlockBio for all writes and
|
|
Packit Service |
310c69 |
* partial block I/O so the rest of the kernel code doesn't need to
|
|
Packit Service |
310c69 |
* make a decision as to what to use.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
dataKVIO->dataBlockBio->bi_private = &dataKVIO->kvio;
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial && isWriteBio(bio)) {
|
|
Packit Service |
310c69 |
clearBioOperationAndFlags(dataKVIO->dataBlockBio);
|
|
Packit Service |
310c69 |
setBioOperationRead(dataKVIO->dataBlockBio);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
copyBioOperationAndFlags(dataKVIO->dataBlockBio, bio);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
dataKVIOAsKVIO(dataKVIO)->bio = dataKVIO->dataBlockBio;
|
|
Packit Service |
310c69 |
dataKVIO->readBlock.data = dataKVIO->dataBlock;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
setBioBlockDevice(bio, getKernelLayerBdev(layer));
|
|
Packit Service |
310c69 |
bio->bi_end_io = completeAsyncBio;
|
|
Packit Service |
310c69 |
*dataKVIOPtr = dataKVIO;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
static void launchDataKVIOWork(KvdoWorkItem *item)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
runCallback(vioAsCompletion(workItemAsKVIO(item)->vio));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Continue discard processing for requests that span multiple physical blocks.
|
|
Packit Service |
310c69 |
* If all have been processed the KVIO is completed. If we have already seen
|
|
Packit Service |
310c69 |
* an error, we skip the rest of the discard and fail immediately.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Invoked in a request-queue thread after the discard of a block has
|
|
Packit Service |
310c69 |
* completed.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion A completion representing the discard KVIO
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void kvdoContinueDiscardKVIO(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = asDataVIO(completion);
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(dataVIO);
|
|
Packit Service |
310c69 |
KernelLayer *layer = getLayerFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
dataKVIO->remainingDiscard
|
|
Packit Service |
310c69 |
-= min(dataKVIO->remainingDiscard,
|
|
Packit Service |
310c69 |
(DiscardSize) (VDO_BLOCK_SIZE - dataKVIO->offset));
|
|
Packit Service |
310c69 |
if ((completion->result != VDO_SUCCESS)
|
|
Packit Service |
310c69 |
|| (dataKVIO->remainingDiscard == 0)) {
|
|
Packit Service |
310c69 |
if (dataKVIO->hasDiscardPermit) {
|
|
Packit Service |
310c69 |
limiterRelease(&layer->discardLimiter);
|
|
Packit Service |
310c69 |
dataKVIO->hasDiscardPermit = false;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
kvdoCompleteDataKVIO(completion);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
BIO *bio = getBIOFromDataKVIO(dataKVIO);
|
|
Packit Service |
310c69 |
resetBio(bio, layer);
|
|
Packit Service |
310c69 |
dataKVIO->isPartial = (dataKVIO->remainingDiscard < VDO_BLOCK_SIZE);
|
|
Packit Service |
310c69 |
dataKVIO->offset = 0;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
VIOOperation operation;
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial) {
|
|
Packit Service |
310c69 |
operation = VIO_READ_MODIFY_WRITE;
|
|
Packit Service |
310c69 |
setBioOperationRead(bio);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
operation = VIO_WRITE;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (requestorSetFUA(dataKVIO)) {
|
|
Packit Service |
310c69 |
operation |= VIO_FLUSH_AFTER;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
prepareDataVIO(dataVIO, dataVIO->logical.lbn + 1, operation,
|
|
Packit Service |
310c69 |
!dataKVIO->isPartial, kvdoContinueDiscardKVIO);
|
|
Packit Service |
310c69 |
enqueueDataKVIO(dataKVIO, launchDataKVIOWork, completion->callback,
|
|
Packit Service |
310c69 |
REQ_Q_ACTION_MAP_BIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Finish a partial read.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param completion The partial read KVIO
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void kvdoCompletePartialRead(VDOCompletion *completion)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(asDataVIO(completion));
|
|
Packit Service |
310c69 |
dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
bioCopyDataOut(dataKVIO->externalIORequest.bio,
|
|
Packit Service |
310c69 |
dataKVIO->readBlock.data + dataKVIO->offset);
|
|
Packit Service |
310c69 |
kvdoCompleteDataKVIO(completion);
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int kvdoLaunchDataKVIOFromBio(KernelLayer *layer,
|
|
Packit Service |
310c69 |
BIO *bio,
|
|
Packit Service |
310c69 |
uint64_t arrivalTime,
|
|
Packit Service |
310c69 |
bool hasDiscardPermit)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = NULL;
|
|
Packit Service |
310c69 |
int result = kvdoCreateKVIOFromBio(layer, bio, arrivalTime, &dataKVIO);
|
|
Packit Service |
310c69 |
if (unlikely(result != VDO_SUCCESS)) {
|
|
Packit Service |
310c69 |
logInfo("%s: KVIO allocation failure", __func__);
|
|
Packit Service |
310c69 |
if (hasDiscardPermit) {
|
|
Packit Service |
310c69 |
limiterRelease(&layer->discardLimiter);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
limiterRelease(&layer->requestLimiter);
|
|
Packit Service |
310c69 |
return mapToSystemError(result);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* Discards behave very differently than other requests when coming
|
|
Packit Service |
310c69 |
* in from device-mapper. We have to be able to handle any size discards
|
|
Packit Service |
310c69 |
* and with various sector offsets within a block.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
KVIO *kvio = &dataKVIO->kvio;
|
|
Packit Service |
310c69 |
VDOAction *callback = kvdoCompleteDataKVIO;
|
|
Packit Service |
310c69 |
VIOOperation operation = VIO_WRITE;
|
|
Packit Service |
310c69 |
bool isTrim = false;
|
|
Packit Service |
310c69 |
if (isDiscardBio(bio)) {
|
|
Packit Service |
310c69 |
dataKVIO->hasDiscardPermit = hasDiscardPermit;
|
|
Packit Service |
310c69 |
dataKVIO->remainingDiscard = getBioSize(bio);
|
|
Packit Service |
310c69 |
callback = kvdoContinueDiscardKVIO;
|
|
Packit Service |
310c69 |
if (dataKVIO->isPartial) {
|
|
Packit Service |
310c69 |
operation = VIO_READ_MODIFY_WRITE;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
isTrim = true;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
} else if (dataKVIO->isPartial) {
|
|
Packit Service |
310c69 |
if (bio_data_dir(bio) == READ) {
|
|
Packit Service |
310c69 |
callback = kvdoCompletePartialRead;
|
|
Packit Service |
310c69 |
operation = VIO_READ;
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
operation = VIO_READ_MODIFY_WRITE;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
} else if (bio_data_dir(bio) == READ) {
|
|
Packit Service |
310c69 |
operation = VIO_READ;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (requestorSetFUA(dataKVIO)) {
|
|
Packit Service |
310c69 |
operation |= VIO_FLUSH_AFTER;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
LogicalBlockNumber lbn
|
|
Packit Service |
310c69 |
= sectorToBlock(layer, getBioSector(bio) - layer->startingSectorOffset);
|
|
Packit Service |
310c69 |
prepareDataVIO(&dataKVIO->dataVIO, lbn, operation, isTrim, callback);
|
|
Packit Service |
310c69 |
enqueueKVIO(kvio, launchDataKVIOWork, vioAsCompletion(kvio->vio)->callback,
|
|
Packit Service |
310c69 |
REQ_Q_ACTION_MAP_BIO);
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Hash a DataKVIO and set its chunk name.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param item The DataKVIO to be hashed
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void kvdoHashDataWork(KvdoWorkItem *item)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = workItemAsDataKVIO(item);
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = &dataKVIO->dataVIO;
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
MurmurHash3_x64_128(dataKVIO->dataBlock, VDO_BLOCK_SIZE, 0x62ea60be,
|
|
Packit Service |
310c69 |
&dataVIO->chunkName);
|
|
Packit Service |
310c69 |
dataKVIO->dedupeContext.chunkName = &dataVIO->chunkName;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
kvdoEnqueueDataVIOCallback(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoHashDataVIO(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO, THIS_LOCATION(NULL));
|
|
Packit Service |
310c69 |
launchDataKVIOOnCPUQueue(dataVIOAsDataKVIO(dataVIO), kvdoHashDataWork, NULL,
|
|
Packit Service |
310c69 |
CPU_Q_ACTION_HASH_BLOCK);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoCheckForDuplication(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
dataVIOAddTraceRecord(dataVIO,
|
|
Packit Service |
310c69 |
THIS_LOCATION("checkForDuplication;dup=post"));
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(!dataVIO->isZeroBlock,
|
|
Packit Service |
310c69 |
"zero block not checked for duplication");
|
|
Packit Service |
310c69 |
ASSERT_LOG_ONLY(dataVIO->newMapped.state != MAPPING_STATE_UNMAPPED,
|
|
Packit Service |
310c69 |
"discard not checked for duplication");
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = dataVIOAsDataKVIO(dataVIO);
|
|
Packit Service |
310c69 |
if (hasAllocation(dataVIO)) {
|
|
Packit Service |
310c69 |
postDedupeAdvice(dataKVIO);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
// This block has not actually been written (presumably because we are
|
|
Packit Service |
310c69 |
// full), so attempt to dedupe without posting bogus advice.
|
|
Packit Service |
310c69 |
queryDedupeAdvice(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void kvdoUpdateDedupeAdvice(DataVIO *dataVIO)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
updateDedupeAdvice(dataVIOAsDataKVIO(dataVIO));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Implements BufferFreeFunction.
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void freePooledDataKVIO(void *poolData, void *data)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
if (data == NULL) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = (DataKVIO *) data;
|
|
Packit Service |
310c69 |
KernelLayer *layer = (KernelLayer *) poolData;
|
|
Packit Service |
310c69 |
if (WRITE_PROTECT_FREE_POOL) {
|
|
Packit Service |
310c69 |
setWriteProtect(dataKVIO, WP_DATA_KVIO_SIZE, false);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dataKVIO->dataBlockBio != NULL) {
|
|
Packit Service |
310c69 |
freeBio(dataKVIO->dataBlockBio, layer);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (dataKVIO->readBlock.bio != NULL) {
|
|
Packit Service |
310c69 |
freeBio(dataKVIO->readBlock.bio, layer);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
FREE(dataKVIO->readBlock.buffer);
|
|
Packit Service |
310c69 |
FREE(dataKVIO->dataBlock);
|
|
Packit Service |
310c69 |
FREE(dataKVIO->scratchBlock);
|
|
Packit Service |
310c69 |
FREE(dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Allocate a DataKVIO. This function is the internals of makePooledDataKVIO().
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param [in] layer The layer in which the DataKVIO will operate
|
|
Packit Service |
310c69 |
* @param [out] dataKVIOPtr A pointer to hold the newly allocated DataKVIO
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @return VDO_SUCCESS or an error
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static int allocatePooledDataKVIO(KernelLayer *layer, DataKVIO **dataKVIOPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO;
|
|
Packit Service |
310c69 |
int result;
|
|
Packit Service |
310c69 |
if (WRITE_PROTECT_FREE_POOL) {
|
|
Packit Service |
310c69 |
STATIC_ASSERT(WP_DATA_KVIO_SIZE >= sizeof(DataKVIO));
|
|
Packit Service |
310c69 |
result = allocateMemory(WP_DATA_KVIO_SIZE, 0, __func__, &dataKVIO);
|
|
Packit Service |
310c69 |
if (result == VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
BUG_ON((((size_t) dataKVIO) & (PAGE_SIZE - 1)) != 0);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
result = ALLOCATE(1, DataKVIO, __func__, &dataKVIO);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result, "DataKVIO allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
STATIC_ASSERT(VDO_BLOCK_SIZE <= PAGE_SIZE);
|
|
Packit Service |
310c69 |
result = allocateMemory(VDO_BLOCK_SIZE, 0, "kvio data",
|
|
Packit Service |
310c69 |
&dataKVIO->dataBlock);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
freePooledDataKVIO(layer, dataKVIO);
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result, "DataKVIO data allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = createBio(layer, dataKVIO->dataBlock, &dataKVIO->dataBlockBio);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
freePooledDataKVIO(layer, dataKVIO);
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result,
|
|
Packit Service |
310c69 |
"DataKVIO data bio allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = allocateMemory(VDO_BLOCK_SIZE, 0, "kvio read buffer",
|
|
Packit Service |
310c69 |
&dataKVIO->readBlock.buffer);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
freePooledDataKVIO(layer, dataKVIO);
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result,
|
|
Packit Service |
310c69 |
"DataKVIO read allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = createBio(layer, dataKVIO->readBlock.buffer,
|
|
Packit Service |
310c69 |
&dataKVIO->readBlock.bio);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
freePooledDataKVIO(layer, dataKVIO);
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result,
|
|
Packit Service |
310c69 |
"DataKVIO read bio allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
dataKVIO->readBlock.bio->bi_private = &dataKVIO->kvio;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
result = allocateMemory(VDO_BLOCK_SIZE, 0, "kvio scratch",
|
|
Packit Service |
310c69 |
&dataKVIO->scratchBlock);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
freePooledDataKVIO(layer, dataKVIO);
|
|
Packit Service |
310c69 |
return logErrorWithStringError(result,
|
|
Packit Service |
310c69 |
"DataKVIO scratch allocation failure");
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
*dataKVIOPtr = dataKVIO;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Implements BufferAllocateFunction.
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static int makePooledDataKVIO(void *poolData, void **dataPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = NULL;
|
|
Packit Service |
310c69 |
int result = allocatePooledDataKVIO((KernelLayer *) poolData, &dataKVIO);
|
|
Packit Service |
310c69 |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
freePooledDataKVIO(poolData, dataKVIO);
|
|
Packit Service |
310c69 |
return result;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
*dataPtr = dataKVIO;
|
|
Packit Service |
310c69 |
return VDO_SUCCESS;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Dump out the waiters on each DataVIO in the DataVIO buffer pool.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param queue The queue to check (logical or physical)
|
|
Packit Service |
310c69 |
* @param waitOn The label to print for queue (logical or physical)
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void dumpVIOWaiters(WaitQueue *queue, char *waitOn)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
Waiter *first = getFirstWaiter(queue);
|
|
Packit Service |
310c69 |
if (first == NULL) {
|
|
Packit Service |
310c69 |
return;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = waiterAsDataVIO(first);
|
|
Packit Service |
310c69 |
logInfo(" %s is locked. Waited on by: VIO %" PRIptr " pbn %" PRIu64
|
|
Packit Service |
310c69 |
" lbn %llu d-pbn %llu lastOp %s",
|
|
Packit Service |
310c69 |
waitOn, dataVIO, getDataVIOAllocation(dataVIO),
|
|
Packit Service |
310c69 |
dataVIO->logical.lbn, dataVIO->duplicate.pbn,
|
|
Packit Service |
310c69 |
getOperationName(dataVIO));
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
Waiter *waiter;
|
|
Packit Service |
310c69 |
for (waiter = first->nextWaiter;
|
|
Packit Service |
310c69 |
waiter != first;
|
|
Packit Service |
310c69 |
waiter = waiter->nextWaiter) {
|
|
Packit Service |
310c69 |
dataVIO = waiterAsDataVIO(waiter);
|
|
Packit Service |
310c69 |
logInfo(" ... and : VIO %" PRIptr " pbn %llu lbn %"
|
|
Packit Service |
310c69 |
PRIu64 " d-pbn %llu lastOp %s",
|
|
Packit Service |
310c69 |
dataVIO, getDataVIOAllocation(dataVIO), dataVIO->logical.lbn,
|
|
Packit Service |
310c69 |
dataVIO->duplicate.pbn, getOperationName(dataVIO));
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Encode various attributes of a VIO as a string of one-character flags for
|
|
Packit Service |
310c69 |
* dump logging. This encoding is for logging brevity:
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* R => VIO completion result not VDO_SUCCESS
|
|
Packit Service |
310c69 |
* W => VIO is on a wait queue
|
|
Packit Service |
310c69 |
* D => VIO is a duplicate
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* The common case of no flags set will result in an empty, null-terminated
|
|
Packit Service |
310c69 |
* buffer. If any flags are encoded, the first character in the string will be
|
|
Packit Service |
310c69 |
* a space character.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param dataVIO The VIO to encode
|
|
Packit Service |
310c69 |
* @param buffer The buffer to receive a null-terminated string of encoded
|
|
Packit Service |
310c69 |
* flag character
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void encodeVIODumpFlags(DataVIO *dataVIO, char buffer[8])
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
char *pFlag = buffer;
|
|
Packit Service |
310c69 |
*pFlag++ = ' ';
|
|
Packit Service |
310c69 |
if (dataVIOAsCompletion(dataVIO)->result != VDO_SUCCESS) {
|
|
Packit Service |
310c69 |
*pFlag++ = 'R';
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (dataVIOAsAllocatingVIO(dataVIO)->waiter.nextWaiter != NULL) {
|
|
Packit Service |
310c69 |
*pFlag++ = 'W';
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (dataVIO->isDuplicate) {
|
|
Packit Service |
310c69 |
*pFlag++ = 'D';
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
if (pFlag == &buffer[1]) {
|
|
Packit Service |
310c69 |
// No flags, so remove the blank space.
|
|
Packit Service |
310c69 |
pFlag = buffer;
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
*pFlag = '\0';
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**
|
|
Packit Service |
310c69 |
* Dump out info on a DataKVIO from the DataKVIO pool.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* Implements BufferDumpFunction.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* @param poolData The pool data
|
|
Packit Service |
310c69 |
* @param data The DataKVIO to dump
|
|
Packit Service |
310c69 |
**/
|
|
Packit Service |
310c69 |
static void dumpPooledDataKVIO(void *poolData __attribute__((unused)),
|
|
Packit Service |
310c69 |
void *data)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = (DataKVIO *) data;
|
|
Packit Service |
310c69 |
DataVIO *dataVIO = &dataKVIO->dataVIO;
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* This just needs to be big enough to hold a queue (thread) name
|
|
Packit Service |
310c69 |
* and a function name (plus a separator character and NUL). The
|
|
Packit Service |
310c69 |
* latter is limited only by taste.
|
|
Packit Service |
310c69 |
*
|
|
Packit Service |
310c69 |
* In making this static, we're assuming only one "dump" will run at
|
|
Packit Service |
310c69 |
* a time. If more than one does run, the log output will be garbled
|
|
Packit Service |
310c69 |
* anyway.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
static char vioWorkItemDumpBuffer[100 + MAX_QUEUE_NAME_LEN];
|
|
Packit Service |
310c69 |
/*
|
|
Packit Service |
310c69 |
* We're likely to be logging a couple thousand of these lines, and
|
|
Packit Service |
310c69 |
* in some circumstances syslogd may have trouble keeping up, so
|
|
Packit Service |
310c69 |
* keep it BRIEF rather than user-friendly.
|
|
Packit Service |
310c69 |
*/
|
|
Packit Service |
310c69 |
dumpWorkItemToBuffer(&dataKVIO->kvio.enqueueable.workItem,
|
|
Packit Service |
310c69 |
vioWorkItemDumpBuffer, sizeof(vioWorkItemDumpBuffer));
|
|
Packit Service |
310c69 |
// Another static buffer...
|
|
Packit Service |
310c69 |
// log10(256) = 2.408+, round up:
|
|
Packit Service |
310c69 |
enum { DECIMAL_DIGITS_PER_UINT64_T = (int) (1 + 2.41 * sizeof(uint64_t)) };
|
|
Packit Service |
310c69 |
static char vioBlockNumberDumpBuffer[sizeof("P L D")
|
|
Packit Service |
310c69 |
+ 3 * DECIMAL_DIGITS_PER_UINT64_T];
|
|
Packit Service |
310c69 |
if (dataVIO->isDuplicate) {
|
|
Packit Service |
310c69 |
snprintf(vioBlockNumberDumpBuffer, sizeof(vioBlockNumberDumpBuffer),
|
|
Packit Service |
310c69 |
"P%llu L%llu D%llu",
|
|
Packit Service |
310c69 |
getDataVIOAllocation(dataVIO), dataVIO->logical.lbn,
|
|
Packit Service |
310c69 |
dataVIO->duplicate.pbn);
|
|
Packit Service |
310c69 |
} else if (hasAllocation(dataVIO)) {
|
|
Packit Service |
310c69 |
snprintf(vioBlockNumberDumpBuffer, sizeof(vioBlockNumberDumpBuffer),
|
|
Packit Service |
310c69 |
"P%llu L%llu",
|
|
Packit Service |
310c69 |
getDataVIOAllocation(dataVIO), dataVIO->logical.lbn);
|
|
Packit Service |
310c69 |
} else {
|
|
Packit Service |
310c69 |
snprintf(vioBlockNumberDumpBuffer, sizeof(vioBlockNumberDumpBuffer),
|
|
Packit Service |
310c69 |
"L%llu",
|
|
Packit Service |
310c69 |
dataVIO->logical.lbn);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
static char vioFlushGenerationBuffer[sizeof(" FG")
|
|
Packit Service |
310c69 |
+ DECIMAL_DIGITS_PER_UINT64_T] = "";
|
|
Packit Service |
310c69 |
if (dataVIO->flushGeneration != 0) {
|
|
Packit Service |
310c69 |
snprintf(vioFlushGenerationBuffer, sizeof(vioFlushGenerationBuffer),
|
|
Packit Service |
310c69 |
" FG%llu", dataVIO->flushGeneration);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// Encode VIO attributes as a string of one-character flags, usually empty.
|
|
Packit Service |
310c69 |
static char flagsDumpBuffer[8];
|
|
Packit Service |
310c69 |
encodeVIODumpFlags(dataVIO, flagsDumpBuffer);
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
logInfo(" kvio %" PRIptr " %s%s %s %s%s",
|
|
Packit Service |
310c69 |
dataKVIO, vioBlockNumberDumpBuffer, vioFlushGenerationBuffer,
|
|
Packit Service |
310c69 |
getOperationName(dataVIO), vioWorkItemDumpBuffer, flagsDumpBuffer);
|
|
Packit Service |
310c69 |
// might want info on: wantAlbireoAnswer / operation / status
|
|
Packit Service |
310c69 |
// might want info on: bio / bioToSubmit / biosMerged
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
dumpVIOWaiters(&dataVIO->logical.waiters, "lbn");
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
// might want to dump more info from VIO here
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
int makeDataKVIOBufferPool(KernelLayer *layer,
|
|
Packit Service |
310c69 |
uint32_t poolSize,
|
|
Packit Service |
310c69 |
BufferPool **bufferPoolPtr)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
return makeBufferPool("DataKVIO Pool", poolSize,
|
|
Packit Service |
310c69 |
makePooledDataKVIO, freePooledDataKVIO,
|
|
Packit Service |
310c69 |
dumpPooledDataKVIO, layer, bufferPoolPtr);
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
DataLocation getDedupeAdvice(const DedupeContext *context)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = container_of(context, DataKVIO, dedupeContext);
|
|
Packit Service |
310c69 |
return (DataLocation) {
|
|
Packit Service |
310c69 |
.state = dataKVIO->dataVIO.newMapped.state,
|
|
Packit Service |
310c69 |
.pbn = dataKVIO->dataVIO.newMapped.pbn,
|
|
Packit Service |
310c69 |
};
|
|
Packit Service |
310c69 |
}
|
|
Packit Service |
310c69 |
|
|
Packit Service |
310c69 |
/**********************************************************************/
|
|
Packit Service |
310c69 |
void setDedupeAdvice(DedupeContext *context, const DataLocation *advice)
|
|
Packit Service |
310c69 |
{
|
|
Packit Service |
310c69 |
DataKVIO *dataKVIO = container_of(context, DataKVIO, dedupeContext);
|
|
Packit Service |
310c69 |
receiveDedupeAdvice(&dataKVIO->dataVIO, advice);
|
|
Packit Service |
310c69 |
}
|