1
0
mirror of git://sourceware.org/git/lvm2.git synced 2026-02-10 08:32:46 +03:00

Compare commits

..

1 Commits

Author SHA1 Message Date
David Teigland
1b3f34b39a dm-integrity support
Create a linear LV with a dm-integrity layer added
above it.  The dm-integrity layer stores checksums
of the data written to the LV, and returns an error
if data read from the LV does not match the previously
saved checksum.

lvcreate --type integrity --integrity String [options]

The --integrity String specifies if the dm-integrity
metadata (checksums) should be interleaved with data
blocks, or written to a separate external LV:

  --integrity external (default)
  Store integrity metadata on a separate LV.
  Allows removing integrity from the LV later.

  --integrity internal
  Store integrity metadata interleaved with data
  on the same LV.  Around 1% of the LV size will
  be used for integrity metadata.

  --integrity y
  Enable default integrity settings (external).

Command variations:

lvcreate --type integrity -n Name -L Size VG
  [Uses integrity external, the default.]

lvcreate --integrity external -n Name -L Size VG
  [Uses type integrity, which is implied.]

lvcreate --integrity y -n Name -L Size VG
  [Uses integrity external, the default, and
   uses type integrity, which is implied.]

lvcreate --integrity internal -n Name -L Size VG
  [Uses type integrity, which is implied.]

lvconvert --integrity none|n LV
  [Removes external integrity.]

Options:

  --integritymetadata LV
  Use the specified LV for external metadata.
  Allows specific device placement of metadata.
  Without this option, the command creates a
  hidden LV (with an _imeta suffix) to hold the
  metadata.

  --integritysettings String
  set dm-integrity parameters, e.g. to use a journal
  instead of bitmap, --integritysettings "mode=J".

Example:

$ lvcreate --integrity external -n lvex -L1G vg
$ lvs -a vg
  LV           VG Attr       LSize  Origin
  lvex         vg -wi-a-----  1.00g [lvex_iorig]
  [lvex_imeta] vg -wi-ao---- 12.00m
  [lvex_iorig] vg -wi-ao----  1.00g

$ lvcreate --integrity internal -n lvin -L1G vg
$ lvs -a vg
  LV           VG Attr       LSize Origin
  lvin         vg -wi-a----- 1.00g [lvin_iorig]
  [lvin_iorig] vg -wi-ao---- 1.00g

Zeroing:

After a new integrity LV is created, zeroes are written to
the entire LV to initialize integrity metadata (checksums).
Without this initialization, the LV will return read errors
for any unwritten (and uninitialized) data.

A large LV may take a long time to zero.  The -Zn option can
be used to disable the whole-LV zeroing, or the lvcreate
command can be canceled while zeroing the new LV.  In either
case, the user may write to the entire LV to initialize the
integrity metadata themselves.
2019-12-04 12:27:25 -06:00
11 changed files with 230 additions and 21 deletions

View File

