Blame vdo/kernel/kvdoFlush.c

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

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

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

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

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