Blame source/vdo/kernel/dmvdo.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/dmvdo.c#42 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "dmvdo.h"
Packit Service d40955
Packit Service d40955
#include <linux/module.h>
Packit Service d40955
Packit Service d40955
#include "logger.h"
Packit Service d40955
#include "memoryAlloc.h"
Packit Service d40955
Packit Service d40955
#include "constants.h"
Packit Service d40955
#include "ringNode.h"
Packit Service d40955
#include "threadConfig.h"
Packit Service d40955
#include "vdo.h"
Packit Service d40955
Packit Service d40955
#include "dedupeIndex.h"
Packit Service d40955
#include "deviceRegistry.h"
Packit Service d40955
#include "dump.h"
Packit Service d40955
#include "instanceNumber.h"
Packit Service d40955
#include "ioSubmitter.h"
Packit Service d40955
#include "kernelLayer.h"
Packit Service d40955
#include "kvdoFlush.h"
Packit Service d40955
#include "memoryUsage.h"
Packit Service d40955
#include "statusProcfs.h"
Packit Service d40955
#include "stringUtils.h"
Packit Service d40955
#include "sysfs.h"
Packit Service d40955
#include "threadDevice.h"
Packit Service d40955
#include "threadRegistry.h"
Packit Service d40955
Packit Service d40955
struct kvdoDevice kvdoDevice;   // global driver state (poorly named)
Packit Service d40955
Packit Service d40955
/*
Packit Service d40955
 * Pre kernel version 4.3, we use the functionality in blkdev_issue_discard
Packit Service d40955
 * and the value in max_discard_sectors to split large discards into smaller
Packit Service d40955
 * ones. 4.3 to 4.18 kernels have removed the code in blkdev_issue_discard
Packit Service d40955
 * and so in place of that, we use the code in device mapper itself to
Packit Service d40955
 * split the discards. Unfortunately, it uses the same value to split large
Packit Service d40955
 * discards as it does to split large data bios.
Packit Service d40955
 *
Packit Service d40955
 * In kernel version 4.18, support for splitting discards was added
Packit Service d40955
 * back into blkdev_issue_discard. Since this mode of splitting
Packit Service d40955
 * (based on max_discard_sectors) is preferable to splitting always
Packit Service d40955
 * on 4k, we are turning off the device mapper splitting from 4.18
Packit Service d40955
 * on.
Packit Service d40955
 */
Packit Service d40955
#define HAS_NO_BLKDEV_SPLIT LINUX_VERSION_CODE >= KERNEL_VERSION(4,3,0) \
Packit Service d40955
                            && LINUX_VERSION_CODE < KERNEL_VERSION(4,18,0)
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get the kernel layer associated with a dm target structure.
Packit Service d40955
 *
Packit Service d40955
 * @param ti  The dm target structure
Packit Service d40955
 *
Packit Service d40955
 * @return The kernel layer, or NULL.
Packit Service d40955
 **/
