1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/metadata/pool_manip.c

885 lines
24 KiB
C
Raw Normal View History

/*
* Copyright (C) 2013-2014 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
*/
/*
* This file holds common pool functions.
*/
#include "lib/misc/lib.h"
#include "lib/activate/activate.h"
#include "lib/locking/locking.h"
#include "lib/metadata/metadata.h"
#include "lib/metadata/segtype.h"
#include "lib/metadata/lv_alloc.h"
#include "lib/config/defaults.h"
#include "lib/device/dev-type.h"
#include "lib/display/display.h"
#include "lib/commands/toolcontext.h"
#include <stddef.h>
int attach_pool_metadata_lv(struct lv_segment *pool_seg,
struct logical_volume *metadata_lv)
{
if (!seg_is_pool(pool_seg)) {
log_error(INTERNAL_ERROR
"Unable to attach pool metadata LV to %s segtype.",
lvseg_name(pool_seg));
return 0;
}
pool_seg->metadata_lv = metadata_lv;
metadata_lv->status |= seg_is_thin_pool(pool_seg) ?
THIN_POOL_METADATA : CACHE_POOL_METADATA;
lv_set_hidden(metadata_lv);
return add_seg_to_segs_using_this_lv(metadata_lv, pool_seg);
}
int detach_pool_metadata_lv(struct lv_segment *pool_seg, struct logical_volume **metadata_lv)
{
struct logical_volume *lv = pool_seg->metadata_lv;
if (!lv ||
!lv_is_pool_metadata(lv) ||
!remove_seg_from_segs_using_this_lv(lv, pool_seg)) {
log_error(INTERNAL_ERROR "Logical volume %s is not valid pool.",
display_lvname(pool_seg->lv));
return 0;
}
lv_set_visible(lv);
lv->status &= ~(THIN_POOL_METADATA | CACHE_POOL_METADATA);
*metadata_lv = lv;
pool_seg->metadata_lv = NULL;
return 1;
}
int attach_pool_data_lv(struct lv_segment *pool_seg,
struct logical_volume *pool_data_lv)
{
if (!seg_is_pool(pool_seg)) {
log_error(INTERNAL_ERROR
2014-01-23 09:04:27 +04:00
"Unable to attach pool data LV to %s segtype.",
lvseg_name(pool_seg));
return 0;
}
if (!set_lv_segment_area_lv(pool_seg, 0, pool_data_lv,
0, seg_is_thin_pool(pool_seg) ?
THIN_POOL_DATA : CACHE_POOL_DATA))
return_0;
pool_seg->lv->status |= seg_is_thin_pool(pool_seg) ?
THIN_POOL : CACHE_POOL;
lv_set_hidden(pool_data_lv);
return 1;
}
int attach_pool_lv(struct lv_segment *seg,
struct logical_volume *pool_lv,
struct logical_volume *origin,
struct generic_logical_volume *indirect_origin,
struct logical_volume *merge_lv)
{
struct glv_list *glvl;
if (!seg_is_thin_volume(seg) && !seg_is_cache(seg)) {
log_error(INTERNAL_ERROR "Unable to attach pool to %s/%s"
" that is not cache or thin volume.",
pool_lv->vg->name, seg->lv->name);
return 0;
}
seg->pool_lv = pool_lv;
seg->origin = origin;
seg->lv->status |= seg_is_cache(seg) ? CACHE : THIN_VOLUME;
if (seg_is_cache(seg)) {
lv_set_hidden(pool_lv); /* Used cache-pool/cachevol is hidden */
if (lv_is_cache_vol(pool_lv))
/*
* This flag is added to the segtype name so that old versions of lvm
* (if they happen to be used with new metadata with a cache LV using a
* cachevol) will report an error when they see the unknown
* cache+CACHE_USES_CACHEVOL segment type. Otherwise the old version
* would expect to find a cache pool and fail.
*/
seg->lv->status |= LV_CACHE_USES_CACHEVOL;
}
if (origin && !add_seg_to_segs_using_this_lv(origin, seg))
return_0;
if (indirect_origin) {
if (!(glvl = get_or_create_glvl(seg->lv->vg->vgmem, seg->lv, NULL)))
return_0;
seg->indirect_origin = indirect_origin;
if (indirect_origin->is_historical)
dm_list_add(&indirect_origin->historical->indirect_glvs, &glvl->list);
else
dm_list_add(&indirect_origin->live->indirect_glvs, &glvl->list);
}
if (!add_seg_to_segs_using_this_lv(pool_lv, seg))
return_0;
if (merge_lv) {
if (origin != merge_lv) {
if (!add_seg_to_segs_using_this_lv(merge_lv, seg))
return_0;
}
init_snapshot_merge(seg, merge_lv);
}
return 1;
}
static struct glv_list *_init_historical_glvl(struct dm_pool *mem, struct lv_segment *seg)
{
struct glv_list *glvl;
struct historical_logical_volume *hlv;
if (!(glvl = dm_pool_zalloc(mem, sizeof(struct glv_list))))
goto_bad;
if (!(glvl->glv = dm_pool_zalloc(mem, sizeof(struct generic_logical_volume))))
goto_bad;
if (!(hlv = dm_pool_zalloc(mem, sizeof(struct historical_logical_volume))))
goto_bad;
hlv->lvid = seg->lv->lvid;
hlv->name = seg->lv->name;
hlv->vg = seg->lv->vg;
hlv->timestamp = seg->lv->timestamp;
toollib: do not process just created historical LV When executing process_each_lv_in_vg, we process live LVs first and after that, we process any historical LVs. In case we have just removed an LV, which also means we have just made it "historical" and so it appears as fresh item in vg->historical_lvs list, we have to skip it when we get to processing historical LVs inside the same process_each_lv_in_vg call. The simplest approach here, without introducing another LV list, is to simply mark such historical LVs as "fresh" directly in struct historical_logical_volume when we have just removed the original LV and created the historical LV for it. Then, we just need to check the flag when processing historical LVs and skip it if it is "fresh". When we read historical LVs out of metadata, they are marked as "not fresh" and so they can be processed as usual. This was mainly an issue in conjuction with -S|--select use: # lvmconfig --type diff metadata { record_lvs_history=1 } (In this example, a thin pool with lvol1 thin LV and lvol2 and lvol3 snapshots.) # lvs -H vg -o name,pool_lv,full_ancestors,full_descendants LV Pool FAncestors FDescendants lvol1 pool lvol2,lvol3 lvol2 pool lvol1 lvol3 lvol3 pool lvol2,lvol1 pool # lvremove -S 'name=lvol2' Logical volume "lvol2" successfully removed. Historical logical volume "lvol2" successfully removed. ...here, the historical LV lvol2 should not have been removed because we have just removed its original non-historical lvol2 and the fresh historical lvol2 must not be included in the same processing spree.
2022-10-12 15:41:58 +03:00
hlv->fresh = 1;
dm_list_init(&hlv->indirect_glvs);
glvl->glv->is_historical = 1;
glvl->glv->historical = hlv;
return glvl;
bad:
log_error("Initialization of historical LV representation for removed logical "
2021-03-09 15:13:42 +03:00
"volume %s failed.", display_lvname(seg->lv));
if (glvl)
dm_pool_free(mem, glvl);
return NULL;
}
static struct generic_logical_volume *_create_historical_glv(struct lv_segment *seg_to_remove)
{
struct dm_pool *mem = seg_to_remove->lv->vg->vgmem;
struct generic_logical_volume *historical_glv, *origin_glv = NULL;
struct glv_list *historical_glvl;
int origin_glv_created = 0;
if (!(historical_glvl = _init_historical_glvl(mem, seg_to_remove)))
goto_bad;
historical_glv = historical_glvl->glv;
if (seg_to_remove->origin) {
if (!(origin_glv = get_or_create_glv(mem, seg_to_remove->origin, &origin_glv_created)))
goto_bad;
if (!add_glv_to_indirect_glvs(mem, origin_glv, historical_glv))
goto_bad;
} else if (seg_to_remove->indirect_origin) {
origin_glv = seg_to_remove->indirect_origin;
if (!remove_glv_from_indirect_glvs(origin_glv, seg_to_remove->lv->this_glv))
goto_bad;
if (!add_glv_to_indirect_glvs(mem, origin_glv, historical_glv))
goto_bad;
}
dm_list_add(&seg_to_remove->lv->vg->historical_lvs, &historical_glvl->list);
return historical_glvl->glv;
bad:
log_error("Failed to create historical LV representation for removed logical "
2021-03-09 15:13:42 +03:00
"volume %s.", display_lvname(seg_to_remove->lv));
if (origin_glv_created)
seg_to_remove->origin->this_glv = NULL;
if (historical_glvl)
dm_pool_free(mem, historical_glvl);
return NULL;
}
static int _set_up_historical_lv(struct lv_segment *seg_to_remove,
struct generic_logical_volume **previous_glv)
{
struct generic_logical_volume *glv = NULL;
if (seg_to_remove->lv->vg->cmd->record_historical_lvs) {
if (seg_to_remove->origin || seg_to_remove->indirect_origin ||
dm_list_size(&seg_to_remove->lv->segs_using_this_lv) ||
dm_list_size(&seg_to_remove->lv->indirect_glvs)) {
if (!(glv = _create_historical_glv(seg_to_remove)))
return_0;
}
} else {
if (seg_to_remove->indirect_origin &&
!remove_glv_from_indirect_glvs(seg_to_remove->indirect_origin,
seg_to_remove->lv->this_glv))
return_0;
}
*previous_glv = glv;
return 1;
}
int detach_pool_lv(struct lv_segment *seg)
{
struct generic_logical_volume *previous_glv = NULL, *glv, *user_glv;
struct glv_list *user_glvl, *tglvl;
struct lv_thin_message *tmsg, *tmp;
struct seg_list *sl, *tsl;
int no_update = 0;
if (!seg->pool_lv) {
log_error(INTERNAL_ERROR
"No pool associated with %s LV, %s.",
2021-03-09 15:13:42 +03:00
lvseg_name(seg), display_lvname(seg->lv));
return 0;
}
if (seg_is_cache(seg)) {
if (!remove_seg_from_segs_using_this_lv(seg->pool_lv, seg))
return_0;
seg->lv->status &= ~CACHE;
seg->lv->status &= ~LV_CACHE_USES_CACHEVOL;
lv_set_visible(seg->pool_lv);
seg->pool_lv->status &= ~LV_CACHE_VOL;
seg->pool_lv = NULL;
return 1;
}
if (!lv_is_thin_pool(seg->pool_lv)) {
2021-03-09 15:13:42 +03:00
log_error(INTERNAL_ERROR "Cannot detach pool from LV %s.",
display_lvname(seg->lv));
return 0;
}
/* Drop any message referencing removed segment */
dm_list_iterate_items_safe(tmsg, tmp, &(first_seg(seg->pool_lv)->thin_messages)) {
switch (tmsg->type) {
case DM_THIN_MESSAGE_CREATE_SNAP:
case DM_THIN_MESSAGE_CREATE_THIN:
if (tmsg->u.lv == seg->lv) {
log_debug_metadata("Discarding message for LV %s.",
2021-03-09 15:13:42 +03:00
display_lvname(tmsg->u.lv));
dm_list_del(&tmsg->list);
no_update = 1; /* Replacing existing */
}
break;
case DM_THIN_MESSAGE_DELETE:
if (tmsg->u.delete_id == seg->device_id) {
log_error(INTERNAL_ERROR "Trying to delete %u again.",
tmsg->u.delete_id);
return 0;
}
break;
default:
log_error(INTERNAL_ERROR "Unsupported message type %u.", tmsg->type);
break;
}
}
if (!_set_up_historical_lv(seg, &previous_glv))
return_0;
if (!detach_thin_external_origin(seg))
return_0;
if (seg->device_id && /* Only thins with device_id > 0 can be deleted */
!attach_thin_pool_message(first_seg(seg->pool_lv),
DM_THIN_MESSAGE_DELETE,
NULL, seg->device_id, no_update))
return_0;
if (!remove_seg_from_segs_using_this_lv(seg->pool_lv, seg))
return_0;
if (seg->origin &&
!remove_seg_from_segs_using_this_lv(seg->origin, seg))
return_0;
/* If thin origin, remove it from related thin snapshots */
/*
* TODO: map removal of origin as snapshot lvconvert --merge?
* i.e. rename thin snapshot to origin thin origin
*/
dm_list_iterate_items_safe(sl, tsl, &seg->lv->segs_using_this_lv) {
if (!seg_is_thin_volume(sl->seg) ||
(seg->lv != sl->seg->origin))
continue;
if (previous_glv) {
if (!(user_glv = get_or_create_glv(seg->lv->vg->vgmem, sl->seg->lv, NULL)))
return_0;
if (!add_glv_to_indirect_glvs(seg->lv->vg->vgmem, previous_glv, user_glv))
return_0;
}
if (!remove_seg_from_segs_using_this_lv(seg->lv, sl->seg))
return_0;
/* Thin snapshot is now regular thin volume */
sl->seg->origin = NULL;
}
dm_list_iterate_items_safe(user_glvl, tglvl, &seg->lv->indirect_glvs) {
user_glv = user_glvl->glv;
if (!(glv = get_or_create_glv(seg->lv->vg->vgmem, seg->lv, NULL)))
return_0;
if (!remove_glv_from_indirect_glvs(glv, user_glv))
return_0;
if (previous_glv) {
if (!add_glv_to_indirect_glvs(seg->lv->vg->vgmem, previous_glv, user_glv))
return_0;
}
}
seg->lv->status &= ~THIN_VOLUME;
seg->pool_lv = NULL;
seg->origin = NULL;
seg->indirect_origin = NULL;
return 1;
}
struct lv_segment *find_pool_seg(const struct lv_segment *seg)
{
2014-11-11 15:31:25 +03:00
struct lv_segment *pool_seg = NULL;
struct seg_list *sl;
2014-11-11 15:31:25 +03:00
dm_list_iterate_items(sl, &seg->lv->segs_using_this_lv) {
/* Needs to be he only item in list */
if (lv_is_pending_delete(sl->seg->lv))
continue;
if (pool_seg) {
log_error("%s is referenced by more then one segments (%s, %s).",
display_lvname(seg->lv), display_lvname(pool_seg->lv),
display_lvname(sl->seg->lv));
return NULL; /* More then one segment */
}
pool_seg = sl->seg;
}
if (!pool_seg) {
log_error("Pool segment not found for %s.", display_lvname(seg->lv));
return NULL;
}
if ((lv_is_thin_type(seg->lv) && !seg_is_pool(pool_seg))) {
log_error("%s on %s is not a %s pool segment",
2021-03-09 15:13:42 +03:00
display_lvname(pool_seg->lv), display_lvname(seg->lv),
lv_is_thin_type(seg->lv) ? "thin" : "cache");
return NULL;
}
return pool_seg;
}
int validate_pool_chunk_size(struct cmd_context *cmd,
const struct segment_type *segtype,
uint32_t chunk_size)
{
if (segtype_is_cache(segtype) || segtype_is_cache_pool(segtype))
return validate_cache_chunk_size(cmd, chunk_size);
return validate_thin_pool_chunk_size(cmd, chunk_size);
}
int recalculate_pool_chunk_size_with_dev_hints(struct logical_volume *pool_lv,
struct logical_volume *pool_data_lv,
int chunk_size_calc_policy)
{
struct lv_segment *seg;
struct physical_volume *pv;
struct cmd_context *cmd = pool_lv->vg->cmd;
unsigned long previous_hint = 0, hint = 0;
uint32_t min_chunk_size, max_chunk_size;
if (!chunk_size_calc_policy)
return 1; /* Chunk size was specified by user */
if (lv_is_thin_pool(pool_lv)) {
min_chunk_size = DM_THIN_MIN_DATA_BLOCK_SIZE;
max_chunk_size = DM_THIN_MAX_DATA_BLOCK_SIZE;
} else if (lv_is_cache_pool(pool_lv)) {
min_chunk_size = DM_CACHE_MIN_DATA_BLOCK_SIZE;
max_chunk_size = DM_CACHE_MAX_DATA_BLOCK_SIZE;
} else {
log_error(INTERNAL_ERROR "%s is not a pool logical volume.", display_lvname(pool_lv));
return 0;
}
dm_list_iterate_items(seg, &pool_data_lv->segments) {
switch (seg_type(seg, 0)) {
case AREA_PV:
pv = seg_pv(seg, 0);
if (chunk_size_calc_policy == THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE)
hint = dev_optimal_io_size(cmd->dev_types, pv_dev(pv));
else
hint = dev_minimum_io_size(cmd->dev_types, pv_dev(pv));
if (!hint)
continue;
if (previous_hint)
hint = lcm(previous_hint, hint);
previous_hint = hint;
break;
case AREA_LV:
/* FIXME: hint for stacked (raid) LVs - estimate geometry from LV ?? */
default:
break;
}
}
if (!hint)
log_debug_alloc("No usable device hint found while recalculating "
"pool chunk size for %s.", display_lvname(pool_lv));
else if ((hint < min_chunk_size) || (hint > max_chunk_size))
log_debug_alloc("Calculated chunk size %s for pool %s "
"is out of allowed range (%s-%s).",
display_size(cmd, hint), display_lvname(pool_lv),
display_size(cmd, min_chunk_size),
display_size(cmd, max_chunk_size));
else if (hint > first_seg(pool_lv)->chunk_size) {
log_debug_alloc("Updating chunk size %s for pool %s to %s.",
display_size(cmd, first_seg(pool_lv)->chunk_size),
display_lvname(pool_lv),
display_size(cmd, hint));
first_seg(pool_lv)->chunk_size = hint;
}
return 1;
}
int create_pool(struct logical_volume *pool_lv,
const struct segment_type *segtype,
struct alloc_handle *ah, uint32_t stripes, uint32_t stripe_size)
{
const struct segment_type *striped;
struct logical_volume *meta_lv, *data_lv;
struct lv_segment *seg;
char name[NAME_LEN];
int r;
if (pool_lv->le_count) {
log_error(INTERNAL_ERROR "Pool %s already has extents.",
2021-03-09 15:13:42 +03:00
display_lvname(pool_lv));
return 0;
}
if (dm_snprintf(name, sizeof(name), "%s_%s", pool_lv->name,
(segtype_is_cache_pool(segtype)) ?
"cmeta" : "tmeta") < 0) {
log_error("Name of logical volume %s is too long to be a pool name.",
display_lvname(pool_lv));
return 0;
}
/* LV is not yet a pool, so it's extension from lvcreate */
if (!(striped = get_segtype_from_string(pool_lv->vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
if (activation() && striped->ops->target_present &&
!striped->ops->target_present(pool_lv->vg->cmd, NULL, NULL)) {
log_error("%s: Required device-mapper target(s) not "
"detected in your kernel.", striped->name);
return 0;
}
/* Metadata segment */
if (!lv_add_segment(ah, stripes, 1, pool_lv, striped, 1, 0, 0))
return_0;
if (!activation())
log_warn("WARNING: Pool %s is created without initialization.",
2021-03-09 15:13:42 +03:00
display_lvname(pool_lv));
else if (!test_mode()) {
if (!vg_write(pool_lv->vg) || !vg_commit(pool_lv->vg))
return_0;
/*
* If killed here, only the VISIBLE striped pool LV is left
* and user could easily remove it.
*
* FIXME: implement lazy clearing when activation is disabled
*/
/*
* pool_lv is a new LV so the VG lock protects us
* Pass in LV_TEMPORARY flag, since device is activated purely for wipe
* and later it is either deactivated (in cluster)
* or directly converted to invisible device via suspend/resume
*/
pool_lv->status |= LV_TEMPORARY;
if (!activate_lv(pool_lv->vg->cmd, pool_lv)) {
log_error("Aborting. Failed to activate pool metadata %s.",
display_lvname(pool_lv));
goto bad;
}
/* Clear pool metadata device. */
if (!(r = wipe_lv(pool_lv, (struct wipe_params) { .is_metadata = 1 }))) {
log_error("Aborting. Failed to wipe pool metadata %s.",
display_lvname(pool_lv));
}
2014-01-24 16:13:37 +04:00
pool_lv->status &= ~LV_TEMPORARY;
/* Deactivates cleared metadata LV */
if (!deactivate_lv(pool_lv->vg->cmd, pool_lv)) {
log_error("Aborting. Could not deactivate pool metadata %s.",
display_lvname(pool_lv));
return 0;
}
if (!r)
goto bad;
}
if (!(meta_lv = lv_create_empty(name, NULL, LVM_READ | LVM_WRITE,
ALLOC_INHERIT, pool_lv->vg)))
goto_bad;
if (!move_lv_segments(meta_lv, pool_lv, 0, 0))
goto_bad;
/* Pool data segment */
if (!lv_add_segment(ah, 0, stripes, pool_lv, striped, stripe_size, 0, 0))
goto_bad;
if (!(data_lv = insert_layer_for_lv(pool_lv->vg->cmd, pool_lv,
pool_lv->status,
(segtype_is_cache_pool(segtype)) ?
"_cdata" : "_tdata")))
goto_bad;
seg = first_seg(pool_lv);
/* Drop reference as attach_pool_data_lv() takes it again */
if (!remove_seg_from_segs_using_this_lv(data_lv, seg))
goto_bad;
seg->segtype = segtype; /* Set as thin_pool or cache_pool segment */
if (!attach_pool_data_lv(seg, data_lv))
goto_bad;
if (!attach_pool_metadata_lv(seg, meta_lv))
goto_bad;
return 1;
bad:
if (activation()) {
/* Without activation there was no intermediate commit */
if (!lv_remove(pool_lv) ||
!vg_write(pool_lv->vg) || !vg_commit(pool_lv->vg))
log_error("Manual intervention may be required to "
"remove abandoned LV(s) before retrying.");
}
return 0;
}
struct logical_volume *alloc_pool_metadata(struct logical_volume *pool_lv,
uint32_t read_ahead,
uint32_t stripes, uint32_t stripe_size,
uint32_t extents, alloc_policy_t alloc,
struct dm_list *pvh)
{
struct logical_volume *metadata_lv;
/* FIXME: Make lvm2api usable */
struct lvcreate_params lvc = {
.activate = CHANGE_ALY,
.alloc = alloc,
.extents = extents,
.major = -1,
.minor = -1,
.permission = LVM_READ | LVM_WRITE,
.pvh = pvh,
.read_ahead = read_ahead,
.stripe_size = stripe_size,
.stripes = stripes,
2014-10-30 15:04:41 +03:00
.tags = DM_LIST_HEAD_INIT(lvc.tags),
.temporary = 1,
2014-10-30 15:04:41 +03:00
.zero = 1,
.is_metadata = 1,
.lv_name = "pool_metadata%d",
};
if (!(lvc.segtype = get_segtype_from_string(pool_lv->vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
/* FIXME: allocate properly space for metadata_lv */
if (!(metadata_lv = lv_create_single(pool_lv->vg, &lvc)))
return_0;
return metadata_lv;
}
static struct logical_volume *_alloc_pool_metadata_spare(struct volume_group *vg,
uint32_t extents,
struct dm_list *pvh)
{
struct logical_volume *lv;
/* FIXME: Make lvm2api usable */
struct lvcreate_params lp = {
.activate = CHANGE_ALY,
.alloc = ALLOC_INHERIT,
.extents = extents,
.major = -1,
.minor = -1,
.permission = LVM_READ | LVM_WRITE,
.pvh = pvh ? : &vg->pvs,
.read_ahead = DM_READ_AHEAD_AUTO,
.stripes = 1,
2014-10-30 15:04:41 +03:00
.tags = DM_LIST_HEAD_INIT(lp.tags),
.temporary = 1,
2014-10-30 15:04:41 +03:00
.zero = 1,
.is_metadata = 1,
};
if (!(lp.segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
/* FIXME: Maybe using silent mode ? */
2014-10-20 17:02:59 +04:00
log_verbose("Preparing pool metadata spare volume for Volume group %s.", vg->name);
if (!(lv = lv_create_single(vg, &lp)))
return_0;
/* Spare LV should not be active */
if (!deactivate_lv(vg->cmd, lv)) {
log_error("Unable to deactivate pool metadata spare LV. "
"Manual intervention required.");
return 0;
}
if (!vg_set_pool_metadata_spare(lv))
return_0;
return lv;
}
/*
* Create/resize pool metadata spare LV
* Caller does vg_write(), vg_commit() with pool creation
* extents is 0, max size is determined
*/
int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents,
struct dm_list *pvh, int poolmetadataspare)
{
/* Max usable size of any spare volume is currently 16GiB rouned to extent size */
const uint64_t MAX_SIZE = (UINT64_C(2 * 16) * 1024 * 1024 + vg->extent_size - 1) / vg->extent_size;
struct logical_volume *lv = vg->pool_metadata_spare_lv;
uint32_t seg_mirrors;
struct lv_segment *seg;
const struct lv_list *lvl;
if (!extents)
/* Find maximal size of metadata LV */
dm_list_iterate_items(lvl, &vg->lvs)
if (lv_is_pool_metadata(lvl->lv) &&
(lvl->lv->le_count > extents)) {
extents = lvl->lv->le_count;
if (extents >= MAX_SIZE)
break;
}
if (!poolmetadataspare) {
/* TODO: Not showing when lvm.conf would define 'n' ? */
if (DEFAULT_POOL_METADATA_SPARE && extents)
/* Warn if there would be any user */
log_warn("WARNING: recovery of pools without pool "
"metadata spare LV is not automated.");
return 1;
}
if (!extents) {
/* pmspare is not needed */
if (lv) {
log_debug_metadata("Dropping unused pool metadata spare LV %s.",
display_lvname(lv));
if (!lv_remove_single(vg->cmd, lv, DONT_PROMPT, 0))
return_0;
}
return 1;
}
if (extents > MAX_SIZE)
extents = MAX_SIZE;
if (!lv) {
log_debug("Adding new pool metadata spare %u extents.", extents);
if (!_alloc_pool_metadata_spare(vg, extents, pvh))
return_0;
return 1;
}
seg = last_seg(lv);
seg_mirrors = lv_mirror_count(lv);
log_debug("Extending pool metadata spare from %u to %u extents.",
lv->le_count, extents);
/* Check spare LV is big enough and preserve segtype */
if ((lv->le_count < extents) && seg &&
!lv_extend(lv, seg->segtype,
seg->area_count / seg_mirrors,
seg->stripe_size,
seg_mirrors,
seg->region_size,
extents - lv->le_count,
pvh, lv->alloc, 0))
return_0;
return 1;
}
thin: improve 16g support for thin pool metadata Initial support for thin-pool used slightly smaller max size 15.81GiB for thin-pool metadata. However the real limit later settled at 15.88GiB (difference is ~64MiB - 16448 4K blocks). lvm2 could not simply increase the size as it has been using hard cropping of the loaded metadata device to avoid warnings printing warning of kernel when the size was bigger (i.e. due to bigger extent_size). This patch adds the new lvm.conf configurable setting: allocation/thin_pool_crop_metadata which defaults to 0 -> no crop of metadata beyond 15.81GiB. Only user with these sizes of metadata will be affected. Without cropping lvm2 now limits metadata allocation size to 15.88GiB. Any space beyond is currently not used by thin-pool target. Even if i.e. bigger LV is used for metadata via lvconvert, or allocated bigger because of to large extent size. With cropping enabled (=1) lvm2 preserves the old limitation 15.81GiB and should allow to work in the evironement with older lvm2 tools (i.e. older distribution). Thin-pool metadata with size bigger then 15.81G is now using CROP_METADATA flag within lvm2 metadata, so older lvm2 recognizes an incompatible thin-pool and cannot activate such pool! Users should use uncropped version as it is not suffering from various issues between thin_repair results and allocated metadata LV as thin_repair limit is 15.88GiB Users should use cropping only when really needed! Patch also better handles resize of thin-pool metadata and prevents resize beoyond usable size 15.88GiB. Resize beyond 15.81GiB automatically switches pool to no-crop version. Even with existing bigger thin-pool metadata command 'lvextend -l+1 vg/pool_tmeta' does the change. Patch gives better controls 'coverted' metadata LV and reports less confusing message during conversion. Patch set also moves the code for updating min/max into pool_manip.c for better sharing with cache_pool code.
2021-01-12 19:59:29 +03:00
int update_pool_metadata_min_max(struct cmd_context *cmd,
uint32_t extent_size,
uint64_t min_metadata_size, /* required min */
uint64_t max_metadata_size, /* writable max */
uint64_t *metadata_size, /* current calculated */
struct logical_volume *metadata_lv, /* name of converted LV or NULL */
uint32_t *metadata_extents) /* resulting extent count */
{
max_metadata_size = dm_round_up(max_metadata_size, extent_size);
min_metadata_size = dm_round_up(min_metadata_size, extent_size);
if (*metadata_size > max_metadata_size) {
if (metadata_lv) {
log_print_unless_silent("Size %s of pool metadata volume %s is bigger then maximum usable size %s.",
display_size(cmd, *metadata_size),
display_lvname(metadata_lv),
display_size(cmd, max_metadata_size));
} else {
if (*metadata_extents)
log_print_unless_silent("Reducing pool metadata size %s to maximum usable size %s.",
display_size(cmd, *metadata_size),
display_size(cmd, max_metadata_size));
*metadata_size = max_metadata_size;
}
} else if (*metadata_size < min_metadata_size) {
if (metadata_lv) {
log_error("Can't use volume %s with size %s as pool metadata. Minimal required size is %s.",
display_lvname(metadata_lv),
display_size(cmd, *metadata_size),
display_size(cmd, min_metadata_size));
return 0;
} else {
if (*metadata_extents)
log_print_unless_silent("Extending pool metadata size %s to required minimal size %s.",
display_size(cmd, *metadata_size),
display_size(cmd, min_metadata_size));
*metadata_size = min_metadata_size;
}
}
if (!(*metadata_extents = extents_from_size(cmd, *metadata_size, extent_size)))
return_0;
return 1;
}
int vg_set_pool_metadata_spare(struct logical_volume *lv)
{
char new_name[NAME_LEN];
struct volume_group *vg = lv->vg;
if (vg->pool_metadata_spare_lv) {
if (vg->pool_metadata_spare_lv == lv)
return 1;
if (!vg_remove_pool_metadata_spare(vg))
return_0;
}
if (dm_snprintf(new_name, sizeof(new_name), "%s_pmspare", lv->name) < 0) {
log_error("Can't create pool metadata spare. Name of pool LV "
"%s is too long.", lv->name);
return 0;
}
2014-10-20 17:02:59 +04:00
log_verbose("Renaming %s as pool metadata spare volume %s.", lv->name, new_name);
if (!lv_rename_update(vg->cmd, lv, new_name, 0))
return_0;
lv_set_hidden(lv);
lv->status |= POOL_METADATA_SPARE;
vg->pool_metadata_spare_lv = lv;
return 1;
}
int vg_remove_pool_metadata_spare(struct volume_group *vg)
{
char new_name[NAME_LEN];
char *c;
struct logical_volume *lv = vg->pool_metadata_spare_lv;
if (!(lv->status & POOL_METADATA_SPARE)) {
log_error(INTERNAL_ERROR "LV %s is not pool metadata spare.",
display_lvname(lv));
return 0;
}
vg->pool_metadata_spare_lv = NULL;
lv->status &= ~POOL_METADATA_SPARE;
lv_set_visible(lv);
/* Cut off suffix _pmspare */
if (!dm_strncpy(new_name, lv->name, sizeof(new_name)) ||
!(c = strchr(new_name, '_'))) {
log_error(INTERNAL_ERROR "LV %s has no suffix for pool metadata spare.",
display_lvname(lv));
return 0;
}
*c = 0;
/* If the name is in use, generate new lvol%d */
if (lv_name_is_used_in_vg(vg, new_name, NULL) &&
!generate_lv_name(vg, "lvol%d", new_name, sizeof(new_name))) {
log_error("Failed to generate unique name for "
"pool metadata spare logical volume.");
return 0;
}
log_print_unless_silent("Renaming existing pool metadata spare "
"logical volume \"%s\" to \"%s/%s\".",
2019-01-24 16:12:42 +03:00
display_lvname(lv), vg->name, new_name);
if (!lv_rename_update(vg->cmd, lv, new_name, 0))
return_0;
/* To display default warning */
(void) handle_pool_metadata_spare(vg, 0, 0, 0);
return 1;
}