Blame source/vdo/kernel/kvio.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/kernel/kvio.c#7 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "kvio.h"
Packit Service d40955
Packit Service d40955
Packit Service d40955
#include "logger.h"
Packit Service d40955
#include "memoryAlloc.h"
Packit Service d40955
Packit Service d40955
#include "numUtils.h"
Packit Service d40955
#include "vdo.h"
Packit Service d40955
#include "waitQueue.h"
Packit Service d40955
Packit Service d40955
#include "bio.h"
Packit Service d40955
#include "ioSubmitter.h"
Packit Service d40955
#include "kvdoFlush.h"
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * A function to tell vdo that we have completed the requested async
Packit Service d40955
 * operation for a vio
Packit Service d40955
 *
Packit Service d40955
 * @param item    The work item of the VIO to complete
Packit Service d40955
 **/
Packit Service d40955
static void kvdoHandleVIOCallback(KvdoWorkItem *item)
Packit Service d40955
{
Packit Service d40955
  KVIO *kvio = workItemAsKVIO(item);
Packit Service d40955
  runCallback(vioAsCompletion(kvio->vio));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void kvdoEnqueueVIOCallback(KVIO *kvio)
Packit Service d40955
{
Packit Service d40955
  enqueueKVIO(kvio, kvdoHandleVIOCallback,
Packit Service d40955
              (KvdoWorkFunction) vioAsCompletion(kvio->vio)->callback,
Packit Service d40955
              REQ_Q_ACTION_VIO_CALLBACK);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void kvdoContinueKvio(KVIO *kvio, int error)
Packit Service d40955
{
Packit Service d40955
  if (unlikely(error != VDO_SUCCESS)) {
Packit Service d40955
    setCompletionResult(vioAsCompletion(kvio->vio), error);
Packit Service d40955
  }
Packit Service d40955
  kvdoEnqueueVIOCallback(kvio);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
// noinline ensures systemtap can hook in here
Packit Service d40955
static noinline void maybeLogKvioTrace(KVIO *kvio)
Packit Service d40955
{
Packit Service d40955
  if (kvio->layer->traceLogging) {
Packit Service d40955
    logKvioTrace(kvio);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void freeKVIO(KVIO **kvioPtr)
Packit Service d40955
{
Packit Service d40955
  KVIO *kvio = *kvioPtr;
Packit Service d40955
  if (kvio == NULL) {
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (unlikely(kvio->vio->trace != NULL)) {
Packit Service d40955
    maybeLogKvioTrace(kvio);
Packit Service d40955
    FREE(kvio->vio->trace);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  freeBio(kvio->bio, kvio->layer);
Packit Service d40955
  FREE(kvio);
Packit Service d40955
  *kvioPtr = NULL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void freeMetadataKVIO(MetadataKVIO **metadataKVIOPtr)
Packit Service d40955
{
Packit Service d40955
  freeKVIO((KVIO **) metadataKVIOPtr);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void freeCompressedWriteKVIO(CompressedWriteKVIO **compressedWriteKVIOPtr)
Packit Service d40955
{
Packit Service d40955
  freeKVIO((KVIO **) compressedWriteKVIOPtr);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void kvdoWriteCompressedBlock(AllocatingVIO *allocatingVIO)
Packit Service d40955
{
Packit Service d40955
  // This method assumes that compressed writes never set the flush or FUA
Packit Service d40955
  // bits.
Packit Service d40955
  CompressedWriteKVIO *compressedWriteKVIO
Packit Service d40955
    = allocatingVIOAsCompressedWriteKVIO(allocatingVIO);
Packit Service d40955
  KVIO *kvio = compressedWriteKVIOAsKVIO(compressedWriteKVIO);
Packit Service d40955
  BIO  *bio  = kvio->bio;
Packit Service d40955
  resetBio(bio, kvio->layer);
Packit Service d40955
  setBioOperationWrite(bio);
Packit Service d40955
  setBioSector(bio, blockToSector(kvio->layer, kvio->vio->physical));
Packit Service d40955
  submitBio(bio, BIO_Q_ACTION_COMPRESSED_DATA);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get the BioQueue action for a metadata VIO based on that VIO's priority.
Packit Service d40955
 *
Packit Service d40955
 * @param vio  The VIO
Packit Service d40955
 *
Packit Service d40955
 * @return The action with which to submit the VIO's BIO.
Packit Service d40955
 **/
Packit Service d40955
static inline BioQAction getMetadataAction(VIO *vio)
Packit Service d40955
{
Packit Service d40955
  return ((vio->priority == VIO_PRIORITY_HIGH)
Packit Service d40955
          ? BIO_Q_ACTION_HIGH : BIO_Q_ACTION_METADATA);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void kvdoSubmitMetadataVIO(VIO *vio)
Packit Service d40955
{
Packit Service d40955
  KVIO *kvio = metadataKVIOAsKVIO(vioAsMetadataKVIO(vio));
Packit Service d40955
  BIO  *bio  = kvio->bio;
Packit Service d40955
  resetBio(bio, kvio->layer);
Packit Service d40955
Packit Service d40955
  setBioSector(bio, blockToSector(kvio->layer, vio->physical));
Packit Service d40955
Packit Service d40955
  // Metadata I/Os bypass the read cache.
Packit Service d40955
  if (isReadVIO(vio)) {
Packit Service d40955
    ASSERT_LOG_ONLY(!vioRequiresFlushBefore(vio),
Packit Service d40955
                    "read VIO does not require flush before");
Packit Service d40955
    vioAddTraceRecord(vio, THIS_LOCATION("$F;io=readMeta"));
Packit Service d40955
    setBioOperationRead(bio);
Packit Service d40955
  } else {
Packit Service d40955
    KernelLayerState state = getKernelLayerState(kvio->layer);
Packit Service d40955
    ASSERT_LOG_ONLY(((state == LAYER_RUNNING)
Packit Service d40955
                     || (state == LAYER_RESUMING)
Packit Service d40955
                     || (state = LAYER_STARTING)),
Packit Service d40955
                    "write metadata in allowed state %d", state);
Packit Service d40955
    if (vioRequiresFlushBefore(vio)) {
Packit Service d40955
      setBioOperationWrite(bio);
Packit Service d40955
      setBioOperationFlagPreflush(bio);
Packit Service d40955
      vioAddTraceRecord(vio, THIS_LOCATION("$F;io=flushWriteMeta"));
Packit Service d40955
    } else {
Packit Service d40955
      setBioOperationWrite(bio);
Packit Service d40955
      vioAddTraceRecord(vio, THIS_LOCATION("$F;io=writeMeta"));
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (vioRequiresFlushAfter(vio)) {
Packit Service d40955
    setBioOperationFlagFua(bio);
Packit Service d40955
  }
Packit Service d40955
  submitBio(bio, getMetadataAction(vio));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
Packit Service d40955
/**
Packit Service d40955
 * Handle the completion of a base-code initiated flush by continuing the flush
Packit Service d40955
 * VIO.
Packit Service d40955
 *
Packit Service d40955
 * @param bio    The bio to complete
Packit Service d40955
 **/
Packit Service d40955
static void completeFlushBio(BIO *bio)
Packit Service d40955
#else
Packit Service d40955
/**
Packit Service d40955
 * Handle the completion of a base-code initiated flush by continuing the flush
Packit Service d40955
 * VIO.
Packit Service d40955
 *
Packit Service d40955
 * @param bio    The bio to complete
Packit Service d40955
 * @param error  Possible error from underlying block device
Packit Service d40955
 **/
Packit Service d40955
static void completeFlushBio(BIO *bio, int error)
Packit Service d40955
#endif
Packit Service d40955
{
Packit Service d40955
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)
Packit Service d40955
  int error = getBioResult(bio);
Packit Service d40955
#endif
Packit Service d40955
  KVIO *kvio = (KVIO *) bio->bi_private;
Packit Service d40955
  // XXX This assumes a VDO-created bio around a buffer contains exactly 1
Packit Service d40955
  // page, which we believe is true, but do not assert.
Packit Service d40955
  bio->bi_vcnt = 1;
Packit Service d40955
  // Restore the bio's notion of its own data.
Packit Service d40955
  resetBio(bio, kvio->layer);
Packit Service d40955
  kvdoContinueKvio(kvio, error);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void kvdoFlushVIO(VIO *vio)
Packit Service d40955
{
Packit Service d40955
  KVIO        *kvio  = metadataKVIOAsKVIO(vioAsMetadataKVIO(vio));
Packit Service d40955
  BIO         *bio   = kvio->bio;
Packit Service d40955
  KernelLayer *layer = kvio->layer;
Packit Service d40955
  resetBio(bio, layer);
Packit Service d40955
  prepareFlushBIO(bio, kvio, getKernelLayerBdev(layer), completeFlushBio);
Packit Service d40955
  submitBio(bio, getMetadataAction(vio));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*
Packit Service d40955
 * Hook for a SystemTap probe to potentially restrict the choices
Packit Service d40955
 * of which VIOs should have their latencies tracked.
Packit Service d40955
 *
Packit Service d40955
 * Normally returns true. Even if true is returned, sampleThisOne may
Packit Service d40955
 * cut down the monitored VIOs by some fraction so as to reduce the
Packit Service d40955
 * impact on system performance.
Packit Service d40955
 *
Packit Service d40955
 * Must be "noinline" so that SystemTap can find the return
Packit Service d40955
 * instruction and modify the return value.
Packit Service d40955
 *
Packit Service d40955
 * @param kvio   The KVIO being initialized
Packit Service d40955
 * @param layer  The kernel layer
Packit Service d40955
 * @param bio    The incoming I/O request
Packit Service d40955
 *
Packit Service d40955
 * @return whether it's useful to track latency for VIOs looking like
Packit Service d40955
 *         this one
Packit Service d40955
 */
Packit Service d40955
static noinline bool
Packit Service d40955
sampleThisVIO(KVIO *kvio, KernelLayer *layer, BIO *bio)
Packit Service d40955
{
Packit Service d40955
  bool result = true;
Packit Service d40955
  // Ensure the arguments and result exist at the same time, for SystemTap.
Packit Service d40955
  __asm__ __volatile__(""
Packit Service d40955
                       : "=g" (result)
Packit Service d40955
                       : "0" (result),
Packit Service d40955
                         "g" (kvio),
Packit Service d40955
                         "g" (layer),
Packit Service d40955
                         "g" (bio)
Packit Service d40955
                       : "memory");
Packit Service d40955
  return result;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void initializeKVIO(KVIO        *kvio,
Packit Service d40955
                    KernelLayer *layer,
Packit Service d40955
                    VIOType      vioType,
Packit Service d40955
                    VIOPriority  priority,
Packit Service d40955
                    void        *parent,
Packit Service d40955
                    BIO         *bio)
Packit Service d40955
{
Packit Service d40955
  if (layer->vioTraceRecording
Packit Service d40955
      && sampleThisVIO(kvio, layer, bio)
Packit Service d40955
      && sampleThisOne(&layer->traceSampleCounter)) {
Packit Service d40955
    int result = (isDataVIOType(vioType)
Packit Service d40955
                  ? allocTraceFromPool(layer, &kvio->vio->trace)
Packit Service d40955
                  : ALLOCATE(1, Trace, "trace", &kvio->vio->trace));
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      logError("trace record allocation failure %d", result);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  kvio->bio   = bio;
Packit Service d40955
  kvio->layer = layer;
Packit Service d40955
  if (bio != NULL) {
Packit Service d40955
    bio->bi_private = kvio;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  initializeVIO(kvio->vio, vioType, priority, parent, getVDO(&layer->kvdo),
Packit Service d40955
                &layer->common);
Packit Service d40955
Packit Service d40955
  // XXX: The "init" label should be replaced depending on the
Packit Service d40955
  // write/read/flush path followed.
Packit Service d40955
  kvioAddTraceRecord(kvio, THIS_LOCATION("$F;io=?init;j=normal"));
Packit Service d40955
Packit Service d40955
  VDOCompletion *completion                = vioAsCompletion(kvio->vio);
Packit Service d40955
  kvio->enqueueable.enqueueable.completion = completion;
Packit Service d40955
  completion->enqueueable                  = &kvio->enqueueable.enqueueable;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Construct a metadata KVIO.
Packit Service d40955
 *
Packit Service d40955
 * @param [in]  layer            The physical layer
Packit Service d40955
 * @param [in]  vioType          The type of VIO to create
Packit Service d40955
 * @param [in]  priority         The relative priority to assign to the
Packit Service d40955
 *                               MetadataKVIO
Packit Service d40955
 * @param [in]  parent           The parent of the MetadataKVIO completion
Packit Service d40955
 * @param [in]  bio              The bio to associate with this MetadataKVIO
Packit Service d40955
 * @param [out] metadataKVIOPtr  A pointer to hold the new MetadataKVIO
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int makeMetadataKVIO(KernelLayer   *layer,
Packit Service d40955
                            VIOType        vioType,
Packit Service d40955
                            VIOPriority    priority,
Packit Service d40955
                            void          *parent,
Packit Service d40955
                            BIO           *bio,
Packit Service d40955
                            MetadataKVIO **metadataKVIOPtr)
Packit Service d40955
{
Packit Service d40955
  // If MetadataKVIO grows past 256 bytes, we'll lose benefits of VDOSTORY-176.
Packit Service d40955
  STATIC_ASSERT(sizeof(MetadataKVIO) <= 256);
Packit Service d40955
Packit Service d40955
  // Metadata VIOs should use direct allocation and not use the buffer pool,
Packit Service d40955
  // which is reserved for submissions from the linux block layer.
Packit Service d40955
  MetadataKVIO *metadataKVIO;
Packit Service d40955
  int result = ALLOCATE(1, MetadataKVIO, __func__, &metadataKVIO);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    logError("metadata KVIO allocation failure %d", result);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  KVIO *kvio = &metadataKVIO->kvio;
Packit Service d40955
  kvio->vio  = &metadataKVIO->vio;
Packit Service d40955
  initializeKVIO(kvio, layer, vioType, priority, parent, bio);
Packit Service d40955
  *metadataKVIOPtr = metadataKVIO;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Construct a CompressedWriteKVIO.
Packit Service d40955
 *
Packit Service d40955
 * @param [in]  layer                   The physical layer
Packit Service d40955
 * @param [in]  parent                  The parent of the CompressedWriteKVIO
Packit Service d40955
 *                                      completion
Packit Service d40955
 * @param [in]  bio                     The bio to associate with this
Packit Service d40955
 *                                      CompressedWriteKVIO
Packit Service d40955
 * @param [out] compressedWriteKVIOPtr  A pointer to hold the new
Packit Service d40955
 *                                      CompressedWriteKVIO
Packit Service d40955
 *
Packit Service d40955
 * @return VDO_SUCCESS or an error
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int
Packit Service d40955
makeCompressedWriteKVIO(KernelLayer          *layer,
Packit Service d40955
                        void                 *parent,
Packit Service d40955
                        BIO                  *bio,
Packit Service d40955
                        CompressedWriteKVIO **compressedWriteKVIOPtr)
Packit Service d40955
{
Packit Service d40955
  // Compressed write VIOs should use direct allocation and not use the buffer
Packit Service d40955
  // pool, which is reserved for submissions from the linux block layer.
Packit Service d40955
  CompressedWriteKVIO *compressedWriteKVIO;
Packit Service d40955
  int result = ALLOCATE(1, CompressedWriteKVIO, __func__,
Packit Service d40955
                        &compressedWriteKVIO);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    logError("compressed write KVIO allocation failure %d", result);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  KVIO *kvio = &compressedWriteKVIO->kvio;
Packit Service d40955
  kvio->vio  = allocatingVIOAsVIO(&compressedWriteKVIO->allocatingVIO);
Packit Service d40955
  initializeKVIO(kvio, layer, VIO_TYPE_COMPRESSED_BLOCK,
Packit Service d40955
                 VIO_PRIORITY_COMPRESSED_DATA, parent, bio);
Packit Service d40955
  *compressedWriteKVIOPtr = compressedWriteKVIO;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int kvdoCreateMetadataVIO(PhysicalLayer  *layer,
Packit Service d40955
                          VIOType         vioType,
Packit Service d40955
                          VIOPriority     priority,
Packit Service d40955
                          void           *parent,
Packit Service d40955
                          char           *data,
Packit Service d40955
                          VIO           **vioPtr)
Packit Service d40955
{
Packit Service d40955
  int result = ASSERT(isMetadataVIOType(vioType),
Packit Service d40955
                      "%d is a metadata type", vioType);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  BIO *bio;
Packit Service d40955
  KernelLayer *kernelLayer = asKernelLayer(layer);
Packit Service d40955
  result = createBio(kernelLayer, data, &bio;;
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  MetadataKVIO *metadataKVIO;
Packit Service d40955
  result = makeMetadataKVIO(kernelLayer, vioType, priority, parent, bio,
Packit Service d40955
                            &metadataKVIO);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeBio(bio, kernelLayer);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  *vioPtr = &metadataKVIO->vio;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int kvdoCreateCompressedWriteVIO(PhysicalLayer  *layer,
Packit Service d40955
                                 void           *parent,
Packit Service d40955
                                 char           *data,
Packit Service d40955
                                 AllocatingVIO **allocatingVIOPtr)
Packit Service d40955
{
Packit Service d40955
  BIO *bio;
Packit Service d40955
  KernelLayer *kernelLayer = asKernelLayer(layer);
Packit Service d40955
  int result = createBio(kernelLayer, data, &bio;;
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  CompressedWriteKVIO *compressedWriteKVIO;
Packit Service d40955
  result = makeCompressedWriteKVIO(kernelLayer, parent, bio,
Packit Service d40955
                                   &compressedWriteKVIO);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeBio(bio, kernelLayer);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  *allocatingVIOPtr = &compressedWriteKVIO->allocatingVIO;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}