Packit Service d40955
static KernelLayer *getKernelLayerForTarget(struct dm_target *ti)
Packit Service d40955
{
Packit Service d40955
  return ((DeviceConfig *) ti->private)->layer;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Begin VDO processing of a bio.  This is called by the device mapper
Packit Service d40955
 * through the "map" function, and has resulted from a call to either
Packit Service d40955
 * submit_bio or generic_make_request.
Packit Service d40955
 *
Packit Service d40955
 * @param ti      The dm_target.  We only need the "private" member to give
Packit Service d40955
 *                us the KernelLayer.
Packit Service d40955
 * @param bio     The bio.
Packit Service d40955
 *
Packit Service d40955
 * @return One of these values:
Packit Service d40955
 *
Packit Service d40955
 *         negative            A negative value is an error code.
Packit Service d40955
 *                             Usually -EIO.
Packit Service d40955
 *
Packit Service d40955
 *         DM_MAPIO_SUBMITTED  VDO will take care of this I/O, either
Packit Service d40955
 *                             processing it completely and calling
Packit Service d40955
 *                             bio_endio, or forwarding it onward by
Packit Service d40955
 *                             calling generic_make_request.
Packit Service d40955
 *
Packit Service d40955
 *         DM_MAPIO_REMAPPED   VDO has modified the bio and the device
Packit Service d40955
 *                             mapper will immediately forward the bio
Packit Service d40955
 *                             onward using generic_make_request.
Packit Service d40955
 *
Packit Service d40955
 *         DM_MAPIO_REQUEUE    We do not use this.  It is used by device
Packit Service d40955
 *                             mapper devices to defer an I/O request
Packit Service d40955
 *                             during suspend/resume processing.
Packit Service d40955
 **/
Packit Service d40955
static int vdoMapBio(struct dm_target *ti, BIO *bio)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  return kvdoMapBio(layer, bio);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoIoHints(struct dm_target *ti, struct queue_limits *limits)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
Packit Service d40955
  limits->logical_block_size  = layer->deviceConfig->logicalBlockSize;
Packit Service d40955
  limits->physical_block_size = VDO_BLOCK_SIZE;
Packit Service d40955
Packit Service d40955
  // The minimum io size for random io
Packit Service d40955
  blk_limits_io_min(limits, VDO_BLOCK_SIZE);
Packit Service d40955
  // The optimal io size for streamed/sequential io
Packit Service d40955
  blk_limits_io_opt(limits, VDO_BLOCK_SIZE);
Packit Service d40955
Packit Service d40955
  /*
Packit Service d40955
   * Sets the maximum discard size that will be passed into VDO. This value
Packit Service d40955
   * comes from a table line value passed in during dmsetup create.
Packit Service d40955
   *
Packit Service d40955
   * The value 1024 is the largest usable value on HD systems.  A 2048 sector
Packit Service d40955
   * discard on a busy HD system takes 31 seconds.  We should use a value no
Packit Service d40955
   * higher than 1024, which takes 15 to 16 seconds on a busy HD system.
Packit Service d40955
   *
Packit Service d40955
   * But using large values results in 120 second blocked task warnings in
Packit Service d40955
   * /var/log/kern.log.  In order to avoid these warnings, we choose to use the
Packit Service d40955
   * smallest reasonable value.  See VDO-3062 and VDO-3087.
Packit Service d40955
   *
Packit Service d40955
   * We allow setting of the value for max_discard_sectors even in situations
Packit Service d40955
   * where we only split on 4k (see comments for HAS_NO_BLKDEV_SPLIT) as the
Packit Service d40955
   * value is still used in other code, like sysfs display of queue limits and
Packit Service d40955
   * most especially in dm-thin to determine whether to pass down discards.
Packit Service d40955
   */
Packit Service d40955
  limits->max_discard_sectors
Packit Service d40955
    = layer->deviceConfig->maxDiscardBlocks * VDO_SECTORS_PER_BLOCK;
Packit Service d40955
Packit Service d40955
  limits->discard_granularity = VDO_BLOCK_SIZE;
Packit Service d40955
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
Packit Service d40955
  limits->discard_zeroes_data = 1;
Packit Service d40955
#endif
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static int vdoIterateDevices(struct dm_target           *ti,
Packit Service d40955
                             iterate_devices_callout_fn  fn,
Packit Service d40955
                             void                       *data)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  sector_t len = blockToSector(layer, layer->deviceConfig->physicalBlocks);
Packit Service d40955
Packit Service d40955
  return fn(ti, layer->deviceConfig->ownedDevice, 0, len, data);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*
Packit Service d40955
 * Status line is:
Packit Service d40955
 *    <device> <operating mode> <in recovery> <index state>
Packit Service d40955
 *    <compression state> <used physical blocks> <total physical blocks>
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoStatus(struct dm_target *ti,
Packit Service d40955
                      status_type_t     status_type,
Packit Service d40955
                      unsigned int      status_flags,
Packit Service d40955
                      char             *result,
Packit Service d40955
                      unsigned int      maxlen)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  char nameBuffer[BDEVNAME_SIZE];
Packit Service d40955
  // N.B.: The DMEMIT macro uses the variables named "sz", "result", "maxlen".
Packit Service d40955
  int sz = 0;
Packit Service d40955
Packit Service d40955
  switch (status_type) {
Packit Service d40955
  case STATUSTYPE_INFO:
Packit Service d40955
    // Report info for dmsetup status
Packit Service d40955
    mutex_lock(&layer->statsMutex);
Packit Service d40955
    getKVDOStatistics(&layer->kvdo, &layer->vdoStatsStorage);
Packit Service d40955
    VDOStatistics *stats = &layer->vdoStatsStorage;
Packit Service d40955
    DMEMIT("/dev/%s %s %s %s %s %llu %llu",
Packit Service d40955
           bdevname(getKernelLayerBdev(layer), nameBuffer),
Packit Service d40955
	   stats->mode,
Packit Service d40955
	   stats->inRecoveryMode ? "recovering" : "-",
Packit Service d40955
	   getDedupeStateName(layer->dedupeIndex),
Packit Service d40955
	   getKVDOCompressing(&layer->kvdo) ? "online" : "offline",
Packit Service d40955
	   stats->dataBlocksUsed + stats->overheadBlocksUsed,
Packit Service d40955
	   stats->physicalBlocks);
Packit Service d40955
    mutex_unlock(&layer->statsMutex);
Packit Service d40955
    break;
Packit Service d40955
Packit Service d40955
  case STATUSTYPE_TABLE:
Packit Service d40955
    // Report the string actually specified in the beginning.
Packit Service d40955
    DMEMIT("%s", ((DeviceConfig *) ti->private)->originalString);
Packit Service d40955
    break;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
//  spin_unlock_irqrestore(&layer->lock, flags);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get the size of the underlying device, in blocks.
Packit Service d40955
 *
Packit Service d40955
 * @param [in] layer  The layer
Packit Service d40955
 *
Packit Service d40955
 * @return The size in blocks
Packit Service d40955
 **/
Packit Service d40955
static BlockCount getUnderlyingDeviceBlockCount(KernelLayer *layer)
Packit Service d40955
{
Packit Service d40955
  uint64_t physicalSize = i_size_read(getKernelLayerBdev(layer)->bd_inode);
Packit Service d40955
  return physicalSize / VDO_BLOCK_SIZE;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static int vdoPrepareToGrowLogical(KernelLayer *layer, char *sizeString)
Packit Service d40955
{
Packit Service d40955
  BlockCount logicalCount;
Packit Service d40955
  if (sscanf(sizeString, "%llu", &logicalCount) != 1) {
Packit Service d40955
    logWarning("Logical block count \"%s\" is not a number", sizeString);
Packit Service d40955
    return -EINVAL;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (logicalCount > MAXIMUM_LOGICAL_BLOCKS) {
Packit Service d40955
    logWarning("Logical block count \"%llu\" exceeds the maximum (%"
Packit Service d40955
               PRIu64 ")", logicalCount, MAXIMUM_LOGICAL_BLOCKS);
Packit Service d40955
    return -EINVAL;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return prepareToResizeLogical(layer, logicalCount);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Process a dmsetup message now that we know no other message is being
Packit Service d40955
 * processed.
Packit Service d40955
 *
Packit Service d40955
 * @param layer The layer to which the message was sent
Packit Service d40955
 * @param argc  The argument count of the message
Packit Service d40955
 * @param argv  The arguments to the message
Packit Service d40955
 *
Packit Service d40955
 * @return -EINVAL if the message is unrecognized or the result of processing
Packit Service d40955
 *                 the message
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int processVDOMessageLocked(KernelLayer   *layer,
Packit Service d40955
                                   unsigned int   argc,
Packit Service d40955
                                   char         **argv)
Packit Service d40955
{
Packit Service d40955
  // Messages with variable numbers of arguments.
Packit Service d40955
  if (strncasecmp(argv[0], "x-", 2) == 0) {
Packit Service d40955
    int result = performKVDOExtendedCommand(&layer->kvdo, argc, argv);
Packit Service d40955
    if (result == VDO_UNKNOWN_COMMAND) {
Packit Service d40955
      logWarning("unknown extended command '%s' to dmsetup message", argv[0]);
Packit Service d40955
      result = -EINVAL;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Messages with fixed numbers of arguments.
Packit Service d40955
  switch (argc) {
Packit Service d40955
  case 1:
Packit Service d40955
    if (strcasecmp(argv[0], "sync-dedupe") == 0) {
Packit Service d40955
      waitForNoRequestsActive(layer);
Packit Service d40955
      return 0;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "trace-on") == 0) {
Packit Service d40955
      logInfo("Tracing on");
Packit Service d40955
      layer->traceLogging = true;
Packit Service d40955
      return 0;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "trace-off") == 0) {
Packit Service d40955
      logInfo("Tracing off");
Packit Service d40955
      layer->traceLogging = false;
Packit Service d40955
      return 0;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "prepareToGrowPhysical") == 0) {
Packit Service d40955
      return prepareToResizePhysical(layer,
Packit Service d40955
                                     getUnderlyingDeviceBlockCount(layer));
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "growPhysical") == 0) {
Packit Service d40955
      // The actual growPhysical will happen when the device is resumed.
Packit Service d40955
Packit Service d40955
      if (layer->deviceConfig->version != 0) {
Packit Service d40955
        // XXX Uncomment this branch when new VDO manager is updated to not
Packit Service d40955
        // send this message.
Packit Service d40955
Packit Service d40955
        // Old style message on new style table is unexpected; it means the
Packit Service d40955
        // user started the VDO with new manager and is growing with old.
Packit Service d40955
        // logInfo("Mismatch between growPhysical method and table version.");
Packit Service d40955
        // return -EINVAL;
Packit Service d40955
      } else {
Packit Service d40955
        layer->deviceConfig->physicalBlocks
Packit Service d40955
          = getUnderlyingDeviceBlockCount(layer);
Packit Service d40955
      }
Packit Service d40955
      return 0;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    break;
Packit Service d40955
Packit Service d40955
  case 2:
Packit Service d40955
    if (strcasecmp(argv[0], "compression") == 0) {
Packit Service d40955
      if (strcasecmp(argv[1], "on") == 0) {
Packit Service d40955
        setKVDOCompressing(&layer->kvdo, true);
Packit Service d40955
        return 0;
Packit Service d40955
      }
Packit Service d40955
Packit Service d40955
      if (strcasecmp(argv[1], "off") == 0) {
Packit Service d40955
        setKVDOCompressing(&layer->kvdo, false);
Packit Service d40955
        return 0;
Packit Service d40955
      }
Packit Service d40955
Packit Service d40955
      logWarning("invalid argument '%s' to dmsetup compression message",
Packit Service d40955
                 argv[1]);
Packit Service d40955
      return -EINVAL;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "prepareToGrowLogical") == 0) {
Packit Service d40955
      return vdoPrepareToGrowLogical(layer, argv[1]);
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    break;
Packit Service d40955
Packit Service d40955
Packit Service d40955
  default:
Packit Service d40955
    break;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  logWarning("unrecognized dmsetup message '%s' received", argv[0]);
Packit Service d40955
  return -EINVAL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Process a dmsetup message. If the message is a dump, just do it. Otherwise,
Packit Service d40955
 * check that no other message is being processed, and only proceed if so.
Packit Service d40955
 *
Packit Service d40955
 * @param layer The layer to which the message was sent
Packit Service d40955
 * @param argc  The argument count of the message
Packit Service d40955
 * @param argv  The arguments to the message
Packit Service d40955
 *
Packit Service d40955
 * @return -EBUSY if another message is being processed or the result of
Packit Service d40955
 *                processsing the message
Packit Service d40955
 **/
Packit Service d40955
__attribute__((warn_unused_result))
Packit Service d40955
static int processVDOMessage(KernelLayer   *layer,
Packit Service d40955
                             unsigned int   argc,
Packit Service d40955
                             char         **argv)
Packit Service d40955
{
Packit Service d40955
  /*
Packit Service d40955
   * All messages which may be processed in parallel with other messages should
Packit Service d40955
   * be handled here before the atomic check below. Messages which should be
Packit Service d40955
   * exclusive should be processed in processVDOMessageLocked().
Packit Service d40955
   */
Packit Service d40955
Packit Service d40955
  // Dump messages should always be processed
Packit Service d40955
  if (strcasecmp(argv[0], "dump") == 0) {
Packit Service d40955
    return vdoDump(layer, argc, argv, "dmsetup message");
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (argc == 1) {
Packit Service d40955
    if (strcasecmp(argv[0], "dump-on-shutdown") == 0) {
Packit Service d40955
      layer->dumpOnShutdown = true;
Packit Service d40955
      return 0;
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    // Index messages should always be processed
Packit Service d40955
    if ((strcasecmp(argv[0], "index-close") == 0)
Packit Service d40955
        || (strcasecmp(argv[0], "index-create") == 0)
Packit Service d40955
        || (strcasecmp(argv[0], "index-disable") == 0)
Packit Service d40955
        || (strcasecmp(argv[0], "index-enable") == 0)) {
Packit Service d40955
      return messageDedupeIndex(layer->dedupeIndex, argv[0]);
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    // XXX - the "connect" messages are misnamed for the kernel index.  These
Packit Service d40955
    //       messages should go away when all callers have been fixed to use
Packit Service d40955
    //       "index-enable" or "index-disable".
Packit Service d40955
    if (strcasecmp(argv[0], "reconnect") == 0) {
Packit Service d40955
      return messageDedupeIndex(layer->dedupeIndex, "index-enable");
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "connect") == 0) {
Packit Service d40955
      return messageDedupeIndex(layer->dedupeIndex, "index-enable");
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    if (strcasecmp(argv[0], "disconnect") == 0) {
Packit Service d40955
      return messageDedupeIndex(layer->dedupeIndex, "index-disable");
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (!compareAndSwapBool(&layer->processingMessage, false, true)) {
Packit Service d40955
    return -EBUSY;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  int result = processVDOMessageLocked(layer, argc, argv);
Packit Service d40955
  atomicStoreBool(&layer->processingMessage, false);
Packit Service d40955
  return result;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
Packit Service d40955
static int vdoMessage(struct dm_target  *ti,
Packit Service d40955
                      unsigned int       argc,
Packit Service d40955
                      char             **argv,
Packit Service d40955
                      char              *resultBuffer,
Packit Service d40955
                      unsigned int       maxlen)
Packit Service d40955
#else
Packit Service d40955
static int vdoMessage(struct dm_target *ti, unsigned int argc, char **argv)
Packit Service d40955
#endif
Packit Service d40955
{
Packit Service d40955
  if (argc == 0) {
Packit Service d40955
    logWarning("unspecified dmsetup message");
Packit Service d40955
    return -EINVAL;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  RegisteredThread allocatingThread, instanceThread;
Packit Service d40955
  registerAllocatingThread(&allocatingThread, NULL);
Packit Service d40955
  registerThreadDevice(&instanceThread, layer);
Packit Service d40955
  int result = processVDOMessage(layer, argc, argv);
Packit Service d40955
  unregisterThreadDeviceID();
Packit Service d40955
  unregisterAllocatingThread();
Packit Service d40955
  return mapToSystemError(result);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Configure the dm_target with our capabilities.
Packit Service d40955
 *
Packit Service d40955
 * @param ti    The device mapper target representing our device
Packit Service d40955
 * @param layer The kernel layer to get the write policy from
Packit Service d40955
 **/
Packit Service d40955
static void configureTargetCapabilities(struct dm_target *ti,
Packit Service d40955
                                        KernelLayer      *layer)
Packit Service d40955
{
Packit Service d40955
  ti->discards_supported = 1;
Packit Service d40955
Packit Service d40955
  /**
Packit Service d40955
   * This may appear to indicate we don't support flushes in sync mode.
Packit Service d40955
   * However, dm will set up the request queue to accept flushes if any
Packit Service d40955
   * device in the stack accepts flushes. Hence if the device under VDO
Packit Service d40955
   * accepts flushes, we will receive flushes.
Packit Service d40955
   **/
Packit Service d40955
  ti->flush_supported = shouldProcessFlush(layer);
Packit Service d40955
  ti->num_discard_bios = 1;
Packit Service d40955
  ti->num_flush_bios = 1;
Packit Service d40955
Packit Service d40955
  // If this value changes, please make sure to update the
Packit Service d40955
  // value for maxDiscardSectors accordingly.
Packit Service d40955
  BUG_ON(dm_set_target_max_io_len(ti, VDO_SECTORS_PER_BLOCK) != 0);
Packit Service d40955
Packit Service d40955
/*
Packit Service d40955
 * Please see comments above where the macro is defined.
Packit Service d40955
 */
Packit Service d40955
#if HAS_NO_BLKDEV_SPLIT
Packit Service d40955
  ti->split_discard_bios = 1;
Packit Service d40955
#endif
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Handle a vdoInitialize failure, freeing all appropriate structures.
Packit Service d40955
 *
Packit Service d40955
 * @param ti            The device mapper target representing our device
Packit Service d40955
 * @param threadConfig  The thread config (possibly NULL)
Packit Service d40955
 * @param layer         The kernel layer (possibly NULL)
Packit Service d40955
 * @param instance      The instance number to be released
Packit Service d40955
 * @param why           The reason for failure
Packit Service d40955
 **/
Packit Service d40955
static void cleanupInitialize(struct dm_target *ti,
Packit Service d40955
                              ThreadConfig     *threadConfig,
Packit Service d40955
                              KernelLayer      *layer,
Packit Service d40955
                              unsigned int      instance,
Packit Service d40955
                              char             *why)
Packit Service d40955
{
Packit Service d40955
  if (threadConfig != NULL) {
Packit Service d40955
    freeThreadConfig(&threadConfig);
Packit Service d40955
  }
Packit Service d40955
  if (layer != NULL) {
Packit Service d40955
    // This releases the instance number too.
Packit Service d40955
    freeKernelLayer(layer);
Packit Service d40955
  } else {
Packit Service d40955
    // With no KernelLayer taking ownership we have to release explicitly.
Packit Service d40955
    releaseKVDOInstance(instance);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  ti->error = why;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Initializes a single VDO instance and loads the data from disk
Packit Service d40955
 *
Packit Service d40955
 * @param ti        The device mapper target representing our device
Packit Service d40955
 * @param instance  The device instantiation counter
Packit Service d40955
 * @param config    The parsed config for the instance
Packit Service d40955
 *
Packit Service d40955
 * @return  VDO_SUCCESS or an error code
Packit Service d40955
 *
Packit Service d40955
 **/
Packit Service d40955
static int vdoInitialize(struct dm_target *ti,
Packit Service d40955
                         unsigned int      instance,
Packit Service d40955
                         DeviceConfig     *config)
Packit Service d40955
{
Packit Service d40955
  logInfo("loading device '%s'", config->poolName);
Packit Service d40955
Packit Service d40955
  uint64_t   blockSize      = VDO_BLOCK_SIZE;
Packit Service d40955
  uint64_t   logicalSize    = to_bytes(ti->len);
Packit Service d40955
  BlockCount logicalBlocks  = logicalSize / blockSize;
Packit Service d40955
Packit Service d40955
  logDebug("Logical block size     = %llu",
Packit Service d40955
           (uint64_t) config->logicalBlockSize);
Packit Service d40955
  logDebug("Logical blocks         = %llu", logicalBlocks);
Packit Service d40955
  logDebug("Physical block size    = %llu", (uint64_t) blockSize);
Packit Service d40955
  logDebug("Physical blocks        = %llu", config->physicalBlocks);
Packit Service d40955
  logDebug("Block map cache blocks = %u", config->cacheSize);
Packit Service d40955
  logDebug("Block map maximum age  = %u", config->blockMapMaximumAge);
Packit Service d40955
  logDebug("MD RAID5 mode          = %s", (config->mdRaid5ModeEnabled
Packit Service d40955
                                           ? "on" : "off"));
Packit Service d40955
  logDebug("Write policy           = %s", getConfigWritePolicyString(config));
Packit Service d40955
  logDebug("Deduplication          = %s", (config->deduplication
Packit Service d40955
                                           ? "on" : "off"));
Packit Service d40955
Packit Service d40955
  // The threadConfig will be copied by the VDO if it's successfully
Packit Service d40955
  // created.
Packit Service d40955
  VDOLoadConfig loadConfig = {
Packit Service d40955
    .cacheSize    = config->cacheSize,
Packit Service d40955
    .threadConfig = NULL,
Packit Service d40955
    .writePolicy  = config->writePolicy,
Packit Service d40955
    .maximumAge   = config->blockMapMaximumAge,
Packit Service d40955
  };
Packit Service d40955
Packit Service d40955
  char        *failureReason;
Packit Service d40955
  KernelLayer *layer;
Packit Service d40955
  int result = makeKernelLayer(ti->begin, instance, config,
Packit Service d40955
                               &kvdoDevice.kobj, &loadConfig.threadConfig,
Packit Service d40955
                               &failureReason, &layer);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    logError("Could not create kernel physical layer. (VDO error %d,"
Packit Service d40955
             " message %s)", result, failureReason);
Packit Service d40955
    cleanupInitialize(ti, loadConfig.threadConfig, NULL, instance,
Packit Service d40955
                      failureReason);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Now that we have read the geometry, we can finish setting up the
Packit Service d40955
  // VDOLoadConfig.
Packit Service d40955
  setLoadConfigFromGeometry(&layer->geometry, &loadConfig);
Packit Service d40955
Packit Service d40955
  if (config->cacheSize < (2 * MAXIMUM_USER_VIOS
Packit Service d40955
                   * loadConfig.threadConfig->logicalZoneCount)) {
Packit Service d40955
    logWarning("Insufficient block map cache for logical zones");
Packit Service d40955
    cleanupInitialize(ti, loadConfig.threadConfig, layer, instance,
Packit Service d40955
                      "Insufficient block map cache for logical zones");
Packit Service d40955
    return VDO_BAD_CONFIGURATION;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Henceforth it is the kernel layer's responsibility to clean up the
Packit Service d40955
  // ThreadConfig.
Packit Service d40955
  result = preloadKernelLayer(layer, &loadConfig, &failureReason);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    logError("Could not start kernel physical layer. (VDO error %d,"
Packit Service d40955
             " message %s)", result, failureReason);
Packit Service d40955
    cleanupInitialize(ti, NULL, layer, instance, failureReason);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  setDeviceConfigLayer(config, layer);
Packit Service d40955
  setKernelLayerActiveConfig(layer, config);
Packit Service d40955
  ti->private = config;
Packit Service d40955
  configureTargetCapabilities(ti, layer);
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static int vdoCtr(struct dm_target *ti, unsigned int argc, char **argv)
Packit Service d40955
{
Packit Service d40955
  int result = VDO_SUCCESS;
Packit Service d40955
  
Packit Service d40955
  RegisteredThread allocatingThread;
Packit Service d40955
  registerAllocatingThread(&allocatingThread, NULL);
Packit Service d40955
Packit Service d40955
  const char *deviceName = dm_device_name(dm_table_get_md(ti->table));  
Packit Service d40955
  KernelLayer *oldLayer = findLayerMatching(layerIsNamed, (void *)deviceName);
Packit Service d40955
  unsigned int instance;
Packit Service d40955
  if (oldLayer == NULL) {
Packit Service d40955
    result = allocateKVDOInstance(&instance);
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      unregisterAllocatingThread();
Packit Service d40955
      return -ENOMEM;
Packit Service d40955
    }
Packit Service d40955
  } else {
Packit Service d40955
    instance = oldLayer->instance;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  RegisteredThread instanceThread;
Packit Service d40955
  registerThreadDeviceID(&instanceThread, &instance);
Packit Service d40955
Packit Service d40955
  bool verbose = (oldLayer == NULL);
Packit Service d40955
  DeviceConfig *config = NULL;
Packit Service d40955
  result = parseDeviceConfig(argc, argv, ti, verbose, &config);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    unregisterThreadDeviceID();
Packit Service d40955
    unregisterAllocatingThread();
Packit Service d40955
    if (oldLayer == NULL) {
Packit Service d40955
      releaseKVDOInstance(instance);
Packit Service d40955
    }
Packit Service d40955
    return -EINVAL;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Is there already a device of this name?
Packit Service d40955
  if (oldLayer != NULL) {
Packit Service d40955
    /*
Packit Service d40955
     * To preserve backward compatibility with old VDO Managers, we need to
Packit Service d40955
     * allow this to happen when either suspended or not. We could assert
Packit Service d40955
     * that if the config is version 0, we are suspended, and if not, we
Packit Service d40955
     * are not, but we can't do that till new VDO Manager does the right
Packit Service d40955
     * order.
Packit Service d40955
     */
Packit Service d40955
    logInfo("preparing to modify device '%s'", config->poolName);
Packit Service d40955
    result = prepareToModifyKernelLayer(oldLayer, config, &ti->error);
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      result = mapToSystemError(result);
Packit Service d40955
      freeDeviceConfig(&config);
Packit Service d40955
    } else {
Packit Service d40955
      setDeviceConfigLayer(config, oldLayer);
Packit Service d40955
      ti->private = config;
Packit Service d40955
      configureTargetCapabilities(ti, oldLayer);
Packit Service d40955
    }
Packit Service d40955
    unregisterThreadDeviceID();
Packit Service d40955
    unregisterAllocatingThread();
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = vdoInitialize(ti, instance, config);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    // vdoInitialize calls into various VDO routines, so map error
Packit Service d40955
    result = mapToSystemError(result);
Packit Service d40955
    freeDeviceConfig(&config);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  unregisterThreadDeviceID();
Packit Service d40955
  unregisterAllocatingThread();
Packit Service d40955
  return result;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoDtr(struct dm_target *ti)
Packit Service d40955
{
Packit Service d40955
  DeviceConfig *config = ti->private;
Packit Service d40955
  KernelLayer  *layer  = config->layer;
Packit Service d40955
Packit Service d40955
  setDeviceConfigLayer(config, NULL);
Packit Service d40955
Packit Service d40955
  if (isRingEmpty(&layer->deviceConfigRing)) {
Packit Service d40955
    // This was the last config referencing the layer. Free it.
Packit Service d40955
    unsigned int instance = layer->instance;
Packit Service d40955
    RegisteredThread allocatingThread, instanceThread;
Packit Service d40955
    registerThreadDeviceID(&instanceThread, &instance);
Packit Service d40955
    registerAllocatingThread(&allocatingThread, NULL);
Packit Service d40955
Packit Service d40955
    waitForNoRequestsActive(layer);
Packit Service d40955
    logInfo("stopping device '%s'", config->poolName);
Packit Service d40955
Packit Service d40955
    if (layer->dumpOnShutdown) {
Packit Service d40955
      vdoDumpAll(layer, "device shutdown");
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    freeKernelLayer(layer);
Packit Service d40955
    logInfo("device '%s' stopped", config->poolName);
Packit Service d40955
    unregisterThreadDeviceID();
Packit Service d40955
    unregisterAllocatingThread();
Packit Service d40955
  } else if (config == layer->deviceConfig) {
Packit Service d40955
    // The layer still references this config. Give it a reference to a
Packit Service d40955
    // config that isn't being destroyed.
Packit Service d40955
    layer->deviceConfig = asDeviceConfig(layer->deviceConfigRing.next);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  freeDeviceConfig(&config);
Packit Service d40955
  ti->private = NULL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoPresuspend(struct dm_target *ti)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  RegisteredThread instanceThread;
Packit Service d40955
  registerThreadDevice(&instanceThread, layer);
Packit Service d40955
  if (dm_noflush_suspending(ti)) {
Packit Service d40955
    layer->noFlushSuspend = true;
Packit Service d40955
  }
Packit Service d40955
  unregisterThreadDeviceID();
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoPostsuspend(struct dm_target *ti)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  RegisteredThread instanceThread;
Packit Service d40955
  registerThreadDevice(&instanceThread, layer);
Packit Service d40955
  const char *poolName = layer->deviceConfig->poolName;
Packit Service d40955
  logInfo("suspending device '%s'", poolName);
Packit Service d40955
  int result = suspendKernelLayer(layer);
Packit Service d40955
  if (result == VDO_SUCCESS) {
Packit Service d40955
    logInfo("device '%s' suspended", poolName);
Packit Service d40955
  } else {
Packit Service d40955
    logError("suspend of device '%s' failed with error: %d", poolName, result);
Packit Service d40955
  }
Packit Service d40955
  layer->noFlushSuspend = false;
Packit Service d40955
  unregisterThreadDeviceID();
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static int vdoPreresume(struct dm_target *ti)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  DeviceConfig *config = ti->private;
Packit Service d40955
  RegisteredThread instanceThread;
Packit Service d40955
Packit Service d40955
  BlockCount backingBlocks = getUnderlyingDeviceBlockCount(layer);
Packit Service d40955
  if (backingBlocks < config->physicalBlocks) {
Packit Service d40955
    logError("resume of device '%s' failed: backing device has %" PRIu64
Packit Service d40955
             " blocks but VDO physical size is %llu blocks",
Packit Service d40955
             config->poolName, backingBlocks, config->physicalBlocks);
Packit Service d40955
    return -EINVAL;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  registerThreadDevice(&instanceThread, layer);
Packit Service d40955
Packit Service d40955
  if (getKernelLayerState(layer) == LAYER_STARTING) {
Packit Service d40955
    // This is the first time this device has been resumed, so run it.
Packit Service d40955
    logInfo("starting device '%s'", config->poolName);
Packit Service d40955
    char *failureReason;
Packit Service d40955
    int result = startKernelLayer(layer, &failureReason);
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      logError("Could not run kernel physical layer. (VDO error %d,"
Packit Service d40955
               " message %s)", result, failureReason);
Packit Service d40955
      setKVDOReadOnly(&layer->kvdo, result);
Packit Service d40955
      unregisterThreadDeviceID();
Packit Service d40955
      return mapToSystemError(result);
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    logInfo("device '%s' started", config->poolName);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  logInfo("resuming device '%s'", config->poolName);
Packit Service d40955
Packit Service d40955
  // This is a noop if nothing has changed, and by calling it every time
Packit Service d40955
  // we capture old-style growPhysicals, which change the config in place.
Packit Service d40955
  int result = modifyKernelLayer(layer, config);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    logErrorWithStringError(result, "Commit of modifications to device '%s'"
Packit Service d40955
                            " failed", config->poolName);
Packit Service d40955
    setKernelLayerActiveConfig(layer, config);
Packit Service d40955
    setKVDOReadOnly(&layer->kvdo, result);
Packit Service d40955
  } else {
Packit Service d40955
    setKernelLayerActiveConfig(layer, config);
Packit Service d40955
    result = resumeKernelLayer(layer);
Packit Service d40955
    if (result != VDO_SUCCESS) {
Packit Service d40955
      logError("resume of device '%s' failed with error: %d",
Packit Service d40955
	       layer->deviceConfig->poolName, result);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  unregisterThreadDeviceID();
Packit Service d40955
  return mapToSystemError(result);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoResume(struct dm_target *ti)
Packit Service d40955
{
Packit Service d40955
  KernelLayer *layer = getKernelLayerForTarget(ti);
Packit Service d40955
  RegisteredThread instanceThread;
Packit Service d40955
  registerThreadDevice(&instanceThread, layer);
Packit Service d40955
  logInfo("device '%s' resumed", layer->deviceConfig->poolName);
Packit Service d40955
  unregisterThreadDeviceID();
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*
Packit Service d40955
 * If anything changes that affects how user tools will interact
Packit Service d40955
 * with vdo, update the version number and make sure
Packit Service d40955
 * documentation about the change is complete so tools can
Packit Service d40955
 * properly update their management code.
Packit Service d40955
 */
Packit Service d40955
static struct target_type vdoTargetBio = {
Packit Service d40955
  .features        = DM_TARGET_SINGLETON,
Packit Service d40955
  .name            = "vdo",
Packit Service d40955
  .version         = {6, 2, 3},
Packit Service d40955
  .module          = THIS_MODULE,
Packit Service d40955
  .ctr             = vdoCtr,
Packit Service d40955
  .dtr             = vdoDtr,
Packit Service d40955
  .io_hints        = vdoIoHints,
Packit Service d40955
  .iterate_devices = vdoIterateDevices,
Packit Service d40955
  .map             = vdoMapBio,
Packit Service d40955
  .message         = vdoMessage,
Packit Service d40955
  .status          = vdoStatus,
Packit Service d40955
  .presuspend      = vdoPresuspend,
Packit Service d40955
  .postsuspend     = vdoPostsuspend,
Packit Service d40955
  .preresume       = vdoPreresume,
Packit Service d40955
  .resume          = vdoResume,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
static bool dmRegistered     = false;
Packit Service d40955
static bool sysfsInitialized = false;
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void vdoDestroy(void)
Packit Service d40955
{
Packit Service d40955
  logDebug("in %s", __func__);
Packit Service d40955
Packit Service d40955
  kvdoDevice.status = SHUTTING_DOWN;
Packit Service d40955
Packit Service d40955
  if (sysfsInitialized) {
Packit Service d40955
    vdoPutSysfs(&kvdoDevice.kobj);
Packit Service d40955
  }
Packit Service d40955
  vdoDestroyProcfs();
Packit Service d40955
Packit Service d40955
  kvdoDevice.status = UNINITIALIZED;
Packit Service d40955
Packit Service d40955
  if (dmRegistered) {
Packit Service d40955
    dm_unregister_target(&vdoTargetBio);
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  cleanUpInstanceNumberTracking();
Packit Service d40955
Packit Service d40955
  logInfo("unloaded version %s", CURRENT_VERSION);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static int __init vdoInit(void)
Packit Service d40955
{
Packit Service d40955
  int result = 0;
Packit Service d40955
Packit Service d40955
  initializeThreadDeviceRegistry();
Packit Service d40955
  initializeStandardErrorBlocks();
Packit Service d40955
  initializeDeviceRegistryOnce();
Packit Service d40955
  logInfo("loaded version %s", CURRENT_VERSION);
Packit Service d40955
Packit Service d40955
  result = dm_register_target(&vdoTargetBio);
Packit Service d40955
  if (result < 0) {
Packit Service d40955
    logError("dm_register_target failed %d", result);
Packit Service d40955
    vdoDestroy();
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
  dmRegistered = true;
Packit Service d40955
Packit Service d40955
  kvdoDevice.status = UNINITIALIZED;
Packit Service d40955
Packit Service d40955
  vdoInitProcfs();
Packit Service d40955
  /*
Packit Service d40955
   * Set up global sysfs stuff
Packit Service d40955
   */
Packit Service d40955
  result = vdoInitSysfs(&kvdoDevice.kobj);
Packit Service d40955
  if (result < 0) {
Packit Service d40955
    logError("sysfs initialization failed %d", result);
Packit Service d40955
    vdoDestroy();
Packit Service d40955
    // vdoInitSysfs only returns system error codes
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
  sysfsInitialized = true;
Packit Service d40955
Packit Service d40955
  initWorkQueueOnce();
Packit Service d40955
  initializeTraceLoggingOnce();
Packit Service d40955
  initKernelVDOOnce();
Packit Service d40955
  initializeInstanceNumberTracking();
Packit Service d40955
Packit Service d40955
  kvdoDevice.status = READY;
Packit Service d40955
  return result;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static void __exit vdoExit(void)
Packit Service d40955
{
Packit Service d40955
  vdoDestroy();
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
module_init(vdoInit);
Packit Service d40955
module_exit(vdoExit);
Packit Service d40955
Packit Service d40955
MODULE_DESCRIPTION(DM_NAME " target for transparent deduplication");
Packit Service d40955
MODULE_AUTHOR("Red Hat, Inc.");
Packit Service d40955
MODULE_LICENSE("GPL");
Packit Service d40955
MODULE_VERSION(CURRENT_VERSION);