mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
vdo: introduce segment types and manip functions
Core functionality introducing lvm VDO support.
This commit is contained in:
parent
c66a960149
commit
a8f84f7801
@ -77,6 +77,7 @@ SOURCES =\
|
||||
metadata/segtype.c \
|
||||
metadata/snapshot_manip.c \
|
||||
metadata/thin_manip.c \
|
||||
metadata/vdo_manip.c \
|
||||
metadata/vg.c \
|
||||
mirror/mirrored.c \
|
||||
misc/crc.c \
|
||||
@ -126,6 +127,10 @@ ifeq ("@BUILD_LVMLOCKD@", "yes")
|
||||
locking/lvmlockd.c
|
||||
endif
|
||||
|
||||
ifeq ("@VDO@", "internal")
|
||||
SOURCES += vdo/vdo.c
|
||||
endif
|
||||
|
||||
LIB_NAME = liblvm-internal
|
||||
LIB_STATIC = $(LIB_NAME).a
|
||||
|
||||
|
@ -261,6 +261,7 @@ void fs_unlock(void);
|
||||
#define TARGET_NAME_STRIPED "striped"
|
||||
#define TARGET_NAME_THIN "thin"
|
||||
#define TARGET_NAME_THIN_POOL "thin-pool"
|
||||
#define TARGET_NAME_VDO "vdo"
|
||||
#define TARGET_NAME_ZERO "zero"
|
||||
|
||||
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
|
||||
|
@ -45,7 +45,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", "tdata", "tmeta", NULL};
|
||||
const char *uuid_suffix_list[] = { "pool", "cdata", "cmeta", "tdata", "tmeta", "vdata", "vpool", NULL};
|
||||
|
||||
struct dlid_list {
|
||||
struct dm_list list;
|
||||
@ -275,6 +275,10 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
|
||||
(length > DM_THIN_MAX_METADATA_SIZE))
|
||||
length = DM_THIN_MAX_METADATA_SIZE;
|
||||
|
||||
/* Uses virtual size with headers for VDO pool device */
|
||||
if (lv_is_vdo_pool(seg_status->seg->lv))
|
||||
length = get_vdo_pool_virtual_size(seg_status->seg);
|
||||
|
||||
do {
|
||||
target = dm_get_next_target(dmt, target, &target_start,
|
||||
&target_length, &target_name, &target_params);
|
||||
|
@ -1399,6 +1399,11 @@ static int _init_segtypes(struct cmd_context *cmd)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#ifdef VDO_INTERNAL
|
||||
if (!init_vdo_segtypes(cmd, &seglib))
|
||||
return_0;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBDL
|
||||
/* Load any formats in shared libs unless static */
|
||||
if (!is_static() &&
|
||||
|
@ -98,6 +98,9 @@ static const struct flag _lv_flags[] = {
|
||||
{CACHE_POOL, NULL, 0},
|
||||
{CACHE_POOL_DATA, NULL, 0},
|
||||
{CACHE_POOL_METADATA, NULL, 0},
|
||||
{LV_VDO, NULL, 0},
|
||||
{LV_VDO_POOL, NULL, 0},
|
||||
{LV_VDO_POOL_DATA, NULL, 0},
|
||||
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
|
||||
{LV_REMOVED, NULL, 0},
|
||||
{0, NULL, 0}
|
||||
|
@ -797,6 +797,9 @@ const char *lv_layer(const struct logical_volume *lv)
|
||||
if (lv_is_thin_pool(lv))
|
||||
return "tpool";
|
||||
|
||||
if (lv_is_vdo_pool(lv))
|
||||
return "vpool";
|
||||
|
||||
if (lv_is_origin(lv) || lv_is_external_origin(lv))
|
||||
return "real";
|
||||
|
||||
|
@ -1034,6 +1034,7 @@ static int _release_and_discard_lv_segment_area(struct lv_segment *seg, uint32_t
|
||||
|
||||
if (lv_is_mirror_image(lv) ||
|
||||
lv_is_thin_pool_data(lv) ||
|
||||
lv_is_vdo_pool_data(lv) ||
|
||||
lv_is_cache_pool_data(lv)) {
|
||||
if (!lv_reduce(lv, area_reduction))
|
||||
return_0; /* FIXME: any upper level reporting */
|
||||
@ -1102,6 +1103,10 @@ static int _release_and_discard_lv_segment_area(struct lv_segment *seg, uint32_t
|
||||
seg_type(seg, s) = AREA_UNASSIGNED;
|
||||
}
|
||||
|
||||
/* When removed last VDO user automatically removes VDO pool */
|
||||
if (lv_is_vdo_pool(lv) && dm_list_empty(&(lv->segs_using_this_lv)))
|
||||
return lv_remove(lv); /* FIXME: any upper level reporting */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3265,11 +3270,46 @@ static int _allocate(struct alloc_handle *ah,
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: Add proper allocation function for VDO segment on top
|
||||
* of VDO pool with virtual size.
|
||||
*
|
||||
* Note: ATM lvm2 can't resize VDO device so it can add only a single segment.
|
||||
*/
|
||||
static int _lv_add_vdo_segment(struct logical_volume *lv, uint64_t status,
|
||||
uint32_t extents, const struct segment_type *segtype)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
|
||||
if (!dm_list_empty(&lv->segments) &&
|
||||
(seg = last_seg(lv)) && (seg->segtype == segtype)) {
|
||||
seg->area_len += extents;
|
||||
seg->len += extents;
|
||||
} else {
|
||||
if (!(seg = alloc_lv_segment(segtype, lv, lv->le_count, extents, 0,
|
||||
status, 0, NULL, 1,
|
||||
extents, 0, 0, 0, 0, NULL))) {
|
||||
log_error("Couldn't allocate new %s segment.", segtype->name);
|
||||
return 0;
|
||||
}
|
||||
lv->status |= LV_VDO;
|
||||
dm_list_add(&lv->segments, &seg->list);
|
||||
}
|
||||
|
||||
lv->le_count += extents;
|
||||
lv->size += (uint64_t) extents * lv->vg->extent_size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_add_virtual_segment(struct logical_volume *lv, uint64_t status,
|
||||
uint32_t extents, const struct segment_type *segtype)
|
||||
{
|
||||
struct lv_segment *seg;
|
||||
|
||||
if (segtype_is_vdo(segtype))
|
||||
return _lv_add_vdo_segment(lv, 0u, extents, segtype);
|
||||
|
||||
if (!dm_list_empty(&lv->segments) &&
|
||||
(seg = last_seg(lv)) && (seg->segtype == segtype)) {
|
||||
seg->area_len += extents;
|
||||
@ -4362,7 +4402,9 @@ static int _rename_cb(struct logical_volume *lv, void *data)
|
||||
|
||||
static int _rename_skip_pools_externals_cb(struct logical_volume *lv, void *data)
|
||||
{
|
||||
if (lv_is_pool(lv) || lv_is_external_origin(lv))
|
||||
if (lv_is_pool(lv) ||
|
||||
lv_is_vdo_pool(lv) ||
|
||||
lv_is_external_origin(lv))
|
||||
return -1; /* and skip subLVs */
|
||||
|
||||
return _rename_cb(lv, data);
|
||||
@ -4458,6 +4500,7 @@ int lv_rename_update(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
* (thin pool is 'visible', but cache may not)
|
||||
*/
|
||||
if (!lv_is_pool(lv) &&
|
||||
!lv_is_vdo_pool(lv) &&
|
||||
!lv_is_visible(lv)) {
|
||||
log_error("Cannot rename internal LV \"%s\".", lv->name);
|
||||
return 0;
|
||||
@ -6351,6 +6394,13 @@ int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume *
|
||||
!_lv_remove_segs_using_this_lv(cmd, lv, force, level, "pool"))
|
||||
return_0;
|
||||
|
||||
if (lv_is_vdo_pool(lv)) {
|
||||
if (!_lv_remove_segs_using_this_lv(cmd, lv, force, level, "VDO pool"))
|
||||
return_0;
|
||||
/* Last user removes VDO pool itself, lv no longer exists */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (lv_is_cache_pool(lv) && !lv_is_used_cache_pool(lv)) {
|
||||
if (!deactivate_lv(cmd, first_seg(lv)->metadata_lv) ||
|
||||
!deactivate_lv(cmd, seg_lv(first_seg(lv),0))) {
|
||||
@ -6787,7 +6837,7 @@ struct logical_volume *insert_layer_for_lv(struct cmd_context *cmd,
|
||||
uint64_t status,
|
||||
const char *layer_suffix)
|
||||
{
|
||||
static const char _suffixes[][8] = { "_tdata", "_cdata", "_corig" };
|
||||
static const char _suffixes[][8] = { "_tdata", "_cdata", "_corig", "_vdata" };
|
||||
int r;
|
||||
char name[NAME_LEN];
|
||||
struct dm_str_list *sl;
|
||||
@ -7386,6 +7436,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
seg_is_mirror(lp) ||
|
||||
(seg_is_raid(lp) && !seg_is_raid0(lp)) ||
|
||||
seg_is_thin(lp) ||
|
||||
seg_is_vdo(lp) ||
|
||||
lp->snapshot) {
|
||||
/*
|
||||
* FIXME: For thin pool add some code to allow delayed
|
||||
@ -7451,7 +7502,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
|
||||
if (seg_is_pool(lp))
|
||||
status |= LVM_WRITE; /* Pool is always writable */
|
||||
else if (seg_is_cache(lp) || seg_is_thin_volume(lp)) {
|
||||
else if (seg_is_cache(lp) || seg_is_thin_volume(lp) || seg_is_vdo(lp)) {
|
||||
/* Resolve pool volume */
|
||||
if (!lp->pool_name) {
|
||||
/* Should be already checked */
|
||||
@ -7619,6 +7670,16 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
status |= LVM_WRITE;
|
||||
lp->zero = 1;
|
||||
lp->wipe_signatures = 0;
|
||||
} else if (seg_is_vdo_pool(lp)) {
|
||||
if (!lp->virtual_extents)
|
||||
log_verbose("Virtual size matching available free logical size in VDO pool.");
|
||||
|
||||
if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
|
||||
return_NULL;
|
||||
|
||||
/* Must zero and format data area */
|
||||
status |= LVM_WRITE;
|
||||
lp->zero = 1;
|
||||
}
|
||||
|
||||
if (!segtype_is_virtual(create_segtype) && !lp->approx_alloc &&
|
||||
@ -7648,7 +7709,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
log_debug_metadata("Setting read ahead sectors %u.", lv->read_ahead);
|
||||
}
|
||||
|
||||
if (!segtype_is_pool(create_segtype) && lp->minor >= 0) {
|
||||
if (!segtype_is_pool(create_segtype) &&
|
||||
!segtype_is_vdo_pool(create_segtype) &&
|
||||
lp->minor >= 0) {
|
||||
lv->major = lp->major;
|
||||
lv->minor = lp->minor;
|
||||
lv->status |= FIXED_MINOR;
|
||||
@ -7670,7 +7733,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
lp->stripes, lp->stripe_size,
|
||||
lp->mirrors,
|
||||
segtype_is_pool(create_segtype) ? lp->pool_metadata_extents : lp->region_size,
|
||||
segtype_is_thin_volume(create_segtype) ? lp->virtual_extents : lp->extents,
|
||||
(segtype_is_thin_volume(create_segtype) ||
|
||||
segtype_is_vdo(create_segtype)) ? lp->virtual_extents : lp->extents,
|
||||
lp->pvh, lp->alloc, lp->approx_alloc)) {
|
||||
unlink_lv_from_vg(lv); /* Keep VG consistent and remove LV without any segment */
|
||||
return_NULL;
|
||||
@ -7686,6 +7750,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
/* Unlock memory if possible */
|
||||
memlock_unlock(vg->cmd);
|
||||
|
||||
if (segtype_is_vdo(create_segtype) && pool_lv) {
|
||||
if (!set_lv_segment_area_lv(first_seg(lv), 0, pool_lv, 0, LV_VDO_POOL))
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
if (lv_is_cache_pool(lv)) {
|
||||
if (!cache_set_params(first_seg(lv),
|
||||
lp->chunk_size,
|
||||
@ -7886,7 +7955,12 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_is_cache(lp) || (origin_lv && lv_is_cache_pool(lv))) {
|
||||
if (seg_is_vdo_pool(lp)) {
|
||||
if (!convert_vdo_pool_lv(lv, &lp->vdo_params, &lp->virtual_extents)) {
|
||||
stack;
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
} else if (seg_is_cache(lp) || (origin_lv && lv_is_cache_pool(lv))) {
|
||||
/* Finish cache conversion magic */
|
||||
if (origin_lv) {
|
||||
/* Convert origin to cached LV */
|
||||
@ -8047,6 +8121,13 @@ struct logical_volume *lv_create_single(struct volume_group *vg,
|
||||
log_print_unless_silent("Logical volume %s is now cached.",
|
||||
display_lvname(lv));
|
||||
return lv;
|
||||
} else if (seg_is_vdo(lp)) {
|
||||
/* The VDO segment needs VDO pool which is layer above created striped data LV */
|
||||
if (!(lp->segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_VDO_POOL)))
|
||||
return_NULL;
|
||||
|
||||
if (!(lv = _lv_create_an_lv(vg, lp, lp->pool_name)))
|
||||
return_NULL;
|
||||
} else {
|
||||
log_error(INTERNAL_ERROR "Creation of pool for unsupported segment type %s.",
|
||||
lp->segtype->name);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@ -145,7 +145,12 @@
|
||||
#define LV_RESHAPE UINT64_C(0x1000000000000000) /* Ongoing reshape (number of stripes, stripesize or raid algorithm change):
|
||||
used as SEGTYPE_FLAG to prevent activation on old runtime */
|
||||
#define LV_RESHAPE_DATA_OFFSET UINT64_C(0x2000000000000000) /* LV reshape flag data offset (out of place reshaping) */
|
||||
/* Next unused flag: UINT64_C(0x8000000000000000) */
|
||||
|
||||
|
||||
#define LV_VDO UINT64_C(0x0000000020000000) /* LV - Internal user only */
|
||||
#define LV_VDO_POOL UINT64_C(0x0000000040000000) /* LV - Internal user only */
|
||||
#define LV_VDO_POOL_DATA UINT64_C(0x8000000000000000) /* LV - Internal user only */
|
||||
|
||||
|
||||
/* Format features flags */
|
||||
#define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */
|
||||
@ -251,6 +256,11 @@
|
||||
#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_vdo(lv) (((lv)->status & LV_VDO) ? 1 : 0)
|
||||
#define lv_is_vdo_pool(lv) (((lv)->status & LV_VDO_POOL) ? 1 : 0)
|
||||
#define lv_is_vdo_pool_data(lv) (((lv)->status & LV_VDO_POOL_DATA) ? 1 : 0)
|
||||
#define lv_is_vdo_type(lv) (((lv)->status & (LV_VDO | LV_VDO_POOL | LV_VDO_POOL_DATA)) ? 1 : 0)
|
||||
|
||||
#define lv_is_removed(lv) (((lv)->status & LV_REMOVED) ? 1 : 0)
|
||||
|
||||
/* Recognize component LV (matching lib/misc/lvm-string.c _lvname_has_reserved_component_string()) */
|
||||
@ -487,6 +497,10 @@ struct lv_segment {
|
||||
const char *policy_name; /* For cache_pool */
|
||||
struct dm_config_node *policy_settings; /* For cache_pool */
|
||||
unsigned cleaner_policy; /* For cache */
|
||||
|
||||
struct dm_vdo_target_params vdo_params; /* For VDO-pool */
|
||||
uint32_t vdo_pool_header_size; /* For VDO-pool */
|
||||
uint32_t vdo_pool_virtual_extents; /* For VDO-pool */
|
||||
};
|
||||
|
||||
#define seg_type(seg, s) (seg)->areas[(s)].type
|
||||
@ -950,6 +964,7 @@ struct lvcreate_params {
|
||||
uint32_t read_ahead; /* all */
|
||||
int approx_alloc; /* all */
|
||||
alloc_policy_t alloc; /* all */
|
||||
struct dm_vdo_target_params vdo_params; /* vdo */
|
||||
|
||||
struct dm_list tags; /* all */
|
||||
|
||||
@ -1232,6 +1247,16 @@ int lv_cache_remove(struct logical_volume *cache_lv);
|
||||
int wipe_cache_pool(struct logical_volume *cache_pool_lv);
|
||||
/* -- metadata/cache_manip.c */
|
||||
|
||||
|
||||
/* ++ metadata/vdo_manip.c */
|
||||
|
||||
uint64_t get_vdo_pool_virtual_size(const struct lv_segment *vdo_pool_seg);
|
||||
struct logical_volume *convert_vdo_pool_lv(struct logical_volume *data_lv,
|
||||
const struct dm_vdo_target_params *vtp,
|
||||
uint32_t *virtual_extents);
|
||||
int get_vdo_write_policy(enum dm_vdo_write_policy *vwp, const char *policy);
|
||||
/* -- metadata/vdo_manip.c */
|
||||
|
||||
struct logical_volume *find_pvmove_lv(struct volume_group *vg,
|
||||
struct device *dev, uint64_t lv_type);
|
||||
const struct logical_volume *find_pvmove_lv_in_lv(const struct logical_volume *lv);
|
||||
|
@ -69,6 +69,8 @@ struct dev_manager;
|
||||
|
||||
#define SEG_STRIPED_TARGET (1ULL << 39)
|
||||
#define SEG_LINEAR_TARGET (1ULL << 40)
|
||||
#define SEG_VDO (1ULL << 41)
|
||||
#define SEG_VDO_POOL (1ULL << 42)
|
||||
|
||||
#define SEG_UNKNOWN (1ULL << 63)
|
||||
|
||||
@ -83,6 +85,8 @@ struct dev_manager;
|
||||
#define SEG_TYPE_NAME_ERROR "error"
|
||||
#define SEG_TYPE_NAME_FREE "free"
|
||||
#define SEG_TYPE_NAME_ZERO "zero"
|
||||
#define SEG_TYPE_NAME_VDO "vdo"
|
||||
#define SEG_TYPE_NAME_VDO_POOL "vdo-pool"
|
||||
#define SEG_TYPE_NAME_RAID "raid"
|
||||
#define SEG_TYPE_NAME_RAID0 "raid0"
|
||||
#define SEG_TYPE_NAME_RAID0_META "raid0_meta"
|
||||
@ -151,6 +155,8 @@ struct dev_manager;
|
||||
#define segtype_is_thin(segtype) ((segtype)->flags & (SEG_THIN_POOL|SEG_THIN_VOLUME) ? 1 : 0)
|
||||
#define segtype_is_thin_pool(segtype) ((segtype)->flags & SEG_THIN_POOL ? 1 : 0)
|
||||
#define segtype_is_thin_volume(segtype) ((segtype)->flags & SEG_THIN_VOLUME ? 1 : 0)
|
||||
#define segtype_is_vdo(segtype) ((segtype)->flags & SEG_VDO ? 1 : 0)
|
||||
#define segtype_is_vdo_pool(segtype) ((segtype)->flags & SEG_VDO_POOL ? 1 : 0)
|
||||
#define segtype_is_virtual(segtype) ((segtype)->flags & SEG_VIRTUAL ? 1 : 0)
|
||||
#define segtype_is_unknown(segtype) ((segtype)->flags & SEG_UNKNOWN ? 1 : 0)
|
||||
|
||||
@ -202,6 +208,8 @@ struct dev_manager;
|
||||
#define seg_is_thin(seg) segtype_is_thin((seg)->segtype)
|
||||
#define seg_is_thin_pool(seg) segtype_is_thin_pool((seg)->segtype)
|
||||
#define seg_is_thin_volume(seg) segtype_is_thin_volume((seg)->segtype)
|
||||
#define seg_is_vdo(seg) segtype_is_vdo((seg)->segtype)
|
||||
#define seg_is_vdo_pool(seg) segtype_is_vdo_pool((seg)->segtype)
|
||||
#define seg_is_virtual(seg) segtype_is_virtual((seg)->segtype)
|
||||
#define seg_unknown(seg) segtype_is_unknown((seg)->segtype)
|
||||
#define seg_can_split(seg) segtype_can_split((seg)->segtype)
|
||||
@ -329,6 +337,10 @@ int init_thin_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
|
||||
int init_cache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
|
||||
#endif
|
||||
|
||||
#ifdef VDO_INTERNAL
|
||||
int init_vdo_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
|
||||
#endif
|
||||
|
||||
#define CACHE_FEATURE_POLICY_MQ (1U << 0)
|
||||
#define CACHE_FEATURE_POLICY_SMQ (1U << 1)
|
||||
#define CACHE_FEATURE_METADATA2 (1U << 2)
|
||||
|
269
lib/metadata/vdo_manip.c
Normal file
269
lib/metadata/vdo_manip.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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/metadata/lv_alloc.h"
|
||||
#include "lib/misc/lvm-signal.h"
|
||||
#include "lib/misc/lvm-exec.h"
|
||||
|
||||
|
||||
/*
|
||||
* Size of VDO virtual LV is adding header_size in front and back of device
|
||||
* to avoid colission with blkid checks.
|
||||
*/
|
||||
static uint64_t _get_virtual_size(uint32_t extents, uint32_t extent_size,
|
||||
uint32_t header_size)
|
||||
{
|
||||
return (uint64_t) extents * extent_size + 2 * header_size;
|
||||
}
|
||||
|
||||
uint64_t get_vdo_pool_virtual_size(const struct lv_segment *vdo_pool_seg)
|
||||
{
|
||||
return _get_virtual_size(vdo_pool_seg->vdo_pool_virtual_extents,
|
||||
vdo_pool_seg->lv->vg->extent_size,
|
||||
vdo_pool_seg->vdo_pool_header_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Formats data LV for a use as a VDO pool LV.
|
||||
*
|
||||
* Calls tool 'vdoformat' on the already active volume.
|
||||
*/
|
||||
static int _format_vdo_pool_data_lv(struct logical_volume *data_lv,
|
||||
const struct dm_vdo_target_params *vtp,
|
||||
uint64_t *logical_size)
|
||||
{
|
||||
char *dpath;
|
||||
const struct dm_config_node *cn;
|
||||
const struct dm_config_value *cv;
|
||||
struct pipe_data pdata;
|
||||
FILE *f;
|
||||
uint64_t lb;
|
||||
unsigned slabbits;
|
||||
int args = 1;
|
||||
char buf_args[5][128];
|
||||
char buf[256]; /* buffer for short disk header (64B) */
|
||||
const char *argv[19] = { /* Max supported args */
|
||||
find_config_tree_str_allow_empty(data_lv->vg->cmd, global_vdo_format_executable_CFG, NULL)
|
||||
};
|
||||
|
||||
if (!(dpath = lv_path_dup(data_lv->vg->cmd->mem, data_lv))) {
|
||||
log_error("Failed to build device path for VDO formating of data volume %s.",
|
||||
display_lvname(data_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*logical_size) {
|
||||
if (dm_snprintf(buf_args[args], sizeof(buf_args[0]), "--logical-size=" FMTu64 "K",
|
||||
(*logical_size / 2)) < 0)
|
||||
return_0;
|
||||
|
||||
argv[args] = buf_args[args];
|
||||
args++;
|
||||
}
|
||||
|
||||
slabbits = 31 - clz(vtp->slab_size_mb / DM_VDO_BLOCK_SIZE * 512);
|
||||
log_debug("Slab size %s converted to %u bits.",
|
||||
display_size(data_lv->vg->cmd, vtp->slab_size_mb * UINT64_C(2 * 1024)), slabbits);
|
||||
if (dm_snprintf(buf_args[args], sizeof(buf_args[0]), "--slab-bits=%u", slabbits) < 0)
|
||||
return_0;
|
||||
|
||||
argv[args] = buf_args[args];
|
||||
args++;
|
||||
|
||||
if (vtp->check_point_frequency) {
|
||||
if (dm_snprintf(buf_args[args], sizeof(buf_args[0]), "--uds-checkpoint-frequency=%u",
|
||||
vtp->check_point_frequency) < 0)
|
||||
return_0;
|
||||
argv[args] = buf_args[args];
|
||||
args++;
|
||||
}
|
||||
|
||||
/* Convert size to GiB units or one of these strings: 0.25, 0.50, 0.75 */
|
||||
if (vtp->index_memory_size_mb >= 1024) {
|
||||
if (dm_snprintf(buf_args[args], sizeof(buf_args[0]), "--uds-memory-size=%u",
|
||||
vtp->index_memory_size_mb / 1024) < 0)
|
||||
return_0;
|
||||
} else if (dm_snprintf(buf_args[args], sizeof(buf_args[0]), "--uds-memory-size=0.%u",
|
||||
(vtp->index_memory_size_mb < 512) ? 25 :
|
||||
(vtp->index_memory_size_mb < 768) ? 50 : 75) < 0)
|
||||
return_0;
|
||||
|
||||
argv[args] = buf_args[args];
|
||||
args++;
|
||||
|
||||
if (vtp->use_sparse_index) {
|
||||
if (dm_snprintf(buf_args[args], sizeof(buf_args[0]), "--uds-sparse") < 0)
|
||||
return_0;
|
||||
|
||||
argv[args] = buf_args[args];
|
||||
args++;
|
||||
}
|
||||
|
||||
/* Any other user opts add here */
|
||||
if (!(cn = find_config_tree_array(data_lv->vg->cmd, global_vdo_format_options_CFG, NULL))) {
|
||||
log_error(INTERNAL_ERROR "Unable to find configuration for vdoformat command options.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cv = cn->v; cv && args < 16; cv = cv->next) {
|
||||
if (cv->type != DM_CFG_STRING) {
|
||||
log_error("Invalid string in config file: "
|
||||
"global/vdoformat_options.");
|
||||
return 0;
|
||||
}
|
||||
if (cv->v.str[0])
|
||||
argv[++args] = cv->v.str;
|
||||
}
|
||||
|
||||
/* Only unused VDO data LV could be activated and wiped */
|
||||
if (!dm_list_empty(&data_lv->segs_using_this_lv)) {
|
||||
log_error(INTERNAL_ERROR "Failed to wipe logical VDO data for volume %s.",
|
||||
display_lvname(data_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
argv[args] = dpath;
|
||||
|
||||
if (!(f = pipe_open(data_lv->vg->cmd, argv, 0, &pdata))) {
|
||||
log_error("WARNING: Cannot read output from %s.", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*logical_size)
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
/* TODO: Watch out for locales */
|
||||
if (sscanf(buf, "Logical blocks defaulted to " FMTu64 " blocks", &lb) == 1) {
|
||||
*logical_size = lb * DM_VDO_BLOCK_SIZE;
|
||||
log_verbose("Available VDO logical blocks " FMTu64 " (%s).",
|
||||
lb, display_size(data_lv->vg->cmd, *logical_size));
|
||||
break;
|
||||
} else
|
||||
log_warn("WARNING: Cannot parse output '%s' from %s.", buf, argv[0]);
|
||||
}
|
||||
|
||||
if (!pipe_close(&pdata)) {
|
||||
log_error("Command %s failed.", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert_vdo_pool_lv
|
||||
* @data_lv
|
||||
* @vtp
|
||||
* @virtual_extents
|
||||
*
|
||||
* Convert given data LV and its target parameters into a VDO LV with VDO pool.
|
||||
*
|
||||
* Returns: old data LV on success (passed data LV becomes VDO LV), NULL on failure
|
||||
*/
|
||||
struct logical_volume *convert_vdo_pool_lv(struct logical_volume *data_lv,
|
||||
const struct dm_vdo_target_params *vtp,
|
||||
uint32_t *virtual_extents)
|
||||
{
|
||||
const uint64_t header_size = DEFAULT_VDO_POOL_HEADER_SIZE;
|
||||
const uint32_t extent_size = data_lv->vg->extent_size;
|
||||
struct cmd_context *cmd = data_lv->vg->cmd;
|
||||
struct logical_volume *vdo_pool_lv = data_lv;
|
||||
const struct segment_type *vdo_pool_segtype;
|
||||
struct lv_segment *vdo_pool_seg;
|
||||
uint64_t vdo_logical_size = 0;
|
||||
uint64_t adjust;
|
||||
|
||||
if (!(vdo_pool_segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_VDO_POOL)))
|
||||
return_NULL;
|
||||
|
||||
adjust = (*virtual_extents * (uint64_t) extent_size) % DM_VDO_BLOCK_SIZE;
|
||||
if (adjust) {
|
||||
*virtual_extents += (DM_VDO_BLOCK_SIZE - adjust) / extent_size;
|
||||
log_print_unless_silent("Rounding size up to 4,00 KiB VDO logical extent boundary: %s.",
|
||||
display_size(data_lv->vg->cmd, *virtual_extents * (uint64_t) extent_size));
|
||||
}
|
||||
|
||||
if (*virtual_extents)
|
||||
vdo_logical_size =
|
||||
_get_virtual_size(*virtual_extents, extent_size, header_size);
|
||||
|
||||
if (!dm_vdo_validate_target_params(vtp, vdo_logical_size))
|
||||
return_0;
|
||||
|
||||
/* Format data LV as VDO volume */
|
||||
if (!_format_vdo_pool_data_lv(data_lv, vtp, &vdo_logical_size)) {
|
||||
log_error("Cannot format VDO pool volume %s.", display_lvname(data_lv));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!deactivate_lv(data_lv->vg->cmd, data_lv)) {
|
||||
log_error("Aborting. Manual intervention required.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vdo_logical_size -= 2 * header_size;
|
||||
|
||||
if (vdo_logical_size < extent_size) {
|
||||
if (!*virtual_extents)
|
||||
/* User has not specified size and at least 1 extent is necessary */
|
||||
log_error("Cannot create fully fitting VDO volume, "
|
||||
"--virtualsize has to be specified.");
|
||||
|
||||
log_error("Size %s for VDO volume cannot be smaller then extent size %s.",
|
||||
display_size(data_lv->vg->cmd, vdo_logical_size),
|
||||
display_size(data_lv->vg->cmd, extent_size));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*virtual_extents = vdo_logical_size / extent_size;
|
||||
|
||||
/* Move segments from existing data_lv into LV_vdata */
|
||||
if (!(data_lv = insert_layer_for_lv(cmd, vdo_pool_lv, 0, "_vdata")))
|
||||
return_NULL;
|
||||
|
||||
vdo_pool_seg = first_seg(vdo_pool_lv);
|
||||
vdo_pool_seg->segtype = vdo_pool_segtype;
|
||||
vdo_pool_seg->vdo_params = *vtp;
|
||||
vdo_pool_seg->vdo_pool_header_size = DEFAULT_VDO_POOL_HEADER_SIZE;
|
||||
vdo_pool_seg->vdo_pool_virtual_extents = *virtual_extents;
|
||||
|
||||
vdo_pool_lv->status |= LV_VDO_POOL;
|
||||
data_lv->status |= LV_VDO_POOL_DATA;
|
||||
|
||||
return data_lv;
|
||||
}
|
||||
|
||||
int get_vdo_write_policy(enum dm_vdo_write_policy *vwp, const char *policy)
|
||||
{
|
||||
if (strcasecmp(policy, "sync") == 0)
|
||||
*vwp = DM_VDO_WRITE_POLICY_SYNC;
|
||||
else if (strcasecmp(policy, "async") == 0)
|
||||
*vwp = DM_VDO_WRITE_POLICY_ASYNC;
|
||||
else if (strcasecmp(policy, "auto") == 0)
|
||||
*vwp = DM_VDO_WRITE_POLICY_AUTO;
|
||||
else {
|
||||
log_error("Unknown VDO write policy %s.", policy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
@ -258,6 +258,8 @@ char *build_dm_uuid(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
lv_is_thin_pool(lv) ? "pool" :
|
||||
lv_is_thin_pool_data(lv) ? "tdata" :
|
||||
lv_is_thin_pool_metadata(lv) ? "tmeta" :
|
||||
lv_is_vdo_pool(lv) ? "vpool" :
|
||||
lv_is_vdo_pool_data(lv) ? "vdata" :
|
||||
NULL;
|
||||
}
|
||||
|
||||
|
484
lib/vdo/vdo.c
Normal file
484
lib/vdo/vdo.c
Normal file
@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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/activate/activate.h"
|
||||
#include "lib/activate/targets.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
#include "lib/datastruct/str_list.h"
|
||||
#include "lib/display/display.h"
|
||||
#include "lib/format_text/text_export.h"
|
||||
#include "lib/log/lvm-logging.h"
|
||||
#include "lib/metadata/metadata.h"
|
||||
#include "lib/metadata/lv_alloc.h"
|
||||
#include "lib/metadata/segtype.h"
|
||||
#include "base/memory/zalloc.h"
|
||||
|
||||
static unsigned _feature_mask;
|
||||
|
||||
static int _bad_field(const char *field)
|
||||
{
|
||||
log_error("Couldn't read '%s' for VDO segment.", field);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _import_bool(const struct dm_config_node *n,
|
||||
const char *name, bool *b)
|
||||
{
|
||||
uint32_t t;
|
||||
|
||||
if (dm_config_has_node(n, name)) {
|
||||
if (!dm_config_get_uint32(n, name, &t))
|
||||
return _bad_field(name);
|
||||
|
||||
if (t) {
|
||||
*b = true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
*b = false;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _print_yes_no(const char *name, bool value)
|
||||
{
|
||||
log_print(" %s\t%s", name, value ? "yes" : "no");
|
||||
}
|
||||
|
||||
/*
|
||||
* VDO linear mapping
|
||||
*/
|
||||
static const char *_vdo_name(const struct lv_segment *seg)
|
||||
{
|
||||
return SEG_TYPE_NAME_VDO;
|
||||
}
|
||||
|
||||
static int _vdo_text_import(struct lv_segment *seg,
|
||||
const struct dm_config_node *n,
|
||||
struct dm_hash_table *pv_hash __attribute__((unused)))
|
||||
{
|
||||
struct logical_volume *vdo_pool_lv;
|
||||
const char *str;
|
||||
uint32_t vdo_offset;
|
||||
|
||||
if (!dm_config_has_node(n, "vdo_pool") ||
|
||||
!(str = dm_config_find_str(n, "vdo_pool", NULL)))
|
||||
return _bad_field("vdo_pool");
|
||||
if (!(vdo_pool_lv = find_lv(seg->lv->vg, str))) {
|
||||
log_error("Unknown VDO pool logical volume %s.", str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_config_get_uint32(n, "vdo_offset", &vdo_offset))
|
||||
return _bad_field("vdo_offset");
|
||||
|
||||
if (!set_lv_segment_area_lv(seg, 0, vdo_pool_lv, vdo_offset, LV_VDO_POOL))
|
||||
return_0;
|
||||
|
||||
seg->lv->status |= LV_VDO;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vdo_text_export(const struct lv_segment *seg, struct formatter *f)
|
||||
{
|
||||
|
||||
if (!seg_is_vdo(seg)) {
|
||||
log_error(INTERNAL_ERROR "Passed segment is not VDO type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
outf(f, "vdo_pool = \"%s\"", seg_lv(seg, 0)->name);
|
||||
outf(f, "vdo_offset = %u", seg_le(seg, 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
static int _vdo_target_status_compatible(const char *type)
|
||||
{
|
||||
return (strcmp(type, TARGET_NAME_LINEAR) == 0);
|
||||
}
|
||||
|
||||
static int _vdo_add_target_line(struct dev_manager *dm,
|
||||
struct dm_pool *mem __attribute__((unused)),
|
||||
struct cmd_context *cmd,
|
||||
void **target_state __attribute__((unused)),
|
||||
struct lv_segment *seg,
|
||||
const struct lv_activate_opts *laopts __attribute__((unused)),
|
||||
struct dm_tree_node *node, uint64_t len,
|
||||
uint32_t *pvmove_mirror_count __attribute__((unused)))
|
||||
{
|
||||
char *vdo_pool_uuid;
|
||||
|
||||
if (!(vdo_pool_uuid = build_dm_uuid(mem, seg_lv(seg, 0), lv_layer(seg_lv(seg, 0)))))
|
||||
return_0;
|
||||
|
||||
if (!add_linear_area_to_dtree(node, len, seg->lv->vg->extent_size,
|
||||
cmd->use_linear_target,
|
||||
seg->lv->vg->name, seg->lv->name))
|
||||
return_0;
|
||||
|
||||
if (!dm_tree_node_add_target_area(node, NULL, vdo_pool_uuid,
|
||||
first_seg(seg_lv(seg, 0))->vdo_pool_header_size +
|
||||
seg->lv->vg->extent_size * (uint64_t)seg_le(seg, 0)))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* VDO pool
|
||||
*/
|
||||
static const char *_vdo_pool_name(const struct lv_segment *seg)
|
||||
{
|
||||
return SEG_TYPE_NAME_VDO_POOL;
|
||||
}
|
||||
|
||||
/* reused as _vdo_text_import_area_count */
|
||||
static int _vdo_pool_text_import_area_count(const struct dm_config_node *sn __attribute__((unused)),
|
||||
uint32_t *area_count)
|
||||
{
|
||||
*area_count = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vdo_pool_text_import(struct lv_segment *seg,
|
||||
const struct dm_config_node *n,
|
||||
struct dm_hash_table *pv_hash __attribute__((unused)))
|
||||
{
|
||||
struct dm_vdo_target_params *vtp = &seg->vdo_params;
|
||||
struct logical_volume *data_lv;
|
||||
const char *str;
|
||||
|
||||
if (!dm_config_has_node(n, "data") ||
|
||||
!(str = dm_config_find_str(n, "data", NULL)))
|
||||
return _bad_field("data");
|
||||
if (!(data_lv = find_lv(seg->lv->vg, str))) {
|
||||
log_error("Unknown logical volume %s.", str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: we may avoid printing settings with FIXED default values
|
||||
* so it would generate smaller metadata.
|
||||
*/
|
||||
if (!dm_config_get_uint32(n, "header_size", &seg->vdo_pool_header_size))
|
||||
return _bad_field("header_size");
|
||||
|
||||
if (!dm_config_get_uint32(n, "virtual_extents", &seg->vdo_pool_virtual_extents))
|
||||
return _bad_field("virtual_extents");
|
||||
|
||||
memset(vtp, 0, sizeof(*vtp));
|
||||
|
||||
if (!_import_bool(n, "use_compression", &vtp->use_compression))
|
||||
return_0;
|
||||
|
||||
if (!_import_bool(n, "use_deduplication", &vtp->use_deduplication))
|
||||
return_0;
|
||||
|
||||
if (!_import_bool(n, "emulate_512_sectors", &vtp->emulate_512_sectors))
|
||||
return_0;
|
||||
|
||||
if (!dm_config_get_uint32(n, "block_map_cache_size_mb", &vtp->block_map_cache_size_mb))
|
||||
return _bad_field("block_map_cache_size_mb");
|
||||
|
||||
if (!dm_config_get_uint32(n, "block_map_period", &vtp->block_map_period))
|
||||
return _bad_field("block_map_period");
|
||||
|
||||
if (!_import_bool(n, "use_sparse_index", &vtp->use_sparse_index))
|
||||
return_0;
|
||||
|
||||
if (!dm_config_get_uint32(n, "index_memory_size_mb", &vtp->index_memory_size_mb))
|
||||
return _bad_field("index_memory_size_mb");
|
||||
|
||||
if (!_import_bool(n, "use_read_cache", &vtp->use_read_cache))
|
||||
return_0;
|
||||
|
||||
if (!dm_config_get_uint32(n, "read_cache_size_mb", &vtp->read_cache_size_mb))
|
||||
return _bad_field("read_cache_size_mb");
|
||||
|
||||
if (!dm_config_get_uint32(n, "slab_size_mb", &vtp->slab_size_mb))
|
||||
return _bad_field("slab_size_mb");
|
||||
|
||||
if (!dm_config_get_uint32(n, "ack_threads", &vtp->ack_threads))
|
||||
return _bad_field("ack_threads");
|
||||
|
||||
if (!dm_config_get_uint32(n, "bio_threads", &vtp->bio_threads))
|
||||
return _bad_field("bio_threads");
|
||||
|
||||
if (!dm_config_get_uint32(n, "bio_rotation", &vtp->bio_rotation))
|
||||
return _bad_field("bio_rotation");
|
||||
|
||||
if (!dm_config_get_uint32(n, "cpu_threads", &vtp->cpu_threads))
|
||||
return _bad_field("cpu_threads");
|
||||
|
||||
if (!dm_config_get_uint32(n, "hash_zone_threads", &vtp->hash_zone_threads))
|
||||
return _bad_field("hash_zone_threads");
|
||||
|
||||
if (!dm_config_get_uint32(n, "logical_threads", &vtp->logical_threads))
|
||||
return _bad_field("logical_threads");
|
||||
|
||||
if (!dm_config_get_uint32(n, "physical_threads", &vtp->physical_threads))
|
||||
return _bad_field("physical_threads");
|
||||
|
||||
|
||||
if (!set_lv_segment_area_lv(seg, 0, data_lv, 0, LV_VDO_POOL_DATA))
|
||||
return_0;
|
||||
|
||||
seg->lv->status |= LV_VDO_POOL;
|
||||
lv_set_hidden(data_lv);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vdo_pool_text_export(const struct lv_segment *seg, struct formatter *f)
|
||||
{
|
||||
const struct dm_vdo_target_params *vtp = &seg->vdo_params;
|
||||
|
||||
outf(f, "data = \"%s\"", seg_lv(seg, 0)->name);
|
||||
outsize(f, seg->vdo_pool_header_size, "header_size = %u\t",
|
||||
seg->vdo_pool_header_size);
|
||||
outsize(f, seg->vdo_pool_virtual_extents * (uint64_t) seg->lv->vg->extent_size,
|
||||
"virtual_extents = %u\t", seg->vdo_pool_virtual_extents);
|
||||
|
||||
outnl(f);
|
||||
|
||||
if (vtp->use_compression)
|
||||
outf(f, "use_compression = 1");
|
||||
if (vtp->use_deduplication)
|
||||
outf(f, "use_deduplication = 1");
|
||||
if (vtp->emulate_512_sectors)
|
||||
outf(f, "emulate_512_sectors = 1");
|
||||
|
||||
outsize(f, vtp->block_map_cache_size_mb * UINT64_C(2 * 1024),
|
||||
"block_map_cache_size_mb = %u", vtp->block_map_cache_size_mb);
|
||||
outf(f, "block_map_period = %u", vtp->block_map_period);
|
||||
|
||||
if (vtp->use_sparse_index)
|
||||
outf(f, "use_sparse_index = 1");
|
||||
// TODO - conditionally
|
||||
outsize(f, vtp->index_memory_size_mb * UINT64_C(2 * 1024),
|
||||
"index_memory_size_mb = %u", vtp->index_memory_size_mb);
|
||||
|
||||
if (vtp->use_read_cache)
|
||||
outf(f, "use_read_cache = 1");
|
||||
// TODO - conditionally
|
||||
outsize(f, vtp->read_cache_size_mb * UINT64_C(2 * 1024),
|
||||
"read_cache_size_mb = %u", vtp->read_cache_size_mb);
|
||||
outsize(f, vtp->slab_size_mb * UINT64_C(2 * 1024),
|
||||
"slab_size_mb = %u", vtp->slab_size_mb);
|
||||
outf(f, "ack_threads = %u", (unsigned) vtp->ack_threads);
|
||||
outf(f, "bio_threads = %u", (unsigned) vtp->bio_threads);
|
||||
outf(f, "bio_rotation = %u", (unsigned) vtp->bio_rotation);
|
||||
outf(f, "cpu_threads = %u", (unsigned) vtp->cpu_threads);
|
||||
outf(f, "hash_zone_threads = %u", (unsigned) vtp->hash_zone_threads);
|
||||
outf(f, "logical_threads = %u", (unsigned) vtp->logical_threads);
|
||||
outf(f, "physical_threads = %u", (unsigned) vtp->physical_threads);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
static int _vdo_pool_target_status_compatible(const char *type)
|
||||
{
|
||||
return (strcmp(type, TARGET_NAME_VDO) == 0);
|
||||
}
|
||||
|
||||
static int _vdo_pool_add_target_line(struct dev_manager *dm,
|
||||
struct dm_pool *mem __attribute__((unused)),
|
||||
struct cmd_context *cmd __attribute__((unused)),
|
||||
void **target_state __attribute__((unused)),
|
||||
struct lv_segment *seg,
|
||||
const struct lv_activate_opts *laopts __attribute__((unused)),
|
||||
struct dm_tree_node *node, uint64_t len,
|
||||
uint32_t *pvmove_mirror_count __attribute__((unused)))
|
||||
{
|
||||
char *data_uuid;
|
||||
|
||||
if (!seg_is_vdo_pool(seg)) {
|
||||
log_error(INTERNAL_ERROR "Passed segment is not VDO pool.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(data_uuid = build_dm_uuid(mem, seg_lv(seg, 0), lv_layer(seg_lv(seg, 0)))))
|
||||
return_0;
|
||||
|
||||
/* VDO uses virtual size instead of its physical size */
|
||||
if (!dm_tree_node_add_vdo_target(node, get_vdo_pool_virtual_size(seg),
|
||||
data_uuid, &seg->vdo_params))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _vdo_target_present(struct cmd_context *cmd,
|
||||
const struct lv_segment *seg __attribute__((unused)),
|
||||
unsigned *attributes __attribute__((unused)))
|
||||
{
|
||||
/* List of features with their kernel target version */
|
||||
static const struct feature {
|
||||
uint32_t maj;
|
||||
uint32_t min;
|
||||
unsigned vdo_feature;
|
||||
const char *feature;
|
||||
} _features[] = {
|
||||
{ 1, 1, 0, "" },
|
||||
//{ 9, 9, VDO_FEATURE_RESIZE, "resize" },
|
||||
};
|
||||
//static const char _lvmconf[] = "global/vdo_disabled_features";
|
||||
static int _vdo_checked = 0;
|
||||
static int _vdo_present = 0;
|
||||
static unsigned _vdo_attrs = 0;
|
||||
uint32_t i, maj, min, patchlevel;
|
||||
const struct segment_type *segtype;
|
||||
|
||||
if (!activation())
|
||||
return 0;
|
||||
|
||||
if (!_vdo_checked) {
|
||||
_vdo_checked = 1;
|
||||
|
||||
if (!target_present_version(cmd, TARGET_NAME_VDO, 0,
|
||||
&maj, &min, &patchlevel)) {
|
||||
/* Try to load kmod VDO module */
|
||||
if (!module_present(cmd, MODULE_NAME_VDO) ||
|
||||
!target_version(TARGET_NAME_VDO, &maj, &min, &patchlevel))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (maj < 6 || (maj == 6 && min < 2)) {
|
||||
log_warn("WARNING: VDO target version %u.%u.%u is too old.",
|
||||
maj, min, patchlevel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If stripe target was already detected, reuse its result */
|
||||
if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)) ||
|
||||
!segtype->ops->target_present || !segtype->ops->target_present(cmd, NULL, NULL)) {
|
||||
/* Linear/Stripe targer is for mapping LVs on top of single VDO volume. */
|
||||
if (!target_present(cmd, TARGET_NAME_LINEAR, 0) ||
|
||||
!target_present(cmd, TARGET_NAME_STRIPED, 0))
|
||||
return 0;
|
||||
}
|
||||
|
||||
_vdo_present = 1;
|
||||
/* Prepare for adding supported features */
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i)
|
||||
if ((maj > _features[i].maj) ||
|
||||
(maj == _features[i].maj && min >= _features[i].min))
|
||||
_vdo_attrs |= _features[i].vdo_feature;
|
||||
else
|
||||
log_very_verbose("Target %s does not support %s.",
|
||||
TARGET_NAME_VDO,
|
||||
_features[i].feature);
|
||||
}
|
||||
|
||||
if (attributes) {
|
||||
*attributes = _vdo_attrs & _feature_mask;
|
||||
}
|
||||
|
||||
return _vdo_present;
|
||||
}
|
||||
|
||||
static int _vdo_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_VDO)) {
|
||||
log_error("String list allocation failed for VDO module.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* reused as _vdo_destroy */
|
||||
static void _vdo_pool_destroy(struct segment_type *segtype)
|
||||
{
|
||||
free((void *)segtype->dso);
|
||||
free((void *)segtype);
|
||||
}
|
||||
|
||||
static struct segtype_handler _vdo_ops = {
|
||||
.name = _vdo_name,
|
||||
.text_import = _vdo_text_import,
|
||||
.text_import_area_count = _vdo_pool_text_import_area_count,
|
||||
.text_export = _vdo_text_export,
|
||||
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
.target_status_compatible = _vdo_target_status_compatible,
|
||||
.add_target_line = _vdo_add_target_line,
|
||||
.target_present = _vdo_target_present,
|
||||
.modules_needed = _vdo_modules_needed,
|
||||
#endif
|
||||
.destroy = _vdo_pool_destroy,
|
||||
};
|
||||
|
||||
static struct segtype_handler _vdo_pool_ops = {
|
||||
.name = _vdo_pool_name,
|
||||
.text_import = _vdo_pool_text_import,
|
||||
.text_import_area_count = _vdo_pool_text_import_area_count,
|
||||
.text_export = _vdo_pool_text_export,
|
||||
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
.target_status_compatible = _vdo_pool_target_status_compatible,
|
||||
.add_target_line = _vdo_pool_add_target_line,
|
||||
.target_present = _vdo_target_present,
|
||||
.modules_needed = _vdo_modules_needed,
|
||||
#endif
|
||||
.destroy = _vdo_pool_destroy,
|
||||
};
|
||||
|
||||
int init_vdo_segtypes(struct cmd_context *cmd,
|
||||
struct segtype_library *seglib)
|
||||
{
|
||||
struct segment_type *segtype, *pool_segtype;
|
||||
|
||||
if (!(segtype = zalloc(sizeof(*segtype))) ||
|
||||
!(pool_segtype = zalloc(sizeof(*segtype)))) {
|
||||
log_error("Failed to allocate memory for VDO segtypes.");
|
||||
free(segtype);
|
||||
return 0;
|
||||
}
|
||||
|
||||
segtype->name = SEG_TYPE_NAME_VDO;
|
||||
segtype->flags = SEG_VDO | SEG_VIRTUAL | SEG_ONLY_EXCLUSIVE;
|
||||
segtype->ops = &_vdo_ops;
|
||||
|
||||
if (!lvm_register_segtype(seglib, segtype)) {
|
||||
free(pool_segtype);
|
||||
return_0;
|
||||
}
|
||||
|
||||
pool_segtype->name = SEG_TYPE_NAME_VDO_POOL;
|
||||
pool_segtype->flags = SEG_VDO_POOL | SEG_ONLY_EXCLUSIVE;
|
||||
pool_segtype->ops = &_vdo_pool_ops;
|
||||
|
||||
if (!lvm_register_segtype(seglib, pool_segtype))
|
||||
return_0;
|
||||
|
||||
log_very_verbose("Initialised segtypes: %s, %s.", segtype->name, pool_segtype->name);
|
||||
|
||||
/* Reset mask for recalc */
|
||||
_feature_mask = 0;
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Reference in New Issue
Block a user