1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-01-19 14:04:17 +03:00

Allow dm-integrity to be used for raid images

dm-integrity stores checksums of the data written to an
LV, and returns an error if data read from the LV does
not match the previously saved checksum.  When used on
raid images, dm-raid will correct the error by reading
the block from another image, and the device user sees
no error.  The integrity metadata (checksums) are stored
on an internal LV allocated by lvm for each linear image.
The internal LV is allocated on the same PV as the image.

Create a raid LV with an integrity layer over each
raid image (for raid levels 1,4,5,6,10):

lvcreate --type raidN --raidintegrity y [options]

Add an integrity layer to images of an existing raid LV:

lvconvert --raidintegrity y LV

Remove the integrity layer from images of a raid LV:

lvconvert --raidintegrity n LV

Settings

Use --raidintegritymode journal|bitmap (journal is default)
to configure the method used by dm-integrity to ensure
crash consistency.

Initialization

When integrity is added to an LV, the kernel needs to
initialize the integrity metadata/checksums for all blocks
in the LV.  The data corruption checking performed by
dm-integrity will only operate on areas of the LV that
are already initialized.  The progress of integrity
initialization is reported by the "syncpercent" LV
reporting field (and under the Cpy%Sync lvs column.)

Example: create a raid1 LV with integrity:

$ lvcreate --type raid1 -m1 --raidintegrity y -n rr -L1G foo
  Creating integrity metadata LV rr_rimage_0_imeta with size 12.00 MiB.
  Logical volume "rr_rimage_0_imeta" created.
  Creating integrity metadata LV rr_rimage_1_imeta with size 12.00 MiB.
  Logical volume "rr_rimage_1_imeta" created.
  Logical volume "rr" created.
$ lvs -a foo
  LV                  VG  Attr       LSize  Origin              Cpy%Sync
  rr                  foo rwi-a-r---  1.00g                     4.93
  [rr_rimage_0]       foo gwi-aor---  1.00g [rr_rimage_0_iorig] 41.02
  [rr_rimage_0_imeta] foo ewi-ao---- 12.00m
  [rr_rimage_0_iorig] foo -wi-ao----  1.00g
  [rr_rimage_1]       foo gwi-aor---  1.00g [rr_rimage_1_iorig] 39.45
  [rr_rimage_1_imeta] foo ewi-ao---- 12.00m
  [rr_rimage_1_iorig] foo -wi-ao----  1.00g
  [rr_rmeta_0]        foo ewi-aor---  4.00m
  [rr_rmeta_1]        foo ewi-aor---  4.00m
This commit is contained in:
David Teigland 2019-11-20 16:07:27 -06:00
parent b6b4ad8e28
commit d9e8895a96
45 changed files with 3790 additions and 37 deletions

27
configure vendored
View File

@ -918,6 +918,7 @@ enable_cache_check_needs_check
with_vdo
with_vdo_format
with_writecache
with_integrity
enable_readline
enable_realtime
enable_ocf
@ -1716,6 +1717,7 @@ Optional Packages:
--with-vdo=TYPE vdo support: internal/none [internal]
--with-vdo-format=PATH vdoformat tool: [autodetect]
--with-writecache=TYPE writecache support: internal/none [none]
--with-integrity=TYPE integrity support: internal/none [none]
--with-ocfdir=DIR install OCF files in
[PREFIX/lib/ocf/resource.d/lvm2]
--with-default-pid-dir=PID_DIR
@ -9761,6 +9763,31 @@ $as_echo "#define WRITECACHE_INTERNAL 1" >>confdefs.h
*) as_fn_error $? "--with-writecache parameter invalid" "$LINENO" 5 ;;
esac
################################################################################
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include integrity" >&5
$as_echo_n "checking whether to include integrity... " >&6; }
# Check whether --with-integrity was given.
if test "${with_integrity+set}" = set; then :
withval=$with_integrity; INTEGRITY=$withval
else
INTEGRITY="none"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INTEGRITY" >&5
$as_echo "$INTEGRITY" >&6; }
case "$INTEGRITY" in
none) ;;
internal)
$as_echo "#define INTEGRITY_INTERNAL 1" >>confdefs.h
;;
*) as_fn_error $? "--with-integrity parameter invalid" "$LINENO" 5 ;;
esac
################################################################################
# Check whether --enable-readline was given.
if test "${enable_readline+set}" = set; then :

View File

