Blame source/vdo/kernel/kvdoFlush.c

Packit Service 75d76b
/*
Packit Service 75d76b
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 75d76b
 *
Packit Service 75d76b
 * This program is free software; you can redistribute it and/or
Packit Service 75d76b
 * modify it under the terms of the GNU General Public License
Packit Service 75d76b
 * as published by the Free Software Foundation; either version 2
Packit Service 75d76b
 * of the License, or (at your option) any later version.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * This program is distributed in the hope that it will be useful,
Packit Service 75d76b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 75d76b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 75d76b
 * GNU General Public License for more details.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * You should have received a copy of the GNU General Public License
Packit Service 75d76b
 * along with this program; if not, write to the Free Software
Packit Service 75d76b
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 75d76b
 * 02110-1301, USA. 
Packit Service 75d76b
 *
Packit Service 75d76b
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/kvdoFlush.c#6 $
Packit Service 75d76b
 */
Packit Service 75d76b
Packit Service 75d76b
#include "kvdoFlush.h"
Packit Service 75d76b
Packit Service 75d76b
#include "logger.h"
Packit Service 75d76b
#include "memoryAlloc.h"
Packit Service 75d76b
Packit Service 75d76b
#include "threadConfig.h"
Packit Service 75d76b
Packit Service 75d76b
#include "bio.h"
Packit Service 75d76b
#include "ioSubmitter.h"
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * A specific (concrete) encapsulation of flush requests.
Packit Service 75d76b
 *
Packit Service 75d76b
 * 

We attempt to allocate a KVDOFlush objects for each incoming flush bio.

Packit Service 75d76b
 * In case the allocate fails, a spare object is pre-allocated by and stored
Packit Service 75d76b
 * in the kernel layer. The first time an allocation fails, the spare is used.
Packit Service 75d76b
 * If another allocation fails while the spare is in use, it will merely be
Packit Service 75d76b
 * queued for later processing.
Packit Service 75d76b
 *
Packit Service 75d76b
 * 

When a KVDOFlush is complete, it will either be freed, immediately

Packit Service 75d76b
 * re-used for queued flushes, or stashed in the kernel layer as the new spare
Packit Service 75d76b
 * object. This ensures that we will always make forward progress.
Packit Service 75d76b
 **/