@@ -105,6 +105,7 @@ static const struct flag _lv_flags[] = {
{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},
{LV_UNCOMMITTED, NULL, 0},

View File

@@ -156,6 +156,9 @@ static int _integrity_text_import(struct lv_segment *seg,
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;

View File

@@ -29,6 +29,19 @@
#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.)
@@ -147,6 +160,49 @@ fail:
return 0;
}
int lv_remove_integrity(struct logical_volume *lv)
{
struct lv_segment *seg = first_seg(lv);
struct logical_volume *origin;
struct logical_volume *meta_lv;
if (!seg_is_integrity(seg)) {
log_error("LV %s segment is not integrity.", display_lvname(lv));
return 0;
}
if (!(meta_lv = seg->integrity_meta_dev)) {
log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
return 0;
}
if (!(origin = seg_lv(seg, 0))) {
log_error("LV %s integrity segment has no origin", display_lvname(lv));
return 0;
}
if (!remove_seg_from_segs_using_this_lv(seg->integrity_meta_dev, seg))
return_0;
lv_set_visible(seg->integrity_meta_dev);
lv->status &= ~INTEGRITY;
meta_lv->status &= ~INTEGRITY_METADATA;
seg->integrity_meta_dev = NULL;
if (!remove_layer_from_lv(lv, origin))
return_0;
if (!lv_remove(origin))
return_0;
if (!lv_remove(meta_lv))
log_warn("WARNING: failed to remove integrity metadata LV.");
return 1;
}
int lv_add_integrity(struct logical_volume *lv, const char *arg,
struct logical_volume *meta_lv_created,
const char *meta_name,
@@ -274,6 +330,7 @@ int lv_add_integrity(struct logical_volume *lv, const char *arg,
return 0;
}
meta_lv->status |= INTEGRITY_METADATA;
seg->integrity_data_sectors = lv_size_sectors;
seg->integrity_meta_dev = meta_lv;
lv_set_hidden(meta_lv);

View File

@@ -1202,10 +1202,13 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
else if (lv_is_pool_metadata(lv) ||
lv_is_pool_metadata_spare(lv) ||
lv_is_raid_metadata(lv))
lv_is_raid_metadata(lv) ||
lv_is_integrity_metadata(lv))
repstr[0] = 'e';
else if (lv_is_cache_type(lv) || lv_is_writecache(lv))
repstr[0] = 'C';
else if (lv_is_integrity(lv))
repstr[0] = 'g';
else if (lv_is_raid(lv))
repstr[0] = (lv_is_not_synced(lv)) ? 'R' : 'r';
else if (lv_is_mirror(lv))

View File

@@ -137,7 +137,9 @@ enum {
LV_TYPE_SANLOCK,
LV_TYPE_CACHEVOL,
LV_TYPE_WRITECACHE,
LV_TYPE_WRITECACHEORIGIN
LV_TYPE_WRITECACHEORIGIN,
LV_TYPE_INTEGRITY,
LV_TYPE_INTEGRITYORIGIN
};
static const char *_lv_type_names[] = {
@@ -193,6 +195,8 @@ static const char *_lv_type_names[] = {
[LV_TYPE_CACHEVOL] = "cachevol",
[LV_TYPE_WRITECACHE] = "writecache",
[LV_TYPE_WRITECACHEORIGIN] = "writecacheorigin",
[LV_TYPE_INTEGRITY] = "integrity",
[LV_TYPE_INTEGRITYORIGIN] = "integrityorigin",
};
static int _lv_layout_and_role_mirror(struct dm_pool *mem,
@@ -464,6 +468,43 @@ bad:
return 0;
}
static int _lv_layout_and_role_integrity(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
struct dm_list *role,
int *public_lv)
{
int top_level = 0;
/* non-top-level LVs */
if (lv_is_integrity_metadata(lv)) {
if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_METADATA]))
goto_bad;
} else if (lv_is_integrity_origin(lv)) {
if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_ORIGIN]) ||
!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITYORIGIN]))
goto_bad;
} else
top_level = 1;
if (!top_level) {
*public_lv = 0;
return 1;
}
/* top-level LVs */
if (lv_is_integrity(lv)) {
if (!str_list_add_no_dup_check(mem, layout, _lv_type_names[LV_TYPE_INTEGRITY]))
goto_bad;
}
return 1;
bad:
return 0;
}
static int _lv_layout_and_role_thick_origin_snapshot(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
@@ -580,6 +621,11 @@ int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv,
!_lv_layout_and_role_cache(mem, lv, *layout, *role, &public_lv))
goto_bad;
/* Integrity related */
if ((lv_is_integrity(lv) || lv_is_integrity_origin(lv) || lv_is_integrity_metadata(lv)) &&
!_lv_layout_and_role_integrity(mem, lv, *layout, *role, &public_lv))
goto_bad;
/* VDO and related */
if (lv_is_vdo_type(lv) &&
!_lv_layout_and_role_vdo(mem, lv, *layout, *role, &public_lv))
@@ -8440,12 +8486,23 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
backup(vg);
/*
* Activate for zeroing
* TODO: activate in zero_lv_name() if zeroing is enabled.
* The standard option combination should be -Zy -ay, in which
* case we activate here, and zero at the end of the command.
* The invalid combination -Zy -an has been prevented earlier.
* The combination -Zn -an involves no zeroing or activation.
* For combination -Zn -ay we activate here.
*/
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate LV to zero integrity.");
goto out;
if (lp->zero) {
/* Activate for zeroing at the end of lvcreate. */
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate LV %s.", display_lvname(lv));
goto out;
}
} else if (!lp->zero && is_change_activating(lp->activate)) {
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate LV %s.", display_lvname(lv));
goto out;
}
}
/*
@@ -8455,7 +8512,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
* problems (if the user doesn't want to wait, or wants
* to do the zeroing themselves.)
*/
lp->post_zero_bytes = first_seg(lv)->integrity_data_sectors * 512;
lp->integrity_bytes_to_zero = first_seg(lv)->integrity_data_sectors * 512;
goto out;
}

