Blame source/vdo/kernel/deviceConfig.c

Packit Service cbade1
/**
Packit Service cbade1
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service cbade1
 *
Packit Service cbade1
 * This program is free software; you can redistribute it and/or
Packit Service cbade1
 * modify it under the terms of the GNU General Public License
Packit Service cbade1
 * as published by the Free Software Foundation; either version 2
Packit Service cbade1
 * of the License, or (at your option) any later version.
Packit Service cbade1
 * 
Packit Service cbade1
 * This program is distributed in the hope that it will be useful,
Packit Service cbade1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cbade1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service cbade1
 * GNU General Public License for more details.
Packit Service cbade1
 * 
Packit Service cbade1
 * You should have received a copy of the GNU General Public License
Packit Service cbade1
 * along with this program; if not, write to the Free Software
Packit Service cbade1
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service cbade1
 * 02110-1301, USA. 
Packit Service cbade1
 *
Packit Service cbade1
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/deviceConfig.c#14 $
Packit Service cbade1
 */
Packit Service cbade1
Packit Service cbade1
#include "deviceConfig.h"
Packit Service cbade1
Packit Service cbade1
#include <linux/device-mapper.h>
Packit Service cbade1
Packit Service cbade1
#include "logger.h"
Packit Service cbade1
#include "memoryAlloc.h"
Packit Service cbade1
#include "stringUtils.h"
Packit Service cbade1
Packit Service cbade1
#include "kernelLayer.h"
Packit Service cbade1
#include "vdoStringUtils.h"
Packit Service cbade1
Packit Service cbade1
#include "constants.h"
Packit Service cbade1
Packit Service cbade1
enum {
Packit Service cbade1
  // If we bump this, update the arrays below
Packit Service cbade1
  TABLE_VERSION = 2,
Packit Service cbade1
  // Limits used when parsing thread-count config spec strings
Packit Service cbade1
  BIO_ROTATION_INTERVAL_LIMIT = 1024,
Packit Service cbade1
  LOGICAL_THREAD_COUNT_LIMIT  = 60,
Packit Service cbade1
  PHYSICAL_THREAD_COUNT_LIMIT = 16,
Packit Service cbade1
  THREAD_COUNT_LIMIT          = 100,
Packit Service cbade1
  // XXX The bio-submission queue configuration defaults are temporarily
Packit Service cbade1
  // still being defined here until the new runtime-based thread
Packit Service cbade1
  // configuration has been fully implemented for managed VDO devices.
Packit Service cbade1
Packit Service cbade1
  // How many bio submission work queues to use
Packit Service cbade1
  DEFAULT_NUM_BIO_SUBMIT_QUEUES             = 4,
Packit Service cbade1
  // How often to rotate between bio submission work queues
Packit Service cbade1
  DEFAULT_BIO_SUBMIT_QUEUE_ROTATE_INTERVAL  = 64,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
// arrays for handling different table versions
Packit Service cbade1
static const uint8_t REQUIRED_ARGC[] = {10, 12, 9};
Packit Service cbade1
static const uint8_t POOL_NAME_ARG_INDEX[] = {8, 10, 8};
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Decide the version number from argv.
Packit Service cbade1
 *
Packit Service cbade1
 * @param [in]  argc         The number of table values
Packit Service cbade1
 * @param [in]  argv         The array of table values
Packit Service cbade1
 * @param [out] errorPtr     A pointer to return a error string in
Packit Service cbade1
 * @param [out] versionPtr   A pointer to return the version
Packit Service cbade1
 *
Packit Service cbade1
 * @return VDO_SUCCESS or an error code
Packit Service cbade1
 **/
Packit Service cbade1
static int getVersionNumber(int            argc,
Packit Service cbade1
                            char         **argv,
Packit Service cbade1
                            char         **errorPtr,
Packit Service cbade1
                            TableVersion  *versionPtr)
Packit Service cbade1
{
Packit Service cbade1
  // version, if it exists, is in a form of V<n>
Packit Service cbade1
  if (sscanf(argv[0], "V%u", versionPtr) == 1) {
Packit Service cbade1
    if (*versionPtr < 1 || *versionPtr > TABLE_VERSION) {
Packit Service cbade1
      *errorPtr = "Unknown version number detected";
Packit Service cbade1
      return VDO_BAD_CONFIGURATION;
Packit Service cbade1
    }
Packit Service cbade1
  } else {
Packit Service cbade1
    // V0 actually has no version number in the table string
Packit Service cbade1
    *versionPtr = 0;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // V0 and V1 have no optional parameters. There will always be
Packit Service cbade1
  // a parameter for thread config, even if its a "." to show
Packit Service cbade1
  // its an empty list.
Packit Service cbade1
  if (*versionPtr <= 1) {
Packit Service cbade1
    if (argc != REQUIRED_ARGC[*versionPtr]) {
Packit Service cbade1
      *errorPtr = "Incorrect number of arguments for version";
Packit Service cbade1
      return VDO_BAD_CONFIGURATION;
Packit Service cbade1
    }
Packit Service cbade1
  } else if (argc < REQUIRED_ARGC[*versionPtr]) {
Packit Service cbade1
    *errorPtr = "Incorrect number of arguments for version";
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (*versionPtr != TABLE_VERSION) {
Packit Service cbade1
    logWarning("Detected version mismatch between kernel module and tools "
Packit Service cbade1
	       " kernel: %d, tool: %d", TABLE_VERSION, *versionPtr);
Packit Service cbade1
    logWarning("Please consider upgrading management tools to match kernel.");
Packit Service cbade1
  }
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
int getPoolNameFromArgv(int    argc,
Packit Service cbade1
                        char **argv,
Packit Service cbade1
                        char **errorPtr,
Packit Service cbade1
                        char **poolNamePtr)
Packit Service cbade1
{
Packit Service cbade1
  TableVersion version;
Packit Service cbade1
  int result = getVersionNumber(argc, argv, errorPtr, &version);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
  *poolNamePtr = argv[POOL_NAME_ARG_INDEX[version]];
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Resolve the config with write policy, physical size, and other unspecified
Packit Service cbade1
 * fields based on the device, if needed.
Packit Service cbade1
 *
Packit Service cbade1
 * @param [in,out] config   The config possibly missing values
Packit Service cbade1
 * @param [in]     verbose  Whether to log about the underlying device
Packit Service cbade1
 **/
Packit Service cbade1
static void resolveConfigWithDevice(DeviceConfig  *config,
Packit Service cbade1
                                    bool           verbose)
Packit Service cbade1
{
Packit Service cbade1
  struct dm_dev *dev = config->ownedDevice;
Packit Service cbade1
  struct request_queue *requestQueue = bdev_get_queue(dev->bdev);
Packit Service cbade1
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
Packit Service cbade1
  bool flushSupported
Packit Service cbade1
    = ((requestQueue->queue_flags & (1ULL << QUEUE_FLAG_WC)) != 0);
Packit Service cbade1
  bool fuaSupported
Packit Service cbade1
    = ((requestQueue->queue_flags & (1ULL << QUEUE_FLAG_FUA)) != 0);
Packit Service cbade1
#else
Packit Service cbade1
  bool flushSupported = ((requestQueue->flush_flags & REQ_FLUSH) == REQ_FLUSH);
Packit Service cbade1
  bool fuaSupported   = ((requestQueue->flush_flags & REQ_FUA) == REQ_FUA);
Packit Service cbade1
#endif
Packit Service cbade1
  if (verbose) {
Packit Service cbade1
    logInfo("underlying device, REQ_FLUSH: %s, REQ_FUA: %s",
Packit Service cbade1
            (flushSupported ? "supported" : "not supported"),
Packit Service cbade1
            (fuaSupported ? "supported" : "not supported"));
Packit Service cbade1
  } else {
Packit Service cbade1
    // We should probably always log, but need to make sure that makes sense
Packit Service cbade1
    // before changing behavior.
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (config->writePolicy == WRITE_POLICY_AUTO) {
Packit Service cbade1
    config->writePolicy
Packit Service cbade1
      = (flushSupported ? WRITE_POLICY_ASYNC : WRITE_POLICY_SYNC);
Packit Service cbade1
    logInfo("Using write policy %s automatically.",
Packit Service cbade1
            getConfigWritePolicyString(config));
Packit Service cbade1
  } else {
Packit Service cbade1
    logInfo("Using write policy %s.", getConfigWritePolicyString(config));
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (flushSupported && (config->writePolicy == WRITE_POLICY_SYNC)) {
Packit Service cbade1
    logWarning("WARNING: Running in sync mode atop a device supporting flushes"
Packit Service cbade1
               " is dangerous!");
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (config->version == 0) {
Packit Service cbade1
    uint64_t deviceSize = i_size_read(dev->bdev->bd_inode);
Packit Service cbade1
    config->physicalBlocks = deviceSize / VDO_BLOCK_SIZE;
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Parse a two-valued option into a bool.
Packit Service cbade1
 *
Packit Service cbade1
 * @param [in]  boolStr    The string value to convert to a bool
Packit Service cbade1
 * @param [in]  trueStr    The string value which should be converted to true
Packit Service cbade1
 * @param [in]  falseStr   The string value which should be converted to false
Packit Service cbade1
 * @param [out] boolPtr    A pointer to return the bool value in
Packit Service cbade1
 *
Packit Service cbade1
 * @return VDO_SUCCESS or an error if boolStr is neither trueStr nor falseStr
Packit Service cbade1
 **/
Packit Service cbade1
__attribute__((warn_unused_result))
Packit Service cbade1
static inline int parseBool(const char *boolStr,
Packit Service cbade1
                            const char *trueStr,
Packit Service cbade1
                            const char *falseStr,
Packit Service cbade1
                            bool       *boolPtr)
Packit Service cbade1
{
Packit Service cbade1
  bool value = false;
Packit Service cbade1
  if (strcmp(boolStr, trueStr) == 0) {
Packit Service cbade1
    value = true;
Packit Service cbade1
  } else if (strcmp(boolStr, falseStr) == 0) {
Packit Service cbade1
    value = false;
Packit Service cbade1
  } else {
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  *boolPtr = value;
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Process one component of a thread parameter configuration string and
Packit Service cbade1
 * update the configuration data structure.
Packit Service cbade1
 *
Packit Service cbade1
 * If the thread count requested is invalid, a message is logged and
Packit Service cbade1
 * -EINVAL returned. If the thread name is unknown, a message is logged
Packit Service cbade1
 * but no error is returned.
Packit Service cbade1
 *
Packit Service cbade1
 * @param threadParamType  The type of thread specified
Packit Service cbade1
 * @param count            The thread count requested
Packit Service cbade1
 * @param config           The configuration data structure to update
Packit Service cbade1
 *
Packit Service cbade1
 * @return   VDO_SUCCESS or -EINVAL
Packit Service cbade1
 **/
Packit Service cbade1
static int processOneThreadConfigSpec(const char        *threadParamType,
Packit Service cbade1
                                      unsigned int       count,
Packit Service cbade1
                                      ThreadCountConfig *config)
Packit Service cbade1
{
Packit Service cbade1
  // Handle limited thread parameters
Packit Service cbade1
  if (strcmp(threadParamType, "bioRotationInterval") == 0) {
Packit Service cbade1
    if (count == 0) {
Packit Service cbade1
      logError("thread config string error:"
Packit Service cbade1
               " 'bioRotationInterval' of at least 1 is required");
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    } else if (count > BIO_ROTATION_INTERVAL_LIMIT) {
Packit Service cbade1
      logError("thread config string error:"
Packit Service cbade1
               " 'bioRotationInterval' cannot be higher than %d",
Packit Service cbade1
               BIO_ROTATION_INTERVAL_LIMIT);
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    }
Packit Service cbade1
    config->bioRotationInterval = count;
Packit Service cbade1
    return VDO_SUCCESS;
Packit Service cbade1
  } else if (strcmp(threadParamType, "logical") == 0) {
Packit Service cbade1
    if (count > LOGICAL_THREAD_COUNT_LIMIT) {
Packit Service cbade1
      logError("thread config string error: at most %d 'logical' threads"
Packit Service cbade1
               " are allowed",
Packit Service cbade1
               LOGICAL_THREAD_COUNT_LIMIT);
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    }
Packit Service cbade1
    config->logicalZones = count;
Packit Service cbade1
    return VDO_SUCCESS;
Packit Service cbade1
  } else if (strcmp(threadParamType, "physical") == 0) {
Packit Service cbade1
    if (count > PHYSICAL_THREAD_COUNT_LIMIT) {
Packit Service cbade1
      logError("thread config string error: at most %d 'physical' threads"
Packit Service cbade1
               " are allowed",
Packit Service cbade1
               PHYSICAL_THREAD_COUNT_LIMIT);
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    }
Packit Service cbade1
    config->physicalZones = count;
Packit Service cbade1
    return VDO_SUCCESS;
Packit Service cbade1
  } else {
Packit Service cbade1
    // Handle other thread count parameters
Packit Service cbade1
    if (count > THREAD_COUNT_LIMIT) {
Packit Service cbade1
      logError("thread config string error: at most %d '%s' threads"
Packit Service cbade1
               " are allowed",
Packit Service cbade1
               THREAD_COUNT_LIMIT, threadParamType);
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
    if (strcmp(threadParamType, "hash") == 0) {
Packit Service cbade1
      config->hashZones = count;
Packit Service cbade1
      return VDO_SUCCESS;
Packit Service cbade1
    } else if (strcmp(threadParamType, "cpu") == 0) {
Packit Service cbade1
      if (count == 0) {
Packit Service cbade1
        logError("thread config string error:"
Packit Service cbade1
                 " at least one 'cpu' thread required");
Packit Service cbade1
        return -EINVAL;
Packit Service cbade1
      }
Packit Service cbade1
      config->cpuThreads = count;
Packit Service cbade1
      return VDO_SUCCESS;
Packit Service cbade1
    } else if (strcmp(threadParamType, "ack") == 0) {
Packit Service cbade1
      config->bioAckThreads = count;
Packit Service cbade1
      return VDO_SUCCESS;
Packit Service cbade1
    } else if (strcmp(threadParamType, "bio") == 0) {
Packit Service cbade1
      if (count == 0) {
Packit Service cbade1
        logError("thread config string error:"
Packit Service cbade1
                 " at least one 'bio' thread required");
Packit Service cbade1
        return -EINVAL;
Packit Service cbade1
      }
Packit Service cbade1
      config->bioThreads = count;
Packit Service cbade1
      return VDO_SUCCESS;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Don't fail, just log. This will handle version mismatches between
Packit Service cbade1
  // user mode tools and kernel.
Packit Service cbade1
  logInfo("unknown thread parameter type \"%s\"", threadParamType);
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Parse one component of a thread parameter configuration string and
Packit Service cbade1
 * update the configuration data structure.
Packit Service cbade1
 *
Packit Service cbade1
 * @param spec    The thread parameter specification string
Packit Service cbade1
 * @param config  The configuration data to be updated
Packit Service cbade1
 **/
Packit Service cbade1
static int parseOneThreadConfigSpec(const char        *spec,
Packit Service cbade1
                                    ThreadCountConfig *config)
Packit Service cbade1
{
Packit Service cbade1
  char **fields;
Packit Service cbade1
  int result = splitString(spec, '=', &fields);
Packit Service cbade1
  if (result != UDS_SUCCESS) {
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
  if ((fields[0] == NULL) || (fields[1] == NULL) || (fields[2] != NULL)) {
Packit Service cbade1
    logError("thread config string error:"
Packit Service cbade1
             " expected thread parameter assignment, saw \"%s\"",
Packit Service cbade1
             spec);
Packit Service cbade1
    freeStringArray(fields);
Packit Service cbade1
    return -EINVAL;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  unsigned int count;
Packit Service cbade1
  result = stringToUInt(fields[1], &count);
Packit Service cbade1
  if (result != UDS_SUCCESS) {
Packit Service cbade1
    logError("thread config string error: integer value needed, found \"%s\"",
Packit Service cbade1
             fields[1]);
Packit Service cbade1
    freeStringArray(fields);
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  result = processOneThreadConfigSpec(fields[0], count, config);
Packit Service cbade1
  freeStringArray(fields);
Packit Service cbade1
  return result;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Parse the configuration string passed and update the specified
Packit Service cbade1
 * counts and other parameters of various types of threads to be created.
Packit Service cbade1
 *
Packit Service cbade1
 * The configuration string should contain one or more comma-separated specs
Packit Service cbade1
 * of the form "typename=number"; the supported type names are "cpu", "ack",
Packit Service cbade1
 * "bio", "bioRotationInterval", "logical", "physical", and "hash".
Packit Service cbade1
 *
Packit Service cbade1
 * If an error occurs during parsing of a single key/value pair, we deem
Packit Service cbade1
 * it serious enough to stop further parsing.
Packit Service cbade1
 *
Packit Service cbade1
 * This function can't set the "reason" value the caller wants to pass
Packit Service cbade1
 * back, because we'd want to format it to say which field was
Packit Service cbade1
 * invalid, and we can't allocate the "reason" strings dynamically. So
Packit Service cbade1
 * if an error occurs, we'll log the details and pass back an error.
Packit Service cbade1
 *
Packit Service cbade1
 * @param string  Thread parameter configuration string
Packit Service cbade1
 * @param config  The thread configuration data to update
Packit Service cbade1
 *
Packit Service cbade1
 * @return   VDO_SUCCESS or -EINVAL or -ENOMEM
Packit Service cbade1
 **/
Packit Service cbade1
static int parseThreadConfigString(const char        *string,
Packit Service cbade1
                                   ThreadCountConfig *config)
Packit Service cbade1
{
Packit Service cbade1
  int result = VDO_SUCCESS;
Packit Service cbade1
Packit Service cbade1
  char **specs;
Packit Service cbade1
  if (strcmp(".", string) != 0) {
Packit Service cbade1
    result = splitString(string, ',', &specs);
Packit Service cbade1
    if (result != UDS_SUCCESS) {
Packit Service cbade1
      return result;
Packit Service cbade1
    }
Packit Service cbade1
    for (unsigned int i = 0; specs[i] != NULL; i++) {
Packit Service cbade1
      result = parseOneThreadConfigSpec(specs[i], config);
Packit Service cbade1
      if (result != VDO_SUCCESS) {
Packit Service cbade1
	break;
Packit Service cbade1
      }
Packit Service cbade1
    }
Packit Service cbade1
    freeStringArray(specs);
Packit Service cbade1
  }
Packit Service cbade1
  return result;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Process one component of an optional parameter string and
Packit Service cbade1
 * update the configuration data structure.
Packit Service cbade1
 *
Packit Service cbade1
 * If the value requested is invalid, a message is logged and
Packit Service cbade1
 * -EINVAL returned. If the key is unknown, a message is logged
Packit Service cbade1
 * but no error is returned.
Packit Service cbade1
 *
Packit Service cbade1
 * @param key    The optional parameter key name
Packit Service cbade1
 * @param value  The optional parameter value
Packit Service cbade1
 * @param config The configuration data structure to update
Packit Service cbade1
 *
Packit Service cbade1
 * @return   VDO_SUCCESS or -EINVAL
Packit Service cbade1
 **/
Packit Service cbade1
static int processOneKeyValuePair(const char   *key,
Packit Service cbade1
                                  unsigned int  value,
Packit Service cbade1
                                  DeviceConfig *config)
Packit Service cbade1
{
Packit Service cbade1
  // Non thread optional parameters
Packit Service cbade1
  if (strcmp(key, "maxDiscard") == 0) {
Packit Service cbade1
    if (value == 0) {
Packit Service cbade1
      logError("optional parameter error:"
Packit Service cbade1
               " at least one max discard block required");
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    }
Packit Service cbade1
    // Max discard sectors in blkdev_issue_discard is UINT_MAX >> 9
Packit Service cbade1
    if (value > (UINT_MAX / VDO_BLOCK_SIZE)) {
Packit Service cbade1
      logError("optional parameter error: at most %d max discard"
Packit Service cbade1
               " blocks are allowed", UINT_MAX / VDO_BLOCK_SIZE);
Packit Service cbade1
      return -EINVAL;
Packit Service cbade1
    }
Packit Service cbade1
    config->maxDiscardBlocks = value;
Packit Service cbade1
    return VDO_SUCCESS;
Packit Service cbade1
  }
Packit Service cbade1
  // Handles unknown key names
Packit Service cbade1
  return processOneThreadConfigSpec(key, value, &config->threadCounts);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Parse one key/value pair and update the configuration
Packit Service cbade1
 * data structure.
Packit Service cbade1
 *
Packit Service cbade1
 * @param key     The optional key name
Packit Service cbade1
 * @param value   The optional value
Packit Service cbade1
 * @param config  The configuration data to be updated
Packit Service cbade1
 *
Packit Service cbade1
 * @return   VDO_SUCCESS or error
Packit Service cbade1
 **/
Packit Service cbade1
static int parseOneKeyValuePair(const char   *key,
Packit Service cbade1
				const char   *value,
Packit Service cbade1
                                DeviceConfig *config)
Packit Service cbade1
{
Packit Service cbade1
  if (strcmp(key, "deduplication") == 0) {
Packit Service cbade1
    return parseBool(value, "on", "off", &config->deduplication);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // The remaining arguments must have integral values.
Packit Service cbade1
  unsigned int count;
Packit Service cbade1
  int result = stringToUInt(value, &count);
Packit Service cbade1
  if (result != UDS_SUCCESS) {
Packit Service cbade1
    logError("optional config string error: integer value needed, found \"%s\"",
Packit Service cbade1
             value);
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
  return processOneKeyValuePair(key, count, config);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Parse all key/value pairs from a list of arguments.
Packit Service cbade1
 *
Packit Service cbade1
 * If an error occurs during parsing of a single key/value pair, we deem
Packit Service cbade1
 * it serious enough to stop further parsing.
Packit Service cbade1
 *
Packit Service cbade1
 * This function can't set the "reason" value the caller wants to pass
Packit Service cbade1
 * back, because we'd want to format it to say which field was
Packit Service cbade1
 * invalid, and we can't allocate the "reason" strings dynamically. So
Packit Service cbade1
 * if an error occurs, we'll log the details and return the error.
Packit Service cbade1
 *
Packit Service cbade1
 * @param argc     The total number of arguments in list
Packit Service cbade1
 * @param argv     The list of key/value pairs
Packit Service cbade1
 * @param config   The device configuration data to update
Packit Service cbade1
 *
Packit Service cbade1
 * @return   VDO_SUCCESS or error
Packit Service cbade1
 **/
Packit Service cbade1
static int parseKeyValuePairs(int            argc,
Packit Service cbade1
			      char         **argv,
Packit Service cbade1
			      DeviceConfig  *config)
Packit Service cbade1
{
Packit Service cbade1
  int result = VDO_SUCCESS;
Packit Service cbade1
  while (argc) {
Packit Service cbade1
    result = parseOneKeyValuePair(argv[0], argv[1], config);
Packit Service cbade1
    if (result != VDO_SUCCESS) {
Packit Service cbade1
      break;
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
    argc -= 2;
Packit Service cbade1
    argv += 2;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  return result;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Parse the configuration string passed in for optional arguments.
Packit Service cbade1
 *
Packit Service cbade1
 * For V0/V1 configurations, there will only be one optional parameter;
Packit Service cbade1
 * the thread configuration. The configuration string should contain
Packit Service cbade1
 * one or more comma-separated specs of the form "typename=number"; the
Packit Service cbade1
 * supported type names are "cpu", "ack", "bio", "bioRotationInterval",
Packit Service cbade1
 * "logical", "physical", and "hash".
Packit Service cbade1
 *
Packit Service cbade1
 * For V2 configurations and beyond, there could be any number of
Packit Service cbade1
 * arguments. They should contain one or more key/value pairs
Packit Service cbade1
 * separated by a space.
Packit Service cbade1
 *
Packit Service cbade1
 * @param argSet   The structure holding the arguments to parse
Packit Service cbade1
 * @param errorPtr Pointer to a buffer to hold the error string
Packit Service cbade1
 * @param config   Pointer to device configuration data to update
Packit Service cbade1
 *
Packit Service cbade1
 * @return   VDO_SUCCESS or error
Packit Service cbade1
 */
Packit Service cbade1
int parseOptionalArguments(struct dm_arg_set  *argSet,
Packit Service cbade1
			   char              **errorPtr,
Packit Service cbade1
			   DeviceConfig       *config)
Packit Service cbade1
{
Packit Service cbade1
  int result = VDO_SUCCESS;
Packit Service cbade1
Packit Service cbade1
  if (config->version == 0 || config->version == 1) {
Packit Service cbade1
    result = parseThreadConfigString(argSet->argv[0],
Packit Service cbade1
				     &config->threadCounts);
Packit Service cbade1
    if (result != VDO_SUCCESS) {
Packit Service cbade1
      *errorPtr = "Invalid thread-count configuration";
Packit Service cbade1
      return VDO_BAD_CONFIGURATION;
Packit Service cbade1
    }
Packit Service cbade1
  } else {
Packit Service cbade1
    if ((argSet->argc % 2) != 0) {
Packit Service cbade1
      *errorPtr = "Odd number of optional arguments given but they"
Packit Service cbade1
	          " should be <key> <value> pairs";
Packit Service cbade1
      return VDO_BAD_CONFIGURATION;
Packit Service cbade1
    }
Packit Service cbade1
    result = parseKeyValuePairs(argSet->argc, argSet->argv, config);
Packit Service cbade1
    if (result != VDO_SUCCESS) {
Packit Service cbade1
      *errorPtr = "Invalid optional argument configuration";
Packit Service cbade1
      return VDO_BAD_CONFIGURATION;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
  return result;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Handle a parsing error.
Packit Service cbade1
 *
Packit Service cbade1
 * @param configPtr     A pointer to the config to free
Packit Service cbade1
 * @param errorPtr      A place to store a constant string about the error
Packit Service cbade1
 * @param errorStr      A constant string to store in errorPtr
Packit Service cbade1
 **/
Packit Service cbade1
static void handleParseError(DeviceConfig **configPtr,
Packit Service cbade1
                             char         **errorPtr,
Packit Service cbade1
                             char          *errorStr)
Packit Service cbade1
{
Packit Service cbade1
  freeDeviceConfig(configPtr);
Packit Service cbade1
  *errorPtr = errorStr;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
int parseDeviceConfig(int                argc,
Packit Service cbade1
                      char             **argv,
Packit Service cbade1
                      struct dm_target  *ti,
Packit Service cbade1
                      bool               verbose,
Packit Service cbade1
                      DeviceConfig     **configPtr)
Packit Service cbade1
{
Packit Service cbade1
  char **errorPtr = &ti->error;
Packit Service cbade1
  DeviceConfig *config = NULL;
Packit Service cbade1
  int result = ALLOCATE(1, DeviceConfig, "DeviceConfig", &config);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Could not allocate config structure");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  config->owningTarget = ti;
Packit Service cbade1
  initializeRing(&config->configNode);
Packit Service cbade1
Packit Service cbade1
  // Save the original string.
Packit Service cbade1
  result = joinStrings(argv, argc, ' ', &config->originalString);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Could not populate string");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Set defaults.
Packit Service cbade1
  //
Packit Service cbade1
  // XXX Defaults for bioThreads and bioRotationInterval are currently defined
Packit Service cbade1
  // using the old configuration scheme of constants.  These values are relied
Packit Service cbade1
  // upon for performance testing on MGH machines currently.
Packit Service cbade1
  // This should be replaced with the normally used testing defaults being
Packit Service cbade1
  // defined in the file-based thread-configuration settings.  The values used
Packit Service cbade1
  // as defaults internally should really be those needed for VDO in its
Packit Service cbade1
  // default shipped-product state.
Packit Service cbade1
  config->threadCounts = (ThreadCountConfig) {
Packit Service cbade1
    .bioAckThreads       = 1,
Packit Service cbade1
    .bioThreads          = DEFAULT_NUM_BIO_SUBMIT_QUEUES,
Packit Service cbade1
    .bioRotationInterval = DEFAULT_BIO_SUBMIT_QUEUE_ROTATE_INTERVAL,
Packit Service cbade1
    .cpuThreads          = 1,
Packit Service cbade1
    .logicalZones        = 0,
Packit Service cbade1
    .physicalZones       = 0,
Packit Service cbade1
    .hashZones           = 0,
Packit Service cbade1
  };
Packit Service cbade1
  config->maxDiscardBlocks = 1;
Packit Service cbade1
  config->deduplication    = true;
Packit Service cbade1
Packit Service cbade1
  struct dm_arg_set argSet;
Packit Service cbade1
Packit Service cbade1
  argSet.argc = argc;
Packit Service cbade1
  argSet.argv = argv;
Packit Service cbade1
Packit Service cbade1
  result = getVersionNumber(argc, argv, errorPtr, &config->version);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    // getVersionNumber sets errorPtr itself.
Packit Service cbade1
    handleParseError(&config, errorPtr, *errorPtr);
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
  // Move the arg pointer forward only if the argument was there.
Packit Service cbade1
  if (config->version >= 1) {
Packit Service cbade1
    dm_shift_arg(&argSet);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  result = duplicateString(dm_shift_arg(&argSet), "parent device name",
Packit Service cbade1
                           &config->parentDeviceName);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Could not copy parent device name");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the physical blocks, if known.
Packit Service cbade1
  if (config->version >= 1) {
Packit Service cbade1
    result = kstrtoull(dm_shift_arg(&argSet), 10, &config->physicalBlocks);
Packit Service cbade1
    if (result != VDO_SUCCESS) {
Packit Service cbade1
      handleParseError(&config, errorPtr, "Invalid physical block count");
Packit Service cbade1
      return VDO_BAD_CONFIGURATION;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the logical block size and validate
Packit Service cbade1
  bool enable512e;
Packit Service cbade1
  result = parseBool(dm_shift_arg(&argSet), "512", "4096", &enable512e);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Invalid logical block size");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
  config->logicalBlockSize = (enable512e ? 512 : 4096);
Packit Service cbade1
Packit Service cbade1
  // Skip past the two no longer used read cache options.
Packit Service cbade1
  if (config->version <= 1) {
Packit Service cbade1
    dm_consume_args(&argSet, 2);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the page cache size.
Packit Service cbade1
  result = stringToUInt(dm_shift_arg(&argSet), &config->cacheSize);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Invalid block map page cache size");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the block map era length.
Packit Service cbade1
  result = stringToUInt(dm_shift_arg(&argSet), &config->blockMapMaximumAge);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Invalid block map maximum age");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the MD RAID5 optimization mode and validate
Packit Service cbade1
  result = parseBool(dm_shift_arg(&argSet), "on", "off",
Packit Service cbade1
                     &config->mdRaid5ModeEnabled);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Invalid MD RAID5 mode");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the write policy and validate.
Packit Service cbade1
  if (strcmp(argSet.argv[0], "async") == 0) {
Packit Service cbade1
    config->writePolicy = WRITE_POLICY_ASYNC;
Packit Service cbade1
  } else if (strcmp(argSet.argv[0], "async-unsafe") == 0) {
Packit Service cbade1
    config->writePolicy = WRITE_POLICY_ASYNC_UNSAFE;
Packit Service cbade1
  } else if (strcmp(argSet.argv[0], "sync") == 0) {
Packit Service cbade1
    config->writePolicy = WRITE_POLICY_SYNC;
Packit Service cbade1
  } else if (strcmp(argSet.argv[0], "auto") == 0) {
Packit Service cbade1
    config->writePolicy = WRITE_POLICY_AUTO;
Packit Service cbade1
  } else {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Invalid write policy");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
  dm_shift_arg(&argSet);
Packit Service cbade1
Packit Service cbade1
  // Make sure the enum to get the pool name from argv directly is still in
Packit Service cbade1
  // sync with the parsing of the table line.
Packit Service cbade1
  if (&argSet.argv[0] != &argv[POOL_NAME_ARG_INDEX[config->version]]) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Pool name not in expected location");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the address where the albserver is running. Check for validation
Packit Service cbade1
  // is done in dedupe.c code during startKernelLayer call
Packit Service cbade1
  result = duplicateString(dm_shift_arg(&argSet), "pool name",
Packit Service cbade1
			   &config->poolName);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    handleParseError(&config, errorPtr, "Could not copy pool name");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  // Get the optional arguments and validate.
Packit Service cbade1
  result = parseOptionalArguments(&argSet, errorPtr, config);
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    // parseOptionalArguments sets errorPtr itself.
Packit Service cbade1
    handleParseError(&config, errorPtr, *errorPtr);
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  /*
Packit Service cbade1
   * Logical, physical, and hash zone counts can all be zero; then we get one
Packit Service cbade1
   * thread doing everything, our older configuration. If any zone count is
Packit Service cbade1
   * non-zero, the others must be as well.
Packit Service cbade1
   */
Packit Service cbade1
  if (((config->threadCounts.logicalZones == 0)
Packit Service cbade1
       != (config->threadCounts.physicalZones == 0))
Packit Service cbade1
      || ((config->threadCounts.physicalZones == 0)
Packit Service cbade1
          != (config->threadCounts.hashZones == 0))
Packit Service cbade1
      ) {
Packit Service cbade1
    handleParseError(&config, errorPtr,
Packit Service cbade1
                     "Logical, physical, and hash zones counts must all be"
Packit Service cbade1
                     " zero or all non-zero");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  result = dm_get_device(ti, config->parentDeviceName,
Packit Service cbade1
                         dm_table_get_mode(ti->table), &config->ownedDevice);
Packit Service cbade1
  if (result != 0) {
Packit Service cbade1
    logError("couldn't open device \"%s\": error %d",
Packit Service cbade1
             config->parentDeviceName, result);
Packit Service cbade1
    handleParseError(&config, errorPtr, "Unable to open storage device");
Packit Service cbade1
    return VDO_BAD_CONFIGURATION;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  resolveConfigWithDevice(config, verbose);
Packit Service cbade1
Packit Service cbade1
  *configPtr = config;
Packit Service cbade1
  return result;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void freeDeviceConfig(DeviceConfig **configPtr)
Packit Service cbade1
{
Packit Service cbade1
  if (configPtr == NULL) {
Packit Service cbade1
    return;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  DeviceConfig *config = *configPtr;
Packit Service cbade1
  if (config == NULL) {
Packit Service cbade1
    *configPtr = NULL;
Packit Service cbade1
    return;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (config->ownedDevice != NULL) {
Packit Service cbade1
    dm_put_device(config->owningTarget, config->ownedDevice);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  FREE(config->poolName);
Packit Service cbade1
  FREE(config->parentDeviceName);
Packit Service cbade1
  FREE(config->originalString);
Packit Service cbade1
Packit Service cbade1
  // Reduce the chance a use-after-free (as in BZ 1669960) happens to work.
Packit Service cbade1
  memset(config, 0, sizeof(*config));
Packit Service cbade1
Packit Service cbade1
  FREE(config);
Packit Service cbade1
  *configPtr = NULL;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
const char *getConfigWritePolicyString(DeviceConfig *config)
Packit Service cbade1
{
Packit Service cbade1
  switch (config->writePolicy) {
Packit Service cbade1
  case WRITE_POLICY_AUTO:
Packit Service cbade1
    return "auto";
Packit Service cbade1
  case WRITE_POLICY_ASYNC:
Packit Service cbade1
    return "async";
Packit Service cbade1
  case WRITE_POLICY_ASYNC_UNSAFE:
Packit Service cbade1
    return "async-unsafe";
Packit Service cbade1
  case WRITE_POLICY_SYNC:
Packit Service cbade1
    return "sync";
Packit Service cbade1
  default:
Packit Service cbade1
    return "unknown";
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void setDeviceConfigLayer(DeviceConfig *config, KernelLayer *layer)
Packit Service cbade1
{
Packit Service cbade1
  unspliceRingNode(&config->configNode);
Packit Service cbade1
  if (layer != NULL) {
Packit Service cbade1
    pushRingNode(&layer->deviceConfigRing, &config->configNode);
Packit Service cbade1
  }
Packit Service cbade1
  config->layer = layer;
Packit Service cbade1
}