Packit Service 75d76b
struct kvdoFlush {
Packit Service 75d76b
  KvdoWorkItem     workItem;
Packit Service 75d76b
  KernelLayer     *layer;
Packit Service 75d76b
  struct bio_list  bios;
Packit Service 75d76b
  Jiffies          arrivalTime;  // Time when earliest bio appeared
Packit Service 75d76b
  VDOFlush         vdoFlush;
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int makeKVDOFlush(KVDOFlush **flushPtr)
Packit Service 75d76b
{
Packit Service 75d76b
  return ALLOCATE(1, KVDOFlush, __func__, flushPtr);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
bool shouldProcessFlush(KernelLayer *layer)
Packit Service 75d76b
{
Packit Service 75d76b
  return (getKVDOWritePolicy(&layer->kvdo) != WRITE_POLICY_SYNC);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Function call to handle an empty flush request from the request queue.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param item  The work item representing the flush request
Packit Service 75d76b
 **/
Packit Service 75d76b
static void kvdoFlushWork(KvdoWorkItem *item)
Packit Service 75d76b
{
Packit Service 75d76b
  KVDOFlush *kvdoFlush = container_of(item, KVDOFlush, workItem);
Packit Service 75d76b
  flush(kvdoFlush->layer->kvdo.vdo, &kvdoFlush->vdoFlush);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Initialize a KVDOFlush object, transferring all the bios in the kernel
Packit Service 75d76b
 * layer's waitingFlushes list to it. The caller MUST already hold the layer's
Packit Service 75d76b
 * flushLock.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param kvdoFlush  The flush to initialize
Packit Service 75d76b
 * @param layer      The kernel layer on which the flushLock is held
Packit Service 75d76b
 **/
Packit Service 75d76b
static void initializeKVDOFlush(KVDOFlush *kvdoFlush, KernelLayer *layer)
Packit Service 75d76b
{
Packit Service 75d76b
  kvdoFlush->layer = layer;
Packit Service 75d76b
  bio_list_init(&kvdoFlush->bios);
Packit Service 75d76b
  bio_list_merge(&kvdoFlush->bios, &layer->waitingFlushes);
Packit Service 75d76b
  bio_list_init(&layer->waitingFlushes);
Packit Service 75d76b
  kvdoFlush->arrivalTime = layer->flushArrivalTime;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
static void enqueueKVDOFlush(KVDOFlush *kvdoFlush)
Packit Service 75d76b
{
Packit Service 75d76b
  setupWorkItem(&kvdoFlush->workItem, kvdoFlushWork, NULL, REQ_Q_ACTION_FLUSH);
Packit Service 75d76b
  KVDO *kvdo = &kvdoFlush->layer->kvdo;
Packit Service 75d76b
  enqueueKVDOWork(kvdo, &kvdoFlush->workItem,
Packit Service 75d76b
                  getPackerZoneThread(getThreadConfig(kvdo->vdo)));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void launchKVDOFlush(KernelLayer *layer, BIO *bio)
Packit Service 75d76b
{
Packit Service 75d76b
  // Try to allocate a KVDOFlush to represent the flush request. If the
Packit Service 75d76b
  // allocation fails, we'll deal with it later.
Packit Service 75d76b
  KVDOFlush *kvdoFlush = ALLOCATE_NOWAIT(KVDOFlush, __func__);
Packit Service 75d76b
Packit Service 75d76b
  spin_lock(&layer->flushLock);
Packit Service 75d76b
Packit Service 75d76b
  // We have a new bio to start.  Add it to the list.  If it becomes the
Packit Service 75d76b
  // only entry on the list, record the time.
Packit Service 75d76b
  if (bio_list_empty(&layer->waitingFlushes)) {
Packit Service 75d76b
    layer->flushArrivalTime = jiffies;
Packit Service 75d76b
  }
Packit Service 75d76b
  bio_list_add(&layer->waitingFlushes, bio);
Packit Service 75d76b
Packit Service 75d76b
  if (kvdoFlush == NULL) {
Packit Service 75d76b
    // The KVDOFlush allocation failed. Try to use the spare KVDOFlush object.
Packit Service 75d76b
    if (layer->spareKVDOFlush == NULL) {
Packit Service 75d76b
      // The spare is already in use. This bio is on waitingFlushes and it
Packit Service 75d76b
      // will be handled by a flush completion or by a bio that can allocate.
Packit Service 75d76b
      spin_unlock(&layer->flushLock);
Packit Service 75d76b
      return;
Packit Service 75d76b
    }
Packit Service 75d76b
Packit Service 75d76b
    // Take and use the spare KVDOFlush object.
Packit Service 75d76b
    kvdoFlush = layer->spareKVDOFlush;
Packit Service 75d76b
    layer->spareKVDOFlush = NULL;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  // We have flushes to start. Capture them in the KVDOFlush object.
Packit Service 75d76b
  initializeKVDOFlush(kvdoFlush, layer);
Packit Service 75d76b
Packit Service 75d76b
  spin_unlock(&layer->flushLock);
Packit Service 75d76b
Packit Service 75d76b
  // Finish launching the flushes.
Packit Service 75d76b
  enqueueKVDOFlush(kvdoFlush);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Release a KVDOFlush object that has completed its work. If there are any
Packit Service 75d76b
 * pending flush requests whose KVDOFlush allocation failed, they will be
Packit Service 75d76b
 * launched by immediately re-using the released KVDOFlush. If there is no
Packit Service 75d76b
 * spare KVDOFlush, the released object will become the spare. Otherwise, the
Packit Service 75d76b
 * KVDOFlush will be freed.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param kvdoFlush  The completed flush object to re-use or free
Packit Service 75d76b
 **/
Packit Service 75d76b
static void releaseKVDOFlush(KVDOFlush *kvdoFlush)
Packit Service 75d76b
{
Packit Service 75d76b
  KernelLayer *layer = kvdoFlush->layer;
Packit Service 75d76b
  bool relaunchFlush = false;
Packit Service 75d76b
  bool freeFlush     = false;
Packit Service 75d76b
Packit Service 75d76b
  spin_lock(&layer->flushLock);
Packit Service 75d76b
  if (bio_list_empty(&layer->waitingFlushes)) {
Packit Service 75d76b
    // Nothing needs to be started.  Save one spare KVDOFlush object.
Packit Service 75d76b
    if (layer->spareKVDOFlush == NULL) {
Packit Service 75d76b
      // Make the new spare all zero, just like a newly allocated one.
Packit Service 75d76b
      memset(kvdoFlush, 0, sizeof(*kvdoFlush));
Packit Service 75d76b
      layer->spareKVDOFlush = kvdoFlush;
Packit Service 75d76b
    } else {
Packit Service 75d76b
      freeFlush = true;
Packit Service 75d76b
    }
Packit Service 75d76b
  } else {
Packit Service 75d76b
    // We have flushes to start.  Capture them in the KVDOFlush object.
Packit Service 75d76b
    initializeKVDOFlush(kvdoFlush, layer);
Packit Service 75d76b
    relaunchFlush = true;
Packit Service 75d76b
  }
Packit Service 75d76b
  spin_unlock(&layer->flushLock);
Packit Service 75d76b
Packit Service 75d76b
  if (relaunchFlush) {
Packit Service 75d76b
    // Finish launching the flushes.
Packit Service 75d76b
    enqueueKVDOFlush(kvdoFlush);
Packit Service 75d76b
  } else if (freeFlush) {
Packit Service 75d76b
    FREE(kvdoFlush);
Packit Service 75d76b
  }
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Function called to complete and free a flush request
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param item    The flush-request work item
Packit Service 75d76b
 **/
Packit Service 75d76b
static void kvdoCompleteFlushWork(KvdoWorkItem *item)
Packit Service 75d76b
{
Packit Service 75d76b
  KVDOFlush   *kvdoFlush = container_of(item, KVDOFlush, workItem);
Packit Service 75d76b
  KernelLayer *layer     = kvdoFlush->layer;
Packit Service 75d76b
Packit Service 75d76b
  BIO *bio;
Packit Service 75d76b
  while ((bio = bio_list_pop(&kvdoFlush->bios)) != NULL) {
Packit Service 75d76b
    // We're not acknowledging this bio now, but we'll never touch it
Packit Service 75d76b
    // again, so this is the last chance to account for it.
Packit Service 75d76b
    countBios(&layer->biosAcknowledged, bio);
Packit Service 75d76b
Packit Service 75d76b
    // Make sure the bio is a empty flush bio.
Packit Service 75d76b
    prepareFlushBIO(bio, bio->bi_private, getKernelLayerBdev(layer),
Packit Service 75d76b
                    bio->bi_end_io);
Packit Service 75d76b
    atomic64_inc(&layer->flushOut);
Packit Service 75d76b
    generic_make_request(bio);
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
Packit Service 75d76b
  // Release the KVDOFlush object, freeing it, re-using it as the spare, or
Packit Service 75d76b
  // using it to launch any flushes that had to wait when allocations failed.
Packit Service 75d76b
  releaseKVDOFlush(kvdoFlush);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
void kvdoCompleteFlush(VDOFlush **kfp)
Packit Service 75d76b
{
Packit Service 75d76b
  if (*kfp != NULL) {
Packit Service 75d76b
    KVDOFlush *kvdoFlush = container_of(*kfp, KVDOFlush, vdoFlush);
Packit Service 75d76b
    setupWorkItem(&kvdoFlush->workItem, kvdoCompleteFlushWork, NULL,
Packit Service 75d76b
                  BIO_Q_ACTION_FLUSH);
Packit Service 75d76b
    enqueueBioWorkItem(kvdoFlush->layer->ioSubmitter,
Packit Service 75d76b
                       &kvdoFlush->workItem);
Packit Service 75d76b
    *kfp = NULL;
Packit Service 75d76b
  }
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int synchronousFlush(KernelLayer *layer)
Packit Service 75d76b
{
Packit Service 75d76b
  BIO bio;
Packit Service 75d76b
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
Packit Service 75d76b
  bio_init(&bio, 0, 0);
Packit Service 75d76b
#else
Packit Service 75d76b
  bio_init(&bio;;
Packit Service 75d76b
#endif
Packit Service 75d76b
  int result = 0;
Packit Service 75d76b
Packit Service 75d76b
  prepareFlushBIO(&bio, layer, getKernelLayerBdev(layer), NULL);
Packit Service 75d76b
  result = submitBioAndWait(&bio;;
Packit Service 75d76b
  atomic64_inc(&layer->flushOut);
Packit Service 75d76b
  if (result != 0) {
Packit Service 75d76b
    logErrorWithStringError(result, "synchronous flush failed");
Packit Service 75d76b
    result = -EIO;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0)
Packit Service 75d76b
  bio_uninit(&bio;;
Packit Service 75d76b
#endif
Packit Service 75d76b
  return result;
Packit Service 75d76b
}