diff options
author | Heinz Mauelshagen <heinzm@redhat.com> | 2017-02-24 02:39:04 +0100 |
---|---|---|
committer | Heinz Mauelshagen <heinzm@redhat.com> | 2017-02-24 05:20:58 +0100 |
commit | 929cf4b73ca6112e59e5e85cf3a2f3992747883b (patch) | |
tree | fe9fca8db4e4e1ec1943f1e55ae3c59be1df03c7 | |
parent | 4de0e692dba592206c8bb1f5000b4a6e445914a5 (diff) | |
download | lvm2-929cf4b73ca6112e59e5e85cf3a2f3992747883b.tar.gz |
lvconvert: add infrastructure for RaidLV reshaping support
In order to support striped raid5/6/10 LV reshaping (change
of LV type, stripesize or number of legs), this patch
introduces more local infrastructure to raid_manip.c
used by followup patches.
Changes:
- add vg metadata update functions
- add pre and post activation callback functions for
proper sequencing of sub lv activations during reshaping
- move and enhance _lv_update_reload_fns_reset_eliminate_lvs()
to support pre and post activation callbacks
- add _reset_flags_passed_to_kernel() which resets anyxi
rebuild/reshape flags after they have been passed into the kernel
and sets the SubLV remove after reshape flags on legs to be removed
Related: rhbz834579
Related: rhbz1191935
Related: rhbz1191978
-rw-r--r-- | lib/metadata/raid_manip.c | 345 |
1 files changed, 269 insertions, 76 deletions
diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c index 1cee8c330..63588ad5e 100644 --- a/lib/metadata/raid_manip.c +++ b/lib/metadata/raid_manip.c @@ -22,6 +22,11 @@ #include "lv_alloc.h" #include "lvm-string.h" +typedef int (*fn_on_lv_t)(struct logical_volume *lv, void *data); +static int _eliminate_extracted_lvs_optional_write_vg(struct volume_group *vg, + struct dm_list *removal_lvs, + int vg_write_requested); + static int _check_restriping(uint32_t new_stripes, struct logical_volume *lv) { if (new_stripes && new_stripes != first_seg(lv)->area_count) { @@ -404,6 +409,141 @@ static int _raid_remove_top_layer(struct logical_volume *lv, return 1; } +/* Reset any rebuild or reshape disk flags on @lv, first segment already passed to the kernel */ +static int _reset_flags_passed_to_kernel(struct logical_volume *lv, int *flags_reset) +{ + uint32_t lv_count = 0, s; + struct logical_volume *slv; + struct lv_segment *seg = first_seg(lv); + uint64_t reset_flags = LV_REBUILD | LV_RESHAPE_DELTA_DISKS_PLUS | LV_RESHAPE_DELTA_DISKS_MINUS; + + for (s = 0; s < seg->area_count; s++) { + if (seg_type(seg, s) == AREA_PV) + continue; + + if (!(slv = seg_lv(seg, s))) + return_0; + + /* Recurse into sub LVs */ + if (!_reset_flags_passed_to_kernel(slv, flags_reset)) + return 0; + + if (slv->status & LV_RESHAPE_DELTA_DISKS_MINUS) { + *flags_reset = 1; + slv->status |= LV_REMOVE_AFTER_RESHAPE; + seg_metalv(seg, s)->status |= LV_REMOVE_AFTER_RESHAPE; + } + + if (slv->status & reset_flags) { + *flags_reset = 1; + slv->status &= ~reset_flags; + } + + lv_count++; + } + + /* Reset passed in data offset (reshaping) */ + if (lv_count) + seg->data_offset = 0; + + return 1; +} + +/* + * HM Helper: + * + * Minimum 4 arguments! + * + * Updates and reloads metadata, clears any flags passed to the kernel, + * eliminates any residual LVs and updates and reloads metadata again. + * + * @lv mandatory argument, rest variable: + * + * @lv @origin_only @removal_lvs/NULL @fn_post_on_lv/NULL [ @fn_post_data/NULL [ @fn_post_on_lv/NULL @fn_post_data/NULL ] ] + * + * Run optional variable args function fn_post_on_lv with fn_post_data on @lv before second metadata update + * Run optional variable args function fn_pre_on_lv with fn_pre_data on @lv before first metadata update + * + * This minimaly involves 2 metadata commits or more, depending on + * pre and post functions carrying out any additional ones or not. + * + * WARNING: needs to be called with at least 4 arguments to suit va_list processing! + */ +static int _lv_update_reload_fns_reset_eliminate_lvs(struct logical_volume *lv, int origin_only, ...) +{ + int flags_reset = 0, r = 0; + va_list ap; + fn_on_lv_t fn_pre_on_lv = NULL, fn_post_on_lv; + void *fn_pre_data, *fn_post_data; + struct dm_list *removal_lvs; + + va_start(ap, origin_only); + removal_lvs = va_arg(ap, struct dm_list *); + + /* Retrieve post/pre functions and post/pre data reference from variable arguments, if any */ + if ((fn_post_on_lv = va_arg(ap, fn_on_lv_t))) { + fn_post_data = va_arg(ap, void *); + if ((fn_pre_on_lv = va_arg(ap, fn_on_lv_t))) + fn_pre_data = va_arg(ap, void *); + } + + /* Call any efn_pre_on_lv before the first update and reload call (e.g. to rename LVs) */ + if (fn_pre_on_lv && !(r = fn_pre_on_lv(lv, fn_pre_data))) { + log_error(INTERNAL_ERROR "Pre callout function failed."); + goto err; + } + + if (r == 2) { + /* + * Returning 2 from pre function -> lv is suspended and + * metadata got updated, don't need to do it again + */ + if (!(origin_only ? resume_lv_origin(lv->vg->cmd, lv_lock_holder(lv)) : + resume_lv(lv->vg->cmd, lv_lock_holder(lv)))) { + log_error("Failed to resume %s.", display_lvname(lv)); + goto err; + } + + /* Update metadata and reload mappings including flags (e.g. LV_REBUILD, LV_RESHAPE_DELTA_DISKS_PLUS) */ + } else if (!(origin_only ? lv_update_and_reload_origin(lv) : lv_update_and_reload(lv))) + goto err; + + /* Eliminate any residual LV and don't commit the metadata */ + if (!_eliminate_extracted_lvs_optional_write_vg(lv->vg, removal_lvs, 0)) + goto err; + + /* + * Now that any 'REBUILD' or 'RESHAPE_DELTA_DISKS' etc. + * has/have made its/their way to the kernel, we must + * remove the flag(s) so that the individual devices are + * not rebuilt/reshaped/taken over upon every activation. + * + * Writes and commits metadata if any flags have been reset + * and if successful, performs metadata backup. + */ + log_debug_metadata("Clearing any flags for %s passed to the kernel.", display_lvname(lv)); + if (!_reset_flags_passed_to_kernel(lv, &flags_reset)) + goto err; + + /* Call any @fn_post_on_lv before the second update call (e.g. to rename LVs back) */ + if (fn_post_on_lv && !(r = fn_post_on_lv(lv, fn_post_data))) { + log_error("Post callout function failed."); + goto err; + } + + /* Update and reload to clear out reset flags in the metadata and in the kernel */ + log_debug_metadata("Updating metadata mappings for %s.", display_lvname(lv)); + if ((r != 2 || flags_reset) && !(origin_only ? lv_update_and_reload_origin(lv) : lv_update_and_reload(lv))) { + log_error(INTERNAL_ERROR "Update of LV %s failed.", display_lvname(lv)); + goto err; + } + + r = 1; +err: + va_end(ap); + return r; +} + /* * Assisted excl_local activation of lvl listed LVs before resume * @@ -1852,6 +1992,131 @@ static int _raid_reshape_keep_images(struct logical_volume *lv, return 1; } +/* HM Helper: write, optionally suspend @lv (origin), commit and optionally backup metadata of @vg */ +static int _vg_write_lv_suspend_commit_backup(struct volume_group *vg, + struct logical_volume *lv, + int origin_only, int do_backup) +{ + int r = 1; + + if (!vg_write(vg)) { + log_error("Write of VG %s failed.", vg->name); + return_0; + } + + if (lv && !(r = (origin_only ? suspend_lv_origin(vg->cmd, lv_lock_holder(lv)) : + suspend_lv(vg->cmd, lv_lock_holder(lv))))) { + log_error("Failed to suspend %s before committing changes.", + display_lvname(lv)); + vg_revert(lv->vg); + } else if (!(r = vg_commit(vg))) + stack; /* !vg_commit() has implicit vg_revert() */ + + if (r && do_backup && !backup(vg)) + log_error("Backup of VG %s failed; continuing.", vg->name); + + return r; +} + +__attribute__ ((__unused__)) +static int _vg_write_commit_backup(struct volume_group *vg) +{ + return _vg_write_lv_suspend_commit_backup(vg, NULL, 1, 1); +} + +__attribute__ ((__unused__)) +static int _vg_write_commit(struct volume_group *vg) +{ + return _vg_write_lv_suspend_commit_backup(vg, NULL, 1, 0); +} + +/* Write vg of @lv, suspend @lv and commit the vg */ +static int _vg_write_lv_suspend_vg_commit(struct logical_volume *lv, int origin_only) +{ + return _vg_write_lv_suspend_commit_backup(lv->vg, lv, origin_only, 0); +} + +/* Helper: function to activate @lv exclusively local */ +static int _activate_sub_lv_excl_local(struct logical_volume *lv) +{ + if (lv && !activate_lv_excl_local(lv->vg->cmd, lv)) { + log_error("Failed to activate %s.", display_lvname(lv)); + return 0; + } + + return 1; +} + +/* Helper: function to activate any sub LVs of @lv exclusively local starting with area indexed by @start_idx */ +static int _activate_sub_lvs_excl_local(struct logical_volume *lv, uint32_t start_idx) +{ + uint32_t s; + struct lv_segment *seg = first_seg(lv); + + /* seg->area_count may be 0 here! */ + log_debug_metadata("Activating %u image component%s of LV %s.", + seg->area_count - start_idx, seg->meta_areas ? " pairs" : "s", + display_lvname(lv)); + for (s = start_idx; s < seg->area_count; s++) + if (!_activate_sub_lv_excl_local(seg_lv(seg, s)) || + !_activate_sub_lv_excl_local(seg_metalv(seg, s))) + return 0; + + return 1; +} + +/* Helper: function to activate any sub LVs of @lv exclusively local starting with area indexed by @start_idx */ +static int _activate_sub_lvs_excl_local_list(struct logical_volume *lv, struct dm_list *lv_list) +{ + int r = 1; + struct lv_list *lvl; + + if (lv_list) { + dm_list_iterate_items(lvl, lv_list) { + log_very_verbose("Activating logical volume %s before %s in kernel.", + display_lvname(lvl->lv), display_lvname(lv_lock_holder(lv))); + if (!_activate_sub_lv_excl_local(lvl->lv)) + r = 0; /* But lets try with the rest */ + } + } + + return r; +} + +/* Helper: callback function to activate any new image component pairs @lv */ +__attribute__ ((__unused__)) +static int _pre_raid_add_legs(struct logical_volume *lv, void *data) +{ + if (!_vg_write_lv_suspend_vg_commit(lv, 1)) + return 0; + + /* Reload any changed image component pairs for out-of-place reshape apace */ + if (!_activate_sub_lvs_excl_local(lv, 0)) + return 0; + + return 2; /* 1: ok, 2: metadata commited */ +} + +/* Helper: callback function to activate any rmetas on @data list */ +__attribute__ ((__unused__)) +static int _pre_raid0_remove_rmeta(struct logical_volume *lv, void *data) +{ + struct dm_list *lv_list = data; + + if (!_vg_write_lv_suspend_vg_commit(lv, 1)) + return 0; + + /* 1: ok, 2: metadata commited */ + return _activate_sub_lvs_excl_local_list(lv, lv_list) ? 2 : 0; +} + +/* Helper: callback dummy needed for */ +__attribute__ ((__unused__)) +static int _post_raid_dummy(struct logical_volume *lv, void *data) +{ + return 1; +} + /* * _alloc_rmeta_for_lv * @lv @@ -3077,78 +3342,6 @@ static int _raid0_add_or_remove_metadata_lvs(struct logical_volume *lv, } /* - * Clear any rebuild disk flags on lv. - * If any flags were cleared, *flags_were_cleared is set to 1. - */ -/* FIXME Generalise into foreach_underlying_lv_segment_area. */ -static void _clear_rebuild_flags(struct logical_volume *lv, int *flags_were_cleared) -{ - struct lv_segment *seg = first_seg(lv); - struct logical_volume *sub_lv; - uint32_t s; - uint64_t flags_to_clear = LV_REBUILD; - - for (s = 0; s < seg->area_count; s++) { - if (seg_type(seg, s) == AREA_PV) - continue; - - sub_lv = seg_lv(seg, s); - - /* Recurse into sub LVs */ - _clear_rebuild_flags(sub_lv, flags_were_cleared); - - if (sub_lv->status & flags_to_clear) { - sub_lv->status &= ~flags_to_clear; - *flags_were_cleared = 1; - } - } -} - -/* - * Updates and reloads metadata, clears any flags passed to the kernel, - * eliminates any residual LVs and updates and reloads metadata again. - * - * lv removal_lvs - * - * This minimally involves 2 metadata commits. - */ -static int _lv_update_reload_fns_reset_eliminate_lvs(struct logical_volume *lv, struct dm_list *removal_lvs) -{ - int flags_were_cleared = 0, r = 0; - - if (!_lv_update_and_reload_list(lv, 1, removal_lvs)) - return_0; - - /* Eliminate any residual LV and don't commit the metadata */ - if (!_eliminate_extracted_lvs_optional_write_vg(lv->vg, removal_lvs, 0)) - return_0; - - /* - * Now that any 'REBUILD' or 'RESHAPE_DELTA_DISKS' etc. - * has/have made its/their way to the kernel, we must - * remove the flag(s) so that the individual devices are - * not rebuilt/reshaped/taken over upon every activation. - * - * Writes and commits metadata if any flags have been reset - * and if successful, performs metadata backup. - */ - /* FIXME This needs to be done through hooks in the metadata */ - log_debug_metadata("Clearing any flags for %s passed to the kernel.", - display_lvname(lv)); - _clear_rebuild_flags(lv, &flags_were_cleared); - - log_debug_metadata("Updating metadata and reloading mappings for %s.", - display_lvname(lv)); - if ((r != 2 || flags_were_cleared) && !lv_update_and_reload(lv)) { - log_error("Update and reload of LV %s failed.", - display_lvname(lv)); - return 0; - } - - return 1; -} - -/* * Adjust all data sub LVs of lv to mirror * or raid name depending on direction * adjusting their LV status @@ -3357,7 +3550,7 @@ static int _convert_raid1_to_mirror(struct logical_volume *lv, if (!attach_mirror_log(first_seg(lv), log_lv)) return_0; - return update_and_reload ? _lv_update_reload_fns_reset_eliminate_lvs(lv, removal_lvs) : 1; + return update_and_reload ? _lv_update_reload_fns_reset_eliminate_lvs(lv, 0, removal_lvs, NULL) : 1; } /* @@ -4288,7 +4481,7 @@ static int _raid45610_to_raid0_or_striped_wrapper(TAKEOVER_FN_ARGS) seg->region_size = new_region_size ?: region_size; - if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, &removal_lvs)) + if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs, NULL)) return_0; if (rename_sublvs) { @@ -4365,7 +4558,7 @@ static int _raid45_to_raid54_wrapper(TAKEOVER_FN_ARGS) seg->region_size = new_region_size ?: region_size; seg->segtype = new_segtype; - if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, &removal_lvs)) + if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, &removal_lvs, NULL)) return_0; init_mirror_in_sync(0); @@ -4486,7 +4679,7 @@ static int _striped_or_raid0_to_raid45610_wrapper(TAKEOVER_FN_ARGS) log_debug_metadata("Updating VG metadata and reloading %s LV %s.", lvseg_name(seg), display_lvname(lv)); - if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, NULL)) + if (!_lv_update_reload_fns_reset_eliminate_lvs(lv, 0, NULL, NULL)) return_0; if (segtype_is_raid4(new_segtype)) { |