Snapshot merging support for LVM. Add a new flag SNAPSHOT_MERGE for a logical volume, that specifies that the snapshot is being merged. Add a command "lvconvert -M vg/lv_snap" that will start merging the snapshot. Signed-off-by: Mikulas Patocka --- lib/activate/dev_manager.c | 15 +++++- lib/format_text/flags.c | 1 lib/metadata/metadata-exported.h | 1 lib/report/report.c | 9 ++-- lib/snapshot/snapshot.c | 5 ++ tools/args.h | 1 tools/commands.h | 9 +++- tools/lvconvert.c | 85 +++++++++++++++++++++++++++++++++------ 8 files changed, 106 insertions(+), 20 deletions(-) Index: LVM2.2.02.33/lib/format_text/flags.c =================================================================== --- LVM2.2.02.33.orig/lib/format_text/flags.c 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/lib/format_text/flags.c 2008-04-10 19:17:02.000000000 +0200 @@ -59,6 +59,7 @@ static struct flag _lv_flags[] = { {MIRRORED, NULL}, {VIRTUAL, NULL}, {SNAPSHOT, NULL}, + {SNAPSHOT_MERGE, NULL}, {ACTIVATE_EXCL, NULL}, {CONVERTING, NULL}, {0, NULL} Index: LVM2.2.02.33/lib/metadata/metadata-exported.h =================================================================== --- LVM2.2.02.33.orig/lib/metadata/metadata-exported.h 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/lib/metadata/metadata-exported.h 2008-04-10 19:17:02.000000000 +0200 @@ -70,6 +70,7 @@ struct pv_segment; //#define ACTIVATE_EXCL 0x00100000U /* LV - internal use only */ //#define PRECOMMITTED 0x00200000U /* VG - internal use only */ #define CONVERTING 0x00400000U /* LV */ +#define SNAPSHOT_MERGE 0x00800000U /* LV */ #define LVM_READ 0x00000100U /* LV VG */ #define LVM_WRITE 0x00000200U /* LV VG */ Index: LVM2.2.02.33/lib/snapshot/snapshot.c =================================================================== --- LVM2.2.02.33.orig/lib/snapshot/snapshot.c 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/lib/snapshot/snapshot.c 2008-04-10 19:17:03.000000000 +0200 @@ -73,6 +73,10 @@ static int _snap_text_import(struct lv_s return 0; } + if (find_config_bool(sn, "merge", 0)) { + cow->status |= SNAPSHOT_MERGE; + } + if (!vg_add_snapshot(seg->lv->name, org, cow, &seg->lv->lvid, seg->len, chunk_size)) return_0; @@ -85,6 +89,7 @@ static int _snap_text_export(const struc outf(f, "chunk_size = %u", seg->chunk_size); outf(f, "origin = \"%s\"", seg->origin->name); outf(f, "cow_store = \"%s\"", seg->cow->name); + outf(f, "merge = %u", !!(seg->cow->status & SNAPSHOT_MERGE)); return 1; } Index: LVM2.2.02.33/tools/args.h =================================================================== --- LVM2.2.02.33.orig/tools/args.h 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/tools/args.h 2008-04-10 19:17:03.000000000 +0200 @@ -93,6 +93,7 @@ arg(list_ARG, 'l', "list", NULL, 0) arg(size_ARG, 'L', "size", size_mb_arg, 0) arg(logicalextent_ARG, 'L', "logicalextent", int_arg_with_sign, 0) arg(persistent_ARG, 'M', "persistent", yes_no_arg, 0) +arg(merge_ARG, 'M', "merge", NULL, 0) arg(major_ARG, 'j', "major", major_arg, 0) arg(mirrors_ARG, 'm', "mirrors", int_arg_with_sign, 0) arg(metadatatype_ARG, 'M', "metadatatype", metadatatype_arg, 0) Index: LVM2.2.02.33/tools/commands.h =================================================================== --- LVM2.2.02.33.orig/tools/commands.h 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/tools/commands.h 2008-04-10 19:17:03.000000000 +0200 @@ -107,10 +107,15 @@ xx(lvconvert, "\t[-v|--verbose]\n" "\t[-Z|--zero {y|n}]\n" "\t[--version]" "\n" - "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n", + "\tOriginalLogicalVolume[Path] SnapshotLogicalVolume[Path]\n\n" + + "lvconvert " + "[-M|--merge]\n" + "\tSnapshotLogicalVolume[Path]\n", alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG, - mirrorlog_ARG, mirrors_ARG, regionsize_ARG, snapshot_ARG, test_ARG, zero_ARG) + mirrorlog_ARG, mirrors_ARG, regionsize_ARG, snapshot_ARG, test_ARG, zero_ARG, + merge_ARG) xx(lvcreate, "Create a logical volume", Index: LVM2.2.02.33/tools/lvconvert.c =================================================================== --- LVM2.2.02.33.orig/tools/lvconvert.c 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/tools/lvconvert.c 2008-04-10 19:17:03.000000000 +0200 @@ -18,6 +18,7 @@ struct lvconvert_params { int snapshot; + int merge; int zero; const char *origin; @@ -49,7 +50,7 @@ static int _lvconvert_name_params(struct char *ptr; const char *vg_name = NULL; - if (lp->snapshot) { + if (lp->snapshot && !lp->merge) { if (!*pargc) { log_error("Please specify a logical volume to act as " "the snapshot origin."); @@ -99,6 +100,11 @@ static int _lvconvert_name_params(struct if (!apply_lvname_restrictions(lp->lv_name)) return_0; + if (*pargc && (lp->snapshot || lp->merge)) { + log_error("Extra arguments for snapshots"); + return 0; + } + return 1; } @@ -110,9 +116,9 @@ static int _read_params(struct lvconvert memset(lp, 0, sizeof(*lp)); - if (arg_count(cmd, snapshot_ARG) && + if ((arg_count(cmd, snapshot_ARG) || arg_count(cmd, merge_ARG)) && (arg_count(cmd, mirrorlog_ARG) || arg_count(cmd, mirrors_ARG))) { - log_error("--snapshots argument cannot be mixed " + log_error("--snapshots or --merge argument cannot be mixed " "with --mirrors or --log"); return 0; } @@ -123,6 +129,9 @@ static int _read_params(struct lvconvert if (arg_count(cmd, snapshot_ARG)) lp->snapshot = 1; + if (arg_count(cmd, merge_ARG)) + lp->merge = 1; + if (arg_count(cmd, mirrors_ARG)) { lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0); lp->mirrors_sign = arg_sign_value(cmd, mirrors_ARG, 0); @@ -132,7 +141,16 @@ static int _read_params(struct lvconvert if (arg_count(cmd, alloc_ARG)) lp->alloc = arg_uint_value(cmd, alloc_ARG, lp->alloc); - if (lp->snapshot) { + if (lp->merge) { + if (arg_count(cmd, regionsize_ARG) || arg_count(cmd, chunksize_ARG) || arg_count(cmd, zero_ARG) || arg_count(cmd, regionsize_ARG)) { + log_error("invalid arguments for snapshot merge"); + return 0; + } + + if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot"))) + return_0; + + } else if (lp->snapshot) { if (arg_count(cmd, regionsize_ARG)) { log_error("--regionsize is only available with mirrors"); return 0; @@ -680,6 +698,41 @@ static int lvconvert_snapshot(struct cmd return 1; } +static int lvconvert_merge(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvconvert_params *lp) +{ + if (lv->status & SNAPSHOT_MERGE) { + log_error("Snapshot %s is already merging", lv->name); + return 0; + } + lv->status |= SNAPSHOT_MERGE; + + /* store vg on disk(s) */ + if (!vg_write(lv->vg)) + return_0; + + backup(lv->vg); + + if (!suspend_lv(cmd, lv)) { + log_error("Failed to suspend snapshot %s", lv->name); + vg_revert(lv->vg); + return 0; + } + + if (!vg_commit(lv->vg)) + return_0; + + if (!resume_lv(cmd, lv)) { + log_error("Problem reactivating snapshot %s", lv->name); + return 0; + } + + log_print("Merging of volume %s started.", lv->name); + + return 1; +} + static int lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle) { @@ -690,24 +743,34 @@ static int lvconvert_single(struct cmd_c return ECMD_FAILED; } + if (lv->status & PVMOVE) { + log_error("Unable to convert pvmove LV %s", lv->name); + return ECMD_FAILED; + } + if (lv_is_origin(lv)) { log_error("Can't convert logical volume \"%s\" under snapshot", lv->name); return ECMD_FAILED; } - if (lv_is_cow(lv)) { + if (lv_is_cow(lv) && !lp->merge) { log_error("Can't convert snapshot logical volume \"%s\"", lv->name); return ECMD_FAILED; } - if (lv->status & PVMOVE) { - log_error("Unable to convert pvmove LV %s", lv->name); - return ECMD_FAILED; - } - - if (lp->snapshot) { + if (lp->merge) { + if (!lv_is_cow(lv)) { + log_error("Logical volume \"%s\" is not a snapshot", + lv->name); + return ECMD_FAILED; + } + if (!archive(lv->vg)) + return ECMD_FAILED; + if (!lvconvert_merge(cmd, lv, lp)) + return ECMD_FAILED; + } else if (lp->snapshot) { if (lv->status & MIRRORED) { log_error("Unable to convert mirrored LV \"%s\" into a snapshot.", lv->name); return ECMD_FAILED; Index: LVM2.2.02.33/lib/report/report.c =================================================================== --- LVM2.2.02.33.orig/lib/report/report.c 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/lib/report/report.c 2008-04-10 19:17:03.000000000 +0200 @@ -323,9 +323,12 @@ static int _lvstatus_disp(struct dm_repo repstr[0] = 'v'; else if (lv_is_origin(lv)) repstr[0] = 'o'; - else if (lv_is_cow(lv)) - repstr[0] = 's'; - else + else if (lv_is_cow(lv)) { + if (lv->status & SNAPSHOT_MERGE) + repstr[0] = 'S'; + else + repstr[0] = 's'; + }else repstr[0] = '-'; if (lv->status & PVMOVE) Index: LVM2.2.02.33/lib/activate/dev_manager.c =================================================================== --- LVM2.2.02.33.orig/lib/activate/dev_manager.c 2008-04-10 18:49:55.000000000 +0200 +++ LVM2.2.02.33/lib/activate/dev_manager.c 2008-04-10 19:17:03.000000000 +0200 @@ -369,10 +369,12 @@ static int _percent_run(struct dev_manag seg = list_item(segh, struct lv_segment); } - if (!type || !params || strcmp(type, target_type)) + /* allow the situation when type is "snapshot-merge" and + target_type is "snapshot" */ + if (!type || !params || strncmp(type, target_type, strlen(target_type))) continue; - if (!(segtype = get_segtype_from_string(dm->cmd, type))) + if (!(segtype = get_segtype_from_string(dm->cmd, target_type))) continue; if (segtype->ops->target_percent && @@ -777,8 +779,13 @@ static int _add_snapshot_target_to_dtree size = (uint64_t) snap_seg->len * snap_seg->origin->vg->extent_size; - if (!dm_tree_node_add_snapshot_target(dnode, size, origin_dlid, cow_dlid, 1, snap_seg->chunk_size)) - return_0; + if (!(lv->status & SNAPSHOT_MERGE)) { + if (!dm_tree_node_add_snapshot_target(dnode, size, origin_dlid, cow_dlid, 1, snap_seg->chunk_size)) + return_0; + } else { + if (!dm_tree_node_add_snapshot_merge_target(dnode, size, origin_dlid, cow_dlid, snap_seg->chunk_size)) + return_0; + } return 1; }