View File

@@ -91,6 +91,7 @@
//#define POSTORDER_FLAG UINT64_C(0x0000000002000000) /* Not real flags, reserved for
//#define POSTORDER_OPEN_FLAG UINT64_C(0x0000000004000000) temporary use inside vg_read_internal. */
#define INTEGRITY_METADATA UINT64_C(0x0000000004000000) /* LV - Internal use only */
#define LV_UNCOMMITTED UINT64_C(0x0000000002000000)
#define VIRTUAL_ORIGIN UINT64_C(0x0000000008000000) /* LV - internal use only */
@@ -263,7 +264,8 @@
#define lv_is_pool_metadata_spare(lv) (((lv)->status & POOL_METADATA_SPARE) ? 1 : 0)
#define lv_is_lockd_sanlock_lv(lv) (((lv)->status & LOCKD_SANLOCK_LV) ? 1 : 0)
#define lv_is_writecache(lv) (((lv)->status & WRITECACHE) ? 1 : 0)
#define lv_is_integrity(lv) (((lv)->status & INTEGRITY) ? 1 : 0)
#define lv_is_integrity(lv) (((lv)->status & INTEGRITY) ? 1 : 0)
#define lv_is_integrity_metadata(lv) (((lv)->status & INTEGRITY_METADATA) ? 1 : 0)
#define lv_is_vdo(lv) (((lv)->status & LV_VDO) ? 1 : 0)
#define lv_is_vdo_pool(lv) (((lv)->status & LV_VDO_POOL) ? 1 : 0)
@@ -275,9 +277,11 @@
/* Recognize component LV (matching lib/misc/lvm-string.c _lvname_has_reserved_component_string()) */
#define lv_is_component(lv) (lv_is_cache_origin(lv) || \
lv_is_writecache_origin(lv) || \
lv_is_integrity_origin(lv) || \
((lv)->status & (\
CACHE_POOL_DATA |\
CACHE_POOL_METADATA |\
INTEGRITY_METADATA |\
LV_CACHE_VOL |\
LV_VDO_POOL_DATA |\
MIRROR_IMAGE |\
@@ -998,12 +1002,12 @@ struct lvcreate_params {
int approx_alloc; /* all */
alloc_policy_t alloc; /* all */
struct dm_vdo_target_params vdo_params; /* vdo */
uint64_t post_zero_bytes; /* write zeros to LV after it's created */
const char *integrity_arg;
const char *integrity_meta_name; /* external LV is user-specified */
struct logical_volume *integrity_meta_lv; /* external LV we create */
struct integrity_settings integrity_settings;
uint64_t integrity_bytes_to_zero; /* zeros the final LV after it's created */
struct dm_list tags; /* all */
@@ -1099,6 +1103,8 @@ int lv_is_cache_origin(const struct logical_volume *lv);
int lv_is_writecache_origin(const struct logical_volume *lv);
int lv_is_writecache_cachevol(const struct logical_volume *lv);
int lv_is_integrity_origin(const struct logical_volume *lv);
int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
int cow_has_min_chunks(const struct volume_group *vg, uint32_t cow_extents, uint32_t chunk_size);
@@ -1404,6 +1410,7 @@ int lv_add_integrity(struct logical_volume *lv, const char *arg,
int lv_create_integrity_metadata(struct cmd_context *cmd,
struct volume_group *vg,
struct lvcreate_params *lp);
int lv_remove_integrity(struct logical_volume *lv);
int zero_lv_name(struct cmd_context *cmd, const char *vg_name, const char *lv_name, uint64_t zero_bytes);

View File

@@ -757,6 +757,14 @@ FLAGS: SECONDARY_SYNTAX
---
lvconvert --integrity String LV
OO: OO_LVCONVERT
OP: PV ...
ID: lvconvert_integrity
DESC: Remove integrity from an LV.
---
# --extents is not specified; it's an automatic alternative for --size
OO_LVCREATE: --addtag Tag, --alloc Alloc, --autobackup Bool, --activate Active,

View File

@@ -5730,6 +5730,75 @@ int lvconvert_to_cache_with_cachevol_cmd(struct cmd_context *cmd, int argc, char
return ret;
}
static int _lvconvert_integrity_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle)
{
struct volume_group *vg = lv->vg;
struct lv_segment *seg;
const char *arg = arg_str_value(cmd, integrity_ARG, NULL);
if (strcmp(arg, "none") && strcmp(arg, "n")) {
log_error("Integrity can only be removed from an existing LV (see --integrity none).");
return ECMD_FAILED;
}
if (!lv_is_integrity(lv)) {
log_error("LV does not have integrity.");
return ECMD_FAILED;
}
seg = first_seg(lv);
if (!seg->integrity_meta_dev) {
log_error("Internal integrity cannot be removed.");
return ECMD_FAILED;
}
/* TODO: lift this restriction */
if (lv_info(cmd, lv, 1, NULL, 0, 0)) {
log_error("LV must be inactive to remove integrity.");
return ECMD_FAILED;
}
if (!archive(vg))
return ECMD_FAILED;
if (!lv_remove_integrity(lv))
return ECMD_FAILED;
if (!vg_write(vg) || !vg_commit(vg))
return ECMD_FAILED;
backup(vg);
log_print_unless_silent("Logical volume %s has removed integrity.", display_lvname(lv));
return ECMD_PROCESSED;
}
int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
struct lvconvert_result lr = { 0 };
int ret;
if (!(handle = init_processing_handle(cmd, NULL))) {
log_error("Failed to initialize processing handle.");
return ECMD_FAILED;
}
handle->custom_handle = &lr;
cmd->cname->flags &= ~GET_VGNAME_FROM_OPTIONS;
ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle, NULL,
&_lvconvert_integrity_single);
destroy_processing_handle(cmd, handle);
return ret;
}
/*
* All lvconvert command defs have their own function,
* so the generic function name is unused.

View File

@@ -1582,6 +1582,13 @@ static int _check_zero_parameters(struct cmd_context *cmd, struct lvcreate_param
if (seg_is_thin(lp))
return 1;
if (seg_is_integrity(lp)) {
if (lp->zero && !is_change_activating(lp->activate)) {
log_error("Zeroing integrity is not compatible with inactive creation (-an).");
return 0;
}
}
/* If there is some problem, buffer will not be empty */
if (dm_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
lp->origin_name ? "origin " : "",
@@ -1801,19 +1808,11 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
_destroy_lvcreate_params(&lp);
destroy_processing_handle(cmd, handle);
/*
* FIXME: make this interruptible. If the user cancels the zeroing,
* they can either use the LV without it being zeroed, or can finish
* zeroing it themselves, e.g. with dd.
*
* FIXME: if the user asked for the lv to be created in inactive
* (-an) then deactivate the LV after zeroing.
*/
if (lp.post_zero_bytes) {
if (lp.integrity_bytes_to_zero) {
if (!lp.zero)
log_warn("WARNING: not zeroing LV, integrity read errors may occur.");
log_warn("WARNING: not zeroing integrity LV, read errors are possible.");
else
zero_lv_name(cmd, lp.vg_name, lp.lv_name, lp.post_zero_bytes);
zero_lv_name(cmd, lp.vg_name, lp.lv_name, lp.integrity_bytes_to_zero);
}
return ret;

View File

@@ -149,6 +149,9 @@ static const struct command_function _command_functions[CMD_COUNT] = {
{ lvconvert_to_vdopool_CMD, lvconvert_to_vdopool_cmd },
{ lvconvert_to_vdopool_param_CMD, lvconvert_to_vdopool_param_cmd },
/* lvconvert for integrity */
{ lvconvert_integrity_CMD, lvconvert_integrity_cmd },
{ pvscan_display_CMD, pvscan_display_cmd },
{ pvscan_cache_CMD, pvscan_cache_cmd },
};

View File

@@ -275,6 +275,8 @@ int lvconvert_merge_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_param_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_display_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv);