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/format1/import-export.c | 2 lib/format_text/flags.c | 1 lib/metadata/metadata-exported.h | 3 - lib/metadata/snapshot_manip.c | 3 - lib/report/report.c | 9 ++- lib/snapshot/snapshot.c | 15 +++++- tools/args.h | 1 tools/commands.h | 9 +++ tools/lvconvert.c | 89 +++++++++++++++++++++++++++++++++------ tools/lvcreate.c | 2 11 files changed, 121 insertions(+), 28 deletions(-) Index: LVM2.2.02.45/lib/format_text/flags.c =================================================================== --- LVM2.2.02.45.orig/lib/format_text/flags.c 2009-05-19 21:52:15.000000000 +0200 +++ LVM2.2.02.45/lib/format_text/flags.c 2009-05-19 22:14:22.000000000 +0200 @@ -61,6 +61,7 @@ static struct flag _lv_flags[] = { {MIRRORED, NULL, 0}, {VIRTUAL, NULL, 0}, {SNAPSHOT, NULL, 0}, + {SNAPSHOT_MERGE, NULL, 0}, {ACTIVATE_EXCL, NULL, 0}, {CONVERTING, NULL, 0}, {PARTIAL_LV, NULL, 0}, Index: LVM2.2.02.45/lib/metadata/metadata-exported.h =================================================================== --- LVM2.2.02.45.orig/lib/metadata/metadata-exported.h 2009-05-19 22:14:08.000000000 +0200 +++ LVM2.2.02.45/lib/metadata/metadata-exported.h 2009-05-19 22:14:22.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 /* SEG */ #define MISSING_PV 0x00800000U /* PV */ #define PARTIAL_LV 0x01000000U /* LV - derived flag, not @@ -547,7 +548,7 @@ struct logical_volume *origin_from_cow(c int vg_add_snapshot(const char *name, struct logical_volume *origin, struct logical_volume *cow, union lvid *lvid, uint32_t extent_count, - uint32_t chunk_size); + uint32_t chunk_size, int merge); int vg_remove_snapshot(struct logical_volume *cow); Index: LVM2.2.02.45/lib/snapshot/snapshot.c =================================================================== --- LVM2.2.02.45.orig/lib/snapshot/snapshot.c 2009-05-19 22:14:02.000000000 +0200 +++ LVM2.2.02.45/lib/snapshot/snapshot.c 2009-05-19 22:14:22.000000000 +0200 @@ -40,6 +40,9 @@ static int _snap_text_import(struct lv_s seg->lv->status |= SNAPSHOT; + if (find_config_bool(sn, "merge", 0)) + seg->status |= SNAPSHOT_MERGE; + if (!get_config_uint32(sn, "chunk_size", &chunk_size)) { log_error("Couldn't read chunk size for snapshot."); return 0; @@ -47,7 +50,7 @@ static int _snap_text_import(struct lv_s log_suppress(1); - if (!(cow_name = find_config_str(sn, "cow_store", NULL))) { + if (!(cow_name = find_config_str(sn, !(seg->status & SNAPSHOT_MERGE) ? "cow_store" : "merging_store", NULL))) { log_suppress(0); log_error("Snapshot cow storage not specified."); return 0; @@ -74,7 +77,8 @@ static int _snap_text_import(struct lv_s } if (!vg_add_snapshot(seg->lv->name, org, cow, - &seg->lv->lvid, seg->len, chunk_size)) + &seg->lv->lvid, seg->len, chunk_size, + !!(seg->status & SNAPSHOT_MERGE))) return_0; return 1; @@ -84,7 +88,12 @@ 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); + if (!(seg->status & SNAPSHOT_MERGE)) { + outf(f, "cow_store = \"%s\"", seg->cow->name); + } else { + outf(f, "merging_store = \"%s\"", seg->cow->name); + outf(f, "merge = 1"); + } return 1; } Index: LVM2.2.02.45/tools/args.h =================================================================== --- LVM2.2.02.45.orig/tools/args.h 2009-05-19 21:52:15.000000000 +0200 +++ LVM2.2.02.45/tools/args.h 2009-05-19 22:14:22.000000000 +0200 @@ -97,6 +97,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.45/tools/commands.h =================================================================== --- LVM2.2.02.45.orig/tools/commands.h 2009-05-19 21:52:15.000000000 +0200 +++ LVM2.2.02.45/tools/commands.h 2009-05-19 22:14:22.000000000 +0200 @@ -112,10 +112,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.45/tools/lvconvert.c =================================================================== --- LVM2.2.02.45.orig/tools/lvconvert.c 2009-05-19 21:52:15.000000000 +0200 +++ LVM2.2.02.45/tools/lvconvert.c 2009-05-19 22:14:22.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; @@ -655,7 +673,7 @@ static int lvconvert_snapshot(struct cmd } if (!vg_add_snapshot(NULL, org, lv, NULL, org->le_count, - lp->chunk_size)) { + lp->chunk_size, 0)) { log_error("Couldn't create snapshot."); return 0; } @@ -685,6 +703,43 @@ static int lvconvert_snapshot(struct cmd return 1; } +static int lvconvert_merge(struct cmd_context *cmd, + struct logical_volume *lv, + struct lvconvert_params *lp) +{ + struct logical_volume *origin = origin_from_cow(lv); + + if (find_cow(lv)->status & SNAPSHOT_MERGE) { + log_error("Snapshot %s is already merging", lv->name); + return 0; + } + find_cow(lv)->status |= SNAPSHOT_MERGE; + + /* store vg on disk(s) */ + if (!vg_write(lv->vg)) + return_0; + + backup(lv->vg); + + if (!suspend_lv(cmd, origin)) { + log_error("Failed to suspend origin %s", origin->name); + vg_revert(lv->vg); + return 0; + } + + if (!vg_commit(lv->vg)) + return_0; + + if (!resume_lv(cmd, origin)) { + log_error("Problem reactivating origin %s", origin->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) { @@ -695,24 +750,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.45/lib/report/report.c =================================================================== --- LVM2.2.02.45.orig/lib/report/report.c 2009-05-19 22:14:03.000000000 +0200 +++ LVM2.2.02.45/lib/report/report.c 2009-05-19 22:14:22.000000000 +0200 @@ -326,9 +326,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 (find_cow(lv)->status & SNAPSHOT_MERGE) + repstr[0] = 'S'; + else + repstr[0] = 's'; + }else repstr[0] = '-'; if (lv->status & PVMOVE) Index: LVM2.2.02.45/lib/activate/dev_manager.c =================================================================== --- LVM2.2.02.45.orig/lib/activate/dev_manager.c 2009-05-19 22:14:02.000000000 +0200 +++ LVM2.2.02.45/lib/activate/dev_manager.c 2009-05-19 22:14:22.000000000 +0200 @@ -377,10 +377,12 @@ static int _percent_run(struct dev_manag seg = dm_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) { @@ -868,8 +870,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 (!(snap_seg->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; } Index: LVM2.2.02.45/lib/metadata/snapshot_manip.c =================================================================== --- LVM2.2.02.45.orig/lib/metadata/snapshot_manip.c 2009-05-19 21:52:15.000000000 +0200 +++ LVM2.2.02.45/lib/metadata/snapshot_manip.c 2009-05-19 22:14:22.000000000 +0200 @@ -58,7 +58,7 @@ struct logical_volume *origin_from_cow(c int vg_add_snapshot(const char *name, struct logical_volume *origin, struct logical_volume *cow, union lvid *lvid, - uint32_t extent_count, uint32_t chunk_size) + uint32_t extent_count, uint32_t chunk_size, int merge) { struct logical_volume *snap; struct lv_segment *seg; @@ -90,6 +90,7 @@ int vg_add_snapshot(const char *name, st seg->origin = origin; seg->cow = cow; seg->lv->status |= SNAPSHOT; + if (merge) seg->status |= SNAPSHOT_MERGE; origin->origin_count++; origin->vg->snapshot_count++; Index: LVM2.2.02.45/tools/lvcreate.c =================================================================== --- LVM2.2.02.45.orig/tools/lvcreate.c 2009-05-19 22:14:07.000000000 +0200 +++ LVM2.2.02.45/tools/lvcreate.c 2009-05-19 22:14:22.000000000 +0200 @@ -833,7 +833,7 @@ static int _lvcreate(struct cmd_context /* cow LV remains active and becomes snapshot LV */ if (!vg_add_snapshot(NULL, org, lv, NULL, - org->le_count, lp->chunk_size)) { + org->le_count, lp->chunk_size, 0)) { log_error("Couldn't create snapshot."); return 0; } Index: LVM2.2.02.45/lib/format1/import-export.c =================================================================== --- LVM2.2.02.45.orig/lib/format1/import-export.c 2009-05-19 22:14:08.000000000 +0200 +++ LVM2.2.02.45/lib/format1/import-export.c 2009-05-19 22:14:22.000000000 +0200 @@ -614,7 +614,7 @@ int import_snapshots(struct dm_pool *mem /* insert the snapshot */ if (!vg_add_snapshot(NULL, org, cow, NULL, org->le_count, - lvd->lv_chunk_size)) { + lvd->lv_chunk_size, 0)) { log_err("Couldn't add snapshot."); return 0; }