@ -667,6 +667,24 @@ case "$WRITECACHE" in
*) AC_MSG_ERROR([--with-writecache parameter invalid]) ;;
esac
################################################################################
dnl -- integrity inclusion type
AC_MSG_CHECKING(whether to include integrity)
AC_ARG_WITH(integrity,
AC_HELP_STRING([--with-integrity=TYPE],
[integrity support: internal/none [none]]),
INTEGRITY=$withval, INTEGRITY="none")
AC_MSG_RESULT($INTEGRITY)
case "$INTEGRITY" in
none) ;;
internal)
AC_DEFINE([INTEGRITY_INTERNAL], 1, [Define to 1 to include built-in support for integrity.])
;;
*) AC_MSG_ERROR([--with-integrity parameter invalid]) ;;
esac
################################################################################
dnl -- Disable readline
AC_ARG_ENABLE([readline],

View File

@ -234,6 +234,7 @@ int dm_task_suppress_identical_reload(struct dm_task *dmt);
int dm_task_secure_data(struct dm_task *dmt);
int dm_task_retry_remove(struct dm_task *dmt);
int dm_task_deferred_remove(struct dm_task *dmt);
void dm_task_skip_reload_params_compare(struct dm_task *dmt);
/*
* Record timestamp immediately after the ioctl returns.
@ -392,6 +393,15 @@ struct dm_status_writecache {
int dm_get_status_writecache(struct dm_pool *mem, const char *params,
struct dm_status_writecache **status);
struct dm_status_integrity {
uint64_t number_of_mismatches;
uint64_t provided_data_sectors;
uint64_t recalc_sector;
};
int dm_get_status_integrity(struct dm_pool *mem, const char *params,
struct dm_status_integrity **status);
/*
* Parse params from STATUS call for snapshot target
*
@ -970,6 +980,35 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
uint32_t writecache_block_size,
struct writecache_settings *settings);
struct integrity_settings {
char mode[8];
uint32_t tag_size;
uint32_t block_size; /* optional table param always set by lvm */
const char *internal_hash; /* optional table param always set by lvm */
uint32_t journal_sectors;
uint32_t interleave_sectors;
uint32_t buffer_sectors;
uint32_t journal_watermark;
uint32_t commit_time;
uint32_t bitmap_flush_interval;
uint64_t sectors_per_bit;
unsigned journal_sectors_set:1;
unsigned interleave_sectors_set:1;
unsigned buffer_sectors_set:1;
unsigned journal_watermark_set:1;
unsigned commit_time_set:1;
unsigned bitmap_flush_interval_set:1;
unsigned sectors_per_bit_set:1;
};
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
uint64_t size,
const char *origin_uuid,
const char *meta_uuid,
struct integrity_settings *settings,
int recalculate);
/*
* VDO target

View File

@ -805,6 +805,11 @@ int dm_task_suppress_identical_reload(struct dm_task *dmt)
return 1;
}
void dm_task_skip_reload_params_compare(struct dm_task *dmt)
{
dmt->skip_reload_params_compare = 1;
}
int dm_task_set_add_node(struct dm_task *dmt, dm_add_node_t add_node)
{
switch (add_node) {
@ -1575,11 +1580,29 @@ static int _reload_with_suppression_v4(struct dm_task *dmt)
len = strlen(t2->params);
while (len-- > 0 && t2->params[len] == ' ')
t2->params[len] = '\0';
if ((t1->start != t2->start) ||
(t1->length != t2->length) ||
(strcmp(t1->type, t2->type)) ||
(strcmp(t1->params, t2->params)))
if (t1->start != t2->start) {
log_debug("reload %u:%u start diff", task->major, task->minor);
goto no_match;
}
if (t1->length != t2->length) {
log_debug("reload %u:%u length diff", task->major, task->minor);
goto no_match;
}
if (strcmp(t1->type, t2->type)) {
log_debug("reload %u:%u type diff %s %s", task->major, task->minor, t1->type, t2->type);
goto no_match;
}
if (strcmp(t1->params, t2->params)) {
if (dmt->skip_reload_params_compare)
log_debug("reload %u:%u skip params ignore %s %s",
task->major, task->minor, t1->params, t2->params);
else {
log_debug("reload %u:%u params diff", task->major, task->minor);
goto no_match;
}
}
t1 = t1->next;
t2 = t2->next;
}

View File

@ -59,6 +59,7 @@ struct dm_task {
int skip_lockfs;
int query_inactive_table;
int suppress_identical_reload;
int skip_reload_params_compare;
dm_add_node_t add_node;
uint64_t existing_table_size;
int cookie_set;

View File

@ -38,6 +38,7 @@ enum {
SEG_STRIPED,
SEG_ZERO,
SEG_WRITECACHE,
SEG_INTEGRITY,
SEG_THIN_POOL,
SEG_THIN,
SEG_VDO,
@ -78,6 +79,7 @@ static const struct {
{ SEG_STRIPED, "striped" },
{ SEG_ZERO, "zero"},
{ SEG_WRITECACHE, "writecache"},
{ SEG_INTEGRITY, "integrity"},
{ SEG_THIN_POOL, "thin-pool"},
{ SEG_THIN, "thin"},
{ SEG_VDO, "vdo" },
@ -221,6 +223,11 @@ struct load_segment {
int writecache_pmem; /* writecache, 1 if pmem, 0 if ssd */
uint32_t writecache_block_size; /* writecache, in bytes */
struct writecache_settings writecache_settings; /* writecache */
uint64_t integrity_data_sectors; /* integrity (provided_data_sectors) */
struct dm_tree_node *integrity_meta_node; /* integrity */
struct integrity_settings integrity_settings; /* integrity */
int integrity_recalculate; /* integrity */
};
/* Per-device properties */
@ -267,6 +274,16 @@ struct load_properties {
*/
unsigned delay_resume_if_extended;
/*
* When comparing table lines to decide if a reload is
* needed, ignore any differences betwen the lvm device
* params and the kernel-reported device params.
* dm-integrity reports many internal parameters on the
* table line when lvm does not explicitly set them,
* causing lvm and the kernel to have differing params.
*/
unsigned skip_reload_params_compare;
/*
* Call node_send_messages(), set to 2 if there are messages
* When != 0, it validates matching transaction id, thus thin-pools
@ -2705,6 +2722,84 @@ static int _writecache_emit_segment_line(struct dm_task *dmt,
return 1;
}
static int _integrity_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
{
struct integrity_settings *set = &seg->integrity_settings;
int pos = 0;
int count;
char origin_dev[DM_FORMAT_DEV_BUFSIZE];
char meta_dev[DM_FORMAT_DEV_BUFSIZE];
if (!_build_dev_string(origin_dev, sizeof(origin_dev), seg->origin))
return_0;
if (seg->integrity_meta_node &&
!_build_dev_string(meta_dev, sizeof(meta_dev), seg->integrity_meta_node))
return_0;
count = 3; /* block_size, internal_hash, fix_padding options are always passed */
if (seg->integrity_meta_node)
count++;
if (seg->integrity_recalculate)
count++;
if (set->journal_sectors_set)
count++;
if (set->interleave_sectors_set)
count++;
if (set->buffer_sectors_set)
count++;
if (set->journal_watermark_set)
count++;
if (set->commit_time_set)
count++;
if (set->bitmap_flush_interval_set)
count++;
if (set->sectors_per_bit_set)
count++;
EMIT_PARAMS(pos, "%s 0 %u %s %d fix_padding block_size:%u internal_hash:%s",
origin_dev,
set->tag_size,
set->mode,
count,
set->block_size,
set->internal_hash);
if (seg->integrity_meta_node)
EMIT_PARAMS(pos, " meta_device:%s", meta_dev);
if (seg->integrity_recalculate)
EMIT_PARAMS(pos, " recalculate");
if (set->journal_sectors_set)
EMIT_PARAMS(pos, " journal_sectors:%u", set->journal_sectors);
if (set->interleave_sectors_set)
EMIT_PARAMS(pos, " ineterleave_sectors:%u", set->interleave_sectors);
if (set->buffer_sectors_set)
EMIT_PARAMS(pos, " buffer_sectors:%u", set->buffer_sectors);
if (set->journal_watermark_set)
EMIT_PARAMS(pos, " journal_watermark:%u", set->journal_watermark);
if (set->commit_time_set)
EMIT_PARAMS(pos, " commit_time:%u", set->commit_time);
if (set->bitmap_flush_interval_set)
EMIT_PARAMS(pos, " bitmap_flush_interval:%u", set->bitmap_flush_interval);
if (set->sectors_per_bit_set)
EMIT_PARAMS(pos, " sectors_per_bit:%llu", (unsigned long long)set->sectors_per_bit);
return 1;
}
static int _thin_pool_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
@ -2889,6 +2984,10 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
if (!_writecache_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
case SEG_INTEGRITY:
if (!_integrity_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
}
switch(seg->type) {
@ -2901,6 +3000,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
case SEG_THIN:
case SEG_CACHE:
case SEG_WRITECACHE:
case SEG_INTEGRITY:
break;
case SEG_CRYPT:
case SEG_LINEAR:
@ -3005,6 +3105,9 @@ static int _load_node(struct dm_tree_node *dnode)
if (!dm_task_suppress_identical_reload(dmt))
log_warn("WARNING: Failed to suppress reload of identical tables.");
if (dnode->props.skip_reload_params_compare)
dm_task_skip_reload_params_compare(dmt);
if ((r = dm_task_run(dmt))) {
r = dm_task_get_info(dmt, &dnode->info);
if (r && !dnode->info.inactive_table)
@ -3023,8 +3126,8 @@ static int _load_node(struct dm_tree_node *dnode)
if (!existing_table_size && dnode->props.delay_resume_if_new)
dnode->props.size_changed = 0;
log_debug_activation("Table size changed from %" PRIu64 " to %"
PRIu64 " for %s.%s", existing_table_size,
log_debug_activation("Table size changed from %" PRIu64 " to %" PRIu64 " for %s.%s",
existing_table_size,
seg_start, _node_name(dnode),
dnode->props.size_changed ? "" : " (Ignoring.)");
@ -3136,7 +3239,10 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
}
/* No resume for a device without parents or with unchanged or smaller size */
if (!dm_tree_node_num_children(child, 1) || (child->props.size_changed <= 0))
if (!dm_tree_node_num_children(child, 1))
continue;
if (child->props.size_changed <= 0)
continue;
if (!child->info.inactive_table && !child->info.suspended)
@ -3738,6 +3844,48 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
return 1;
}
int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
uint64_t size,
const char *origin_uuid,
const char *meta_uuid,
struct integrity_settings *settings,
int recalculate)
{
struct load_segment *seg;
if (!(seg = _add_segment(node, SEG_INTEGRITY, size)))
return_0;
if (!meta_uuid) {
log_error("No integrity meta uuid.");
return 0;
}
if (!(seg->integrity_meta_node = dm_tree_find_node_by_uuid(node->dtree, meta_uuid))) {
log_error("Missing integrity's meta uuid %s.", meta_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->integrity_meta_node))
return_0;
if (!(seg->origin = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
log_error("Missing integrity's origin uuid %s.", origin_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->origin))
return_0;
memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
seg->integrity_recalculate = recalculate;
node->props.skip_reload_params_compare = 1;
return 1;
}
int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
uint64_t size,
const char *rlog_uuid,

View File

@ -380,6 +380,33 @@ int dm_get_status_writecache(struct dm_pool *mem, const char *params,
return 1;
}
int dm_get_status_integrity(struct dm_pool *mem, const char *params,
struct dm_status_integrity **status)
{
struct dm_status_integrity *s;
char recalc_str[16] = "\0";
if (!(s = dm_pool_zalloc(mem, sizeof(*s))))
return_0;
if (sscanf(params, "%llu %llu %s",
(unsigned long long *)&s->number_of_mismatches,
(unsigned long long *)&s->provided_data_sectors,
recalc_str) != 3) {
log_error("Failed to parse integrity params: %s.", params);
dm_pool_free(mem, s);
return 0;
}
if (recalc_str[0] == '-')
s->recalc_sector = 0;
else
s->recalc_sector = strtoull(recalc_str, NULL, 0);
*status = s;
return 1;
}
int parse_thin_pool_status(const char *params, struct dm_status_thin_pool *s)
{
int pos;

View File

@ -678,6 +678,9 @@
/* Define to 1 to include built-in support for writecache. */
#undef WRITECACHE_INTERNAL
/* Define to 1 to include built-in support for integrity. */
#undef INTEGRITY_INTERNAL
/* Define to get access to GNU/Linux extension */
#undef _GNU_SOURCE

View File

@ -20,6 +20,7 @@ SOURCES =\
activate/activate.c \
cache/lvmcache.c \
writecache/writecache.c \
integrity/integrity.c \
cache_segtype/cache.c \
commands/toolcontext.c \
config/config.c \
@ -67,6 +68,7 @@ SOURCES =\
log/log.c \
metadata/cache_manip.c \
metadata/writecache_manip.c \
metadata/integrity_manip.c \
metadata/lv.c \
metadata/lv_manip.c \
metadata/merge.c \

View File

@ -2535,6 +2535,13 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
goto out;
}
if ((cmd->partial_activation || cmd->degraded_activation) &&
lv_is_partial(lv) && lv_is_raid(lv) && lv_raid_has_integrity((struct logical_volume *)lv)) {
cmd->partial_activation = 0;
cmd->degraded_activation = 0;
log_print("No degraded or partial activation for raid with integrity.");
}
if ((!lv->vg->cmd->partial_activation) && lv_is_partial(lv)) {
if (!lv_is_raid_type(lv) || !partial_raid_lv_supports_degraded_activation(lv)) {
log_error("Refusing activation of partial LV %s. "

View File

@ -39,6 +39,7 @@ typedef enum {
SEG_STATUS_THIN_POOL,
SEG_STATUS_VDO_POOL,
SEG_STATUS_WRITECACHE,
SEG_STATUS_INTEGRITY,
SEG_STATUS_UNKNOWN
} lv_seg_status_type_t;
@ -53,6 +54,7 @@ struct lv_seg_status {
struct dm_status_thin *thin;
struct dm_status_thin_pool *thin_pool;
struct dm_status_writecache *writecache;
struct dm_status_integrity *integrity;
struct lv_status_vdo vdo_pool;
};
};
@ -260,6 +262,7 @@ void fs_unlock(void);
#define TARGET_NAME_CACHE "cache"
#define TARGET_NAME_WRITECACHE "writecache"
#define TARGET_NAME_INTEGRITY "integrity"
#define TARGET_NAME_ERROR "error"
#define TARGET_NAME_ERROR_OLD "erro" /* Truncated in older kernels */
#define TARGET_NAME_LINEAR "linear"
@ -277,6 +280,7 @@ void fs_unlock(void);
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
#define MODULE_NAME_CACHE TARGET_NAME_CACHE
#define MODULE_NAME_WRITECACHE TARGET_NAME_WRITECACHE
#define MODULE_NAME_INTEGRITY TARGET_NAME_INTEGRITY
#define MODULE_NAME_ERROR TARGET_NAME_ERROR
#define MODULE_NAME_LOG_CLUSTERED "log-clustered"
#define MODULE_NAME_LOG_USERSPACE "log-userspace"

View File

@ -46,7 +46,7 @@ typedef enum {
} action_t;
/* This list must match lib/misc/lvm-string.c:build_dm_uuid(). */
const char *uuid_suffix_list[] = { "pool", "cdata", "cmeta", "cvol", "tdata", "tmeta", "vdata", "vpool", NULL};
const char *uuid_suffix_list[] = { "pool", "cdata", "cmeta", "cvol", "tdata", "tmeta", "vdata", "vpool", "imeta", NULL};
struct dlid_list {
struct dm_list list;
@ -222,6 +222,10 @@ static int _get_segment_status_from_target_params(const char *target_name,
if (!dm_get_status_writecache(seg_status->mem, params, &(seg_status->writecache)))
return_0;
seg_status->type = SEG_STATUS_WRITECACHE;
} else if (segtype_is_integrity(segtype)) {
if (!dm_get_status_integrity(seg_status->mem, params, &(seg_status->integrity)))
return_0;
seg_status->type = SEG_STATUS_INTEGRITY;
} else
/*
* TODO: Add support for other segment types too!
@ -299,6 +303,9 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
if (lv_is_vdo_pool(seg_status->seg->lv))
length = get_vdo_pool_virtual_size(seg_status->seg);
if (lv_is_integrity(seg_status->seg->lv))
length = seg_status->seg->integrity_data_sectors;
do {
target = dm_get_next_target(dmt, target, &target_start,
&target_length, &target_name, &target_params);
@ -2620,6 +2627,10 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
if (!_add_lv_to_dtree(dm, dtree, seg->writecache, dm->activation ? origin_only : 1))
return_0;
}
if (seg->integrity_meta_dev && seg_is_integrity(seg)) {
if (!_add_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, dm->activation ? origin_only : 1))
return_0;
}
if (seg->pool_lv &&
(lv_is_cache_pool(seg->pool_lv) || lv_is_cache_vol(seg->pool_lv) || dm->track_external_lv_deps) &&
/* When activating and not origin_only detect linear 'overlay' over pool */
@ -3076,6 +3087,11 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
lv_layer(seg->writecache)))
return_0;
if (seg->integrity_meta_dev && !laopts->origin_only &&
!_add_new_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, laopts,
lv_layer(seg->integrity_meta_dev)))
return_0;
/* Add any LVs used by this segment */
for (s = 0; s < seg->area_count; ++s) {
if ((seg_type(seg, s) == AREA_LV) &&

View File

@ -1362,6 +1362,11 @@ static int _init_segtypes(struct cmd_context *cmd)
return 0;
#endif
#ifdef INTEGRITY_INTERNAL
if (!init_integrity_segtypes(cmd, &seglib))
return 0;
#endif
return 1;
}

View File

@ -646,6 +646,45 @@ out:
return ret;
}
#ifdef BLKID_WIPING_SUPPORT
int get_fs_block_size(struct device *dev, uint32_t *fs_block_size)
{
blkid_probe probe = NULL;
const char *block_size_str = NULL;
uint64_t block_size_val;
int r = 0;
*fs_block_size = 0;
if (!(probe = blkid_new_probe_from_filename(dev_name(dev)))) {
log_error("Failed to create a new blkid probe for device %s.", dev_name(dev));
goto out;
}
blkid_probe_enable_partitions(probe, 1);
(void) blkid_probe_lookup_value(probe, "BLOCK_SIZE", &block_size_str, NULL);
if (!block_size_str)
goto out;
block_size_val = strtoull(block_size_str, NULL, 10);
*fs_block_size = (uint32_t)block_size_val;
r = 1;
out:
if (probe)
blkid_free_probe(probe);
return r;
}
#else
int get_fs_block_size(struct device *dev, uint32_t *fs_block_size)
{
*fs_block_size = 0;
return 0;
}
#endif
#ifdef BLKID_WIPING_SUPPORT
static inline int _type_in_flag_list(const char *type, uint32_t flag_list)

View File

@ -97,4 +97,6 @@ int dev_is_pmem(struct device *dev);
int dev_is_lv(struct device *dev);
int get_fs_block_size(struct device *dev, uint32_t *fs_block_size);
#endif

View File

@ -104,6 +104,8 @@ static const struct flag _lv_flags[] = {
{LV_VDO_POOL, NULL, 0},
{LV_VDO_POOL_DATA, NULL, 0},
{WRITECACHE, NULL, 0},
{INTEGRITY, NULL, 0},
{INTEGRITY_METADATA, NULL, 0},
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
{LV_REMOVED, NULL, 0},
{0, NULL, 0}

343
lib/integrity/integrity.c Normal file
View File

@ -0,0 +1,343 @@
/*
* Copyright (C) 2013-2016 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/metadata/segtype.h"
#include "lib/display/display.h"
#include "lib/format_text/text_export.h"
#include "lib/config/config.h"
#include "lib/datastruct/str_list.h"
#include "lib/misc/lvm-string.h"
#include "lib/activate/activate.h"
#include "lib/metadata/metadata.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/config/defaults.h"
#define SEG_LOG_ERROR(t, p...) \
log_error(t " segment %s of logical volume %s.", ## p, \
dm_config_parent_name(sn), seg->lv->name), 0;
static void _integrity_display(const struct lv_segment *seg)
{
/* TODO: lvdisplay segments */
}
static int _integrity_text_import(struct lv_segment *seg,
const struct dm_config_node *sn,
struct dm_hash_table *pv_hash __attribute__((unused)))
{
struct integrity_settings *set;
struct logical_volume *origin_lv = NULL;
struct logical_volume *meta_lv = NULL;
const char *origin_name = NULL;
const char *meta_dev = NULL;
const char *mode = NULL;
const char *hash = NULL;
memset(&seg->integrity_settings, 0, sizeof(struct integrity_settings));
set = &seg->integrity_settings;
/* origin always set */
if (!dm_config_has_node(sn, "origin"))
return SEG_LOG_ERROR("origin not specified in");
if (!dm_config_get_str(sn, "origin", &origin_name))
return SEG_LOG_ERROR("origin must be a string in");
if (!(origin_lv = find_lv(seg->lv->vg, origin_name)))
return SEG_LOG_ERROR("Unknown LV specified for integrity origin %s in", origin_name);
if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0))
return_0;
/* data_sectors always set */
if (!dm_config_get_uint64(sn, "data_sectors", &seg->integrity_data_sectors))
return SEG_LOG_ERROR("integrity data_sectors must be set in");
/* mode always set */
if (!dm_config_get_str(sn, "mode", &mode))
return SEG_LOG_ERROR("integrity mode must be set in");
if (strlen(mode) > 7)
return SEG_LOG_ERROR("integrity mode invalid in");
strncpy(set->mode, mode, 7);
/* tag_size always set */
if (!dm_config_get_uint32(sn, "tag_size", &set->tag_size))
return SEG_LOG_ERROR("integrity tag_size must be set in");
/* block_size always set */
if (!dm_config_get_uint32(sn, "block_size", &set->block_size))
return SEG_LOG_ERROR("integrity block_size invalid in");
/* internal_hash always set */
if (!dm_config_get_str(sn, "internal_hash", &hash))
return SEG_LOG_ERROR("integrity internal_hash must be set in");
if (!(set->internal_hash = dm_pool_strdup(seg->lv->vg->vgmem, hash)))
return SEG_LOG_ERROR("integrity internal_hash failed to be set in");
/* meta_dev optional */
if (dm_config_has_node(sn, "meta_dev")) {
if (!dm_config_get_str(sn, "meta_dev", &meta_dev))
return SEG_LOG_ERROR("meta_dev must be a string in");
if (!(meta_lv = find_lv(seg->lv->vg, meta_dev)))
return SEG_LOG_ERROR("Unknown logical volume %s specified for integrity in", meta_dev);
}
if (dm_config_has_node(sn, "recalculate")) {
if (!dm_config_get_uint32(sn, "recalculate", &seg->integrity_recalculate))
return SEG_LOG_ERROR("integrity recalculate error in");
}
/* the rest are optional */
if (dm_config_has_node(sn, "journal_sectors")) {
if (!dm_config_get_uint32(sn, "journal_sectors", &set->journal_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->journal_sectors_set = 1;
}
if (dm_config_has_node(sn, "interleave_sectors")) {
if (!dm_config_get_uint32(sn, "interleave_sectors", &set->interleave_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->interleave_sectors_set = 1;
}
if (dm_config_has_node(sn, "buffer_sectors")) {
if (!dm_config_get_uint32(sn, "buffer_sectors", &set->buffer_sectors))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->buffer_sectors_set = 1;
}
if (dm_config_has_node(sn, "journal_watermark")) {
if (!dm_config_get_uint32(sn, "journal_watermark", &set->journal_watermark))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->journal_watermark_set = 1;
}
if (dm_config_has_node(sn, "commit_time")) {
if (!dm_config_get_uint32(sn, "commit_time", &set->commit_time))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->commit_time_set = 1;
}
if (dm_config_has_node(sn, "bitmap_flush_interval")) {
if (!dm_config_get_uint32(sn, "bitmap_flush_interval", &set->bitmap_flush_interval))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->bitmap_flush_interval_set = 1;
}
if (dm_config_has_node(sn, "sectors_per_bit")) {
if (!dm_config_get_uint64(sn, "sectors_per_bit", &set->sectors_per_bit))
return SEG_LOG_ERROR("Unknown integrity_setting in");
set->sectors_per_bit_set = 1;
}
seg->origin = origin_lv;
seg->integrity_meta_dev = meta_lv;
seg->lv->status |= INTEGRITY;
if (meta_lv)
meta_lv->status |= INTEGRITY_METADATA;
if (meta_lv && !add_seg_to_segs_using_this_lv(meta_lv, seg))
return_0;
return 1;
}
static int _integrity_text_import_area_count(const struct dm_config_node *sn,
uint32_t *area_count)
{
*area_count = 1;
return 1;
}
static int _integrity_text_export(const struct lv_segment *seg,
struct formatter *f)
{
const struct integrity_settings *set = &seg->integrity_settings;
outf(f, "origin = \"%s\"", seg_lv(seg, 0)->name);
outf(f, "data_sectors = %llu", (unsigned long long)seg->integrity_data_sectors);
outf(f, "mode = \"%s\"", set->mode);
outf(f, "tag_size = %u", set->tag_size);
outf(f, "block_size = %u", set->block_size);
outf(f, "internal_hash = \"%s\"", set->internal_hash);
if (seg->integrity_meta_dev)
outf(f, "meta_dev = \"%s\"", seg->integrity_meta_dev->name);
if (seg->integrity_recalculate)
outf(f, "recalculate = %u", seg->integrity_recalculate);
if (set->journal_sectors_set)
outf(f, "journal_sectors = %u", set->journal_sectors);
if (set->interleave_sectors_set)
outf(f, "interleave_sectors = %u", set->interleave_sectors);
if (set->buffer_sectors_set)
outf(f, "buffer_sectors = %u", set->buffer_sectors);
if (set->journal_watermark_set)
outf(f, "journal_watermark = %u", set->journal_watermark);
if (set->commit_time_set)
outf(f, "commit_time = %u", set->commit_time);
if (set->bitmap_flush_interval)
outf(f, "bitmap_flush_interval = %u", set->bitmap_flush_interval);
if (set->sectors_per_bit)
outf(f, "sectors_per_bit = %llu", (unsigned long long)set->sectors_per_bit);
return 1;
}
static void _destroy(struct segment_type *segtype)
{
free((void *) segtype);
}
#ifdef DEVMAPPER_SUPPORT
static int _target_present(struct cmd_context *cmd,
const struct lv_segment *seg __attribute__((unused)),
unsigned *attributes __attribute__((unused)))
{
static int _integrity_checked = 0;
static int _integrity_present = 0;
uint32_t maj, min, patchlevel;
if (!activation())
return 0;
if (!_integrity_checked) {
_integrity_checked = 1;
_integrity_present = target_present(cmd, TARGET_NAME_INTEGRITY, 1);
if (!target_version(TARGET_NAME_INTEGRITY, &maj, &min, &patchlevel))
return 0;
if (maj < 1 || min < 6) {
log_error("Integrity target version older than minimum 1.6.0");
return 0;
}
}
return _integrity_present;
}
static int _modules_needed(struct dm_pool *mem,
const struct lv_segment *seg __attribute__((unused)),
struct dm_list *modules)
{
if (!str_list_add(mem, modules, MODULE_NAME_INTEGRITY)) {
log_error("String list allocation failed for integrity module.");
return 0;
}
return 1;
}
#endif /* DEVMAPPER_SUPPORT */
#ifdef DEVMAPPER_SUPPORT
static int _integrity_add_target_line(struct dev_manager *dm,
struct dm_pool *mem,
struct cmd_context *cmd __attribute__((unused)),
void **target_state __attribute__((unused)),
struct lv_segment *seg,
const struct lv_activate_opts *laopts,
struct dm_tree_node *node, uint64_t len,
uint32_t *pvmove_mirror_count __attribute__((unused)))
{
char *origin_uuid;
char *meta_uuid = NULL;
if (!seg_is_integrity(seg)) {
log_error(INTERNAL_ERROR "Passed segment is not integrity.");
return 0;
}
if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0), NULL)))
return_0;
if (seg->integrity_meta_dev) {
if (!(meta_uuid = build_dm_uuid(mem, seg->integrity_meta_dev, NULL)))
return_0;
}
if (!seg->integrity_data_sectors) {
log_error("_integrity_add_target_line zero size");
return_0;
}
if (!dm_tree_node_add_integrity_target(node, seg->integrity_data_sectors,
origin_uuid, meta_uuid,
&seg->integrity_settings,
seg->integrity_recalculate))
return_0;
return 1;
}
#endif /* DEVMAPPER_SUPPORT */
static struct segtype_handler _integrity_ops = {
.display = _integrity_display,
.text_import = _integrity_text_import,
.text_import_area_count = _integrity_text_import_area_count,
.text_export = _integrity_text_export,
#ifdef DEVMAPPER_SUPPORT
.add_target_line = _integrity_add_target_line,
.target_present = _target_present,
.modules_needed = _modules_needed,
#endif
.destroy = _destroy,
};
int init_integrity_segtypes(struct cmd_context *cmd,
struct segtype_library *seglib)
{
struct segment_type *segtype = zalloc(sizeof(*segtype));
if (!segtype) {
log_error("Failed to allocate memory for integrity segtype");
return 0;
}
segtype->name = SEG_TYPE_NAME_INTEGRITY;
segtype->flags = SEG_INTEGRITY;
segtype->ops = &_integrity_ops;
if (!lvm_register_segtype(seglib, segtype))
return_0;
log_very_verbose("Initialised segtype: %s", segtype->name);
return 1;
}

