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