mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
426c3c7b88
Use 64bit math to multiply extent_size.
1017 lines
27 KiB
C
1017 lines
27 KiB
C
/*
|
|
* 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"
|
|
|
|
#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
|
|
#define ONE_GB_IN_BYTES 1073741824
|
|
|
|
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.)
|
|
*
|
|
* plus some initial space for journals.
|
|
* (again from trial and error testing.)
|
|
*/
|
|
static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes, uint32_t journal_sectors,
|
|
uint32_t extent_size)
|
|
{
|
|
uint64_t meta_bytes;
|
|
uint64_t initial_bytes;
|
|
|
|
/* Every 500M of data needs 4M of metadata. */
|
|
meta_bytes = ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);
|
|
|
|
if (journal_sectors) {
|
|
/* for calculating the metadata LV size for the specified
|
|
journal size, round the specified journal size up to the
|
|
nearest extent. extent_size is in sectors. */
|
|
initial_bytes = dm_round_up(journal_sectors, (int64_t)extent_size) * 512;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* initial space used for journals (when journal size is not specified):
|
|
* lv_size <= 512M -> 4M
|
|
* lv_size <= 1G -> 8M
|
|
* lv_size <= 4G -> 32M
|
|
* lv_size > 4G -> 64M
|
|
*/
|
|
if (lv_size_bytes <= (512 * ONE_MB_IN_BYTES))
|
|
initial_bytes = 4 * ONE_MB_IN_BYTES;
|
|
else if (lv_size_bytes <= ONE_GB_IN_BYTES)
|
|
initial_bytes = 8 * ONE_MB_IN_BYTES;
|
|
else if (lv_size_bytes <= (4ULL * ONE_GB_IN_BYTES))
|
|
initial_bytes = 32 * ONE_MB_IN_BYTES;
|
|
else if (lv_size_bytes > (4ULL * ONE_GB_IN_BYTES))
|
|
initial_bytes = 64 * ONE_MB_IN_BYTES;
|
|
out:
|
|
log_debug("integrity_meta_bytes %llu from lv_size_bytes %llu meta_bytes %llu initial_bytes %llu journal_sectors %u",
|
|
(unsigned long long)(meta_bytes+initial_bytes), (unsigned long long)lv_size_bytes,
|
|
(unsigned long long)meta_bytes, (unsigned long long)initial_bytes, journal_sectors);
|
|
return meta_bytes + initial_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 integrity_settings *settings,
|
|
struct logical_volume **meta_lv)
|
|
{
|
|
char metaname[NAME_LEN] = { 0 };
|
|
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,
|
|
.temporary = 1,
|
|
.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, settings->journal_sectors, vg->extent_size);
|
|
meta_sectors = meta_bytes / 512;
|
|
lp_meta.extents = meta_sectors / vg->extent_size;
|
|
|
|
log_verbose("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;
|
|
|
|
if (!lv_is_raid(lv))
|
|
return_0;
|
|
|
|
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, 0, 0);
|
|
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;
|
|
}
|
|
|
|
int integrity_mode_set(const char *mode, struct integrity_settings *settings)
|
|
{
|
|
if (!mode)
|
|
settings->mode[0] = DEFAULT_MODE;
|
|
else if (!strcmp(mode, "bitmap") || !strcmp(mode, "B"))
|
|
settings->mode[0] = 'B';
|
|
else if (!strcmp(mode, "journal") || !strcmp(mode, "J"))
|
|
settings->mode[0] = 'J';
|
|
else {
|
|
log_error("Invalid raid integrity mode (use \"bitmap\" or \"journal\")");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int _set_integrity_block_size(struct cmd_context *cmd, struct logical_volume *lv, int is_active,
|
|
struct integrity_settings *settings,
|
|
int lbs_4k, int lbs_512, int pbs_4k, int pbs_512)
|
|
{
|
|
char pathname[PATH_MAX];
|
|
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;
|
|
}
|
|
|
|
if (!strcmp(cmd->name, "lvcreate")) {
|
|
if (lbs_4k) {
|
|
settings->block_size = 4096;
|
|
} else if (lbs_512 && pbs_4k && !pbs_512) {
|
|
settings->block_size = 4096;
|
|
} else if (lbs_512) {
|
|
if (!settings->block_size)
|
|
settings->block_size = 512;
|
|
} else if (!lbs_4k && !lbs_512) {
|
|
if (!settings->block_size)
|
|
settings->block_size = 512;
|
|
log_print_unless_silent("Using integrity block size %u with unknown device logical block size.",
|
|
settings->block_size);
|
|
} else {
|
|
goto_bad;
|
|
}
|
|
|
|
} else if (!strcmp(cmd->name, "lvconvert")) {
|
|
if (dm_snprintf(pathname, sizeof(pathname), "%s%s/%s", cmd->dev_dir,
|
|
lv->vg->name, lv->name) < 0) {
|
|
log_error("Path name too long to get LV block size %s", display_lvname(lv));
|
|
goto bad;
|
|
}
|
|
|
|
/*
|
|
* fs_block_size_and_type() returns the libblkid BLOCK_SIZE value,
|
|
* where libblkid has fs-specific code to set BLOCK_SIZE to the
|
|
* value we need here.
|
|
*
|
|
* The term "block size" here may not equate directly to what the fs
|
|
* calls the block size, e.g. xfs calls this the sector size (and
|
|
* something different the block size); while ext4 does call this
|
|
* value the block size, but it's possible values are not the same
|
|
* as xfs's, and do not seem to relate directly to the device LBS.
|
|
*/
|
|
rv = fs_block_size_and_type(pathname, &fs_block_size, NULL, NULL);
|
|
if (!rv || !fs_block_size) {
|
|
unsigned use_bs;
|
|
|
|
if (lbs_4k && pbs_4k) {
|
|
use_bs = 4096;
|
|
} else if (lbs_512 && pbs_512) {
|
|
use_bs = 512;
|
|
} else if (lbs_512 && pbs_4k) {
|
|
if (settings->block_size == 4096)
|
|
use_bs = 4096;
|
|
else
|
|
use_bs = 512;
|
|
} else {
|
|
use_bs = 512;
|
|
}
|
|
|
|
if (settings->block_size && (settings->block_size != use_bs)) {
|
|
log_error("Cannot use integrity block size %u with unknown file system block size, logical block size %u, physical block size %u.",
|
|
settings->block_size, lbs_4k ? 4096 : 512, pbs_4k ? 4096 : 512);
|
|
goto bad;
|
|
}
|
|
|
|
settings->block_size = use_bs;
|
|
|
|
log_print_unless_silent("Using integrity block size %u for unknown file system block size, logical block size %u, physical block size %u.",
|
|
settings->block_size, lbs_4k ? 4096 : 512, pbs_4k ? 4096 : 512);
|
|
goto out;
|
|
}
|
|
|
|
if (!settings->block_size) {
|
|
if (is_active && lbs_512) {
|
|
/* increasing the lbs from 512 to 4k under an active LV could cause problems
|
|
for an application that expects a given io size/alignment is possible. */
|
|
settings->block_size = 512;
|
|
if (fs_block_size > 512)
|
|
log_print_unless_silent("Limiting integrity block size to 512 because the LV is active.");
|
|
} else if (fs_block_size <= 4096)
|
|
settings->block_size = fs_block_size;
|
|
else
|
|
settings->block_size = 4096; /* dm-integrity max is 4096 */
|
|
log_print_unless_silent("Using integrity block size %u for file system block size %u.",
|
|
settings->block_size, fs_block_size);
|
|
} else {
|
|
/* let user specify integrity block size that is less than fs block size */
|
|
if (settings->block_size > fs_block_size) {
|
|
log_error("Integrity block size %u cannot be larger than file system block size %u.",
|
|
settings->block_size, fs_block_size);
|
|
goto bad;
|
|
}
|
|
log_print_unless_silent("Using integrity block size %u for file system block size %u.",
|
|
settings->block_size, fs_block_size);
|
|
}
|
|
}
|
|
out:
|
|
return 1;
|
|
bad:
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Add integrity to each raid image.
|
|
*
|
|
* for each rimage_N:
|
|
* . create and allocate a new linear LV rimage_N_imeta
|
|
* . move the segments from rimage_N to a new rimage_N_iorig
|
|
* . add an integrity segment to rimage_N with
|
|
* origin=rimage_N_iorig, meta_dev=rimage_N_imeta
|
|
*
|
|
* Before:
|
|
* rimage_0
|
|
* segment1: striped: pv0:A
|
|
* rimage_1
|
|
* segment1: striped: pv1:B
|
|
*
|
|
* After:
|
|
* rimage_0
|
|
* segment1: integrity: rimage_0_iorig, rimage_0_imeta
|
|
* rimage_1
|
|
* segment1: integrity: rimage_1_iorig, rimage_1_imeta
|
|
* rimage_0_iorig
|
|
* segment1: striped: pv0:A
|
|
* rimage_1_iorig
|
|
* segment1: striped: pv1:B
|
|
* rimage_0_imeta
|
|
* segment1: striped: pv2:A
|
|
* rimage_1_imeta
|
|
* segment1: striped: pv2:B
|
|
*
|
|
*/
|
|
|
|
int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_settings *settings,
|
|
struct dm_list *pvh, struct logical_volume *lv_imeta_0)
|
|
{
|
|
char imeta_name[NAME_LEN];
|
|
char *imeta_name_dup;
|
|
struct lvcreate_params lp;
|
|
struct dm_list allocatable_pvs;
|
|
struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
|
|
struct cmd_context *cmd = lv->vg->cmd;
|
|
struct volume_group *vg = lv->vg;
|
|
struct logical_volume *lv_image, *lv_imeta;
|
|
struct lv_segment *seg_top, *seg_image;
|
|
struct pv_list *pvl;
|
|
const struct segment_type *segtype;
|
|
struct integrity_settings *set = NULL;
|
|
struct dm_list *use_pvh = NULL;
|
|
uint32_t area_count, s;
|
|
uint32_t revert_meta_lvs = 0;
|
|
int lbs_4k = 0, lbs_512 = 0, lbs_unknown = 0;
|
|
int pbs_4k = 0, pbs_512 = 0, pbs_unknown = 0;
|
|
int is_active;
|
|
|
|
memset(imeta_lvs, 0, sizeof(imeta_lvs));
|
|
|
|
is_active = lv_is_active(lv);
|
|
|
|
if (dm_list_size(&lv->segments) != 1)
|
|
return_0;
|
|
|
|
if (!dm_list_empty(&lv->segs_using_this_lv)) {
|
|
log_error("Integrity can only be added to top level raid LV.");
|
|
return 0;
|
|
}
|
|
|
|
seg_top = first_seg(lv);
|
|
area_count = seg_top->area_count;
|
|
|
|
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("Integrity can only be added to raid1,4,5,6,10.");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For each rimage, create an _imeta LV for integrity metadata.
|
|
* Each needs to be zeroed.
|
|
*/
|
|
for (s = 0; s < area_count; s++) {
|
|
struct logical_volume *meta_lv;
|
|
struct wipe_params wipe = { .do_zero = 1 };
|
|
|
|
if (s >= DEFAULT_RAID_MAX_IMAGES)
|
|
goto_bad;
|
|
|
|
lv_image = seg_lv(seg_top, s);
|
|
|
|
/*
|
|
* This function is used to add integrity to new images added
|
|
* to the raid, in which case old images will already be
|
|
* integrity.
|
|
*/
|
|
if (seg_is_integrity(first_seg(lv_image)))
|
|
continue;
|
|
|
|
if (!seg_is_striped(first_seg(lv_image))) {
|
|
log_error("raid image must be linear to add integrity");
|
|
goto bad;
|
|
}
|
|
|
|
/*
|
|
* Use an existing lv_imeta from previous linear+integrity LV.
|
|
* FIXME: is it guaranteed that lv_image_0 is the existing?
|
|
*/
|
|
if (!s && lv_imeta_0) {
|
|
if (dm_snprintf(imeta_name, sizeof(imeta_name), "%s_imeta", lv_image->name) > 0) {
|
|
if ((imeta_name_dup = dm_pool_strdup(vg->vgmem, imeta_name)))
|
|
lv_imeta_0->name = imeta_name_dup;
|
|
}
|
|
imeta_lvs[0] = lv_imeta_0;
|
|
continue;
|
|
}
|
|
|
|
dm_list_init(&allocatable_pvs);
|
|
|
|
if (!get_pv_list_for_lv(cmd->mem, lv_image, &allocatable_pvs)) {
|
|
log_error("Failed to build list of PVs for %s.", display_lvname(lv_image));
|
|
goto bad;
|
|
}
|
|
|
|
dm_list_iterate_items(pvl, &allocatable_pvs) {
|
|
unsigned int pbs = 0;
|
|
unsigned int lbs = 0;
|
|
|
|
if (!dev_get_direct_block_sizes(pvl->pv->dev, &pbs, &lbs)) {
|
|
lbs_unknown++;
|
|
pbs_unknown++;
|
|
continue;
|
|
}
|
|
if (lbs == 4096)
|
|
lbs_4k++;
|
|
else if (lbs == 512)
|
|
lbs_512++;
|
|
else
|
|
lbs_unknown++;
|
|
if (pbs == 4096)
|
|
pbs_4k++;
|
|
else if (pbs == 512)
|
|
pbs_512++;
|
|
else
|
|
pbs_unknown++;
|
|
}
|
|
|
|
use_pvh = &allocatable_pvs;
|
|
|
|
/*
|
|
* allocate a new linear LV NAME_rimage_N_imeta
|
|
*/
|
|
memset(&lp, 0, sizeof(lp));
|
|
lp.lv_name = lv_image->name;
|
|
lp.pvh = use_pvh;
|
|
lp.extents = lv_image->size / vg->extent_size;
|
|
|
|
if (!_lv_create_integrity_metadata(cmd, vg, &lp, settings, &meta_lv))
|
|
goto_bad;
|
|
|
|
revert_meta_lvs++;
|
|
|
|
/* Used below to set up the new integrity segment. */
|
|
imeta_lvs[s] = meta_lv;
|
|
|
|
/*
|
|
* dm-integrity requires the metadata LV header to be zeroed.
|
|
*/
|
|
|
|
if (!activate_lv(cmd, meta_lv)) {
|
|
log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
|
|
goto bad;
|
|
}
|
|
|
|
if (!wipe_lv(meta_lv, wipe)) {
|
|
log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
|
|
if (deactivate_lv(cmd, meta_lv))
|
|
log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
|
|
goto bad;
|
|
}
|
|
|
|
if (!deactivate_lv(cmd, meta_lv)) {
|
|
log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (!is_active) {
|
|
/* checking block size of fs on the lv requires the lv to be active */
|
|
if (!activate_lv(cmd, lv)) {
|
|
log_error("Failed to activate LV to check block size %s", display_lvname(lv));
|
|
goto bad;
|
|
}
|
|
if (!sync_local_dev_names(cmd))
|
|
stack;
|
|
}
|
|
|
|
/*
|
|
* Set settings->block_size which will be copied to segment settings below.
|
|
* integrity block size chosen based on device logical block size and
|
|
* file system block size.
|
|
*/
|
|
if (!_set_integrity_block_size(cmd, lv, is_active, settings, lbs_4k, lbs_512, pbs_4k, pbs_512)) {
|
|
if (!is_active && !deactivate_lv(cmd, lv))
|
|
stack;
|
|
goto_bad;
|
|
}
|
|
|
|
if (!is_active) {
|
|
if (!deactivate_lv(cmd, lv)) {
|
|
log_error("Failed to deactivate LV after checking block size %s", display_lvname(lv));
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For each rimage, move its segments to a new rimage_iorig and give
|
|
* the rimage a new integrity segment.
|
|
*/
|
|
for (s = 0; s < area_count; s++) {
|
|
lv_image = seg_lv(seg_top, s);
|
|
|
|
/* Not adding integrity to this image. */
|
|
if (!imeta_lvs[s])
|
|
continue;
|
|
|
|
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
|
|
goto_bad;
|
|
|
|
log_debug("Adding integrity to raid image %s", lv_image->name);
|
|
|
|
/*
|
|
* lv_iorig returned by insert_layer_for_lv() (but unused here)
|
|
* "lv_iorig" is a new LV with new id, but with the segments
|
|
* from "lv_image". "lv_image" keeps the existing name and id,
|
|
* but gets a new integrity segment, in place of the segments
|
|
* that were moved to lv_iorig.
|
|
*/
|
|
/* coverity[format_string_injection] lv name is already validated */
|
|
if (!insert_layer_for_lv(cmd, lv_image, 0, "_iorig"))
|
|
goto_bad;
|
|
|
|
lv_image->status |= INTEGRITY;
|
|
|
|
/*
|
|
* Set up the new first segment of lv_image as integrity.
|
|
*/
|
|
seg_image = first_seg(lv_image);
|
|
seg_image->segtype = segtype;
|
|
|
|
lv_imeta = imeta_lvs[s];
|
|
lv_imeta->status |= INTEGRITY_METADATA;
|
|
lv_set_hidden(lv_imeta);
|
|
seg_image->integrity_data_sectors = lv_image->size;
|
|
seg_image->integrity_meta_dev = lv_imeta;
|
|
seg_image->integrity_recalculate = 1;
|
|
|
|
memcpy(&seg_image->integrity_settings, settings, sizeof(struct integrity_settings));
|
|
set = &seg_image->integrity_settings;
|
|
|
|
if (!set->mode[0])
|
|
set->mode[0] = DEFAULT_MODE;
|
|
|
|
if (!set->tag_size)
|
|
set->tag_size = DEFAULT_TAG_SIZE;
|
|
|
|
if (!set->block_size)
|
|
set->block_size = DEFAULT_BLOCK_SIZE;
|
|
|
|
if (!set->internal_hash)
|
|
set->internal_hash = DEFAULT_INTERNAL_HASH;
|
|
}
|
|
|
|
if (is_active) {
|
|
log_debug("Writing VG and updating LV with new integrity LV %s", lv->name);
|
|
|
|
/* vg_write(), suspend_lv(), vg_commit(), resume_lv() */
|
|
if (!lv_update_and_reload(lv)) {
|
|
log_error("LV update and reload failed");
|
|
goto bad;
|
|
}
|
|
revert_meta_lvs = 0;
|
|
|
|
} else {
|
|
log_debug("Writing VG with new integrity LV %s", lv->name);
|
|
|
|
if (!vg_write(vg) || !vg_commit(vg))
|
|
goto_bad;
|
|
|
|
revert_meta_lvs = 0;
|
|
|
|
/*
|
|
* This first activation includes "recalculate" which starts the
|
|
* kernel's recalculating (initialization) process.
|
|
*/
|
|
|
|
log_debug("Activating to start integrity initialization for LV %s", lv->name);
|
|
|
|
if (!activate_lv(cmd, lv)) {
|
|
log_error("Failed to activate integrity LV to initialize.");
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that the device is being initialized, update the VG to clear
|
|
* integrity_recalculate so that subsequent activations will not
|
|
* include "recalculate" and restart initialization.
|
|
*/
|
|
|
|
log_debug("Writing VG with initialized integrity LV %s", lv->name);
|
|
|
|
for (s = 0; s < area_count; s++) {
|
|
lv_image = seg_lv(seg_top, s);
|
|
seg_image = first_seg(lv_image);
|
|
seg_image->integrity_recalculate = 0;
|
|
}
|
|
|
|
if (!vg_write(vg) || !vg_commit(vg))
|
|
goto_bad;
|
|
|
|
return 1;
|
|
|
|
bad:
|
|
log_error("Failed to add integrity.");
|
|
|
|
if (revert_meta_lvs) {
|
|
for (s = 0; s < DEFAULT_RAID_MAX_IMAGES; s++) {
|
|
if (!imeta_lvs[s])
|
|
continue;
|
|
if (!lv_remove(imeta_lvs[s]))
|
|
log_error("New integrity metadata LV may require manual removal.");
|
|
}
|
|
}
|
|
|
|
if (!vg_write(vg) || !vg_commit(vg))
|
|
log_error("New integrity metadata LV may require manual removal.");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This should rarely if ever be used. A command that adds integrity
|
|
* to an LV will activate and then clear the flag. If it fails before
|
|
* clearing the flag, then this function will be used by a subsequent
|
|
* activation to clear the flag.
|
|
*/
|
|
void lv_clear_integrity_recalculate_metadata(struct logical_volume *lv)
|
|
{
|
|
struct volume_group *vg = lv->vg;
|
|
struct logical_volume *lv_image;
|
|
struct lv_segment *seg, *seg_image;
|
|
uint32_t s;
|
|
|
|
if (!lv_is_raid(lv) && !lv_is_integrity(lv)) {
|
|
log_error("Invalid LV type for clearing integrity");
|
|
return;
|
|
}
|
|
|
|
seg = first_seg(lv);
|
|
|
|
if (seg_is_raid(seg)) {
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
lv_image = seg_lv(seg, s);
|
|
seg_image = first_seg(lv_image);
|
|
seg_image->integrity_recalculate = 0;
|
|
}
|
|
} else if (seg_is_integrity(seg)) {
|
|
seg->integrity_recalculate = 0;
|
|
}
|
|
|
|
if (!vg_write(vg) || !vg_commit(vg)) {
|
|
log_warn("WARNING: failed to clear integrity recalculate flag for %s",
|
|
display_lvname(lv));
|
|
}
|
|
}
|
|
|
|
int lv_has_integrity_recalculate_metadata(struct logical_volume *lv)
|
|
{
|
|
struct logical_volume *lv_image;
|
|
struct lv_segment *seg, *seg_image;
|
|
uint32_t s;
|
|
int ret = 0;
|
|
|
|
if (!lv_is_raid(lv) && !lv_is_integrity(lv))
|
|
return 0;
|
|
|
|
seg = first_seg(lv);
|
|
|
|
if (seg_is_raid(seg)) {
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
lv_image = seg_lv(seg, s);
|
|
seg_image = first_seg(lv_image);
|
|
|
|
if (!seg_is_integrity(seg_image))
|
|
continue;
|
|
if (seg_image->integrity_recalculate)
|
|
ret = 1;
|
|
}
|
|
} else if (seg_is_integrity(seg)) {
|
|
ret = seg->integrity_recalculate;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int lv_raid_has_integrity(struct logical_volume *lv)
|
|
{
|
|
struct logical_volume *lv_image;
|
|
struct lv_segment *seg, *seg_image;
|
|
uint32_t s;
|
|
|
|
if (!lv_is_raid(lv))
|
|
return 0;
|
|
|
|
seg = first_seg(lv);
|
|
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
lv_image = seg_lv(seg, s);
|
|
seg_image = first_seg(lv_image);
|
|
|
|
if (seg_is_integrity(seg_image))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lv_get_raid_integrity_settings(struct logical_volume *lv, struct integrity_settings **isettings)
|
|
{
|
|
struct logical_volume *lv_image;
|
|
struct lv_segment *seg, *seg_image;
|
|
uint32_t s;
|
|
|
|
if (!lv_is_raid(lv))
|
|
return_0;
|
|
|
|
seg = first_seg(lv);
|
|
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
lv_image = seg_lv(seg, s);
|
|
seg_image = first_seg(lv_image);
|
|
|
|
if (seg_is_integrity(seg_image)) {
|
|
*isettings = &seg_image->integrity_settings;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lv_raid_integrity_total_mismatches(struct cmd_context *cmd,
|
|
const struct logical_volume *lv,
|
|
uint64_t *mismatches)
|
|
{
|
|
struct logical_volume *lv_image;
|
|
struct lv_segment *seg, *seg_image;
|
|
uint32_t s;
|
|
uint64_t mismatches_image;
|
|
uint64_t total = 0;
|
|
int errors = 0;
|
|
|
|
if (!lv_is_raid(lv))
|
|
return 0;
|
|
|
|
seg = first_seg(lv);
|
|
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
lv_image = seg_lv(seg, s);
|
|
seg_image = first_seg(lv_image);
|
|
|
|
if (!seg_is_integrity(seg_image))
|
|
continue;
|
|
|
|
mismatches_image = 0;
|
|
|
|
if (!lv_integrity_mismatches(cmd, lv_image, &mismatches_image))
|
|
errors++;
|
|
|
|
total += mismatches_image;
|
|
}
|
|
*mismatches = total;
|
|
|
|
if (errors)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int lv_integrity_mismatches(struct cmd_context *cmd,
|
|
const struct logical_volume *lv,
|
|
uint64_t *mismatches)
|
|
{
|
|
struct lv_with_info_and_seg_status status = {
|
|
.seg_status.type = SEG_STATUS_NONE,
|
|
};
|
|
|
|
if (lv_is_raid(lv) && lv_raid_has_integrity((struct logical_volume *)lv))
|
|
return lv_raid_integrity_total_mismatches(cmd, lv, mismatches);
|
|
|
|
if (!lv_is_integrity(lv))
|
|
return_0;
|
|
|
|
status.seg_status.seg = first_seg(lv);
|
|
|
|
/* FIXME: why reporter_pool? */
|
|
if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024))) {
|
|
log_error("Failed to get mem for LV status.");
|
|
return 0;
|
|
}
|
|
|
|
if (!lv_info_with_seg_status(cmd, first_seg(lv), &status, 1, 1)) {
|
|
log_error("Failed to get device mapper status for %s", display_lvname(lv));
|
|
goto fail;
|
|
}
|
|
|
|
if (!status.info.exists)
|
|
goto fail;
|
|
|
|
if (status.seg_status.type != SEG_STATUS_INTEGRITY) {
|
|
log_error("Invalid device mapper status type (%d) for %s",
|
|
(uint32_t)status.seg_status.type, display_lvname(lv));
|
|
goto fail;
|
|
}
|
|
|
|
*mismatches = status.seg_status.integrity->number_of_mismatches;
|
|
|
|
dm_pool_destroy(status.seg_status.mem);
|
|
return 1;
|
|
|
|
fail:
|
|
dm_pool_destroy(status.seg_status.mem);
|
|
return 0;
|
|
}
|
|
|
|
int integrity_settings_to_str_list(struct integrity_settings *settings, struct dm_list *result, struct dm_pool *mem)
|
|
{
|
|
int errors = 0;
|
|
|
|
if (settings->journal_watermark_set)
|
|
if (!setting_str_list_add("journal_watermark", settings->journal_watermark, NULL, result, mem))
|
|
errors++;
|
|
|
|
if (settings->commit_time_set)
|
|
if (!setting_str_list_add("commit_time", settings->commit_time, NULL, result, mem))
|
|
errors++;
|
|
|
|
if (settings->bitmap_flush_interval_set)
|
|
if (!setting_str_list_add("bitmap_flush_interval", settings->bitmap_flush_interval, NULL, result, mem))
|
|
errors++;
|
|
|
|
if (settings->allow_discards_set)
|
|
if (!setting_str_list_add("allow_discards", settings->allow_discards, NULL, result, mem))
|
|
errors++;
|
|
if (errors)
|
|
log_warn("Failed to create list of integrity settings.");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|