View File

@ -0,0 +1,821 @@
/*
* Copyright (C) 2014-2015 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib/misc/lib.h"
#include "lib/metadata/metadata.h"
#include "lib/locking/locking.h"
#include "lib/misc/lvm-string.h"
#include "lib/commands/toolcontext.h"
#include "lib/display/display.h"
#include "lib/metadata/segtype.h"
#include "lib/activate/activate.h"
#include "lib/config/defaults.h"
#include "lib/activate/dev_manager.h"
#define DEFAULT_TAG_SIZE 4 /* bytes */
#define DEFAULT_MODE 'J'
#define DEFAULT_INTERNAL_HASH "crc32c"
#define DEFAULT_BLOCK_SIZE 512
#define ONE_MB_IN_BYTES 1048576
int lv_is_integrity_origin(const struct logical_volume *lv)
{
struct seg_list *sl;
dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
if (!sl->seg || !sl->seg->lv || !sl->seg->origin)
continue;
if (lv_is_integrity(sl->seg->lv) && (sl->seg->origin == lv))
return 1;
}
return 0;
}
/*
* Every 500M of data needs 4M of metadata.
* (From trial and error testing.)
*/
static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes)
{
return ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);
}
/*
* The user wants external metadata, but did not specify an existing
* LV to hold metadata, so create an LV for metadata.
*/
static int _lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg,
struct lvcreate_params *lp,
struct logical_volume **meta_lv)
{
char metaname[NAME_LEN];
uint64_t lv_size_bytes, meta_bytes, meta_sectors;
struct logical_volume *lv;
struct lvcreate_params lp_meta = {
.activate = CHANGE_AN,
.alloc = ALLOC_INHERIT,
.major = -1,
.minor = -1,
.permission = LVM_READ | LVM_WRITE,
.pvh = &vg->pvs,
.read_ahead = DM_READ_AHEAD_NONE,
.stripes = 1,
.vg_name = vg->name,
.zero = 0,
.wipe_signatures = 0,
.suppress_zero_warn = 1,
};
if (lp->lv_name &&
dm_snprintf(metaname, NAME_LEN, "%s_imeta", lp->lv_name) < 0) {
log_error("Failed to create metadata LV name.");
return 0;
}
lp_meta.lv_name = metaname;
lp_meta.pvh = lp->pvh;
lv_size_bytes = (uint64_t)lp->extents * (uint64_t)vg->extent_size * 512;
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
meta_sectors = meta_bytes / 512;
lp_meta.extents = meta_sectors / vg->extent_size;
log_print_unless_silent("Creating integrity metadata LV %s with size %s.",
metaname, display_size(cmd, meta_sectors));
dm_list_init(&lp_meta.tags);
if (!(lp_meta.segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
if (!(lv = lv_create_single(vg, &lp_meta))) {
log_error("Failed to create integrity metadata LV");
return 0;
}
if (dm_list_size(&lv->segments) > 1) {
log_error("Integrity metadata uses more than one segment.");
return 0;
}
*meta_lv = lv;
return 1;
}
int lv_extend_integrity_in_raid(struct logical_volume *lv, struct dm_list *pvh)
{
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
const struct segment_type *segtype;
struct lv_segment *seg_top, *seg_image;
struct logical_volume *lv_image;
struct logical_volume *lv_iorig;
struct logical_volume *lv_imeta;
struct dm_list allocatable_pvs;
struct dm_list *use_pvh;
uint64_t lv_size_bytes, meta_bytes, meta_sectors, prev_meta_sectors;
uint32_t meta_extents, prev_meta_extents;
uint32_t area_count, s;
seg_top = first_seg(lv);
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
area_count = seg_top->area_count;
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
seg_image = first_seg(lv_image);
if (!(lv_imeta = seg_image->integrity_meta_dev)) {
log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
return 0;
}
if (!(lv_iorig = seg_lv(seg_image, 0))) {
log_error("LV %s integrity segment has no origin", display_lvname(lv));
return 0;
}
lv_size_bytes = lv_iorig->size * 512;
meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
meta_sectors = meta_bytes / 512;
meta_extents = meta_sectors / vg->extent_size;
prev_meta_sectors = lv_imeta->size;
prev_meta_extents = prev_meta_sectors / vg->extent_size;
if (meta_extents <= prev_meta_extents) {
log_debug("extend not needed for imeta LV %s", lv_imeta->name);
continue;
}
/*
* We only allow lv_imeta to exist on a single PV (for now),
* so the allocatable_pvs is the one PV currently used by
* lv_imeta.
*/
dm_list_init(&allocatable_pvs);
if (!get_pv_list_for_lv(cmd->mem, lv_imeta, &allocatable_pvs)) {
log_error("Failed to build list of PVs for extending %s.", display_lvname(lv_imeta));
return 0;
}
use_pvh = &allocatable_pvs;
if (!lv_extend(lv_imeta, segtype, 1, 0, 0, 0,
meta_extents - prev_meta_extents,
use_pvh, lv_imeta->alloc, 0)) {
log_error("Failed to extend integrity metadata LV %s", lv_imeta->name);
return 0;
}
}
return 1;
}
int lv_remove_integrity_from_raid(struct logical_volume *lv)
{
struct logical_volume *iorig_lvs[DEFAULT_RAID_MAX_IMAGES];
struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
struct lv_segment *seg_top, *seg_image;
struct logical_volume *lv_image;
struct logical_volume *lv_iorig;
struct logical_volume *lv_imeta;
uint32_t area_count, s;
int is_active = lv_is_active(lv);
seg_top = first_seg(lv);
if (!seg_is_raid1(seg_top) && !seg_is_raid4(seg_top) &&
!seg_is_any_raid5(seg_top) && !seg_is_any_raid6(seg_top) &&
!seg_is_any_raid10(seg_top)) {
log_error("LV %s segment is unsupported raid for integrity.", display_lvname(lv));
return 0;
}
area_count = seg_top->area_count;
for (s = 0; s < area_count; s++) {
lv_image = seg_lv(seg_top, s);
seg_image = first_seg(lv_image);
if (!(lv_imeta = seg_image->integrity_meta_dev)) {
log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
return 0;
}
if (!(lv_iorig = seg_lv(seg_image, 0))) {
log_error("LV %s integrity segment has no origin", display_lvname(lv));
return 0;
}
if (!remove_seg_from_segs_using_this_lv(seg_image->integrity_meta_dev, seg_image))
return_0;
iorig_lvs[s] = lv_iorig;
imeta_lvs[s] = lv_imeta;
lv_image->status &= ~INTEGRITY;
seg_image->integrity_meta_dev = NULL;
seg_image->integrity_data_sectors = 0;
memset(&seg_image->integrity_settings, 0, sizeof(seg_image->integrity_settings));
if (!remove_layer_from_lv(lv_image, lv_iorig))
return_0;
}
if (is_active) {
/* vg_write(), suspend_lv(), vg_commit(), resume_lv() */
if (!lv_update_and_reload(lv)) {
log_error("Failed to update and reload LV after integrity remove.");
return 0;
}
}
for (s = 0; s < area_count; s++) {
lv_iorig = iorig_lvs[s];
lv_imeta = imeta_lvs[s];
if (is_active) {
if (!deactivate_lv(cmd, lv_iorig))
log_error("Failed to deactivate unused iorig LV %s.", lv_iorig->name);
if (!deactivate_lv(cmd, lv_imeta))
log_error("Failed to deactivate unused imeta LV %s.", lv_imeta->name);
}
lv_imeta->status &= ~INTEGRITY_METADATA;
lv_set_visible(lv_imeta);
if (!lv_remove(lv_iorig))
log_error("Failed to remove unused iorig LV %s.", lv_iorig->name);
if (!lv_remove(lv_imeta))
log_error("Failed to remove unused imeta LV %s.", lv_imeta->name);
}
if (!vg_write(vg) || !vg_commit(vg))
return_0;
return 1;
}
static int _set_integrity_block_size(struct cmd_context *cmd, struct logical_volume *lv,
struct integrity_settings *settings,
int lbs_4k, int lbs_512, int pbs_4k, int pbs_512)
{
char pathname[PATH_MAX];
struct device *fs_dev;
uint32_t fs_block_size = 0;
int rv;
if (lbs_4k && lbs_512) {
log_error("Integrity requires consistent logical block size for LV devices.");
goto_bad;
}
if (settings->block_size &&
(settings->block_size != 512 && settings->block_size != 1024 &&
settings->block_size != 2048 && settings->block_size != 4096)) {
log_error("Invalid integrity block size, possible values are 512, 1024, 2048, 4096");
goto_bad;
}
if (lbs_4k && settings->block_size && (settings->block_size < 4096)) {
log_error("Integrity block size %u not allowed with device logical block size 4096.",
settings->block_size);
goto_bad;
}