From 4c7ee1ce658c69bfe16773c824b8127902e91f78 Mon Sep 17 00:00:00 2001 From: CentOS Buildsys Date: Mar 26 2014 13:29:15 +0000 Subject: import lvm2-2.02.105-14.el7.src.rpm --- diff --git a/.lvm2.metadata b/.lvm2.metadata index 6ec16fd..2f3f5b8 100644 --- a/.lvm2.metadata +++ b/.lvm2.metadata @@ -1 +1 @@ -1194babd8d3cd5a9620bec1fe98a3be060465daf SOURCES/LVM2.2.02.103.tgz +796163e766480cdc427cd443dc1336ae8e8e3bd7 SOURCES/LVM2.2.02.105.tgz diff --git a/SOURCES/lvm2-2_02_104-add-dev-block-major-minor-device-systemd-alias-and-pvscan-cache-shortcut.patch b/SOURCES/lvm2-2_02_104-add-dev-block-major-minor-device-systemd-alias-and-pvscan-cache-shortcut.patch deleted file mode 100644 index 47ce627..0000000 --- a/SOURCES/lvm2-2_02_104-add-dev-block-major-minor-device-systemd-alias-and-pvscan-cache-shortcut.patch +++ /dev/null @@ -1,181 +0,0 @@ - WHATS_NEW | 2 + - man/pvscan.8.in | 4 +- - scripts/Makefile.in | 2 +- - scripts/lvm2_pvscan_systemd_red_hat@.service.in | 11 +++-- - tools/pvscan.c | 61 +++++++++++++++++++------ - udev/Makefile.in | 2 +- - 6 files changed, 60 insertions(+), 22 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 5d3bb8b..aabfc78 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,7 @@ - Version 2.02.104 - - =================================== -+ Add dev-block-:.device systemd alias for complete PV tracking. -+ Use major:minor as short form of --major and --minor arg for pvscan --cache. - Fix lvconvert swap of poolmetadata volume for active thin pool. - Add configure --enable-udev-systemd-background-jobs. - Add lvm2-pvscan@.service to run pvscan as a service for lvmetad/autoactivation. -diff --git a/man/pvscan.8.in b/man/pvscan.8.in -index 211c82b..37ecaaf 100644 ---- a/man/pvscan.8.in -+++ b/man/pvscan.8.in -@@ -25,7 +25,9 @@ pvscan \- scan all disks for physical volumes - .B \-\-minor - .I minor - | --.IR DevicePath ]... -+.IR DevicePath -+| -+.IR major:minor ]... - .SH DESCRIPTION - pvscan scans all supported LVM block devices in the system for - physical volumes. -diff --git a/scripts/Makefile.in b/scripts/Makefile.in -index fac7e40..3616afa 100644 ---- a/scripts/Makefile.in -+++ b/scripts/Makefile.in -@@ -119,7 +119,7 @@ DISTCLEAN_TARGETS += clvmd_init_red_hat cmirrord_init_red_hat \ - dm_event_systemd_red_hat.socket dm_event_systemd_red_hat.service \ - lvm2_monitoring_systemd_red_hat.service \ - lvm2_lvmetad_systemd_red_hat.socket lvm2_lvmetad_systemd_red_hat.service \ -- lvm2_lvmetad_systemd_red_hat@.service \ -+ lvm2_pvscan_systemd_red_hat@.service \ - lvm2_tmpfiles_red_hat.conf blk_availability_init_red_hat \ - blk_availability_systemd_red_hat.service \ - blkdeactivate.sh -diff --git a/scripts/lvm2_pvscan_systemd_red_hat@.service.in b/scripts/lvm2_pvscan_systemd_red_hat@.service.in -index 9d91b5e..4225982 100644 ---- a/scripts/lvm2_pvscan_systemd_red_hat@.service.in -+++ b/scripts/lvm2_pvscan_systemd_red_hat@.service.in -@@ -1,11 +1,14 @@ - [Unit] --Description=LVM2 PV scan on %I -+Description=LVM2 PV scan on device %i - Documentation=man:pvscan(8) - DefaultDependencies=no --After=lvm2-lvmetad.socket %i.device -+BindsTo=dev-block-%i.device -+After=lvm2-lvmetad.socket - Before=shutdown.target - Conflicts=shutdown.target - - [Service] --Type=simple --ExecStart=@sbindir@/pvscan --cache --activate ay %I -+Type=oneshot -+RemainAfterExit=yes -+ExecStart=@sbindir@/pvscan --cache --activate ay /dev/block/%i -+ExecStop=@sbindir@/pvscan --cache %i -diff --git a/tools/pvscan.c b/tools/pvscan.c -index 3f16b05..b6a07bd 100644 ---- a/tools/pvscan.c -+++ b/tools/pvscan.c -@@ -132,6 +132,27 @@ out: - return r; - } - -+static int _clear_dev_from_lvmetad_cache(dev_t devno, int32_t major, int32_t minor, -+ activation_handler handler) -+{ -+ char *buf; -+ -+ if (!dm_asprintf(&buf, "%" PRIi32 ":%" PRIi32, major, minor)) -+ stack; -+ if (!lvmetad_pv_gone(devno, buf ? : "", handler)) { -+ if (buf) -+ dm_free(buf); -+ return 0; -+ } -+ -+ log_print_unless_silent("Device %s not found. " -+ "Cleared from lvmetad cache.", buf ? : ""); -+ if (buf) -+ dm_free(buf); -+ -+ return 1; -+} -+ - static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) - { - int ret = ECMD_PROCESSED; -@@ -142,7 +163,6 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) - int devno_args = 0; - struct arg_value_group_list *current_group; - dev_t devno; -- char *buf; - activation_handler handler = NULL; - - /* -@@ -193,11 +213,30 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) - /* Process any command line PVs first. */ - while (argc--) { - pv_name = *argv++; -- dev = dev_cache_get(pv_name, cmd->lvmetad_filter); -- if (!dev) { -- log_error("Physical Volume %s not found.", pv_name); -- ret = ECMD_FAILED; -- continue; -+ if (pv_name[0] == '/') { -+ /* device path */ -+ if (!(dev = dev_cache_get(pv_name, cmd->lvmetad_filter))) { -+ log_error("Physical Volume %s not found.", pv_name); -+ ret = ECMD_FAILED; -+ continue; -+ } -+ } -+ else { -+ /* device major:minor */ -+ if (sscanf(pv_name, "%d:%d", &major, &minor) != 2) { -+ log_error("Failed to parse major:minor from %s", pv_name); -+ ret = ECMD_FAILED; -+ continue; -+ } -+ devno = MKDEV((dev_t)major, minor); -+ if (!(dev = dev_cache_get_by_devt(devno, cmd->lvmetad_filter))) { -+ if (!(_clear_dev_from_lvmetad_cache(devno, major, minor, handler))) { -+ stack; -+ ret = ECMD_FAILED; -+ break; -+ } -+ continue; -+ } - } - if (sigint_caught()) { - ret = ECMD_FAILED; -@@ -225,19 +264,11 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) - devno = MKDEV((dev_t)major, minor); - - if (!(dev = dev_cache_get_by_devt(devno, cmd->lvmetad_filter))) { -- if (!dm_asprintf(&buf, "%" PRIi32 ":%" PRIi32, major, minor)) -+ if (!(_clear_dev_from_lvmetad_cache(devno, major, minor, handler))) { - stack; -- if (!lvmetad_pv_gone(devno, buf ? : "", handler)) { - ret = ECMD_FAILED; -- if (buf) -- dm_free(buf); - break; - } -- -- log_print_unless_silent("Device %s not found. " -- "Cleared from lvmetad cache.", buf ? : ""); -- if (buf) -- dm_free(buf); - continue; - } - if (sigint_caught()) { -diff --git a/udev/Makefile.in b/udev/Makefile.in -index fdf43df..40a4671 100644 ---- a/udev/Makefile.in -+++ b/udev/Makefile.in -@@ -47,7 +47,7 @@ BLKID_RULE=IMPORT{program}=\"${SBIN}\/blkid -o udev -p \$$tempnode\" - endif - - ifeq ("@UDEV_SYSTEMD_BACKGROUND_JOBS@", "yes") --PVSCAN_RULE=ENV{SYSTEMD_WANTS}=\"lvm2-pvscan@\$$devnode.service\" -+PVSCAN_RULE=ENV{SYSTEMD_ALIAS}=\"\/dev\/block\/\$$major:\$$minor\"\nENV{ID_MODEL}=\"LVM PV \$$env{ID_FS_UUID_ENC} on \/dev\/\$$name\"\nENV{SYSTEMD_WANTS}=\"lvm2-pvscan@\$$major:\$$minor.service\" - else - PVSCAN_RULE=RUN\+\=\"$(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major \$$major --minor \$$minor\", ENV{LVM_SCANNED}=\"1\" - endif diff --git a/SOURCES/lvm2-2_02_104-fix-lvconvert-swap-of-poolmetadata-volume-for-active-thin-pool.patch b/SOURCES/lvm2-2_02_104-fix-lvconvert-swap-of-poolmetadata-volume-for-active-thin-pool.patch deleted file mode 100644 index 20ab559..0000000 --- a/SOURCES/lvm2-2_02_104-fix-lvconvert-swap-of-poolmetadata-volume-for-active-thin-pool.patch +++ /dev/null @@ -1,77 +0,0 @@ - WHATS_NEW | 1 + - tools/lvconvert.c | 16 +++++++++++++++- - 2 files changed, 16 insertions(+), 1 deletion(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 2dfeada..5d3bb8b 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,6 @@ - Version 2.02.104 - - =================================== -+ Fix lvconvert swap of poolmetadata volume for active thin pool. - Add configure --enable-udev-systemd-background-jobs. - Add lvm2-pvscan@.service to run pvscan as a service for lvmetad/autoactivation. - Fix possible race during daemon worker thread creation (lvmetad). -diff --git a/tools/lvconvert.c b/tools/lvconvert.c -index 1970ca3..1098642 100644 ---- a/tools/lvconvert.c -+++ b/tools/lvconvert.c -@@ -2271,6 +2271,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - struct logical_volume *pool_metadata_lv; - struct logical_volume *external_lv = NULL; - char metadata_name[NAME_LEN], data_name[NAME_LEN]; -+ int activate_pool; - - if (!lv_is_visible(pool_lv)) { - log_error("Can't convert internal LV %s/%s.", -@@ -2299,6 +2300,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - } - - if (lv_is_thin_pool(pool_lv)) { -+ activate_pool = lv_is_active(pool_lv); - r = 1; /* Already existing thin pool */ - goto out; - } -@@ -2311,6 +2313,9 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - return 0; - } - -+ /* Allow to have only thinpool active and restore it's active state */ -+ activate_pool = lv_is_active(pool_lv); -+ - /* We are changing target type, so deactivate first */ - if (!deactivate_lv(cmd, pool_lv)) { - log_error("Aborting. Failed to deactivate logical volume %s/%s.", -@@ -2318,6 +2323,13 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - return 0; - } - -+ if (lv_is_thin_pool(pool_lv) && pool_is_active(pool_lv)) { -+ /* If any thin volume is also active - abort here */ -+ log_error("Cannot convert pool %s/%s with active thin volumes.", -+ pool_lv->vg->name, pool_lv->name); -+ return 0; -+ } -+ - if ((dm_snprintf(metadata_name, sizeof(metadata_name), "%s_tmeta", - pool_lv->name) < 0) || - (dm_snprintf(data_name, sizeof(data_name), "%s_tdata", -@@ -2419,6 +2431,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - goto mda_write; - } - -+ metadata_lv->status |= LV_NOSCAN; - if (!lv_is_active(metadata_lv) && - !activate_lv_local(cmd, metadata_lv)) { - log_error("Aborting. Failed to activate thin metadata lv."); -@@ -2511,7 +2524,8 @@ mda_write: - if (!vg_write(pool_lv->vg) || !vg_commit(pool_lv->vg)) - return_0; - -- if (!activate_lv_excl(cmd, pool_lv)) { -+ if (activate_pool && -+ !activate_lv_excl(cmd, pool_lv)) { - log_error("Failed to activate pool logical volume %s/%s.", - pool_lv->vg->name, pool_lv->name); - /* Deactivate subvolumes */ diff --git a/SOURCES/lvm2-2_02_104-fix-lvconvert-when-converting-to-a-thin-pool-and-thin-lv-at-once.patch b/SOURCES/lvm2-2_02_104-fix-lvconvert-when-converting-to-a-thin-pool-and-thin-lv-at-once.patch deleted file mode 100644 index 58eed09..0000000 --- a/SOURCES/lvm2-2_02_104-fix-lvconvert-when-converting-to-a-thin-pool-and-thin-lv-at-once.patch +++ /dev/null @@ -1,62 +0,0 @@ -commit e56934c37ce575239fec2fe057cfe81f32be8263 -Author: Peter Rajnoha -Date: Thu Oct 10 10:14:27 2013 +0200 - - 0 ---- - WHATS_NEW | 4 ++++ - test/shell/lvconvert-thin-external.sh | 8 ++++++++ - tools/lvconvert.c | 3 ++- - 3 files changed, 14 insertions(+), 1 deletion(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index d69e74e..d8d6e7c 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,3 +1,7 @@ -+Version 2.02.104 - -+=================================== -+ Fix lvconvert when converting to a thin pool and thin LV at once. -+ - Version 2.02.103 - 4th October 2013 - =================================== - Ensure vgid matches before removing vgname entry from lvmetad cache. -diff --git a/test/shell/lvconvert-thin-external.sh b/test/shell/lvconvert-thin-external.sh -index a700b37..d9d4d19 100644 ---- a/test/shell/lvconvert-thin-external.sh -+++ b/test/shell/lvconvert-thin-external.sh -@@ -144,5 +144,13 @@ lvs -a -o+origin_size,seg_size,segtype $vg - lvremove -f $vg/extorg2 - # Only pool is left - check vg_field $vg lv_count 1 -+lvremove -ff $vg -+ -+# Test conversion to the pool and thin external at the same time (rhbz #1003461) -+lvcreate -l50 -n pool $vg -+lvcreate -l100 -n thin $vg -+lvconvert --thin --thinpool $vg/pool $vg/thin --originname thin-origin -+check lv_field $vg/thin segtype thin -+check lv_field $vg/thin-origin segtype linear - - vgremove -ff $vg -diff --git a/tools/lvconvert.c b/tools/lvconvert.c -index 49881fa..1970ca3 100644 ---- a/tools/lvconvert.c -+++ b/tools/lvconvert.c -@@ -2266,7 +2266,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - int r = 0; - const char *old_name; - struct lv_segment *seg; -- struct logical_volume *data_lv = pool_lv; -+ struct logical_volume *data_lv; - struct logical_volume *metadata_lv; - struct logical_volume *pool_metadata_lv; - struct logical_volume *external_lv = NULL; -@@ -2304,6 +2304,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - } - } - -+ data_lv = pool_lv; - if (lv_is_thin_type(pool_lv) && !lp->pool_metadata_lv_name) { - log_error("Can't use thin logical volume %s/%s for thin pool data.", - pool_lv->vg->name, pool_lv->name); diff --git a/SOURCES/lvm2-2_02_104-improve-discards-when-pool-active-error.patch b/SOURCES/lvm2-2_02_104-improve-discards-when-pool-active-error.patch deleted file mode 100644 index 9bb2a23..0000000 --- a/SOURCES/lvm2-2_02_104-improve-discards-when-pool-active-error.patch +++ /dev/null @@ -1,29 +0,0 @@ - WHATS_NEW | 1 + - tools/lvchange.c | 3 +-- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index d8d6e7c..e0cb795 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,6 @@ - Version 2.02.104 - - =================================== -+ Improve message when unable to change discards setting on active thin pool. - Fix lvconvert when converting to a thin pool and thin LV at once. - - Version 2.02.103 - 4th October 2013 -diff --git a/tools/lvchange.c b/tools/lvchange.c -index 1d4f0a5..6ae9720 100644 ---- a/tools/lvchange.c -+++ b/tools/lvchange.c -@@ -120,8 +120,7 @@ static int lvchange_pool_update(struct cmd_context *cmd, - if (((discards == THIN_DISCARDS_IGNORE) || - (first_seg(lv)->discards == THIN_DISCARDS_IGNORE)) && - pool_is_active(lv)) -- log_error("Cannot change discards state for active " -- "pool volume \"%s\".", lv->name); -+ log_error("Cannot change support for discards while pool volume \"%s\" is active.", lv->name); - else { - first_seg(lv)->discards = discards; - update++; diff --git a/SOURCES/lvm2-2_02_104-udev-noscan-flag.patch b/SOURCES/lvm2-2_02_104-udev-noscan-flag.patch deleted file mode 100644 index 99c0688..0000000 --- a/SOURCES/lvm2-2_02_104-udev-noscan-flag.patch +++ /dev/null @@ -1,343 +0,0 @@ - WHATS_NEW | 2 ++ - daemons/clvmd/lvm-functions.c | 2 +- - lib/activate/activate.c | 20 ++++++++++++-------- - lib/activate/activate.h | 5 +++-- - lib/activate/dev_manager.c | 15 +++++++++++---- - lib/format_text/flags.c | 5 +++-- - lib/locking/file_locking.c | 4 ++-- - lib/locking/no_locking.c | 4 ++-- - lib/metadata/lv_manip.c | 6 ++++++ - lib/metadata/metadata-exported.h | 1 + - udev/11-dm-lvm.rules.in | 15 +++++++++++++++ - udev/13-dm-disk.rules.in | 3 ++- - udev/69-dm-lvm-metad.rules.in | 2 ++ - 13 files changed, 62 insertions(+), 22 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index e0cb795..cd55d54 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,7 @@ - Version 2.02.104 - - =================================== -+ Recognize new flag to skip udev scanning in udev rules and act appropriately. -+ Add support for flagging an LV to skip udev scanning during activation. - Improve message when unable to change discards setting on active thin pool. - Fix lvconvert when converting to a thin pool and thin LV at once. - -diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c -index da7d335..f544218 100644 ---- a/daemons/clvmd/lvm-functions.c -+++ b/daemons/clvmd/lvm-functions.c -@@ -401,7 +401,7 @@ static int do_activate_lv(char *resource, unsigned char command, unsigned char l - } - - /* Now activate it */ -- if (!lv_activate(cmd, resource, exclusive, NULL)) -+ if (!lv_activate(cmd, resource, exclusive, 0, NULL)) - goto error; - - return 0; -diff --git a/lib/activate/activate.c b/lib/activate/activate.c -index 28549fc..c077113 100644 ---- a/lib/activate/activate.c -+++ b/lib/activate/activate.c -@@ -337,12 +337,13 @@ int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s, - { - return 1; - } --int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv) -+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan, -+ struct logical_volume *lv) - { - return 1; - } - int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, -- struct logical_volume *lv) -+ int noscan, struct logical_volume *lv) - { - return 1; - } -@@ -2027,9 +2028,10 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s, - if (filter) - laopts->read_only = _passes_readonly_filter(cmd, lv); - -- log_debug_activation("Activating %s/%s%s%s.", lv->vg->name, lv->name, -+ log_debug_activation("Activating %s/%s%s%s%s.", lv->vg->name, lv->name, - laopts->exclusive ? " exclusively" : "", -- laopts->read_only ? " read-only" : ""); -+ laopts->read_only ? " read-only" : "", -+ laopts->noscan ? " noscan" : ""); - - if (!lv_info(cmd, lv, 0, &info, 0, 0)) - goto_out; -@@ -2066,9 +2068,10 @@ out: - } - - /* Activate LV */ --int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv) -+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, -+ int noscan, struct logical_volume *lv) - { -- struct lv_activate_opts laopts = { .exclusive = exclusive }; -+ struct lv_activate_opts laopts = { .exclusive = exclusive, .noscan = noscan }; - - if (!_lv_activate(cmd, lvid_s, &laopts, 0, lv)) - return_0; -@@ -2077,9 +2080,10 @@ int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, stru - } - - /* Activate LV only if it passes filter */ --int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv) -+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, -+ int noscan, struct logical_volume *lv) - { -- struct lv_activate_opts laopts = { .exclusive = exclusive }; -+ struct lv_activate_opts laopts = { .exclusive = exclusive, .noscan = noscan }; - - if (!_lv_activate(cmd, lvid_s, &laopts, 1, lv)) - return_0; -diff --git a/lib/activate/activate.h b/lib/activate/activate.h -index f34d376..4eac320 100644 ---- a/lib/activate/activate.h -+++ b/lib/activate/activate.h -@@ -38,6 +38,7 @@ struct lv_activate_opts { - int skip_in_use; - unsigned revert; - unsigned read_only; -+ unsigned noscan; - }; - - /* target attribute flags */ -@@ -80,9 +81,9 @@ int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned o - int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only, struct logical_volume *lv); - int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, - unsigned origin_only, unsigned exclusive, unsigned revert, struct logical_volume *lv); --int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv); -+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan, struct logical_volume *lv); - int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, -- int exclusive, struct logical_volume *lv); -+ int exclusive, int noscan, struct logical_volume *lv); - int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, struct logical_volume *lv); - - int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv); -diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c -index b8233bf..0f5a04c 100644 ---- a/lib/activate/dev_manager.c -+++ b/lib/activate/dev_manager.c -@@ -30,6 +30,7 @@ - #include - - #define MAX_TARGET_PARAMSIZE 50000 -+#define LVM_UDEV_NOSCAN_FLAG DM_SUBSYSTEM_UDEV_FLAG0 - - typedef enum { - PRELOAD, -@@ -1399,7 +1400,7 @@ static int _check_udev_fallback(struct cmd_context *cmd) - #endif /* UDEV_SYNC_SUPPORT */ - - static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *lv, -- const char *layer) -+ const char *layer, uint16_t flags) - { - uint16_t udev_flags = 0; - -@@ -1447,6 +1448,11 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *l - udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG | - DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; - -+ /* -+ * Firmly set requested flags. -+ */ -+ udev_flags |= flags; -+ - return udev_flags; - } - -@@ -1493,7 +1499,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, - } - - if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor, -- _get_udev_flags(dm, lv, layer))) { -+ _get_udev_flags(dm, lv, layer, 0))) { - log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree", - info.major, info.minor); - return 0; -@@ -2334,7 +2340,7 @@ static int _set_udev_flags_for_children(struct dev_manager *dm, - } - - dm_tree_node_set_udev_flags(child, -- _get_udev_flags(dm, lvl->lv, NULL)); -+ _get_udev_flags(dm, lvl->lv, NULL, 0)); - } - - return 1; -@@ -2411,7 +2417,8 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, - read_only_lv(lv, laopts), - ((lv->vg->status & PRECOMMITTED) | laopts->revert) ? 1 : 0, - lvlayer, -- _get_udev_flags(dm, lv, layer)))) -+ _get_udev_flags(dm, lv, layer, -+ laopts->noscan ? LVM_UDEV_NOSCAN_FLAG : 0)))) - return_0; - - /* Store existing name so we can do rename later */ -diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c -index 5077576..a27b791 100644 ---- a/lib/format_text/flags.c -+++ b/lib/format_text/flags.c -@@ -61,6 +61,7 @@ static const struct flag _lv_flags[] = { - {LV_REBUILD, "REBUILD", STATUS_FLAG}, - {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG}, - {LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG}, -+ {LV_NOSCAN, NULL, 0}, - {POOL_METADATA_SPARE, NULL, 0}, - {RAID, NULL, 0}, - {RAID_META, NULL, 0}, -@@ -144,8 +145,8 @@ int print_flags(uint64_t status, int type, char *buffer, size_t size) - return 0; - - if (status) -- log_warn("Metadata inconsistency: Not all flags successfully " -- "exported."); -+ log_warn(INTERNAL_ERROR "Metadata inconsistency: " -+ "Not all flags successfully exported."); - - return 1; - } -diff --git a/lib/locking/file_locking.c b/lib/locking/file_locking.c -index 5e49bc4..b6b2b10 100644 ---- a/lib/locking/file_locking.c -+++ b/lib/locking/file_locking.c -@@ -305,7 +305,7 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource, - break; - case LCK_READ: - log_very_verbose("Locking LV %s (R)", resource); -- if (!lv_activate_with_filter(cmd, resource, 0, lv_ondisk(lv))) -+ if (!lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv))) - return 0; - break; - case LCK_PREAD: -@@ -318,7 +318,7 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource, - break; - case LCK_EXCL: - log_very_verbose("Locking LV %s (EX)", resource); -- if (!lv_activate_with_filter(cmd, resource, 1, lv_ondisk(lv))) -+ if (!lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv))) - return 0; - break; - default: -diff --git a/lib/locking/no_locking.c b/lib/locking/no_locking.c -index 5f3f0b6..4772706 100644 ---- a/lib/locking/no_locking.c -+++ b/lib/locking/no_locking.c -@@ -48,11 +48,11 @@ static int _no_lock_resource(struct cmd_context *cmd, const char *resource, - case LCK_UNLOCK: - return lv_resume_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1: 0, 0, (flags & LCK_REVERT) ? 1 : 0, lv_ondisk(lv)); - case LCK_READ: -- return lv_activate_with_filter(cmd, resource, 0, lv_ondisk(lv)); -+ return lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv)); - case LCK_WRITE: - return lv_suspend_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1 : 0, 0, lv_ondisk(lv), lv); - case LCK_EXCL: -- return lv_activate_with_filter(cmd, resource, 1, lv_ondisk(lv)); -+ return lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv)); - default: - break; - } -diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c -index f42db1d..22327d7 100644 ---- a/lib/metadata/lv_manip.c -+++ b/lib/metadata/lv_manip.c -@@ -5421,6 +5421,8 @@ int set_lv(struct cmd_context *cmd, struct logical_volume *lv, - if (!dev_close_immediate(dev)) - stack; - -+ lv->status &= ~LV_NOSCAN; -+ - return 1; - } - -@@ -5977,6 +5979,10 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, - goto out; - } - -+ /* Do not scan this LV until properly zeroed. */ -+ if (lp->zero) -+ lv->status |= LV_NOSCAN; -+ - if (lv_is_thin_pool(lv)) { - if (is_change_activating(lp->activate)) { - if (vg_is_clustered(lv->vg)) { -diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h -index 308dcfe..1e9543a 100644 ---- a/lib/metadata/metadata-exported.h -+++ b/lib/metadata/metadata-exported.h -@@ -102,6 +102,7 @@ - #define LV_WRITEMOSTLY UINT64_C(0x0000020000000000) /* LV (RAID1) */ - - #define LV_ACTIVATION_SKIP UINT64_C(0x0000040000000000) /* LV */ -+#define LV_NOSCAN UINT64_C(0x0000080000000000) /* LV */ - - /* Format features flags */ - #define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ -diff --git a/udev/11-dm-lvm.rules.in b/udev/11-dm-lvm.rules.in -index f21d0aa..5032280 100644 ---- a/udev/11-dm-lvm.rules.in -+++ b/udev/11-dm-lvm.rules.in -@@ -20,6 +20,21 @@ ENV{DM_UUID}!="LVM-?*", GOTO="lvm_end" - # Use DM name and split it up into its VG/LV/layer constituents. - IMPORT{program}="(DM_EXEC)/dmsetup splitname --nameprefixes --noheadings --rows $env{DM_NAME}" - -+# DM_SUBSYSTEM_UDEV_FLAG0 is the 'NOSCAN' flag for LVM subsystem. -+# This flag is used to temporarily disable selected rules to prevent any -+# processing or scanning done on the LVM volume before LVM has any chance -+# to zero any stale metadata found within the LV data area. Such stale -+# metadata could cause false claim of the LV device, keeping it open etc. -+# -+# If the NOSCAN flag is present, backup selected existing flags used to -+# disable rules, then set them firmly so those selected rules are surely skipped. -+# Restore these flags once the NOSCAN flag is dropped (which is normally any -+# uevent that follows for this LV, even an artificially generated one). -+ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_NOSCAN}="1", ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" -+ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", IMPORT{db}="DM_NOSCAN", IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" -+ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}", \ -+ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG_OLD}="", ENV{DM_NOSCAN}="" -+ - ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}=="1", GOTO="lvm_end" - - OPTIONS+="event_timeout=180" -diff --git a/udev/13-dm-disk.rules.in b/udev/13-dm-disk.rules.in -index 1920260..4b64dd6 100644 ---- a/udev/13-dm-disk.rules.in -+++ b/udev/13-dm-disk.rules.in -@@ -18,6 +18,7 @@ SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}" - ENV{DM_UUID}=="?*", SYMLINK+="disk/by-id/dm-uuid-$env{DM_UUID}" - - ENV{DM_SUSPENDED}=="1", GOTO="dm_end" -+ENV{DM_NOSCAN}=="1", GOTO="dm_watch" - - (BLKID_RULE) - ENV{DM_UDEV_LOW_PRIORITY_FLAG}=="1", OPTIONS="link_priority=-100" -@@ -32,7 +33,7 @@ ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk - # (like creating a filesystem, changing filesystem label etc.). - # - # But let's use this until we have something better... -- -+LABEL="dm_watch" - OPTIONS+="watch" - - LABEL="dm_end" -diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in -index ba43396..3303f4d 100644 ---- a/udev/69-dm-lvm-metad.rules.in -+++ b/udev/69-dm-lvm-metad.rules.in -@@ -17,6 +17,8 @@ - SUBSYSTEM!="block", GOTO="lvm_end" - (LVM_EXEC_RULE) - -+ENV{DM_NOSCAN}=="1", GOTO="lvm_end" -+ - # If the PV label got lost, inform lvmetad immediately. - # Detect the lost PV label by comparing previous ID_FS_TYPE value with current one. - ENV{.ID_FS_TYPE_NEW}="$env{ID_FS_TYPE}" diff --git a/SOURCES/lvm2-2_02_104-udev-systemd-background-job-for-pvscan-cache-aay.patch b/SOURCES/lvm2-2_02_104-udev-systemd-background-job-for-pvscan-cache-aay.patch deleted file mode 100644 index f75cf9b..0000000 --- a/SOURCES/lvm2-2_02_104-udev-systemd-background-job-for-pvscan-cache-aay.patch +++ /dev/null @@ -1,219 +0,0 @@ -commit 6a2f2b0374b679ddff67668c9830df22d9a044a8 -Author: Peter Rajnoha -Date: Fri Oct 18 11:22:35 2013 +0200 - - udev_systemd_background_jobs ---- - WHATS_NEW | 2 ++ - configure | 26 +++++++++++++++++++++---- - configure.in | 11 +++++++++++ - scripts/Makefile.in | 2 ++ - scripts/lvm2_pvscan_systemd_red_hat@.service.in | 11 +++++++++++ - udev/69-dm-lvm-metad.rules.in | 2 +- - udev/Makefile.in | 8 +++++++- - 7 files changed, 56 insertions(+), 6 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index e49a98d..2dfeada 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,7 @@ - Version 2.02.104 - - =================================== -+ Add configure --enable-udev-systemd-background-jobs. -+ Add lvm2-pvscan@.service to run pvscan as a service for lvmetad/autoactivation. - Fix possible race during daemon worker thread creation (lvmetad). - Fix possible deadlock while clearing lvmetad cache for full rescan. - Fix possible race while creating/destroying memory pools. -diff --git a/configure b/configure -index 20a706a..002a7d2 100755 ---- a/configure -+++ b/configure -@@ -613,6 +613,7 @@ DMEVENTD_PIDFILE - WRITE_INSTALL - UDEV_HAS_BUILTIN_BLKID - UDEV_RULE_EXEC_DETECTION -+UDEV_SYSTEMD_BACKGROUND_JOBS - UDEV_SYNC - UDEV_RULES - UDEV_PC -@@ -849,6 +850,7 @@ enable_valgrind_pool - enable_devmapper - enable_lvmetad - with_lvmetad_pidfile -+enable_udev_systemd_background_jobs - enable_udev_sync - enable_udev_rules - enable_udev_rule_exec_detection -@@ -1552,6 +1554,9 @@ Optional Features: - --enable-valgrind-pool enable valgrind awareness of pools - --disable-devmapper disable LVM2 device-mapper interaction - --enable-lvmetad enable the LVM Metadata Daemon -+ --enable-udev-systemd-background-jobs -+ enable udev-systemd protocol to instantiate a -+ service for background job - --enable-udev_sync enable synchronisation with udev processing - --enable-udev_rules install rule files needed for udev synchronisation - --enable-udev-rule-exec-detection -@@ -9090,6 +9095,19 @@ _ACEOF - fi - - ################################################################################ -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use udev-systemd protocol for jobs in background" >&5 -+$as_echo_n "checking whether to use udev-systemd protocol for jobs in background... " >&6; } -+# Check whether --enable-udev-systemd-background-jobs was given. -+if test "${enable_udev_systemd_background_jobs+set}" = set; then : -+ enableval=$enable_udev_systemd_background_jobs; UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval -+else -+ UDEV_SYSTEMD_BACKGROUND_JOBS=no -+fi -+ -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $UDEV_SYSTEMD_BACKGROUND_JOBS" >&5 -+$as_echo "$UDEV_SYSTEMD_BACKGROUND_JOBS" >&6; } -+ -+################################################################################ - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable synchronisation with udev processing" >&5 - $as_echo_n "checking whether to enable synchronisation with udev processing... " >&6; } - # Check whether --enable-udev_sync was given. -@@ -9668,8 +9686,7 @@ if [ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \ - fi - - ################################################################################ --if [ "$DMEVENTD" = yes -o "$CLVMD" != none ] ; then -- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_lock in -lpthread" >&5 -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_lock in -lpthread" >&5 - $as_echo_n "checking for pthread_mutex_lock in -lpthread... " >&6; } - if test "${ac_cv_lib_pthread_pthread_mutex_lock+set}" = set; then : - $as_echo_n "(cached) " >&6 -@@ -9711,7 +9728,6 @@ else - hard_bailout - fi - --fi - - ################################################################################ - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable selinux support" >&5 -@@ -10933,8 +10949,9 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'` - - - -+ - ################################################################################ --ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/default.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" -+ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/default.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" - - cat >confcache <<\_ACEOF - # This file is a shell script that caches the results of configure -@@ -11671,6 +11688,7 @@ do - "scripts/lvm2_lvmetad_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_init_red_hat" ;; - "scripts/lvm2_lvmetad_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.socket" ;; - "scripts/lvm2_lvmetad_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.service" ;; -+ "scripts/lvm2_pvscan_systemd_red_hat@.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_pvscan_systemd_red_hat@.service" ;; - "scripts/lvm2_monitoring_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_init_red_hat" ;; - "scripts/dm_event_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/dm_event_systemd_red_hat.socket" ;; - "scripts/dm_event_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/dm_event_systemd_red_hat.service" ;; -diff --git a/configure.in b/configure.in -index 3bd2439..07b0afc 100644 ---- a/configure.in -+++ b/configure.in -@@ -940,6 +940,15 @@ if test x$BUILD_LVMETAD = xyes; then - fi - - ################################################################################ -+dnl -- Enable udev-systemd protocol to instantiate a service for background jobs -+AC_MSG_CHECKING(whether to use udev-systemd protocol for jobs in background) -+AC_ARG_ENABLE(udev-systemd-background-jobs, -+ AC_HELP_STRING([--enable-udev-systemd-background-jobs], -+ [enable udev-systemd protocol to instantiate a service for background job]), -+ UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval, UDEV_SYSTEMD_BACKGROUND_JOBS=no) -+AC_MSG_RESULT($UDEV_SYSTEMD_BACKGROUND_JOBS) -+ -+################################################################################ - dnl -- Enable udev synchronisation - AC_MSG_CHECKING(whether to enable synchronisation with udev processing) - AC_ARG_ENABLE(udev_sync, -@@ -1626,6 +1635,7 @@ AC_SUBST(UDEV_LIBS) - AC_SUBST(UDEV_PC) - AC_SUBST(UDEV_RULES) - AC_SUBST(UDEV_SYNC) -+AC_SUBST(UDEV_SYSTEMD_BACKGROUND_JOBS) - AC_SUBST(UDEV_RULE_EXEC_DETECTION) - AC_SUBST(UDEV_HAS_BUILTIN_BLKID) - AC_SUBST(CUNIT_LIBS) -@@ -1700,6 +1710,7 @@ scripts/cmirrord_init_red_hat - scripts/lvm2_lvmetad_init_red_hat - scripts/lvm2_lvmetad_systemd_red_hat.socket - scripts/lvm2_lvmetad_systemd_red_hat.service -+scripts/lvm2_pvscan_systemd_red_hat@.service - scripts/lvm2_monitoring_init_red_hat - scripts/dm_event_systemd_red_hat.socket - scripts/dm_event_systemd_red_hat.service -diff --git a/scripts/Makefile.in b/scripts/Makefile.in -index a658903..fac7e40 100644 ---- a/scripts/Makefile.in -+++ b/scripts/Makefile.in -@@ -107,6 +107,7 @@ endif - ifeq ("@BUILD_LVMETAD@", "yes") - $(INSTALL_DATA) lvm2_lvmetad_systemd_red_hat.socket $(systemd_unit_dir)/lvm2-lvmetad.socket - $(INSTALL_DATA) lvm2_lvmetad_systemd_red_hat.service $(systemd_unit_dir)/lvm2-lvmetad.service -+ $(INSTALL_DATA) lvm2_pvscan_systemd_red_hat@.service $(systemd_unit_dir)/lvm2-pvscan@.service - endif - - install_tmpfiles_configuration: -@@ -118,6 +119,7 @@ DISTCLEAN_TARGETS += clvmd_init_red_hat cmirrord_init_red_hat \ - dm_event_systemd_red_hat.socket dm_event_systemd_red_hat.service \ - lvm2_monitoring_systemd_red_hat.service \ - lvm2_lvmetad_systemd_red_hat.socket lvm2_lvmetad_systemd_red_hat.service \ -+ lvm2_lvmetad_systemd_red_hat@.service \ - lvm2_tmpfiles_red_hat.conf blk_availability_init_red_hat \ - blk_availability_systemd_red_hat.service \ - blkdeactivate.sh -diff --git a/scripts/lvm2_pvscan_systemd_red_hat@.service.in b/scripts/lvm2_pvscan_systemd_red_hat@.service.in -new file mode 100644 -index 0000000..9d91b5e ---- /dev/null -+++ b/scripts/lvm2_pvscan_systemd_red_hat@.service.in -@@ -0,0 +1,11 @@ -+[Unit] -+Description=LVM2 PV scan on %I -+Documentation=man:pvscan(8) -+DefaultDependencies=no -+After=lvm2-lvmetad.socket %i.device -+Before=shutdown.target -+Conflicts=shutdown.target -+ -+[Service] -+Type=simple -+ExecStart=@sbindir@/pvscan --cache --activate ay %I -diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in -index 3303f4d..3e303b1 100644 ---- a/udev/69-dm-lvm-metad.rules.in -+++ b/udev/69-dm-lvm-metad.rules.in -@@ -79,6 +79,6 @@ LABEL="lvm_scan" - # MD | | X | X* | | - # loop | | X | X* | | - # other | X | | X | | X --RUN+="(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major $major --minor $minor", ENV{LVM_SCANNED}="1" -+(PVSCAN_RULE) - - LABEL="lvm_end" -diff --git a/udev/Makefile.in b/udev/Makefile.in -index 5c15bdb..fdf43df 100644 ---- a/udev/Makefile.in -+++ b/udev/Makefile.in -@@ -46,8 +46,14 @@ else - BLKID_RULE=IMPORT{program}=\"${SBIN}\/blkid -o udev -p \$$tempnode\" - endif - -+ifeq ("@UDEV_SYSTEMD_BACKGROUND_JOBS@", "yes") -+PVSCAN_RULE=ENV{SYSTEMD_WANTS}=\"lvm2-pvscan@\$$devnode.service\" -+else -+PVSCAN_RULE=RUN\+\=\"$(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major \$$major --minor \$$minor\", ENV{LVM_SCANNED}=\"1\" -+endif -+ - %.rules: %.rules.in -- $(SED) -e "s+(DM_DIR)+$(DM_DIR)+;s+(BLKID_RULE)+$(BLKID_RULE)+;s+(DM_EXEC_RULE)+$(DM_EXEC_RULE)+;s+(DM_EXEC)+$(DM_EXEC)+;s+(LVM_EXEC_RULE)+$(LVM_EXEC_RULE)+;s+(LVM_EXEC)+$(LVM_EXEC)+;" $< >$@ -+ $(SED) -e "s+(DM_DIR)+$(DM_DIR)+;s+(BLKID_RULE)+$(BLKID_RULE)+;s+(PVSCAN_RULE)+$(PVSCAN_RULE)+;s+(DM_EXEC_RULE)+$(DM_EXEC_RULE)+;s+(DM_EXEC)+$(DM_EXEC)+;s+(LVM_EXEC_RULE)+$(LVM_EXEC_RULE)+;s+(LVM_EXEC)+$(LVM_EXEC)+;" $< >$@ - - %_install: %.rules - $(INSTALL_DATA) -D $< $(udevdir)/$(pvid_to_pvmeta, pvid); - pvid_old = dm_hash_lookup_binary(s->device_to_pvid, &device, sizeof(device)); -+ char *vgid = dm_hash_lookup(s->pvid_to_vgid, pvid); -+ -+ if (vgid && !(vgid = dm_strdup(vgid))) { -+ unlock_pvid_to_pvmeta(s); -+ return reply_fail("out of memory"); -+ } -+ - dm_hash_remove_binary(s->device_to_pvid, &device, sizeof(device)); - dm_hash_remove(s->pvid_to_pvmeta, pvid); -- vg_remove_if_missing(s, dm_hash_lookup(s->pvid_to_vgid, pvid)); - unlock_pvid_to_pvmeta(s); - -+ if (vgid) { -+ lock_vg(s, vgid); -+ vg_remove_if_missing(s, vgid, 1); -+ unlock_vg(s, vgid); -+ dm_free(vgid); -+ } -+ - if (pvid_old) - dm_free(pvid_old); - -@@ -816,8 +829,8 @@ static response pv_clear_all(lvmetad_state *s, request r) - DEBUGLOG(s, "pv_clear_all"); - - lock_pvid_to_pvmeta(s); -- lock_vgid_to_metadata(s); - lock_pvid_to_vgid(s); -+ lock_vgid_to_metadata(s); - - destroy_metadata_hashes(s); - create_metadata_hashes(s); -diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c -index f43283f..8940704 100644 ---- a/lib/cache/lvmetad.c -+++ b/lib/cache/lvmetad.c -@@ -392,8 +392,6 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna - pvl->pv->dev = lvmcache_device(info); - if (!pvl->pv->dev) - pvl->pv->status |= MISSING_PV; -- else -- check_reappeared_pv(vg, pvl->pv); - if (!lvmcache_fid_add_mdas_pv(info, fid)) { - vg = NULL; - goto_out; /* FIXME error path */ -diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c -index 4ffd502..8571e0a 100644 ---- a/lib/metadata/metadata.c -+++ b/lib/metadata/metadata.c -@@ -2883,10 +2883,11 @@ int vg_missing_pv_count(const struct volume_group *vg) - return ret; - } - --void check_reappeared_pv(struct volume_group *correct_vg, -- struct physical_volume *pv) -+static int _check_reappeared_pv(struct volume_group *correct_vg, -+ struct physical_volume *pv, int act) - { - struct pv_list *pvl; -+ int rv = 0; - - /* - * Skip these checks in case the tool is going to deal with missing -@@ -2894,21 +2895,47 @@ void check_reappeared_pv(struct volume_group *correct_vg, - * confusing. - */ - if (correct_vg->cmd->handles_missing_pvs) -- return; -+ return rv; - - dm_list_iterate_items(pvl, &correct_vg->pvs) - if (pv->dev == pvl->pv->dev && is_missing_pv(pvl->pv)) { -- log_warn("Missing device %s reappeared, updating " -- "metadata for VG %s to version %u.", -- pv_dev_name(pvl->pv), pv_vg_name(pvl->pv), -- correct_vg->seqno); -+ if (act) -+ log_warn("Missing device %s reappeared, updating " -+ "metadata for VG %s to version %u.", -+ pv_dev_name(pvl->pv), pv_vg_name(pvl->pv), -+ correct_vg->seqno); - if (pvl->pv->pe_alloc_count == 0) { -- pv->status &= ~MISSING_PV; -- pvl->pv->status &= ~MISSING_PV; -- } else -+ if (act) { -+ pv->status &= ~MISSING_PV; -+ pvl->pv->status &= ~MISSING_PV; -+ } -+ ++ rv; -+ } else if (act) - log_warn("Device still marked missing because of allocated data " - "on it, remove volumes and consider vgreduce --removemissing."); - } -+ return rv; -+} -+ -+static int _repair_inconsistent_vg(struct volume_group *vg) -+{ -+ unsigned saved_handles_missing_pvs = vg->cmd->handles_missing_pvs; -+ -+ vg->cmd->handles_missing_pvs = 1; -+ if (!vg_write(vg)) { -+ log_error("Automatic metadata correction failed"); -+ vg->cmd->handles_missing_pvs = saved_handles_missing_pvs; -+ return 0; -+ } -+ -+ vg->cmd->handles_missing_pvs = saved_handles_missing_pvs; -+ -+ if (!vg_commit(vg)) { -+ log_error("Automatic metadata correction commit failed"); -+ return 0; -+ } -+ -+ return 1; - } - - static int _check_mda_in_use(struct metadata_area *mda, void *_in_use) -@@ -2951,12 +2978,12 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, - int inconsistent_mdas = 0; - int inconsistent_mda_count = 0; - unsigned use_precommitted = precommitted; -- unsigned saved_handles_missing_pvs = cmd->handles_missing_pvs; - struct dm_list *pvids; - struct pv_list *pvl, *pvl2; - struct dm_list all_pvs; - char uuid[64] __attribute__((aligned(8))); - unsigned seqno = 0; -+ int reappeared = 0; - - if (is_orphan_vg(vgname)) { - if (use_precommitted) { -@@ -2969,8 +2996,16 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, - } - - if (lvmetad_active() && !use_precommitted) { -- *consistent = 1; -- return lvmcache_get_vg(cmd, vgname, vgid, precommitted); -+ if ((correct_vg = lvmcache_get_vg(cmd, vgname, vgid, precommitted))) { -+ dm_list_iterate_items(pvl, &correct_vg->pvs) -+ if (pvl->pv->dev) -+ reappeared += _check_reappeared_pv(correct_vg, pvl->pv, *consistent); -+ if (reappeared && *consistent) -+ *consistent = _repair_inconsistent_vg(correct_vg); -+ else -+ *consistent = !reappeared; -+ } -+ return correct_vg; - } - - /* -@@ -3339,22 +3374,11 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, - * update metadata and remove MISSING flag - */ - dm_list_iterate_items(pvl, &all_pvs) -- check_reappeared_pv(correct_vg, pvl->pv); -+ _check_reappeared_pv(correct_vg, pvl->pv, 1); - -- cmd->handles_missing_pvs = 1; -- if (!vg_write(correct_vg)) { -- log_error("Automatic metadata correction failed"); -+ if (!_repair_inconsistent_vg(correct_vg)) { - _free_pv_list(&all_pvs); - release_vg(correct_vg); -- cmd->handles_missing_pvs = saved_handles_missing_pvs; -- return NULL; -- } -- cmd->handles_missing_pvs = saved_handles_missing_pvs; -- -- if (!vg_commit(correct_vg)) { -- log_error("Automatic metadata correction commit " -- "failed"); -- release_vg(correct_vg); - return NULL; - } - -diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h -index 2408c23..21ac204 100644 ---- a/lib/metadata/metadata.h -+++ b/lib/metadata/metadata.h -@@ -486,7 +486,4 @@ int add_pv_to_vg(struct volume_group *vg, const char *pv_name, - uint64_t find_min_mda_size(struct dm_list *mdas); - char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags); - --void check_reappeared_pv(struct volume_group *correct_vg, -- struct physical_volume *pv); -- - #endif -diff --git a/libdaemon/client/daemon-io.c b/libdaemon/client/daemon-io.c -index 906f375..e2c5180 100644 ---- a/libdaemon/client/daemon-io.c -+++ b/libdaemon/client/daemon-io.c -@@ -38,7 +38,7 @@ int buffer_read(int fd, struct buffer *buffer) { - result = read(fd, buffer->mem + buffer->used, buffer->allocated - buffer->used); - if (result > 0) { - buffer->used += result; -- if (!strncmp((buffer->mem) + buffer->used - 4, "\n##\n", 4)) { -+ if (buffer->used >= 4 && !strncmp((buffer->mem) + buffer->used - 4, "\n##\n", 4)) { - buffer->used -= 4; - buffer->mem[buffer->used] = 0; - break; /* success, we have the full message now */ -diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c -index df2c852..b114b9f 100644 ---- a/libdaemon/server/daemon-server.c -+++ b/libdaemon/server/daemon-server.c -@@ -381,6 +381,7 @@ static void *client_thread(void *baton) - request req; - response res; - -+ b->client.thread_id = pthread_self(); - buffer_init(&req.buffer); - - while (1) { -@@ -431,6 +432,7 @@ static int handle_connect(daemon_state s) - struct sockaddr_un sockaddr; - client_handle client = { .thread_id = 0 }; - socklen_t sl = sizeof(sockaddr); -+ pthread_t tid; - - client.socket_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl); - if (client.socket_fd < 0) -@@ -446,10 +448,10 @@ static int handle_connect(daemon_state s) - baton->s = s; - baton->client = client; - -- if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton)) -+ if (pthread_create(&tid, NULL, client_thread, baton)) - return 0; - -- pthread_detach(baton->client.thread_id); -+ pthread_detach(tid); - - return 1; - } -diff --git a/libdm/Makefile.in b/libdm/Makefile.in -index bddb0a0..2aa70d4 100644 ---- a/libdm/Makefile.in -+++ b/libdm/Makefile.in -@@ -57,7 +57,7 @@ include $(top_builddir)/make.tmpl - DEFS += -DDM_DEVICE_UID=@DM_DEVICE_UID@ -DDM_DEVICE_GID=@DM_DEVICE_GID@ \ - -DDM_DEVICE_MODE=@DM_DEVICE_MODE@ - --LIBS += $(SELINUX_LIBS) $(UDEV_LIBS) -+LIBS += $(SELINUX_LIBS) $(UDEV_LIBS) $(PTHREAD_LIBS) - - device-mapper: all - -diff --git a/libdm/mm/pool-fast.c b/libdm/mm/pool-fast.c -index 2b494d6..edb31a0 100644 ---- a/libdm/mm/pool-fast.c -+++ b/libdm/mm/pool-fast.c -@@ -62,7 +62,9 @@ struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint) - while (new_size < p->chunk_size) - new_size <<= 1; - p->chunk_size = new_size; -+ pthread_mutex_lock(&_dm_pools_mutex); - dm_list_add(&_dm_pools, &p->list); -+ pthread_mutex_unlock(&_dm_pools_mutex); - return p; - } - -@@ -77,7 +79,9 @@ void dm_pool_destroy(struct dm_pool *p) - c = pr; - } - -+ pthread_mutex_lock(&_dm_pools_mutex); - dm_list_del(&p->list); -+ pthread_mutex_unlock(&_dm_pools_mutex); - dm_free(p); - } - -diff --git a/libdm/mm/pool.c b/libdm/mm/pool.c -index fd08307..ef006a4 100644 ---- a/libdm/mm/pool.c -+++ b/libdm/mm/pool.c -@@ -15,9 +15,10 @@ - - #include "dmlib.h" - #include -+#include - --/* FIXME: thread unsafe */ - static DM_LIST_INIT(_dm_pools); -+static pthread_mutex_t _dm_pools_mutex = PTHREAD_MUTEX_INITIALIZER; - void dm_pools_check_leaks(void); - - #ifdef DEBUG_ENFORCE_POOL_LOCKING -@@ -81,8 +82,11 @@ void dm_pools_check_leaks(void) - { - struct dm_pool *p; - -- if (dm_list_empty(&_dm_pools)) -+ pthread_mutex_lock(&_dm_pools_mutex); -+ if (dm_list_empty(&_dm_pools)) { -+ pthread_mutex_unlock(&_dm_pools_mutex); - return; -+ } - - log_error("You have a memory leak (not released memory pool):"); - dm_list_iterate_items(p, &_dm_pools) { -@@ -94,6 +98,7 @@ void dm_pools_check_leaks(void) - log_error(" [%p] %s", p, p->name); - #endif - } -+ pthread_mutex_unlock(&_dm_pools_mutex); - log_error(INTERNAL_ERROR "Unreleased memory pool(s) found."); - } - -diff --git a/test/Makefile.in b/test/Makefile.in -index 1b9789f..5685544 100644 ---- a/test/Makefile.in -+++ b/test/Makefile.in -@@ -34,7 +34,8 @@ T ?= . - S ?= @ # never match anything by default - VERBOSE ?= 0 - ALL = $(shell find $(srcdir) \( -path \*/shell/\*.sh -or -path \*/api/\*.sh \) | sort) --RUN = $(shell find $(srcdir) -regextype posix-egrep \( -path \*/shell/\*.sh -or -path \*/api/\*.sh \) -and -regex "$(srcdir)/.*($(T)).*" -and -not -regex "$(srcdir)/.*($(S)).*" | sort) -+comma = , -+RUN = $(shell find $(srcdir) -regextype posix-egrep \( -path \*/shell/\*.sh -or -path \*/api/\*.sh \) -and -regex "$(srcdir)/.*($(subst $(comma),|,$(T))).*" -and -not -regex "$(srcdir)/.*($(subst $(comma),|,$(S))).*" | sort) - RUN_BASE = $(subst $(srcdir)/,,$(RUN)) - - # Shell quote; -diff --git a/test/shell/lvmcache-exercise.sh b/test/shell/lvmcache-exercise.sh -index b1e2b92..6e8efda 100644 ---- a/test/shell/lvmcache-exercise.sh -+++ b/test/shell/lvmcache-exercise.sh -@@ -1,5 +1,5 @@ - #!/bin/sh --# Copyright (C) 2008 Red Hat, Inc. All rights reserved. -+# Copyright (C) 2008-2013 Red Hat, Inc. All rights reserved. - # - # This copyrighted material is made available to anyone wishing to use, - # modify, copy, or redistribute it subject to the terms and conditions -@@ -14,10 +14,23 @@ - aux prepare_pvs 5 - - vgcreate $vg1 "$dev1" --vgcreate $vg2 "$dev3" -+vgcreate $vg2 "$dev3" "$dev4" "$dev5" - - aux disable_dev "$dev1" - pvscan - vgcreate $vg1 "$dev2" - aux enable_dev "$dev1" - pvs -+ -+# reappearing device (rhbz 995440) -+lvcreate -aey -m2 --type mirror -l4 --alloc anywhere --corelog -n $lv1 $vg2 -+ -+aux disable_dev "$dev3" -+lvconvert --yes --repair $vg2/$lv1 -+aux enable_dev "$dev3" -+ -+# here it should fix any reappeared devices -+lvs -+ -+lvs -a $vg2 -o+devices 2>&1 | tee out -+not grep reappeared out -diff --git a/test/shell/vgrename-usage.sh b/test/shell/vgrename-usage.sh -index 2b8ac5a..59576ac 100644 ---- a/test/shell/vgrename-usage.sh -+++ b/test/shell/vgrename-usage.sh -@@ -38,3 +38,18 @@ vgcreate $vg1 "$dev1" - vgcreate $vg2 "$dev2" - not vgrename $vg1 $vg2 - vgremove $vg1 $vg2 -+ -+# vgrename duplicate name -+vgcreate $vg1 "$dev1" -+aux disable_dev "$dev1" -+vgcreate $vg1 "$dev2" -+UUID=$(vgs --noheading -o vg_uuid $vg1) -+aux enable_dev "$dev1" -+ -+not vgrename $vg1 $vg2 -+vgrename $UUID $vg2 -+not vgrename $UUID $vg1 -+ -+vgs -+ -+vgremove $vg1 $vg2 -diff --git a/tools/vgrename.c b/tools/vgrename.c -index 154a6f3..b5e778f 100644 ---- a/tools/vgrename.c -+++ b/tools/vgrename.c -@@ -83,6 +83,8 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, - if (!lvmetad_vg_list_to_lvmcache(cmd)) - stack; - -+ lvmcache_label_scan(cmd, 2); -+ - /* Avoid duplicates */ - if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) { - log_error("No complete volume groups found"); diff --git a/SOURCES/lvm2-2_02_104-various-udev-support-fixes.patch b/SOURCES/lvm2-2_02_104-various-udev-support-fixes.patch deleted file mode 100644 index 9a9ecf3..0000000 --- a/SOURCES/lvm2-2_02_104-various-udev-support-fixes.patch +++ /dev/null @@ -1,379 +0,0 @@ - WHATS_NEW | 3 +++ - daemons/clvmd/lvm-functions.c | 2 +- - lib/activate/activate.c | 21 +++++++++++++-------- - lib/activate/activate.h | 20 ++++++++++++++++---- - lib/activate/dev_manager.c | 18 +++++++++++------- - lib/format_text/flags.c | 1 + - lib/locking/file_locking.c | 6 ++++-- - lib/locking/no_locking.c | 6 ++++-- - lib/metadata/lv_manip.c | 3 +++ - lib/metadata/metadata-exported.h | 8 +++++++- - lib/metadata/thin_manip.c | 1 + - tools/lvconvert.c | 20 ++++++++++++-------- - udev/11-dm-lvm.rules.in | 2 +- - udev/69-dm-lvm-metad.rules.in | 3 ++- - 14 files changed, 79 insertions(+), 35 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 7fd107a..2532a99 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,8 @@ - Version 2.02.104 - - =================================== -+ Fix missing lvmetad scan for PVs found on MD partitions. -+ Add internal flag for temporary LVs to properly direct udev to not interfere. -+ Respect DM_UDEV_DISABLE_OTHER_RULES_FLAG in lvmetad udev rules. - Workaround VG refresh race during autoactivation by retrying the refresh. - Add dev-block-:.device systemd alias for complete PV tracking. - Use major:minor as short form of --major and --minor arg for pvscan --cache. -diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c -index f544218..b15732f 100644 ---- a/daemons/clvmd/lvm-functions.c -+++ b/daemons/clvmd/lvm-functions.c -@@ -401,7 +401,7 @@ static int do_activate_lv(char *resource, unsigned char command, unsigned char l - } - - /* Now activate it */ -- if (!lv_activate(cmd, resource, exclusive, 0, NULL)) -+ if (!lv_activate(cmd, resource, exclusive, 0, 0, NULL)) - goto error; - - return 0; -diff --git a/lib/activate/activate.c b/lib/activate/activate.c -index c077113..6ab95aa 100644 ---- a/lib/activate/activate.c -+++ b/lib/activate/activate.c -@@ -338,12 +338,12 @@ int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s, - return 1; - } - int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan, -- struct logical_volume *lv) -+ int temporary, struct logical_volume *lv) - { - return 1; - } - int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, -- int noscan, struct logical_volume *lv) -+ int noscan, int temporary, struct logical_volume *lv) - { - return 1; - } -@@ -2028,10 +2028,11 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s, - if (filter) - laopts->read_only = _passes_readonly_filter(cmd, lv); - -- log_debug_activation("Activating %s/%s%s%s%s.", lv->vg->name, lv->name, -+ log_debug_activation("Activating %s/%s%s%s%s%s.", lv->vg->name, lv->name, - laopts->exclusive ? " exclusively" : "", - laopts->read_only ? " read-only" : "", -- laopts->noscan ? " noscan" : ""); -+ laopts->noscan ? " noscan" : "", -+ laopts->temporary ? " temporary" : ""); - - if (!lv_info(cmd, lv, 0, &info, 0, 0)) - goto_out; -@@ -2069,9 +2070,11 @@ out: - - /* Activate LV */ - int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, -- int noscan, struct logical_volume *lv) -+ int noscan, int temporary, struct logical_volume *lv) - { -- struct lv_activate_opts laopts = { .exclusive = exclusive, .noscan = noscan }; -+ struct lv_activate_opts laopts = { .exclusive = exclusive, -+ .noscan = noscan, -+ .temporary = temporary }; - - if (!_lv_activate(cmd, lvid_s, &laopts, 0, lv)) - return_0; -@@ -2081,9 +2084,11 @@ int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, - - /* Activate LV only if it passes filter */ - int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, -- int noscan, struct logical_volume *lv) -+ int noscan, int temporary, struct logical_volume *lv) - { -- struct lv_activate_opts laopts = { .exclusive = exclusive, .noscan = noscan }; -+ struct lv_activate_opts laopts = { .exclusive = exclusive, -+ .noscan = noscan, -+ .temporary = temporary }; - - if (!_lv_activate(cmd, lvid_s, &laopts, 1, lv)) - return_0; -diff --git a/lib/activate/activate.h b/lib/activate/activate.h -index 4eac320..df888cd 100644 ---- a/lib/activate/activate.h -+++ b/lib/activate/activate.h -@@ -38,7 +38,18 @@ struct lv_activate_opts { - int skip_in_use; - unsigned revert; - unsigned read_only; -- unsigned noscan; -+ unsigned noscan; /* Mark this LV to avoid its scanning. This also -+ directs udev to use proper udev flag to avoid -+ any scanning in udev. This udev flag is automatically -+ dropped in udev db on any spurious event that follows. */ -+ unsigned temporary; /* Mark this LV as temporary. It means, the LV -+ * is created, used and deactivated within single -+ * LVM command execution. Such LVs are mostly helper -+ * LVs to do some action or cleanup before the proper -+ * LV is created. This also directs udev to use proper -+ * set of flags to avoid any scanning in udev. These udev -+ * flags are persistent in udev db for any spurious event -+ * that follows. */ - }; - - /* target attribute flags */ -@@ -81,9 +92,10 @@ int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned o - int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only, struct logical_volume *lv); - int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, - unsigned origin_only, unsigned exclusive, unsigned revert, struct logical_volume *lv); --int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan, struct logical_volume *lv); --int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, -- int exclusive, int noscan, struct logical_volume *lv); -+int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, -+ int noscan, int temporary, struct logical_volume *lv); -+int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, -+ int noscan, int temporary, struct logical_volume *lv); - int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, struct logical_volume *lv); - - int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv); -diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c -index 0f5a04c..0b911f2 100644 ---- a/lib/activate/dev_manager.c -+++ b/lib/activate/dev_manager.c -@@ -1400,7 +1400,7 @@ static int _check_udev_fallback(struct cmd_context *cmd) - #endif /* UDEV_SYNC_SUPPORT */ - - static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *lv, -- const char *layer, uint16_t flags) -+ const char *layer, int noscan, int temporary) - { - uint16_t udev_flags = 0; - -@@ -1449,9 +1449,14 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *l - DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; - - /* -- * Firmly set requested flags. -+ * LVM subsystem specific flags. - */ -- udev_flags |= flags; -+ if (noscan) -+ udev_flags |= DM_SUBSYSTEM_UDEV_FLAG0; -+ -+ if (temporary) -+ udev_flags |= DM_UDEV_DISABLE_DISK_RULES_FLAG | -+ DM_UDEV_DISABLE_OTHER_RULES_FLAG; - - return udev_flags; - } -@@ -1499,7 +1504,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, - } - - if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor, -- _get_udev_flags(dm, lv, layer, 0))) { -+ _get_udev_flags(dm, lv, layer, 0, 0))) { - log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree", - info.major, info.minor); - return 0; -@@ -2340,7 +2345,7 @@ static int _set_udev_flags_for_children(struct dev_manager *dm, - } - - dm_tree_node_set_udev_flags(child, -- _get_udev_flags(dm, lvl->lv, NULL, 0)); -+ _get_udev_flags(dm, lvl->lv, NULL, 0, 0)); - } - - return 1; -@@ -2417,8 +2422,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, - read_only_lv(lv, laopts), - ((lv->vg->status & PRECOMMITTED) | laopts->revert) ? 1 : 0, - lvlayer, -- _get_udev_flags(dm, lv, layer, -- laopts->noscan ? LVM_UDEV_NOSCAN_FLAG : 0)))) -+ _get_udev_flags(dm, lv, layer, laopts->noscan, laopts->temporary)))) - return_0; - - /* Store existing name so we can do rename later */ -diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c -index a27b791..e31429e 100644 ---- a/lib/format_text/flags.c -+++ b/lib/format_text/flags.c -@@ -62,6 +62,7 @@ static const struct flag _lv_flags[] = { - {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG}, - {LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG}, - {LV_NOSCAN, NULL, 0}, -+ {LV_TEMPORARY, NULL, 0}, - {POOL_METADATA_SPARE, NULL, 0}, - {RAID, NULL, 0}, - {RAID_META, NULL, 0}, -diff --git a/lib/locking/file_locking.c b/lib/locking/file_locking.c -index b6b2b10..ab3dabd 100644 ---- a/lib/locking/file_locking.c -+++ b/lib/locking/file_locking.c -@@ -305,7 +305,8 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource, - break; - case LCK_READ: - log_very_verbose("Locking LV %s (R)", resource); -- if (!lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv))) -+ if (!lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, -+ lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv))) - return 0; - break; - case LCK_PREAD: -@@ -318,7 +319,8 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource, - break; - case LCK_EXCL: - log_very_verbose("Locking LV %s (EX)", resource); -- if (!lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv))) -+ if (!lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, -+ lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv))) - return 0; - break; - default: -diff --git a/lib/locking/no_locking.c b/lib/locking/no_locking.c -index 4772706..dac2f80 100644 ---- a/lib/locking/no_locking.c -+++ b/lib/locking/no_locking.c -@@ -48,11 +48,13 @@ static int _no_lock_resource(struct cmd_context *cmd, const char *resource, - case LCK_UNLOCK: - return lv_resume_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1: 0, 0, (flags & LCK_REVERT) ? 1 : 0, lv_ondisk(lv)); - case LCK_READ: -- return lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv)); -+ return lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, -+ lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv)); - case LCK_WRITE: - return lv_suspend_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1 : 0, 0, lv_ondisk(lv), lv); - case LCK_EXCL: -- return lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, lv_ondisk(lv)); -+ return lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, -+ lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv)); - default: - break; - } -diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c -index 22327d7..8884c50 100644 ---- a/lib/metadata/lv_manip.c -+++ b/lib/metadata/lv_manip.c -@@ -5983,6 +5983,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, - if (lp->zero) - lv->status |= LV_NOSCAN; - -+ if (lp->temporary) -+ lv->status |= LV_TEMPORARY; -+ - if (lv_is_thin_pool(lv)) { - if (is_change_activating(lp->activate)) { - if (vg_is_clustered(lv->vg)) { -diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h -index 1e9543a..c00e4e5 100644 ---- a/lib/metadata/metadata-exported.h -+++ b/lib/metadata/metadata-exported.h -@@ -102,7 +102,12 @@ - #define LV_WRITEMOSTLY UINT64_C(0x0000020000000000) /* LV (RAID1) */ - - #define LV_ACTIVATION_SKIP UINT64_C(0x0000040000000000) /* LV */ --#define LV_NOSCAN UINT64_C(0x0000080000000000) /* LV */ -+#define LV_NOSCAN UINT64_C(0x0000080000000000) /* LV - internal use only - the LV -+ should not be scanned */ -+#define LV_TEMPORARY UINT64_C(0x0000100000000000) /* LV - internal use only - the LV -+ is supposed to be created and -+ removed during single LVM -+ command execution. */ - - /* Format features flags */ - #define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ -@@ -721,6 +726,7 @@ struct lvcreate_params { - int log_count; /* mirror */ - int nosync; /* mirror */ - int poolmetadataspare; /* thin pool */ -+ int temporary; /* temporary LV */ - #define ACTIVATION_SKIP_SET 0x01 /* request to set LV activation skip flag state */ - #define ACTIVATION_SKIP_SET_ENABLED 0x02 /* set the LV activation skip flag state to 'enabled' */ - #define ACTIVATION_SKIP_IGNORE 0x04 /* request to ignore LV activation skip flag (if any) */ -diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c -index a6e0fc2..bd5b117 100644 ---- a/lib/metadata/thin_manip.c -+++ b/lib/metadata/thin_manip.c -@@ -809,6 +809,7 @@ static struct logical_volume *_alloc_pool_metadata_spare(struct volume_group *vg - .stripes = 1, - .vg_name = vg->name, - .zero = 1, -+ .temporary = 1, - }; - - dm_list_init(&lp.tags); -diff --git a/tools/lvconvert.c b/tools/lvconvert.c -index 1098642..170d76f 100644 ---- a/tools/lvconvert.c -+++ b/tools/lvconvert.c -@@ -2431,14 +2431,8 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - goto mda_write; - } - -- metadata_lv->status |= LV_NOSCAN; -- if (!lv_is_active(metadata_lv) && -- !activate_lv_local(cmd, metadata_lv)) { -- log_error("Aborting. Failed to activate thin metadata lv."); -- return 0; -- } -- if (!set_lv(cmd, metadata_lv, UINT64_C(0), 0)) { -- log_error("Aborting. Failed to wipe thin metadata lv."); -+ if (!deactivate_lv(cmd, metadata_lv)) { -+ log_error("Aborting. Failed to deactivate thin metadata lv."); - return 0; - } - -@@ -2458,6 +2452,16 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, - &lp->thin_chunk_size_calc_policy, &lp->chunk_size, - &lp->discards, &lp->poolmetadata_size, &lp->zero)) - return_0; -+ -+ metadata_lv->status |= LV_TEMPORARY; -+ if (!activate_lv_local(cmd, metadata_lv)) { -+ log_error("Aborting. Failed to activate thin metadata lv."); -+ return 0; -+ } -+ if (!set_lv(cmd, metadata_lv, UINT64_C(0), 0)) { -+ log_error("Aborting. Failed to wipe thin metadata lv."); -+ return 0; -+ } - } - - if (!deactivate_lv(cmd, metadata_lv)) { -diff --git a/udev/11-dm-lvm.rules.in b/udev/11-dm-lvm.rules.in -index 5032280..9ca0375 100644 ---- a/udev/11-dm-lvm.rules.in -+++ b/udev/11-dm-lvm.rules.in -@@ -32,7 +32,7 @@ IMPORT{program}="(DM_EXEC)/dmsetup splitname --nameprefixes --noheadings --rows - # uevent that follows for this LV, even an artificially generated one). - ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_NOSCAN}="1", ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" - ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", IMPORT{db}="DM_NOSCAN", IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" --ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}", \ -+ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", ENV{DM_NOSCAN}=="1", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}", \ - ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG_OLD}="", ENV{DM_NOSCAN}="" - - ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}=="1", GOTO="lvm_end" -diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in -index 3e303b1..5b15b6f 100644 ---- a/udev/69-dm-lvm-metad.rules.in -+++ b/udev/69-dm-lvm-metad.rules.in -@@ -17,7 +17,7 @@ - SUBSYSTEM!="block", GOTO="lvm_end" - (LVM_EXEC_RULE) - --ENV{DM_NOSCAN}=="1", GOTO="lvm_end" -+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", GOTO="lvm_end" - - # If the PV label got lost, inform lvmetad immediately. - # Detect the lost PV label by comparing previous ID_FS_TYPE value with current one. -@@ -51,6 +51,7 @@ KERNEL!="md[0-9]*", GOTO="next" - IMPORT{db}="LVM_MD_PV_ACTIVATED" - ACTION=="add", ENV{LVM_MD_PV_ACTIVATED}=="1", GOTO="lvm_scan" - ACTION=="change", ENV{LVM_MD_PV_ACTIVATED}!="1", TEST=="md/array_state", ENV{LVM_MD_PV_ACTIVATED}="1", GOTO="lvm_scan" -+ACTION=="add", KERNEL=="md[0-9]*p[0-9]*", GOTO="lvm_scan" - GOTO="lvm_end" - - # Loop device: diff --git a/SOURCES/lvm2-2_02_104-workaround-vg-refresh-during-autoactivation-by-retrying-the-refresh.patch b/SOURCES/lvm2-2_02_104-workaround-vg-refresh-during-autoactivation-by-retrying-the-refresh.patch deleted file mode 100644 index 36fd4d8..0000000 --- a/SOURCES/lvm2-2_02_104-workaround-vg-refresh-during-autoactivation-by-retrying-the-refresh.patch +++ /dev/null @@ -1,71 +0,0 @@ - WHATS_NEW | 1 + - tools/pvscan.c | 34 +++++++++++++++++++++++++++++++++- - 2 files changed, 34 insertions(+), 1 deletion(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index aabfc78..7fd107a 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,5 +1,6 @@ - Version 2.02.104 - - =================================== -+ Workaround VG refresh race during autoactivation by retrying the refresh. - Add dev-block-:.device systemd alias for complete PV tracking. - Use major:minor as short form of --major and --minor arg for pvscan --cache. - Fix lvconvert swap of poolmetadata volume for active thin pool. -diff --git a/tools/pvscan.c b/tools/pvscan.c -index b6a07bd..ce8c446 100644 ---- a/tools/pvscan.c -+++ b/tools/pvscan.c -@@ -91,10 +91,15 @@ static void _pvscan_display_single(struct cmd_context *cmd, - display_size(cmd, (uint64_t) (pv_pe_count(pv) - pv_pe_alloc_count(pv)) * pv_pe_size(pv))); - } - -+#define REFRESH_BEFORE_AUTOACTIVATION_RETRIES 5 -+#define REFRESH_BEFORE_AUTOACTIVATION_RETRY_USLEEP_DELAY 100000 -+ - static int _auto_activation_handler(struct cmd_context *cmd, - const char *vgid, int partial, - activation_change_t activate) - { -+ unsigned int refresh_retries = REFRESH_BEFORE_AUTOACTIVATION_RETRIES; -+ int refresh_done = 0; - struct volume_group *vg; - int consistent = 0; - struct id vgid_raw; -@@ -115,7 +120,34 @@ static int _auto_activation_handler(struct cmd_context *cmd, - r = 1; goto out; - } - -- if (!vg_refresh_visible(vg->cmd, vg)) { -+ /* FIXME: There's a tiny race when suspending the device which is part -+ * of the refresh because when suspend ioctl is performed, the dm -+ * kernel driver executes (do_suspend and dm_suspend kernel fn): -+ * -+ * step 1: a check whether the dev is already suspended and -+ * if yes it returns success immediately as there's -+ * nothing to do -+ * step 2: it grabs the suspend lock -+ * step 3: another check whether the dev is already suspended -+ * and if found suspended, it exits with -EINVAL now -+ * -+ * The race can occur in between step 1 and step 2. To prevent premature -+ * autoactivation failure, we're using a simple retry logic here before -+ * we fail completely. For a complete solution, we need to fix the -+ * locking so there's no possibility for suspend calls to interleave -+ * each other to cause this kind of race. -+ * -+ * Remove this workaround with "refresh_retries" once we have proper locking in! -+ */ -+ while (refresh_retries--) { -+ if (vg_refresh_visible(vg->cmd, vg)) { -+ refresh_done = 1; -+ break; -+ } -+ usleep(REFRESH_BEFORE_AUTOACTIVATION_RETRY_USLEEP_DELAY); -+ } -+ -+ if (!refresh_done) { - log_error("%s: refresh before autoactivation failed.", vg->name); - goto out; - } diff --git a/SOURCES/lvm2-2_02_105-do-no-fail-the-whole-autoactivation-if-the-vg-refresh-done-before-fails.patch b/SOURCES/lvm2-2_02_105-do-no-fail-the-whole-autoactivation-if-the-vg-refresh-done-before-fails.patch deleted file mode 100644 index f69262f..0000000 --- a/SOURCES/lvm2-2_02_105-do-no-fail-the-whole-autoactivation-if-the-vg-refresh-done-before-fails.patch +++ /dev/null @@ -1,33 +0,0 @@ - WHATS_NEW | 4 ++++ - tools/pvscan.c | 6 ++---- - 2 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/WHATS_NEW b/WHATS_NEW -index 2532a99..c642f47 100644 ---- a/WHATS_NEW -+++ b/WHATS_NEW -@@ -1,3 +1,7 @@ -+Version 2.02.105 - -+=================================== -+ Do not fail the whole autoactivation if the VG refresh done before fails. -+ - Version 2.02.104 - - =================================== - Fix missing lvmetad scan for PVs found on MD partitions. -diff --git a/tools/pvscan.c b/tools/pvscan.c -index ce8c446..4f99f45 100644 ---- a/tools/pvscan.c -+++ b/tools/pvscan.c -@@ -147,10 +147,8 @@ static int _auto_activation_handler(struct cmd_context *cmd, - usleep(REFRESH_BEFORE_AUTOACTIVATION_RETRY_USLEEP_DELAY); - } - -- if (!refresh_done) { -- log_error("%s: refresh before autoactivation failed.", vg->name); -- goto out; -- } -+ if (!refresh_done) -+ log_warn("%s: refresh before autoactivation failed.", vg->name); - - if (!vgchange_activate(vg->cmd, vg, activate)) { - log_error("%s: autoactivation failed.", vg->name); diff --git a/SOURCES/lvm2-2_02_106-additional-lvmetad-fixes.patch b/SOURCES/lvm2-2_02_106-additional-lvmetad-fixes.patch new file mode 100644 index 0000000..f828438 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-additional-lvmetad-fixes.patch @@ -0,0 +1,127 @@ +commit 923b95504e189fb3b9353a66a3c3a9e147a46e39 +Author: Peter Rajnoha +Date: Wed Mar 5 16:48:17 2014 +0100 + + lvmetad_fixes +--- + WHATS_NEW | 3 +++ + daemons/lvmetad/lvmetad-core.c | 20 +++++++++++++++++--- + test/shell/lvmetad-ambiguous.sh | 34 ++++++++++++++++++++++++++++++++++ + 3 files changed, 54 insertions(+), 3 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 224e351..3ee9585 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,8 @@ + Version 2.02.106 - + ==================================== ++ Fix cache consistency in lvmetad when PV moves around. ++ Fix memleak when lvmetad discovers PV to appear on another device. ++ Fix invalid memory read in lvmetad that could cause a deadlock. + Fix calculation of maximum size of COW device for snapshot (2.02.99). + Do not allow stripe size to be bigger then extent size for lvresize. + Zero snapshot COW header when creating read-only snapshot. +diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c +index e6e222f..f35db89 100644 +--- a/daemons/lvmetad/lvmetad-core.c ++++ b/daemons/lvmetad/lvmetad-core.c +@@ -861,7 +861,7 @@ static response pv_found(lvmetad_state *s, request r) + const char *vgid = daemon_request_str(r, "metadata/id", NULL); + const char *vgid_old = NULL; + struct dm_config_node *pvmeta = dm_config_find_node(r.cft->root, "pvmeta"); +- uint64_t device; ++ uint64_t device, device_old_pvid = 0; + struct dm_config_tree *cft, *pvmeta_old_dev = NULL, *pvmeta_old_pvid = NULL; + char *old; + char *pvid_dup; +@@ -883,9 +883,12 @@ static response pv_found(lvmetad_state *s, request r) + dm_hash_remove(s->pvid_to_pvmeta, old); + vgid_old = dm_hash_lookup(s->pvid_to_vgid, old); + } +- pvmeta_old_pvid = dm_hash_lookup(s->pvid_to_pvmeta, pvid); + +- DEBUGLOG(s, "pv_found %s, vgid = %s, device = %" PRIu64 ", old = %s", pvid, vgid, device, old); ++ if ((pvmeta_old_pvid = dm_hash_lookup(s->pvid_to_pvmeta, pvid))) ++ dm_config_get_uint64(pvmeta_old_pvid->root, "pvmeta/device", &device_old_pvid); ++ ++ DEBUGLOG(s, "pv_found %s, vgid = %s, device = %" PRIu64 " (previously %" PRIu64 "), old = %s", ++ pvid, vgid, device, device_old_pvid, old); + + dm_free(old); + +@@ -903,6 +906,12 @@ static response pv_found(lvmetad_state *s, request r) + return reply_fail("out of memory"); + } + ++ if (pvmeta_old_pvid && device != device_old_pvid) { ++ DEBUGLOG(s, "pv %s no longer on device %" PRIu64, pvid, device_old_pvid); ++ dm_free(dm_hash_lookup_binary(s->device_to_pvid, &device_old_pvid, sizeof(device_old_pvid))); ++ dm_hash_remove_binary(s->device_to_pvid, &device_old_pvid, sizeof(device_old_pvid)); ++ } ++ + if (!dm_hash_insert(s->pvid_to_pvmeta, pvid, cft) || + !dm_hash_insert_binary(s->device_to_pvid, &device, sizeof(device), (void*)pvid_dup)) { + dm_hash_remove(s->pvid_to_pvmeta, pvid); +@@ -911,6 +920,7 @@ static response pv_found(lvmetad_state *s, request r) + dm_free(pvid_dup); + return reply_fail("out of memory"); + } ++ + if (pvmeta_old_pvid) + dm_config_destroy(pvmeta_old_pvid); + if (pvmeta_old_dev && pvmeta_old_dev != pvmeta_old_pvid) +@@ -949,9 +959,13 @@ static response pv_found(lvmetad_state *s, request r) + } + + if (vgid_old && (!vgid || strcmp(vgid, vgid_old))) { ++ /* make a copy, because vg_remove_if_missing will deallocate the ++ * storage behind vgid_old */ ++ vgid_old = dm_strdup(vgid_old); + lock_vg(s, vgid_old); + vg_remove_if_missing(s, vgid_old, 1); + unlock_vg(s, vgid_old); ++ dm_free((char*)vgid_old); + } + + return daemon_reply_simple("OK", +diff --git a/test/shell/lvmetad-ambiguous.sh b/test/shell/lvmetad-ambiguous.sh +new file mode 100644 +index 0000000..455aa5d +--- /dev/null ++++ b/test/shell/lvmetad-ambiguous.sh +@@ -0,0 +1,34 @@ ++#!/bin/sh ++# Copyright (C) 2012 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++. lib/test ++ ++test -e LOCAL_LVMETAD || skip ++ ++aux prepare_pvs 2 ++ ++# flip the devices around ++aux init_udev_transaction ++dmsetup remove -f "$dev1" ++dmsetup remove -f "$dev2" ++dmsetup create -u TEST-${PREFIX}pv2 ${PREFIX}pv2 ${PREFIX}pv2.table ++dmsetup create -u TEST-${PREFIX}pv1 ${PREFIX}pv1 ${PREFIX}pv1.table ++aux finish_udev_transaction ++ ++# re-scan them ++pvscan --cache $dev1 ++pvscan --cache $dev2 ++ ++# expect both to be there ++pvs | tee pvs.txt ++grep $dev1 pvs.txt ++grep $dev2 pvs.txt ++ diff --git a/SOURCES/lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror-addendum.patch b/SOURCES/lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror-addendum.patch new file mode 100644 index 0000000..3b4924b --- /dev/null +++ b/SOURCES/lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror-addendum.patch @@ -0,0 +1,20 @@ + daemons/cmirrord/cluster.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/daemons/cmirrord/cluster.c b/daemons/cmirrord/cluster.c +index 67391f1..3fd5d23 100644 +--- a/daemons/cmirrord/cluster.c ++++ b/daemons/cmirrord/cluster.c +@@ -985,9 +985,9 @@ static int do_cluster_work(void *data __attribute__((unused))) + dm_list_iterate_items_safe(entry, tmp, &clog_cpg_list) { + r = cpg_dispatch(entry->handle, CS_DISPATCH_ALL); + if (r != CS_OK) { +- if ((entry->cpg_state == INVALID) && +- (entry->state == LEAVING) && +- (r == CS_ERR_BAD_HANDLE)) ++ if ((r == CS_ERR_BAD_HANDLE) && ++ ((entry->state == INVALID) || ++ (entry->state == LEAVING))) + /* It's ok if we've left the cluster */ + r = CS_OK; + else diff --git a/SOURCES/lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror.patch b/SOURCES/lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror.patch new file mode 100644 index 0000000..bff73f3 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror.patch @@ -0,0 +1,43 @@ +commit 52aa3dbcabe85b38b51c68c27fb2397eb0fb1efd +Author: Jonathan Brassow +Date: Wed Mar 5 10:44:20 2014 -0600 + + cmirrord: Clean-up stray warning message + + cmirrord polls for messages on the kernel and cluster interfaces. + Sometimes it is possible for messages to be received on the cluster + interface and be waiting for processing while the node is in the + process of leaving the cluster group. When this happens, the + messages received on the cluster interface are attempted to be + dispatched, but an error is returned because the connection is no + longer valid. It is a harmless situation. So, if we get the + specific error (CS_ERR_BAD_HANDLE) and we know that we have left + the group, then simply don't print the message. +--- + daemons/cmirrord/cluster.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/daemons/cmirrord/cluster.c b/daemons/cmirrord/cluster.c +index fea739a..67391f1 100644 +--- a/daemons/cmirrord/cluster.c ++++ b/daemons/cmirrord/cluster.c +@@ -984,9 +984,16 @@ static int do_cluster_work(void *data __attribute__((unused))) + + dm_list_iterate_items_safe(entry, tmp, &clog_cpg_list) { + r = cpg_dispatch(entry->handle, CS_DISPATCH_ALL); +- if (r != CS_OK) +- LOG_ERROR("cpg_dispatch failed: %s", +- str_ais_error(r)); ++ if (r != CS_OK) { ++ if ((entry->cpg_state == INVALID) && ++ (entry->state == LEAVING) && ++ (r == CS_ERR_BAD_HANDLE)) ++ /* It's ok if we've left the cluster */ ++ r = CS_OK; ++ else ++ LOG_ERROR("cpg_dispatch failed: %s", ++ str_ais_error(r)); ++ } + + if (entry->free_me) { + free(entry); diff --git a/SOURCES/lvm2-2_02_106-enahnce-lvmetad-protocol-for-pvscan-to-lock-vg-properly-and-run-refresh-only-when-needed.patch b/SOURCES/lvm2-2_02_106-enahnce-lvmetad-protocol-for-pvscan-to-lock-vg-properly-and-run-refresh-only-when-needed.patch new file mode 100644 index 0000000..336de06 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-enahnce-lvmetad-protocol-for-pvscan-to-lock-vg-properly-and-run-refresh-only-when-needed.patch @@ -0,0 +1,325 @@ + WHATS_NEW | 5 +++ + daemons/lvmetad/lvmetad-core.c | 74 +++++++++++++++++++++++------------------- + lib/cache/lvmetad.c | 9 +++-- + lib/cache/lvmetad.h | 3 +- + lib/locking/file_locking.c | 2 +- + tools/pvscan.c | 32 ++++++++++-------- + 6 files changed, 74 insertions(+), 51 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index a3d48f3..b5afb19 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,10 @@ + Version 2.02.106 - + ==================================== ++ Use VG read lock during 'pvscan --cache -aay' autoactivation. ++ Issue a VG refresh before autoactivation only if the PV has changed/is new. ++ Add flag to lvmetad protocol to indicate the PV scanned has changed/is new. ++ Also add vgname to lvmetad protocol when referencing VGs for PVs scanned. ++ Use correct PATH_MAX for locking dir path. + Update API for internal function build_dm_uuid(). + Do not try to check empty pool with scheduled messages. + Fix return value in pool_has_message() when quering for any message. +diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c +index f35db89..1858ee2 100644 +--- a/daemons/lvmetad/lvmetad-core.c ++++ b/daemons/lvmetad/lvmetad-core.c +@@ -344,8 +344,8 @@ static response pv_lookup(lvmetad_state *s, request r) + pvid = dm_hash_lookup_binary(s->device_to_pvid, &devt, sizeof(devt)); + + if (!pvid) { +- WARN(s, "pv_lookup: could not find device %" PRIu64, devt); + unlock_pvid_to_pvmeta(s); ++ WARN(s, "pv_lookup: could not find device %" PRIu64, devt); + dm_config_destroy(res.cft); + return reply_unknown("device not found"); + } +@@ -809,30 +809,28 @@ static response pv_gone(lvmetad_state *s, request r) + pvid_old = dm_hash_lookup_binary(s->device_to_pvid, &device, sizeof(device)); + vgid = dm_hash_lookup(s->pvid_to_vgid, pvid); + +- if (vgid && !(vgid = dm_strdup(vgid))) { +- unlock_pvid_to_pvmeta(s); +- return reply_fail("out of memory"); +- } +- + dm_hash_remove_binary(s->device_to_pvid, &device, sizeof(device)); + dm_hash_remove(s->pvid_to_pvmeta, pvid); + unlock_pvid_to_pvmeta(s); + ++ dm_free(pvid_old); ++ + if (vgid) { ++ if (!(vgid = dm_strdup(vgid))) ++ return reply_fail("out of memory"); ++ + lock_vg(s, vgid); + vg_remove_if_missing(s, vgid, 1); + unlock_vg(s, vgid); + dm_free(vgid); + } + +- if (pvid_old) +- dm_free(pvid_old); +- +- if (pvmeta) { +- dm_config_destroy(pvmeta); +- return daemon_reply_simple("OK", NULL); +- } else ++ if (!pvmeta) + return reply_unknown("PVID does not exist"); ++ ++ dm_config_destroy(pvmeta); ++ ++ return daemon_reply_simple("OK", NULL); + } + + static response pv_clear_all(lvmetad_state *s, request r) +@@ -866,7 +864,7 @@ static response pv_found(lvmetad_state *s, request r) + char *old; + char *pvid_dup; + int complete = 0, orphan = 0; +- int64_t seqno = -1, seqno_old = -1; ++ int64_t seqno = -1, seqno_old = -1, changed = 0; + + if (!pvid) + return reply_fail("need PV UUID"); +@@ -876,58 +874,60 @@ static response pv_found(lvmetad_state *s, request r) + if (!dm_config_get_uint64(pvmeta, "pvmeta/device", &device)) + return reply_fail("need PV device number"); + ++ if (!(cft = dm_config_create()) || ++ (!(pvid_dup = dm_strdup(pvid)))) { ++ if (cft) ++ dm_config_destroy(cft); ++ return reply_fail("out of memory"); ++ } ++ + lock_pvid_to_pvmeta(s); + ++ if ((pvmeta_old_pvid = dm_hash_lookup(s->pvid_to_pvmeta, pvid))) ++ dm_config_get_uint64(pvmeta_old_pvid->root, "pvmeta/device", &device_old_pvid); ++ + if ((old = dm_hash_lookup_binary(s->device_to_pvid, &device, sizeof(device)))) { + pvmeta_old_dev = dm_hash_lookup(s->pvid_to_pvmeta, old); + dm_hash_remove(s->pvid_to_pvmeta, old); + vgid_old = dm_hash_lookup(s->pvid_to_vgid, old); + } + +- if ((pvmeta_old_pvid = dm_hash_lookup(s->pvid_to_pvmeta, pvid))) +- dm_config_get_uint64(pvmeta_old_pvid->root, "pvmeta/device", &device_old_pvid); +- + DEBUGLOG(s, "pv_found %s, vgid = %s, device = %" PRIu64 " (previously %" PRIu64 "), old = %s", + pvid, vgid, device, device_old_pvid, old); + +- dm_free(old); +- +- if (!(cft = dm_config_create()) || +- !(cft->root = dm_config_clone_node(cft, pvmeta, 0))) { +- unlock_pvid_to_pvmeta(s); +- if (cft) +- dm_config_destroy(cft); +- return reply_fail("out of memory"); +- } ++ if (!(cft->root = dm_config_clone_node(cft, pvmeta, 0))) ++ goto out_of_mem; + +- if (!(pvid_dup = dm_strdup(pvid))) { +- unlock_pvid_to_pvmeta(s); +- dm_config_destroy(cft); +- return reply_fail("out of memory"); +- } ++ if (!pvmeta_old_pvid || compare_config(pvmeta_old_pvid->root, cft->root)) ++ changed |= 1; + + if (pvmeta_old_pvid && device != device_old_pvid) { + DEBUGLOG(s, "pv %s no longer on device %" PRIu64, pvid, device_old_pvid); + dm_free(dm_hash_lookup_binary(s->device_to_pvid, &device_old_pvid, sizeof(device_old_pvid))); + dm_hash_remove_binary(s->device_to_pvid, &device_old_pvid, sizeof(device_old_pvid)); ++ changed |= 1; + } + + if (!dm_hash_insert(s->pvid_to_pvmeta, pvid, cft) || + !dm_hash_insert_binary(s->device_to_pvid, &device, sizeof(device), (void*)pvid_dup)) { + dm_hash_remove(s->pvid_to_pvmeta, pvid); ++out_of_mem: + unlock_pvid_to_pvmeta(s); + dm_config_destroy(cft); + dm_free(pvid_dup); ++ dm_free(old); + return reply_fail("out of memory"); + } + ++ unlock_pvid_to_pvmeta(s); ++ ++ dm_free(old); ++ + if (pvmeta_old_pvid) + dm_config_destroy(pvmeta_old_pvid); + if (pvmeta_old_dev && pvmeta_old_dev != pvmeta_old_pvid) + dm_config_destroy(pvmeta_old_dev); + +- unlock_pvid_to_pvmeta(s); +- + if (metadata) { + if (!vgid) + return reply_fail("need VG UUID"); +@@ -939,6 +939,7 @@ static response pv_found(lvmetad_state *s, request r) + + if (!update_metadata(s, vgname, vgid, metadata, &seqno_old)) + return reply_fail("metadata update failed"); ++ changed |= (seqno_old != dm_config_find_int(metadata, "metadata/seqno", -1)); + } else { + lock_pvid_to_vgid(s); + vgid = dm_hash_lookup(s->pvid_to_vgid, pvid); +@@ -956,6 +957,11 @@ static response pv_found(lvmetad_state *s, request r) + return reply_fail("non-orphan VG without metadata encountered"); + } + unlock_vg(s, vgid); ++ ++ // TODO: separate vgid->vgname lock ++ lock_vgid_to_metadata(s); ++ vgname = dm_hash_lookup(s->vgid_to_vgname, vgid); ++ unlock_vgid_to_metadata(s); + } + + if (vgid_old && (!vgid || strcmp(vgid, vgid_old))) { +@@ -971,7 +977,9 @@ static response pv_found(lvmetad_state *s, request r) + return daemon_reply_simple("OK", + "status = %s", orphan ? "orphan" : + (complete ? "complete" : "partial"), ++ "changed = %d", changed, + "vgid = %s", vgid ? vgid : "#orphan", ++ "vgname = %s", vgname ? vgname : "#orphan", + "seqno_before = %"PRId64, seqno_old, + "seqno_after = %"PRId64, seqno, + NULL); +diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c +index c994874..38d9042 100644 +--- a/lib/cache/lvmetad.c ++++ b/lib/cache/lvmetad.c +@@ -749,7 +749,8 @@ int lvmetad_pv_found(const struct id *pvid, struct device *dev, const struct for + daemon_reply reply; + struct lvmcache_info *info; + struct dm_config_tree *pvmeta, *vgmeta; +- const char *status, *vgid; ++ const char *status, *vgname, *vgid; ++ int64_t changed; + int result; + + if (!lvmetad_active() || test_mode()) +@@ -818,11 +819,13 @@ int lvmetad_pv_found(const struct id *pvid, struct device *dev, const struct for + + if (result && handler) { + status = daemon_reply_str(reply, "status", ""); ++ vgname = daemon_reply_str(reply, "vgname", ""); + vgid = daemon_reply_str(reply, "vgid", ""); ++ changed = daemon_reply_int(reply, "changed", 0); + if (!strcmp(status, "partial")) +- handler(_lvmetad_cmd, vgid, 1, CHANGE_AAY); ++ handler(_lvmetad_cmd, vgname, vgid, 1, changed, CHANGE_AAY); + else if (!strcmp(status, "complete")) +- handler(_lvmetad_cmd, vgid, 0, CHANGE_AAY); ++ handler(_lvmetad_cmd, vgname, vgid, 0, changed, CHANGE_AAY); + else if (!strcmp(status, "orphan")) + ; + else +diff --git a/lib/cache/lvmetad.h b/lib/cache/lvmetad.h +index 85b71c2..d9aa77f 100644 +--- a/lib/cache/lvmetad.h ++++ b/lib/cache/lvmetad.h +@@ -23,7 +23,8 @@ struct dm_config_tree; + enum activation_change; + + typedef int (*activation_handler) (struct cmd_context *cmd, +- const char *vgid, int partial, ++ const char *vgname, const char *vgid, ++ int partial, int changed, + enum activation_change activate); + + #ifdef LVMETAD_SUPPORT +diff --git a/lib/locking/file_locking.c b/lib/locking/file_locking.c +index fb84c5b..734e0b4 100644 +--- a/lib/locking/file_locking.c ++++ b/lib/locking/file_locking.c +@@ -37,7 +37,7 @@ struct lock_list { + }; + + static struct dm_list _lock_list; +-static char _lock_dir[NAME_LEN]; ++static char _lock_dir[PATH_MAX]; + static int _prioritise_write_locks; + + static sig_t _oldhandler; +diff --git a/tools/pvscan.c b/tools/pvscan.c +index 4f99f45..5db627a 100644 +--- a/tools/pvscan.c ++++ b/tools/pvscan.c +@@ -95,13 +95,13 @@ static void _pvscan_display_single(struct cmd_context *cmd, + #define REFRESH_BEFORE_AUTOACTIVATION_RETRY_USLEEP_DELAY 100000 + + static int _auto_activation_handler(struct cmd_context *cmd, +- const char *vgid, int partial, ++ const char *vgname, const char *vgid, ++ int partial, int changed, + activation_change_t activate) + { + unsigned int refresh_retries = REFRESH_BEFORE_AUTOACTIVATION_RETRIES; + int refresh_done = 0; + struct volume_group *vg; +- int consistent = 0; + struct id vgid_raw; + int r = 0; + +@@ -113,8 +113,12 @@ static int _auto_activation_handler(struct cmd_context *cmd, + return_0; + + /* NB. This is safe because we know lvmetad is running and we won't hit disk. */ +- if (!(vg = vg_read_internal(cmd, NULL, (const char *) &vgid_raw, 0, &consistent))) +- return 1; ++ vg = vg_read(cmd, vgname, (const char *)&vgid_raw, 0); ++ if (vg_read_error(vg)) { ++ log_error("Failed to read Volume Group \"%s\" (%s) during autoactivation.", vgname, vgid); ++ release_vg(vg); ++ return 0; ++ } + + if (vg_is_clustered(vg)) { + r = 1; goto out; +@@ -139,16 +143,18 @@ static int _auto_activation_handler(struct cmd_context *cmd, + * + * Remove this workaround with "refresh_retries" once we have proper locking in! + */ +- while (refresh_retries--) { +- if (vg_refresh_visible(vg->cmd, vg)) { +- refresh_done = 1; +- break; ++ if (changed) { ++ while (refresh_retries--) { ++ if (vg_refresh_visible(vg->cmd, vg)) { ++ refresh_done = 1; ++ break; ++ } ++ usleep(REFRESH_BEFORE_AUTOACTIVATION_RETRY_USLEEP_DELAY); + } +- usleep(REFRESH_BEFORE_AUTOACTIVATION_RETRY_USLEEP_DELAY); +- } + +- if (!refresh_done) +- log_warn("%s: refresh before autoactivation failed.", vg->name); ++ if (!refresh_done) ++ log_warn("%s: refresh before autoactivation failed.", vg->name); ++ } + + if (!vgchange_activate(vg->cmd, vg, activate)) { + log_error("%s: autoactivation failed.", vg->name); +@@ -158,7 +164,7 @@ static int _auto_activation_handler(struct cmd_context *cmd, + r = 1; + + out: +- release_vg(vg); ++ unlock_and_release_vg(cmd, vg, vgname); + return r; + } + diff --git a/SOURCES/lvm2-2_02_106-fix-dmeventd-logging-with-parallel-wait-processing.patch b/SOURCES/lvm2-2_02_106-fix-dmeventd-logging-with-parallel-wait-processing.patch new file mode 100644 index 0000000..510cf5b --- /dev/null +++ b/SOURCES/lvm2-2_02_106-fix-dmeventd-logging-with-parallel-wait-processing.patch @@ -0,0 +1,67 @@ +commit 6281d212fa5e70e400c70677dfc20e99553e60d1 +Author: Peter Rajnoha +Date: Wed Mar 12 14:36:55 2014 +0100 + + dmeventd parallel processing +--- + WHATS_NEW_DM | 1 + + daemons/dmeventd/dmeventd.c | 16 ++++++++++++++-- + 2 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 32c2265..e470cd2 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.85 - + =================================== ++ Fix dmeventd logging with parallel wait event processing. + Reuse _node_send_messages() for validation of transaction_id in preload. + Transaction_id could be lower by one only when messages are prepared. + Do not call callback when preload fails. +diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c +index 4a17fb2..f27444c 100644 +--- a/daemons/dmeventd/dmeventd.c ++++ b/daemons/dmeventd/dmeventd.c +@@ -661,6 +661,7 @@ static sigset_t _unblock_sigalrm(void) + /* Wait on a device until an event occurs. */ + static int _event_wait(struct thread_status *thread, struct dm_task **task) + { ++ static unsigned _in_event_counter = 0; + sigset_t set; + int ret = DM_WAIT_RETRY; + struct dm_task *dmt; +@@ -677,12 +678,20 @@ static int _event_wait(struct thread_status *thread, struct dm_task **task) + !dm_task_set_event_nr(dmt, thread->event_nr)) + goto out; + ++ _lock_mutex(); ++ /* ++ * Check if there are already some waiting events, ++ * in this case the logging is unmodified. ++ * TODO: audit libdm thread usage ++ */ ++ if (!_in_event_counter++) ++ dm_log_init(_no_intr_log); ++ _unlock_mutex(); + /* + * This is so that you can break out of waiting on an event, + * either for a timeout event, or to cancel the thread. + */ + set = _unblock_sigalrm(); +- dm_log_init(_no_intr_log); + errno = 0; + if (dm_task_run(dmt)) { + thread->current_events |= DM_EVENT_DEVICE_ERROR; +@@ -706,7 +715,10 @@ static int _event_wait(struct thread_status *thread, struct dm_task **task) + } + + pthread_sigmask(SIG_SETMASK, &set, NULL); +- dm_log_init(NULL); ++ _lock_mutex(); ++ if (--_in_event_counter == 0) ++ dm_log_init(NULL); ++ _unlock_mutex(); + + out: + if (ret == DM_WAIT_FATAL || ret == DM_WAIT_RETRY) { diff --git a/SOURCES/lvm2-2_02_106-fix-incorrect-snapshot-calculation-of-cow-size.patch b/SOURCES/lvm2-2_02_106-fix-incorrect-snapshot-calculation-of-cow-size.patch new file mode 100644 index 0000000..3bff830 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-fix-incorrect-snapshot-calculation-of-cow-size.patch @@ -0,0 +1,122 @@ +commit d00fc1de784cee27d6ddb1b045a67c930f743074 +Author: Zdenek Kabelac +Date: Thu Feb 27 12:55:50 2014 +0100 + + snapshot: correct previous snapshot commit + + Condition was swapped - however since it's been based on 'random' + memory content it's been missed as attribute has not been set. + + So now we have quite a few possible results when testing. + + We have old status without separate metadata and + we have kernels with fixed snapshot leak bug. + + (in-release update) +--- + lib/metadata/snapshot_manip.c | 4 ++-- + lib/snapshot/snapshot.c | 3 +++ + test/shell/snapshot-usage.sh | 31 ++++++++++++++++++++++++------- + 3 files changed, 29 insertions(+), 9 deletions(-) + +diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c +index 25fee76..8fcab0c 100644 +--- a/lib/metadata/snapshot_manip.c ++++ b/lib/metadata/snapshot_manip.c +@@ -46,9 +46,9 @@ static uint64_t _cow_extra_chunks(struct cmd_context *cmd, uint64_t n_chunks) + segtype->ops->target_present && + segtype->ops->target_present(cmd, NULL, &attrs) && + (attrs & SNAPSHOT_FEATURE_FIXED_LEAK)) +- return (n_chunks + 63) / 64; ++ return 0; + +- return 0; ++ return (n_chunks + 63) / 64; + } + + static uint64_t _cow_max_size(struct cmd_context *cmd, uint64_t origin_size, uint32_t chunk_size) +diff --git a/lib/snapshot/snapshot.c b/lib/snapshot/snapshot.c +index 0b7b845..64919b0 100644 +--- a/lib/snapshot/snapshot.c ++++ b/lib/snapshot/snapshot.c +@@ -165,6 +165,9 @@ static int _snap_target_present(struct cmd_context *cmd, + log_very_verbose("Target snapshot may leak metadata."); + } + ++ if (attributes) ++ *attributes = _snap_attrs; ++ + /* TODO: test everything at once */ + if (seg && (seg->status & MERGING)) { + if (!_snap_merge_checked) { +diff --git a/test/shell/snapshot-usage.sh b/test/shell/snapshot-usage.sh +index cb60556..bd0723d 100644 +--- a/test/shell/snapshot-usage.sh ++++ b/test/shell/snapshot-usage.sh +@@ -33,6 +33,25 @@ vgcreate -s 4M $vg $(cat DEVICES) + TSIZE=15P + aux can_use_16T || TSIZE=15T + ++# With different snapshot target driver we may obtain different results. ++# Older targets have metadata leak bug which needs extra compenstion. ++# Ancient targets do not even provide separate info for metadata. ++EXPECT1="16.00k" ++EXPECT2="512.00k" ++EXPECT3="32.00k" ++EXPECT4="66.67" ++if aux target_at_least dm-snapshot 1 10 0 ; then ++ # Extra metadata size ++ EXPECT4="0.00" ++ ++ if aux target_at_least dm-snapshot 1 12 0 ; then ++ # When fixed leak, expect smaller sizes ++ EXPECT1="12.00k" ++ EXPECT2="384.00k" ++ EXPECT3="28.00k" ++ fi ++fi ++ + lvcreate -s -l 100%FREE -n $lv $vg --virtualsize $TSIZE + + aux extend_filter_LVMTEST +@@ -86,12 +105,12 @@ lvcreate -an -Zn -l1 -n $lv1 $vg1 + not lvcreate -s -l1 $vg1/$lv1 + not lvcreate -s -l3 $vg1/$lv1 + lvcreate -s -l30 -n $lv2 $vg1/$lv1 +-check lv_field $vg1/$lv2 size "12.00k" ++check lv_field $vg1/$lv2 size "$EXPECT1" + + not lvcreate -s -c512 -l512 $vg1/$lv1 + lvcreate -s -c128 -l1700 -n $lv3 $vg1/$lv1 + # 3 * 128 +-check lv_field $vg1/$lv3 size "384.00k" ++check lv_field $vg1/$lv3 size "$EXPECT2" + lvremove -ff $vg1 + + lvcreate -aey -l20 $vg1 +@@ -110,7 +129,7 @@ lvextend --use-policies $vg1/lvol1 + check lv_field $vg1/lvol1 size "18.00k" + + lvextend -l+33 $vg1/lvol1 +-check lv_field $vg1/lvol1 size "32.00k" ++check lv_field $vg1/lvol1 size "$EXPECT3" + + fill 20K + lvremove -f $vg1 +@@ -138,14 +157,12 @@ fsck -n "$DM_DEV_DIR/$vg1/snap" + # This test would trigger read of weird percentage for undeleted header + # And since older snapshot target counts with metadata sectors + # we have 2 valid results (unsure about correct version number) +-EXPECT="0.00" +-aux target_at_least dm-snapshot 1 10 0 || EXPECT="66.67" +-check lv_field $vg1/snap data_percent "$EXPECT" ++check lv_field $vg1/snap data_percent "$EXPECT4" + + vgremove -ff $vg1 + + # Can't test >= 16T devices on 32bit +-if test "$TSIZE" -eq 15P ; then ++if test "$TSIZE" = 15P ; then + + # Check usability with largest extent size + pvcreate "$DM_DEV_DIR/$vg/$lv" diff --git a/SOURCES/lvm2-2_02_106-fix-timeout-for-initial-lvmetad-scan-when-done-in-parallel.patch b/SOURCES/lvm2-2_02_106-fix-timeout-for-initial-lvmetad-scan-when-done-in-parallel.patch new file mode 100644 index 0000000..c4c73f4 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-fix-timeout-for-initial-lvmetad-scan-when-done-in-parallel.patch @@ -0,0 +1,66 @@ +commit a7864d8f81e29dd7228c16f35314843e2f0c7b86 +Author: Peter Rajnoha +Date: Wed Mar 26 09:09:33 2014 +0100 + + 0 +--- + lib/cache/lvmetad.c | 16 +++++++++++----- + libdaemon/client/daemon-client.h | 2 +- + 2 files changed, 12 insertions(+), 6 deletions(-) + +diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c +index 38d9042..c019250 100644 +--- a/lib/cache/lvmetad.c ++++ b/lib/cache/lvmetad.c +@@ -143,6 +143,7 @@ static daemon_reply _lvmetad_send(const char *id, ...) + daemon_reply repl; + daemon_request req; + int try = 0; ++ int time = 0, wait, sleep = 1; + + retry: + req = daemon_request_make(id); +@@ -159,7 +160,7 @@ retry: + daemon_request_destroy(req); + + if (!repl.error && !strcmp(daemon_reply_str(repl, "response", ""), "token_mismatch") && +- try < 60 && !test_mode()) { ++ try < 10 && time < 80000000 && !test_mode()) { + /* + * If another process is trying to scan, they might have the + * same future token id and it's better to wait and avoid doing +@@ -172,12 +173,17 @@ retry: + * the update, we back off for a short while (0.2-2 seconds) and + * try again. + */ +- if (!strcmp(daemon_reply_str(repl, "expected", ""), "update in progress") || try % 5) +- usleep( 50000 + random() % 450000 ); /* 0.05 - 0.5s */ +- else ++ if (!strcmp(daemon_reply_str(repl, "expected", ""), "update in progress") || sleep) { ++ wait = 50000 + random() % 450000; /* 0.05 - 0.5s */ ++ time += wait; ++ usleep( wait ); ++ -- sleep; ++ } else { + /* If the re-scan fails here, we try again later. */ + lvmetad_pvscan_all_devs(_lvmetad_cmd, NULL); +- ++ try; ++ ++ try; ++ sleep = 5; ++ } + daemon_reply_destroy(repl); + goto retry; + } +diff --git a/libdaemon/client/daemon-client.h b/libdaemon/client/daemon-client.h +index 6ba65e6..8a44f8b 100644 +--- a/libdaemon/client/daemon-client.h ++++ b/libdaemon/client/daemon-client.h +@@ -102,7 +102,7 @@ static inline int64_t daemon_reply_int(daemon_reply r, const char *path, int64_t + } + + static inline const char *daemon_reply_str(daemon_reply r, const char *path, const char *def) { +- return dm_config_find_str(r.cft->root, path, def); ++ return dm_config_find_str_allow_empty(r.cft->root, path, def); + } + + diff --git a/SOURCES/lvm2-2_02_106-reinitialise-lvmcache-properly-on-fork-to-fix-premature-polldaemon-exit.patch b/SOURCES/lvm2-2_02_106-reinitialise-lvmcache-properly-on-fork-to-fix-premature-polldaemon-exit.patch new file mode 100644 index 0000000..2563f9b --- /dev/null +++ b/SOURCES/lvm2-2_02_106-reinitialise-lvmcache-properly-on-fork-to-fix-premature-polldaemon-exit.patch @@ -0,0 +1,185 @@ +commit 129b8f8e525126f660fa22f88ee58cd086c5f338 +Author: Peter Rajnoha +Date: Tue Mar 25 11:01:23 2014 +0100 + + lvmcache init +--- + WHATS_NEW | 1 + + lib/cache/lvmcache.c | 9 ++++++--- + lib/cache/lvmcache.h | 2 +- + lib/commands/toolcontext.c | 4 ++-- + test/shell/pvmove-background.sh | 24 ++++++++++++++++++++++++ + tools/lvmcmdline.c | 2 +- + tools/pvscan.c | 2 +- + tools/toollib.c | 1 + + tools/vgrename.c | 2 +- + tools/vgscan.c | 2 +- + 10 files changed, 39 insertions(+), 10 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index b5afb19..cf6103d 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.106 - + ==================================== ++ Reinitialise lvmcache properly on fork to fix polldaemon exiting prematurely. + Use VG read lock during 'pvscan --cache -aay' autoactivation. + Issue a VG refresh before autoactivation only if the PV has changed/is new. + Add flag to lvmetad protocol to indicate the PV scanned has changed/is new. +diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c +index d40bdce..fe64c8a 100644 +--- a/lib/cache/lvmcache.c ++++ b/lib/cache/lvmcache.c +@@ -1609,7 +1609,7 @@ static void _lvmcache_destroy_lockname(struct dm_hash_node *n) + dm_hash_get_key(_lock_hash, n)); + } + +-void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans) ++void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset) + { + struct dm_hash_node *n; + log_verbose("Wiping internal VG cache"); +@@ -1635,8 +1635,11 @@ void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans) + } + + if (_lock_hash) { +- dm_hash_iterate(n, _lock_hash) +- _lvmcache_destroy_lockname(n); ++ if (reset) ++ _vg_global_lock_held = 0; ++ else ++ dm_hash_iterate(n, _lock_hash) ++ _lvmcache_destroy_lockname(n); + dm_hash_destroy(_lock_hash); + _lock_hash = NULL; + } +diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h +index bf26664..83d561b 100644 +--- a/lib/cache/lvmcache.h ++++ b/lib/cache/lvmcache.h +@@ -42,7 +42,7 @@ struct lvmcache_vginfo; + int lvmcache_init(void); + void lvmcache_allow_reads_with_lvmetad(void); + +-void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans); ++void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset); + + /* Set full_scan to 1 to reread every filtered device label or + * 2 to rescan /dev for new devices */ +diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c +index a709284..d08ca61 100644 +--- a/lib/commands/toolcontext.c ++++ b/lib/commands/toolcontext.c +@@ -1640,7 +1640,7 @@ int refresh_toolcontext(struct cmd_context *cmd) + */ + + activation_release(); +- lvmcache_destroy(cmd, 0); ++ lvmcache_destroy(cmd, 0, 0); + label_exit(); + _destroy_segtypes(&cmd->segtypes); + _destroy_formats(cmd, &cmd->formats); +@@ -1732,7 +1732,7 @@ void destroy_toolcontext(struct cmd_context *cmd) + + archive_exit(cmd); + backup_exit(cmd); +- lvmcache_destroy(cmd, 0); ++ lvmcache_destroy(cmd, 0, 0); + label_exit(); + _destroy_segtypes(&cmd->segtypes); + _destroy_formats(cmd, &cmd->formats); +diff --git a/test/shell/pvmove-background.sh b/test/shell/pvmove-background.sh +new file mode 100644 +index 0000000..0f657be +--- /dev/null ++++ b/test/shell/pvmove-background.sh +@@ -0,0 +1,24 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Check pvmove behavior when it's progress and machine is rebooted ++ ++. lib/test ++ ++aux prepare_vg 3 ++ ++lvcreate -l1 -n $lv1 $vg "$dev1" ++ ++lvs -o +devices | grep $dev1 ++pvmove -i 1 -b "$dev1" "$dev2" ++sleep 5 # arbitrary... ++lvs -o +devices | not grep "pvmove" ++lvs -o +devices | grep "$dev2" +diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c +index 6fe4670..e70e64a 100644 +--- a/tools/lvmcmdline.c ++++ b/tools/lvmcmdline.c +@@ -1183,7 +1183,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) + out: + if (test_mode()) { + log_verbose("Test mode: Wiping internal cache"); +- lvmcache_destroy(cmd, 1); ++ lvmcache_destroy(cmd, 1, 0); + } + + if ((old_cft = remove_config_tree_by_source(cmd, CONFIG_STRING))) { +diff --git a/tools/pvscan.c b/tools/pvscan.c +index 5db627a..af834fe 100644 +--- a/tools/pvscan.c ++++ b/tools/pvscan.c +@@ -373,7 +373,7 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) + + if (cmd->filter->wipe) + cmd->filter->wipe(cmd->filter); +- lvmcache_destroy(cmd, 1); ++ lvmcache_destroy(cmd, 1, 0); + + /* populate lvmcache */ + if (!lvmetad_vg_list_to_lvmcache(cmd)) +diff --git a/tools/toollib.c b/tools/toollib.c +index 91b0559..6b68f55 100644 +--- a/tools/toollib.c ++++ b/tools/toollib.c +@@ -92,6 +92,7 @@ int become_daemon(struct cmd_context *cmd, int skip_lvm) + + if (!skip_lvm) { + reset_locking(); ++ lvmcache_destroy(cmd, 1, 1); + if (!lvmcache_init()) + /* FIXME Clean up properly here */ + _exit(ECMD_FAILED); +diff --git a/tools/vgrename.c b/tools/vgrename.c +index b5e778f..d97b871 100644 +--- a/tools/vgrename.c ++++ b/tools/vgrename.c +@@ -185,7 +185,7 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, + /* FIXME lvmcache corruption - vginfo duplicated instead of renamed */ + if (cmd->filter->wipe) + cmd->filter->wipe(cmd->filter); +- lvmcache_destroy(cmd, 1); ++ lvmcache_destroy(cmd, 1, 0); + + return 1; + +diff --git a/tools/vgscan.c b/tools/vgscan.c +index baf25b0..fff0e89 100644 +--- a/tools/vgscan.c ++++ b/tools/vgscan.c +@@ -44,7 +44,7 @@ int vgscan(struct cmd_context *cmd, int argc, char **argv) + + if (cmd->filter->wipe) + cmd->filter->wipe(cmd->filter); +- lvmcache_destroy(cmd, 1); ++ lvmcache_destroy(cmd, 1, 0); + + if (arg_count(cmd, cache_ARG)) { + if (lvmetad_active()) { diff --git a/SOURCES/lvm2-2_02_106-upstream-with-cache-support.patch b/SOURCES/lvm2-2_02_106-upstream-with-cache-support.patch new file mode 100644 index 0000000..b9b3d8c --- /dev/null +++ b/SOURCES/lvm2-2_02_106-upstream-with-cache-support.patch @@ -0,0 +1,13782 @@ +commit 8cb42e4d9a3f46998defef78bb5bc38971ef3870 +Author: Peter Rajnoha +Date: Wed Feb 26 16:19:41 2014 +0100 + + v106+cache +--- + WHATS_NEW | 42 +- + WHATS_NEW_DM | 12 + + autoconf/config.guess | 415 +++++++++------- + autoconf/config.sub | 288 +++++++---- + conf/example.conf.in | 28 +- + configure | 37 +- + configure.in | 26 + + daemons/dmeventd/dmeventd.c | 145 +++++- + daemons/dmeventd/dmeventd.h | 1 + + daemons/dmeventd/libdevmapper-event.c | 7 + + daemons/dmeventd/libdevmapper-event.h | 2 +- + daemons/lvmetad/lvmetad-core.c | 20 +- + lib/Makefile.in | 13 +- + lib/activate/activate.c | 168 +++++++ + lib/activate/activate.h | 17 + + lib/activate/dev_manager.c | 68 ++- + lib/activate/dev_manager.h | 3 + + lib/cache/lvmcache.c | 4 +- + lib/cache/lvmetad.c | 29 +- + lib/cache_segtype/.exported_symbols | 1 + + lib/cache_segtype/Makefile.in | 24 + + lib/cache_segtype/cache.c | 448 +++++++++++++++++ + lib/commands/toolcontext.c | 9 +- + lib/config/config.c | 41 +- + lib/config/config.h | 11 +- + lib/config/config_settings.h | 47 +- + lib/config/defaults.h | 7 +- + lib/device/dev-io.c | 4 +- + lib/device/dev-type.c | 71 ++- + lib/device/dev-type.h | 6 +- + lib/device/device-types.h | 9 +- + lib/display/display.c | 16 + + lib/display/display.h | 3 + + lib/format_text/export.c | 6 +- + lib/format_text/flags.c | 4 + + lib/format_text/format-text.c | 16 +- + lib/format_text/tags.c | 10 +- + lib/label/label.c | 1 - + lib/locking/file_locking.c | 17 +- + lib/metadata/cache_manip.c | 278 +++++++++++ + lib/metadata/lv.c | 47 +- + lib/metadata/lv_alloc.h | 2 +- + lib/metadata/lv_manip.c | 543 +++++++++++++++----- + lib/metadata/merge.c | 60 ++- + lib/metadata/metadata-exported.h | 66 ++- + lib/metadata/metadata.c | 65 ++- + lib/metadata/metadata.h | 2 +- + lib/metadata/mirror.c | 14 +- + lib/metadata/pool_manip.c | 325 ++++++++++++ + lib/metadata/raid_manip.c | 10 +- + lib/metadata/segtype.h | 10 + + lib/metadata/snapshot_manip.c | 46 +- + lib/metadata/thin_manip.c | 282 ++--------- + lib/metadata/vg.c | 1 + + lib/metadata/vg.h | 1 + + lib/mirror/mirrored.c | 51 +- + lib/misc/configure.h.in | 3 + + lib/misc/lvm-exec.c | 1 - + lib/raid/raid.c | 40 +- + lib/report/report.c | 17 +- + lib/snapshot/snapshot.c | 15 +- + lib/thin/thin.c | 35 +- + libdm/libdevmapper.h | 134 +++-- + libdm/libdm-common.c | 19 +- + libdm/libdm-deptree.c | 438 ++++++++++++++--- + liblvm/lvm_lv.c | 21 +- + make.tmpl.in | 1 + + man/lvchange.8.in | 5 +- + man/lvconvert.8.in | 46 +- + man/lvcreate.8.in | 61 ++- + man/lvextend.8.in | 7 + + man/lvm.8.in | 199 ++++++++ + man/lvm.conf.5.in | 2 +- + man/lvreduce.8.in | 6 + + man/lvresize.8.in | 11 +- + man/lvs.8.in | 4 +- + man/vgchange.8.in | 7 +- + python/liblvm.c | 25 +- + scripts/Makefile.in | 13 +- + scripts/dm_event_systemd_red_hat.service.in | 5 +- + .../lvm2_activation_generator_systemd_red_hat.c | 2 +- + scripts/lvm2_cluster_activation_red_hat.sh.in | 70 +++ + ...2_cluster_activation_systemd_red_hat.service.in | 17 + + scripts/lvm2_clvmd_systemd_red_hat.service.in | 23 + + scripts/lvm2_cmirrord_systemd_red_hat.service.in | 17 + + scripts/lvm2_lvmetad_systemd_red_hat.service.in | 5 +- + scripts/lvm2_monitoring_init_red_hat.in | 8 +- + scripts/lvm2_monitoring_systemd_red_hat.service.in | 4 +- + test/api/pytest.sh | 15 +- + test/lib/aux.sh | 7 +- + test/lib/check.sh | 17 +- + test/lib/get.sh | 7 + + test/lib/test.sh | 2 +- + test/shell/activation-skip.sh | 32 ++ + test/shell/lock-parallel.sh | 40 ++ + test/shell/lvchange-partial.sh | 6 +- + test/shell/lvchange-raid.sh | 146 +++--- + test/shell/lvconvert-mirror-updown.sh | 36 ++ + test/shell/lvconvert-mirror.sh | 4 + + test/shell/lvconvert-repair-dmeventd.sh | 4 +- + test/shell/lvconvert-repair-thin.sh | 26 +- + test/shell/lvconvert-repair-transient-dmeventd.sh | 2 +- + test/shell/lvconvert-repair-transient.sh | 2 +- + test/shell/lvconvert-thin-external.sh | 20 +- + test/shell/lvcreate-cache.sh | 137 ++++++ + test/shell/lvcreate-large-raid.sh | 4 +- + test/shell/lvcreate-large-raid10.sh | 4 +- + test/shell/lvcreate-large.sh | 4 +- + test/shell/lvcreate-pvtags.sh | 2 +- + test/shell/lvcreate-raid.sh | 118 +++++ + test/shell/lvcreate-raid10.sh | 33 +- + test/shell/lvcreate-thin-external.sh | 18 +- + test/shell/lvcreate-thin-snap.sh | 8 +- + test/shell/lvcreate-thin.sh | 4 +- + test/shell/lvextend-thin-metadata-dmeventd.sh | 93 ++++ + test/shell/lvmetad-disabled.sh | 13 +- + test/shell/lvresize-thin-external-origin.sh | 42 ++ + test/shell/lvresize-thin-metadata.sh | 26 +- + test/shell/lvresize-usage.sh | 9 +- + test/shell/name-mangling.sh | 4 +- + test/shell/process-each-lv.sh | 545 +++++---------------- + test/shell/pvmove-all-segtypes.sh | 25 +- + test/shell/pvmove-cache-segtypes.sh | 178 +++++++ + test/shell/read-ahead.sh | 2 +- + test/shell/snapshot-usage.sh | 49 +- + test/shell/thin-merge.sh | 23 +- + test/shell/thin-vglock.sh | 51 ++ + test/shell/vgsplit-stacked.sh | 4 +- + tools/Makefile.in | 3 +- + tools/args.h | 2 + + tools/commands.h | 38 +- + tools/dumpconfig.c | 10 +- + tools/lvchange.c | 5 +- + tools/lvconvert.c | 229 +++++++-- + tools/lvcreate.c | 311 +++++++++--- + tools/pvmove.c | 43 ++ + tools/tags.c | 23 + + tools/toollib.c | 97 ++-- + tools/toollib.h | 4 +- + tools/vgchange.c | 25 +- + tools/vgsplit.c | 79 +++ + udev/10-dm.rules.in | 6 - + udev/69-dm-lvm-metad.rules.in | 3 + + 143 files changed, 5891 insertions(+), 1889 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 26b63ae..224e351 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,3 +1,43 @@ ++Version 2.02.106 - ++==================================== ++ Fix calculation of maximum size of COW device for snapshot (2.02.99). ++ Do not allow stripe size to be bigger then extent size for lvresize. ++ Zero snapshot COW header when creating read-only snapshot. ++ Comment out config lines in dumpconfig output without default values defined. ++ Improve detection of clustered mirror support. ++ Enhance raid code with feature flags, for now checks for raid10. ++ Move parsing of VG metadata from vg_commit() back to vg_write() (2.02.99) ++ Avoid a PV label scan while in a critical section. ++ Remove (always 0) skip argument from lv_activation_skip(). ++ Create /dev/disk/by-id/lvm-pv-uuid- symlink for each PV via udev. ++ lvcreate computes RAID4/5/6 stripes if not given from # of allocatable PVs. ++ Fix merging of old snapshot into thin volume origin. ++ Use --ignoreskippedcluster in lvm2-monitor initscript/systemd unit. ++ Do not use VG read/write state for LV read/write state. ++ Use --ignoreskippedcluster in activation systemd units if use_lvmetad=0. ++ Allow approximate allocation when specifying size in percentage terms. ++ Add basic LVM support for cache[pool] segment types. ++ Use local exclusive activation for creation of raid in cluster. ++ Use correctly signed 64b constant when selecting raid volumes. ++ Add systemd native service for clvmd, cmirrord and clustered LV activation. ++ Remove ExecReload from lvmetad systemd unit: lvmetad -R undefined. (2.02.98) ++ Do not fork lvmetad if running under systemd. ++ Wipe DM_snapshot_cow signature without prompt in new LVs with blkid wiping. ++ Avoid exposing temporary devices when initializing raid metadata volumes. ++ Add internal tags command to display any tags defined on the host. ++ Prohibit use of external origin with size incompatible with thin pool. ++ Avoid trying to convert single to thin pool and volume at the same time. ++ Add support for partitions on ZFS zvol. ++ Fix unwanted drop of hold flocks on forked children. ++ Respect LVM_LVMETAD_PIDFILE env var for lvm command. ++ Avoid exposing temporary devices when initializing thin pool volume. ++ Fix test when checking target version for available thin features. ++ Detect thin feature external_origin_extend and limit extend when missing. ++ Rename internal pool_can_resize_metadata() to thin_pool_feature_supported(). ++ Issue error if libbblkid detects signature and fails to return offset/length. ++ Update autoconf config.guess/sub to 2014-01-01. ++ Online thin pool metadata resize requires 1.10 kernel thin pool target. ++ + Version 2.02.105 - 20th January 2014 + ==================================== + Fix thin LV flagging for udev to skip scanning only if the LV is wiped. +@@ -31,7 +71,7 @@ Version 2.02.105 - 20th January 2014 + Add --splitsnapshot to lvconvert to separate out cow LV. + Reinstate origin reload to complete lvconvert -s with active LVs. (2.02.98) + Select only active volume groups if vgdisplay -A is used. +- Add -p and LVM_LVMETAD_PID env var to lvmetad to change pid file. ++ Add -p and LVM_LVMETAD_PIDFILE env var to lvmetad to change pid file. + Allow lvmetad to reuse stale socket. + Only unlink lvmetad socket on error if created by the same process. + Append missing newline to lvmetad missing socket path error message. +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index ce302f3..32c2265 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,3 +1,15 @@ ++Version 1.02.85 - ++=================================== ++ Reuse _node_send_messages() for validation of transaction_id in preload. ++ Transaction_id could be lower by one only when messages are prepared. ++ Do not call callback when preload fails. ++ Wrap is_selinux_enabled() to be called just once. ++ Use correctly signed 64b constant when working with raid volumes. ++ Exit dmeventd with pidfile cleanup instead of raising SIGKILL on DIE request. ++ Add new DM_EVENT_GET_PARAMETERS request to dmeventd protocol. ++ Do not use systemd's reload for dmeventd restart, use dmeventd -R instead. ++ Drop cryptsetup rules from 10-dm.rules - cryptsetup >= 1.1.3 sets them. ++ + Version 1.02.84 - 20th January 2014 + =================================== + Revert activation of activated nodes if a node preload callback fails. +diff --git a/autoconf/config.guess b/autoconf/config.guess +index dc84c68..4438cd7 100755 +--- a/autoconf/config.guess ++++ b/autoconf/config.guess +@@ -1,14 +1,12 @@ + #! /bin/sh + # Attempt to guess a canonical system name. +-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +-# Free Software Foundation, Inc. ++# Copyright 1992-2014 Free Software Foundation, Inc. + +-timestamp='2009-11-20' ++timestamp='2014-01-01' + + # This file is free software; you can redistribute it and/or modify it + # under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2 of the License, or ++# the Free Software Foundation; either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, but +@@ -17,26 +15,22 @@ timestamp='2009-11-20' + # General Public License for more details. + # + # You should have received a copy of the GNU 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. ++# along with this program; if not, see . + # + # As a special exception to the GNU General Public License, if you + # distribute this file as part of a program that contains a + # configuration script generated by Autoconf, you may include it under +-# the same distribution terms that you use for the rest of that program. +- +- +-# Originally written by Per Bothner. Please send patches (context +-# diff format) to and include a ChangeLog +-# entry. ++# the same distribution terms that you use for the rest of that ++# program. This Exception is an additional permission under section 7 ++# of the GNU General Public License, version 3 ("GPLv3"). + # +-# This script attempts to guess a canonical system name similar to +-# config.sub. If it succeeds, it prints the system name on stdout, and +-# exits with 0. Otherwise, it exits with 1. ++# Originally written by Per Bothner. + # + # You can get the latest version of this script from: + # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD ++# ++# Please send patches with a ChangeLog entry to config-patches@gnu.org. ++ + + me=`echo "$0" | sed -e 's,.*/,,'` + +@@ -56,8 +50,7 @@ version="\ + GNU config.guess ($timestamp) + + Originally written by Per Bothner. +-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. ++Copyright 1992-2014 Free Software Foundation, Inc. + + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." +@@ -139,12 +132,33 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown + UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown + UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + ++case "${UNAME_SYSTEM}" in ++Linux|GNU|GNU/*) ++ # If the system lacks a compiler, then just pick glibc. ++ # We could probably try harder. ++ LIBC=gnu ++ ++ eval $set_cc_for_build ++ cat <<-EOF > $dummy.c ++ #include ++ #if defined(__UCLIBC__) ++ LIBC=uclibc ++ #elif defined(__dietlibc__) ++ LIBC=dietlibc ++ #else ++ LIBC=gnu ++ #endif ++ EOF ++ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` ++ ;; ++esac ++ + # Note: order is significant - the case branches are not exclusive. + + case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or +- # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, ++ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward +@@ -180,7 +194,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + fi + ;; + *) +- os=netbsd ++ os=netbsd + ;; + esac + # The OS release +@@ -201,6 +215,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; ++ *:Bitrig:*:*) ++ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` ++ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} ++ exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} +@@ -223,7 +241,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) +- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ++ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on +@@ -269,7 +287,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` +- exit ;; ++ # Reset EXIT trap before exiting to avoid spurious non-zero exit code. ++ exitcode=$? ++ trap '' 0 ++ exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead +@@ -295,12 +316,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) +- echo powerpc-ibm-os400 ++ echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; +- arm:riscos:*:*|arm:RISCOS:*:*) ++ arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) +@@ -394,23 +415,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) +- echo m68k-atari-mint${UNAME_RELEASE} ++ echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} +- exit ;; ++ exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) +- echo m68k-atari-mint${UNAME_RELEASE} ++ echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) +- echo m68k-milan-mint${UNAME_RELEASE} +- exit ;; ++ echo m68k-milan-mint${UNAME_RELEASE} ++ exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) +- echo m68k-hades-mint${UNAME_RELEASE} +- exit ;; ++ echo m68k-hades-mint${UNAME_RELEASE} ++ exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) +- echo m68k-unknown-mint${UNAME_RELEASE} +- exit ;; ++ echo m68k-unknown-mint${UNAME_RELEASE} ++ exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; +@@ -480,8 +501,8 @@ EOF + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) +- # DG/UX returns AViiON for all architectures +- UNAME_PROCESSOR=`/usr/bin/uname -p` ++ # DG/UX returns AViiON for all architectures ++ UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ +@@ -494,7 +515,7 @@ EOF + else + echo i586-dg-dgux${UNAME_RELEASE} + fi +- exit ;; ++ exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; +@@ -551,7 +572,7 @@ EOF + echo rs6000-ibm-aix3.2 + fi + exit ;; +- *:AIX:*:[456]) ++ *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 +@@ -594,52 +615,52 @@ EOF + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` +- sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` +- case "${sc_cpu_version}" in +- 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 +- 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 +- 532) # CPU_PA_RISC2_0 +- case "${sc_kernel_bits}" in +- 32) HP_ARCH="hppa2.0n" ;; +- 64) HP_ARCH="hppa2.0w" ;; ++ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` ++ case "${sc_cpu_version}" in ++ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 ++ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 ++ 532) # CPU_PA_RISC2_0 ++ case "${sc_kernel_bits}" in ++ 32) HP_ARCH="hppa2.0n" ;; ++ 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 +- esac ;; +- esac ++ esac ;; ++ esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build +- sed 's/^ //' << EOF >$dummy.c ++ sed 's/^ //' << EOF >$dummy.c + +- #define _HPUX_SOURCE +- #include +- #include ++ #define _HPUX_SOURCE ++ #include ++ #include + +- int main () +- { +- #if defined(_SC_KERNEL_BITS) +- long bits = sysconf(_SC_KERNEL_BITS); +- #endif +- long cpu = sysconf (_SC_CPU_VERSION); ++ int main () ++ { ++ #if defined(_SC_KERNEL_BITS) ++ long bits = sysconf(_SC_KERNEL_BITS); ++ #endif ++ long cpu = sysconf (_SC_CPU_VERSION); + +- switch (cpu) +- { +- case CPU_PA_RISC1_0: puts ("hppa1.0"); break; +- case CPU_PA_RISC1_1: puts ("hppa1.1"); break; +- case CPU_PA_RISC2_0: +- #if defined(_SC_KERNEL_BITS) +- switch (bits) +- { +- case 64: puts ("hppa2.0w"); break; +- case 32: puts ("hppa2.0n"); break; +- default: puts ("hppa2.0"); break; +- } break; +- #else /* !defined(_SC_KERNEL_BITS) */ +- puts ("hppa2.0"); break; +- #endif +- default: puts ("hppa1.0"); break; +- } +- exit (0); +- } ++ switch (cpu) ++ { ++ case CPU_PA_RISC1_0: puts ("hppa1.0"); break; ++ case CPU_PA_RISC1_1: puts ("hppa1.1"); break; ++ case CPU_PA_RISC2_0: ++ #if defined(_SC_KERNEL_BITS) ++ switch (bits) ++ { ++ case 64: puts ("hppa2.0w"); break; ++ case 32: puts ("hppa2.0n"); break; ++ default: puts ("hppa2.0"); break; ++ } break; ++ #else /* !defined(_SC_KERNEL_BITS) */ ++ puts ("hppa2.0"); break; ++ #endif ++ default: puts ("hppa1.0"); break; ++ } ++ exit (0); ++ } + EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa +@@ -730,22 +751,22 @@ EOF + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd +- exit ;; ++ exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi +- exit ;; ++ exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd +- exit ;; ++ exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd +- exit ;; ++ exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd +- exit ;; ++ exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; +@@ -769,14 +790,14 @@ EOF + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` +- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` +- FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` +- echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" +- exit ;; ++ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` ++ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` ++ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" ++ exit ;; + 5000:UNIX_System_V:4.*:*) +- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` +- FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` +- echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" ++ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` ++ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` ++ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} +@@ -788,30 +809,35 @@ EOF + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) +- case ${UNAME_MACHINE} in +- pc98) +- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; ++ UNAME_PROCESSOR=`/usr/bin/uname -p` ++ case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) +- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; ++ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; ++ *:MINGW64*:*) ++ echo ${UNAME_MACHINE}-pc-mingw64 ++ exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; ++ i*:MSYS*:*) ++ echo ${UNAME_MACHINE}-pc-msys ++ exit ;; + i*:windows32*:*) +- # uname -m includes "-pc" on this system. +- echo ${UNAME_MACHINE}-mingw32 ++ # uname -m includes "-pc" on this system. ++ echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) +- case ${UNAME_MACHINE} in ++ case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; +@@ -848,15 +874,22 @@ EOF + exit ;; + *:GNU:*:*) + # the GNU system +- echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` ++ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland +- echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu ++ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; ++ aarch64:Linux:*:*) ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} ++ exit ;; ++ aarch64_be:Linux:*:*) ++ UNAME_MACHINE=aarch64_be ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} ++ exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; +@@ -866,52 +899,56 @@ EOF + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; +- esac ++ esac + objdump --private-headers /bin/sh | grep -q ld.so.1 +- if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi +- echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ++ if test "$?" = 0 ; then LIBC="gnulibc1" ; fi ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} ++ exit ;; ++ arc:Linux:*:* | arceb:Linux:*:*) ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else +- echo ${UNAME_MACHINE}-unknown-linux-gnueabi ++ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ ++ | grep -q __ARM_PCS_VFP ++ then ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi ++ else ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf ++ fi + fi + exit ;; + avr32*:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) +- echo cris-axis-linux-gnu ++ echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) +- echo crisv32-axis-linux-gnu ++ echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) +- echo frv-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} ++ exit ;; ++ hexagon:Linux:*:*) ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) +- LIBC=gnu +- eval $set_cc_for_build +- sed 's/^ //' << EOF >$dummy.c +- #ifdef __dietlibc__ +- LIBC=dietlibc +- #endif +-EOF +- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` +- echo "${UNAME_MACHINE}-pc-linux-${LIBC}" ++ echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build +@@ -930,51 +967,63 @@ EOF + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` +- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ++ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; ++ or1k:Linux:*:*) ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} ++ exit ;; + or32:Linux:*:*) +- echo or32-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) +- echo sparc-unknown-linux-gnu ++ echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) +- echo hppa64-unknown-linux-gnu ++ echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in +- PA7*) echo hppa1.1-unknown-linux-gnu ;; +- PA8*) echo hppa2.0-unknown-linux-gnu ;; +- *) echo hppa-unknown-linux-gnu ;; ++ PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; ++ PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; ++ *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) +- echo powerpc64-unknown-linux-gnu ++ echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) +- echo powerpc-unknown-linux-gnu ++ echo powerpc-unknown-linux-${LIBC} ++ exit ;; ++ ppc64le:Linux:*:*) ++ echo powerpc64le-unknown-linux-${LIBC} ++ exit ;; ++ ppcle:Linux:*:*) ++ echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) +- echo ${UNAME_MACHINE}-ibm-linux ++ echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} ++ exit ;; ++ tile*:Linux:*:*) ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) +- echo ${UNAME_MACHINE}-dec-linux-gnu ++ echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) +- echo x86_64-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) +- echo ${UNAME_MACHINE}-unknown-linux-gnu ++ echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. +@@ -983,11 +1032,11 @@ EOF + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) +- # Unixware is an offshoot of SVR4, but it has its own version +- # number series starting with 2... +- # I am not positive that other SVR4 systems won't match this, ++ # Unixware is an offshoot of SVR4, but it has its own version ++ # number series starting with 2... ++ # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. +- # Use sysv4.2uw... so that sysv4* matches it. ++ # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) +@@ -1019,7 +1068,7 @@ EOF + fi + exit ;; + i*86:*:5:[678]*) +- # UnixWare 7.x, OpenUNIX and OpenServer 6. ++ # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; +@@ -1047,13 +1096,13 @@ EOF + exit ;; + pc:*:*:*) + # Left here for compatibility: +- # uname -m prints for DJGPP always 'pc', but it prints nothing about +- # the processor, so we play safe by assuming i586. ++ # uname -m prints for DJGPP always 'pc', but it prints nothing about ++ # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp +- exit ;; ++ exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; +@@ -1088,8 +1137,8 @@ EOF + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) +- /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ +- && { echo i486-ncr-sysv4; exit; } ;; ++ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ ++ && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ +@@ -1132,10 +1181,10 @@ EOF + echo ns32k-sni-sysv + fi + exit ;; +- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort +- # says +- echo i586-unisys-sysv4 +- exit ;; ++ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort ++ # says ++ echo i586-unisys-sysv4 ++ exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm +@@ -1161,11 +1210,11 @@ EOF + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then +- echo mips-nec-sysv${UNAME_RELEASE} ++ echo mips-nec-sysv${UNAME_RELEASE} + else +- echo mips-unknown-sysv${UNAME_RELEASE} ++ echo mips-unknown-sysv${UNAME_RELEASE} + fi +- exit ;; ++ exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; +@@ -1178,6 +1227,9 @@ EOF + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; ++ x86_64:Haiku:*:*) ++ echo x86_64-unknown-haiku ++ exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; +@@ -1204,19 +1256,31 @@ EOF + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown +- case $UNAME_PROCESSOR in +- i386) +- eval $set_cc_for_build +- if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then +- if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ +- (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ +- grep IS_64BIT_ARCH >/dev/null +- then +- UNAME_PROCESSOR="x86_64" +- fi +- fi ;; +- unknown) UNAME_PROCESSOR=powerpc ;; +- esac ++ eval $set_cc_for_build ++ if test "$UNAME_PROCESSOR" = unknown ; then ++ UNAME_PROCESSOR=powerpc ++ fi ++ if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then ++ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then ++ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ ++ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ ++ grep IS_64BIT_ARCH >/dev/null ++ then ++ case $UNAME_PROCESSOR in ++ i386) UNAME_PROCESSOR=x86_64 ;; ++ powerpc) UNAME_PROCESSOR=powerpc64 ;; ++ esac ++ fi ++ fi ++ elif test "$UNAME_PROCESSOR" = i386 ; then ++ # Avoid executing cc on OS X 10.9, as it ships with a stub ++ # that puts up a graphical alert prompting to install ++ # developer tools. Any system running Mac OS X 10.7 or ++ # later (Darwin 11 and later) is required to have a 64-bit ++ # processor. This is not true of the ARM version of Darwin ++ # that Apple uses in portable devices. ++ UNAME_PROCESSOR=x86_64 ++ fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) +@@ -1230,7 +1294,10 @@ EOF + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; +- NSE-?:NONSTOP_KERNEL:*:*) ++ NEO-?:NONSTOP_KERNEL:*:*) ++ echo neo-tandem-nsk${UNAME_RELEASE} ++ exit ;; ++ NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) +@@ -1275,13 +1342,13 @@ EOF + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) +- echo mips-sei-seiux${UNAME_RELEASE} ++ echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) +- UNAME_MACHINE=`(uname -p) 2>/dev/null` ++ UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; +@@ -1299,11 +1366,11 @@ EOF + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; ++ x86_64:VMkernel:*:*) ++ echo ${UNAME_MACHINE}-unknown-esx ++ exit ;; + esac + +-#echo '(No uname command or uname output not recognized.)' 1>&2 +-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 +- + eval $set_cc_for_build + cat >$dummy.c < + printf ("m68k-sony-newsos%s\n", + #ifdef NEWSOS4 +- "4" ++ "4" + #else +- "" ++ "" + #endif +- ); exit (0); ++ ); exit (0); + #endif + #endif + +diff --git a/autoconf/config.sub b/autoconf/config.sub +index 2a55a50..092cff0 100755 +--- a/autoconf/config.sub ++++ b/autoconf/config.sub +@@ -1,38 +1,31 @@ + #! /bin/sh + # Configuration validation subroutine script. +-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +-# Free Software Foundation, Inc. ++# Copyright 1992-2014 Free Software Foundation, Inc. + +-timestamp='2009-11-20' ++timestamp='2014-01-01' + +-# This file is (in principle) common to ALL GNU software. +-# The presence of a machine in this file suggests that SOME GNU software +-# can handle that machine. It does not imply ALL GNU software can. +-# +-# This file is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2 of the License, or ++# This file is free software; you can redistribute it and/or modify it ++# under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or + # (at your option) any later version. + # +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. ++# This program is distributed in the hope that it will be useful, but ++# WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# General Public License for more details. + # + # You should have received a copy of the GNU 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. ++# along with this program; if not, see . + # + # As a special exception to the GNU General Public License, if you + # distribute this file as part of a program that contains a + # configuration script generated by Autoconf, you may include it under +-# the same distribution terms that you use for the rest of that program. ++# the same distribution terms that you use for the rest of that ++# program. This Exception is an additional permission under section 7 ++# of the GNU General Public License, version 3 ("GPLv3"). + + +-# Please send patches to . Submit a context +-# diff and a properly formatted GNU ChangeLog entry. ++# Please send patches with a ChangeLog entry to config-patches@gnu.org. + # + # Configuration subroutine to validate and canonicalize a configuration type. + # Supply the specified configuration type as an argument. +@@ -75,8 +68,7 @@ Report bugs and patches to ." + version="\ + GNU config.sub ($timestamp) + +-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. ++Copyright 1992-2014 Free Software Foundation, Inc. + + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." +@@ -123,13 +115,18 @@ esac + # Here we must recognize all the valid KERNEL-OS combinations. + maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` + case $maybe_os in +- nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ +- uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ ++ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ ++ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ ++ knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; ++ android-linux) ++ os=-linux-android ++ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ++ ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] +@@ -152,12 +149,12 @@ case $os in + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ +- -apple | -axis | -knuth | -cray | -microblaze) ++ -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; +- -bluegene*) +- os=-cnk ++ -bluegene*) ++ os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= +@@ -173,10 +170,10 @@ case $os in + os=-chorusos + basic_machine=$1 + ;; +- -chorusrdb) +- os=-chorusrdb ++ -chorusrdb) ++ os=-chorusrdb + basic_machine=$1 +- ;; ++ ;; + -hiux*) + os=-hiuxwe2 + ;; +@@ -221,6 +218,12 @@ case $os in + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; ++ -lynx*178) ++ os=-lynxos178 ++ ;; ++ -lynx*5) ++ os=-lynxos5 ++ ;; + -lynx*) + os=-lynxos + ;; +@@ -245,20 +248,28 @@ case $basic_machine in + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ ++ | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ +- | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ ++ | arc | arceb \ ++ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ ++ | avr | avr32 \ ++ | be32 | be64 \ + | bfin \ +- | c4x | clipper \ ++ | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ ++ | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ ++ | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ ++ | k1om \ ++ | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ +- | maxq | mb | microblaze | mcore | mep | metag \ ++ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ +@@ -276,34 +287,45 @@ case $basic_machine in + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ ++ | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ +- | nios | nios2 \ ++ | nds32 | nds32le | nds32be \ ++ | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ +- | or32 \ ++ | open8 \ ++ | or1k | or32 \ + | pdp10 | pdp11 | pj | pjl \ +- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ ++ | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ +- | rx \ ++ | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ +- | spu | strongarm \ +- | tahoe | thumb | tic4x | tic80 | tron \ ++ | spu \ ++ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ +- | v850 | v850e \ ++ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ +- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ ++ | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; +- m6811 | m68hc11 | m6812 | m68hc12 | picochip) +- # Motorola 68HC11/12. ++ c54x) ++ basic_machine=tic54x-unknown ++ ;; ++ c55x) ++ basic_machine=tic55x-unknown ++ ;; ++ c6x) ++ basic_machine=tic6x-unknown ++ ;; ++ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; +@@ -313,6 +335,21 @@ case $basic_machine in + basic_machine=mt-unknown + ;; + ++ strongarm | thumb | xscale) ++ basic_machine=arm-unknown ++ ;; ++ xgate) ++ basic_machine=$basic_machine-unknown ++ os=-none ++ ;; ++ xscaleeb) ++ basic_machine=armeb-unknown ++ ;; ++ ++ xscaleel) ++ basic_machine=armel-unknown ++ ;; ++ + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. +@@ -327,25 +364,31 @@ case $basic_machine in + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ ++ | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ +- | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ ++ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ ++ | be32-* | be64-* \ + | bfin-* | bs2000-* \ +- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ +- | clipper-* | craynv-* | cydra-* \ ++ | c[123]* | c30-* | [cjt]90-* | c4x-* \ ++ | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ ++ | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ ++ | k1om-* \ ++ | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ +- | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ ++ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ ++ | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ +@@ -363,29 +406,34 @@ case $basic_machine in + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ ++ | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ +- | nios-* | nios2-* \ ++ | nds32-* | nds32le-* | nds32be-* \ ++ | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ ++ | open8-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ +- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ ++ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ +- | romp-* | rs6000-* | rx-* \ ++ | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ +- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ +- | tahoe-* | thumb-* \ +- | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ ++ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ ++ | tahoe-* \ ++ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ ++ | tile*-* \ + | tron-* \ + | ubicom32-* \ +- | v850-* | v850e-* | vax-* \ ++ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ ++ | vax-* \ + | we32k-* \ +- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ ++ | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) +@@ -410,7 +458,7 @@ case $basic_machine in + basic_machine=a29k-amd + os=-udi + ;; +- abacus) ++ abacus) + basic_machine=abacus-unknown + ;; + adobe68k) +@@ -480,11 +528,20 @@ case $basic_machine in + basic_machine=powerpc-ibm + os=-cnk + ;; ++ c54x-*) ++ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ++ ;; ++ c55x-*) ++ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ++ ;; ++ c6x-*) ++ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ++ ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; +- cegcc) ++ cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; +@@ -516,7 +573,7 @@ case $basic_machine in + basic_machine=craynv-cray + os=-unicosmp + ;; +- cr16) ++ cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; +@@ -674,7 +731,6 @@ case $basic_machine in + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +-# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 +@@ -732,11 +788,15 @@ case $basic_machine in + basic_machine=ns32k-utek + os=-sysv + ;; +- microblaze) ++ microblaze*) + basic_machine=microblaze-xilinx + ;; ++ mingw64) ++ basic_machine=x86_64-pc ++ os=-mingw64 ++ ;; + mingw32) +- basic_machine=i386-pc ++ basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) +@@ -771,10 +831,18 @@ case $basic_machine in + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; ++ msys) ++ basic_machine=i686-pc ++ os=-msys ++ ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; ++ nacl) ++ basic_machine=le32-unknown ++ os=-nacl ++ ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 +@@ -839,6 +907,12 @@ case $basic_machine in + np1) + basic_machine=np1-gould + ;; ++ neo-tandem) ++ basic_machine=neo-tandem ++ ;; ++ nse-tandem) ++ basic_machine=nse-tandem ++ ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; +@@ -921,9 +995,10 @@ case $basic_machine in + ;; + power) basic_machine=power-ibm + ;; +- ppc) basic_machine=powerpc-unknown ++ ppc | ppcbe) basic_machine=powerpc-unknown + ;; +- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ++ ppc-* | ppcbe-*) ++ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown +@@ -948,7 +1023,11 @@ case $basic_machine in + basic_machine=i586-unknown + os=-pw32 + ;; +- rdos) ++ rdos | rdos64) ++ basic_machine=x86_64-pc ++ os=-rdos ++ ;; ++ rdos32) + basic_machine=i386-pc + os=-rdos + ;; +@@ -1017,6 +1096,9 @@ case $basic_machine in + basic_machine=i860-stratus + os=-sysv4 + ;; ++ strongarm-* | thumb-*) ++ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ++ ;; + sun2) + basic_machine=m68000-sun + ;; +@@ -1073,20 +1155,8 @@ case $basic_machine in + basic_machine=t90-cray + os=-unicos + ;; +- tic54x | c54x*) +- basic_machine=tic54x-unknown +- os=-coff +- ;; +- tic55x | c55x*) +- basic_machine=tic55x-unknown +- os=-coff +- ;; +- tic6x | c6x*) +- basic_machine=tic6x-unknown +- os=-coff +- ;; + tile*) +- basic_machine=tile-unknown ++ basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) +@@ -1156,6 +1226,9 @@ case $basic_machine in + xps | xps100) + basic_machine=xps100-honeywell + ;; ++ xscale-* | xscalee[bl]-*) ++ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ++ ;; + ymp) + basic_machine=ymp-cray + os=-unicos +@@ -1253,11 +1326,11 @@ esac + if [ x"$os" != x"" ] + then + case $os in +- # First match some system type aliases +- # that might get confused with valid system types. ++ # First match some system type aliases ++ # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. +- -auroraux) +- os=-auroraux ++ -auroraux) ++ os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` +@@ -1281,20 +1354,21 @@ case $os in + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ +- | -sym* | -kopensolaris* \ ++ | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ +- | -openbsd* | -solidbsd* \ ++ | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ +- | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ +- | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ ++ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ ++ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ ++ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ +@@ -1341,7 +1415,7 @@ case $os in + -opened*) + os=-openedition + ;; +- -os400*) ++ -os400*) + os=-os400 + ;; + -wince*) +@@ -1390,7 +1464,7 @@ case $os in + -sinix*) + os=-sysv4 + ;; +- -tpf*) ++ -tpf*) + os=-tpf + ;; + -triton*) +@@ -1426,15 +1500,14 @@ case $os in + -aros*) + os=-aros + ;; +- -kaos*) +- os=-kaos +- ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; ++ -nacl*) ++ ;; + -none) + ;; + *) +@@ -1457,10 +1530,10 @@ else + # system, and we'll never get to this point. + + case $basic_machine in +- score-*) ++ score-*) + os=-elf + ;; +- spu-*) ++ spu-*) + os=-elf + ;; + *-acorn) +@@ -1472,8 +1545,23 @@ case $basic_machine in + arm*-semi) + os=-aout + ;; +- c4x-* | tic4x-*) +- os=-coff ++ c4x-* | tic4x-*) ++ os=-coff ++ ;; ++ c8051-*) ++ os=-elf ++ ;; ++ hexagon-*) ++ os=-elf ++ ;; ++ tic54x-*) ++ os=-coff ++ ;; ++ tic55x-*) ++ os=-coff ++ ;; ++ tic6x-*) ++ os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) +@@ -1493,14 +1581,11 @@ case $basic_machine in + ;; + m68000-sun) + os=-sunos3 +- # This also exists in the configure program, but was not the +- # default. +- # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; +- mep-*) ++ mep-*) + os=-elf + ;; + mips*-cisco) +@@ -1509,6 +1594,9 @@ case $basic_machine in + mips*-*) + os=-elf + ;; ++ or1k-*) ++ os=-elf ++ ;; + or32-*) + os=-coff + ;; +@@ -1527,7 +1615,7 @@ case $basic_machine in + *-ibm) + os=-aix + ;; +- *-knuth) ++ *-knuth) + os=-mmixware + ;; + *-wec) +diff --git a/conf/example.conf.in b/conf/example.conf.in +index 79b574e..5e23291 100644 +--- a/conf/example.conf.in ++++ b/conf/example.conf.in +@@ -301,6 +301,21 @@ allocation { + # until version 2.02.85. + mirror_logs_require_separate_pvs = 0 + ++ # Set to 1 to guarantee that cache_pool metadata will always be ++ # placed on different PVs from the cache_pool data. ++ cache_pool_metadata_require_separate_pvs = 0 ++ ++ # Specify the minimal chunk size (in kiB) for cache pool volumes. ++ # Using a chunk_size that is too large can result in wasteful use of ++ # the cache, where small reads and writes can cause large sections of ++ # an LV to be mapped into the cache. However, choosing a chunk_size ++ # that is too small can result in more overhead trying to manage the ++ # numerous chunks that become mapped into the cache. The former is ++ # more of a problem than the latter in most cases, so we default to ++ # a value that is on the smaller end of the spectrum. Supported values ++ # range from 32(kiB) to 1048576 in multiples of 32. ++ # cache_pool_chunk_size = 64 ++ + # Set to 1 to guarantee that thin pool metadata will always + # be placed on different PVs from the pool data. + thin_pool_metadata_require_separate_pvs = 0 +@@ -321,14 +336,14 @@ allocation { + # thin_pool_chunk_size_policy = "generic" + + # Specify the minimal chunk size (in KB) for thin pool volumes. +- # Use of the larger chunk size may improve perfomance for plain ++ # Use of the larger chunk size may improve performance for plain + # thin volumes, however using them for snapshot volumes is less efficient, + # as it consumes more space and takes extra time for copying. + # When unset, lvm tries to estimate chunk size starting from 64KB + # Supported values are in range from 64 to 1048576. + # thin_pool_chunk_size = 64 + +- # Specify discards behavior of the thin pool volume. ++ # Specify discards behaviour of the thin pool volume. + # Select one of "ignore", "nopassdown", "passdown" + # thin_pool_discards = "passdown" + +@@ -408,7 +423,7 @@ log { + # Configuration of metadata backups and archiving. In LVM2 when we + # talk about a 'backup' we mean making a copy of the metadata for the + # *current* system. The 'archive' contains old metadata configurations. +-# Backups are stored in a human readeable text format. ++# Backups are stored in a human readable text format. + backup { + + # Should we maintain a backup of the current metadata configuration ? +@@ -599,7 +614,7 @@ global { + # "mirror" - LVM will layer the 'mirror' and 'stripe' segment types. It + # will do this by creating a mirror on top of striped sub-LVs; + # effectively creating a RAID 0+1 array. This is suboptimal +- # in terms of providing redunancy and performance. Changing to ++ # in terms of providing redundancy and performance. Changing to + # this setting is not advised. + # Specify the '--type ' option to override this default + # setting. +@@ -663,7 +678,7 @@ global { + # Array of string options passed with thin_check command. By default, + # option "-q" is for quiet output. + # With thin_check version 2.1 or newer you can add "--ignore-non-fatal-errors" +- # to let it pass through ignoreable errors and fix them later. ++ # to let it pass through ignorable errors and fix them later. + # + # thin_check_options = [ "-q" ] + +@@ -691,6 +706,7 @@ global { + # discards_non_power_2 + # external_origin + # metadata_resize ++ # external_origin_extend + # + # thin_disabled_features = [ "discards", "block_size" ] + } +@@ -822,7 +838,7 @@ activation { + # auto_set_activation_skip = 1 + + # For RAID or 'mirror' segment types, 'raid_region_size' is the +- # size (in kiB) of each: ++ # size (in KiB) of each: + # - synchronization operation when initializing + # - each copy operation when performing a 'pvmove' (using 'mirror' segtype) + # This setting has replaced 'mirror_region_size' since version 2.02.99 +diff --git a/configure b/configure +index c96911c..6803010 100755 +--- a/configure ++++ b/configure +@@ -688,6 +688,7 @@ CLUSTER + CLDWHOLEARCHIVE + CLDNOWHOLEARCHIVE + CLDFLAGS ++CACHE + BUILD_LVMETAD + BUILD_DMEVENTD + BUILD_CMIRRORD +@@ -839,6 +840,7 @@ with_thin + with_thin_check + with_thin_dump + with_thin_repair ++with_cache + enable_readline + enable_realtime + enable_ocf +@@ -1622,6 +1624,8 @@ Optional Packages: + --with-thin-check=PATH thin_check tool: [[autodetect]] + --with-thin-dump=PATH thin_dump tool: [[autodetect]] + --with-thin-repair=PATH thin_repair tool: [[autodetect]] ++ --with-cache=TYPE cache support: internal/shared/none ++ [[TYPE=none]] + --with-ocfdir=DIR install OCF files in DIR + [[PREFIX/lib/ocf/resource.d/lvm2]] + --with-default-pid-dir=PID_DIR +@@ -7531,6 +7535,30 @@ cat >>confdefs.h <<_ACEOF + _ACEOF + + ++################################################################################ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include cache" >&5 ++$as_echo_n "checking whether to include cache... " >&6; } ++ ++# Check whether --with-cache was given. ++if test "${with_cache+set}" = set; then : ++ withval=$with_cache; CACHE=$withval ++else ++ CACHE=none ++fi ++ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CACHE" >&5 ++$as_echo "$CACHE" >&6; } ++ ++if [ "x$CACHE" != xnone -a "x$CACHE" != xinternal -a "x$CACHE" != xshared ]; ++ then as_fn_error $? "--with-cache parameter invalid ++" "$LINENO" 5 ++fi; ++ ++if test x$CACHE = xinternal; then ++ ++$as_echo "#define CACHE_INTERNAL 1" >>confdefs.h ++ ++fi + + ################################################################################ + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable readline" >&5 +@@ -10150,6 +10178,7 @@ fi + if [ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \ + -o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \ + -o "x$RAID" = xshared \ ++ -o "x$CACHE" = xshared \ + \) -a "x$STATIC_LINK" = xyes ]; + then as_fn_error $? "Features cannot be 'shared' when building statically + " "$LINENO" 5 +@@ -11423,8 +11452,9 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'` + + + ++ + ################################################################################ +-ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/default.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" ++ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/default.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/lvm2_clvmd_systemd_red_hat.service scripts/cmirrord_init_red_hat scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" + + cat >confcache <<\_ACEOF + # This file is a shell script that caches the results of configure +@@ -12142,6 +12172,7 @@ do + "lib/raid/Makefile") CONFIG_FILES="$CONFIG_FILES lib/raid/Makefile" ;; + "lib/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES lib/snapshot/Makefile" ;; + "lib/thin/Makefile") CONFIG_FILES="$CONFIG_FILES lib/thin/Makefile" ;; ++ "lib/cache_segtype/Makefile") CONFIG_FILES="$CONFIG_FILES lib/cache_segtype/Makefile" ;; + "libdaemon/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/Makefile" ;; + "libdaemon/client/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/client/Makefile" ;; + "libdaemon/server/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/server/Makefile" ;; +@@ -12156,8 +12187,12 @@ do + "scripts/blkdeactivate.sh") CONFIG_FILES="$CONFIG_FILES scripts/blkdeactivate.sh" ;; + "scripts/blk_availability_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/blk_availability_init_red_hat" ;; + "scripts/blk_availability_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/blk_availability_systemd_red_hat.service" ;; ++ "scripts/lvm2_cluster_activation_red_hat.sh") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_cluster_activation_red_hat.sh" ;; ++ "scripts/lvm2_cluster_activation_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_cluster_activation_systemd_red_hat.service" ;; + "scripts/clvmd_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/clvmd_init_red_hat" ;; ++ "scripts/lvm2_clvmd_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_clvmd_systemd_red_hat.service" ;; + "scripts/cmirrord_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/cmirrord_init_red_hat" ;; ++ "scripts/lvm2_cmirrord_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_cmirrord_systemd_red_hat.service" ;; + "scripts/lvm2_lvmetad_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_init_red_hat" ;; + "scripts/lvm2_lvmetad_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.socket" ;; + "scripts/lvm2_lvmetad_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.service" ;; +diff --git a/configure.in b/configure.in +index cff7e45..f125209 100644 +--- a/configure.in ++++ b/configure.in +@@ -476,6 +476,25 @@ AC_DEFINE_UNQUOTED([THIN_DUMP_CMD], ["$THIN_DUMP_CMD"], + AC_DEFINE_UNQUOTED([THIN_REPAIR_CMD], ["$THIN_REPAIR_CMD"], + [The path to 'thin_repair', if available.]) + ++################################################################################ ++dnl -- cache inclusion type ++AC_MSG_CHECKING(whether to include cache) ++AC_ARG_WITH(cache, ++ AC_HELP_STRING([--with-cache=TYPE], ++ [cache support: internal/shared/none ++ [[TYPE=none]]]), ++ CACHE=$withval, CACHE=none) ++AC_MSG_RESULT($CACHE) ++ ++if [[ "x$CACHE" != xnone -a "x$CACHE" != xinternal -a "x$CACHE" != xshared ]]; ++ then AC_MSG_ERROR( ++--with-cache parameter invalid ++) ++fi; ++ ++if test x$CACHE = xinternal; then ++ AC_DEFINE([CACHE_INTERNAL], 1, [Define to 1 to include built-in support for cache.]) ++fi + + ################################################################################ + dnl -- Disable readline +@@ -1241,6 +1260,7 @@ dnl -- Check for shared/static conflicts + if [[ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \ + -o "x$SNAPSHOTS" = xshared -o "x$MIRRORS" = xshared \ + -o "x$RAID" = xshared \ ++ -o "x$CACHE" = xshared \ + \) -a "x$STATIC_LINK" = xyes ]]; + then AC_MSG_ERROR( + Features cannot be 'shared' when building statically +@@ -1596,6 +1616,7 @@ AC_SUBST(BLKID_WIPING) + AC_SUBST(BUILD_CMIRRORD) + AC_SUBST(BUILD_DMEVENTD) + AC_SUBST(BUILD_LVMETAD) ++AC_SUBST(CACHE) + AC_SUBST(CFLAGS) + AC_SUBST(CFLOW_CMD) + AC_SUBST(CLDFLAGS) +@@ -1752,6 +1773,7 @@ lib/misc/lvm-version.h + lib/raid/Makefile + lib/snapshot/Makefile + lib/thin/Makefile ++lib/cache_segtype/Makefile + libdaemon/Makefile + libdaemon/client/Makefile + libdaemon/server/Makefile +@@ -1766,8 +1788,12 @@ python/setup.py + scripts/blkdeactivate.sh + scripts/blk_availability_init_red_hat + scripts/blk_availability_systemd_red_hat.service ++scripts/lvm2_cluster_activation_red_hat.sh ++scripts/lvm2_cluster_activation_systemd_red_hat.service + scripts/clvmd_init_red_hat ++scripts/lvm2_clvmd_systemd_red_hat.service + scripts/cmirrord_init_red_hat ++scripts/lvm2_cmirrord_systemd_red_hat.service + scripts/lvm2_lvmetad_init_red_hat + scripts/lvm2_lvmetad_systemd_red_hat.socket + scripts/lvm2_lvmetad_systemd_red_hat.service +diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c +index 179775a..4a17fb2 100644 +--- a/daemons/dmeventd/dmeventd.c ++++ b/daemons/dmeventd/dmeventd.c +@@ -514,6 +514,30 @@ static int _get_status(struct message_data *message_data) + + } + ++static int _get_parameters(struct message_data *message_data) { ++ struct dm_event_daemon_message *msg = message_data->msg; ++ char buf[128]; ++ int r = -1; ++ ++ dm_free(msg->data); ++ ++ if (!(dm_snprintf(buf, sizeof(buf), "%s pid=%d daemon=%s exec_method=%s", ++ message_data->id, ++ getpid(), ++ _foreground ? "no" : "yes", ++ _systemd_activation ? "systemd" : "direct"))) ++ goto_out; ++ ++ msg->size = strlen(buf) + 1; ++ if (!(msg->data = dm_malloc(msg->size))) ++ goto_out; ++ if (!dm_strncpy(msg->data, buf, msg->size)) ++ goto_out; ++ r = 0; ++out: ++ return r; ++} ++ + /* Cleanup at exit. */ + static void _exit_dm_lib(void) + { +@@ -1437,6 +1461,14 @@ static int _handle_request(struct dm_event_daemon_message *msg, + { DM_EVENT_CMD_GET_TIMEOUT, _get_timeout}, + { DM_EVENT_CMD_ACTIVE, _active}, + { DM_EVENT_CMD_GET_STATUS, _get_status}, ++ /* dmeventd parameters of running dmeventd, ++ * returns 'pid= daemon= exec_method=' ++ * pid - pidfile of running dmeventd ++ * daemon - running as a daemon or not (foreground)? ++ * exec_method - "direct" if executed directly or ++ * "systemd" if executed via systemd ++ */ ++ { DM_EVENT_CMD_GET_PARAMETERS, _get_parameters}, + }, *req; + + for (req = requests; req < requests + sizeof(requests) / sizeof(struct request); req++) +@@ -1504,7 +1536,11 @@ static void _process_request(struct dm_event_fifos *fifos) + + dm_free(msg.data); + +- if (die) raise(9); ++ if (die) { ++ if (unlink(DMEVENTD_PIDFILE)) ++ perror(DMEVENTD_PIDFILE ": unlink failed"); ++ _exit(0); ++ } + } + + static void _process_initial_registrations(void) +@@ -1732,6 +1768,7 @@ out: + unsetenv(SD_LISTEN_FDS_ENV_VAR_NAME); + return r; + } ++ + #endif + + static void _remove_files_on_exit(void) +@@ -1823,6 +1860,59 @@ static void _daemonize(void) + setsid(); + } + ++static int _reinstate_registrations(struct dm_event_fifos *fifos) ++{ ++ static const char _failed_parsing_msg[] = "Failed to parse existing event registration.\n"; ++ static const char *_delim = " "; ++ struct dm_event_daemon_message msg = { 0 }; ++ char *endp, *dso_name, *dev_name, *mask, *timeout; ++ unsigned long mask_value, timeout_value; ++ int i, ret; ++ ++ ret = daemon_talk(fifos, &msg, DM_EVENT_CMD_HELLO, NULL, NULL, 0, 0); ++ dm_free(msg.data); ++ msg.data = NULL; ++ ++ if (ret) { ++ fprintf(stderr, "Failed to communicate with new instance of dmeventd.\n"); ++ return 0; ++ } ++ ++ for (i = 0; _initial_registrations[i]; ++i) { ++ if (!(strtok(_initial_registrations[i], _delim)) || ++ !(dso_name = strtok(NULL, _delim)) || ++ !(dev_name = strtok(NULL, _delim)) || ++ !(mask = strtok(NULL, _delim)) || ++ !(timeout = strtok(NULL, _delim))) { ++ fprintf(stderr, _failed_parsing_msg); ++ continue; ++ } ++ ++ errno = 0; ++ mask_value = strtoul(mask, &endp, 10); ++ if (errno || !endp || *endp) { ++ fprintf(stderr, _failed_parsing_msg); ++ continue; ++ } ++ ++ errno = 0; ++ timeout_value = strtoul(timeout, &endp, 10); ++ if (errno || !endp || *endp) { ++ fprintf(stderr, _failed_parsing_msg); ++ continue; ++ } ++ ++ if (daemon_talk(fifos, &msg, DM_EVENT_CMD_REGISTER_FOR_EVENT, ++ dso_name, ++ dev_name, ++ (enum dm_event_mask) mask_value, ++ timeout_value)) ++ fprintf(stderr, "Failed to reinstate monitoring for device %s.\n", dev_name); ++ } ++ ++ return 1; ++} ++ + static void restart(void) + { + struct dm_event_fifos fifos = { +@@ -1837,9 +1927,9 @@ static void restart(void) + char *message; + int length; + int version; ++ const char *e; + + /* Get the list of registrations from the running daemon. */ +- + if (!init_fifos(&fifos)) { + fprintf(stderr, "WARNING: Could not initiate communication with existing dmeventd.\n"); + exit(EXIT_FAILURE); +@@ -1885,24 +1975,61 @@ static void restart(void) + } + _initial_registrations[count] = 0; + ++ if (version >= 2) { ++ if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_GET_PARAMETERS, "-", "-", 0, 0)) { ++ fprintf(stderr, "Failed to acquire parameters from old dmeventd.\n"); ++ goto bad; ++ } ++ if (strstr(msg.data, "exec_method=systemd")) ++ _systemd_activation = 1; ++ } ++#ifdef __linux__ ++ /* ++ * If the protocol version is old, just assume that if systemd is running, ++ * the dmeventd is also run as a systemd service via fifo activation. ++ */ ++ if (version < 2) { ++ /* This check is copied from sd-daemon.c. */ ++ struct stat st; ++ if (!lstat("/run/systemd/system/", &st) && !!S_ISDIR(st.st_mode)) ++ _systemd_activation = 1; ++ } ++#endif ++ + if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) { + fprintf(stderr, "Old dmeventd refused to die.\n"); + goto bad; + } + +- /* +- * Wait for daemon to die, detected by sending further DIE messages +- * until one fails. +- */ ++ if (!_systemd_activation && ++ ((e = getenv(SD_ACTIVATION_ENV_VAR_NAME)) && strcmp(e, "1"))) ++ _systemd_activation = 1; ++ + for (i = 0; i < 10; ++i) { +- if (daemon_talk(&fifos, &msg, DM_EVENT_CMD_DIE, "-", "-", 0, 0)) +- break; /* yep, it's dead probably */ ++ if ((access(DMEVENTD_PIDFILE, F_OK) == -1) && (errno == ENOENT)) ++ break; + usleep(10); + } + ++ if (!_systemd_activation) { ++ fini_fifos(&fifos); ++ return; ++ } ++ ++ /* Reopen fifos. */ + fini_fifos(&fifos); ++ if (!init_fifos(&fifos)) { ++ fprintf(stderr, "Could not initiate communication with new instance of dmeventd.\n"); ++ exit(EXIT_FAILURE); ++ } + +- return; ++ if (!_reinstate_registrations(&fifos)) { ++ fprintf(stderr, "Failed to reinstate monitoring with new instance of dmeventd.\n"); ++ goto bad; ++ } ++ ++ fini_fifos(&fifos); ++ exit(EXIT_SUCCESS); + bad: + fini_fifos(&fifos); + exit(EXIT_FAILURE); +diff --git a/daemons/dmeventd/dmeventd.h b/daemons/dmeventd/dmeventd.h +index e21cf45..25a4bbb 100644 +--- a/daemons/dmeventd/dmeventd.h ++++ b/daemons/dmeventd/dmeventd.h +@@ -34,6 +34,7 @@ enum dm_event_command { + DM_EVENT_CMD_HELLO, + DM_EVENT_CMD_DIE, + DM_EVENT_CMD_GET_STATUS, ++ DM_EVENT_CMD_GET_PARAMETERS, + }; + + /* Message passed between client and daemon. */ +diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c +index 1b5273d..db9200f 100644 +--- a/daemons/dmeventd/libdevmapper-event.c ++++ b/daemons/dmeventd/libdevmapper-event.c +@@ -619,6 +619,13 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh) + + uuid = dm_task_get_uuid(dmt); + ++ if (!strstr(dmevh->dso, "libdevmapper-event-lvm2thin.so") && ++ !strstr(dmevh->dso, "libdevmapper-event-lvm2snapshot.so") && ++ !strstr(dmevh->dso, "libdevmapper-event-lvm2mirror.so") && ++ !strstr(dmevh->dso, "libdevmapper-event-lvm2raid.so")) ++ log_warn("WARNING: %s: dmeventd plugins are deprecated", dmevh->dso); ++ ++ + if ((err = _do_event(DM_EVENT_CMD_REGISTER_FOR_EVENT, dmevh->dmeventd_path, &msg, + dmevh->dso, uuid, dmevh->mask, dmevh->timeout)) < 0) { + log_error("%s: event registration failed: %s", +diff --git a/daemons/dmeventd/libdevmapper-event.h b/daemons/dmeventd/libdevmapper-event.h +index 9c1cc6f..532bebf 100644 +--- a/daemons/dmeventd/libdevmapper-event.h ++++ b/daemons/dmeventd/libdevmapper-event.h +@@ -46,7 +46,7 @@ enum dm_event_mask { + }; + + #define DM_EVENT_ALL_ERRORS DM_EVENT_ERROR_MASK +-#define DM_EVENT_PROTOCOL_VERSION 1 ++#define DM_EVENT_PROTOCOL_VERSION 2 + + struct dm_task; + struct dm_event_handler; +diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c +index eefbf1d..e6e222f 100644 +--- a/daemons/lvmetad/lvmetad-core.c ++++ b/daemons/lvmetad/lvmetad-core.c +@@ -1095,7 +1095,8 @@ static response handler(daemon_state s, client_handle h, request r) + return daemon_reply_simple("token_mismatch", + "expected = %s", state->token, + "received = %s", token, +- "reason = %s", "token mismatch", NULL); ++ "reason = %s", ++ "lvmetad cache is invalid due to a global_filter change or due to a running rescan", NULL); + } + pthread_mutex_unlock(&state->token_lock); + +@@ -1189,7 +1190,7 @@ static int fini(daemon_state *s) + return 1; + } + +-static void usage(char *prog, FILE *file) ++static void usage(const char *prog, FILE *file) + { + fprintf(file, "Usage:\n" + "%s [-V] [-h] [-f] [-l {all|wire|debug}] [-s path]\n\n" +@@ -1204,27 +1205,19 @@ static void usage(char *prog, FILE *file) + int main(int argc, char *argv[]) + { + signed char opt; +- lvmetad_state ls; ++ lvmetad_state ls = { .log_config = "" }; + daemon_state s = { + .daemon_fini = fini, + .daemon_init = init, + .handler = handler, + .name = "lvmetad", +- .pidfile = getenv("LVM_LVMETAD_PIDFILE"), ++ .pidfile = getenv("LVM_LVMETAD_PIDFILE") ? : LVMETAD_PIDFILE, + .private = &ls, + .protocol = "lvmetad", + .protocol_version = 1, +- .socket_path = getenv("LVM_LVMETAD_SOCKET"), ++ .socket_path = getenv("LVM_LVMETAD_SOCKET") ? : LVMETAD_SOCKET, + }; + +- if (!s.pidfile) +- s.pidfile = LVMETAD_PIDFILE; +- +- if (!s.socket_path) +- s.socket_path = LVMETAD_SOCKET; +- +- ls.log_config = ""; +- + // use getopt_long + while ((opt = getopt(argc, argv, "?fhVl:p:s:")) != EOF) { + switch (opt) { +@@ -1253,5 +1246,6 @@ int main(int argc, char *argv[]) + } + + daemon_start(s); ++ + return 0; + } +diff --git a/lib/Makefile.in b/lib/Makefile.in +index 8fdc194..968ad00 100644 +--- a/lib/Makefile.in ++++ b/lib/Makefile.in +@@ -1,6 +1,6 @@ + # + # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +-# Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. ++# Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + # + # This file is part of LVM2. + # +@@ -44,6 +44,10 @@ ifeq ("@THIN@", "shared") + SUBDIRS += thin + endif + ++ifeq ("@CACHE@", "shared") ++ SUBDIRS += cache_segtype ++endif ++ + SOURCES =\ + activate/activate.c \ + cache/lvmcache.c \ +@@ -83,11 +87,13 @@ SOURCES =\ + locking/locking.c \ + locking/no_locking.c \ + log/log.c \ ++ metadata/cache_manip.c \ + metadata/lv.c \ + metadata/lv_manip.c \ + metadata/merge.c \ + metadata/metadata.c \ + metadata/mirror.c \ ++ metadata/pool_manip.c \ + metadata/pv.c \ + metadata/pv_manip.c \ + metadata/pv_map.c \ +@@ -164,6 +170,10 @@ ifeq ("@THIN@", "internal") + SOURCES += thin/thin.c + endif + ++ifeq ("@CACHE@", "internal") ++ SOURCES += cache_segtype/cache.c ++endif ++ + ifeq ("@DEVMAPPER@", "yes") + SOURCES +=\ + activate/dev_manager.c \ +@@ -198,6 +208,7 @@ ifeq ($(MAKECMDGOALS),distclean) + raid \ + replicator \ + thin \ ++ cache_segtype \ + locking + endif + +diff --git a/lib/activate/activate.c b/lib/activate/activate.c +index 4c8c16d..26dc0e1 100644 +--- a/lib/activate/activate.c ++++ b/lib/activate/activate.c +@@ -285,6 +285,18 @@ int lv_raid_message(const struct logical_volume *lv, const char *msg) + { + return 0; + } ++int lv_cache_block_info(const struct logical_volume *lv, ++ uint32_t *chunk_size, uint64_t *dirty_count, ++ uint64_t *used_count, uint64_t *total_count) ++{ ++ return 0; ++} ++int lv_cache_policy_info(const struct logical_volume *lv, ++ char **policy_name, int *policy_argc, ++ char ***policy_argv) ++{ ++ return 0; ++} + int lv_thin_pool_percent(const struct logical_volume *lv, int metadata, + percent_t *percent) + { +@@ -969,6 +981,162 @@ out: + return r; + } + ++int lv_cache_block_info(struct logical_volume *lv, ++ uint32_t *chunk_size, uint64_t *dirty_count, ++ uint64_t *used_count, uint64_t *total_count) ++{ ++ struct lv_segment *cache_seg; ++ struct logical_volume *cache_lv; ++ struct dev_manager *dm; ++ struct dm_status_cache *status; ++ ++ /* The user is free to choose which args they are interested in */ ++ if (chunk_size) ++ *chunk_size = 0; ++ if (dirty_count) ++ *dirty_count = 0; ++ if (used_count) ++ *used_count = 0; ++ if (total_count) ++ *total_count = 0; ++ ++ if (lv_is_cache(lv)) ++ cache_lv = lv; ++ else if (lv_is_cache_pool(lv)) { ++ if (dm_list_empty(&lv->segs_using_this_lv)) { ++ //FIXME: Ok to return value not sourced from kernel? ++ // This could be valuable - esp for 'lvs' output ++ log_error(INTERNAL_ERROR "Unable to get block info" ++ " of unlinked cache_pool, %s", lv->name); ++ //FIXME: ... because we could do this: ++ if (chunk_size) ++ *chunk_size = first_seg(lv)->chunk_size; ++ /* Unlinked cache_pools have 0 dirty & used blocks */ ++ if (total_count) { ++ *total_count = lv->size; /* in sectors */ ++ *total_count /= first_seg(lv)->chunk_size; ++ } ++ ++ return 1; ++ } ++ if (!(cache_seg = get_only_segment_using_this_lv(lv))) ++ return_0; ++ cache_lv = cache_seg->lv; ++ } else { ++ log_error(INTERNAL_ERROR ++ "Unable to get block info of non-cache LV, %s", ++ lv->name); ++ return 0; ++ } ++ ++ if (!lv_info(cache_lv->vg->cmd, cache_lv, 0, NULL, 0, 0)) ++ return_0; ++ ++ log_debug_activation("Checking cache block info for LV %s/%s", ++ cache_lv->vg->name, cache_lv->name); ++ ++ if (!(dm = dev_manager_create(cache_lv->vg->cmd, cache_lv->vg->name, 1))) ++ return_0; ++ ++ if (!dev_manager_cache_status(dm, cache_lv, &status)) { ++ dev_manager_destroy(dm); ++ return_0; ++ } ++ ++ if (chunk_size) ++ *chunk_size = status->block_size; ++ if (dirty_count) ++ *dirty_count = status->dirty_blocks; ++ if (used_count) ++ *used_count = status->used_blocks; ++ if (total_count) ++ *total_count = status->total_blocks; ++ ++ dev_manager_destroy(dm); ++ ++ return 1; ++} ++ ++int lv_cache_policy_info(struct logical_volume *lv, ++ char **policy_name, int *policy_argc, ++ char ***policy_argv) ++{ ++ int i; ++ struct lv_segment *cache_seg; ++ struct logical_volume *cache_lv; ++ struct dev_manager *dm; ++ struct dm_status_cache *status; ++ struct dm_pool *mem = lv->vg->cmd->mem; ++ ++ /* The user is free to choose which args they are interested in */ ++ if (policy_name) ++ *policy_name = NULL; ++ if (policy_argc) ++ *policy_argc = 0; ++ if (policy_argv) ++ *policy_argv = NULL; ++ ++ if (lv_is_cache(lv)) ++ cache_lv = lv; ++ else if (lv_is_cache_pool(lv)) { ++ if (dm_list_empty(&lv->segs_using_this_lv)) { ++ //FIXME: Ok to return value not sourced from kernel? ++ log_error(INTERNAL_ERROR "Unable to get policy info" ++ " of unlinked cache_pool, %s", lv->name); ++ //FIXME: ... because we could do this: ++ if (policy_name) ++ *policy_name = first_seg(lv)->policy_name; ++ if (policy_argc) ++ *policy_argc = first_seg(lv)->policy_argc; ++ if (policy_argv) ++ *policy_argv = first_seg(lv)->policy_argv; ++ ++ return 1; ++ } ++ if (!(cache_seg = get_only_segment_using_this_lv(lv))) ++ return_0; ++ cache_lv = cache_seg->lv; ++ } else { ++ log_error(INTERNAL_ERROR ++ "Unable to get policy info of non-cache LV, %s", ++ lv->name); ++ return 0; ++ } ++ ++ if (!lv_info(cache_lv->vg->cmd, cache_lv, 0, NULL, 0, 0)) ++ return_0; ++ ++ log_debug_activation("Checking cache policy for LV %s/%s", ++ cache_lv->vg->name, cache_lv->name); ++ ++ if (!(dm = dev_manager_create(cache_lv->vg->cmd, cache_lv->vg->name, 1))) ++ return_0; ++ ++ if (!dev_manager_cache_status(dm, cache_lv, &status)) { ++ dev_manager_destroy(dm); ++ return_0; ++ } ++ ++ if (policy_name && ++ !(*policy_name = dm_pool_strdup(mem, status->policy_name))) ++ return_0; ++ if (policy_argc) ++ *policy_argc = status->policy_argc; ++ if (policy_argv) { ++ if (!(*policy_argv = ++ dm_pool_zalloc(mem, sizeof(char *) * *policy_argc))) ++ return_0; ++ for (i = 0; i < *policy_argc; i++) ++ if (!((*policy_argv)[i] = ++ dm_pool_strdup(mem, status->policy_argv[i]))) ++ return_0; ++ } ++ ++ dev_manager_destroy(dm); ++ ++ return 1; ++} ++ + /* + * Returns data or metadata percent usage, depends on metadata 0/1. + * Returns 1 if percent set, else 0 on failure. +diff --git a/lib/activate/activate.h b/lib/activate/activate.h +index 1881f75..ad14a57 100644 +--- a/lib/activate/activate.h ++++ b/lib/activate/activate.h +@@ -55,6 +55,11 @@ struct lv_activate_opts { + /* target attribute flags */ + #define MIRROR_LOG_CLUSTERED 0x00000001U + ++/* snapshot target attribute flags */ ++enum { ++ SNAPSHOT_FEATURE_FIXED_LEAK = (1 << 0), /* version 1.12 */ ++}; ++ + /* thin target attribute flags */ + enum { + /* bitfields - new features from 1.1 version */ +@@ -64,6 +69,12 @@ enum { + THIN_FEATURE_BLOCK_SIZE = (1 << 3), + THIN_FEATURE_DISCARDS_NON_POWER_2 = (1 << 4), + THIN_FEATURE_METADATA_RESIZE = (1 << 5), ++ THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND = (1 << 6), ++}; ++ ++/* raid target attribute flags */ ++enum { ++ RAID_FEATURE_RAID10 = (1 << 0), /* version 1.3 */ + }; + + void set_activation(int activation); +@@ -134,6 +145,12 @@ int lv_raid_dev_health(const struct logical_volume *lv, char **dev_health); + int lv_raid_mismatch_count(const struct logical_volume *lv, uint64_t *cnt); + int lv_raid_sync_action(const struct logical_volume *lv, char **sync_action); + int lv_raid_message(const struct logical_volume *lv, const char *msg); ++int lv_cache_block_info(struct logical_volume *lv, ++ uint32_t *chunk_size, uint64_t *dirty_count, ++ uint64_t *used_count, uint64_t *total_count); ++int lv_cache_policy_info(struct logical_volume *lv, ++ char **policy_name, int *policy_argc, ++ char ***policy_argv); + int lv_thin_pool_percent(const struct logical_volume *lv, int metadata, + percent_t *percent); + int lv_thin_percent(const struct logical_volume *lv, int mapped, +diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c +index 7b0b6e2..6b5f8c2 100644 +--- a/lib/activate/dev_manager.c ++++ b/lib/activate/dev_manager.c +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * +@@ -63,7 +63,7 @@ struct lv_layer { + + int read_only_lv(struct logical_volume *lv, struct lv_activate_opts *laopts) + { +- return (laopts->read_only || !(lv->vg->status & LVM_WRITE) || !(lv->status & LVM_WRITE)); ++ return (laopts->read_only || !(lv->status & LVM_WRITE)); + } + + /* +@@ -1160,6 +1160,55 @@ out: + return r; + } + ++int dev_manager_cache_status(struct dev_manager *dm, ++ const struct logical_volume *lv, ++ struct dm_status_cache **status) ++{ ++ int r = 0; ++ const char *dlid; ++ struct dm_task *dmt; ++ struct dm_info info; ++ uint64_t start, length; ++ char *type = NULL; ++ char *params = NULL; ++ const char *layer = lv_layer(lv); ++ ++ if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ return_0; ++ ++ log_debug_activation("Getting cache device status for %s.", lv->name); ++ ++ if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0))) ++ return_0; ++ ++ if (!dm_task_no_open_count(dmt)) ++ log_error("Failed to disable open_count."); ++ ++ if (!dm_task_run(dmt)) ++ goto_out; ++ ++ if (!dm_task_get_info(dmt, &info) || !info.exists) ++ goto_out; ++ ++ dm_get_next_target(dmt, NULL, &start, &length, &type, ¶ms); ++ ++ if (!type || strcmp(type, "cache")) { ++ log_debug("Expected cache segment type but got %s instead", ++ type ? type : "NULL"); ++ goto out; ++ } ++ ++ if (!dm_get_status_cache(dm->mem, params, status)) ++ goto_out; ++ ++ r = 1; ++out: ++ dm_task_destroy(dmt); ++ ++ return r; ++} ++ ++//FIXME: Can we get rid of this crap below? + #if 0 + log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", name); + +@@ -1863,6 +1912,10 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + !_add_lv_to_dtree(dm, dtree, seg->pool_lv, 1)) /* stack */ + return_0; + ++ if (seg->pool_lv && lv_is_cache_pool(seg->pool_lv) && ++ !_add_lv_to_dtree(dm, dtree, seg->pool_lv, 0)) ++ return_0; ++ + for (s = 0; s < seg->area_count; s++) { + if (seg_type(seg, s) == AREA_LV && seg_lv(seg, s) && + !_add_lv_to_dtree(dm, dtree, seg_lv(seg, s), 0)) +@@ -2327,15 +2380,18 @@ static int _add_segment_to_dtree(struct dev_manager *dm, + if (seg->external_lv && + !_add_new_external_lv_to_dtree(dm, dtree, seg->external_lv, laopts)) + return_0; ++ + /* Add mirror log */ + if (seg->log_lv && + !_add_new_lv_to_dtree(dm, dtree, seg->log_lv, laopts, NULL)) + return_0; +- /* Add thin pool metadata */ ++ ++ /* Add pool metadata */ + if (seg->metadata_lv && + !_add_new_lv_to_dtree(dm, dtree, seg->metadata_lv, laopts, NULL)) + return_0; +- /* Add thin pool layer */ ++ ++ /* Add pool layer */ + if (seg->pool_lv && + !_add_new_lv_to_dtree(dm, dtree, seg->pool_lv, laopts, + lv_layer(seg->pool_lv))) +@@ -2450,7 +2506,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + dinfo->open_count)) { + if (seg_is_thin_volume(seg) || + /* FIXME Is there anything simpler to check for instead? */ +- !lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge")) ++ !lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge")) + laopts->no_merging = 1; + } + } +@@ -2506,7 +2562,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + return_0; + if (!laopts->no_merging && lv_is_merging_origin(lv)) { + if (!_add_new_lv_to_dtree(dm, dtree, +- find_snapshot(lv)->cow, laopts, "cow")) ++ find_snapshot(lv)->cow, laopts, "cow")) + return_0; + /* + * Must also add "real" LV for use when +diff --git a/lib/activate/dev_manager.h b/lib/activate/dev_manager.h +index 032766e..446b349 100644 +--- a/lib/activate/dev_manager.h ++++ b/lib/activate/dev_manager.h +@@ -60,6 +60,9 @@ int dev_manager_raid_status(struct dev_manager *dm, + int dev_manager_raid_message(struct dev_manager *dm, + const struct logical_volume *lv, + const char *msg); ++int dev_manager_cache_status(struct dev_manager *dm, ++ const struct logical_volume *lv, ++ struct dm_status_cache **status); + int dev_manager_thin_pool_status(struct dev_manager *dm, + const struct logical_volume *lv, + struct dm_status_thin_pool **status, +diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c +index ec8699b..d40bdce 100644 +--- a/lib/cache/lvmcache.c ++++ b/lib/cache/lvmcache.c +@@ -264,9 +264,9 @@ static void _drop_metadata(const char *vgname, int drop_precommitted) + } + + /* +- * Remote node uses this to upgrade precommited metadata to commited state ++ * Remote node uses this to upgrade precommitted metadata to commited state + * when receives vg_commit notification. +- * (Note that devices can be suspended here, if so, precommited metadata are already read.) ++ * (Note that devices can be suspended here, if so, precommitted metadata are already read.) + */ + void lvmcache_commit_metadata(const char *vgname) + { +diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c +index aec6a71..c994874 100644 +--- a/lib/cache/lvmetad.c ++++ b/lib/cache/lvmetad.c +@@ -40,7 +40,7 @@ void lvmetad_disconnect(void) + + void lvmetad_init(struct cmd_context *cmd) + { +- if (!_lvmetad_use && !access(LVMETAD_PIDFILE, F_OK)) ++ if (!_lvmetad_use && !access(getenv("LVM_LVMETAD_PIDFILE") ? : LVMETAD_PIDFILE, F_OK)) + log_warn("WARNING: lvmetad is running but disabled." + " Restart lvmetad before enabling it!"); + _lvmetad_cmd = cmd; +@@ -159,12 +159,27 @@ retry: + daemon_request_destroy(req); + + if (!repl.error && !strcmp(daemon_reply_str(repl, "response", ""), "token_mismatch") && +- try < 2 && !test_mode()) { +- if (lvmetad_pvscan_all_devs(_lvmetad_cmd, NULL)) { +- ++ try; +- daemon_reply_destroy(repl); +- goto retry; +- } ++ try < 60 && !test_mode()) { ++ /* ++ * If another process is trying to scan, they might have the ++ * same future token id and it's better to wait and avoid doing ++ * the work multiple times. For the case the future token is ++ * different, the wait is randomized so that multiple waiting ++ * processes do not start scanning all at once. ++ * ++ * If the token is mismatched because of global_filter changes, ++ * we re-scan immediately, but if we lose the potential race for ++ * the update, we back off for a short while (0.2-2 seconds) and ++ * try again. ++ */ ++ if (!strcmp(daemon_reply_str(repl, "expected", ""), "update in progress") || try % 5) ++ usleep( 50000 + random() % 450000 ); /* 0.05 - 0.5s */ ++ else ++ /* If the re-scan fails here, we try again later. */ ++ lvmetad_pvscan_all_devs(_lvmetad_cmd, NULL); ++ ++ try; ++ daemon_reply_destroy(repl); ++ goto retry; + } + + return repl; +diff --git a/lib/cache_segtype/.exported_symbols b/lib/cache_segtype/.exported_symbols +new file mode 100644 +index 0000000..95cb3ff +--- /dev/null ++++ b/lib/cache_segtype/.exported_symbols +@@ -0,0 +1 @@ ++init_cache_segtypes +diff --git a/lib/cache_segtype/Makefile.in b/lib/cache_segtype/Makefile.in +new file mode 100644 +index 0000000..32a1f2b +--- /dev/null ++++ b/lib/cache_segtype/Makefile.in +@@ -0,0 +1,24 @@ ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++srcdir = @srcdir@ ++top_srcdir = @top_srcdir@ ++top_builddir = @top_builddir@ ++ ++SOURCES = cache.c ++ ++LIB_SHARED = liblvm2cache.$(LIB_SUFFIX) ++LIB_VERSION = $(LIB_VERSION_LVM) ++ ++include $(top_builddir)/make.tmpl ++ ++install: install_lvm2_plugin +diff --git a/lib/cache_segtype/cache.c b/lib/cache_segtype/cache.c +new file mode 100644 +index 0000000..57c7a5c +--- /dev/null ++++ b/lib/cache_segtype/cache.c +@@ -0,0 +1,448 @@ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "lib.h" ++#include "toolcontext.h" ++#include "segtype.h" ++#include "display.h" ++#include "text_export.h" ++#include "config.h" ++#include "str_list.h" ++#include "targets.h" ++#include "lvm-string.h" ++#include "activate.h" ++#include "metadata.h" ++#include "lv_alloc.h" ++#include "defaults.h" ++ ++#define SEG_LOG_ERROR(t, p...) \ ++ log_error(t " segment %s of logical volume %s.", ## p, \ ++ dm_config_parent_name(sn), seg->lv->name), 0; ++ ++ ++static const char *_name(const struct lv_segment *seg) ++{ ++ return seg->segtype->name; ++} ++ ++static int _cache_pool_text_import(struct lv_segment *seg, ++ const struct dm_config_node *sn, ++ struct dm_hash_table *pv_hash __attribute__((unused))) ++{ ++ uint32_t chunk_size; ++ struct logical_volume *data_lv, *meta_lv; ++ const char *str = NULL; ++ char *argv_str; ++ struct dm_pool *mem = seg->lv->vg->vgmem; //FIXME: what mempool should be used? ++ ++ if (!dm_config_has_node(sn, "data")) ++ return SEG_LOG_ERROR("Cache data not specified in"); ++ if (!(str = dm_config_find_str(sn, "data", NULL))) ++ return SEG_LOG_ERROR("Cache data must be a string in"); ++ if (!(data_lv = find_lv(seg->lv->vg, str))) ++ return SEG_LOG_ERROR("Unknown logical volume %s specified for " ++ "cache data in", str); ++ ++ if (!dm_config_has_node(sn, "metadata")) ++ return SEG_LOG_ERROR("Cache metadata not specified in"); ++ if (!(str = dm_config_find_str(sn, "metadata", NULL))) ++ return SEG_LOG_ERROR("Cache metadata must be a string in"); ++ if (!(meta_lv = find_lv(seg->lv->vg, str))) ++ return SEG_LOG_ERROR("Unknown logical volume %s specified for " ++ "cache metadata in", str); ++ ++ if (!dm_config_get_uint32(sn, "chunk_size", &chunk_size)) ++ return SEG_LOG_ERROR("Couldn't read cache chunk_size in"); ++ ++ /* ++ * Read in features: ++ * cache_mode = {writethrough|writeback} ++ * ++ * 'cache_mode' does not have to be present. ++ */ ++ if (dm_config_has_node(sn, "cache_mode")) { ++ if (!(str = dm_config_find_str(sn, "cache_mode", NULL))) ++ return SEG_LOG_ERROR("cache_mode must be a string in"); ++ if (!strcmp(str, "writethrough")) ++ seg->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; ++ else if (!strcmp(str, "writeback")) ++ seg->feature_flags |= DM_CACHE_FEATURE_WRITEBACK; ++ else ++ return SEG_LOG_ERROR("Unknown cache_mode in"); ++ } ++ ++ /* ++ * Read in core arguments (these are key/value pairs) ++ * core_argc = <# args> ++ * core_argv = "[ ]..." ++ * ++ * 'core_argc' does not have to be present. If it is not present, ++ * any other core_* fields are ignored. If it is present, then ++ * 'core_argv' must be present - even if they are ++ * 'core_argc = 0' and 'core_argv = ""'. ++ */ ++ if (dm_config_has_node(sn, "core_argc")) { ++ if (!dm_config_has_node(sn, "core_argv")) ++ return SEG_LOG_ERROR("not all core arguments defined in"); ++ ++ if (!dm_config_get_uint32(sn, "core_argc", &seg->core_argc)) ++ return SEG_LOG_ERROR("Unable to read core_argc in"); ++ ++ str = dm_config_find_str(sn, "core_argv", NULL); ++ if ((str && !seg->core_argc) || (!str && seg->core_argc)) ++ return SEG_LOG_ERROR("core_argc and core_argv do" ++ " not match in"); ++ ++ if (!(seg->core_argv = ++ dm_pool_alloc(mem, sizeof(char *) * seg->core_argc))) ++ return_0; ++ if (str && ++ (!(argv_str = dm_pool_strdup(mem, str)) || ++ ((int)seg->core_argc != dm_split_words(argv_str, seg->core_argc, ++ 0, seg->core_argv)))) ++ return SEG_LOG_ERROR("core_argc and core_argv do" ++ " not match in"); ++ } ++ ++ /* ++ * Read in policy: ++ * policy_name = "" ++ * policy_argc = <# args> ++ * policy_argv = "[ ]..." ++ * ++ * 'policy_name' does not have to be present. If it is not present, ++ * any other policy_* fields are ignored. If it is present, then ++ * the other policy_* fields must be present - even if they are ++ * 'policy_argc = 0' and 'policy_argv = ""'. ++ */ ++ if (dm_config_has_node(sn, "policy_name")) { ++ if (!dm_config_has_node(sn, "policy_argc") || ++ !dm_config_has_node(sn, "policy_argv")) ++ return SEG_LOG_ERROR("not all policy arguments defined in"); ++ if (!(str = dm_config_find_str(sn, "policy_name", NULL))) ++ return SEG_LOG_ERROR("policy_name must be a string in"); ++ seg->policy_name = dm_pool_strdup(mem, str); ++ ++ if (!dm_config_get_uint32(sn, "policy_argc", &seg->policy_argc)) ++ return SEG_LOG_ERROR("Unable to read policy_argc in"); ++ ++ str = dm_config_find_str(sn, "policy_argv", NULL); ++ if ((str && !seg->policy_argc) || (!str && seg->policy_argc)) ++ return SEG_LOG_ERROR("policy_argc and policy_argv do" ++ " not match in"); ++ ++ if (!(seg->policy_argv = ++ dm_pool_alloc(mem, sizeof(char *) * seg->policy_argc))) ++ return_0; ++ if (str && ++ (!(argv_str = dm_pool_strdup(mem, str)) || ++ ((int)seg->policy_argc != dm_split_words(argv_str, ++ seg->policy_argc, ++ 0, seg->policy_argv)))) ++ return SEG_LOG_ERROR("policy_argc and policy_argv do" ++ " not match in"); ++ } ++ ++ if (!attach_pool_data_lv(seg, data_lv)) ++ return_0; ++ if (!attach_pool_metadata_lv(seg, meta_lv)) ++ return_0; ++ seg->chunk_size = chunk_size; ++ ++ return 1; ++} ++ ++static int _cache_pool_text_import_area_count(const struct dm_config_node *sn, ++ uint32_t *area_count) ++{ ++ *area_count = 1; ++ ++ return 1; ++} ++ ++static int _cache_pool_text_export(const struct lv_segment *seg, ++ struct formatter *f) ++{ ++ unsigned i; ++ char buf[256]; //FIXME: IS THERE AN 'outf' THAT DOESN'T DO NEWLINE?!? ++ uint32_t feature_flags = seg->feature_flags; ++ ++ outf(f, "data = \"%s\"", seg_lv(seg, 0)->name); ++ outf(f, "metadata = \"%s\"", seg->metadata_lv->name); ++ outf(f, "chunk_size = %" PRIu32, seg->chunk_size); ++ ++ if (feature_flags) { ++ if (feature_flags & DM_CACHE_FEATURE_WRITETHROUGH) { ++ outf(f, "cache_mode = \"writethrough\""); ++ feature_flags &= ~DM_CACHE_FEATURE_WRITETHROUGH; ++ } else if (feature_flags & DM_CACHE_FEATURE_WRITEBACK) { ++ outf(f, "cache_mode = \"writeback\""); ++ feature_flags &= ~DM_CACHE_FEATURE_WRITEBACK; ++ } else { ++ log_error(INTERNAL_ERROR "Unknown feature flags " ++ "in cache_pool segment for %s", seg->lv->name); ++ return 0; ++ } ++ } ++ ++ if (seg->core_argc) { ++ outf(f, "core_argc = %u", seg->core_argc); ++ outf(f, "core_argv = \""); ++ for (i = 0; i < seg->core_argc; i++) ++ outf(f, "%s%s", i ? " " : "", seg->core_argv[i]); ++ outf(f, "\""); ++ } ++ ++ if (seg->policy_name) { ++ outf(f, "policy_name = \"%s\"", seg->policy_name); ++ outf(f, "policy_argc = %u", seg->policy_argc); ++ buf[0] = '\0'; ++ for (i = 0; i < seg->policy_argc; i++) ++ sprintf(buf, "%s%s", i ? " " : "", seg->policy_argv[i]); ++ outf(f, "policy_argv = \"%s\"", buf); ++ } ++ ++ return 1; ++} ++ ++static int _cache_pool_add_target_line(struct dev_manager *dm, ++ struct dm_pool *mem, ++ 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))) ++{ ++ /* ++ * This /could/ be directed at _cdata, but I prefer ++ * not to give a user direct access to a sub-LV via ++ * this cache_pool. ++ */ ++ return dm_tree_node_add_error_target(node, len); ++} ++ ++static int _modules_needed(struct dm_pool *mem, ++ const struct lv_segment *seg __attribute__((unused)), ++ struct dm_list *modules) ++{ ++ if (!str_list_add(mem, modules, "cache")) { ++ log_error("cache module string list allocation failed"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void _destroy(struct segment_type *segtype) ++{ ++ dm_free((void *) segtype); ++} ++ ++#ifdef DEVMAPPER_SUPPORT ++static int _target_present(struct cmd_context *cmd, ++ const struct lv_segment *seg __attribute__((unused)), ++ unsigned *attributes __attribute__((unused))) ++{ ++ uint32_t maj, min, patchlevel; ++ static int _cache_checked = 0; ++ static int _cache_present = 0; ++ ++ if (!_cache_checked) { ++ _cache_present = target_present(cmd, "cache", 1); ++ ++ if (!target_version("cache", &maj, &min, &patchlevel)) { ++ log_error("Failed to determine version of cache kernel module"); ++ return 0; ++ } ++ ++ _cache_checked = 1; ++ ++ if ((maj < 1) || ++ ((maj == 1) && (min < 3))) { ++ log_error("The cache kernel module is version %u.%u.%u." ++ " Version 1.3.0+ is required.", ++ maj, min, patchlevel); ++ return 0; ++ } ++ } ++ ++ return _cache_present; ++} ++ ++#endif /* DEVMAPPER_SUPPORT */ ++ ++static struct segtype_handler _cache_pool_ops = { ++ .name = _name, ++ .text_import = _cache_pool_text_import, ++ .text_import_area_count = _cache_pool_text_import_area_count, ++ .text_export = _cache_pool_text_export, ++ .add_target_line = _cache_pool_add_target_line, ++#ifdef DEVMAPPER_SUPPORT ++ .target_present = _target_present, ++# ifdef DMEVENTD ++# endif /* DMEVENTD */ ++#endif ++ .modules_needed = _modules_needed, ++ .destroy = _destroy, ++}; ++ ++static int _cache_text_import(struct lv_segment *seg, ++ const struct dm_config_node *sn, ++ struct dm_hash_table *pv_hash __attribute__((unused))) ++{ ++ struct logical_volume *pool_lv, *origin_lv; ++ const char *name = NULL; ++ ++ if (!dm_config_has_node(sn, "cache_pool")) ++ return SEG_LOG_ERROR("cache_pool not specified in"); ++ if (!(name = dm_config_find_str(sn, "cache_pool", NULL))) ++ return SEG_LOG_ERROR("cache_pool must be a string in"); ++ if (!(pool_lv = find_lv(seg->lv->vg, name))) ++ return SEG_LOG_ERROR("Unknown logical volume %s specified for " ++ "cache_pool in", name); ++ ++ if (!dm_config_has_node(sn, "origin")) ++ return SEG_LOG_ERROR("Cache origin not specified in"); ++ if (!(name = dm_config_find_str(sn, "origin", NULL))) ++ return SEG_LOG_ERROR("Cache origin must be a string in"); ++ if (!(origin_lv = find_lv(seg->lv->vg, name))) ++ return SEG_LOG_ERROR("Unknown logical volume %s specified for " ++ "cache origin in", name); ++ ++ if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0)) ++ return_0; ++ if (!attach_pool_lv(seg, pool_lv, NULL, NULL)) ++ return_0; ++ ++ return 1; ++} ++ ++static int _cache_text_import_area_count(const struct dm_config_node *sn, ++ uint32_t *area_count) ++{ ++ *area_count = 1; ++ ++ return 1; ++} ++ ++static int _cache_text_export(const struct lv_segment *seg, struct formatter *f) ++{ ++ if (!seg_lv(seg, 0)) ++ return_0; ++ ++ outf(f, "cache_pool = \"%s\"", seg->pool_lv->name); ++ outf(f, "origin = \"%s\"", seg_lv(seg, 0)->name); ++ ++ return 1; ++} ++ ++static int _cache_add_target_line(struct dev_manager *dm, ++ struct dm_pool *mem, ++ 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))) ++{ ++ struct lv_segment *cache_pool_seg = first_seg(seg->pool_lv); ++ char *metadata_uuid, *data_uuid, *origin_uuid; ++ ++ if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv->lvid.s, NULL))) ++ return_0; ++ ++ if (!(data_uuid = build_dm_uuid(mem, seg_lv(cache_pool_seg, 0)->lvid.s, NULL))) ++ return_0; ++ ++ if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0)->lvid.s, NULL))) ++ return_0; ++ ++ if (!dm_tree_node_add_cache_target(node, len, ++ metadata_uuid, ++ data_uuid, ++ origin_uuid, ++ cache_pool_seg->chunk_size, ++ cache_pool_seg->feature_flags, ++ cache_pool_seg->core_argc, ++ cache_pool_seg->core_argv, ++ cache_pool_seg->policy_name, ++ cache_pool_seg->policy_argc, ++ cache_pool_seg->policy_argv)) ++ return_0; ++ ++ return add_areas_line(dm, seg, node, 0u, seg->area_count); ++} ++ ++static struct segtype_handler _cache_ops = { ++ .name = _name, ++ .text_import = _cache_text_import, ++ .text_import_area_count = _cache_text_import_area_count, ++ .text_export = _cache_text_export, ++ .add_target_line = _cache_add_target_line, ++#ifdef DEVMAPPER_SUPPORT ++ .target_present = _target_present, ++# ifdef DMEVENTD ++# endif /* DMEVENTD */ ++#endif ++ .modules_needed = _modules_needed, ++ .destroy = _destroy, ++}; ++ ++#ifdef CACHE_INTERNAL /* Shared */ ++int init_cache_segtypes(struct cmd_context *cmd, ++ struct segtype_library *seglib) ++#else ++int init_cache_segtypes(struct cmd_context *cmd, ++ struct segtype_library *seglib); ++int init_cache_segtypes(struct cmd_context *cmd, ++ struct segtype_library *seglib) ++#endif ++{ ++ struct segment_type *segtype = dm_zalloc(sizeof(*segtype)); ++ ++ if (!segtype) { ++ log_error("Failed to allocate memory for cache_pool segtype"); ++ return 0; ++ } ++ segtype->cmd = cmd; ++ ++ segtype->name = "cache-pool"; ++ segtype->flags = SEG_CACHE_POOL; ++ segtype->ops = &_cache_pool_ops; ++ segtype->private = NULL; ++ ++ if (!lvm_register_segtype(seglib, segtype)) ++ return_0; ++ log_very_verbose("Initialised segtype: %s", segtype->name); ++ ++ segtype = dm_zalloc(sizeof(*segtype)); ++ if (!segtype) { ++ log_error("Failed to allocate memory for cache segtype"); ++ return 0; ++ } ++ segtype->cmd = cmd; ++ ++ segtype->name = "cache"; ++ segtype->flags = SEG_CACHE; ++ segtype->ops = &_cache_ops; ++ segtype->private = NULL; ++ ++ if (!lvm_register_segtype(seglib, segtype)) ++ return_0; ++ log_very_verbose("Initialised segtype: %s", segtype->name); ++ ++ return 1; ++} ++ +diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c +index 9d6ef5e..a709284 100644 +--- a/lib/commands/toolcontext.c ++++ b/lib/commands/toolcontext.c +@@ -1,6 +1,6 @@ +- /* ++/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * +@@ -1181,6 +1181,11 @@ static int _init_segtypes(struct cmd_context *cmd) + return 0; + #endif + ++#ifdef CACHE_INTERNAL ++ if (!init_cache_segtypes(cmd, &seglib)) ++ return 0; ++#endif ++ + #ifdef HAVE_LIBDL + /* Load any formats in shared libs unless static */ + if (!is_static() && +diff --git a/lib/config/config.c b/lib/config/config.c +index 6e93c40..e51c26f 100644 +--- a/lib/config/config.c ++++ b/lib/config/config.c +@@ -477,20 +477,24 @@ time_t config_file_timestamp(struct dm_config_tree *cft) + + #define cfg_def_get_item_p(id) (&_cfg_def_items[id]) + #define cfg_def_get_default_value(item,type) item->default_value.v_##type +-#define cfg_def_get_path(item) (_cfg_def_make_path(_cfg_path,CFG_PATH_MAX_LEN,item->id,item),_cfg_path) ++#define cfg_def_get_path(item) (_cfg_def_make_path(_cfg_path,CFG_PATH_MAX_LEN,item->id,item, 0),_cfg_path) ++#define cfg_def_get_path_xlated(item) (_cfg_def_make_path(_cfg_path,CFG_PATH_MAX_LEN,item->id,item, 1),_cfg_path) + +-static int _cfg_def_make_path(char *buf, size_t buf_size, int id, cfg_def_item_t *item) ++static int _cfg_def_make_path(char *buf, size_t buf_size, int id, cfg_def_item_t *item, int xlate) + { ++ int variable = item->flags & CFG_NAME_VARIABLE; + int parent_id = item->parent; + int count, n; + + if (id == parent_id) + return 0; + +- count = _cfg_def_make_path(buf, buf_size, parent_id, cfg_def_get_item_p(parent_id)); +- if ((n = dm_snprintf(buf + count, buf_size - count, "%s%s", ++ count = _cfg_def_make_path(buf, buf_size, parent_id, cfg_def_get_item_p(parent_id), xlate); ++ if ((n = dm_snprintf(buf + count, buf_size - count, "%s%s%s%s", + count ? "/" : "", +- item->flags & CFG_NAME_VARIABLE ? "#" : item->name)) < 0) { ++ xlate && variable ? "<" : "", ++ !xlate && variable ? "#" : item->name, ++ xlate && variable ? ">" : "")) < 0) { + log_error(INTERNAL_ERROR "_cfg_def_make_path: supplied buffer too small for %s/%s", + cfg_def_get_item_p(parent_id)->name, item->name); + buf[0] = '\0'; +@@ -502,7 +506,7 @@ static int _cfg_def_make_path(char *buf, size_t buf_size, int id, cfg_def_item_t + + int config_def_get_path(char *buf, size_t buf_size, int id) + { +- return _cfg_def_make_path(buf, buf_size, id, cfg_def_get_item_p(id)); ++ return _cfg_def_make_path(buf, buf_size, id, cfg_def_get_item_p(id), 0); + } + + static void _get_type_name(char *buf, size_t buf_size, cfg_def_type_t type) +@@ -1086,8 +1090,7 @@ int merge_config_tree(struct cmd_context *cmd, struct dm_config_tree *cft, + + struct out_baton { + FILE *fp; +- int withcomment; +- int withversion; ++ struct config_def_tree_spec *tree_spec; + }; + + static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, void *baton) +@@ -1108,8 +1111,8 @@ static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, voi + + cfg_def = cfg_def_get_item_p(cn->id); + +- if (out->withcomment) { +- path = cfg_def_get_path(cfg_def); ++ if (out->tree_spec->withcomments) { ++ path = cfg_def_get_path_xlated(cfg_def); + fprintf(out->fp, "%s# Configuration %s %s.\n", line, node_type_name, path); + + if (cfg_def->comment) +@@ -1120,9 +1123,15 @@ static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, voi + + if (cfg_def->flags & CFG_UNSUPPORTED) + fprintf(out->fp, "%s# This configuration %s is not officially supported.\n", line, node_type_name); ++ ++ if (cfg_def->flags & CFG_NAME_VARIABLE) ++ fprintf(out->fp, "%s# This configuration %s has variable name.\n", line, node_type_name); ++ ++ if (cfg_def->flags & CFG_DEFAULT_UNDEFINED) ++ fprintf(out->fp, "%s# This configuration %s does not have a default value defined.\n", line, node_type_name); + } + +- if (out->withversion) { ++ if (out->tree_spec->withversions) { + if (dm_snprintf(version, 9, "%u.%u.%u", + (cfg_def->since_version & 0xE000) >> 13, + (cfg_def->since_version & 0x1E00) >> 9, +@@ -1139,7 +1148,10 @@ static int _out_prefix_fn(const struct dm_config_node *cn, const char *line, voi + static int _out_line_fn(const struct dm_config_node *cn, const char *line, void *baton) + { + struct out_baton *out = baton; +- fprintf(out->fp, "%s\n", line); ++ struct cfg_def_item *cfg_def = cfg_def_get_item_p(cn->id); ++ ++ fprintf(out->fp, "%s%s\n", (out->tree_spec->type != CFG_DEF_TREE_CURRENT) && ++ (cfg_def->flags & CFG_DEFAULT_UNDEFINED) ? "#" : "", line); + return 1; + } + +@@ -1149,7 +1161,7 @@ static int _out_suffix_fn(const struct dm_config_node *cn, const char *line, voi + } + + int config_write(struct dm_config_tree *cft, +- int withcomment, int withversion, ++ struct config_def_tree_spec *tree_spec, + const char *file, int argc, char **argv) + { + static const struct dm_config_node_out_spec _out_spec = { +@@ -1159,8 +1171,7 @@ int config_write(struct dm_config_tree *cft, + }; + const struct dm_config_node *cn; + struct out_baton baton = { +- .withcomment = withcomment, +- .withversion = withversion ++ .tree_spec = tree_spec + }; + int r = 1; + +diff --git a/lib/config/config.h b/lib/config/config.h +index 0769c40..f716efb 100644 +--- a/lib/config/config.h ++++ b/lib/config/config.h +@@ -82,6 +82,8 @@ typedef union { + #define CFG_UNSUPPORTED 0x08 + /* whether the configuration item is customizable by a profile */ + #define CFG_PROFILABLE 0x10 ++/* whether the default value is undefned */ ++#define CFG_DEFAULT_UNDEFINED 0x20 + + /* configuration definition item structure */ + typedef struct cfg_def_item { +@@ -109,8 +111,10 @@ typedef enum { + struct config_def_tree_spec { + cfg_def_tree_t type; /* tree type */ + uint16_t version; /* tree at this LVM2 version */ +- int ignoreadvanced; /* do not include advanced configs */ +- int ignoreunsupported; /* do not include unsupported configs */ ++ int ignoreadvanced:1; /* do not include advanced configs */ ++ int ignoreunsupported:1; /* do not include unsupported configs */ ++ int withcomments:1; /* include comments */ ++ int withversions:1; /* include versions */ + uint8_t *check_status; /* status of last tree check (currently needed for CFG_DEF_TREE_MISSING only) */ + }; + +@@ -163,8 +167,7 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, + checksum_fn_t checksum_fn, uint32_t checksum); + int config_file_read(struct dm_config_tree *cft); + struct dm_config_tree *config_file_open_and_read(const char *config_file, config_source_t source); +-int config_write(struct dm_config_tree *cft, +- int withcomment, int withversion, ++int config_write(struct dm_config_tree *cft, struct config_def_tree_spec *tree_spec, + const char *file, int argc, char **argv); + struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec); + void config_destroy(struct dm_config_tree *cft); +diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h +index 06cff7e..2c0dff2 100644 +--- a/lib/config/config_settings.h ++++ b/lib/config/config_settings.h +@@ -32,6 +32,7 @@ + * CFG_ADVANCED - this node belongs to advanced config set + * CFG_UNSUPPORTED - this node belongs to unsupported config set + * CFG_PROFILABLE - this node is customizable by a profile ++ * CFG_DEFAULT_UNDEFINED - node's default value is undefined + * type: allowed type for the value of simple configuation setting, one of: + * CFG_TYPE_BOOL + * CFG_TYPE_INT +@@ -77,16 +78,16 @@ cfg(config_profile_dir_CFG, "profile_dir", config_CFG_SECTION, 0, CFG_TYPE_STRIN + + cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DEV_DIR, vsn(1, 0, 0), NULL) + cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, 0, CFG_TYPE_STRING, "#S/dev", vsn(1, 0, 0), NULL) +-cfg_array(devices_loopfiles_CFG, "loopfiles", devices_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 2, 0), NULL) ++cfg_array(devices_loopfiles_CFG, "loopfiles", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 2, 0), NULL) + cfg(devices_obtain_device_list_from_udev_CFG, "obtain_device_list_from_udev", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV, vsn(2, 2, 85), NULL) +-cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, NULL, vsn(1, 2, 19), NULL) +-cfg_array(devices_filter_CFG, "filter", devices_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) +-cfg_array(devices_global_filter_CFG, "global_filter", devices_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(2, 2, 98), NULL) ++cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 2, 19), NULL) ++cfg_array(devices_filter_CFG, "filter", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) ++cfg_array(devices_global_filter_CFG, "global_filter", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 98), NULL) + cfg(devices_cache_CFG, "cache", devices_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) + cfg(devices_cache_dir_CFG, "cache_dir", devices_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 2, 19), NULL) + cfg(devices_cache_file_prefix_CFG, "cache_file_prefix", devices_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 2, 19), NULL) + cfg(devices_write_cache_state_CFG, "write_cache_state", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(1, 0, 0), NULL) +-cfg_array(devices_types_CFG, "types", devices_CFG_SECTION, 0, CFG_TYPE_INT | CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) ++cfg_array(devices_types_CFG, "types", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT | CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) + cfg(devices_sysfs_scan_CFG, "sysfs_scan", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SYSFS_SCAN, vsn(1, 0, 8), NULL) + cfg(devices_multipath_component_detection_CFG, "multipath_component_detection", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_MULTIPATH_COMPONENT_DETECTION, vsn(2, 2, 89), NULL) + cfg(devices_md_component_detection_CFG, "md_component_detection", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_MD_COMPONENT_DETECTION, vsn(1, 0, 18), NULL) +@@ -102,11 +103,15 @@ cfg(devices_require_restorefile_with_uuid_CFG, "require_restorefile_with_uuid", + cfg(devices_pv_min_size_CFG, "pv_min_size", devices_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_PV_MIN_SIZE_KB, vsn(2, 2, 85), NULL) + cfg(devices_issue_discards_CFG, "issue_discards", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ISSUE_DISCARDS, vsn(2, 2, 85), NULL) + +-cfg_array(allocation_cling_tag_list_CFG, "cling_tag_list", allocation_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(2, 2, 77), NULL) ++cfg_array(allocation_cling_tag_list_CFG, "cling_tag_list", allocation_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 77), NULL) + cfg(allocation_maximise_cling_CFG, "maximise_cling", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_MAXIMISE_CLING, vsn(2, 2, 85), NULL) + cfg(allocation_use_blkid_wiping_CFG, "use_blkid_wiping", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 105), NULL) + cfg(allocation_wipe_signatures_when_zeroing_new_lvs_CFG, "wipe_signatures_when_zeroing_new_lvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 105), NULL) + cfg(allocation_mirror_logs_require_separate_pvs_CFG, "mirror_logs_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_MIRROR_LOGS_REQUIRE_SEPARATE_PVS, vsn(2, 2, 85), NULL) ++ ++cfg(allocation_cache_pool_metadata_require_separate_pvs_CFG, "cache_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 106), NULL) ++cfg(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", allocation_CFG_SECTION, 0, CFG_TYPE_INT, 0, vsn(2, 2, 106), NULL) ++ + cfg(allocation_thin_pool_metadata_require_separate_pvs_CFG, "thin_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 89), NULL) + cfg(allocation_thin_pool_zero_CFG, "thin_pool_zero", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_ZERO, vsn(2, 2, 99), NULL) + cfg(allocation_thin_pool_discards_CFG, "thin_pool_discards", allocation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_STRING, DEFAULT_THIN_POOL_DISCARDS, vsn(2, 2, 99), NULL) +@@ -144,8 +149,8 @@ cfg(global_activation_CFG, "activation", global_CFG_SECTION, 0, CFG_TYPE_BOOL, D + cfg(global_suffix_CFG, "suffix", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SUFFIX, vsn(1, 0, 0), NULL) + cfg(global_fallback_to_lvm1_CFG, "fallback_to_lvm1", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_FALLBACK_TO_LVM1, vsn(1, 0, 18), NULL) + cfg(global_format_CFG, "format", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_FORMAT, vsn(1, 0, 0), NULL) +-cfg_array(global_format_libraries_CFG, "format_libraries", global_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) +-cfg_array(global_segment_libraries_CFG, "segment_libraries", global_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL) ++cfg_array(global_format_libraries_CFG, "format_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) ++cfg_array(global_segment_libraries_CFG, "segment_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL) + cfg(global_proc_CFG, "proc", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_PROC_DIR, vsn(1, 0, 0), NULL) + cfg(global_locking_type_CFG, "locking_type", global_CFG_SECTION, 0, CFG_TYPE_INT, 1, vsn(1, 0, 0), NULL) + cfg(global_wait_for_locks_CFG, "wait_for_locks", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_WAIT_FOR_LOCKS, vsn(2, 2, 50), NULL) +@@ -179,9 +184,9 @@ cfg(activation_use_linear_target_CFG, "use_linear_target", activation_CFG_SECTIO + cfg(activation_reserved_stack_CFG, "reserved_stack", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RESERVED_STACK, vsn(1, 0, 0), NULL) + cfg(activation_reserved_memory_CFG, "reserved_memory", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RESERVED_MEMORY, vsn(1, 0, 0), NULL) + cfg(activation_process_priority_CFG, "process_priority", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_PROCESS_PRIORITY, vsn(1, 0, 0), NULL) +-cfg_array(activation_volume_list_CFG, "volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL) +-cfg_array(activation_auto_activation_volume_list_CFG, "auto_activation_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, NULL, vsn(2, 2, 97), NULL) +-cfg_array(activation_read_only_volume_list_CFG, "read_only_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, NULL, vsn(2, 2, 89), NULL) ++cfg_array(activation_volume_list_CFG, "volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY|CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL) ++cfg_array(activation_auto_activation_volume_list_CFG, "auto_activation_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 97), NULL) ++cfg_array(activation_read_only_volume_list_CFG, "read_only_volume_list", activation_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 89), NULL) + cfg(activation_mirror_region_size_CFG, "mirror_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(1, 0, 0), NULL) + cfg(activation_raid_region_size_CFG, "raid_region_size", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_RAID_REGION_SIZE, vsn(2, 2, 99), NULL) + cfg(activation_readahead_CFG, "readahead", activation_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_READ_AHEAD, vsn(1, 0, 23), NULL) +@@ -193,7 +198,7 @@ cfg(activation_snapshot_autoextend_threshold_CFG, "snapshot_autoextend_threshold + cfg(activation_snapshot_autoextend_percent_CFG, "snapshot_autoextend_percent", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_SNAPSHOT_AUTOEXTEND_PERCENT, vsn(2, 2, 75), NULL) + cfg(activation_thin_pool_autoextend_threshold_CFG, "thin_pool_autoextend_threshold", activation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_INT, DEFAULT_THIN_POOL_AUTOEXTEND_THRESHOLD, vsn(2, 2, 89), NULL) + cfg(activation_thin_pool_autoextend_percent_CFG, "thin_pool_autoextend_percent", activation_CFG_SECTION, CFG_PROFILABLE, CFG_TYPE_INT, DEFAULT_THIN_POOL_AUTOEXTEND_PERCENT, vsn(2, 2, 89), NULL) +-cfg_array(activation_mlock_filter_CFG, "mlock_filter", activation_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(2, 2, 62), NULL) ++cfg_array(activation_mlock_filter_CFG, "mlock_filter", activation_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 62), NULL) + cfg(activation_use_mlockall_CFG, "use_mlockall", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_USE_MLOCKALL, vsn(2, 2, 62), NULL) + cfg(activation_monitoring_CFG, "monitoring", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_DMEVENTD_MONITOR, vsn(2, 2, 63), NULL) + cfg(activation_polling_interval_CFG, "polling_interval", activation_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_INTERVAL, vsn(2, 2, 63), NULL) +@@ -204,13 +209,13 @@ cfg(metadata_vgmetadatacopies_CFG, "vgmetadatacopies", metadata_CFG_SECTION, CFG + cfg(metadata_pvmetadatasize_CFG, "pvmetadatasize", metadata_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_INT, DEFAULT_PVMETADATASIZE, vsn(1, 0, 0), NULL) + cfg(metadata_pvmetadataignore_CFG, "pvmetadataignore", metadata_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_BOOL, DEFAULT_PVMETADATAIGNORE, vsn(2, 2, 69), NULL) + cfg(metadata_stripesize_CFG, "stripesize", metadata_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_INT, DEFAULT_STRIPESIZE, vsn(1, 0, 0), NULL) +-cfg_array(metadata_dirs_CFG, "dirs", metadata_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) ++cfg_array(metadata_dirs_CFG, "dirs", metadata_CFG_SECTION, CFG_ADVANCED | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) + +-cfg_section(metadata_disk_areas_CFG_SUBSECTION, "disk_areas", metadata_CFG_SECTION, CFG_ADVANCED | CFG_UNSUPPORTED, vsn(1, 0, 0), NULL) +-cfg_section(disk_area_CFG_SUBSECTION, "disk_area", metadata_disk_areas_CFG_SUBSECTION, CFG_NAME_VARIABLE | CFG_ADVANCED | CFG_UNSUPPORTED, vsn(1, 0, 0), NULL) +-cfg(disk_area_start_sector_CFG, "start_sector", disk_area_CFG_SUBSECTION, CFG_ADVANCED | CFG_UNSUPPORTED, CFG_TYPE_INT, 0, vsn(1, 0, 0), NULL) +-cfg(disk_area_size_CFG, "size", disk_area_CFG_SUBSECTION, CFG_ADVANCED | CFG_UNSUPPORTED, CFG_TYPE_INT, 0, vsn(1, 0, 0), NULL) +-cfg(disk_area_id_CFG, "id", disk_area_CFG_SUBSECTION, CFG_ADVANCED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) ++cfg_section(metadata_disk_areas_CFG_SUBSECTION, "disk_areas", metadata_CFG_SECTION, CFG_ADVANCED | CFG_UNSUPPORTED | CFG_DEFAULT_UNDEFINED, vsn(1, 0, 0), NULL) ++cfg_section(disk_area_CFG_SUBSECTION, "disk_area", metadata_disk_areas_CFG_SUBSECTION, CFG_NAME_VARIABLE | CFG_ADVANCED | CFG_UNSUPPORTED | CFG_DEFAULT_UNDEFINED, vsn(1, 0, 0), NULL) ++cfg(disk_area_start_sector_CFG, "start_sector", disk_area_CFG_SUBSECTION, CFG_ADVANCED | CFG_UNSUPPORTED | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, 0, vsn(1, 0, 0), NULL) ++cfg(disk_area_size_CFG, "size", disk_area_CFG_SUBSECTION, CFG_ADVANCED | CFG_UNSUPPORTED | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, 0, vsn(1, 0, 0), NULL) ++cfg(disk_area_id_CFG, "id", disk_area_CFG_SUBSECTION, CFG_ADVANCED | CFG_UNSUPPORTED | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL) + + cfg(report_aligned_CFG, "aligned", report_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_REP_ALIGNED, vsn(1, 0, 0), NULL) + cfg(report_buffered_CFG, "buffered", report_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_REP_BUFFERED, vsn(1, 0, 0), NULL) +@@ -242,11 +247,11 @@ cfg(dmeventd_mirror_library_CFG, "mirror_library", dmeventd_CFG_SECTION, 0, CFG_ + cfg(dmeventd_raid_library_CFG, "raid_library", dmeventd_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DMEVENTD_RAID_LIB, vsn(2, 2, 87), NULL) + cfg(dmeventd_snapshot_library_CFG, "snapshot_library", dmeventd_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DMEVENTD_SNAPSHOT_LIB, vsn(1, 2, 26), NULL) + cfg(dmeventd_thin_library_CFG, "thin_library", dmeventd_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DMEVENTD_THIN_LIB, vsn(2, 2, 89), NULL) +-cfg(dmeventd_executable_CFG, "executable", dmeventd_CFG_SECTION, 0, CFG_TYPE_STRING, NULL, vsn(2, 2, 73), NULL) ++cfg(dmeventd_executable_CFG, "executable", dmeventd_CFG_SECTION, 0, CFG_TYPE_STRING, DMEVENTD_PATH, vsn(2, 2, 73), NULL) + + cfg(tags_hosttags_CFG, "hosttags", tags_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_HOSTTAGS, vsn(1, 0, 18), NULL) + +-cfg_section(tag_CFG_SUBSECTION, "tag", tags_CFG_SECTION, CFG_NAME_VARIABLE, vsn(1, 0, 18), NULL) +-cfg(tag_host_list_CFG, "host_list", tag_CFG_SUBSECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL) ++cfg_section(tag_CFG_SUBSECTION, "tag", tags_CFG_SECTION, CFG_NAME_VARIABLE | CFG_DEFAULT_UNDEFINED, vsn(1, 0, 18), NULL) ++cfg(tag_host_list_CFG, "host_list", tag_CFG_SUBSECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL) + + cfg(CFG_COUNT, NULL, root_CFG_SECTION, 0, CFG_TYPE_INT, 0, vsn(0, 0, 0), NULL) +diff --git a/lib/config/defaults.h b/lib/config/defaults.h +index 141b7ae..6820def 100644 +--- a/lib/config/defaults.h ++++ b/lib/config/defaults.h +@@ -77,7 +77,12 @@ + #define DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE 512 /* KB */ + #define DEFAULT_THIN_POOL_DISCARDS "passdown" + #define DEFAULT_THIN_POOL_ZERO 1 +-#define DEFAULT_POOL_METADATA_SPARE 1 ++#define DEFAULT_POOL_METADATA_SPARE 1 /* thin + cache */ ++ ++#define DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS 0 ++#define DEFAULT_CACHE_POOL_CHUNK_SIZE 64 /* KB */ ++#define DEFAULT_CACHE_POOL_MIN_METADATA_SIZE 2048 /* KB */ ++#define DEFAULT_CACHE_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */ + + #define DEFAULT_UMASK 0077 + +diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c +index 82cc2fc..6697456 100644 +--- a/lib/device/dev-io.c ++++ b/lib/device/dev-io.c +@@ -178,8 +178,8 @@ int dev_get_block_size(struct device *dev, unsigned int *physical_block_size, un + *physical_block_size = (unsigned int) dev->phys_block_size; + *block_size = (unsigned int) dev->block_size; + out: +- if (needs_open) +- dev_close(dev); ++ if (needs_open && !dev_close(dev)) ++ stack; + + return r; + } +diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c +index 02bc99f..1efc447 100644 +--- a/lib/device/dev-type.c ++++ b/lib/device/dev-type.c +@@ -449,25 +449,45 @@ out: + + #ifdef BLKID_WIPING_SUPPORT + ++static inline int _type_in_flag_list(const char *type, uint32_t flag_list) ++{ ++ return (((flag_list & TYPE_LVM2_MEMBER) && !strcmp(type, "LVM2_member")) || ++ ((flag_list & TYPE_LVM1_MEMBER) && !strcmp(type, "LVM1_member")) || ++ ((flag_list & TYPE_DM_SNAPSHOT_COW) && !strcmp(type, "DM_snapshot_cow"))); ++} ++ + static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name, +- int exclude_lvm_member, int yes, force_t force) ++ uint32_t types_to_exclude, uint32_t types_no_prompt, ++ int yes, force_t force) + { ++ static const char _msg_failed_offset[] = "Failed to get offset of the %s signature on %s."; ++ static const char _msg_failed_length[] = "Failed to get length of the %s signature on %s."; ++ static const char _msg_wiping[] = "Wiping %s signature on %s."; + const char *offset = NULL, *type = NULL, *magic = NULL, + *usage = NULL, *label = NULL, *uuid = NULL; + loff_t offset_value; + size_t len; + + if (!blkid_probe_lookup_value(probe, "TYPE", &type, NULL)) { +- if (exclude_lvm_member && +- (!strcmp(type, "LVM1_member") || !strcmp(type, "LVM2_member"))) ++ if (_type_in_flag_list(type, types_to_exclude)) + return 1; +- if (!blkid_probe_lookup_value(probe, "SBMAGIC_OFFSET", &offset, NULL) && +- blkid_probe_lookup_value(probe, "SBMAGIC", &magic, &len)) +- return_0; ++ if (blkid_probe_lookup_value(probe, "SBMAGIC_OFFSET", &offset, NULL)) { ++ log_error(_msg_failed_offset, type, name); ++ return 0; ++ } ++ if (blkid_probe_lookup_value(probe, "SBMAGIC", &magic, &len)) { ++ log_error(_msg_failed_length, type, name); ++ return 0; ++ } + } else if (!blkid_probe_lookup_value(probe, "PTTYPE", &type, NULL)) { +- if (!blkid_probe_lookup_value(probe, "PTMAGIC_OFFSET", &offset, NULL) && +- blkid_probe_lookup_value(probe, "PTMAGIC", &magic, &len)) +- return_0; ++ if (blkid_probe_lookup_value(probe, "PTMAGIC_OFFSET", &offset, NULL)) { ++ log_error(_msg_failed_offset, type, name); ++ return 0; ++ } ++ if (blkid_probe_lookup_value(probe, "PTMAGIC", &magic, &len)) { ++ log_error(_msg_failed_length, type, name); ++ return 0; ++ } + usage = "partition table"; + } else + return_0; +@@ -483,12 +503,15 @@ static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name, + "UUID=\"%s\" TYPE=\"%s\" USAGE=\"%s\"", + name, offset, label, uuid, type, usage); + +- if (!yes && (force == PROMPT) && +- yes_no_prompt("WARNING: %s signature detected on %s at offset %s. " +- "Wipe it? [y/n] ", type, name, offset) != 'y') +- return_0; ++ if (!_type_in_flag_list(type, types_no_prompt)) { ++ if (!yes && (force == PROMPT) && ++ yes_no_prompt("WARNING: %s signature detected on %s at offset %s. " ++ "Wipe it? [y/n] ", type, name, offset) != 'y') ++ return_0; ++ log_print_unless_silent(_msg_wiping, type, name); ++ } else ++ log_verbose(_msg_wiping, type, name); + +- log_print_unless_silent("Wiping %s signature on %s.", type, name); + if (!dev_set(dev, offset_value, len, 0)) { + log_error("Failed to wipe %s signature on %s.", type, name); + return 0; +@@ -498,7 +521,8 @@ static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name, + } + + static int _wipe_known_signatures_with_blkid(struct device *dev, const char *name, +- int exclude_lvm_member, ++ uint32_t types_to_exclude, ++ uint32_t types_no_prompt, + int yes, force_t force) + { + blkid_probe probe = NULL; +@@ -525,7 +549,7 @@ static int _wipe_known_signatures_with_blkid(struct device *dev, const char *nam + + while (!blkid_do_probe(probe)) { + found++; +- if (_blkid_wipe(probe, dev, name, exclude_lvm_member, yes, force)) ++ if (_blkid_wipe(probe, dev, name, types_to_exclude, types_no_prompt, yes, force)) + wiped++; + } + +@@ -579,7 +603,8 @@ static int _wipe_signature(struct device *dev, const char *type, const char *nam + } + + static int _wipe_known_signatures_with_lvm(struct device *dev, const char *name, +- int exclude_lvm_member, ++ uint32_t types_to_exclude __attribute__((unused)), ++ uint32_t types_no_prompt __attribute__((unused)), + int yes, force_t force) + { + if (!_wipe_signature(dev, "software RAID md superblock", name, 4, yes, force, dev_is_md) || +@@ -591,16 +616,20 @@ static int _wipe_known_signatures_with_lvm(struct device *dev, const char *name, + } + + int wipe_known_signatures(struct cmd_context *cmd, struct device *dev, +- const char *name, int exclude_lvm_member, +- int yes, force_t force) ++ const char *name, uint32_t types_to_exclude, ++ uint32_t types_no_prompt, int yes, force_t force) + { + #ifdef BLKID_WIPING_SUPPORT + if (find_config_tree_bool(cmd, allocation_use_blkid_wiping_CFG, NULL)) + return _wipe_known_signatures_with_blkid(dev, name, +- exclude_lvm_member, yes, force); ++ types_to_exclude, ++ types_no_prompt, ++ yes, force); + #endif + return _wipe_known_signatures_with_lvm(dev, name, +- exclude_lvm_member, yes, force); ++ types_to_exclude, ++ types_no_prompt, ++ yes, force); + } + + #ifdef __linux__ +diff --git a/lib/device/dev-type.h b/lib/device/dev-type.h +index 284280e..b1520ee 100644 +--- a/lib/device/dev-type.h ++++ b/lib/device/dev-type.h +@@ -60,8 +60,12 @@ int dev_is_swap(struct device *dev, uint64_t *signature); + int dev_is_luks(struct device *dev, uint64_t *signature); + + /* Signature wiping. */ ++#define TYPE_LVM1_MEMBER 0x001 ++#define TYPE_LVM2_MEMBER 0x002 ++#define TYPE_DM_SNAPSHOT_COW 0x004 + int wipe_known_signatures(struct cmd_context *cmd, struct device *dev, const char *name, +- int exclude_lvm_member, int yes, force_t force); ++ uint32_t types_to_exclude, uint32_t types_no_prompt, ++ int yes, force_t force); + + /* Type-specific device properties */ + unsigned long dev_md_stripe_width(struct dev_types *dt, struct device *dev); +diff --git a/lib/device/device-types.h b/lib/device/device-types.h +index d716878..463f847 100644 +--- a/lib/device/device-types.h ++++ b/lib/device/device-types.h +@@ -28,11 +28,13 @@ typedef struct { + * The list can be supplemented with devices/types in the config file. + */ + static const dev_known_type_t _dev_known_types[] = { +- {"ide", 64, "IDE disk"}, + {"sd", 16, "SCSI disk"}, ++ {"ide", 64, "IDE disk"}, + {"md", 1, "Multiple Disk (MD/SoftRAID)"}, +- {"mdp", 1, "Partitionable MD"}, + {"loop", 1, "Loop device"}, ++ {"ramdisk", 1, "RAM disk"}, ++ {"device-mapper", 1, "Mapped device"}, ++ {"mdp", 1, "Partitionable MD"}, + {"dasd", 4, "DASD disk (IBM S/390, zSeries)"}, + {"dac960", 8, "DAC960"}, + {"nbd", 16, "Network Block Device"}, +@@ -46,9 +48,7 @@ static const dev_known_type_t _dev_known_types[] = { + {"i2o_block", 16, "i2o Block Disk"}, + {"iseries/vd", 8, "iSeries disks"}, + {"gnbd", 1, "Network block device"}, +- {"ramdisk", 1, "RAM disk"}, + {"aoe", 16, "ATA over Ethernet"}, +- {"device-mapper", 1, "Mapped device"}, + {"xvd", 16, "Xen virtual block device"}, + {"vdisk", 8, "SUN's LDOM virtual block device"}, + {"ps3disk", 16, "PlayStation 3 internal disk"}, +@@ -62,5 +62,6 @@ static const dev_known_type_t _dev_known_types[] = { + {"scm", 8, "Storage Class Memory (IBM S/390)"}, + {"bcache", 1, "bcache block device cache"}, + {"nvme", 64, "NVM Express"}, ++ {"zvol", 16, "ZFS Zvols"}, + {"", 0, ""} + }; +diff --git a/lib/display/display.c b/lib/display/display.c +index 1babffc..66acb88 100644 +--- a/lib/display/display.c ++++ b/lib/display/display.c +@@ -182,6 +182,13 @@ alloc_policy_t get_alloc_from_string(const char *str) + return ALLOC_INVALID; + } + ++static const char *_percent_types[7] = { "NONE", "VGS", "FREE", "LVS", "PVS", "ORIGIN" }; ++ ++const char *get_percent_string(percent_type_t def) ++{ ++ return _percent_types[def]; ++} ++ + #define BASE_UNKNOWN 0 + #define BASE_SHARED 1 + #define BASE_1024 8 +@@ -921,6 +928,15 @@ void display_segtypes(const struct cmd_context *cmd) + } + } + ++void display_tags(const struct cmd_context *cmd) ++{ ++ const struct str_list *sl; ++ ++ dm_list_iterate_items(sl, &cmd->tags) { ++ log_print("%s", sl->str); ++ } ++} ++ + void display_name_error(name_error_t name_error) + { + if (name_error != NAME_VALID) { +diff --git a/lib/display/display.h b/lib/display/display.h +index 077fff4..41fba03 100644 +--- a/lib/display/display.h ++++ b/lib/display/display.h +@@ -53,6 +53,7 @@ void vgdisplay_short(const struct volume_group *vg); + + void display_formats(const struct cmd_context *cmd); + void display_segtypes(const struct cmd_context *cmd); ++void display_tags(const struct cmd_context *cmd); + + void display_name_error(name_error_t name_error); + +@@ -63,6 +64,8 @@ const char *get_alloc_string(alloc_policy_t alloc); + char alloc_policy_char(alloc_policy_t alloc); + alloc_policy_t get_alloc_from_string(const char *str); + ++const char *get_percent_string(percent_type_t def); ++ + char yes_no_prompt(const char *prompt, ...) __attribute__ ((format(printf, 1, 2))); + + #endif +diff --git a/lib/format_text/export.c b/lib/format_text/export.c +index 73030e4..883e076 100644 +--- a/lib/format_text/export.c ++++ b/lib/format_text/export.c +@@ -367,12 +367,12 @@ static int _print_flag_config(struct formatter *f, uint64_t status, int type) + } + + +-static int _out_tags(struct formatter *f, struct dm_list *tags) ++static int _out_tags(struct formatter *f, struct dm_list *tagsl) + { + char *tag_buffer; + +- if (!dm_list_empty(tags)) { +- if (!(tag_buffer = alloc_printed_tags(tags))) ++ if (!dm_list_empty(tagsl)) { ++ if (!(tag_buffer = alloc_printed_tags(tagsl))) + return_0; + if (!out_text(f, "tags = %s", tag_buffer)) { + dm_free(tag_buffer); +diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c +index e31429e..bc48952 100644 +--- a/lib/format_text/flags.c ++++ b/lib/format_text/flags.c +@@ -83,6 +83,10 @@ static const struct flag _lv_flags[] = { + {THIN_POOL, NULL, 0}, + {THIN_POOL_DATA, NULL, 0}, + {THIN_POOL_METADATA, NULL, 0}, ++ {CACHE, NULL, 0}, ++ {CACHE_POOL, NULL, 0}, ++ {CACHE_POOL_DATA, NULL, 0}, ++ {CACHE_POOL_METADATA, NULL, 0}, + {0, NULL, 0} + }; + +diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c +index 6e309a3..b4f96f8 100644 +--- a/lib/format_text/format-text.c ++++ b/lib/format_text/format-text.c +@@ -29,6 +29,7 @@ + #include "label.h" + #include "lvmcache.h" + #include "lvmetad.h" ++#include "memlock.h" + + #include + #include +@@ -1912,8 +1913,19 @@ static int _create_vg_text_instance(struct format_instance *fid, + } + + if (type & FMT_INSTANCE_MDAS) { +- /* Scan PVs in VG for any further MDAs */ +- lvmcache_label_scan(fid->fmt->cmd, 0); ++ /* ++ * TODO in theory, this function should be never reached ++ * while in critical_section(), because lvmcache's ++ * cached_vg should be valid. However, this assumption ++ * sometimes fails (possibly due to inconsistent ++ * (precommit) metadata and/or missing devices), and ++ * calling lvmcache_label_scan inside the critical ++ * section may be fatal (i.e. deadlock). ++ */ ++ if (!critical_section()) ++ /* Scan PVs in VG for any further MDAs */ ++ lvmcache_label_scan(fid->fmt->cmd, 0); ++ + if (!(vginfo = lvmcache_vginfo_from_vgname(vg_name, vg_id))) + goto_out; + if (!lvmcache_fid_add_mdas_vg(vginfo, fid)) +diff --git a/lib/format_text/tags.c b/lib/format_text/tags.c +index b0f0732..d0bf2b1 100644 +--- a/lib/format_text/tags.c ++++ b/lib/format_text/tags.c +@@ -19,14 +19,14 @@ + #include "str_list.h" + #include "lvm-string.h" + +-char *alloc_printed_tags(struct dm_list *tags) ++char *alloc_printed_tags(struct dm_list *tagsl) + { + struct str_list *sl; + int first = 1; + size_t size = 0; + char *buffer, *buf; + +- dm_list_iterate_items(sl, tags) ++ dm_list_iterate_items(sl, tagsl) + /* '"' + tag + '"' + ',' + ' ' */ + size += strlen(sl->str) + 4; + /* '[' + ']' + '\0' */ +@@ -40,7 +40,7 @@ char *alloc_printed_tags(struct dm_list *tags) + if (!emit_to_buffer(&buf, &size, "[")) + goto_bad; + +- dm_list_iterate_items(sl, tags) { ++ dm_list_iterate_items(sl, tagsl) { + if (!first) { + if (!emit_to_buffer(&buf, &size, ", ")) + goto_bad; +@@ -61,7 +61,7 @@ bad: + return_NULL; + } + +-int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct dm_config_value *cv) ++int read_tags(struct dm_pool *mem, struct dm_list *tagsl, const struct dm_config_value *cv) + { + if (cv->type == DM_CFG_EMPTY_ARRAY) + return 1; +@@ -72,7 +72,7 @@ int read_tags(struct dm_pool *mem, struct dm_list *tags, const struct dm_config_ + return 0; + } + +- if (!str_list_add(mem, tags, dm_pool_strdup(mem, cv->v.str))) ++ if (!str_list_add(mem, tagsl, dm_pool_strdup(mem, cv->v.str))) + return_0; + + cv = cv->next; +diff --git a/lib/label/label.c b/lib/label/label.c +index 25e5f2c..703fef7 100644 +--- a/lib/label/label.c ++++ b/lib/label/label.c +@@ -19,7 +19,6 @@ + #include "xlate.h" + #include "lvmcache.h" + #include "lvmetad.h" +-#include "metadata.h" + + #include + #include +diff --git a/lib/locking/file_locking.c b/lib/locking/file_locking.c +index 1fa23b3..fb84c5b 100644 +--- a/lib/locking/file_locking.c ++++ b/lib/locking/file_locking.c +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * +@@ -44,6 +44,15 @@ static sig_t _oldhandler; + static sigset_t _fullsigset, _intsigset; + static volatile sig_atomic_t _handler_installed; + ++/* Drop lock known to be shared with another file descriptor. */ ++static void _drop_shared_flock(const char *file, int fd) ++{ ++ log_debug_locking("_drop_shared_flock %s.", file); ++ ++ if (close(fd) < 0) ++ log_sys_debug("close", file); ++} ++ + static void _undo_flock(const char *file, int fd) + { + struct stat buf1, buf2; +@@ -74,9 +83,9 @@ static int _release_lock(const char *file, int unlock) + log_very_verbose("Unlocking %s", ll->res); + if (flock(ll->lf, LOCK_NB | LOCK_UN)) + log_sys_debug("flock", ll->res); +- } +- +- _undo_flock(ll->res, ll->lf); ++ _undo_flock(ll->res, ll->lf); ++ } else ++ _drop_shared_flock(ll->res, ll->lf); + + dm_free(ll->res); + dm_free(llh); +diff --git a/lib/metadata/cache_manip.c b/lib/metadata/cache_manip.c +new file mode 100644 +index 0000000..673d90e +--- /dev/null ++++ b/lib/metadata/cache_manip.c +@@ -0,0 +1,278 @@ ++/* ++ * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "lib.h" ++#include "metadata.h" ++#include "locking.h" ++#include "pv_map.h" ++#include "lvm-string.h" ++#include "toolcontext.h" ++#include "lv_alloc.h" ++#include "pv_alloc.h" ++#include "display.h" ++#include "segtype.h" ++#include "archiver.h" ++#include "activate.h" ++#include "str_list.h" ++#include "defaults.h" ++#include "lvm-exec.h" ++ ++int update_cache_pool_params(struct volume_group *vg, unsigned attr, ++ int passed_args, ++ uint32_t data_extents, uint32_t extent_size, ++ int *chunk_size_calc_method, uint32_t *chunk_size, ++ thin_discards_t *discards, ++ uint64_t *pool_metadata_size, int *zero) ++{ ++ uint64_t min_meta_size; ++ ++ if ((*chunk_size < DM_CACHE_MIN_DATA_BLOCK_SIZE) || ++ (*chunk_size > DM_CACHE_MAX_DATA_BLOCK_SIZE)) { ++ log_error("Chunk size must be in the range %s to %s.", ++ display_size(vg->cmd, DM_CACHE_MIN_DATA_BLOCK_SIZE), ++ display_size(vg->cmd, DM_CACHE_MAX_DATA_BLOCK_SIZE)); ++ return 0; ++ } ++ ++ if (*chunk_size & (DM_CACHE_MIN_DATA_BLOCK_SIZE - 1)) { ++ log_error("Chunk size must be a multiple of %u sectors.", ++ DM_CACHE_MIN_DATA_BLOCK_SIZE); ++ return 0; ++ } ++ ++ /* ++ * Default meta size is: ++ * (4MiB + (16 Bytes for each chunk-sized block)) ++ * ... plus a good amount of padding (2x) to cover any ++ * policy hint data that may be added in the future. ++ */ ++ min_meta_size = 16 * (data_extents * vg->extent_size); ++ min_meta_size /= *chunk_size; /* # of Bytes we need */ ++ min_meta_size *= 2; /* plus some padding */ ++ min_meta_size /= 512; /* in sectors */ ++ min_meta_size += 4*1024*2; /* plus 4MiB */ ++ ++ if (!*pool_metadata_size) ++ *pool_metadata_size = min_meta_size; ++ ++ if (*pool_metadata_size < min_meta_size) { ++ *pool_metadata_size = min_meta_size; ++ log_print("Increasing metadata device size to %" ++ PRIu64 " sectors", *pool_metadata_size); ++ } ++ if (*pool_metadata_size > (2 * DEFAULT_CACHE_POOL_MAX_METADATA_SIZE)) { ++ *pool_metadata_size = 2 * DEFAULT_CACHE_POOL_MAX_METADATA_SIZE; ++ log_print("Reducing metadata device size to %" PRIu64 " sectors", ++ *pool_metadata_size); ++ } ++ ++ return 1; ++} ++ ++/* ++ * lv_cache_create ++ * @pool ++ * @origin ++ * ++ * Given a cache_pool and an origin, link the two and create a ++ * cached LV. ++ * ++ * Returns: cache LV on success, NULL on failure ++ */ ++struct logical_volume *lv_cache_create(struct logical_volume *pool, ++ struct logical_volume *origin) ++{ ++ const struct segment_type *segtype; ++ struct cmd_context *cmd = pool->vg->cmd; ++ struct logical_volume *cache_lv; ++ struct lv_segment *seg; ++ ++ if (!lv_is_cache_pool(pool)) { ++ log_error(INTERNAL_ERROR ++ "%s is not a cache_pool LV", pool->name); ++ return NULL; ++ } ++ ++ if (!dm_list_empty(&pool->segs_using_this_lv)) { ++ seg = get_only_segment_using_this_lv(pool); ++ log_error("%s is already in use by %s", ++ pool->name, seg ? seg->lv->name : "another LV"); ++ return NULL; ++ } ++ ++ if (lv_is_cache_type(origin)) { ++ /* ++ * FIXME: We can layer caches, insert_layer_for_lv() would ++ * have to do a better job renaming the LVs in the stack ++ * first so that there isn't a name collision with _corig. ++ * The origin under the origin would become *_corig_corig ++ * before renaming the origin above to *_corig. ++ */ ++ log_error(INTERNAL_ERROR ++ "The origin, %s, cannot be of cache type", ++ origin->name); ++ return NULL; ++ } ++ ++ if (!(segtype = get_segtype_from_string(cmd, "cache"))) ++ return_NULL; ++ ++ cache_lv = origin; ++ if (!(origin = insert_layer_for_lv(cmd, cache_lv, CACHE, "_corig"))) ++ return_NULL; ++ ++ seg = first_seg(cache_lv); ++ seg->segtype = segtype; ++ ++ if (!attach_pool_lv(seg, pool, NULL, NULL)) ++ return_NULL; ++ ++ return cache_lv; ++} ++ ++/* ++ * lv_cache_remove ++ * @cache_lv ++ * ++ * Given a cache LV, remove the cache layer. This will unlink ++ * the origin and cache_pool, remove the cache LV layer, and promote ++ * the origin to a usable non-cached LV of the same name as the ++ * given cache_lv. ++ * ++ * Returns: 1 on success, 0 on failure ++ */ ++int lv_cache_remove(struct logical_volume *cache_lv) ++{ ++ struct cmd_context *cmd = cache_lv->vg->cmd; ++ char *policy_name; ++ uint64_t dirty_blocks; ++ struct segment_type *segtype; ++ struct lv_segment *cache_seg = first_seg(cache_lv); ++ struct logical_volume *origin_lv; ++ struct logical_volume *cache_pool_lv; ++ ++ if (!lv_is_cache(cache_lv)) ++ return_0; ++ ++ /* ++ * FIXME: ++ * Before the link can be broken, we must ensure that the ++ * cache has been flushed. This may already be the case ++ * if the cache mode is writethrough (or the cleaner ++ * policy is in place from a previous half-finished attempt ++ * to remove the cache_pool). It could take a long time to ++ * flush the cache - it should probably be done in the background. ++ * ++ * Also, if we do perform the flush in the background and we ++ * happen to also be removing the cache/origin LV, then we ++ * could check if the cleaner policy is in place and simply ++ * remove the cache_pool then without waiting for the flush to ++ * complete. ++ */ ++ if (!lv_cache_policy_info(cache_lv, &policy_name, NULL, NULL)) ++ return_0; ++ ++ if (strcmp(policy_name, "cleaner")) { ++ /* We must swap in the cleaner to flush the cache */ ++ log_error("Flushing cache for %s", cache_lv->name); ++ ++ /* ++ * Is there are clean way to free the memory for the name ++ * and argv when changing the policy? ++ */ ++ cache_seg->policy_name = (char *)"cleaner"; ++ cache_seg->policy_argc = 0; ++ cache_seg->policy_argv = NULL; ++ ++ /* update the kernel to put the cleaner policy in place */ ++ if (!vg_write(cache_lv->vg)) ++ return_0; ++ if (!suspend_lv(cmd, cache_lv)) ++ return_0; ++ if (!vg_commit(cache_lv->vg)) ++ return_0; ++ if (!resume_lv(cmd, cache_lv)) ++ return_0; ++ } ++ ++ //FIXME: use polling to do this... ++ do { ++ if (!lv_cache_block_info(cache_lv, NULL, ++ &dirty_blocks, NULL, NULL)) ++ return_0; ++ log_error("%" PRIu64 " blocks must still be flushed.", ++ dirty_blocks); ++ if (dirty_blocks) ++ sleep(5); ++ } while (dirty_blocks); ++ ++ cache_pool_lv = first_seg(cache_lv)->pool_lv; ++ if (!detach_pool_lv(first_seg(cache_lv))) ++ return_0; ++ ++ origin_lv = seg_lv(first_seg(cache_lv), 0); ++ lv_set_visible(origin_lv); ++ ++//FIXME: We should be able to use 'remove_layer_from_lv', but ++// there is a call to 'lv_empty' in there that recursively ++// deletes everything down the tree - including the origin_lv ++// that we are trying to preserve! ++// if (!remove_layer_from_lv(cache_lv, origin_lv)) ++// return_0; ++ ++ if (!remove_seg_from_segs_using_this_lv(origin_lv, first_seg(cache_lv))) ++ return_0; ++ if (!move_lv_segments(cache_lv, origin_lv, 0, 0)) ++ return_0; ++ ++ cache_lv->status &= ~CACHE; ++ ++ segtype = get_segtype_from_string(cmd, "error"); ++ if (!lv_add_virtual_segment(origin_lv, 0, ++ cache_lv->le_count, segtype, NULL)) ++ return_0; ++ ++ if (!vg_write(cache_lv->vg)) ++ return_0; ++ ++ /* ++ * suspend_lv on this cache LV will suspend all of the components: ++ * - the top-level cache LV ++ * - the origin ++ * - the cache_pool and all of its sub-LVs ++ */ ++ if (!suspend_lv(cmd, cache_lv)) ++ return_0; ++ ++ if (!vg_commit(cache_lv->vg)) ++ return_0; ++ ++ /* ++ * resume_lv on this (former) cache LV will resume all ++ * but the cache_pool LV. It must be resumed seperately. ++ */ ++ if (!resume_lv(cmd, cache_lv)) ++ return_0; ++ if (!resume_lv(cmd, cache_pool_lv)) ++ return_0; ++ ++ if (!activate_lv(cmd, origin_lv)) ++ return_0; ++ if (!deactivate_lv(cmd, origin_lv)) ++ return_0; ++ if (!lv_remove(origin_lv)) ++ return_0; ++ ++ return 1; ++} +diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c +index 3e1458c..4549f03 100644 +--- a/lib/metadata/lv.c ++++ b/lib/metadata/lv.c +@@ -170,7 +170,7 @@ uint64_t lvseg_chunksize(const struct lv_segment *seg) + + if (lv_is_cow(seg->lv)) + size = (uint64_t) find_snapshot(seg->lv)->chunk_size; +- else if (seg_is_thin_pool(seg)) ++ else if (seg_is_thin_pool(seg) || seg_is_cache_pool(seg)) + size = (uint64_t) seg->chunk_size; + else + size = UINT64_C(0); +@@ -202,6 +202,9 @@ char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv) + if (lv_is_cow(lv)) + return lv_name_dup(mem, origin_from_cow(lv)); + ++ if (lv_is_cache(lv) && first_seg(lv)->origin) ++ return lv_name_dup(mem, first_seg(lv)->origin); ++ + if (lv_is_thin_volume(lv) && first_seg(lv)->origin) + return lv_name_dup(mem, first_seg(lv)->origin); + +@@ -246,7 +249,8 @@ char *lv_pool_lv_dup(struct dm_pool *mem, const struct logical_volume *lv) + struct lv_segment *seg; + + dm_list_iterate_items(seg, &lv->segments) +- if (seg_is_thin_volume(seg) && seg->pool_lv) ++ if (seg->pool_lv && ++ (seg_is_thin_volume(seg) || seg_is_cache(seg))) + return dm_pool_strdup(mem, seg->pool_lv->name); + + return NULL; +@@ -254,14 +258,16 @@ char *lv_pool_lv_dup(struct dm_pool *mem, const struct logical_volume *lv) + + char *lv_data_lv_dup(struct dm_pool *mem, const struct logical_volume *lv) + { +- struct lv_segment *seg = lv_is_thin_pool(lv) ? first_seg(lv) : NULL; ++ struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ? ++ first_seg(lv) : NULL; + + return seg ? dm_pool_strdup(mem, seg_lv(seg, 0)->name) : NULL; + } + + char *lv_metadata_lv_dup(struct dm_pool *mem, const struct logical_volume *lv) + { +- struct lv_segment *seg = lv_is_thin_pool(lv) ? first_seg(lv) : NULL; ++ struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ? ++ first_seg(lv) : NULL; + + return seg ? dm_pool_strdup(mem, seg->metadata_lv->name) : NULL; + } +@@ -338,7 +344,8 @@ uint64_t lv_origin_size(const struct logical_volume *lv) + + uint64_t lv_metadata_size(const struct logical_volume *lv) + { +- struct lv_segment *seg = lv_is_thin_pool(lv) ? first_seg(lv) : NULL; ++ struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ? ++ first_seg(lv) : NULL; + + return seg ? seg->metadata_lv->size : 0; + } +@@ -386,10 +393,14 @@ uint64_t lv_size(const struct logical_volume *lv) + static int _lv_mimage_in_sync(const struct logical_volume *lv) + { + percent_t percent; +- struct lv_segment *mirror_seg = find_mirror_seg(first_seg(lv)); ++ struct lv_segment *seg = first_seg(lv); ++ struct lv_segment *mirror_seg; + +- if (!(lv->status & MIRROR_IMAGE) || !mirror_seg) +- return_0; ++ if (!(lv->status & MIRROR_IMAGE) || !seg || ++ !(mirror_seg = find_mirror_seg(seg))) { ++ log_error(INTERNAL_ERROR "Cannot find mirror segment."); ++ return 0; ++ } + + if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent, + NULL)) +@@ -403,7 +414,7 @@ static int _lv_raid_image_in_sync(const struct logical_volume *lv) + unsigned s; + percent_t percent; + char *raid_health; +- struct lv_segment *raid_seg; ++ struct lv_segment *seg, *raid_seg = NULL; + + /* + * If the LV is not active locally, +@@ -417,7 +428,8 @@ static int _lv_raid_image_in_sync(const struct logical_volume *lv) + return 0; + } + +- raid_seg = get_only_segment_using_this_lv(first_seg(lv)->lv); ++ if ((seg = first_seg(lv))) ++ raid_seg = get_only_segment_using_this_lv(seg->lv); + if (!raid_seg) { + log_error("Failed to find RAID segment for %s", lv->name); + return 0; +@@ -465,7 +477,7 @@ static int _lv_raid_healthy(const struct logical_volume *lv) + { + unsigned s; + char *raid_health; +- struct lv_segment *raid_seg; ++ struct lv_segment *seg, *raid_seg = NULL; + + /* + * If the LV is not active locally, +@@ -481,8 +493,8 @@ static int _lv_raid_healthy(const struct logical_volume *lv) + + if (lv->status & RAID) + raid_seg = first_seg(lv); +- else +- raid_seg = get_only_segment_using_this_lv(first_seg(lv)->lv); ++ else if ((seg = first_seg(lv))) ++ raid_seg = get_only_segment_using_this_lv(seg->lv); + + if (!raid_seg) { + log_error("Failed to find RAID segment for %s", lv->name); +@@ -546,6 +558,10 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv) + /* Origin takes precedence over mirror and thin volume */ + else if (lv_is_origin(lv) || lv_is_external_origin(lv)) + repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o'; ++ else if (lv_is_cache_pool_metadata(lv)) ++ repstr[0] = 'e'; ++ else if (lv_is_cache_type(lv)) ++ repstr[0] = 'C'; + else if (lv_is_thin_pool_metadata(lv) || + lv_is_pool_metadata_spare(lv) || + (lv->status & RAID_META)) +@@ -638,6 +654,8 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv) + + if (lv_is_thin_pool(lv) || lv_is_thin_volume(lv)) + repstr[6] = 't'; ++ else if (lv_is_cache_type(lv)) ++ repstr[6] = 'C'; + else if (lv_is_raid_type(lv)) + repstr[6] = 'r'; + else if (lv_is_mirror_type(lv)) +@@ -741,7 +759,8 @@ static int _lv_is_exclusive(struct logical_volume *lv) + /* Some devices require exlusivness */ + return seg_is_raid(first_seg(lv)) || + lv_is_origin(lv) || +- lv_is_thin_type(lv); ++ lv_is_thin_type(lv) || ++ lv_is_cache_type(lv); + } + + int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv, +diff --git a/lib/metadata/lv_alloc.h b/lib/metadata/lv_alloc.h +index acfebca..395420d 100644 +--- a/lib/metadata/lv_alloc.h ++++ b/lib/metadata/lv_alloc.h +@@ -54,7 +54,7 @@ struct alloc_handle *allocate_extents(struct volume_group *vg, + uint32_t mirrors, uint32_t log_count, + uint32_t log_region_size, uint32_t extents, + struct dm_list *allocatable_pvs, +- alloc_policy_t alloc, ++ alloc_policy_t alloc, int approx_alloc, + struct dm_list *parallel_areas); + + int lv_add_segment(struct alloc_handle *ah, +diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c +index f45c89f..57ce2d9 100644 +--- a/lib/metadata/lv_manip.c ++++ b/lib/metadata/lv_manip.c +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * +@@ -346,6 +346,11 @@ struct lv_segment *get_only_segment_using_this_lv(struct logical_volume *lv) + { + struct seg_list *sl; + ++ if (!lv) { ++ log_error(INTERNAL_ERROR "get_only_segment_using_this_lv() called with NULL LV."); ++ return NULL; ++ } ++ + if (dm_list_size(&lv->segs_using_this_lv) != 1) { + log_error("%s is expected to have only one segment using it, " + "while it has %d", lv->name, +@@ -542,6 +547,8 @@ struct lv_segment *alloc_snapshot_seg(struct logical_volume *lv, + static int _release_and_discard_lv_segment_area(struct lv_segment *seg, uint32_t s, + uint32_t area_reduction, int with_discard) + { ++ struct lv_segment *cache_seg; ++ + if (seg_type(seg, s) == AREA_UNASSIGNED) + return 1; + +@@ -558,13 +565,24 @@ static int _release_and_discard_lv_segment_area(struct lv_segment *seg, uint32_t + return 1; + } + +- if ((seg_lv(seg, s)->status & MIRROR_IMAGE) || +- (seg_lv(seg, s)->status & THIN_POOL_DATA)) { ++ if (seg_is_cache(seg) || ++ (seg_lv(seg, s)->status & MIRROR_IMAGE) || ++ (seg_lv(seg, s)->status & THIN_POOL_DATA) || ++ (seg_lv(seg, s)->status & CACHE_POOL_DATA)) { + if (!lv_reduce(seg_lv(seg, s), area_reduction)) + return_0; /* FIXME: any upper level reporting */ + return 1; + } + ++ if (seg_is_cache_pool(seg) && ++ !dm_list_empty(&seg->lv->segs_using_this_lv)) { ++ if (!(cache_seg = get_only_segment_using_this_lv(seg->lv))) ++ return_0; ++ ++ if (!lv_cache_remove(cache_seg->lv)) ++ return_0; ++ } ++ + if (seg_lv(seg, s)->status & RAID_IMAGE) { + /* + * FIXME: Use lv_reduce not lv_remove +@@ -905,6 +923,7 @@ struct alloc_handle { + struct dm_pool *mem; + + alloc_policy_t alloc; /* Overall policy */ ++ int approx_alloc; /* get as much as possible up to new_extents */ + uint32_t new_extents; /* Number of new extents required */ + uint32_t area_count; /* Number of parallel areas */ + uint32_t parity_count; /* Adds to area_count, but not area_multiple */ +@@ -924,8 +943,12 @@ struct alloc_handle { + * that is new_extents + log_len and then split that between two + * allocated areas when found. 'alloc_and_split_meta' indicates + * that this is the desired dynamic. ++ * ++ * This same idea is used by cache LVs to get the metadata device ++ * and data device allocated together. + */ + unsigned alloc_and_split_meta; ++ unsigned split_metadata_is_allocated; /* Metadata has been allocated */ + + const struct dm_config_node *cling_tag_list_cn; + +@@ -1025,7 +1048,7 @@ static uint32_t mirror_log_extents(uint32_t region_size, uint32_t pe_size, uint3 + static struct alloc_handle *_alloc_init(struct cmd_context *cmd, + struct dm_pool *mem, + const struct segment_type *segtype, +- alloc_policy_t alloc, ++ alloc_policy_t alloc, int approx_alloc, + uint32_t new_extents, + uint32_t mirrors, + uint32_t stripes, +@@ -1115,6 +1138,7 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd, + * a correct area_multiple. + */ + ah->area_multiple = _calc_area_multiple(segtype, area_count + parity_count, stripes); ++ //FIXME: s/mirror_logs_separate/metadata_separate/ so it can be used by otehrs? + ah->mirror_logs_separate = find_config_tree_bool(cmd, allocation_mirror_logs_require_separate_pvs_CFG, NULL); + + if (segtype_is_raid(segtype)) { +@@ -1131,23 +1155,53 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd, + * We need 'log_len' extents for each + * RAID device's metadata_area + */ +- ah->new_extents += (ah->log_len * ah->area_multiple); ++ if (!approx_alloc) ++ ah->new_extents += (ah->log_len * ah->area_multiple); + } else { + ah->log_area_count = 0; + ah->log_len = 0; + } ++ if (approx_alloc) { ++ ah->new_extents = ah->new_extents * ah->area_multiple / (ah->area_count + ah->parity_count); ++ ah->new_extents = (ah->new_extents / ah->area_multiple) * ah->area_multiple; ++ log_debug("Adjusted allocation request to %" PRIu32 " data extents.", ah->new_extents); ++ } + } else if (segtype_is_thin_pool(segtype)) { +- ah->log_area_count = metadata_area_count; +- /* thin_pool uses region_size to pass metadata size in extents */ ++ /* ++ * thin_pool uses ah->region_size to ++ * pass metadata size in extents ++ */ + ah->log_len = ah->region_size; ++ ah->log_area_count = metadata_area_count; + ah->region_size = 0; + ah->mirror_logs_separate = + find_config_tree_bool(cmd, allocation_thin_pool_metadata_require_separate_pvs_CFG, NULL); ++ } else if (segtype_is_cache_pool(segtype)) { ++ /* ++ * Like thin_pool, cache_pool uses ah->region_size to ++ * pass metadata size in extents ++ */ ++ ah->log_len = ah->region_size; ++ /* use metadata_area_count, not log_area_count */ ++ ah->metadata_area_count = metadata_area_count; ++ ah->region_size = 0; ++ ah->mirror_logs_separate = ++ find_config_tree_bool(cmd, allocation_cache_pool_metadata_require_separate_pvs_CFG, NULL); ++ if (!ah->mirror_logs_separate) { ++ ah->alloc_and_split_meta = 1; ++ if (!approx_alloc) ++ ah->new_extents += ah->log_len; ++ } + } else { + ah->log_area_count = metadata_area_count; + ah->log_len = !metadata_area_count ? 0 : + mirror_log_extents(ah->region_size, extent_size, + new_extents / ah->area_multiple); ++ if (approx_alloc) { ++ ah->new_extents = ah->new_extents * ah->area_multiple / ah->area_count; ++ ah->new_extents = (ah->new_extents / ah->area_multiple) * ah->area_multiple; ++ log_debug("Adjusted allocation request to %" PRIu32 " data extents.", ah->new_extents); ++ } + } + + for (s = 0; s < alloc_count; s++) +@@ -1159,6 +1213,8 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd, + + ah->maximise_cling = find_config_tree_bool(cmd, allocation_maximise_cling_CFG, NULL); + ++ ah->approx_alloc = approx_alloc; ++ + return ah; + } + +@@ -1384,7 +1440,7 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat + if (area_len > alloc_state->areas[s].used) + area_len = alloc_state->areas[s].used; + +- len = (ah->alloc_and_split_meta) ? total_area_count * 2 : total_area_count; ++ len = (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? total_area_count * 2 : total_area_count; + len *= sizeof(*aa); + if (!(aa = dm_pool_alloc(ah->mem, len))) { + log_error("alloced_area allocation failed"); +@@ -1404,7 +1460,7 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat + } + + pva = alloc_state->areas[s + ix_log_skip].pva; +- if (ah->alloc_and_split_meta) { ++ if (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) { + /* + * The metadata area goes at the front of the allocated + * space for now, but could easily go at the end (or +@@ -1430,7 +1486,7 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat + dm_list_add(&ah->alloced_areas[s], &aa[s].list); + s -= ah->area_count + ah->parity_count; + } +- aa[s].len = (ah->alloc_and_split_meta) ? len - ah->log_len : len; ++ aa[s].len = (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? len - ah->log_len : len; + /* Skip empty allocations */ + if (!aa[s].len) + continue; +@@ -1448,7 +1504,8 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat + } + + /* Only need to alloc metadata from the first batch */ +- ah->alloc_and_split_meta = 0; ++ if (ah->alloc_and_split_meta) ++ ah->split_metadata_is_allocated = 1; + + ah->total_area_len += area_len; + +@@ -1950,33 +2007,40 @@ static void _reset_unreserved(struct dm_list *pvms) + } + + static void _report_needed_allocation_space(struct alloc_handle *ah, +- struct alloc_state *alloc_state) ++ struct alloc_state *alloc_state, ++ struct dm_list *pvms) + { + const char *metadata_type; + uint32_t parallel_areas_count, parallel_area_size; + uint32_t metadata_count, metadata_size; + +- parallel_area_size = (ah->new_extents - alloc_state->allocated) / ah->area_multiple - +- ((ah->alloc_and_split_meta) ? ah->log_len : 0); ++ parallel_area_size = ah->new_extents - alloc_state->allocated; ++ parallel_area_size /= ah->area_multiple; ++ parallel_area_size -= (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? ah->log_len : 0; + + parallel_areas_count = ah->area_count + ah->parity_count; + + metadata_size = ah->log_len; + if (ah->alloc_and_split_meta) { +- metadata_type = "RAID metadata area"; ++ metadata_type = "metadata area"; + metadata_count = parallel_areas_count; ++ if (ah->split_metadata_is_allocated) ++ metadata_size = 0; + } else { + metadata_type = "mirror log"; + metadata_count = alloc_state->log_area_count_still_needed; + } + +- log_debug_alloc("Still need %" PRIu32 " total extents:", +- parallel_area_size * parallel_areas_count + metadata_size * metadata_count); ++ log_debug_alloc("Still need %s%" PRIu32 " total extents from %" PRIu32 " remaining:", ++ ah->approx_alloc ? "up to " : "", ++ parallel_area_size * parallel_areas_count + metadata_size * metadata_count, pv_maps_size(pvms)); + log_debug_alloc(" %" PRIu32 " (%" PRIu32 " data/%" PRIu32 + " parity) parallel areas of %" PRIu32 " extents each", + parallel_areas_count, ah->area_count, ah->parity_count, parallel_area_size); +- log_debug_alloc(" %" PRIu32 " %ss of %" PRIu32 " extents each", +- metadata_count, metadata_type, metadata_size); ++ log_debug_alloc(" %" PRIu32 " %s%s of %" PRIu32 " extents each", ++ metadata_count, metadata_type, ++ (metadata_count == 1) ? "" : "s", ++ metadata_size); + } + /* + * Returns 1 regardless of whether any space was found, except on error. +@@ -2015,7 +2079,7 @@ static int _find_some_parallel_space(struct alloc_handle *ah, const struct alloc + _clear_areas(alloc_state); + _reset_unreserved(pvms); + +- _report_needed_allocation_space(ah, alloc_state); ++ _report_needed_allocation_space(ah, alloc_state, pvms); + + /* ix holds the number of areas found on other PVs */ + do { +@@ -2240,11 +2304,11 @@ static int _find_max_parallel_space_for_one_policy(struct alloc_handle *ah, stru + * data together will be split, we must adjust + * the comparison accordingly. + */ +- if (ah->alloc_and_split_meta) ++ if (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) + max_tmp -= ah->log_len; + if (max_tmp > (spvs->le + spvs->len) * ah->area_multiple) { + max_to_allocate = (spvs->le + spvs->len) * ah->area_multiple - alloc_state->allocated; +- max_to_allocate += ah->alloc_and_split_meta ? ah->log_len : 0; ++ max_to_allocate += (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) ? ah->log_len : 0; + } + parallel_pvs = &spvs->pvs; + break; +@@ -2272,7 +2336,7 @@ static int _find_max_parallel_space_for_one_policy(struct alloc_handle *ah, stru + } else if (ah->maximise_cling && alloc_parms->alloc == ALLOC_NORMAL && + !(alloc_parms->flags & A_CLING_TO_ALLOCED)) + alloc_parms->flags |= A_CLING_TO_ALLOCED; +- } while ((alloc_parms->alloc != ALLOC_CONTIGUOUS) && alloc_state->allocated != alloc_parms->extents_still_needed && (alloc_parms->flags & A_CAN_SPLIT)); ++ } while ((alloc_parms->alloc != ALLOC_CONTIGUOUS) && alloc_state->allocated != alloc_parms->extents_still_needed && (alloc_parms->flags & A_CAN_SPLIT) && (!ah->approx_alloc || pv_maps_size(pvms))); + + return 1; + } +@@ -2366,7 +2430,7 @@ static int _allocate(struct alloc_handle *ah, + old_allocated = alloc_state.allocated; + log_debug_alloc("Trying allocation using %s policy.", get_alloc_string(alloc)); + +- if (!_sufficient_pes_free(ah, pvms, alloc_state.allocated, ah->new_extents)) ++ if (!ah->approx_alloc && !_sufficient_pes_free(ah, pvms, alloc_state.allocated, ah->new_extents)) + goto_out; + + _init_alloc_parms(ah, &alloc_parms, alloc, prev_lvseg, +@@ -2376,19 +2440,36 @@ static int _allocate(struct alloc_handle *ah, + if (!_find_max_parallel_space_for_one_policy(ah, &alloc_parms, pvms, &alloc_state)) + goto_out; + +- if ((alloc_state.allocated == ah->new_extents && !alloc_state.log_area_count_still_needed) || ++ if ((alloc_state.allocated == ah->new_extents && ++ !alloc_state.log_area_count_still_needed) || + (!can_split && (alloc_state.allocated != old_allocated))) + break; + } + + if (alloc_state.allocated != ah->new_extents) { +- log_error("Insufficient suitable %sallocatable extents " +- "for logical volume %s: %u more required", +- can_split ? "" : "contiguous ", +- lv ? lv->name : "", +- (ah->new_extents - alloc_state.allocated) * ah->area_count +- / ah->area_multiple); +- goto out; ++ if (!ah->approx_alloc) { ++ log_error("Insufficient suitable %sallocatable extents " ++ "for logical volume %s: %u more required", ++ can_split ? "" : "contiguous ", ++ lv ? lv->name : "", ++ (ah->new_extents - alloc_state.allocated) * ++ ah->area_count / ah->area_multiple); ++ goto out; ++ } ++ if (!alloc_state.allocated) { ++ log_error("Insufficient suitable %sallocatable extents " ++ "found for logical volume %s.", ++ can_split ? "" : "contiguous ", ++ lv ? lv->name : ""); ++ goto out; ++ } ++ log_verbose("Found fewer %sallocatable extents " ++ "for logical volume %s than requested: using %" PRIu32 " extents (reduced by %u).", ++ can_split ? "" : "contiguous ", ++ lv ? lv->name : "", ++ alloc_state.allocated, ++ (ah->new_extents - alloc_state.allocated) * ah->area_count / ah->area_multiple); ++ ah->new_extents = alloc_state.allocated; + } + + if (alloc_state.log_area_count_still_needed) { +@@ -2465,7 +2546,7 @@ struct alloc_handle *allocate_extents(struct volume_group *vg, + uint32_t mirrors, uint32_t log_count, + uint32_t region_size, uint32_t extents, + struct dm_list *allocatable_pvs, +- alloc_policy_t alloc, ++ alloc_policy_t alloc, int approx_alloc, + struct dm_list *parallel_areas) + { + struct alloc_handle *ah; +@@ -2495,7 +2576,7 @@ struct alloc_handle *allocate_extents(struct volume_group *vg, + alloc = vg->alloc; + + new_extents = (lv ? lv->le_count : 0) + extents; +- if (!(ah = _alloc_init(vg->cmd, vg->cmd->mem, segtype, alloc, ++ if (!(ah = _alloc_init(vg->cmd, vg->cmd->mem, segtype, alloc, approx_alloc, + new_extents, mirrors, stripes, log_count, + vg->extent_size, region_size, + parallel_areas))) +@@ -2955,26 +3036,38 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah, + + /* + * Entry point for single-step LV allocation + extension. ++ * Extents is the number of logical extents to append to the LV unless ++ * approx_alloc is set when it is an upper limit for the total number of ++ * extents to use from the VG. ++ * ++ * FIXME The approx_alloc raid/stripe conversion should be performed ++ * before calling this function. + */ + int lv_extend(struct logical_volume *lv, + const struct segment_type *segtype, + uint32_t stripes, uint32_t stripe_size, + uint32_t mirrors, uint32_t region_size, + uint32_t extents, const char *thin_pool_name, +- struct dm_list *allocatable_pvs, alloc_policy_t alloc) ++ struct dm_list *allocatable_pvs, alloc_policy_t alloc, ++ int approx_alloc) + { + int r = 1; + int log_count = 0; + struct alloc_handle *ah; + uint32_t sub_lv_count; + +- log_very_verbose("Extending segment type, %s", segtype->name); ++ log_very_verbose("Adding segment of type %s to LV %s.", segtype->name, lv->name); + + if (segtype_is_virtual(segtype)) + return lv_add_virtual_segment(lv, 0u, extents, segtype, thin_pool_name); + +- if (!lv->le_count && segtype_is_thin_pool(segtype)) { +- /* Thin pool allocation treats its metadata device like a mirror log. */ ++ if (!lv->le_count && ++ (segtype_is_thin_pool(segtype) || ++ segtype_is_cache_pool(segtype))) { ++ /* ++ * Thinpool and cache_pool allocations treat the metadata ++ * device like a mirror log. ++ */ + /* FIXME Allow pool and data on same device with NORMAL */ + /* FIXME Support striped metadata pool */ + log_count = 1; +@@ -2984,13 +3077,19 @@ int lv_extend(struct logical_volume *lv, + + if (!(ah = allocate_extents(lv->vg, lv, segtype, stripes, mirrors, + log_count, region_size, extents, +- allocatable_pvs, alloc, NULL))) ++ allocatable_pvs, alloc, approx_alloc, NULL))) + return_0; + +- if (segtype_is_thin_pool(segtype)) { ++ if (ah->approx_alloc) { ++ extents = ah->new_extents; ++ if (segtype_is_raid(segtype)) ++ extents -= ah->log_len * ah->area_multiple; ++ } ++ ++ if (segtype_is_thin_pool(segtype) || segtype_is_cache_pool(segtype)) { + if (lv->le_count) { + /* lv_resize abstracts properly _tdata */ +- log_error(INTERNAL_ERROR "Cannot lv_extend() the existing thin pool segment."); ++ log_error(INTERNAL_ERROR "Cannot lv_extend() the existing %s segment.", segtype->name); + return 0; + } + if (!(r = create_pool(lv, segtype, ah, stripes, stripe_size))) +@@ -3307,6 +3406,7 @@ int lv_rename(struct cmd_context *cmd, struct logical_volume *lv, + + #define SIZE_BUF 128 + ++/* TODO: unify stripe size validation across source code */ + static int _validate_stripesize(struct cmd_context *cmd, + const struct volume_group *vg, + struct lvresize_params *lp) +@@ -3320,11 +3420,11 @@ static int _validate_stripesize(struct cmd_context *cmd, + + if (!(vg->fid->fmt->features & FMT_SEGMENTS)) + log_warn("Varied stripesize not supported. Ignoring."); +- else if (lp->ac_stripesize_value > (uint64_t) vg->extent_size * 2) { +- log_error("Reducing stripe size %s to maximum, " +- "physical extent size %s", +- display_size(cmd,lp->ac_stripesize_value), +- display_size(cmd, (uint64_t) vg->extent_size)); ++ else if (lp->ac_stripesize_value > vg->extent_size) { ++ log_print_unless_silent("Reducing stripe size %s to maximum, " ++ "physical extent size %s", ++ display_size(cmd, lp->ac_stripesize_value), ++ display_size(cmd, vg->extent_size)); + lp->stripe_size = vg->extent_size; + } else + lp->stripe_size = lp->ac_stripesize_value; +@@ -3469,7 +3569,7 @@ static int _adjust_policy_params(struct cmd_context *cmd, + return_0; + if ((PERCENT_0 < percent && percent <= PERCENT_100) && + (percent > policy_threshold)) { +- if (!pool_can_resize_metadata(lv)) { ++ if (!thin_pool_feature_supported(lv, THIN_FEATURE_METADATA_RESIZE)) { + log_error_once("Online metadata resize for %s/%s is not supported.", + lp->vg_name, lp->lv_name); + return 0; +@@ -3535,7 +3635,7 @@ static int _lvresize_poolmetadata_prepare(struct cmd_context *cmd, + + lp->poolmetadataextents = 0; + +- if (!pool_can_resize_metadata(pool_lv)) { ++ if (!thin_pool_feature_supported(pool_lv, THIN_FEATURE_METADATA_RESIZE)) { + log_error("Support for online metadata resize not detected."); + return 0; + } +@@ -3603,7 +3703,7 @@ static int _lvresize_poolmetadata(struct cmd_context *cmd, struct volume_group * + seg_mirrors, + mseg->region_size, + lp->poolmetadataextents - lv->le_count, NULL, +- pvh, alloc)) ++ pvh, alloc, 0)) + return_0; + + return 1; +@@ -3716,7 +3816,7 @@ static int _lvresize_adjust_extents(struct cmd_context *cmd, struct logical_volu + { + struct volume_group *vg = lv->vg; + uint32_t pv_extent_count; +- uint32_t extents_used; ++ uint32_t extents_used, extents; + uint32_t seg_stripes = 0, seg_stripesize = 0, seg_size; + uint32_t seg_mirrors = 0; + struct lv_segment *seg, *uninitialized_var(mirr_seg); +@@ -3728,24 +3828,24 @@ static int _lvresize_adjust_extents(struct cmd_context *cmd, struct logical_volu + /* If percent options were used, convert them into actual numbers of extents */ + switch (lp->percent) { + case PERCENT_VG: +- lp->extents = percent_of_extents(lp->extents, vg->extent_count, ++ extents = percent_of_extents(lp->extents, vg->extent_count, + (lp->sign != SIGN_MINUS)); + break; + case PERCENT_FREE: +- lp->extents = percent_of_extents(lp->extents, vg->free_count, ++ extents = percent_of_extents(lp->extents, vg->free_count, + (lp->sign != SIGN_MINUS)); + break; + case PERCENT_LV: +- lp->extents = percent_of_extents(lp->extents, lv->le_count, ++ extents = percent_of_extents(lp->extents, lv->le_count, + (lp->sign != SIGN_MINUS)); + break; + case PERCENT_PVS: + if (lp->argc) { + pv_extent_count = pv_list_extents_free(pvh); +- lp->extents = percent_of_extents(lp->extents, pv_extent_count, ++ extents = percent_of_extents(lp->extents, pv_extent_count, + (lp->sign != SIGN_MINUS)); + } else +- lp->extents = percent_of_extents(lp->extents, vg->extent_count, ++ extents = percent_of_extents(lp->extents, vg->extent_count, + (lp->sign != SIGN_MINUS)); + break; + case PERCENT_ORIGIN: +@@ -3753,11 +3853,23 @@ static int _lvresize_adjust_extents(struct cmd_context *cmd, struct logical_volu + log_error("Specified LV does not have an origin LV."); + return 0; + } +- lp->extents = percent_of_extents(lp->extents, origin_from_cow(lv)->le_count, ++ extents = percent_of_extents(lp->extents, origin_from_cow(lv)->le_count, + (lp->sign != SIGN_MINUS)); + break; + case PERCENT_NONE: ++ extents = lp->extents; + break; ++ default: ++ log_error(INTERNAL_ERROR "Unsupported percent type %u.", lp->percent); ++ return 0; ++ } ++ ++ if (lp->percent != PERCENT_NONE) { ++ log_verbose("Converted %" PRIu32 "%%%s into %" PRIu32 " extents.", lp->extents, get_percent_string(lp->percent), extents); ++ lp->extents = extents; ++ if (lp->sign == SIGN_NONE && (lp->percent != PERCENT_LV && lp->percent != PERCENT_ORIGIN)) ++ lp->approx_alloc = 1; ++ /* FIXME Adjust for parallel areas here before processing relative allocations */ + } + + if (lp->sign == SIGN_PLUS) { +@@ -4028,6 +4140,26 @@ static int _lvresize_check_type(struct cmd_context *cmd, const struct logical_vo + } + } + ++ if (lv_is_thin_volume(lv) && first_seg(lv)->external_lv && ++ (lp->resize == LV_EXTEND)) { ++ /* ++ * TODO: currently we do not support extension of already reduced thin volume. ++ * But it might be possible to create combined mapping of some part of ++ * the external origin followed by zero target. ++ */ ++ if (first_seg(lv)->external_lv->size > lv->size) { ++ log_error("Extension of reduced thin volume with external origin is unsupported."); ++ return 0; ++ } ++ ++ /* Validate thin target supports bigger size of thin volume then external origin */ ++ if (first_seg(lv)->external_lv->size <= lv->size && ++ !thin_pool_feature_supported(first_seg(lv)->pool_lv, THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND)) { ++ log_error("Thin target does not support external origin smaller then thin volume."); ++ return 0; ++ } ++ } ++ + return 1; + } + +@@ -4096,7 +4228,7 @@ static struct logical_volume *_lvresize_volume(struct cmd_context *cmd, + lp->stripes, lp->stripe_size, + lp->mirrors, first_seg(lv)->region_size, + lp->extents - lv->le_count, NULL, +- pvh, alloc)) ++ pvh, alloc, lp->approx_alloc)) + return_NULL; + + if (lock_lv) { +@@ -4157,6 +4289,11 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv, + struct logical_volume *lock_lv = NULL; + int inactive = 0; + ++ if (lv_is_cache_type(lv)) { ++ log_error("Unable to resize logical volumes of cache type."); ++ return 0; ++ } ++ + if (lp->sizeargs && + !(lock_lv = _lvresize_volume(cmd, lv, lp, pvh))) + return_0; +@@ -4502,6 +4639,7 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, + int format1_reload_required = 0; + int visible; + struct logical_volume *pool_lv = NULL; ++ struct lv_segment *cache_seg = NULL; + int ask_discard; + + vg = lv->vg; +@@ -4546,6 +4684,27 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, + } else if (lv_is_thin_volume(lv)) + pool_lv = first_seg(lv)->pool_lv; + ++ /* ++ * If we are removing a cache_pool, we must first unlink ++ * it from any origins (i.e. remove the cache layer). ++ * ++ * If the cache_pool is not linked, we can simply proceed ++ * to remove it. ++ */ ++ if (lv_is_cache_pool(lv) && !dm_list_empty(&lv->segs_using_this_lv)) { ++ if (!(cache_seg = get_only_segment_using_this_lv(lv))) ++ return_0; ++ ++ if (!lv_cache_remove(cache_seg->lv)) ++ return_0; ++ } ++ ++ if (lv_is_cache_pool_data(lv) || lv_is_cache_pool_metadata(lv)) { ++ log_error("Can't remove logical volume %s used by a cache_pool.", ++ lv->name); ++ return 0; ++ } ++ + if (lv->status & LOCKED) { + log_error("Can't remove locked LV %s", lv->name); + return 0; +@@ -4774,12 +4933,14 @@ int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume * + if (lv_is_used_thin_pool(lv) && + !_lv_remove_segs_using_this_lv(cmd, lv, force, level, "pool")) + return_0; +- +- if (lv_is_thin_pool(lv) && lv->vg->pool_metadata_spare_lv) { +- /* When removing last thin pool, remove also spare */ ++ if ((lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) && ++ lv->vg->pool_metadata_spare_lv) { ++ /* When removing last pool, also remove the spare */ + is_last_pool = 1; + dm_list_iterate_items(lvl, &lv->vg->lvs) +- if (lv_is_thin_pool(lvl->lv) && lvl->lv != lv) { ++ if ((lv_is_thin_pool(lvl->lv) || ++ lv_is_cache_pool(lvl->lv)) && ++ lvl->lv != lv) { + is_last_pool = 0; + break; + } +@@ -4792,9 +4953,10 @@ int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume * + + if (lv_is_pool_metadata_spare(lv) && + (force == PROMPT) && +- (yes_no_prompt("Removal of pool metadata spare logical volume \"%s\" " +- "disables automatic recovery attempts after damage " +- "to a thin pool. Proceed? [y/n]: ", lv->name) == 'n')) ++ (yes_no_prompt("Removal of pool metadata spare logical volume" ++ " \"%s\" disables automatic recovery attempts" ++ " after damage to a thin or cache pool." ++ " Proceed? [y/n]: ", lv->name) == 'n')) + goto no_remove; + + return lv_remove_single(cmd, lv, force, 0); +@@ -5061,6 +5223,8 @@ int remove_layer_from_lv(struct logical_volume *lv, + parent->le_count != layer_lv->le_count) + return_0; + ++ //FIXME: why do we empty the parent? It removes everything below. ++ //This makes the function unusable for 'lv_cache_remove' + if (!lv_empty(parent)) + return_0; + +@@ -5448,7 +5612,9 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp) + if (wp.do_wipe_signatures) { + log_verbose("Wiping known signatures on logical volume \"%s/%s\"", + lv->vg->name, lv->name); +- if (!wipe_known_signatures(lv->vg->cmd, dev, name, 0, wp.yes, wp.force)) ++ if (!wipe_known_signatures(lv->vg->cmd, dev, name, 0, ++ TYPE_DM_SNAPSHOT_COW, ++ wp.yes, wp.force)) + stack; + } + +@@ -5458,8 +5624,9 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp) + if (zero_sectors > lv->size) + zero_sectors = lv->size; + +- log_verbose("Clearing start of logical volume \"%s/%s\"", +- lv->vg->name, lv->name); ++ log_verbose("Initializing %s of logical volume \"%s/%s\" with value %d.", ++ display_size(lv->vg->cmd, zero_sectors), ++ lv->vg->name, lv->name, wp.zero_value); + + if (!dev_set(dev, UINT64_C(0), (size_t) zero_sectors << SECTOR_SHIFT, wp.zero_value)) + stack; +@@ -5503,7 +5670,7 @@ static struct logical_volume *_create_virtual_origin(struct cmd_context *cmd, + return_NULL; + + if (!lv_extend(lv, segtype, 1, 0, 1, 0, voriginextents, +- NULL, NULL, ALLOC_INHERIT)) ++ NULL, NULL, ALLOC_INHERIT, 0)) + return_NULL; + + /* store vg on disk(s) */ +@@ -5549,16 +5716,16 @@ void lv_set_activation_skip(struct logical_volume *lv, int override_default, + * of the 'skip' arg supplied instead. + */ + int lv_activation_skip(struct logical_volume *lv, activation_change_t activate, +- int override_lv_skip_flag, int skip) ++ int override_lv_skip_flag) + { +- /* Do not skip deactivation! */ +- if ((activate == CHANGE_AN) || (activate == CHANGE_ALN)) ++ if (!(lv->status & LV_ACTIVATION_SKIP) || ++ !is_change_activating(activate) || /* Do not skip deactivation */ ++ override_lv_skip_flag) + return 0; + +- if (override_lv_skip_flag) +- return skip; +- +- return (lv->status & LV_ACTIVATION_SKIP) ? 1 : 0; ++ log_verbose("ACTIVATON_SKIP flag set for LV %s/%s, skipping activation.", ++ lv->vg->name, lv->name); ++ return 1; + } + + /* Greatest common divisor */ +@@ -5583,8 +5750,8 @@ static unsigned long _lcm(unsigned long n1, unsigned long n2) + return (n1 * n2) / _gcd(n1, n2); + } + +-static int _recalculate_thin_pool_chunk_size_with_dev_hints(struct lvcreate_params *lp, +- struct logical_volume *pool_lv) ++static int _recalculate_pool_chunk_size_with_dev_hints(struct lvcreate_params *lp, ++ struct logical_volume *pool_lv) + { + struct logical_volume *pool_data_lv; + struct lv_segment *seg; +@@ -5592,13 +5759,34 @@ static int _recalculate_thin_pool_chunk_size_with_dev_hints(struct lvcreate_para + struct cmd_context *cmd = pool_lv->vg->cmd; + unsigned long previous_hint = 0, hint = 0; + uint32_t chunk_size = lp->chunk_size; +- uint32_t default_chunk_size = lp->thin_chunk_size_calc_policy == THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE ? +- DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE*2 : DEFAULT_THIN_POOL_CHUNK_SIZE*2; ++ uint32_t default_chunk_size; ++ uint32_t min_chunk_size, max_chunk_size; + +- if (lp->passed_args & PASS_ARG_CHUNK_SIZE || +- find_config_tree_int(cmd, allocation_thin_pool_chunk_size_CFG, NULL)) ++ if (lp->passed_args & PASS_ARG_CHUNK_SIZE) + goto out; + ++ if (seg_is_thin_pool(lp)) { ++ if (find_config_tree_int(cmd, allocation_thin_pool_chunk_size_CFG, NULL)) ++ goto out; ++ ++ min_chunk_size = DM_THIN_MIN_DATA_BLOCK_SIZE; ++ max_chunk_size = DM_THIN_MAX_DATA_BLOCK_SIZE; ++ if (lp->thin_chunk_size_calc_policy == THIN_CHUNK_SIZE_CALC_METHOD_PERFORMANCE) ++ default_chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE_PERFORMANCE*2; ++ else ++ default_chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE*2; ++ } else if (seg_is_cache_pool(lp)) { ++ if (find_config_tree_int(cmd, allocation_cache_pool_chunk_size_CFG, NULL)) ++ goto out; ++ min_chunk_size = DM_CACHE_MIN_DATA_BLOCK_SIZE; ++ max_chunk_size = DM_CACHE_MAX_DATA_BLOCK_SIZE; ++ default_chunk_size = DEFAULT_CACHE_POOL_CHUNK_SIZE*2; ++ } else { ++ log_error(INTERNAL_ERROR "%s is not a thin pool or cache pool", ++ pool_lv->name); ++ return 0; ++ } ++ + pool_data_lv = seg_lv(first_seg(pool_lv), 0); + + dm_list_iterate_items(seg, &pool_data_lv->segments) { +@@ -5616,19 +5804,19 @@ static int _recalculate_thin_pool_chunk_size_with_dev_hints(struct lvcreate_para + } + + if (!hint) { +- log_debug_alloc("No usable device hint found while recalculating " +- "thin pool chunk size for %s.", pool_lv->name); ++ log_debug_alloc("No usable device hint found while recalculating" ++ " thin pool chunk size for %s.", pool_lv->name); + goto out; + } + +- if (hint < DM_THIN_MIN_DATA_BLOCK_SIZE || +- hint > DM_THIN_MAX_DATA_BLOCK_SIZE) { +- log_debug_alloc("Calculated chunk size value of %ld sectors " +- "for thin pool %s is out of allowed range (%d-%d).", +- hint, pool_lv->name, DM_THIN_MIN_DATA_BLOCK_SIZE, +- DM_THIN_MAX_DATA_BLOCK_SIZE); ++ if ((hint < min_chunk_size) || (hint > max_chunk_size)) { ++ log_debug_alloc("Calculated chunk size value of %ld sectors for" ++ " thin pool %s is out of allowed range (%d-%d).", ++ hint, pool_lv->name, ++ min_chunk_size, max_chunk_size); + } else +- chunk_size = hint >= default_chunk_size ? hint : default_chunk_size; ++ chunk_size = (hint >= default_chunk_size) ? ++ hint : default_chunk_size; + out: + first_seg(pool_lv)->chunk_size = chunk_size; + return 1; +@@ -5637,7 +5825,7 @@ out: + static int _should_wipe_lv(struct lvcreate_params *lp, struct logical_volume *lv) { + int r = lp->zero | lp->wipe_signatures; + +- if (!seg_is_thin(lp)) ++ if (!seg_is_thin(lp) && !seg_is_cache_pool(lp)) + return r; + + if (lv_is_thin_volume(lv)) +@@ -5676,9 +5864,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + return NULL; + } + +- if ((segtype_is_mirrored(lp->segtype) || +- segtype_is_raid(lp->segtype) || segtype_is_thin(lp->segtype)) && +- !(vg->fid->fmt->features & FMT_SEGMENTS)) { ++ if (!(vg->fid->fmt->features & FMT_SEGMENTS) && ++ (segtype_is_mirrored(lp->segtype) || ++ segtype_is_raid(lp->segtype) || ++ segtype_is_thin(lp->segtype) || ++ segtype_is_cache(lp->segtype))) { + log_error("Metadata does not support %s segments.", + lp->segtype->name); + return NULL; +@@ -5725,7 +5915,59 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + + status |= lp->permission | VISIBLE_LV; + +- if (seg_is_thin(lp) && lp->snapshot) { ++ if (segtype_is_cache(lp->segtype) && lp->pool) { ++ /* We have the cache_pool, create the origin with cache */ ++ if (!(pool_lv = find_lv(vg, lp->pool))) { ++ log_error("Couldn't find origin volume '%s'.", ++ lp->pool); ++ return NULL; ++ } ++ ++ if (pool_lv->status & LOCKED) { ++ log_error("Caching locked devices is not supported."); ++ return NULL; ++ } ++ ++ if (!lp->extents) { ++ log_error("No size given for new cache origin LV"); ++ return NULL; ++ } ++ ++ if (lp->extents < pool_lv->le_count) { ++ log_error("Origin size cannot be smaller than" ++ " the cache_pool"); ++ return NULL; ++ } ++ ++ if (!(lp->segtype = get_segtype_from_string(vg->cmd, "striped"))) ++ return_0; ++ } else if (segtype_is_cache(lp->segtype) && lp->origin) { ++ /* We have the origin, create the cache_pool and cache */ ++ if (!(org = find_lv(vg, lp->origin))) { ++ log_error("Couldn't find origin volume '%s'.", ++ lp->origin); ++ return NULL; ++ } ++ ++ if (org->status & LOCKED) { ++ log_error("Caching locked devices is not supported."); ++ return NULL; ++ } ++ ++ if (!lp->extents) { ++ log_error("No size given for new cache_pool LV"); ++ return NULL; ++ } ++ ++ if (lp->extents > org->le_count) { ++ log_error("cache-pool size cannot be larger than" ++ " the origin"); ++ return NULL; ++ } ++ if (!(lp->segtype = get_segtype_from_string(vg->cmd, ++ "cache-pool"))) ++ return_0; ++ } else if (seg_is_thin(lp) && lp->snapshot) { + if (!(org = find_lv(vg, lp->origin))) { + log_error("Couldn't find origin volume '%s'.", + lp->origin); +@@ -5806,10 +6048,12 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + return NULL; + } + +- if (seg_is_thin_pool(lp) && +- ((uint64_t)lp->extents * vg->extent_size < lp->chunk_size)) { +- log_error("Unable to create thin pool smaller than 1 chunk."); +- return NULL; ++ if (seg_is_thin_pool(lp) || seg_is_cache_pool(lp)) { ++ if (((uint64_t)lp->extents * vg->extent_size < lp->chunk_size)) { ++ log_error("Unable to create %s smaller than 1 chunk.", ++ lp->segtype->name); ++ return NULL; ++ } + } + + if (lp->snapshot && !seg_is_thin(lp) && +@@ -5824,7 +6068,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + } + + if (!seg_is_virtual(lp) && +- vg->free_count < lp->extents) { ++ vg->free_count < lp->extents && !lp->approx_alloc) { + log_error("Volume group \"%s\" has insufficient free space " + "(%u extents): %u required.", + vg->name, vg->free_count, lp->extents); +@@ -5841,7 +6085,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + if (!activation() && + (seg_is_mirrored(lp) || + seg_is_raid(lp) || +- seg_is_thin_pool(lp))) { ++ seg_is_thin_pool(lp) || ++ seg_is_cache_pool(lp))) { + /* + * FIXME: For thin pool add some code to allow delayed + * initialization of empty thin pool volume. +@@ -5850,9 +6095,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + */ + log_error("Can't create %s without using " + "device-mapper kernel driver.", +- segtype_is_raid(lp->segtype) ? lp->segtype->name : +- segtype_is_mirrored(lp->segtype) ? "mirror" : +- "thin pool volume"); ++ seg_is_thin_pool(lp) ? "thin pool volume" : ++ lp->segtype->name); + return NULL; + } + +@@ -5888,10 +6132,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + return NULL; + } + +- if (lv_is_active(lvl->lv) || +- ((lp->activate != CHANGE_AN) && (lp->activate != CHANGE_ALN))) +- if (!update_pool_lv(lvl->lv, 1)) +- return_NULL; ++ if ((lv_is_active(lvl->lv) || is_change_activating(lp->activate)) && ++ !update_pool_lv(lvl->lv, 1)) ++ return_NULL; + + /* For thin snapshot we must have matching pool */ + if (org && lv_is_thin_volume(org) && (!lp->pool || +@@ -5938,13 +6181,18 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + if (!lv_extend(lv, lp->segtype, + lp->stripes, lp->stripe_size, + lp->mirrors, +- seg_is_thin_pool(lp) ? lp->poolmetadataextents : lp->region_size, ++ (seg_is_thin_pool(lp) || seg_is_cache_pool(lp)) ? ++ lp->poolmetadataextents : lp->region_size, + seg_is_thin_volume(lp) ? lp->voriginextents : lp->extents, +- thin_name, lp->pvh, lp->alloc)) ++ thin_name, lp->pvh, lp->alloc, lp->approx_alloc)) + return_NULL; + +- if (seg_is_thin_pool(lp)) { +- if (!_recalculate_thin_pool_chunk_size_with_dev_hints(lp, lv)) ++ if (seg_is_cache_pool(lp)) { ++ if (!_recalculate_pool_chunk_size_with_dev_hints(lp, lv)) ++ return_NULL; ++ first_seg(lv)->feature_flags = lp->feature_flags; ++ } else if (seg_is_thin_pool(lp)) { ++ if (!_recalculate_pool_chunk_size_with_dev_hints(lp, lv)) + return_NULL; + first_seg(lv)->zero_new_blocks = lp->zero ? 1 : 0; + first_seg(lv)->discards = lp->discards; +@@ -5960,6 +6208,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + * within the same thin pool + */ + if (lp->snapshot && (first_seg(org)->pool_lv != pool_lv)) { ++ if (!pool_supports_external_origin(first_seg(pool_lv), org)) ++ return_0; + if (org->status & LVM_WRITE) { + log_error("Cannot use writable LV as the external origin."); + return 0; // TODO conversion for inactive +@@ -5978,14 +6228,42 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + } else if (seg_is_raid(lp)) { + first_seg(lv)->min_recovery_rate = lp->min_recovery_rate; + first_seg(lv)->max_recovery_rate = lp->max_recovery_rate; +- if (vg_is_clustered(lv->vg) && +- is_change_activating(lp->activate) && +- (lp->activate != CHANGE_AE)) { +- log_debug_activation("Creating RAID logical volume in a" +- " cluster: setting activation" +- " mode to EX"); +- lp->activate = CHANGE_AE; ++ } ++ ++ if (lp->cache) { ++ struct logical_volume *tmp_lv; ++ ++ if (lp->origin) { ++ /* ++ * FIXME: At this point, create_pool has created ++ * the pool and added the data and metadata sub-LVs, ++ * but only the metadata sub-LV is in the kernel - ++ * a suspend/resume cycle is still necessary on the ++ * cache_pool to actualize it in the kernel. ++ * ++ * Should the suspend/resume be added to create_pool? ++ * I say that would be cleaner, but I'm not sure ++ * about the effects on thinpool yet... ++ */ ++ if (!vg_write(vg) || !suspend_lv(cmd, lv) || ++ !vg_commit(vg) || !resume_lv(cmd, lv)) ++ goto deactivate_and_revert_new_lv; ++ ++ if (!(lvl = find_lv_in_vg(vg, lp->origin))) ++ goto deactivate_and_revert_new_lv; ++ org = lvl->lv; ++ pool_lv = lv; ++ } else { ++ if (!(lvl = find_lv_in_vg(vg, lp->pool))) ++ goto deactivate_and_revert_new_lv; ++ pool_lv = lvl->lv; ++ org = lv; + } ++ ++ if (!(tmp_lv = lv_cache_create(pool_lv, org))) ++ goto deactivate_and_revert_new_lv; ++ ++ lv = tmp_lv; + } + + /* FIXME Log allocation and attachment should have happened inside lv_extend. */ +@@ -6010,11 +6288,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + lp->activate = lv_passes_auto_activation_filter(cmd, lv) ? + CHANGE_ALY : CHANGE_ALN; + +- if (lv_activation_skip(lv, lp->activate, lp->activation_skip & ACTIVATION_SKIP_IGNORE, 0)) { +- log_verbose("ACTIVATION_SKIP flag set for LV %s/%s, skipping activation.", +- lv->vg->name, lv->name); ++ if (lv_activation_skip(lv, lp->activate, lp->activation_skip & ACTIVATION_SKIP_IGNORE)) + lp->activate = CHANGE_AN; +- } + + /* + * For thin pools - deactivate when inactive pool is requested or +@@ -6047,7 +6322,25 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + if (lp->temporary) + lv->status |= LV_TEMPORARY; + +- if (lv_is_thin_pool(lv)) { ++ if (lv_is_cache_type(lv)) { ++ if (!lv_is_active(lv)) { ++ if (!activate_lv_excl(cmd, lv)) { ++ log_error("Failed to activate pool %s.", ++ lv->name); ++ goto deactivate_and_revert_new_lv; ++ } ++ } else { ++ if (!suspend_lv(cmd, lv)) { ++ log_error("Failed to suspend pool %s.", ++ lv->name); ++ goto deactivate_and_revert_new_lv; ++ } ++ if (!resume_lv(cmd, lv)) { ++ log_error("Failed to resume pool %s.", lv->name); ++ goto deactivate_and_revert_new_lv; ++ } ++ } ++ } else if (lv_is_thin_pool(lv)) { + if (is_change_activating(lp->activate)) { + if (vg_is_clustered(lv->vg)) { + if (!activate_lv_excl(cmd, lv)) { +@@ -6227,7 +6520,7 @@ struct logical_volume *lv_create_single(struct volume_group *vg, + struct logical_volume *lv; + + /* Create thin pool first if necessary */ +- if (lp->create_thin_pool) { ++ if (lp->create_pool && !seg_is_cache_pool(lp) && !seg_is_cache(lp)) { + if (!seg_is_thin_pool(lp) && + !(lp->segtype = get_segtype_from_string(vg->cmd, "thin-pool"))) + return_0; +diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c +index 93e4bfa..f6a14a8 100644 +--- a/lib/metadata/merge.c ++++ b/lib/metadata/merge.c +@@ -190,31 +190,54 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg) + inc_error_count; + } + +- if (seg->area_count != 1 || seg_type(seg, 0) != AREA_LV) { +- log_error("LV %s: thin pool segment %u is missing a pool data LV", +- lv->name, seg_count); ++ } ++ if (seg_is_thin_pool(seg) || seg_is_cache_pool(seg)) { ++ if (seg->area_count != 1 || ++ seg_type(seg, 0) != AREA_LV) { ++ log_error("LV %s: %spool segment %u is missing a pool data LV", ++ lv->name, ++ seg_is_thin_pool(seg) ? ++ "thin " : "cache", ++ seg_count); + inc_error_count; + } else if (!(seg2 = first_seg(seg_lv(seg, 0))) || find_pool_seg(seg2) != seg) { +- log_error("LV %s: thin pool segment %u data LV does not refer back to pool LV", +- lv->name, seg_count); ++ log_error("LV %s: %spool segment %u data LV does not refer back to pool LV", ++ lv->name, ++ seg_is_thin_pool(seg) ? ++ "thin " : "cache", ++ seg_count); + inc_error_count; + } + + if (!seg->metadata_lv) { +- log_error("LV %s: thin pool segment %u is missing a pool metadata LV", +- lv->name, seg_count); ++ log_error("LV %s: %spool segment %u is missing a pool metadata LV", ++ lv->name, ++ seg_is_thin_pool(seg) ? ++ "thin " : "cache", ++ seg_count); + inc_error_count; + } else if (!(seg2 = first_seg(seg->metadata_lv)) || + find_pool_seg(seg2) != seg) { +- log_error("LV %s: thin pool segment %u metadata LV does not refer back to pool LV", +- lv->name, seg_count); ++ log_error("LV %s: %spool segment %u metadata LV does not refer back to pool LV", ++ lv->name, ++ seg_is_thin_pool(seg) ? ++ "thin " : "cache", ++ seg_count); + inc_error_count; + } + +- if (seg->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE || +- seg->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE) { +- log_error("LV %s: thin pool segment %u has chunk size %u out of range.", +- lv->name, seg_count, seg->chunk_size); ++ if ((seg_is_thin_pool(seg) && ++ ((seg->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) || ++ (seg->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE))) || ++ (seg_is_cache_pool(seg) && ++ ((seg->chunk_size < DM_CACHE_MIN_DATA_BLOCK_SIZE) || ++ (seg->chunk_size > DM_CACHE_MAX_DATA_BLOCK_SIZE)))) ++{ ++ log_error("LV %s: %spool segment %u has chunk size %u out of range.", ++ lv->name, ++ seg_is_thin_pool(seg) ? ++ "thin " : "cache", ++ seg_count, seg->chunk_size); + inc_error_count; + } + } else { +@@ -271,6 +294,17 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg) + inc_error_count; + } + } ++ } else if (seg_is_cache(seg)) { ++ if (!lv_is_cache(lv)) { ++ log_error("LV %s is missing cache flag for segment %u", ++ lv->name, seg_count); ++ inc_error_count; ++ } ++ if (!seg->pool_lv) { ++ log_error("LV %s: segment %u is missing cache_pool LV", ++ lv->name, seg_count); ++ inc_error_count; ++ } + } else { + if (seg->pool_lv) { + log_error("LV %s: segment %u must not have thin pool LV set", +diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h +index 8e36891..87e6046 100644 +--- a/lib/metadata/metadata-exported.h ++++ b/lib/metadata/metadata-exported.h +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * +@@ -105,9 +105,15 @@ + #define LV_NOSCAN UINT64_C(0x0000080000000000) /* LV - internal use only - the LV + should not be scanned */ + #define LV_TEMPORARY UINT64_C(0x0000100000000000) /* LV - internal use only - the LV +- is supposed to be created and +- removed during single LVM +- command execution. */ ++ is supposed to be created and ++ removed or reactivated with ++ this flag dropped during single ++ LVM command execution. */ ++ ++#define CACHE_POOL UINT64_C(0x0000200000000000) /* LV */ ++#define CACHE_POOL_DATA UINT64_C(0x0000400000000000) /* LV */ ++#define CACHE_POOL_METADATA UINT64_C(0x0000800000000000) /* LV */ ++#define CACHE UINT64_C(0x0001000000000000) /* LV */ + + /* Format features flags */ + #define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ +@@ -168,6 +174,12 @@ + #define lv_is_raid(lv) (((lv)->status & (RAID)) ? 1 : 0) + #define lv_is_raid_type(lv) (((lv)->status & (RAID | RAID_IMAGE | RAID_META)) ? 1 : 0) + ++#define lv_is_cache(lv) (((lv)->status & (CACHE)) ? 1 : 0) ++#define lv_is_cache_pool(lv) (((lv)->status & (CACHE_POOL)) ? 1 : 0) ++#define lv_is_cache_pool_data(lv) (((lv)->status & (CACHE_POOL_DATA)) ? 1 : 0) ++#define lv_is_cache_pool_metadata(lv) (((lv)->status & (CACHE_POOL_METADATA)) ? 1 : 0) ++#define lv_is_cache_type(lv) (((lv)->status & (CACHE | CACHE_POOL | CACHE_POOL_DATA | CACHE_POOL_METADATA)) ? 1 : 0) ++ + #define lv_is_virtual(lv) (((lv)->status & VIRTUAL) ? 1 : 0) + #define lv_is_pool_metadata_spare(lv) (((lv)->status & POOL_METADATA_SPARE) ? 1 : 0) + +@@ -384,6 +396,13 @@ struct lv_segment { + struct logical_volume *pool_lv; /* For thin */ + uint32_t device_id; /* For thin, 24bit */ + ++ uint32_t feature_flags; /* For cache */ ++ unsigned core_argc; /* For cache */ ++ char **core_argv; /* For cache */ ++ char *policy_name; /* For cache */ ++ unsigned policy_argc; /* For cache */ ++ char **policy_argv; /* For cache */ ++ + struct logical_volume *replicator;/* For replicator-devs - link to replicator LV */ + struct logical_volume *rlog_lv; /* For replicators */ + const char *rlog_type; /* For replicators */ +@@ -465,6 +484,7 @@ struct lvresize_params { + uint64_t poolmetadatasize; + sign_t poolmetadatasign; + uint32_t poolmetadataextents; ++ int approx_alloc; + percent_type_t percent; + + enum { +@@ -653,7 +673,8 @@ int lv_extend(struct logical_volume *lv, + uint32_t stripes, uint32_t stripe_size, + uint32_t mirrors, uint32_t region_size, + uint32_t extents, const char *thin_pool_name, +- struct dm_list *allocatable_pvs, alloc_policy_t alloc); ++ struct dm_list *allocatable_pvs, alloc_policy_t alloc, ++ int approx_alloc); + + /* lv must be part of lv->vg->lvs */ + int lv_remove(struct logical_volume *lv); +@@ -674,17 +695,19 @@ uint64_t extents_from_size(struct cmd_context *cmd, uint64_t size, + + struct logical_volume *find_pool_lv(const struct logical_volume *lv); + int pool_is_active(const struct logical_volume *pool_lv); +-int pool_can_resize_metadata(const struct logical_volume *pool_lv); ++int pool_supports_external_origin(const struct lv_segment *pool_seg, const struct logical_volume *external_lv); ++int thin_pool_feature_supported(const struct logical_volume *pool_lv, int feature); + int update_pool_lv(struct logical_volume *lv, int activate); + int update_profilable_pool_params(struct cmd_context *cmd, struct profile *profile, + int passed_args, int *chunk_size_calc_method, + uint32_t *chunk_size, thin_discards_t *discards, + int *zero); +-int update_pool_params(struct volume_group *vg, unsigned attr, int passed_args, +- uint32_t data_extents, uint32_t extent_size, +- int *chunk_size_calc_method, uint32_t *chunk_size, +- thin_discards_t *discards, +- uint64_t *pool_metadata_size, int *zero); ++int update_thin_pool_params(struct volume_group *vg, unsigned attr, ++ int passed_args, ++ uint32_t data_extents, uint32_t extent_size, ++ int *chunk_size_calc_method, uint32_t *chunk_size, ++ thin_discards_t *discards, ++ uint64_t *pool_metadata_size, int *zero); + int get_pool_discards(const char *str, thin_discards_t *discards); + const char *get_pool_discards_name(thin_discards_t discards); + struct logical_volume *alloc_pool_metadata(struct logical_volume *pool_lv, +@@ -729,9 +752,10 @@ static inline int is_change_activating(activation_change_t change) + /* FIXME: refactor and reduce the size of this struct! */ + struct lvcreate_params { + /* flags */ ++ int cache; + int snapshot; /* snap */ + int thin; /* thin */ +- int create_thin_pool; /* thin */ ++ int create_pool; /* thin */ + int zero; /* all */ + int wipe_signatures; /* all */ + int major; /* all */ +@@ -773,6 +797,8 @@ struct lvcreate_params { + uint32_t min_recovery_rate; /* RAID */ + uint32_t max_recovery_rate; /* RAID */ + ++ uint32_t feature_flags; /* cache */ ++ + const struct segment_type *segtype; /* all */ + unsigned target_attr; /* all */ + +@@ -786,6 +812,7 @@ struct lvcreate_params { + + uint32_t permission; /* all */ + uint32_t read_ahead; /* all */ ++ int approx_alloc; /* all */ + alloc_policy_t alloc; /* all */ + + struct dm_list tags; /* all */ +@@ -806,7 +833,7 @@ struct logical_volume *lv_create_single(struct volume_group *vg, + */ + void lv_set_activation_skip(struct logical_volume *lv, int override_default, int add_skip_flag); + int lv_activation_skip(struct logical_volume *lv, activation_change_t activate, +- int override_lv_skip_flag, int skip); ++ int override_lv_skip_flag); + + /* + * Functions for layer manipulation +@@ -996,9 +1023,20 @@ int lv_raid_reshape(struct logical_volume *lv, + int lv_raid_replace(struct logical_volume *lv, struct dm_list *remove_pvs, + struct dm_list *allocate_pvs); + int lv_raid_remove_missing(struct logical_volume *lv); +- + /* -- metadata/raid_manip.c */ + ++/* ++ metadata/cache_manip.c */ ++int update_cache_pool_params(struct volume_group *vg, unsigned attr, ++ int passed_args, ++ uint32_t data_extents, uint32_t extent_size, ++ int *chunk_size_calc_method, uint32_t *chunk_size, ++ thin_discards_t *discards, ++ uint64_t *pool_metadata_size, int *zero); ++struct logical_volume *lv_cache_create(struct logical_volume *pool, ++ struct logical_volume *origin); ++int lv_cache_remove(struct logical_volume *cache_lv); ++/* -- metadata/cache_manip.c */ ++ + struct cmd_vg *cmd_vg_add(struct dm_pool *mem, struct dm_list *cmd_vgs, + const char *vg_name, const char *vgid, + uint32_t flags); +diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c +index 4d4778b..8643b75 100644 +--- a/lib/metadata/metadata.c ++++ b/lib/metadata/metadata.c +@@ -856,30 +856,45 @@ int vgcreate_params_validate(struct cmd_context *cmd, + return 1; + } + +-static int _vg_update_vg_ondisk(struct volume_group *vg) ++/* ++ * Update content of precommitted VG ++ * ++ * TODO: Optimize in the future, since lvmetad needs similar ++ * config tree processing in lvmetad_vg_update(). ++ */ ++static int _vg_update_vg_precommitted(struct volume_group *vg) + { + struct dm_config_tree *cft; +- int pool_locked; + +- if (vg->vg_ondisk || is_orphan_vg(vg->name)) /* we already have it */ +- return 1; ++ release_vg(vg->vg_precommitted); ++ vg->vg_precommitted = NULL; + +- pool_locked = dm_pool_locked(vg->vgmem); +- if (pool_locked && !dm_pool_unlock(vg->vgmem, 0)) ++ if (!(cft = export_vg_to_config_tree(vg))) + return_0; + +- cft = export_vg_to_config_tree(vg); +- if (!cft) +- return 0; ++ if (!(vg->vg_precommitted = import_vg_from_config_tree(cft, vg->fid))) ++ stack; + +- vg->vg_ondisk = import_vg_from_config_tree(cft, vg->fid); + dm_config_destroy(cft); + +- /* recompute the pool crc */ +- if (pool_locked && !dm_pool_lock(vg->vgmem, 1)) ++ return vg->vg_precommitted ? 1 : 0; ++} ++ ++static int _vg_update_vg_ondisk(struct volume_group *vg) ++{ ++ if (dm_pool_locked(vg->vgmem)) ++ return 1; ++ ++ if (vg->vg_ondisk || is_orphan_vg(vg->name)) /* we already have it */ ++ return 1; ++ ++ if (!_vg_update_vg_precommitted(vg)) + return_0; + +- return vg->vg_ondisk ? 1 : 0; ++ vg->vg_ondisk = vg->vg_precommitted; ++ vg->vg_precommitted = NULL; ++ ++ return 1; + } + + /* +@@ -1322,8 +1337,7 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name, + /* FIXME Check partition type is LVM unless --force is given */ + + /* Is there a pv here already? */ +- if (!(pv = find_pv_by_name(cmd, name, 1, 1))) +- stack; ++ pv = find_pv_by_name(cmd, name, 1, 1); + + /* Allow partial & exported VGs to be destroyed. */ + /* We must have -ff to overwrite a non orphan */ +@@ -1372,7 +1386,9 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name, + goto bad; + } + +- if (!wipe_known_signatures(cmd, dev, name, 1, pp->yes, pp->force)) { ++ if (!wipe_known_signatures(cmd, dev, name, ++ TYPE_LVM1_MEMBER | TYPE_LVM2_MEMBER, ++ 0, pp->yes, pp->force)) { + log_error("Aborting pvcreate on %s.", name); + goto bad; + } +@@ -2686,6 +2702,9 @@ int vg_write(struct volume_group *vg) + if (!(vg->fid->fmt->features & FMT_PRECOMMIT) && !lvmetad_vg_update(vg)) + return_0; + ++ if (!_vg_update_vg_precommitted(vg)) /* prepare precommited */ ++ return_0; ++ + return 1; + } + +@@ -2752,9 +2771,8 @@ int vg_commit(struct volume_group *vg) + + /* This *is* the original now that it's commited. */ + release_vg(vg->vg_ondisk); +- vg->vg_ondisk = NULL; +- if (!_vg_update_vg_ondisk(vg)) /* make a new one for future edits */ +- return_0; ++ vg->vg_ondisk = vg->vg_precommitted; ++ vg->vg_precommitted = NULL; + } + + /* If update failed, remove any cached precommitted metadata. */ +@@ -2771,6 +2789,9 @@ void vg_revert(struct volume_group *vg) + { + struct metadata_area *mda; + ++ release_vg(vg->vg_precommitted); /* VG is no longer needed */ ++ vg->vg_precommitted = NULL; ++ + dm_list_iterate_items(mda, &vg->fid->metadata_areas_in_use) { + if (mda->ops->vg_revert && + !mda->ops->vg_revert(vg->fid, vg, mda)) { +@@ -4653,7 +4674,7 @@ int pv_change_metadataignore(struct physical_volume *pv, uint32_t mda_ignored) + return 1; + } + +-char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags) ++char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tagsl) + { + struct str_list *sl; + +@@ -4662,9 +4683,9 @@ char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags) + return NULL; + } + +- dm_list_iterate_items(sl, tags) { ++ dm_list_iterate_items(sl, tagsl) { + if (!dm_pool_grow_object(mem, sl->str, strlen(sl->str)) || +- (sl->list.n != tags && !dm_pool_grow_object(mem, ",", 1))) { ++ (sl->list.n != tagsl && !dm_pool_grow_object(mem, ",", 1))) { + log_error("dm_pool_grow_object failed"); + return NULL; + } +diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h +index fef4b3f..83959bb 100644 +--- a/lib/metadata/metadata.h ++++ b/lib/metadata/metadata.h +@@ -488,6 +488,6 @@ int add_pv_to_vg(struct volume_group *vg, const char *pv_name, + struct physical_volume *pv, struct pvcreate_params *pp); + + uint64_t find_min_mda_size(struct dm_list *mdas); +-char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags); ++char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tagsl); + + #endif +diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c +index 18917d3..86ec4ca 100644 +--- a/lib/metadata/mirror.c ++++ b/lib/metadata/mirror.c +@@ -279,7 +279,7 @@ static int _write_log_header(struct cmd_context *cmd, struct logical_volume *lv) + */ + static int _init_mirror_log(struct cmd_context *cmd, + struct logical_volume *log_lv, int in_sync, +- struct dm_list *tags, int remove_on_failure) ++ struct dm_list *tagsl, int remove_on_failure) + { + struct str_list *sl; + uint64_t orig_status = log_lv->status; +@@ -315,7 +315,7 @@ static int _init_mirror_log(struct cmd_context *cmd, + lv_set_visible(log_lv); + + /* Temporary tag mirror log for activation */ +- dm_list_iterate_items(sl, tags) ++ dm_list_iterate_items(sl, tagsl) + if (!str_list_add(cmd->mem, &log_lv->tags, sl->str)) { + log_error("Aborting. Unable to tag mirror log."); + goto activate_lv; +@@ -336,7 +336,7 @@ static int _init_mirror_log(struct cmd_context *cmd, + } + + /* Remove the temporary tags */ +- dm_list_iterate_items(sl, tags) ++ dm_list_iterate_items(sl, tagsl) + str_list_del(&log_lv->tags, sl->str); + + if (activation()) { +@@ -376,7 +376,7 @@ deactivate_and_revert_new_lv: + revert_new_lv: + log_lv->status = orig_status; + +- dm_list_iterate_items(sl, tags) ++ dm_list_iterate_items(sl, tagsl) + str_list_del(&log_lv->tags, sl->str); + + if (remove_on_failure && !lv_remove(log_lv)) { +@@ -1676,7 +1676,7 @@ int add_mirrors_to_segments(struct cmd_context *cmd, struct logical_volume *lv, + region_size); + + if (!(ah = allocate_extents(lv->vg, NULL, segtype, 1, mirrors, 0, 0, +- lv->le_count, allocatable_pvs, alloc, ++ lv->le_count, allocatable_pvs, alloc, 0, + parallel_areas))) { + log_error("Unable to allocate mirror extents for %s.", lv->name); + return 0; +@@ -1944,7 +1944,7 @@ int add_mirror_log(struct cmd_context *cmd, struct logical_volume *lv, + ah = allocate_extents(lv->vg, NULL, segtype, + 0, 0, log_count - old_log_count, region_size, + lv->le_count, allocatable_pvs, +- alloc, parallel_areas); ++ alloc, 0, parallel_areas); + if (!ah) { + log_error("Unable to allocate extents for mirror log."); + return 0; +@@ -2008,7 +2008,7 @@ int add_mirror_images(struct cmd_context *cmd, struct logical_volume *lv, + + ah = allocate_extents(lv->vg, NULL, segtype, + stripes, mirrors, log_count, region_size, lv->le_count, +- allocatable_pvs, alloc, parallel_areas); ++ allocatable_pvs, alloc, 0, parallel_areas); + if (!ah) { + log_error("Unable to allocate extents for mirror(s)."); + return 0; +diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c +new file mode 100644 +index 0000000..0c7bf96 +--- /dev/null ++++ b/lib/metadata/pool_manip.c +@@ -0,0 +1,325 @@ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* ++ * This file holds common pool functions. ++ */ ++ ++#include "lib.h" ++#include "activate.h" ++#include "locking.h" ++#include "metadata.h" ++#include "segtype.h" ++#include "lv_alloc.h" ++#include "defaults.h" ++ ++int attach_pool_metadata_lv(struct lv_segment *pool_seg, ++ struct logical_volume *metadata_lv) ++{ ++ if (!seg_is_thin_pool(pool_seg) && !seg_is_cache_pool(pool_seg)) { ++ log_error(INTERNAL_ERROR ++ "Unable to attach pool metadata LV to %s segtype.", ++ pool_seg->segtype->ops->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 attach_pool_data_lv(struct lv_segment *pool_seg, ++ struct logical_volume *pool_data_lv) ++{ ++ if (!seg_is_thin_pool(pool_seg) && !seg_is_cache_pool(pool_seg)) { ++ log_error(INTERNAL_ERROR ++ "Unable to attach pool data LV to %s segtype.", ++ pool_seg->segtype->ops->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 logical_volume *merge_lv) ++{ ++ 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 (origin && !add_seg_to_segs_using_this_lv(origin, seg)) ++ return_0; ++ ++ 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; ++} ++ ++int detach_pool_lv(struct lv_segment *seg) ++{ ++ 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.", ++ seg->segtype->ops->name(seg), seg->lv->name); ++ return 0; ++ } ++ ++ if (seg_is_cache(seg)) { ++ if (!remove_seg_from_segs_using_this_lv(seg->pool_lv, seg)) ++ return_0; ++ seg->pool_lv = NULL; ++ return 1; ++ } ++ ++ if (!lv_is_thin_pool(seg->pool_lv)) { ++ log_error(INTERNAL_ERROR ++ "Cannot detach pool from LV %s.", ++ seg->lv->name); ++ 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.", ++ tmsg->u.lv->name); ++ 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 (!detach_thin_external_origin(seg)) ++ return_0; ++ ++ if (!attach_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 (!remove_seg_from_segs_using_this_lv(seg->lv, sl->seg)) ++ return_0; ++ /* Thin snapshot is now regular thin volume */ ++ sl->seg->origin = NULL; ++ } ++ ++ seg->lv->status &= ~THIN_VOLUME; ++ seg->pool_lv = NULL; ++ seg->origin = NULL; ++ ++ return 1; ++} ++ ++struct lv_segment *find_pool_seg(const struct lv_segment *seg) ++{ ++ struct lv_segment *pool_seg; ++ ++ pool_seg = get_only_segment_using_this_lv(seg->lv); ++ ++ if (!pool_seg) { ++ log_error("Failed to find pool_seg for %s", seg->lv->name); ++ return NULL; ++ } ++ ++ if ((lv_is_thin_type(seg->lv) && !seg_is_thin_pool(pool_seg)) && ++ !seg_is_cache_pool(pool_seg)) { ++ log_error("%s on %s is not a %s pool segment", ++ pool_seg->lv->name, seg->lv->name, ++ lv_is_thin_type(seg->lv) ? "thin" : "cache"); ++ return NULL; ++ } ++ ++ return pool_seg; ++} ++ ++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]; ++ ++ if (pool_lv->le_count) { ++ log_error(INTERNAL_ERROR "Pool %s already has extents.", ++ pool_lv->name); ++ return 0; ++ } ++ ++ /* LV is not yet a pool, so it's extension from lvcreate */ ++ if (!(striped = get_segtype_from_string(pool_lv->vg->cmd, "striped"))) ++ return_0; ++ ++ if (activation() && segtype->ops->target_present && ++ !segtype->ops->target_present(pool_lv->vg->cmd, NULL, NULL)) { ++ log_error("%s: Required device-mapper target(s) not " ++ "detected in your kernel.", segtype->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.", ++ pool_lv->name); ++ else { ++ 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_local(pool_lv->vg->cmd, pool_lv) || ++ /* Clear 4KB of metadata device for new thin-pool. */ ++ !wipe_lv(pool_lv, (struct wipe_params) { .do_zero = 1 })) { ++ log_error("Aborting. Failed to wipe pool metadata %s.", ++ pool_lv->name); ++ goto bad; ++ } ++ pool_lv->status &= ~LV_TEMPORARY; ++ } ++ ++ if (dm_snprintf(name, sizeof(name), "%s_%s", pool_lv->name, ++ (segtype_is_cache_pool(segtype)) ? ++ "cmeta" : "tmeta") < 0) { ++ log_error("Name is too long to be a pool name."); ++ 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()) { ++ if (deactivate_lv_local(pool_lv->vg->cmd, pool_lv)) { ++ log_error("Aborting. Could not deactivate pool %s.", ++ pool_lv->name); ++ return 0; ++ } ++ 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; ++} +diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c +index a29ea85..d1759ae 100644 +--- a/lib/metadata/raid_manip.c ++++ b/lib/metadata/raid_manip.c +@@ -174,11 +174,13 @@ static int _clear_lv(struct logical_volume *lv) + if (test_mode()) + return 1; + +- if (!was_active && !activate_lv_excl_local(lv->vg->cmd, lv)) { +- log_error("Failed to activate %s for clearing", ++ lv->status |= LV_TEMPORARY; ++ if (!was_active && !activate_lv_local(lv->vg->cmd, lv)) { ++ log_error("Failed to activate localy %s for clearing", + lv->name); + return 0; + } ++ lv->status &= ~LV_TEMPORARY; + + log_verbose("Clearing metadata area of %s/%s", + lv->vg->name, lv->name); +@@ -417,7 +419,7 @@ static int _alloc_image_components(struct logical_volume *lv, + + if (!(ah = allocate_extents(lv->vg, NULL, segtype, 0, count, count, + region_size, extents, pvs, +- lv->alloc, parallel_areas))) ++ lv->alloc, 0, parallel_areas))) + return_0; + + for (s = 0; s < count; s++) { +@@ -481,7 +483,7 @@ static int _alloc_rmeta_for_lv(struct logical_volume *data_lv, + if (!(ah = allocate_extents(data_lv->vg, NULL, seg->segtype, 0, 1, 0, + seg->region_size, + 1 /*RAID_METADATA_AREA_LEN*/, +- &allocatable_pvs, data_lv->alloc, NULL))) ++ &allocatable_pvs, data_lv->alloc, 0, NULL))) + return_0; + + if (!_alloc_image_component(data_lv, base_name, ah, 0, +diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h +index 2c02d1b..75429b4 100644 +--- a/lib/metadata/segtype.h ++++ b/lib/metadata/segtype.h +@@ -41,8 +41,12 @@ struct dev_manager; + #define SEG_RAID 0x00000400U + #define SEG_THIN_POOL 0x00000800U + #define SEG_THIN_VOLUME 0x00001000U ++#define SEG_CACHE 0x00002000U ++#define SEG_CACHE_POOL 0x00004000U + #define SEG_UNKNOWN 0x80000000U + ++#define segtype_is_cache(segtype) ((segtype)->flags & SEG_CACHE ? 1 : 0) ++#define segtype_is_cache_pool(segtype) ((segtype)->flags & SEG_CACHE_POOL ? 1 : 0) + #define segtype_is_mirrored(segtype) ((segtype)->flags & SEG_AREAS_MIRRORED ? 1 : 0) + #define segtype_is_raid(segtype) ((segtype)->flags & SEG_RAID ? 1 : 0) + #define segtype_is_striped(segtype) ((segtype)->flags & SEG_AREAS_STRIPED ? 1 : 0) +@@ -51,6 +55,8 @@ struct dev_manager; + #define segtype_is_thin_volume(segtype) ((segtype)->flags & SEG_THIN_VOLUME ? 1 : 0) + #define segtype_is_virtual(segtype) ((segtype)->flags & SEG_VIRTUAL ? 1 : 0) + ++#define seg_is_cache(seg) segtype_is_cache((seg)->segtype) ++#define seg_is_cache_pool(seg) segtype_is_cache_pool((seg)->segtype) + #define seg_is_linear(seg) (seg_is_striped(seg) && ((seg)->area_count == 1)) + #define seg_is_mirrored(seg) segtype_is_mirrored((seg)->segtype) + #define seg_is_raid(seg) segtype_is_raid((seg)->segtype) +@@ -147,6 +153,10 @@ int init_replicator_segtype(struct cmd_context *cmd, struct segtype_library *seg + int init_thin_segtypes(struct cmd_context *cmd, struct segtype_library *seglib); + #endif + ++#ifdef CACHE_INTERNAL ++int init_cache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib); ++#endif ++ + #ifdef SNAPSHOT_INTERNAL + struct segment_type *init_snapshot_segtype(struct cmd_context *cmd); + #endif +diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c +index e563a78..25fee76 100644 +--- a/lib/metadata/snapshot_manip.c ++++ b/lib/metadata/snapshot_manip.c +@@ -15,6 +15,7 @@ + + #include "lib.h" + #include "metadata.h" ++#include "segtype.h" + #include "locking.h" + #include "toolcontext.h" + #include "lv_alloc.h" +@@ -31,7 +32,26 @@ int lv_is_cow(const struct logical_volume *lv) + return (!lv_is_thin_volume(lv) && !lv_is_origin(lv) && lv->snapshot) ? 1 : 0; + } + +-static uint64_t _cow_max_size(uint64_t origin_size, uint32_t chunk_size) ++/* ++ * Some kernels have a bug that they may leak space in the snapshot on crash. ++ * If the kernel is buggy, we add some extra space. ++ */ ++static uint64_t _cow_extra_chunks(struct cmd_context *cmd, uint64_t n_chunks) ++{ ++ const struct segment_type *segtype; ++ unsigned attrs; ++ ++ if (activation() && ++ (segtype = get_segtype_from_string(cmd, "snapshot")) && ++ segtype->ops->target_present && ++ segtype->ops->target_present(cmd, NULL, &attrs) && ++ (attrs & SNAPSHOT_FEATURE_FIXED_LEAK)) ++ return (n_chunks + 63) / 64; ++ ++ return 0; ++} ++ ++static uint64_t _cow_max_size(struct cmd_context *cmd, uint64_t origin_size, uint32_t chunk_size) + { + /* Snapshot disk layout: + * COW is divided into chunks +@@ -40,21 +60,22 @@ static uint64_t _cow_max_size(uint64_t origin_size, uint32_t chunk_size) + * 3rd. chunk is the 1st. data chunk + */ + +- /* Size of metadata for snapshot in sectors */ +- uint64_t mdata_size = ((origin_size + chunk_size - 1) / chunk_size * 16 + 511) >> SECTOR_SHIFT; ++ uint64_t origin_chunks = (origin_size + chunk_size - 1) / chunk_size; ++ uint64_t chunks_per_metadata_area = (uint64_t)chunk_size << (SECTOR_SHIFT - 4); + +- /* Sum all chunks - header + metadata size + origin size (aligned on chunk boundary) */ +- uint64_t size = chunk_size + +- ((mdata_size + chunk_size - 1) & ~(uint64_t)(chunk_size - 1)) + +- ((origin_size + chunk_size - 1) & ~(uint64_t)(chunk_size - 1)); ++ /* ++ * Note: if origin_chunks is divisible by chunks_per_metadata_area, we ++ * need one extra metadata chunk as a terminator. ++ */ ++ uint64_t metadata_chunks = (origin_chunks + chunks_per_metadata_area) / chunks_per_metadata_area; ++ uint64_t n_chunks = 1 + origin_chunks + metadata_chunks; + +- /* Does not overflow since size is in sectors (9 bits) */ +- return size; ++ return (n_chunks + _cow_extra_chunks(cmd, n_chunks)) * chunk_size; + } + + uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size) + { +- uint64_t size = _cow_max_size(origin->size, chunk_size); ++ uint64_t size = _cow_max_size(origin->vg->cmd, origin->size, chunk_size); + uint32_t extent_size = origin->vg->extent_size; + uint64_t max_size = (uint64_t) MAX_EXTENT_COUNT * extent_size; + +@@ -70,7 +91,8 @@ uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_siz + int lv_is_cow_covering_origin(const struct logical_volume *lv) + { + return lv_is_cow(lv) && +- (lv->size >= _cow_max_size(origin_from_cow(lv)->size, find_snapshot(lv)->chunk_size)); ++ (lv->size >= _cow_max_size(lv->vg->cmd, origin_from_cow(lv)->size, ++ find_snapshot(lv)->chunk_size)); + } + + int lv_is_visible(const struct logical_volume *lv) +@@ -151,7 +173,7 @@ void init_snapshot_merge(struct lv_segment *snap_seg, + origin->snapshot = snap_seg; + origin->status |= MERGING; + +- if (lv_is_thin_volume(origin)) { ++ if (seg_is_thin_volume(snap_seg)) { + snap_seg->merge_lv = origin; + /* Making thin LV inivisible with regular log */ + lv_set_hidden(snap_seg->lv); +diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c +index e12a8ad..b28f5a0 100644 +--- a/lib/metadata/thin_manip.c ++++ b/lib/metadata/thin_manip.c +@@ -17,19 +17,9 @@ + #include "locking.h" + #include "metadata.h" + #include "segtype.h" +-#include "lv_alloc.h" + #include "defaults.h" + #include "display.h" + +-int attach_pool_metadata_lv(struct lv_segment *pool_seg, struct logical_volume *metadata_lv) +-{ +- pool_seg->metadata_lv = metadata_lv; +- metadata_lv->status |= THIN_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; +@@ -48,117 +38,6 @@ int detach_pool_metadata_lv(struct lv_segment *pool_seg, struct logical_volume * + return 1; + } + +-int attach_pool_data_lv(struct lv_segment *pool_seg, struct logical_volume *pool_data_lv) +-{ +- if (!set_lv_segment_area_lv(pool_seg, 0, pool_data_lv, 0, THIN_POOL_DATA)) +- return_0; +- +- pool_seg->lv->status |= THIN_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 logical_volume *merge_lv) +-{ +- seg->pool_lv = pool_lv; +- seg->lv->status |= THIN_VOLUME; +- seg->origin = origin; +- +- if (origin && !add_seg_to_segs_using_this_lv(origin, seg)) +- return_0; +- +- 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; +-} +- +-int detach_pool_lv(struct lv_segment *seg) +-{ +- struct lv_thin_message *tmsg, *tmp; +- struct seg_list *sl, *tsl; +- int no_update = 0; +- +- if (!seg->pool_lv || !lv_is_thin_pool(seg->pool_lv)) { +- log_error(INTERNAL_ERROR "Cannot detach pool from non-thin LV %s", +- seg->lv->name); +- 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.", +- tmsg->u.lv->name); +- 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 (!detach_thin_external_origin(seg)) +- return_0; +- +- if (!attach_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 (!remove_seg_from_segs_using_this_lv(seg->lv, sl->seg)) +- return_0; +- /* Thin snapshot is now regular thin volume */ +- sl->seg->origin = NULL; +- } +- +- seg->lv->status &= ~THIN_VOLUME; +- seg->pool_lv = NULL; +- seg->origin = NULL; +- +- return 1; +-} +- + int attach_pool_message(struct lv_segment *pool_seg, dm_thin_message_t type, + struct logical_volume *lv, uint32_t delete_id, + int no_update) +@@ -206,9 +85,9 @@ int attach_pool_message(struct lv_segment *pool_seg, dm_thin_message_t type, + + dm_list_add(&pool_seg->thin_messages, &tmsg->list); + +- log_debug_metadata("Added %s message", ++ log_debug_metadata("Added %s message.", + (type == DM_THIN_MESSAGE_CREATE_SNAP || +- type == DM_THIN_MESSAGE_CREATE_THIN) ? "create" : ++ type == DM_THIN_MESSAGE_CREATE_THIN) ? "create" : + (type == DM_THIN_MESSAGE_DELETE) ? "delete" : "unknown"); + + return 1; +@@ -261,7 +140,9 @@ int detach_thin_external_origin(struct lv_segment *seg) + + int lv_is_merging_thin_snapshot(const struct logical_volume *lv) + { +- return (first_seg(lv)->status & MERGING) ? 1 : 0; ++ struct lv_segment *seg = first_seg(lv); ++ ++ return (seg && seg->status & MERGING) ? 1 : 0; + } + + /* +@@ -326,7 +207,7 @@ int pool_is_active(const struct logical_volume *lv) + return 0; + } + +-int pool_can_resize_metadata(const struct logical_volume *lv) ++int thin_pool_feature_supported(const struct logical_volume *lv, int feature) + { + static unsigned attr = 0U; + struct lv_segment *seg; +@@ -345,7 +226,7 @@ int pool_can_resize_metadata(const struct logical_volume *lv) + return 0; + } + +- return (attr & THIN_FEATURE_METADATA_RESIZE) ? 1 : 0; ++ return (attr & feature) ? 1 : 0; + } + + int pool_below_threshold(const struct lv_segment *pool_seg) +@@ -360,36 +241,37 @@ int pool_below_threshold(const struct lv_segment *pool_seg) + return_0; + + if (percent >= threshold) +- return_0; ++ return 0; + + /* Metadata */ + if (!lv_thin_pool_percent(pool_seg->lv, 1, &percent)) + return_0; + + if (percent >= threshold) +- return_0; ++ return 0; + + return 1; + } + +-struct lv_segment *find_pool_seg(const struct lv_segment *seg) ++/* ++ * Validate given external origin could be used with thin pool ++ */ ++int pool_supports_external_origin(const struct lv_segment *pool_seg, const struct logical_volume *external_lv) + { +- struct lv_segment *pool_seg; +- +- pool_seg = get_only_segment_using_this_lv(seg->lv); +- +- if (!pool_seg) { +- log_error("Failed to find pool_seg for %s", seg->lv->name); +- return NULL; +- } +- +- if (!seg_is_thin_pool(pool_seg)) { +- log_error("%s on %s is not a pool segment", +- pool_seg->lv->name, seg->lv->name); +- return NULL; ++ uint32_t csize = pool_seg->chunk_size; ++ ++ if ((external_lv->size < csize) || (external_lv->size % csize)) { ++ /* TODO: Validate with thin feature flag once, it will be supported */ ++ log_error("Can't use \"%s/%s\" as external origin with \"%s/%s\" pool. " ++ "Size %s is not a multiple of pool's chunk size %s.", ++ external_lv->vg->name, external_lv->name, ++ pool_seg->lv->vg->name, pool_seg->lv->name, ++ display_size(external_lv->vg->cmd, external_lv->size), ++ display_size(external_lv->vg->cmd, csize)); ++ return 0; + } + +- return pool_seg; ++ return 1; + } + + struct logical_volume *find_pool_lv(const struct logical_volume *lv) +@@ -443,107 +325,6 @@ uint32_t get_free_pool_device_id(struct lv_segment *thin_pool_seg) + return max_id; + } + +-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]; +- +- if (pool_lv->le_count) { +- /* FIXME move code for manipulation from lv_manip.c */ +- log_error(INTERNAL_ERROR "Pool %s has already extents.", pool_lv->name); +- return 0; +- } +- +- /* LV is not yet a pool, so it's extension from lvcreate */ +- if (!(striped = get_segtype_from_string(pool_lv->vg->cmd, "striped"))) +- return_0; +- +- if (activation() && segtype->ops->target_present && +- !segtype->ops->target_present(pool_lv->vg->cmd, NULL, NULL)) { +- log_error("%s: Required device-mapper target(s) not " +- "detected in your kernel.", segtype->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.", pool_lv->name); +- else { +- 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 */ +- if (!activate_lv_local(pool_lv->vg->cmd, pool_lv) || +- /* Clear 4KB of metadata device for new thin-pool. */ +- !wipe_lv(pool_lv, (struct wipe_params) { .do_zero = 1 })) { +- log_error("Aborting. Failed to wipe pool metadata %s.", +- pool_lv->name); +- goto bad; +- } +- } +- +- if (dm_snprintf(name, sizeof(name), "%s_tmeta", pool_lv->name) < 0) { +- log_error("Name is too long to be a pool name."); +- 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, "_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; +- +- if (!attach_pool_data_lv(seg, data_lv)) +- goto_bad; +- +- if (!attach_pool_metadata_lv(seg, meta_lv)) +- goto_bad; +- +- seg->segtype = segtype; /* Set as thin_pool segment */ +- +- return 1; +- +-bad: +- if (activation()) { +- if (deactivate_lv_local(pool_lv->vg->cmd, pool_lv)) { +- log_error("Aborting. Could not deactivate pool %s.", +- pool_lv->name); +- return 0; +- } +- 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; +-} +- + int update_pool_lv(struct logical_volume *lv, int activate) + { + int monitored; +@@ -640,11 +421,12 @@ int update_profilable_pool_params(struct cmd_context *cmd, struct profile *profi + return 1; + } + +-int update_pool_params(struct volume_group *vg, unsigned attr, int passed_args, +- uint32_t data_extents, uint32_t extent_size, +- int *chunk_size_calc_method, uint32_t *chunk_size, +- thin_discards_t *discards, uint64_t *pool_metadata_size, +- int *zero) ++int update_thin_pool_params(struct volume_group *vg, unsigned attr, ++ int passed_args, ++ uint32_t data_extents, uint32_t extent_size, ++ int *chunk_size_calc_method, uint32_t *chunk_size, ++ thin_discards_t *discards, ++ uint64_t *pool_metadata_size, int *zero) + { + size_t estimate_chunk_size; + struct cmd_context *cmd = vg->cmd; +@@ -897,7 +679,7 @@ int handle_pool_metadata_spare(struct volume_group *vg, uint32_t extents, + seg_mirrors, + seg->region_size, + extents - lv->le_count, NULL, +- pvh, lv->alloc)) ++ pvh, lv->alloc, 0)) + return_0; + + return 1; +diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c +index 877371e..8f23d56 100644 +--- a/lib/metadata/vg.c ++++ b/lib/metadata/vg.c +@@ -89,6 +89,7 @@ void release_vg(struct volume_group *vg) + return; + + release_vg(vg->vg_ondisk); ++ release_vg(vg->vg_precommitted); + _free_vg(vg); + } + +diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h +index 43d27bf..028471b 100644 +--- a/lib/metadata/vg.h ++++ b/lib/metadata/vg.h +@@ -57,6 +57,7 @@ struct volume_group { + * _vg_update_vg_ondisk. + */ + struct volume_group *vg_ondisk; ++ struct volume_group *vg_precommitted; /* Parsed from precommitted */ + + alloc_policy_t alloc; + struct profile *profile; +diff --git a/lib/mirror/mirrored.c b/lib/mirror/mirrored.c +index c3eb27b..5088173 100644 +--- a/lib/mirror/mirrored.c ++++ b/lib/mirror/mirrored.c +@@ -150,7 +150,6 @@ static int _mirrored_text_export(const struct lv_segment *seg, struct formatter + + #ifdef DEVMAPPER_SUPPORT + static int _block_on_error_available = 0; +-static unsigned _mirror_attributes = 0; + + static struct mirror_state *_mirrored_init_target(struct dm_pool *mem, + struct cmd_context *cmd) +@@ -462,13 +461,13 @@ static int _mirrored_target_present(struct cmd_context *cmd, + { + static int _mirrored_checked = 0; + static int _mirrored_present = 0; ++ static unsigned _mirror_attributes = 0; + uint32_t maj, min, patchlevel; + unsigned maj2, min2, patchlevel2; + char vsn[80]; +- struct utsname uts; +- unsigned kmaj, kmin, krel; + + if (!_mirrored_checked) { ++ _mirrored_checked = 1; + _mirrored_present = target_present(cmd, "mirror", 1); + + /* +@@ -492,14 +491,16 @@ static int _mirrored_target_present(struct cmd_context *cmd, + sscanf(vsn, "%u.%u.%u", &maj2, &min2, &patchlevel2) == 3 && + maj2 == 4 && min2 == 5 && patchlevel2 == 0))) /* RHEL4U3 */ + _block_on_error_available = 1; +- } + +- /* +- * Check only for modules if atttributes requested and no previous check. +- * FIXME: Fails incorrectly if cmirror was built into kernel. +- */ +- if (attributes) { +- if (!_mirror_attributes) { ++#ifdef CMIRRORD_PIDFILE ++ /* ++ * The cluster mirror log daemon must be running, ++ * otherwise, the kernel module will fail to make ++ * contact. ++ */ ++ if (dm_daemon_is_running(CMIRRORD_PIDFILE)) { ++ struct utsname uts; ++ unsigned kmaj, kmin, krel; + /* + * The dm-log-userspace module was added to the + * 2.6.31 kernel. +@@ -508,31 +509,25 @@ static int _mirrored_target_present(struct cmd_context *cmd, + (sscanf(uts.release, "%u.%u.%u", &kmaj, &kmin, &krel) == 3) && + KERNEL_VERSION(kmaj, kmin, krel) < KERNEL_VERSION(2, 6, 31)) { + if (module_present(cmd, "log-clustered")) +- _mirror_attributes |= MIRROR_LOG_CLUSTERED; ++ _mirror_attributes |= MIRROR_LOG_CLUSTERED; + } else if (module_present(cmd, "log-userspace")) + _mirror_attributes |= MIRROR_LOG_CLUSTERED; + + if (!(_mirror_attributes & MIRROR_LOG_CLUSTERED)) +- log_verbose("Cluster mirror log module is not available"); +- +- /* +- * The cluster mirror log daemon must be running, +- * otherwise, the kernel module will fail to make +- * contact. +- */ +-#ifdef CMIRRORD_PIDFILE +- if (!dm_daemon_is_running(CMIRRORD_PIDFILE)) { +- log_verbose("Cluster mirror log daemon is not running"); +- _mirror_attributes &= ~MIRROR_LOG_CLUSTERED; +- } ++ log_verbose("Cluster mirror log module is not available."); ++ } else ++ log_verbose("Cluster mirror log daemon is not running."); + #else +- log_verbose("Cluster mirror log daemon not included in build"); +- _mirror_attributes &= ~MIRROR_LOG_CLUSTERED; ++ log_verbose("Cluster mirror log daemon not included in build."); + #endif +- } +- *attributes = _mirror_attributes; + } +- _mirrored_checked = 1; ++ ++ /* ++ * Check only for modules if atttributes requested and no previous check. ++ * FIXME: Fails incorrectly if cmirror was built into kernel. ++ */ ++ if (attributes) ++ *attributes = _mirror_attributes; + + return _mirrored_present; + } +diff --git a/lib/misc/configure.h.in b/lib/misc/configure.h.in +index 4e9ffd1..c1f088e 100644 +--- a/lib/misc/configure.h.in ++++ b/lib/misc/configure.h.in +@@ -3,6 +3,9 @@ + /* Define to 1 to use libblkid detection of signatures when wiping. */ + #undef BLKID_WIPING_SUPPORT + ++/* Define to 1 to include built-in support for cache. */ ++#undef CACHE_INTERNAL ++ + /* Define to 1 if the `closedir' function returns void instead of `int'. */ + #undef CLOSEDIR_VOID + +diff --git a/lib/misc/lvm-exec.c b/lib/misc/lvm-exec.c +index 01704ad..e862bad 100644 +--- a/lib/misc/lvm-exec.c ++++ b/lib/misc/lvm-exec.c +@@ -18,7 +18,6 @@ + #include "locking.h" + #include "lvm-exec.h" + #include "toolcontext.h" +-#include "activate.h" + + #include + #include +diff --git a/lib/raid/raid.c b/lib/raid/raid.c +index 956472e..e592573 100644 +--- a/lib/raid/raid.c ++++ b/lib/raid/raid.c +@@ -221,11 +221,11 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)), + + for (s = 0; s < seg->area_count; s++) + if (seg_lv(seg, s)->status & LV_REBUILD) +- rebuilds |= 1 << s; ++ rebuilds |= 1ULL << s; + + for (s = 0; s < seg->area_count; s++) + if (seg_lv(seg, s)->status & LV_WRITEMOSTLY) +- writemostly |= 1 << s; ++ writemostly |= 1ULL << s; + + if (mirror_in_sync()) + flags = DM_NOSYNC; +@@ -326,15 +326,45 @@ static int _raid_target_percent(void **target_state, + + static int _raid_target_present(struct cmd_context *cmd, + const struct lv_segment *seg __attribute__((unused)), +- unsigned *attributes __attribute__((unused))) ++ unsigned *attributes) + { ++ /* List of features with their kernel target version */ ++ static const struct feature { ++ uint32_t maj; ++ uint32_t min; ++ unsigned raid_feature; ++ const char *feature; ++ } const _features[] = { ++ { 1, 3, RAID_FEATURE_RAID10, "raid10" }, ++ }; ++ + static int _raid_checked = 0; + static int _raid_present = 0; ++ static int _raid_attrs = 0; ++ uint32_t maj, min, patchlevel; ++ unsigned i; + +- if (!_raid_checked) ++ if (!_raid_checked) { + _raid_present = target_present(cmd, "raid", 1); + +- _raid_checked = 1; ++ if (!target_version("raid", &maj, &min, &patchlevel)) { ++ log_error("Cannot read target version of RAID kernel module."); ++ return 0; ++ } ++ ++ for (i = 0; i < sizeof(_features)/sizeof(*_features); i++) ++ if ((maj > _features[i].maj) || ++ (maj == _features[i].maj && min >= _features[i].min)) ++ _raid_attrs |= _features[i].raid_feature; ++ else ++ log_very_verbose("Target raid does not support %s.", ++ _features[i].feature); ++ ++ _raid_checked = 1; ++ } ++ ++ if (attributes) ++ *attributes = _raid_attrs; + + return _raid_present; + } +diff --git a/lib/report/report.c b/lib/report/report.c +index 3a5119d..681e80c 100644 +--- a/lib/report/report.c ++++ b/lib/report/report.c +@@ -130,10 +130,10 @@ static int _tags_disp(struct dm_report *rh __attribute__((unused)), struct dm_po + struct dm_report_field *field, + const void *data, void *private __attribute__((unused))) + { +- const struct dm_list *tags = (const struct dm_list *) data; ++ const struct dm_list *tagsl = (const struct dm_list *) data; + char *tags_str; + +- if (!(tags_str = tags_format_and_copy(mem, tags))) ++ if (!(tags_str = tags_format_and_copy(mem, tagsl))) + return_0; + + return _field_set_value(field, tags_str, NULL); +@@ -321,7 +321,7 @@ static int _datalv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__( + const void *data, void *private __attribute__((unused))) + { + const struct logical_volume *lv = (const struct logical_volume *) data; +- const struct lv_segment *seg = lv_is_thin_pool(lv) ? first_seg(lv) : NULL; ++ const struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ? first_seg(lv) : NULL; + + if (seg) + return _lvname_disp(rh, mem, field, seg_lv(seg, 0), private); +@@ -334,7 +334,7 @@ static int _metadatalv_disp(struct dm_report *rh, struct dm_pool *mem __attribut + const void *data, void *private __attribute__((unused))) + { + const struct logical_volume *lv = (const struct logical_volume *) data; +- const struct lv_segment *seg = lv_is_thin_pool(lv) ? first_seg(lv) : NULL; ++ const struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ? first_seg(lv) : NULL; + + if (seg) + return _lvname_disp(rh, mem, field, seg->metadata_lv, private); +@@ -347,7 +347,8 @@ static int _poollv_disp(struct dm_report *rh, struct dm_pool *mem __attribute__( + const void *data, void *private __attribute__((unused))) + { + const struct logical_volume *lv = (const struct logical_volume *) data; +- struct lv_segment *seg = lv_is_thin_volume(lv) ? first_seg(lv) : NULL; ++ struct lv_segment *seg = (lv_is_thin_volume(lv) || lv_is_cache(lv)) ? ++ first_seg(lv) : NULL; + + if (seg) + return _lvname_disp(rh, mem, field, seg->pool_lv, private); +@@ -373,10 +374,14 @@ static int _origin_disp(struct dm_report *rh, struct dm_pool *mem, + const void *data, void *private) + { + const struct logical_volume *lv = (const struct logical_volume *) data; ++ const struct lv_segment *seg = first_seg(lv); + + if (lv_is_cow(lv)) + return _lvname_disp(rh, mem, field, origin_from_cow(lv), private); + ++ if (lv_is_cache(lv)) ++ return _lvname_disp(rh, mem, field, seg_lv(seg, 0), private); ++ + if (lv_is_thin_volume(lv) && first_seg(lv)->origin) + return _lvname_disp(rh, mem, field, first_seg(lv)->origin, private); + +@@ -1051,7 +1056,7 @@ static int _lvmetadatasize_disp(struct dm_report *rh, struct dm_pool *mem, + const struct logical_volume *lv = (const struct logical_volume *) data; + uint64_t size; + +- if (lv_is_thin_pool(lv)) { ++ if (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) { + size = lv_metadata_size(lv); + return _size64_disp(rh, mem, field, &size, private); + } +diff --git a/lib/snapshot/snapshot.c b/lib/snapshot/snapshot.c +index 91e778f..0b7b845 100644 +--- a/lib/snapshot/snapshot.c ++++ b/lib/snapshot/snapshot.c +@@ -142,19 +142,30 @@ static int _snap_target_percent(void **target_state __attribute__((unused)), + + static int _snap_target_present(struct cmd_context *cmd, + const struct lv_segment *seg, +- unsigned *attributes __attribute__((unused))) ++ unsigned *attributes) + { + static int _snap_checked = 0; + static int _snap_merge_checked = 0; + static int _snap_present = 0; + static int _snap_merge_present = 0; ++ static unsigned _snap_attrs = 0; ++ uint32_t maj, min, patchlevel; + + if (!_snap_checked) { ++ _snap_checked = 1; + _snap_present = target_present(cmd, "snapshot", 1) && + target_present(cmd, "snapshot-origin", 0); +- _snap_checked = 1; ++ ++ if (_snap_present && ++ target_version("snapshot", &maj, &min, &patchlevel) && ++ (maj > 1 || ++ (maj == 1 && (min >= 12 || (min == 10 && patchlevel >= 2))))) ++ _snap_attrs |= SNAPSHOT_FEATURE_FIXED_LEAK; ++ else ++ log_very_verbose("Target snapshot may leak metadata."); + } + ++ /* TODO: test everything at once */ + if (seg && (seg->status & MERGING)) { + if (!_snap_merge_checked) { + _snap_merge_present = target_present(cmd, "snapshot-merge", 0); +diff --git a/lib/thin/thin.c b/lib/thin/thin.c +index ff263d9..7c989f8 100644 +--- a/lib/thin/thin.c ++++ b/lib/thin/thin.c +@@ -215,7 +215,7 @@ static int _thin_pool_text_export(const struct lv_segment *seg, struct formatter + + #ifdef DEVMAPPER_SUPPORT + static int _thin_target_present(struct cmd_context *cmd, +- const struct lv_segment *seg, ++ const struct lv_segment *seg __attribute__((unused)), + unsigned *attributes); + + static int _thin_pool_modules_needed(struct dm_pool *mem, +@@ -262,7 +262,7 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, + uint64_t transaction_id = 0; + unsigned attr; + +- if (!_thin_target_present(cmd, seg, &attr)) ++ if (!_thin_target_present(cmd, NULL, &attr)) + return_0; + + if (!seg->metadata_lv) { +@@ -528,6 +528,7 @@ static int _thin_add_target_line(struct dev_manager *dm, + { + char *pool_dlid, *external_dlid; + uint32_t device_id = seg->device_id; ++ unsigned attr; + + if (!seg->pool_lv) { + log_error(INTERNAL_ERROR "Segment %s has no pool.", +@@ -541,18 +542,19 @@ static int _thin_add_target_line(struct dev_manager *dm, + } + + if (!laopts->no_merging) { ++ if (seg->merge_lv) { ++ log_error(INTERNAL_ERROR "Failed to add merged segment of %s.", ++ seg->lv->name); ++ return 0; ++ } + /* + * merge support for thinp snapshots is implemented by + * simply swapping the thinp device_id of the snapshot + * and origin. + */ +- if (seg->merge_lv) { +- /* snapshot, use merging lv's device_id */ +- device_id = first_seg(seg->merge_lv)->device_id; +- } else if (lv_is_merging_origin(seg->lv)) { ++ if (lv_is_merging_origin(seg->lv) && seg_is_thin_volume(find_snapshot(seg->lv))) + /* origin, use merging snapshot's device_id */ + device_id = find_snapshot(seg->lv)->device_id; +- } + } + + if (!dm_tree_node_add_thin_target(node, len, pool_dlid, device_id)) +@@ -560,6 +562,17 @@ static int _thin_add_target_line(struct dev_manager *dm, + + /* Add external origin LV */ + if (seg->external_lv) { ++ if (!pool_supports_external_origin(first_seg(seg->pool_lv), seg->external_lv)) ++ return_0; ++ if (seg->external_lv->size < seg->lv->size) { ++ /* Validate target supports smaller external origin */ ++ if (!_thin_target_present(cmd, NULL, &attr) || ++ !(attr & THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND)) { ++ log_error("Thin target does not support smaller size of external origin LV %s.", ++ seg->external_lv->name); ++ return 0; ++ } ++ } + if (!(external_dlid = build_dm_uuid(mem, seg->external_lv->lvid.s, + lv_layer(seg->external_lv)))) { + log_error("Failed to build uuid for external origin LV %s.", +@@ -604,7 +617,7 @@ static int _thin_target_percent(void **target_state __attribute__((unused)), + } + + static int _thin_target_present(struct cmd_context *cmd, +- const struct lv_segment *seg, ++ const struct lv_segment *seg __attribute__((unused)), + unsigned *attributes) + { + /* List of features with their kernel target version */ +@@ -618,7 +631,8 @@ static int _thin_target_present(struct cmd_context *cmd, + { 1, 1, THIN_FEATURE_EXTERNAL_ORIGIN, "external_origin" }, + { 1, 4, THIN_FEATURE_BLOCK_SIZE, "block_size" }, + { 1, 5, THIN_FEATURE_DISCARDS_NON_POWER_2, "discards_non_power_2" }, +- { 9, 9, THIN_FEATURE_METADATA_RESIZE, "metadata_resize" }, ++ { 1, 10, THIN_FEATURE_METADATA_RESIZE, "metadata_resize" }, ++ { 9, 11, THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND, "external_origin_extend" }, + }; + + static const char _lvmconf[] = "global/thin_disabled_features"; +@@ -640,7 +654,8 @@ static int _thin_target_present(struct cmd_context *cmd, + } + + for (i = 0; i < sizeof(_features)/sizeof(*_features); i++) +- if (maj >= _features[i].maj && min >= _features[i].min) ++ if ((maj > _features[i].maj) || ++ (maj == _features[i].maj && min >= _features[i].min)) + _attrs |= _features[i].thin_feature; + else + log_very_verbose("Target %s does not support %s.", +diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h +index adfbb76..dd57457 100644 +--- a/libdm/libdevmapper.h ++++ b/libdm/libdevmapper.h +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * +@@ -294,6 +294,39 @@ struct dm_status_raid { + int dm_get_status_raid(struct dm_pool *mem, const char *params, + struct dm_status_raid **status); + ++struct dm_status_cache { ++ uint64_t version; /* zero for now */ ++ ++ uint32_t metadata_block_size; /* in 512B sectors */ ++ uint32_t block_size; /* AKA 'chunk_size' */ ++ ++ uint64_t metadata_used_blocks; ++ uint64_t metadata_total_blocks; ++ ++ uint64_t used_blocks; ++ uint64_t dirty_blocks; ++ uint64_t total_blocks; ++ ++ uint64_t read_hits; ++ uint64_t read_misses; ++ uint64_t write_hits; ++ uint64_t write_misses; ++ ++ uint64_t demotions; ++ uint64_t promotions; ++ ++ uint32_t feature_flags; ++ ++ int core_argc; ++ char **core_argv; ++ ++ char *policy_name; ++ int policy_argc; ++ char **policy_argv; ++}; ++ ++int dm_get_status_cache(struct dm_pool *mem, const char *params, ++ struct dm_status_cache **status); + + /* + * Snapshot target's format: +@@ -524,10 +557,10 @@ struct dm_tree_node *dm_tree_add_new_dev_with_udev_flags(struct dm_tree *tree, + * Set major and minor to 0 or uuid to NULL to get the root node. + */ + struct dm_tree_node *dm_tree_find_node(struct dm_tree *tree, +- uint32_t major, +- uint32_t minor); ++ uint32_t major, ++ uint32_t minor); + struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *tree, +- const char *uuid); ++ const char *uuid); + + /* + * Use this to walk through all children of a given node. +@@ -559,8 +592,8 @@ int dm_tree_node_num_children(const struct dm_tree_node *node, uint32_t inverted + * Ignores devices that don't have a uuid starting with uuid_prefix. + */ + int dm_tree_deactivate_children(struct dm_tree_node *dnode, +- const char *uuid_prefix, +- size_t uuid_prefix_len); ++ const char *uuid_prefix, ++ size_t uuid_prefix_len); + /* + * Preload/create a device plus all dependencies. + * Ignores devices that don't have a uuid starting with uuid_prefix. +@@ -582,8 +615,8 @@ int dm_tree_activate_children(struct dm_tree_node *dnode, + * Ignores devices that don't have a uuid starting with uuid_prefix. + */ + int dm_tree_suspend_children(struct dm_tree_node *dnode, +- const char *uuid_prefix, +- size_t uuid_prefix_len); ++ const char *uuid_prefix, ++ size_t uuid_prefix_len); + + /* + * Skip the filesystem sync when suspending. +@@ -613,36 +646,36 @@ void dm_tree_retry_remove(struct dm_tree_node *dnode); + * Returns 1 if the tree walk has to be aborted. + */ + int dm_tree_children_use_uuid(struct dm_tree_node *dnode, +- const char *uuid_prefix, +- size_t uuid_prefix_len); ++ const char *uuid_prefix, ++ size_t uuid_prefix_len); + + /* + * Construct tables for new nodes before activating them. + */ + int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode, +- uint64_t size, +- const char *origin_uuid); ++ uint64_t size, ++ const char *origin_uuid); + int dm_tree_node_add_snapshot_target(struct dm_tree_node *node, +- uint64_t size, +- const char *origin_uuid, +- const char *cow_uuid, +- int persistent, +- uint32_t chunk_size); ++ uint64_t size, ++ const char *origin_uuid, ++ const char *cow_uuid, ++ int persistent, ++ uint32_t chunk_size); + int dm_tree_node_add_snapshot_merge_target(struct dm_tree_node *node, +- uint64_t size, +- const char *origin_uuid, +- const char *cow_uuid, +- const char *merge_uuid, +- uint32_t chunk_size); ++ uint64_t size, ++ const char *origin_uuid, ++ const char *cow_uuid, ++ const char *merge_uuid, ++ uint32_t chunk_size); + int dm_tree_node_add_error_target(struct dm_tree_node *node, +- uint64_t size); ++ uint64_t size); + int dm_tree_node_add_zero_target(struct dm_tree_node *node, +- uint64_t size); ++ uint64_t size); + int dm_tree_node_add_linear_target(struct dm_tree_node *node, +- uint64_t size); ++ uint64_t size); + int dm_tree_node_add_striped_target(struct dm_tree_node *node, +- uint64_t size, +- uint32_t stripe_size); ++ uint64_t size, ++ uint32_t stripe_size); + + #define DM_CRYPT_IV_DEFAULT UINT64_C(-1) /* iv_offset == seg offset */ + /* +@@ -659,7 +692,7 @@ int dm_tree_node_add_crypt_target(struct dm_tree_node *node, + uint64_t iv_offset, + const char *key); + int dm_tree_node_add_mirror_target(struct dm_tree_node *node, +- uint64_t size); ++ uint64_t size); + + /* Mirror log flags */ + #define DM_NOSYNC 0x00000001 /* Known already in sync */ +@@ -668,11 +701,11 @@ int dm_tree_node_add_mirror_target(struct dm_tree_node *node, + #define DM_CORELOG 0x00000008 /* In-memory log */ + + int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node, +- uint32_t region_size, +- unsigned clustered, +- const char *log_uuid, +- unsigned area_count, +- uint32_t flags); ++ uint32_t region_size, ++ unsigned clustered, ++ const char *log_uuid, ++ unsigned area_count, ++ uint32_t flags); + + int dm_tree_node_add_raid_target(struct dm_tree_node *node, + uint64_t size, +@@ -682,6 +715,22 @@ int dm_tree_node_add_raid_target(struct dm_tree_node *node, + uint64_t rebuilds, + uint64_t flags); + ++/* ++ * Defines bellow are based on kernel's dm-cache.c defines ++ * DM_CACHE_MIN_DATA_BLOCK_SIZE (32 * 1024 >> SECTOR_SHIFT) ++ * DM_CACHE_MAX_DATA_BLOCK_SIZE (1024 * 1024 * 1024 >> SECTOR_SHIFT) ++ */ ++#define DM_CACHE_MIN_DATA_BLOCK_SIZE (UINT32_C(64)) ++#define DM_CACHE_MAX_DATA_BLOCK_SIZE (UINT32_C(2097152)) ++/* ++ * Max supported size for cache pool metadata device. ++ * Limitation is hardcoded into the kernel and bigger device sizes ++ * are not accepted. ++ * ++ * Limit defined in drivers/md/dm-cache-metadata.h ++ */ ++#define DM_CACHE_METADATA_MAX_SECTORS DM_THIN_METADATA_MAX_SECTORS ++ + struct dm_tree_node_raid_params { + const char *raid_type; + +@@ -712,6 +761,23 @@ int dm_tree_node_add_raid_target_with_params(struct dm_tree_node *node, + uint64_t size, + struct dm_tree_node_raid_params *p); + ++/* Cache feature_flags */ ++#define DM_CACHE_FEATURE_WRITEBACK 0x00000001 ++#define DM_CACHE_FEATURE_WRITETHROUGH 0x00000002 ++ ++int dm_tree_node_add_cache_target(struct dm_tree_node *node, ++ uint64_t size, ++ const char *metadata_uuid, ++ const char *data_uuid, ++ const char *origin_uuid, ++ uint32_t chunk_size, ++ uint32_t feature_flags, /* DM_CACHE_FEATURE_* */ ++ unsigned core_argc, ++ char **core_argv, ++ char *policy_name, ++ unsigned policy_argc, ++ char **policy_argv); ++ + /* + * Replicator operation mode + * Note: API for Replicator is not yet stable +@@ -1083,7 +1149,7 @@ void dm_hash_remove(struct dm_hash_table *t, const char *key); + + void *dm_hash_lookup_binary(struct dm_hash_table *t, const void *key, uint32_t len); + int dm_hash_insert_binary(struct dm_hash_table *t, const void *key, uint32_t len, +- void *data); ++ void *data); + void dm_hash_remove_binary(struct dm_hash_table *t, const void *key, uint32_t len); + + unsigned dm_hash_get_num_entries(struct dm_hash_table *t); +diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c +index 5ec5769..2ec66e7 100644 +--- a/libdm/libdm-common.c ++++ b/libdm/libdm-common.c +@@ -868,12 +868,27 @@ static int _selabel_lookup(const char *path, mode_t mode, + } + #endif + ++#ifdef HAVE_SELINUX ++static int _is_selinux_enabled(void) ++{ ++ static int _tested = 0; ++ static int _enabled; ++ ++ if (!_tested) { ++ _tested = 1; ++ _enabled = is_selinux_enabled(); ++ } ++ ++ return _enabled; ++} ++#endif ++ + int dm_prepare_selinux_context(const char *path, mode_t mode) + { + #ifdef HAVE_SELINUX + security_context_t scontext = NULL; + +- if (is_selinux_enabled() <= 0) ++ if (_is_selinux_enabled() <= 0) + return 1; + + if (path) { +@@ -901,7 +916,7 @@ int dm_set_selinux_context(const char *path, mode_t mode) + #ifdef HAVE_SELINUX + security_context_t scontext = NULL; + +- if (is_selinux_enabled() <= 0) ++ if (_is_selinux_enabled() <= 0) + return 1; + + if (!_selabel_lookup(path, mode, &scontext)) +diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c +index 42bb9e7..e513885 100644 +--- a/libdm/libdm-deptree.c ++++ b/libdm/libdm-deptree.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2005-2013 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2005-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * +@@ -27,6 +27,7 @@ + + /* Supported segment types */ + enum { ++ SEG_CACHE, + SEG_CRYPT, + SEG_ERROR, + SEG_LINEAR, +@@ -59,6 +60,7 @@ struct { + unsigned type; + const char *target; + } dm_segtypes[] = { ++ { SEG_CACHE, "cache" }, + { SEG_CRYPT, "crypt" }, + { SEG_ERROR, "error" }, + { SEG_LINEAR, "linear" }, +@@ -158,18 +160,24 @@ struct load_segment { + uint32_t stripe_size; /* Striped + raid */ + + int persistent; /* Snapshot */ +- uint32_t chunk_size; /* Snapshot */ ++ uint32_t chunk_size; /* Snapshot + cache */ + struct dm_tree_node *cow; /* Snapshot */ +- struct dm_tree_node *origin; /* Snapshot + Snapshot origin */ ++ struct dm_tree_node *origin; /* Snapshot + Snapshot origin + Cache */ + struct dm_tree_node *merge; /* Snapshot */ + + struct dm_tree_node *log; /* Mirror + Replicator */ + uint32_t region_size; /* Mirror + raid */ + unsigned clustered; /* Mirror */ + unsigned mirror_area_count; /* Mirror */ +- uint32_t flags; /* Mirror log */ ++ uint32_t flags; /* Mirror + raid + Cache */ + char *uuid; /* Clustered mirror log */ + ++ unsigned core_argc; /* Cache */ ++ char **core_argv; /* Cache */ ++ char *policy_name; /* Cache */ ++ unsigned policy_argc; /* Cache */ ++ char **policy_argv; /* Cache */ ++ + const char *cipher; /* Crypt */ + const char *chainmode; /* Crypt */ + const char *iv; /* Crypt */ +@@ -189,7 +197,7 @@ struct load_segment { + uint32_t max_recovery_rate; /* raid kB/sec/disk */ + uint32_t min_recovery_rate; /* raid kB/sec/disk */ + +- struct dm_tree_node *metadata; /* Thin_pool */ ++ struct dm_tree_node *metadata; /* Thin_pool + Cache */ + struct dm_tree_node *pool; /* Thin_pool, Thin */ + struct dm_tree_node *external; /* Thin */ + struct dm_list thin_messages; /* Thin_pool */ +@@ -530,7 +538,7 @@ static struct dm_tree_node *_find_dm_tree_node(struct dm_tree *dtree, + dev_t dev = MKDEV((dev_t)major, minor); + + return dm_hash_lookup_binary(dtree->devs, (const char *) &dev, +- sizeof(dev)); ++ sizeof(dev)); + } + + static struct dm_tree_node *_find_dm_tree_node_by_uuid(struct dm_tree *dtree, +@@ -699,8 +707,8 @@ static int _children_suspended(struct dm_tree_node *node, + * Set major and minor to zero for root of tree. + */ + struct dm_tree_node *dm_tree_find_node(struct dm_tree *dtree, +- uint32_t major, +- uint32_t minor) ++ uint32_t major, ++ uint32_t minor) + { + if (!major && !minor) + return &dtree->root; +@@ -712,7 +720,7 @@ struct dm_tree_node *dm_tree_find_node(struct dm_tree *dtree, + * Set uuid to NULL for root of tree. + */ + struct dm_tree_node *dm_tree_find_node_by_uuid(struct dm_tree *dtree, +- const char *uuid) ++ const char *uuid) + { + if (!uuid || !*uuid) + return &dtree->root; +@@ -1470,14 +1478,17 @@ out: + return r; + } + ++/* For preload pass only validate pool's transaction_id */ + static int _node_send_messages(struct dm_tree_node *dnode, + const char *uuid_prefix, +- size_t uuid_prefix_len) ++ size_t uuid_prefix_len, ++ int send) + { + struct load_segment *seg; + struct thin_message *tmsg; + uint64_t trans_id; + const char *uuid; ++ int have_messages; + + if (!dnode->info.exists || (dm_list_size(&dnode->props.segs) != 1)) + return 1; +@@ -1495,32 +1506,34 @@ static int _node_send_messages(struct dm_tree_node *dnode, + } + + if (!_thin_pool_status_transaction_id(dnode, &trans_id)) +- goto_bad; ++ return_0; + ++ have_messages = !dm_list_empty(&seg->thin_messages) ? 1 : 0; + if (trans_id == seg->transaction_id) { +- if (!dm_list_empty(&seg->thin_messages)) ++ dnode->props.send_messages = 0; /* messages already committed */ ++ if (have_messages) + log_debug_activation("Thin pool transaction_id matches %" PRIu64 + ", skipping messages.", trans_id); +- return 1; /* In sync - skip messages */ ++ return 1; + } + +- if (trans_id != (seg->transaction_id - 1)) { ++ /* Error if there are no stacked messages or id mismatches */ ++ if (trans_id != (seg->transaction_id - have_messages)) { + log_error("Thin pool transaction_id=%" PRIu64 ", while expected: %" PRIu64 ".", +- trans_id, seg->transaction_id - 1); +- goto bad; /* Nothing to send */ ++ trans_id, seg->transaction_id - have_messages); ++ return 0; + } + ++ if (!send) ++ return 1; /* transaction_id is matching */ ++ + dm_list_iterate_items(tmsg, &seg->thin_messages) + if (!(_thin_pool_node_message(dnode, tmsg))) +- goto_bad; ++ return_0; + +- return 1; +-bad: +- /* Try to deactivate */ +- if (!(dm_tree_deactivate_children(dnode, uuid_prefix, uuid_prefix_len))) +- log_error("Failed to deactivate %s", dnode->name); ++ dnode->props.send_messages = 0; /* messages posted */ + +- return 0; ++ return 1; + } + + /* +@@ -1613,7 +1626,9 @@ static int _dm_tree_deactivate_children(struct dm_tree_node *dnode, + info.minor); + r = 0; + continue; +- } else if (info.suspended && info.live_table) ++ } ++ ++ if (info.suspended && info.live_table) + dec_suspended(); + + if (child->callback && +@@ -1856,14 +1871,9 @@ int dm_tree_activate_children(struct dm_tree_node *dnode, + * resume should continue further, just whole command + * has to report failure. + */ +- if (r && dnode->props.send_messages) { +- if (!(r = _node_send_messages(dnode, uuid_prefix, uuid_prefix_len))) +- stack; +- else +- dnode->props.send_messages = 0; /* messages posted */ +- } +- +- handle = NULL; ++ if (r && dnode->props.send_messages && ++ !(r = _node_send_messages(dnode, uuid_prefix, uuid_prefix_len, 1))) ++ stack; + + return r; + } +@@ -2235,11 +2245,11 @@ static int _raid_emit_segment_line(struct dm_task *dmt, uint32_t major, + EMIT_PARAMS(pos, " region_size %u", seg->region_size); + + for (i = 0; i < (seg->area_count / 2); i++) +- if (seg->rebuilds & (1 << i)) ++ if (seg->rebuilds & (1ULL << i)) + EMIT_PARAMS(pos, " rebuild %u", i); + + for (i = 0; i < (seg->area_count / 2); i++) +- if (seg->writemostly & (1 << i)) ++ if (seg->writemostly & (1ULL << i)) + EMIT_PARAMS(pos, " write_mostly %u", i); + + if (seg->writebehind) +@@ -2262,6 +2272,70 @@ static int _raid_emit_segment_line(struct dm_task *dmt, uint32_t major, + return 1; + } + ++static int _cache_emit_segment_line(struct dm_task *dmt, ++ struct load_segment *seg, ++ char *params, size_t paramsize) ++{ ++ int pos = 0; ++ unsigned i = 0; ++ unsigned feature_count; ++ struct seg_area *area; ++ char data[DM_FORMAT_DEV_BUFSIZE]; ++ char metadata[DM_FORMAT_DEV_BUFSIZE]; ++ char origin[DM_FORMAT_DEV_BUFSIZE]; ++ ++ /* Metadata Dev */ ++ if (!_build_dev_string(metadata, sizeof(metadata), seg->metadata)) ++ return_0; ++ ++ /* Cache Dev */ ++ if (!_build_dev_string(data, sizeof(origin), seg->pool)) ++ return_0; ++ ++ /* Origin Dev */ ++ dm_list_iterate_items(area, &seg->areas) ++ break; /* There is only ever 1 area */ ++ if (!_build_dev_string(origin, sizeof(data), area->dev_node)) ++ return_0; ++ ++ EMIT_PARAMS(pos, " %s %s %s", metadata, data, origin); ++ ++ /* Chunk size */ ++ EMIT_PARAMS(pos, " %u", seg->chunk_size); ++ ++ /* Features */ ++ feature_count = hweight32(seg->flags); ++ EMIT_PARAMS(pos, " %u", feature_count); ++ if (seg->flags & DM_CACHE_FEATURE_WRITETHROUGH) ++ EMIT_PARAMS(pos, " writethrough"); ++ else if (seg->flags & DM_CACHE_FEATURE_WRITEBACK) ++ EMIT_PARAMS(pos, " writeback"); ++ ++ /* Core Arguments (like 'migration_threshold') */ ++ if (seg->core_argc) { ++ EMIT_PARAMS(pos, " %u", seg->core_argc); ++ for (i = 0; i < seg->core_argc; i++) ++ EMIT_PARAMS(pos, " %s", seg->core_argv[i]); ++ } ++ ++ /* Cache Policy */ ++ if (!seg->policy_name) ++ EMIT_PARAMS(pos, " default 0"); ++ else { ++ EMIT_PARAMS(pos, " %s %u", seg->policy_name, seg->policy_argc); ++ if (seg->policy_argc % 2) { ++ log_error(INTERNAL_ERROR ++ "Cache policy arguments must be in " ++ " pairs"); ++ return 0; ++ } ++ for (i = 0; i < seg->policy_argc; i++) ++ EMIT_PARAMS(pos, " %s", seg->policy_argv[i]); ++ } ++ ++ return 1; ++} ++ + static int _thin_pool_emit_segment_line(struct dm_task *dmt, + struct load_segment *seg, + char *params, size_t paramsize) +@@ -2398,6 +2472,10 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major, + if (!_thin_emit_segment_line(dmt, seg, params, paramsize)) + return_0; + break; ++ case SEG_CACHE: ++ if (!_cache_emit_segment_line(dmt, seg, params, paramsize)) ++ return_0; ++ break; + } + + switch(seg->type) { +@@ -2409,6 +2487,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major, + case SEG_ZERO: + case SEG_THIN_POOL: + case SEG_THIN: ++ case SEG_CACHE: + break; + case SEG_CRYPT: + case SEG_LINEAR: +@@ -2550,13 +2629,16 @@ out: + return r; + } + ++/* ++ * Currently try to deactivate only nodes created during preload. ++ * New node is always attached to the front of activated_list ++ */ + static int _dm_tree_revert_activated(struct dm_tree_node *parent) + { + struct dm_tree_node *child; + +- dm_list_iterate_back_items_gen(child, &parent->activated, activated_list) { +- _dm_tree_revert_activated(child); +- log_debug("Reverting preloaded %s.", child->name); ++ dm_list_iterate_items_gen(child, &parent->activated, activated_list) { ++ log_debug_activation("Reverting %s.", child->name); + if (!_deactivate_node(child->name, child->info.major, child->info.minor, + &child->dtree->cookie, child->udev_flags, 0)) { + log_error("Unable to deactivate %s (%" PRIu32 +@@ -2564,6 +2646,8 @@ static int _dm_tree_revert_activated(struct dm_tree_node *parent) + child->info.minor); + return 0; + } ++ if (!_dm_tree_revert_activated(child)) ++ return_0; + } + + return 1; +@@ -2623,7 +2707,7 @@ int dm_tree_preload_children(struct dm_tree_node *dnode, + child->info.minor); + /* If the device was not previously active, we might as well remove this node. */ + if (!child->info.live_table && +- !_deactivate_node(child->name, child->info.major,child->info.minor, ++ !_deactivate_node(child->name, child->info.major, child->info.minor, + &child->dtree->cookie, child->udev_flags, 0)) + log_error("Unable to deactivate %s (%" PRIu32 + ":%" PRIu32 ")", child->name, child->info.major, +@@ -2633,9 +2717,22 @@ int dm_tree_preload_children(struct dm_tree_node *dnode, + continue; + } + +- if (!child->info.live_table) ++ if (!child->info.live_table) { + /* Collect newly introduced devices for revert */ +- dm_list_add(&dnode->activated, &child->activated_list); ++ dm_list_add_h(&dnode->activated, &child->activated_list); ++ ++ /* When creating new node also check transaction_id. */ ++ if (child->props.send_messages && ++ !_node_send_messages(child, uuid_prefix, uuid_prefix_len, 0)) { ++ stack; ++ if (!dm_udev_wait(dm_tree_get_cookie(dnode))) ++ stack; ++ dm_tree_set_cookie(dnode, 0); ++ (void) _dm_tree_revert_activated(dnode); ++ r = 0; ++ continue; ++ } ++ } + + /* Update cached info */ + child->info = newinfo; +@@ -2649,12 +2746,12 @@ int dm_tree_preload_children(struct dm_tree_node *dnode, + } + + if (update_devs_flag || +- (!dnode->info.exists && dnode->callback)) { ++ (r && !dnode->info.exists && dnode->callback)) { + if (!dm_udev_wait(dm_tree_get_cookie(dnode))) + stack; + dm_tree_set_cookie(dnode, 0); + +- if (!dnode->info.exists && dnode->callback && ++ if (r && !dnode->info.exists && dnode->callback && + !dnode->callback(dnode, DM_NODE_CALLBACK_PRELOADED, + dnode->callback_data)) + { +@@ -2724,8 +2821,8 @@ static struct load_segment *_add_segment(struct dm_tree_node *dnode, unsigned ty + } + + int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode, +- uint64_t size, +- const char *origin_uuid) ++ uint64_t size, ++ const char *origin_uuid) + { + struct load_segment *seg; + struct dm_tree_node *origin_node; +@@ -2755,12 +2852,12 @@ int dm_tree_node_add_snapshot_origin_target(struct dm_tree_node *dnode, + } + + static int _add_snapshot_target(struct dm_tree_node *node, +- uint64_t size, +- const char *origin_uuid, +- const char *cow_uuid, +- const char *merge_uuid, +- int persistent, +- uint32_t chunk_size) ++ uint64_t size, ++ const char *origin_uuid, ++ const char *cow_uuid, ++ const char *merge_uuid, ++ int persistent, ++ uint32_t chunk_size) + { + struct load_segment *seg; + struct dm_tree_node *origin_node, *cow_node, *merge_node; +@@ -2873,7 +2970,7 @@ int dm_get_status_snapshot(struct dm_pool *mem, const char *params, + } + + int dm_tree_node_add_error_target(struct dm_tree_node *node, +- uint64_t size) ++ uint64_t size) + { + if (!_add_segment(node, SEG_ERROR, size)) + return_0; +@@ -2882,7 +2979,7 @@ int dm_tree_node_add_error_target(struct dm_tree_node *node, + } + + int dm_tree_node_add_zero_target(struct dm_tree_node *node, +- uint64_t size) ++ uint64_t size) + { + if (!_add_segment(node, SEG_ZERO, size)) + return_0; +@@ -2891,7 +2988,7 @@ int dm_tree_node_add_zero_target(struct dm_tree_node *node, + } + + int dm_tree_node_add_linear_target(struct dm_tree_node *node, +- uint64_t size) ++ uint64_t size) + { + if (!_add_segment(node, SEG_LINEAR, size)) + return_0; +@@ -2900,8 +2997,8 @@ int dm_tree_node_add_linear_target(struct dm_tree_node *node, + } + + int dm_tree_node_add_striped_target(struct dm_tree_node *node, +- uint64_t size, +- uint32_t stripe_size) ++ uint64_t size, ++ uint32_t stripe_size) + { + struct load_segment *seg; + +@@ -2936,11 +3033,11 @@ int dm_tree_node_add_crypt_target(struct dm_tree_node *node, + } + + int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node, +- uint32_t region_size, +- unsigned clustered, +- const char *log_uuid, +- unsigned area_count, +- uint32_t flags) ++ uint32_t region_size, ++ unsigned clustered, ++ const char *log_uuid, ++ unsigned area_count, ++ uint32_t flags) + { + struct dm_tree_node *log_node = NULL; + struct load_segment *seg; +@@ -2988,7 +3085,7 @@ int dm_tree_node_add_mirror_target_log(struct dm_tree_node *node, + } + + int dm_tree_node_add_mirror_target(struct dm_tree_node *node, +- uint64_t size) ++ uint64_t size) + { + if (!_add_segment(node, SEG_MIRRORED, size)) + return_0; +@@ -3033,19 +3130,17 @@ int dm_tree_node_add_raid_target(struct dm_tree_node *node, + uint64_t rebuilds, + uint64_t flags) + { +- struct dm_tree_node_raid_params params; +- +- memset(¶ms, 0, sizeof(params)); +- params.raid_type = raid_type; +- params.region_size = region_size; +- params.stripe_size = stripe_size; +- params.rebuilds = rebuilds; +- params.flags = flags; ++ struct dm_tree_node_raid_params params = { ++ .raid_type = raid_type, ++ .region_size = region_size, ++ .stripe_size = stripe_size, ++ .rebuilds = rebuilds, ++ .flags = flags ++ }; + + return dm_tree_node_add_raid_target_with_params(node, size, ¶ms); + } + +- + /* + * Various RAID status versions include: + * Versions < 1.5.0 (4 fields): +@@ -3124,6 +3219,203 @@ bad: + return 0; + } + ++int dm_tree_node_add_cache_target(struct dm_tree_node *node, ++ uint64_t size, ++ const char *metadata_uuid, ++ const char *data_uuid, ++ const char *origin_uuid, ++ uint32_t chunk_size, ++ uint32_t feature_flags, /* DM_CACHE_FEATURE_* */ ++ unsigned core_argc, ++ char **core_argv, ++ char *policy_name, ++ unsigned policy_argc, ++ char **policy_argv) ++{ ++ int i; ++ struct load_segment *seg = NULL; ++ ++ for (i = 0; dm_segtypes[i].target && !seg; i++) { ++ if (strcmp("cache", dm_segtypes[i].target)) ++ continue; ++ if (!(seg = _add_segment(node, dm_segtypes[i].type, size))) ++ return_0; ++ } ++ ++ if (!seg) ++ return_0; ++ ++ if (!(seg->pool = dm_tree_find_node_by_uuid(node->dtree, ++ data_uuid))) { ++ log_error("Missing cache's data uuid %s.", ++ data_uuid); ++ return 0; ++ } ++ if (!_link_tree_nodes(node, seg->pool)) ++ return_0; ++ ++ if (!(seg->metadata = dm_tree_find_node_by_uuid(node->dtree, ++ metadata_uuid))) { ++ log_error("Missing cache's metadata uuid %s.", ++ metadata_uuid); ++ return 0; ++ } ++ if (!_link_tree_nodes(node, seg->metadata)) ++ return_0; ++ ++ seg->chunk_size = chunk_size; ++ ++ seg->flags = feature_flags; ++ ++ /* FIXME: validation missing */ ++ ++ seg->core_argc = core_argc; ++ seg->core_argv = core_argv; ++ ++ seg->policy_name = policy_name; ++ seg->policy_argc = policy_argc; ++ seg->policy_argv = policy_argv; ++ ++ return 1; ++} ++ ++static const char *advance_to_next_word(const char *str, int count) ++{ ++ int i; ++ const char *p; ++ ++ for (p = str, i = 0; i < count; i++, p++) ++ if (!(p = strchr(p, ' '))) ++ return NULL; ++ ++ return p; ++} ++ ++/* ++ * <#used metadata blocks>/<#total metadata blocks> ++ * <#used cache blocks>/<#total cache blocks> ++ * <#read hits> <#read misses> <#write hits> <#write misses> ++ * <#demotions> <#promotions> <#dirty> <#features> * ++ * <#core args> * <#policy args> * ++ * ++ * metadata block size : Fixed block size for each metadata block in ++ * sectors ++ * #used metadata blocks : Number of metadata blocks used ++ * #total metadata blocks : Total number of metadata blocks ++ * cache block size : Configurable block size for the cache device ++ * in sectors ++ * #used cache blocks : Number of blocks resident in the cache ++ * #total cache blocks : Total number of cache blocks ++ * #read hits : Number of times a READ bio has been mapped ++ * to the cache ++ * #read misses : Number of times a READ bio has been mapped ++ * to the origin ++ * #write hits : Number of times a WRITE bio has been mapped ++ * to the cache ++ * #write misses : Number of times a WRITE bio has been ++ * mapped to the origin ++ * #demotions : Number of times a block has been removed ++ * from the cache ++ * #promotions : Number of times a block has been moved to ++ * the cache ++ * #dirty : Number of blocks in the cache that differ ++ * from the origin ++ * #feature args : Number of feature args to follow ++ * feature args : 'writethrough' (optional) ++ * #core args : Number of core arguments (must be even) ++ * core args : Key/value pairs for tuning the core ++ * e.g. migration_threshold ++ * *policy name : Name of the policy ++ * #policy args : Number of policy arguments to follow (must be even) ++ * policy args : Key/value pairs ++ * e.g. sequential_threshold ++ */ ++int dm_get_status_cache(struct dm_pool *mem, const char *params, ++ struct dm_status_cache **status) ++{ ++ int i, feature_argc; ++ char *str; ++ const char *p, *pp; ++ struct dm_status_cache *s; ++ ++ if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_cache)))) ++ return_0; ++ ++ /* Read in args that have definitive placement */ ++ if (sscanf(params, ++ " %" PRIu32 ++ " %" PRIu64 "/%" PRIu64 ++ " %" PRIu32 ++ " %" PRIu64 "/%" PRIu64 ++ " %" PRIu64 " %" PRIu64 ++ " %" PRIu64 " %" PRIu64 ++ " %" PRIu64 " %" PRIu64 ++ " %" PRIu64 ++ " %d", ++ &s->metadata_block_size, ++ &s->metadata_used_blocks, &s->metadata_total_blocks, ++ &s->block_size, /* AKA, chunk_size */ ++ &s->used_blocks, &s->total_blocks, ++ &s->read_hits, &s->read_misses, ++ &s->write_hits, &s->write_misses, ++ &s->demotions, &s->promotions, ++ &s->dirty_blocks, ++ &feature_argc) != 14) ++ goto bad; ++ ++ /* Now jump to "features" section */ ++ if (!(p = advance_to_next_word(params, 12))) ++ goto bad; ++ ++ /* Read in features */ ++ for (i = 0; i < feature_argc; i++) { ++ if (!strncmp(p, "writethrough ", 13)) ++ s->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; ++ else if (!strncmp(p, "writeback ", 10)) ++ s->feature_flags |= DM_CACHE_FEATURE_WRITEBACK; ++ else ++ log_error("Unknown feature in status: %s", params); ++ ++ if (!(p = advance_to_next_word(p, 1))) ++ goto bad; ++ } ++ ++ /* Read in core_args. */ ++ if (sscanf(p, "%d ", &s->core_argc) != 1) ++ goto bad; ++ if (s->core_argc && ++ (!(s->core_argv = dm_pool_zalloc(mem, sizeof(char *) * s->core_argc)) || ++ !(p = advance_to_next_word(p, 1)) || ++ !(str = dm_pool_strdup(mem, p)) || ++ !(p = advance_to_next_word(p, s->core_argc)) || ++ (dm_split_words(str, s->core_argc, 0, s->core_argv) != s->core_argc))) ++ goto bad; ++ ++ /* Read in policy args */ ++ pp = p; ++ if (!(p = advance_to_next_word(p, 1)) || ++ !(s->policy_name = dm_pool_zalloc(mem, (p - pp)))) ++ goto bad; ++ if (sscanf(pp, "%s %d", s->policy_name, &s->policy_argc) != 2) ++ goto bad; ++ if (s->policy_argc && ++ (!(s->policy_argv = dm_pool_zalloc(mem, sizeof(char *) * s->policy_argc)) || ++ !(p = advance_to_next_word(p, 1)) || ++ !(str = dm_pool_strdup(mem, p)) || ++ (dm_split_words(str, s->policy_argc, 0, s->policy_argv) != s->policy_argc))) ++ goto bad; ++ ++ *status = s; ++ return 1; ++ ++bad: ++ log_error("Failed to parse cache params: %s", params); ++ dm_pool_free(mem, s); ++ *status = NULL; ++ ++ return 0; ++} ++ + int dm_tree_node_add_replicator_target(struct dm_tree_node *node, + uint64_t size, + const char *rlog_uuid, +@@ -3614,9 +3906,9 @@ static int _add_area(struct dm_tree_node *node, struct load_segment *seg, struct + } + + int dm_tree_node_add_target_area(struct dm_tree_node *node, +- const char *dev_name, +- const char *uuid, +- uint64_t offset) ++ const char *dev_name, ++ const char *uuid, ++ uint64_t offset) + { + struct load_segment *seg; + struct stat info; +diff --git a/liblvm/lvm_lv.c b/liblvm/lvm_lv.c +index 3b0e18d..d89e8ae 100644 +--- a/liblvm/lvm_lv.c ++++ b/liblvm/lvm_lv.c +@@ -507,7 +507,7 @@ static int _lv_set_pool_params(struct lvcreate_params *lp, + + lp->pool = pool; + +- lp->create_thin_pool = 1; ++ lp->create_pool = 1; + lp->segtype = get_segtype_from_string(vg->cmd, "thin-pool"); + lp->stripes = 1; + +@@ -757,17 +757,15 @@ struct lvm_property_value lvm_lv_params_get_property( + const lv_create_params_t params, + const char *name) + { +- struct lvm_property_value rc = { +- .is_valid = 0 +- }; +- struct saved_env e = store_user_env(params->vg->cmd); ++ struct lvm_property_value rc = { .is_valid = 0 }; + + if (params && params->magic == LV_CREATE_PARAMS_MAGIC) { ++ struct saved_env e = store_user_env(params->vg->cmd); + rc = get_property(NULL, NULL, NULL, NULL, NULL, ¶ms->lvp, NULL, name); +- } else { ++ restore_user_env(&e); ++ } else + log_error("Invalid lv_create_params parameter"); +- } +- restore_user_env(&e); ++ + return rc; + } + +@@ -775,15 +773,14 @@ int lvm_lv_params_set_property(lv_create_params_t params, const char *name, + struct lvm_property_value *prop) + { + int rc = -1; +- struct saved_env e = store_user_env(params->vg->cmd); + + if (params && params->magic == LV_CREATE_PARAMS_MAGIC) { ++ struct saved_env e = store_user_env(params->vg->cmd); + rc = set_property(NULL, NULL, NULL, ¶ms->lvp, NULL, name, prop); +- } else { ++ restore_user_env(&e); ++ } else + log_error("Invalid lv_create_params parameter"); +- } + +- restore_user_env(&e); + return rc; + } + +diff --git a/make.tmpl.in b/make.tmpl.in +index 65b1da0..7d7d701 100644 +--- a/make.tmpl.in ++++ b/make.tmpl.in +@@ -84,6 +84,7 @@ pkgconfigdir = $(usrlibdir)/pkgconfig + initdir = $(DESTDIR)$(sysconfdir)/rc.d/init.d + systemd_unit_dir = $(DESTDIR)@systemdsystemunitdir@ + systemd_generator_dir = $(DESTDIR)@systemdutildir@/system-generators ++systemd_dir = $(DESTDIR)@systemdutildir@ + tmpfiles_dir = $(DESTDIR)@tmpfilesdir@ + ocf_scriptdir = $(DESTDIR)@OCFDIR@ + pyexecdir = $(DESTDIR)$(prefix) +diff --git a/man/lvchange.8.in b/man/lvchange.8.in +index 49337e4..64231c8 100644 +--- a/man/lvchange.8.in ++++ b/man/lvchange.8.in +@@ -74,8 +74,9 @@ logical volumes. If autoactivation option is used (\-aay), + the logical volume is activated only if it matches an item in + the activation/auto_activation_volume_list set in lvm.conf. + If this list is not set, then all volumes are considered for +-autoactivation. The autoactivation is not yet supported for +-logical volumes that are part of partial or clustered volume groups. ++activation. The \-aay option should be also used during system ++boot so it's possible to select which volumes to activate using ++the activation/auto_activation_volume_list setting. + .IP + If clustered locking is enabled, -aey will activate exclusively + on one node and -aly will activate only on the local node. +diff --git a/man/lvconvert.8.in b/man/lvconvert.8.in +index c8cb2dc..190e221 100644 +--- a/man/lvconvert.8.in ++++ b/man/lvconvert.8.in +@@ -113,6 +113,30 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot + .RB [ \-v | \-\-verbose ] + .RB [ \-\-version ] + .sp ++.B lvconvert \-\-type cache-pool ++.RB [ \-c | \-\-chunksize ++.IR ChunkSize [ bBsSkKmMgG ]] ++.RB [ \-\-cachemode ++.RI { writeback | writethrough }] ++.RB [[ \-\-poolmetadata ++.IR CachePoolMetadataLogicalVolume { Name | Path }] ++| ++.RB [ \-\-poolmetadatasize ++.IR CachePoolMetadataSize [ bBsSkKmMgG ]] ++.IR LogicalVolume [ Path ] ++.RI [ PhysicalVolume [ Path ][ :PE [ -PE ]]...] ++.RB [ \-h | \-? | \-\-help ] ++.RB [ \-v | \-\-verbose ] ++.RB [ \-\-version ] ++.sp ++.B lvconvert \-\-type cache ++.RB \-\-cachepool ++.IR CachePoolLV { Name | Path } ++.IR LogicalVolume [ Path ] ++.RB [ \-h | \-? | \-\-help ] ++.RB [ \-v | \-\-verbose ] ++.RB [ \-\-version ] ++.sp + + .SH DESCRIPTION + lvconvert is used to change the segment type (i.e. linear, mirror, etc) or +@@ -146,6 +170,10 @@ arguments is required. + .BR \-b ", " \-\-background + Run the daemon in the background. + .TP ++.BR \-\-cachepool " " \fCachePoolLV ++This argument is necessary when converting a logical volume to a cache LV. ++For more information on cache pool LVs and cache LVs, see \fBlvm\fP(8). ++.TP + .BR \-m ", " \-\-mirrors " " \fIMirrors + Specifies the degree of the mirror you wish to create. + For example, "\fB-m 1\fP" would convert the original logical +@@ -176,9 +204,9 @@ A mirror is divided into regions of this size (in MB), and the mirror log + uses this granularity to track which regions are in sync. + .TP + .B \-\-type \fISegmentType +-Used to convert a logical volume to another segment type or to explicitly state +-the desired RAID1 segment type (\fImirror\fP or \fIraid1\fP) when converting +-a linear logical volume to a mirror with the \fB-m\fP argument. ++Used to convert a logical volume to another segment type, like cache-pool, ++cache, raid1, or thin-pool. When converting a logical volume to a cache LV, ++the \-\-cachepool argument is required. + .TP + .BR \-i ", " \-\-interval " " \fISeconds + Report progress as a percentage at regular intervals. +@@ -447,6 +475,18 @@ For the read-only external origin use the new name "vg00/external". + .sp + .B lvconvert \-\-thinpool vg00/lvpool \-\-originname external -T vg00/origin + ++Convert an existing logical volume to a cache pool LV using the ++given cache metadata LV. ++.sp ++.B lvconvert --type cache-pool --poolmetadata vg00/lvx_meta vg00/lvx_data ++.br ++.B lvrename vg00/lvx_data vg00/lvx_cachepool ++ ++Convert an existing logical volume to a cache LV using the given ++cache pool LV. ++.sp ++.B lvconvert \-\-type cache \-\-cachepool vg00/lvx_cachepool vg00/lvx ++ + .SH SEE ALSO + .BR lvm (8), + .BR lvm.conf (5), +diff --git a/man/lvcreate.8.in b/man/lvcreate.8.in +index 4a769cb..325baad 100644 +--- a/man/lvcreate.8.in ++++ b/man/lvcreate.8.in +@@ -59,6 +59,8 @@ lvcreate \- create a logical volume in an existing volume group + .RI { ReadAheadSectors | auto | none }] + .RB [ \-t | \-\-test ] + .RB [ \-T | \-\-thin ++.RB [ \-\-cachemode ++.IR { writeback | writethrough } + .RB [ \-c | \-\-chunksize + .IR ChunkSize [ bBsSkKmMgG ]] + .RB [ \-\-discards +@@ -155,14 +157,29 @@ use \fBlvs\fP command where the state of the flag is reported within + .TP + .IR \fB\-K ", " \fB\-\-ignoreactivationskip + Ignore the flag to skip Logical Volumes during activation. ++ ++.TP ++.BR \-\-cachemode " " { writeback | writethrough } ++Specifying a cache mode determines when the writes to a cache LV ++are considered complete. When \fIwriteback\fP is specified, a write is ++considered complete as soon as it is stored in the cache pool LV. ++If \fIwritethough\fP is specified, a write is considered complete only ++when it has been stored in the cache pool LV and on the origin LV. ++While \fIwritethrough\fP may be slower for writes, it is more ++resilient if something should happen to a device associated with the ++cache pool LV. ++ + .TP + .BR \-c ", " \-\-chunksize " " \fIChunkSize [ \fIbBsSkKmMgG ] +-Gives the size of chunk for snapshot and thin pool logical volumes. ++Gives the size of chunk for snapshot, cache pool and thin pool logical volumes. + Default unit is in kilobytes. + .br + For snapshots the value must be power of 2 between 4KiB and 512KiB + and the default value is 4. + .br ++For cache pool LVs the value must be between 32KiB and 1GiB. The default ++is 64KiB. Values must be a multiple of 32KiB. ++.br + For thin pools the value must be between 64KiB and + 1GiB and the default value starts with 64 and scales + up to fit the pool metadata size within 128MiB, +@@ -188,7 +205,11 @@ the extra devices which are necessary for parity are + internally accounted for. Specifying + .BI \-i 3 + would use 3 devices for striped logical volumes, +-4 devices for RAID 4/5, and 5 devices for RAID 6. ++4 devices for RAID 4/5, and 5 devices for RAID 6. Alternatively, ++RAID 4/5/6 will stripe across all PVs in the volume group or ++all of the PVs specified if the ++.B \-i ++argument is omitted. + .TP + .BR \-I ", " \-\-stripesize " " \fIStripeSize + Gives the number of kilobytes for the granularity of the stripes. +@@ -203,14 +224,17 @@ is specified. + .TP + .IR \fB\-l ", " \fB\-\-extents " " LogicalExtentsNumber [ % { VG | PVS | FREE | ORIGIN }] + Gives the number of logical extents to allocate for the new +-logical volume. ++logical volume. The total number of physical extents allocated will be ++greater than this, for example, if the volume is mirrored. + The number can also be expressed as a percentage of the total space + in the Volume Group with the suffix \fI%VG\fR, as a percentage of the + remaining free space in the Volume Group with the suffix \fI%FREE\fR, as a + percentage of the remaining free space for the specified + PhysicalVolume(s) with the suffix \fI%PVS\fR, or (for a snapshot) as a + percentage of the total space in the Origin Logical Volume with the +-suffix \fI%ORIGIN\fR. ++suffix \fI%ORIGIN\fR. When expressed as a percentage, the number is treated ++as an approximate upper limit for the total number of physical extents ++to be allocated (including extents used by any mirrors, for example). + .TP + .IR \fB\-L ", " \fB\-\-size " " LogicalVolumeSize [ bBsSkKmMgGtTpPeE ] + Gives the size to allocate for the new logical volume. +@@ -382,6 +406,7 @@ commandline switch alias that will enable their use + However, this argument must be used when no existing + commandline switch alias is available for the desired type, + as is the case with ++.IR cache , + .IR error , + .IR raid1 , + .IR raid4 , +@@ -390,6 +415,9 @@ as is the case with + .IR raid10 + or + .IR zero . ++Note that the cache segment type requires a dm-cache kernel module version ++1.3.0 or greater. ++ + .TP + .BR \-V ", " \-\-virtualsize " " \fIVirtualSize [ \fIbBsSkKmMgGtTpPeE ] + Creates a sparse device of the given size (in MiB by default) using a snapshot +@@ -471,8 +499,14 @@ a parity drive for a total of 4 devices) and a stripesize of 64KiB: + .sp + .B lvcreate \-\-type raid5 \-L 5G \-i 3 \-I 64 \-n my_lv vg00 + ++Creates a RAID5 logical volume "vg00/my_lv", using all of the free ++space in the VG and spanning all the PVs in the VG: ++.sp ++.B lvcreate \-\-type raid5 \-l 100%FREE \-n my_lv vg00 ++ + Creates a 5GiB RAID10 logical volume "vg00/my_lv", with 2 stripes on +-2 2-way mirrors. Note that the \fB-i\fP and \fB-m\fP arguments behave differently. ++2 2-way mirrors. Note that the \fB-i\fP and \fB-m\fP arguments behave ++differently. + The \fB-i\fP specifies the number of stripes. + The \fB-m\fP specifies the number of + .B additional +@@ -499,6 +533,23 @@ in vg00 that will use an existing thin pool "vg00/pool": + .sp + .B lvcreate -s --thinpool vg00/pool origin + ++Create a cache pool LV that can later be used to cache one ++logical volume. ++.sp ++.B lvcreate --type cache-pool -L 1G -n my_lv_cachepool vg /dev/fast1 ++ ++If there is an existing cache pool LV, create the large slow ++device (i.e. the origin LV) and link it to the supplied cache pool LV, ++creating a cache LV. ++.sp ++.B lvcreate --type cache -L 100G -n my_lv vg/my_lv_cachepool /dev/slow1 ++ ++If there is an existing logical volume, create the small and fast ++cache pool LV and link it to the supplied existing logical ++volume (i.e. the origin LV), creating a cache LV. ++.sp ++.B lvcreate --type cache -L 1G -n my_lv_cachepool vg/my_lv /dev/fast1 ++ + .SH SEE ALSO + .BR lvm (8), + .BR lvm.conf (5), +diff --git a/man/lvextend.8.in b/man/lvextend.8.in +index c51a9dc..d9bc0c8 100644 +--- a/man/lvextend.8.in ++++ b/man/lvextend.8.in +@@ -45,6 +45,8 @@ Proceed with size extension without prompting. + Extend or set the logical volume size in units of logical extents. + With the '\fI+\fP' sign the value is added to the actual size + of the logical volume and without it, the value is taken as an absolute one. ++The total number of physical extents allocated will be ++greater than this, for example, if the volume is mirrored. + The number can also be expressed as a percentage of the total space + in the Volume Group with the suffix \fI%VG\fP, relative to the existing + size of the Logical Volume with the suffix \fI%LV\fP, of the remaining +@@ -53,6 +55,11 @@ as a percentage of the remaining free space in the Volume Group + with the suffix \fI%FREE\fP, or (for a snapshot) as a percentage of the total + space in the Origin Logical Volume with the suffix \fI%ORIGIN\fP. + The resulting value is rounded upward. ++N.B. In a future release, when expressed as a percentage with PVS, VG or FREE, ++the number will be treated as an approximate upper limit for the total number ++of physical extents to be allocated (including extents used by any mirrors, for ++example). The code may currently allocate more space than you might otherwise ++expect. + .TP + .IR \fB\-L ", " \fB\-\-size " [" + ] LogicalVolumeSize [ bBsSkKmMgGtTpPeE ] + Extend or set the logical volume size in units of megabytes. +diff --git a/man/lvm.8.in b/man/lvm.8.in +index d78281a..39e47d3 100644 +--- a/man/lvm.8.in ++++ b/man/lvm.8.in +@@ -56,6 +56,8 @@ loading \fBlvm.conf\fP(5) and any other configuration files. + .TP + \fBsegtypes\fP \(em Display recognised Logical Volume segment types. + .TP ++\fBtags\fP \(em Display any tags defined on this host. ++.TP + \fBversion\fP \(em Display version information. + .LP + .SH COMMANDS +@@ -301,6 +303,12 @@ is executed. + The Volume Group name that is assumed for + any reference to a Logical Volume that doesn't specify a path. + Not set by default. ++.TP ++.B LVM_LVMETAD_PIDFILE ++Path for the lvmetad pid file. ++.TP ++.B LVM_LVMETAD_SOCKET ++Path for the lvmetad socket file. + .SH VALID NAMES + The following characters are valid for VG and LV names: + .B a-z A-Z 0-9 + _ . - +@@ -381,6 +389,197 @@ discretion over the layout. + To view the way the allocation process currently works in any specific + case, read the debug logging output, for example by adding \fB\-vvvv\fP to + a command. ++ ++.SH LOGICAL VOLUME TYPES ++Some logical volume types are simple to create and can be done with a ++single \fBlvcreate\fP(8) command. The linear and striped logical ++volume types are an example of this. Other logical volume types may ++require more than one command to create. The cache and thin provisioning ++types are examples of this. ++ ++.br ++.SS Cache ++The \fIcache\fP logical volume type uses a small and fast LV to improve ++the performance of a large and slow LV. It does this by storing the ++frequently used blocks on the faster LV. ++LVM refers to the small fast LV as a \fBcache pool LV\fP. The large ++slow LV is called the \fBorigin LV\fP. Due to requirements from dm-cache ++(the kernel driver), LVM further splits the cache pool LV into two ++devices - the \fBcache data LV\fP and \fBcache metadata LV\fP. The cache ++data LV is where copies of data blocks are kept from the ++origin LV to increase speed. The cache metadata LV holds the ++accounting information that specifies where data blocks are stored (e.g. ++on the origin LV or on the cache data LV). Users should be familiar with ++these LVs if they wish to create the best and most robust cached ++logical volumes. ++ ++.SS Cache Terms ++.nf ++origin LV OriginLV large slow LV ++cache data LV CacheDataLV small fast LV for cache pool data ++cache metadata LV CacheMetaLV small fast LV for cache pool metadata ++cache pool LV CachePoolLV CacheDataLV + CacheMetaLV ++cache LV CacheLV OriginLV + CachePoolLV ++.fi ++ ++.SS Cache Steps ++The steps to create a logical volume of \fIcache\fP type are as follows: ++.TP ++0. ++Create an LV or identify an existing LV to be the origin LV. ++.TP ++1. ++Create the cache data LV. The size of this LV is the size of the cache ++and will be reported as the size of the cache pool LV. ++.TP ++2. ++Create the cache metadata LV. ++The size of this LV should be 1000 times smaller than the cache data LV ++with a minimum size of 8MiB. ++.TP ++3. ++Create the cache pool LV by combining the cache data LV (from step 1) ++and cache metadata LV (from step 2). When performing this step, ++behavioral characteristics of the cache pool LV can be set. ++The name of the cache pool LV takes the name of the cache data LV and ++the cache data LV and cache metadata LV are renamed ++to CachePoolLV_cdata and CachePoolLV_cmeta. ++.TP ++4. ++Create a cache LV by linking the cache pool LV to the origin LV. ++The user accessible cache LV takes the name of the origin LV, ++while the origin LV becomes a hidden LV with the name ++OriginLV_corig. Users can perform this step while the origin LV ++is in use. ++ ++.P ++The steps above represent the best way to create a cache LV. ++They provide the most options and have the ability to create the ++most robust logical volumes. The examples below illustrate how these ++steps might be used in practice. ++ ++.SS Cache Commands ++.nf ++0. create OriginLV ++lvcreate -L LargeSize -n OriginLV VG SlowPVs ++ ++1. create CacheDataLV ++lvcreate -L CacheSize -n CacheDataLV VG FastPVs ++ ++2. create CacheMetaLV ++lvcreate -L MetaSize -n CacheMetaLV VG FastPVs ++ ++3. create CachePoolLV ++lvconvert --type cache-pool --poolmetadata VG/CacheMetaLV VG/CacheDataLV ++CachePoolLV takes the name of CacheDataLV. ++CacheDataLV is renamed CachePoolLV_cdata and becomes hidden. ++CacheMetaLV is renamed CachePoolLV_cmeta and becomes hidden. ++ ++4. create CacheLV ++lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV ++CacheLV takes the name of OriginLV. ++OriginLV is renamed OriginLV_corig and becomes hidden. ++.fi ++ ++.SS Cache Examples ++ ++.B Example 1: ++Creating a simple cache LV. ++.br ++ ++.nf ++0. Create the origin LV ++# lvcreate -L 10G -n lvx vg /dev/slow_dev ++ ++1. Create a cache data LV ++# lvcreate -L 1G -n lvx_cache vg /dev/fast_dev ++ ++2. Create a cache metadata LV (~1/1000th size of CacheDataLV or 8MiB) ++# lvcreate -L 8M -n lvx_cache_meta vg /dev/fast_dev ++ ++3. Create a cache pool LV, combining cache data LV and cache metadata LV ++# lvconvert --type cache-pool --poolmetadata vg/lvx_cache_meta \\ ++ vg/lvx_cache ++ ++4. Create a cached LV by combining the cache pool LV and origin LV ++# lvconvert --type cache --cachepool vg/lvx_cache vg/lvx ++.fi ++ ++.B Example 2: ++Creating a cache LV with a fault tolerant cache pool LV. ++ ++Users who are concerned about the possibility of failures in their fast devices ++that could lead to data loss might consider making their cache pool sub-LVs ++redundant. Example 2 illustrates how to do that. Note that only steps ++1 & 2 change. ++ ++.nf ++0. Create an origin LV we wish to cache ++# lvcreate -L 10G -n lvx vg /dev/slow_devs ++ ++1. Create a 2-way RAID1 cache data LV ++# lvcreate --type raid1 -m 1 -L 1G -n lvx_cache vg \\ ++ /dev/fast1 /dev/fast2 ++ ++2. Create a 2-way RAID1 cache metadata LV ++# lvcreate --type raid1 -m 1 -L 8M -n lvx_cache_meta vg \\ ++ /dev/fast1 /dev/fast2 ++ ++3. Create a cache pool LV combining cache data LV and cache metadata LV ++# lvconvert --type cache-pool --poolmetadata vg/lvx_cache_meta \\ ++ vg/lvx_cache ++ ++4. Create a cached LV by combining the cache pool LV and origin LV ++# lvconvert --type cache --cachepool vg/lvx_cache vg/lvx ++.fi ++ ++.B Example 3: ++Creating a simple cache LV with \fIwritethough\fP caching. ++ ++Some users wish to ensure that any data written will be stored both in the ++cache pool LV and on the origin LV. The loss of a device associated with ++the cache pool LV in this case would not mean the loss of any data. When ++combining the cache data LV and the cache metadata LV to form the cache pool ++LV, properties of the cache can be specified - in this case, ++\fIwritethrough\fP vs. \fIwriteback\fP. Note that only step 3 is affected ++in this case. ++ ++.nf ++0. Create an origin LV we wish to cache (yours may already exist) ++# lvcreate -L 10G -n lvx vg /dev/slow ++ ++1. Create a cache data LV ++# lvcreate -L 1G -n lvx_cache vg /dev/fast ++ ++2. Create a cache metadata LV ++# lvcreate -L 8M -n lvx_cache_meta vg /dev/fast ++ ++3. Create a cache pool LV specifying cache mode "writethrough" ++# lvconvert --type cache-pool --poolmetadata vg/lvx_cache_meta \\ ++ --cachemode writethrough vg/lvx_cache ++ ++4. Create a cache LV by combining the cache pool LV and origin LV ++# lvconvert --type cache --cachepool vg/lvx_cache vg/lvx ++.fi ++ ++.SS Removing Cache Logical Volumes ++If you wish to remove all logical volumes associated with a cache ++LV, you must remove both top-level, user-visible devices. ++The cache metadata LV and cache data LV cannot be removed ++directly. If only the cache pool LV is specfied for removal, any cached ++blocks not yet on the origin LV will be flush, the cache pool LV will be ++removed, and the now un-cached origin LV will remain. If the user ++specifies a cache LV for removal, then the origin LV is ++removed and only the cache pool LV will remain. The cache pool LV can then ++be used to create another cache LV with a different origin LV if desired. ++ ++When users intend to remove all logical volumes associated with a ++cache LV, it is generally better to start with the origin LV and then ++remove the cache pool LV. If the operations are performed in the ++reverse order, the user will have to wait for the contents of the ++cache pool LV to be flushed before the origin LV is removed. This ++could take some time. ++ + .SH DIAGNOSTICS + All tools return a status code of zero on success or non-zero on failure. + .SH FILES +diff --git a/man/lvm.conf.5.in b/man/lvm.conf.5.in +index 2786df2..73fcf47 100644 +--- a/man/lvm.conf.5.in ++++ b/man/lvm.conf.5.in +@@ -102,7 +102,7 @@ e.g. backup { + .br + An assignment associates a type with an identifier. + .br +-e.g. max_archives = 42 ++e.g. level = 7 + .br + .TP + .BR array " = '" [ "' ( " type " '" , "')* " type " '" ] "' | '" [ "' '" ] ' +diff --git a/man/lvreduce.8.in b/man/lvreduce.8.in +index 2c38f5b..2b0f3f8 100644 +--- a/man/lvreduce.8.in ++++ b/man/lvreduce.8.in +@@ -51,6 +51,8 @@ Reduce or set the logical volume size in units of logical extents. + With the \fI-\fP sign the value will be subtracted from + the logical volume's actual size and without it the value will be taken + as an absolute size. ++The total number of physical extents freed will be greater than this logical ++value if, for example, the volume is mirrored. + The number can also be expressed as a percentage of the total space + in the Volume Group with the suffix \fI%VG\fP, relative to the existing + size of the Logical Volume with the suffix \fI%LV\fP, as a percentage of the +@@ -59,6 +61,10 @@ a snapshot) as a percentage of the total space in the Origin Logical + Volume with the suffix \fI%ORIGIN\fP. + The resulting value for the subtraction is rounded downward, for the absolute + size it is rounded upward. ++N.B. In a future release, when expressed as a percentage with VG or FREE, the ++number will be treated as an approximate total number of physical extents to be ++freed (including extents used by any mirrors, for example). The code may ++currently release more space than you might otherwise expect. + .TP + .IR \fB\-L ", " \fB\-\-size " [" \- ] LogicalVolumeSize [ bBsSkKmMgGtTpPeE ] + Reduce or set the logical volume size in units of megabytes. +diff --git a/man/lvresize.8.in b/man/lvresize.8.in +index 3606762..75d20a0 100644 +--- a/man/lvresize.8.in ++++ b/man/lvresize.8.in +@@ -49,6 +49,8 @@ Resize underlying filesystem together with the logical volume using + Change or set the logical volume size in units of logical extents. + With the \fI+\fP or \fI-\fP sign the value is added to or subtracted from the actual size + of the logical volume and without it, the value is taken as an absolute one. ++The total number of physical extents affected will be ++greater than this if, for example, the volume is mirrored. + The number can also be expressed as a percentage of the total space + in the Volume Group with the suffix \fI%VG\fP, relative to the existing + size of the Logical Volume with the suffix \fI%LV\fP, as a percentage of +@@ -58,6 +60,11 @@ Volume Group with the suffix \fI%FREE\fP, or (for a snapshot) as a percentage + of the total space in the Origin Logical Volume with the suffix \fI%ORIGIN\fP. + The resulting value is rounded downward for the subtraction otherwise + it is rounded upward. ++N.B. In a future release, when expressed as a percentage with PVS, VG or FREE, ++the number will be treated as an approximate total number of physical extents ++to be allocated or freed (including extents used by any mirrors, for example). ++The code may currently allocate or remove more space than you might otherwise ++expect. + .TP + .IR \fB\-L ", " \fB\-\-size " [" + | - ] LogicalVolumeSize [ bBsSkKmMgGtTpPeE ] + Change or set the logical volume size in units of megabytes. +@@ -88,7 +95,9 @@ Defaults to whatever the last segment of the Logical Volume uses. + Not applicable to LVs using the original metadata LVM format, which + must use a single value throughout. + .br +-StripeSize must be 2^n (n = 2 to 9). ++StripeSize must be 2^n (n = 2 to 9) for metadata in LVM1 format. ++For metadata in LVM2 format, the stripe size may be a larger ++power of 2 but must not exceed the physical extent size. + .TP + .B \-\-noudevsync + Disable udev synchronisation. The +diff --git a/man/lvs.8.in b/man/lvs.8.in +index 8427aca..95eb25a 100644 +--- a/man/lvs.8.in ++++ b/man/lvs.8.in +@@ -135,12 +135,12 @@ can also be chosen. + The lv_attr bits are: + .RS + .IP 1 3 +-Volume type: (m)irrored, (M)irrored without initial sync, (o)rigin, ++Volume type: (C)ache, (m)irrored, (M)irrored without initial sync, (o)rigin, + (O)rigin with merging snapshot, (r)aid, (R)aid without initial sync, + (s)napshot, merging (S)napshot, (p)vmove, (v)irtual, + mirror or raid (i)mage, mirror or raid (I)mage out-of-sync, mirror (l)og device, + under (c)onversion, thin (V)olume, (t)hin pool, (T)hin pool data, raid or +-thin pool m(e)tadata or pool metadata spare. ++pool m(e)tadata or pool metadata spare. + .IP 2 3 + Permissions: (w)riteable, (r)ead-only, (R)ead-only activation of non-read-only + volume +diff --git a/man/vgchange.8.in b/man/vgchange.8.in +index 823d134..16adf4df 100644 +--- a/man/vgchange.8.in ++++ b/man/vgchange.8.in +@@ -76,9 +76,10 @@ In other words, makes the logical volumes known/unknown to the kernel. + If autoactivation option is used (\-aay), each logical volume in + the volume group is activated only if it matches an item in the + activation/auto_activation_volume_list set in lvm.conf. If this +-list is not set, then all volumes are considered for autoactivation. +-The autoactivation is not yet supported for partial or clustered +-volume groups. ++list is not set, then all volumes are considered for activation. ++The \-aay option should be also used during system boot so it's ++possible to select which volumes to activate using the ++activation/auto_activation_volume_list settting. + .IP + If clustered locking is enabled, add 'e' to activate/deactivate + exclusively on one node or 'l' to activate/deactivate only +diff --git a/python/liblvm.c b/python/liblvm.c +index 6abd5ff..094aec2 100644 +--- a/python/liblvm.c ++++ b/python/liblvm.c +@@ -1043,22 +1043,22 @@ static PyObject *_liblvm_lvm_vg_list_lvs(vgobject *self) + + static PyObject *_liblvm_lvm_vg_get_tags(vgobject *self) + { +- struct dm_list *tags; ++ struct dm_list *tagsl; + struct lvm_str_list *strl; + PyObject * pytuple; + int i = 0; + + VG_VALID(self); + +- if (!(tags = lvm_vg_get_tags(self->vg))) { ++ if (!(tagsl = lvm_vg_get_tags(self->vg))) { + PyErr_SetObject(_LibLVMError, _liblvm_get_last_error()); + return NULL; + } + +- if (!(pytuple = PyTuple_New(dm_list_size(tags)))) ++ if (!(pytuple = PyTuple_New(dm_list_size(tagsl)))) + return NULL; + +- dm_list_iterate_items(strl, tags) { ++ dm_list_iterate_items(strl, tagsl) { + PyTuple_SET_ITEM(pytuple, i, PyString_FromString(strl->str)); + i++; + } +@@ -1163,8 +1163,9 @@ static PyObject *_liblvm_lvm_vg_create_lv_thin(vgobject *self, PyObject *args) + static void liblvm_lv_dealloc(lvobject *self) + { + /* We can dealloc an object that didn't get fully created */ +- if (self->parent_vgobj) ++ if (self->parent_vgobj) { + Py_DECREF(self->parent_vgobj); ++ } + + PyObject_Del(self); + } +@@ -1291,11 +1292,13 @@ static PyObject *_liblvm_lvm_pv_from_uuid(vgobject *self, PyObject *arg) + + static void _liblvm_pv_dealloc(pvobject *self) + { +- if (self->parent_vgobj) ++ if (self->parent_vgobj) { + Py_DECREF(self->parent_vgobj); ++ } + +- if (self->parent_pvslistobj) ++ if (self->parent_pvslistobj) { + Py_DECREF(self->parent_pvslistobj); ++ } + + self->parent_vgobj = NULL; + self->parent_pvslistobj = NULL; +@@ -1474,22 +1477,22 @@ static PyObject *_liblvm_lvm_lv_remove_tag(lvobject *self, PyObject *args) + + static PyObject *_liblvm_lvm_lv_get_tags(lvobject *self) + { +- struct dm_list *tags; ++ struct dm_list *tagsl; + struct lvm_str_list *strl; + PyObject * pytuple; + int i = 0; + + LV_VALID(self); + +- if (!(tags = lvm_lv_get_tags(self->lv))) { ++ if (!(tagsl = lvm_lv_get_tags(self->lv))) { + PyErr_SetObject(_LibLVMError, _liblvm_get_last_error()); + return NULL; + } + +- if (!(pytuple = PyTuple_New(dm_list_size(tags)))) ++ if (!(pytuple = PyTuple_New(dm_list_size(tagsl)))) + return NULL; + +- dm_list_iterate_items(strl, tags) { ++ dm_list_iterate_items(strl, tagsl) { + PyTuple_SET_ITEM(pytuple, i, PyString_FromString(strl->str)); + i++; + } +diff --git a/scripts/Makefile.in b/scripts/Makefile.in +index ed587ca..8fdb5f7 100644 +--- a/scripts/Makefile.in ++++ b/scripts/Makefile.in +@@ -111,6 +111,14 @@ ifeq ("@BUILD_LVMETAD@", "yes") + $(INSTALL_DATA) lvm2_lvmetad_systemd_red_hat.service $(systemd_unit_dir)/lvm2-lvmetad.service + $(INSTALL_DATA) lvm2_pvscan_systemd_red_hat@.service $(systemd_unit_dir)/lvm2-pvscan@.service + endif ++ifneq ("@CLVMD@", "none") ++ $(INSTALL_DATA) lvm2_clvmd_systemd_red_hat.service $(systemd_unit_dir)/lvm2-clvmd.service ++ $(INSTALL_DATA) lvm2_cluster_activation_systemd_red_hat.service $(systemd_unit_dir)/lvm2-cluster-activation.service ++ $(INSTALL_DATA) lvm2_cluster_activation_red_hat.sh $(systemd_dir)/lvm2-cluster-activation ++endif ++ifeq ("@BUILD_CMIRRORD@", "yes") ++ $(INSTALL_DATA) lvm2_cmirrord_systemd_red_hat.service $(systemd_unit_dir)/lvm2-cmirrord.service ++endif + + install_tmpfiles_configuration: + $(INSTALL_DIR) $(tmpfiles_dir) +@@ -124,4 +132,7 @@ DISTCLEAN_TARGETS += clvmd_init_red_hat cmirrord_init_red_hat \ + lvm2_pvscan_systemd_red_hat@.service \ + lvm2_tmpfiles_red_hat.conf blk_availability_init_red_hat \ + blk_availability_systemd_red_hat.service \ +- blkdeactivate.sh ++ blkdeactivate.sh lvm2_clvmd_systemd_red_hat.service \ ++ lvm2_cmirrord_systemd_red_hat.service \ ++ lvm2_cluster_activation_systemd_red_hat.service \ ++ lvm2_cluster_activation_red_hat.sh +diff --git a/scripts/dm_event_systemd_red_hat.service.in b/scripts/dm_event_systemd_red_hat.service.in +index 96c5225..3791618 100644 +--- a/scripts/dm_event_systemd_red_hat.service.in ++++ b/scripts/dm_event_systemd_red_hat.service.in +@@ -7,9 +7,8 @@ Before=local-fs.target + DefaultDependencies=no + + [Service] +-Type=forking +-ExecStart=@sbindir@/dmeventd +-ExecReload=@sbindir@/dmeventd -R ++Type=simple ++ExecStart=@sbindir@/dmeventd -f + Environment=SD_ACTIVATION=1 + PIDFile=@DMEVENTD_PIDFILE@ + OOMScoreAdjust=-1000 +diff --git a/scripts/lvm2_activation_generator_systemd_red_hat.c b/scripts/lvm2_activation_generator_systemd_red_hat.c +index f2e2e49..07a2563 100644 +--- a/scripts/lvm2_activation_generator_systemd_red_hat.c ++++ b/scripts/lvm2_activation_generator_systemd_red_hat.c +@@ -150,7 +150,7 @@ static int generate_unit(const char *dir, int unit) + "[Service]\n", f); + } + +- fputs("ExecStart=" LVM_PATH " vgchange -aay --sysinit\n" ++ fputs("ExecStart=" LVM_PATH " vgchange -aay --sysinit --ignoreskippedcluster\n" + "Type=oneshot\n", f); + + if (fclose(f) < 0) { +diff --git a/scripts/lvm2_cluster_activation_red_hat.sh.in b/scripts/lvm2_cluster_activation_red_hat.sh.in +new file mode 100644 +index 0000000..0d4676c +--- /dev/null ++++ b/scripts/lvm2_cluster_activation_red_hat.sh.in +@@ -0,0 +1,70 @@ ++#!/bin/bash ++ ++sbindir=@sbindir@ ++ ++lvm_vgchange=${sbindir}/vgchange ++lvm_vgscan=${sbindir}/vgscan ++lvm_vgs=${sbindir}/vgs ++lvm_lvm=${sbindir}/lvm ++ ++parse_clustered_vgs() { ++ while read -r name attrs; ++ do ++ test "${attrs:5:1}" == 'c' && echo -n "$name " ++ done ++} ++ ++# NOTE: replace this with vgs, once display filter per attr is implemented. ++clustered_vgs() { ++ ${lvm_vgs} -o vg_name,vg_attr --noheadings | parse_clustered_vgs ++} ++ ++activate() { ++ eval local $(${lvm_lvm} dumpconfig devices/obtain_device_list_from_udev 2>/dev/null) 2>/dev/null ++ if [ $? -ne 0 ]; then ++ echo "Warning: expected single couple of key=value in output of dumpconfig" ++ fi ++ ++ if [ -z $obtain_device_list_from_udev -o $obtain_device_list_from_udev -ne 1 ]; then ++ echo -n "lvm.conf option obtain_device_list_from_udev!=1: Executing vgscan" ++ ${lvm_vgscan} > /dev/null 2>&1 ++ fi ++ ++ echo -n "Activating ${LVM_VGS:-"all VG(s)"}: " ++ # Respect activation/auto_activation_volume_list! ++ # Call "-aay" which is equal to "-aly" but respects this list. ++ ${lvm_vgchange} -aay $LVM_VGS || return 1 ++ ++ return 0 ++} ++ ++deactivate() ++{ ++ # NOTE: following section will be replaced by blkdeactivate script ++ # with option supporting request to deactivate all clustered volume ++ # groups in the system ++ [ -z $LVM_VGS ] && LVM_VGS="$(clustered_vgs)" ++ if [ -n "$LVM_VGS" ]; then ++ echo -n "Deactivating clustered VG(s): " ++ ${lvm_vgchange} -anl $LVM_VGS || return 1 ++ fi ++ ++ return 0 ++} ++ ++case "$1" in ++ deactivate) ++ deactivate ++ rtrn=$? ++ ;; ++ activate) ++ activate ++ rtrn=$? ++ ;; ++ *) ++ echo $"Usage: $0 {activate|deactivate}" ++ rtrn=3 ++ ;; ++esac ++ ++exit $rtrn +diff --git a/scripts/lvm2_cluster_activation_systemd_red_hat.service.in b/scripts/lvm2_cluster_activation_systemd_red_hat.service.in +new file mode 100644 +index 0000000..970e93a +--- /dev/null ++++ b/scripts/lvm2_cluster_activation_systemd_red_hat.service.in +@@ -0,0 +1,17 @@ ++[Unit] ++Description=Clustered LVM volumes activation service ++Requires=lvm2-clvmd.service ++After=lvm2-clvmd.service lvm2-cmirrord.service ++OnFailure=lvm2-clvmd.service ++DefaultDependencies=no ++Conflicts=shutdown.target ++ ++[Service] ++Type=simple ++RemainAfterExit=yes ++EnvironmentFile=-@sysconfdir@/sysconfig/clvmd ++ExecStart=@systemdutildir@/lvm2-cluster-activation activate ++ExecStop=@systemdutildir@/lvm2-cluster-activation deactivate ++ ++[Install] ++WantedBy=multi-user.target +diff --git a/scripts/lvm2_clvmd_systemd_red_hat.service.in b/scripts/lvm2_clvmd_systemd_red_hat.service.in +new file mode 100644 +index 0000000..2978d21 +--- /dev/null ++++ b/scripts/lvm2_clvmd_systemd_red_hat.service.in +@@ -0,0 +1,23 @@ ++[Unit] ++Description=Clustered LVM daemon ++Documentation=man:clvmd(8) ++After=dlm.service corosync.service ++Before=remote-fs.target ++Requires=network.target dlm.service corosync.service ++RefuseManualStart=true ++RefuseManualStop=true ++StopWhenUnneeded=true ++DefaultDependencies=no ++Conflicts=shutdown.target ++ ++[Service] ++Type=forking ++Environment=CLVMD_OPTS=-T30 ++EnvironmentFile=-@sysconfdir@/sysconfig/clvmd ++ExecStart=@sbindir@/clvmd $CLVMD_OPTS ++SuccessExitStatus=5 ++TimeoutStartSec=30 ++TimeoutStopSec=10 ++OOMScoreAdjust=-1000 ++Restart=on-abort ++PIDFile=@CLVMD_PIDFILE@ +diff --git a/scripts/lvm2_cmirrord_systemd_red_hat.service.in b/scripts/lvm2_cmirrord_systemd_red_hat.service.in +new file mode 100644 +index 0000000..16d38ce +--- /dev/null ++++ b/scripts/lvm2_cmirrord_systemd_red_hat.service.in +@@ -0,0 +1,17 @@ ++[Unit] ++Description=Clustered LVM mirror log daemon ++Documentation=man:cmirrord(8) ++Requires=corosync.service ++After=corosync.service ++Before=remote-fs.target ++DefaultDependencies=no ++Conflicts=shutdown.target ++ ++[Service] ++Type=forking ++ExecStart=@sbindir@/cmirrord ++PIDFile=@CMIRRORD_PIDFILE@ ++Restart=on-abort ++ ++[Install] ++WantedBy=multi-user.target +diff --git a/scripts/lvm2_lvmetad_systemd_red_hat.service.in b/scripts/lvm2_lvmetad_systemd_red_hat.service.in +index 0150726..8f4c60d 100644 +--- a/scripts/lvm2_lvmetad_systemd_red_hat.service.in ++++ b/scripts/lvm2_lvmetad_systemd_red_hat.service.in +@@ -7,10 +7,9 @@ DefaultDependencies=no + Conflicts=shutdown.target + + [Service] +-Type=forking ++Type=simple + NonBlocking=true +-ExecStart=@sbindir@/lvmetad +-ExecReload=@sbindir@/lvmetad -R ++ExecStart=@sbindir@/lvmetad -f + Environment=SD_ACTIVATION=1 + Restart=on-abort + PIDFile=@LVMETAD_PIDFILE@ +diff --git a/scripts/lvm2_monitoring_init_red_hat.in b/scripts/lvm2_monitoring_init_red_hat.in +index cae652c..44de07f 100644 +--- a/scripts/lvm2_monitoring_init_red_hat.in ++++ b/scripts/lvm2_monitoring_init_red_hat.in +@@ -48,10 +48,10 @@ start() + { + ret=0 + # TODO do we want to separate out already active groups only? +- VGSLIST=`$VGS --noheadings -o name --config 'log{command_names=0 prefix=" "}' 2> /dev/null` ++ VGSLIST=`$VGS --noheadings -o name --ignoreskippedcluster --config 'log{command_names=0 prefix=" "}' 2> /dev/null` + for vg in $VGSLIST + do +- action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y --poll y --config 'log{command_names=0 prefix=" "}' $vg || ret=$? ++ action "Starting monitoring for VG $vg:" $VGCHANGE --monitor y --poll y --ignoreskippedcluster --config 'log{command_names=0 prefix=" "}' $vg || ret=$? + done + + return $ret +@@ -66,10 +66,10 @@ stop() + echo "Not stopping monitoring, this is a dangerous operation. Please use force-stop to override." + return 1 + fi +- VGSLIST=`$VGS --noheadings -o name --config 'log{command_names=0 prefix=" "}' 2> /dev/null` ++ VGSLIST=`$VGS --noheadings -o name --ignoreskippedcluster --config 'log{command_names=0 prefix=" "}' 2> /dev/null` + for vg in $VGSLIST + do +- action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n --config 'log{command_names=0 prefix=" "}' $vg || ret=$? ++ action "Stopping monitoring for VG $vg:" $VGCHANGE --monitor n --ignoreskippedcluster --config 'log{command_names=0 prefix=" "}' $vg || ret=$? + done + return $ret + } +diff --git a/scripts/lvm2_monitoring_systemd_red_hat.service.in b/scripts/lvm2_monitoring_systemd_red_hat.service.in +index 670d0c4..05f911b 100644 +--- a/scripts/lvm2_monitoring_systemd_red_hat.service.in ++++ b/scripts/lvm2_monitoring_systemd_red_hat.service.in +@@ -10,9 +10,9 @@ Conflicts=shutdown.target + [Service] + Type=oneshot + Environment=LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES=1 +-ExecStart=@sbindir@/lvm vgchange --monitor y ++ExecStart=@sbindir@/lvm vgchange --monitor y --ignoreskippedcluster + # The lvmetad must be disabled here, it needs https://bugzilla.redhat.com/show_bug.cgi?id=843587 to be resolved first. +-ExecStop=@sbindir@/lvm vgchange --monitor n --config 'global{use_lvmetad=0}' ++ExecStop=@sbindir@/lvm vgchange --monitor n --config 'global{use_lvmetad=0}' --ignoreskippedcluster + RemainAfterExit=yes + + [Install] +diff --git a/test/api/pytest.sh b/test/api/pytest.sh +index a0f9a2f..8359c01 100644 +--- a/test/api/pytest.sh ++++ b/test/api/pytest.sh +@@ -20,19 +20,20 @@ + # Until fixed - testing always runs with enabled monitoring + # thus it needs dmeventd + # +-aux prepare_dmeventd +-test ! -e LOCAL_CLVMD || skip +-test ! -e LOCAL_LVMETAD || skip +- +-#If you change this change the unit test case too. +-aux prepare_pvs 6 + + #Locate the python binding library to use. + python_lib=$(find $abs_top_builddir -name lvm.so) +- + # Unable to test python bindings if library not available + test -n "$python_lib" || skip + ++test -e LOCAL_CLVMD && skip ++test -e LOCAL_LVMETAD && skip ++ ++aux prepare_dmeventd ++ ++#If you change this change the unit test case too. ++aux prepare_pvs 6 ++ + export PYTHONPATH=$(dirname $python_lib):$PYTHONPATH + + #Setup which devices the unit test can use. +diff --git a/test/lib/aux.sh b/test/lib/aux.sh +index 2a882a2..66ed910 100644 +--- a/test/lib/aux.sh ++++ b/test/lib/aux.sh +@@ -52,6 +52,7 @@ prepare_clvmd() { + + prepare_dmeventd() { + if pgrep dmeventd ; then ++ rm -f debug.log + echo "Cannot test dmeventd with real dmeventd ($(pgrep dmeventd)) running." + skip + fi +@@ -392,6 +393,7 @@ disable_dev() { + enable_dev() { + local dev + ++ rm -f debug.log + init_udev_transaction + for dev in "$@"; do + local name=$(echo "$dev" | sed -e 's,.*/,,') +@@ -536,7 +538,7 @@ generate_config() { + devices/dir = "$DM_DEV_DIR" + devices/scan = "$DM_DEV_DIR" + devices/filter = "a|.*|" +-devices/global_filter = [ "a|$DM_DEV_DIR/mirror|", "a|$DM_DEV_DIR/mapper/.*pv[0-9_]*$|", "r|.*|" ] ++devices/global_filter = [ "a|$DM_DEV_DIR/mapper/.*pv[0-9_]*$|", "r|.*|" ] + devices/cache_dir = "$TESTDIR/etc" + devices/sysfs_scan = 0 + devices/default_data_alignment = 1 +@@ -654,7 +656,7 @@ skip_if_raid456_replace_broken() { + + udev_wait() { + pgrep udev >/dev/null || return 0 +- which udevadm >/dev/null || return 0 ++ which udevadm &>/dev/null || return 0 + if test -n "$1" ; then + udevadm settle --exit-if-exists="$1" || true + else +@@ -689,6 +691,7 @@ can_use_16T() + # i.e. dm_target_at_least dm-thin-pool 1 0 + target_at_least() + { ++ rm -f debug.log + case "$1" in + dm-*) modprobe "$1" || true ;; + esac +diff --git a/test/lib/check.sh b/test/lib/check.sh +index 84604e3..9c55338 100644 +--- a/test/lib/check.sh ++++ b/test/lib/check.sh +@@ -240,7 +240,7 @@ inactive() { + die "$lv expected inactive, but lvs says it's not:" \ + $(lvl $lv -o+devices) + not dmsetup info $1-$2 2>/dev/null || \ +- die "$lv expected inactive, lvs thinks it is but there are mappings!" ++ die "$lv expected inactive, lvs thinks it is but there are mappings!" + } + + # Check for list of LVs from given VG +@@ -251,10 +251,25 @@ lv_exists() { + shift + lv="$lv $vg/$1" + done ++ test -n "$lv" || lv=$vg + lvl $lv &>/dev/null || \ + die "$lv expected to exist but does not" + } + ++lv_not_exists() { ++ local vg=$1 ++ if test $# -le 1 ; then ++ lvl $vg &>/dev/null || return ++ die "$vg expected to not exist but it does!" ++ else ++ while [ $# -gt 1 ]; do ++ shift ++ lvl $vg/$1 &>/dev/null || continue ++ die "$vg/$1 expected to not exist but it does!" ++ done ++ fi ++} ++ + pv_field() { + local actual=$(get pv_field "$1" "$2" "${@:4}") + test "$actual" = "$3" || \ +diff --git a/test/lib/get.sh b/test/lib/get.sh +index 5dd5451..babc4c9 100644 +--- a/test/lib/get.sh ++++ b/test/lib/get.sh +@@ -70,6 +70,13 @@ lv_tree_devices_() { + lv_tree_devices_ "$1" "$(lv_field_lv_ $lv data_lv)" + lv_tree_devices_ "$1" "$(lv_field_lv_ $lv metadata_lv)" + ;; ++ cache) ++ lv_tree_devices_ "$1" "$(lv_devices $lv)" ++ ;; ++ cache-pool) ++ lv_tree_devices_ "$1" "$(lv_field_lv_ $lv data_lv)" ++ lv_tree_devices_ "$1" "$(lv_field_lv_ $lv metadata_lv)" ++ ;; + esac + } + +diff --git a/test/lib/test.sh b/test/lib/test.sh +index 265d61d..71ad55d 100644 +--- a/test/lib/test.sh ++++ b/test/lib/test.sh +@@ -81,9 +81,9 @@ test -n "$BASH" && set -eE -o pipefail + aux lvmconf + aux prepare_clvmd + test -n "$LVM_TEST_LVMETAD" && { +- aux prepare_lvmetad + export LVM_LVMETAD_SOCKET="$TESTDIR/lvmetad.socket" + export LVM_LVMETAD_PIDFILE="$TESTDIR/lvmetad.pid" ++ aux prepare_lvmetad + } + echo "@TESTDIR=$TESTDIR" + echo "@PREFIX=$PREFIX" +diff --git a/test/shell/activation-skip.sh b/test/shell/activation-skip.sh +new file mode 100644 +index 0000000..7c80283 +--- /dev/null ++++ b/test/shell/activation-skip.sh +@@ -0,0 +1,32 @@ ++#!/bin/bash ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++. lib/test ++ ++# Test skip activation flag -k|--setactivationskip ++ ++aux prepare_vg ++ ++lvcreate -an --zero n -l 1 -n $lv1 $vg ++lvcreate -ky -K -l1 -n $lv2 $vg ++get lv_field $vg/$lv2 lv_attr | grep -- "-wi-a----k" ++ ++lvchange -ay -K $vg ++check active $vg $lv1 ++lvchange -an $vg ++ ++lvchange -ay --setactivationskip y $vg/$lv1 ++check inactive $vg $lv1 ++ ++get lv_field $vg/$lv1 lv_attr | grep -- "-wi------k" ++ ++lvchange -ay -K $vg ++check active $vg $lv1 +diff --git a/test/shell/lock-parallel.sh b/test/shell/lock-parallel.sh +new file mode 100644 +index 0000000..4820129 +--- /dev/null ++++ b/test/shell/lock-parallel.sh +@@ -0,0 +1,40 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Test parallel use of lvm commands and check locks aren't dropped ++# RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1049296 ++ ++. lib/test ++ ++which mkfs.ext3 || skip ++ ++aux prepare_vg ++ ++lvcreate -L10 -n $lv1 $vg ++lvcreate -l1 -n $lv2 $vg ++mkfs.ext3 "$DM_DEV_DIR/$vg/$lv1" ++ ++# Slowdown PV for resized LV ++aux delay_dev "$dev1" 20 20 ++ ++lvresize -L-5 -r $vg/$lv1 & ++ ++# Let's wait till resize starts ++sleep 2 ++ ++lvremove -f $vg/$lv2 ++ ++wait ++ ++aux enable_dev "$dev1" ++ ++# Check removed $lv2 does not reappear ++not check lv_exists $vg $lv2 +diff --git a/test/shell/lvchange-partial.sh b/test/shell/lvchange-partial.sh +index 891dd0a..b79004d 100644 +--- a/test/shell/lvchange-partial.sh ++++ b/test/shell/lvchange-partial.sh +@@ -11,11 +11,13 @@ + + . lib/test + +-aux target_at_least dm-raid 1 1 0 || skip + + aux prepare_vg 4 + +-lvcreate --type raid1 -m 1 -l 2 -n $lv1 $vg ++TYPE=raid1 ++aux target_at_least dm-raid 1 1 0 || TYPE=mirror ++ ++lvcreate -aey --type $TYPE -m 1 -l 2 -n $lv1 $vg + lvchange -an $vg/$lv1 + aux disable_dev "$dev1" + +diff --git a/test/shell/lvchange-raid.sh b/test/shell/lvchange-raid.sh +index 91c8e8d..0ee5290 100644 +--- a/test/shell/lvchange-raid.sh ++++ b/test/shell/lvchange-raid.sh +@@ -19,7 +19,7 @@ THIN_POSTFIX="" + # Proper mismatch count 1.5.2 upstream,1.3.5 < x < 1.4.0 in RHEL6 + # + # We will simplify and simple test for 1.5.2 and 1.3.5 < x < 1.4.0 +-aux target_at_least dm-raid 1 3 5 && ++aux target_at_least dm-raid 1 3 5 && + ! aux target_at_least dm-raid 1 4 0 || + aux target_at_least dm-raid 1 5 2 || skip + +@@ -29,108 +29,103 @@ aux prepare_vg 6 + + # run_writemostly_check + run_writemostly_check() { +- local d0 +- local d1 + local vg=$1 + local lv=${2}${THIN_POSTFIX} ++ local segtype=$(get lv_field $vg/$lv segtype -a) ++ local d0=$(get lv_devices $vg/${lv}_rimage_0) ++ local d1=$(get lv_devices $vg/${lv}_rimage_1) + + printf "#\n#\n#\n# %s/%s (%s): run_writemostly_check\n#\n#\n#\n" \ +- $vg $lv `lvs -a --noheadings -o segtype $vg/$lv` +- d0=`lvs -a --noheadings -o devices $vg/${lv}_rimage_0 | sed s/\(.\)//` +- d0=$(sed s/^[[:space:]]*// <<< "$d0") +- d1=`lvs -a --noheadings -o devices $vg/${lv}_rimage_1 | sed s/\(.\)//` +- d1=$(sed s/^[[:space:]]*// <<< "$d1") ++ $vg $lv $segtype + + # No writemostly flag should be there yet. +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_1 | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_1 lv_attr -a | grep '.*-.$' + +- if [ `lvs -a --noheadings -o segtype $vg/$lv` != "raid1" ]; then ++ if [ "$segtype" != "raid1" ]; then + not lvchange --writemostly $d0 $vg/$lv + return + fi + + # Set the flag + lvchange --writemostly $d0 $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' + + # Running again should leave it set (not toggle) + lvchange --writemostly $d0 $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' + + # Running again with ':y' should leave it set + lvchange --writemostly $d0:y $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' + + # ':n' should unset it + lvchange --writemostly $d0:n $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' + + # ':n' again should leave it unset + lvchange --writemostly $d0:n $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' + + # ':t' toggle to set + lvchange --writemostly $d0:t $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' + + # ':t' toggle to unset + lvchange --writemostly $d0:t $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' + + # ':y' to set + lvchange --writemostly $d0:y $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' + + # Toggle both at once + lvchange --writemostly $d0:t --writemostly $d1:t $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_1 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_1 lv_attr -a | grep '.*w.$' + + # Toggle both at once again + lvchange --writemostly $d0:t --writemostly $d1:t $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_1 | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_1 lv_attr -a | grep '.*-.$' + + # Toggle one, unset the other + lvchange --writemostly $d0:n --writemostly $d1:t $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_1 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_1 lv_attr -a | grep '.*w.$' + + # Toggle one, set the other + lvchange --writemostly $d0:y --writemostly $d1:t $vg/$lv +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_1 | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_1 lv_attr -a | grep '.*-.$' + + # Partial flag supercedes writemostly flag + aux disable_dev $d0 +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*p.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*p.$' + + # It is possible for the kernel to detect the failed device before + # we re-enable it. If so, the field will be set to 'r'efresh since + # that also takes precedence over 'w'ritemostly. If this has happened, + # we refresh the LV and then check for 'w'. + aux enable_dev $d0 +- if lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*r.$'; then +- lvchange --refresh $vg/$lv +- fi +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*w.$' ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*r.$' && lvchange --refresh $vg/$lv ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*w.$' + + # Catch Bad writebehind values + not lvchange --writebehind "invalid" $vg/$lv + not lvchange --writebehind -256 $vg/$lv + + # Set writebehind +- [ ! `lvs --noheadings -o raid_write_behind $vg/$lv` ] ++ check lv_field $vg/$lv raid_write_behind "" + lvchange --writebehind 512 $vg/$lv +- [ `lvs --noheadings -o raid_write_behind $vg/$lv` -eq 512 ] ++ check lv_field $vg/$lv raid_write_behind "512" + + # Converting to linear should clear flags and writebehind + lvconvert -m 0 $vg/$lv $d1 + lvconvert --type raid1 -m 1 $vg/$lv $d1 +- [ ! `lvs --noheadings -o raid_write_behind $vg/$lv` ] +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_0 | grep '.*-.$' +- lvs -a --noheadings -o lv_attr $vg/${lv}_rimage_1 | grep '.*-.$' ++ check lv_field $vg/$lv raid_write_behind "" ++ get lv_field $vg/${lv}_rimage_0 lv_attr -a | grep '.*-.$' ++ get lv_field $vg/${lv}_rimage_1 lv_attr -a | grep '.*-.$' + } + + # run_syncaction_check +@@ -143,33 +138,29 @@ run_syncaction_check() { + local lv=${2}${THIN_POSTFIX} + + printf "#\n#\n#\n# %s/%s (%s): run_syncaction_check\n#\n#\n#\n" \ +- $vg $lv `lvs -a --noheadings -o segtype $vg/$lv` ++ $vg $lv $(get lv_field $vg/$lv segtype -a) + aux wait_for_sync $vg $lv + +- device=`lvs -a --noheadings -o devices $vg/${lv}_rimage_1 | sed s/\(.\)//` +- device=$(sed s/^[[:space:]]*// <<< "$device") ++ device=$(get lv_devices $vg/${lv}_rimage_1) + +- size=`lvs -a --noheadings -o size --units 1k $vg/${lv}_rimage_1 | sed s/\.00k//` +- size=$(sed s/^[[:space:]]*// <<< "$size") +- size=$(($size / 2)) ++ size=$(get lv_field $vg/${lv}_rimage_1 size -a --units 1k) ++ size=$((${size%\.00k} / 2)) + +- tmp=`pvs --noheadings -o mda_size --units 1k $device | sed s/\.00k//` +- tmp=$(sed s/^[[:space:]]*// <<< "$tmp") +- seek=$tmp # Jump over MDA ++ tmp=$(get pv_field $device mda_size --units 1k) ++ seek=${tmp%\.00k} # Jump over MDA + +- tmp=`lvs -a --noheadings -o size --units 1k $vg/${lv}_rmeta_1 | sed s/\.00k//` +- tmp=$(sed s/^[[:space:]]*// <<< "$tmp") +- seek=$(($seek + $tmp)) # Jump over RAID metadata image ++ tmp=$(get lv_field $vg/${lv}_rmeta_1 size -a --units 1k) ++ seek=$(($seek + ${tmp%\.00k})) # Jump over RAID metadata image + + seek=$(($seek + $size)) # Jump halfway through the RAID image + +- lvs --noheadings -o lv_attr $vg/$lv | grep '.*-.$' +- [ `lvs --noheadings -o raid_mismatch_count $vg/$lv` == 0 ] ++ get lv_field $vg/$lv lv_attr | grep '.*-.$' ++ check lv_field $vg/$lv raid_mismatch_count "0" + + # Overwrite the last half of one of the PVs with crap + dd if=/dev/urandom of=$device bs=1k count=$size seek=$seek + +- if [ ! -z $THIN_POSTFIX ]; then ++ if [ -n "$THIN_POSTFIX" ]; then + # + # Seems to work fine on real devices, + # but can't make the system notice the bad blocks +@@ -189,12 +180,11 @@ run_syncaction_check() { + # 'lvs' should show results + lvchange --syncaction check $vg/$lv + aux wait_for_sync $vg $lv +- if ! lvs --noheadings -o lv_attr $vg/$lv | grep '.*m.$'; then +- lvs --noheadings -o lv_attr $vg/$lv ++ if ! get lv_field $vg/$lv lv_attr -a | grep '.*m.$'; then + dmsetup status | grep $vg + false + fi +- [ `lvs --noheadings -o raid_mismatch_count $vg/$lv` != 0 ] ++ not check lv_field $vg/$lv raid_mismatch_count "0" + + # "repair" will fix discrepancies + lvchange --syncaction repair $vg/$lv +@@ -204,40 +194,35 @@ run_syncaction_check() { + # 'lvs' should show results + lvchange --syncaction check $vg/$lv + aux wait_for_sync $vg $lv +- lvs --noheadings -o lv_attr $vg/$lv | grep '.*-.$' +- [ `lvs --noheadings -o raid_mismatch_count $vg/$lv` == 0 ] ++ get lv_field $vg/$lv lv_attr | grep '.*-.$' ++ check lv_field $vg/$lv raid_mismatch_count "0" + } + + # run_refresh_check + # Assumes "$dev2" is in the array + run_refresh_check() { + local size ++ local sizelv + local vg=$1 + local lv=${2}${THIN_POSTFIX} + + printf "#\n#\n#\n# %s/%s (%s): run_refresh_check\n#\n#\n#\n" \ +- $vg $lv `lvs -a --noheadings -o segtype $vg/$lv` ++ $vg $lv $(get lv_field $vg/$lv segtype -a) + + aux wait_for_sync $vg $lv + +- if [ -z $THIN_POSTFIX ]; then +- size=`lvs -a --noheadings -o size --units 1k $vg/$lv | sed s/\.00k//` +- else +- size=`lvs -a --noheadings -o size --units 1k $vg/thinlv | sed s/\.00k//` +- fi +- size=$(sed s/^[[:space:]]*// <<< "$size") ++ sizelv=$vg/$lv ++ test -z "$THIN_POSTFIX" || sizelv=$vg/thinlv ++ size=$(get lv_field $sizelv size --units 1k) ++ size=${size%\.00k} + + # Disable dev2 and do some I/O to make the kernel notice + aux disable_dev "$dev2" +- if [ -z $THIN_POSTFIX ]; then +- dd if=/dev/urandom of=/dev/$vg/$lv bs=1k count=$size +- else +- dd if=/dev/urandom of=/dev/$vg/thinlv bs=1k count=$size +- sync; sync; sync +- fi ++ dd if=/dev/urandom of=/dev/$sizelv bs=1k count=$size ++ sync + + # Check for 'p'artial flag +- lvs --noheadings -o lv_attr $vg/$lv | grep '.*p.$' ++ get lv_field $vg/$lv lv_attr | grep '.*p.$' + dmsetup status + lvs -a -o name,attr,devices $vg + +@@ -247,18 +232,18 @@ run_refresh_check() { + lvs -a -o name,attr,devices $vg + + # Check for 'r'efresh flag +- lvs --noheadings -o lv_attr $vg/$lv | grep '.*r.$' ++ get lv_field $vg/$lv lv_attr | grep '.*r.$' + + lvchange --refresh $vg/$lv + aux wait_for_sync $vg $lv +- lvs --noheadings -o lv_attr $vg/$lv | grep '.*-.$' ++ get lv_field $vg/$lv lv_attr | grep '.*-.$' + + # Writing random data above should mean that the devices + # were out-of-sync. The refresh should have taken care + # of properly reintegrating the device. + lvchange --syncaction repair $vg/$lv + aux wait_for_sync $vg $lv +- lvs --noheadings -o lv_attr $vg/$lv | grep '.*-.$' ++ get lv_field $vg/$lv lv_attr | grep '.*-.$' + } + + # run_recovery_rate_check +@@ -267,14 +252,13 @@ run_recovery_rate_check() { + local vg=$1 + local lv=${2}${THIN_POSTFIX} + +- printf "#\n#\n#\n# %s/%s (%s): run_recovery_rate_check\n#\n#\n#\n" \ +- $vg $lv `lvs -a --noheadings -o segtype $vg/$lv` +- ++ printf "#\n#\n#\n# %s/%s $(%s): run_recovery_rate_check\n#\n#\n#\n" \ ++ $vg $lv $(get lv_field $vg/$lv segtype -a) + lvchange --minrecoveryrate 50 $vg/$lv + lvchange --maxrecoveryrate 100 $vg/$lv + +- [ `lvs --noheadings -o raid_min_recovery_rate $vg/$lv` == "50" ] +- [ `lvs --noheadings -o raid_max_recovery_rate $vg/$lv` == "100" ] ++ check lv_field $vg/$lv raid_min_recovery_rate "50" ++ check lv_field $vg/$lv raid_max_recovery_rate "100" + } + + # run_checks <"-"|snapshot_dev|"thinpool_data"|"thinpool_meta"> +@@ -292,7 +276,7 @@ run_checks() { + run_refresh_check $1 $2 + run_recovery_rate_check $1 $2 + elif [ 'thinpool_data' == $3 ]; then +- aux target_at_least dm-thin-pool 1 8 0 || return 0 ++ aux have_thin 1 8 0 || return 0 + + # RAID works EX in cluster + # thinpool works EX in cluster +@@ -313,7 +297,7 @@ run_checks() { + run_refresh_check $1 $2 + run_recovery_rate_check $1 $2 + elif [ 'thinpool_meta' == $3 ]; then +- aux target_at_least dm-thin-pool 1 8 0 || return 0 ++ aux have_thin 1 8 0 || return 0 + test -e LOCAL_CLVMD && return 0 + printf "#\n#\n# run_checks: RAID as thinpool metadata\n#\n#\n" + +diff --git a/test/shell/lvconvert-mirror-updown.sh b/test/shell/lvconvert-mirror-updown.sh +new file mode 100644 +index 0000000..3b30738 +--- /dev/null ++++ b/test/shell/lvconvert-mirror-updown.sh +@@ -0,0 +1,36 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Demonstrate problem when upconverting and cutting leg in clvmd ++ ++. lib/test ++ ++aux prepare_pvs 3 ++ ++vgcreate -s 64k $vg $(cat DEVICES) ++ ++lvcreate -aey -l10 --type mirror -m1 -n $lv1 $vg "$dev1" "$dev2" ++ ++# Slow down device so we are able to start next conversion in parallel ++aux delay_dev "$dev3" 0 200 ++ ++lvconvert -m+1 -b $vg/$lv1 "$dev3" ++ ++# To fix - wait helps here.... ++#lvconvert $vg/$lv1 ++ ++lvs -a $vg ++# ++# It fails so use 'should' and -vvvv for now ++# ++should lvconvert -vvvv -m-1 $vg/$lv1 "$dev2" ++ ++vgremove -f $vg +diff --git a/test/shell/lvconvert-mirror.sh b/test/shell/lvconvert-mirror.sh +index 911e022..097e0d4 100644 +--- a/test/shell/lvconvert-mirror.sh ++++ b/test/shell/lvconvert-mirror.sh +@@ -176,6 +176,10 @@ lvremove -ff $vg + # "remove from original mirror (the original is still mirror)" + lvcreate -aey -l2 --type mirror -m2 -n $lv1 $vg "$dev1" "$dev2" "$dev5" "$dev3:$DEVRANGE" + lvconvert -m+1 -b $vg/$lv1 "$dev4" ++# FIXME: Extra wait here for mirror upconvert synchronization ++# otherwise we may fail her on parallel upconvert and downconvert ++# lvconvert-mirror-updown.sh tests this errornous case separately ++lvconvert $vg/$lv1 + lvconvert -m-1 $vg/$lv1 "$dev2" + lvconvert $vg/$lv1 + +diff --git a/test/shell/lvconvert-repair-dmeventd.sh b/test/shell/lvconvert-repair-dmeventd.sh +index 55eee37..a2d3ef8 100644 +--- a/test/shell/lvconvert-repair-dmeventd.sh ++++ b/test/shell/lvconvert-repair-dmeventd.sh +@@ -14,13 +14,13 @@ + which mkfs.ext2 || skip + aux skip_if_mirror_recovery_broken + +-aux prepare_vg 5 + aux prepare_dmeventd ++aux prepare_vg 5 + + lvcreate -aey --type mirror -m 3 --ignoremonitoring -L 1 -n 4way $vg + lvchange --monitor y $vg/4way + aux disable_dev "$dev2" "$dev4" +-mkfs.ext2 $DM_DEV_DIR/$vg/4way ++mkfs.ext2 "$DM_DEV_DIR/$vg/4way" + sleep 10 # FIXME: need a "poll" utility, akin to "check" + aux enable_dev "$dev2" "$dev4" + check mirror $vg 4way +diff --git a/test/shell/lvconvert-repair-thin.sh b/test/shell/lvconvert-repair-thin.sh +index aa301d6..b80b855 100644 +--- a/test/shell/lvconvert-repair-thin.sh ++++ b/test/shell/lvconvert-repair-thin.sh +@@ -10,7 +10,7 @@ + # along with this program; if not, write to the Free Software Foundation, + # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-# Test repairing of broken thin pool metadata ++# Test repairing of broken thin pool metadata + + . lib/test + +@@ -34,8 +34,8 @@ aux prepare_vg 4 + lvcreate -T -L20 -V10 -n $lv1 $vg/pool "$dev1" "$dev2" + lvcreate -T -V10 -n $lv2 $vg/pool + +-mkfs.ext2 $DM_DEV_DIR/$vg/$lv1 +-mkfs.ext2 $DM_DEV_DIR/$vg/$lv2 ++mkfs.ext2 "$DM_DEV_DIR/$vg/$lv1" ++mkfs.ext2 "$DM_DEV_DIR/$vg/$lv2" + + lvcreate -L20 -n repair $vg + lvcreate -L2 -n fixed $vg +@@ -55,26 +55,26 @@ lvconvert -y -f --poolmetadata $vg/repair --thinpool $vg/pool + + lvchange -aey $vg/repair $vg/fixed + +-#dd if=$DM_DEV_DIR/$vg/repair of=back bs=1M ++#dd if="$DM_DEV_DIR/$vg/repair" of=back bs=1M + + # Make some 'repairable' damage?? +-dd if=/dev/zero of=$DM_DEV_DIR/$vg/repair bs=1 seek=40960 count=1 ++dd if=/dev/zero of="$DM_DEV_DIR/$vg/repair" bs=1 seek=40960 count=1 + +-#dd if=$DM_DEV_DIR/$vg/repair of=back_trashed bs=1M ++#dd if="$DM_DEV_DIR/$vg/repair" of=back_trashed bs=1M + #not vgchange -ay $vg + + #lvconvert --repair $vg/pool + + # Using now SHOULD - since thin tools currently do not seem to work +-should not $THIN_CHECK $DM_DEV_DIR/$vg/repair ++should not "$THIN_CHECK" "$DM_DEV_DIR/$vg/repair" + +-should not $LVM_TEST_THIN_DUMP_CMD $DM_DEV_DIR/$vg/repair | tee dump ++should not "$LVM_TEST_THIN_DUMP_CMD" "$DM_DEV_DIR/$vg/repair" | tee dump + +-should $LVM_TEST_THIN_REPAIR_CMD -i $DM_DEV_DIR/$vg/repair -o $DM_DEV_DIR/$vg/fixed ++should "$LVM_TEST_THIN_REPAIR_CMD" -i "$DM_DEV_DIR/$vg/repair" -o "$DM_DEV_DIR/$vg/fixed" + +-should $LVM_TEST_THIN_DUMP_CMD --repair $DM_DEV_DIR/$vg/repair | tee repaired_xml ++should "$LVM_TEST_THIN_DUMP_CMD" --repair "$DM_DEV_DIR/$vg/repair" | tee repaired_xml + +-should $LVM_TEST_THIN_CHECK_CMD $DM_DEV_DIR/$vg/fixed ++should "$LVM_TEST_THIN_CHECK_CMD" "$DM_DEV_DIR/$vg/fixed" + + # Swap repaired metadata back + lvconvert -y -f --poolmetadata $vg/fixed --thinpool $vg/pool +@@ -83,7 +83,7 @@ lvs -a $vg + # Activate pool - this should now work + should vgchange -ay $vg + +-lvs -a -o+devices $vg ++lvs -a -o+devices $vg + dmsetup table + dmsetup info -c + dmsetup ls --tree +@@ -99,7 +99,7 @@ dmsetup remove $vg-pool_tmeta || true + + dmsetup table + +-# FIXME: needs also --yes with double force ++# FIXME: needs also --yes with double force + pvremove --yes -ff "$dev1" + pvremove --yes -ff "$dev2" + +diff --git a/test/shell/lvconvert-repair-transient-dmeventd.sh b/test/shell/lvconvert-repair-transient-dmeventd.sh +index 699195a..6bd1442 100644 +--- a/test/shell/lvconvert-repair-transient-dmeventd.sh ++++ b/test/shell/lvconvert-repair-transient-dmeventd.sh +@@ -18,7 +18,7 @@ aux prepare_dmeventd + lvcreate -aey --type mirror -m 3 --ignoremonitoring -L 1 -n 4way $vg + lvchange --monitor y $vg/4way + aux disable_dev "$dev2" "$dev4" +-mkfs.ext3 $DM_DEV_DIR/$vg/4way ++mkfs.ext3 "$DM_DEV_DIR/$vg/4way" + aux enable_dev "$dev2" "$dev4" + sleep 3 + lvs -a -o +devices $vg | tee out +diff --git a/test/shell/lvconvert-repair-transient.sh b/test/shell/lvconvert-repair-transient.sh +index 28b06c6..3baa293 100644 +--- a/test/shell/lvconvert-repair-transient.sh ++++ b/test/shell/lvconvert-repair-transient.sh +@@ -16,7 +16,7 @@ aux prepare_vg 5 + + lvcreate -aey --type mirror -m 3 --ignoremonitoring -L 1 -n 4way $vg + aux disable_dev "$dev2" "$dev4" +-mkfs.ext3 $DM_DEV_DIR/$vg/4way & ++mkfs.ext3 "$DM_DEV_DIR/$vg/4way" & + sleep 1 + aux enable_dev "$dev2" "$dev4" + echo n | lvconvert --repair $vg/4way 2>&1 | tee 4way.out +diff --git a/test/shell/lvconvert-thin-external.sh b/test/shell/lvconvert-thin-external.sh +index d9d4d19..00712bd 100644 +--- a/test/shell/lvconvert-thin-external.sh ++++ b/test/shell/lvconvert-thin-external.sh +@@ -49,16 +49,24 @@ fi + + lvcreate -l10 -T $vg/pool + # Can't convert pool to external origin +-lvcreate -l10 -T $vg/pool1 ++lvcreate -l10 -T $vg/pool1 -c 192k + not lvconvert -T --thinpool $vg/pool1 $vg/pool --originname origin +-lvremove -f $vg/pool1 ++# Create pool1 chunk_size unaligned LV and check failing conversion ++lvcreate -l2 -n $lv1 $vg ++not lvconvert -T --thinpool $vg/pool1 $vg/$lv1 ++ ++lvremove -f $vg/pool1 $vg/$lv1 + + # create plain LV (will be used for external origin) + lvcreate -L8M -n $lv1 $vg + +-mkfs.ext2 $DM_DEV_DIR/$vg/$lv1 ++# Can't convert same LV to the thin pool and thin volume ++not lvconvert --thinpool $vg/$lv1 -T $vg/$lv1 ++check lv_field $vg/$lv1 segtype linear ++ ++mkfs.ext2 "$DM_DEV_DIR/$vg/$lv1" + mkdir mnt +-mount $DM_DEV_DIR/$vg/$lv1 mnt ++mount "$DM_DEV_DIR/$vg/$lv1" mnt + + dd if=/dev/zero of=mnt/test1 bs=1M count=1 + +@@ -76,7 +84,7 @@ touch mnt/test + umount mnt + + # check fs is without errors +-fsck -n $DM_DEV_DIR/$vg/$lv1 ++fsck -n "$DM_DEV_DIR/$vg/$lv1" + + lvchange -aey $vg/extorg + lvchange -an $vg/$lv1 +@@ -85,7 +93,7 @@ check active $vg extorg + check inactive $vg $lv1 + + # fsck in read-only mode +-fsck -n $DM_DEV_DIR/$vg/extorg ++fsck -n "$DM_DEV_DIR/$vg/extorg" + + not lvresize -l+8 $vg/extorg + not lvresize -l-4 $vg/extorg +diff --git a/test/shell/lvcreate-cache.sh b/test/shell/lvcreate-cache.sh +new file mode 100644 +index 0000000..ccfb4ad +--- /dev/null ++++ b/test/shell/lvcreate-cache.sh +@@ -0,0 +1,137 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++. lib/test ++ ++aux target_at_least dm-cache 1 3 0 || skip ++ ++# Skip in cluster for now, but should test EX mode... ++test -e LOCAL_CLVMD && skip ++ ++aux prepare_vg 5 80 ++ ++#################### ++# Cache_Pool creation ++#################### ++ ++# Full CLI (the advertised form) ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvremove -ff $vg/${lv}_cache_pool ++ ++# Shorthand CLI (not advertised) -- not yet implemented ++# lvcreate --cache -l 1 vg ++# lvremove -ff $vg ++ ++# Shorthand CLI (not advertised) -- not yet implemented ++# lvcreate -H -l 1 vg ++# lvremove -ff $vg ++ ++################ ++# Cache creation ++# Creating a cache is a two phase process ++# - first, cache_pool (or origin) ++# - then, the cache LV (lvcreate distinguishes supplied origin vs cache_pool) ++################ ++ ++# Create/remove cache_pool ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvremove -ff $vg ++ ++# Create cache_pool, then origin with cache, then remove all ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvcreate --type cache -l 2 $vg/${lv}_cache_pool -n $lv1 ++dmsetup table ${vg}-$lv1 | grep cache # ensure it is loaded in kernel ++lvremove -ff $vg ++ ++# Create cache_pool, then origin with cache, then remove cache_pool/cache ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvcreate --type cache -l 2 $vg/${lv}_cache_pool -n $lv1 ++lvremove -ff $vg/${lv}_cache_pool ++lvremove -ff $vg/$lv1 ++ ++# Create cache_pool, then origin with cache, then remove origin ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvcreate --type cache -l 2 $vg/${lv}_cache_pool -n $lv1 ++lvremove -ff $vg/$lv1 ++lvremove -ff $vg/${lv}_cache_pool ++ ++# Shorthand CLI (cache_pool exists, create origin w/ cache) ++#lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++#lvcreate --cache -l 2 $vg/${lv}_cache_pool -n $lv1 ++#lvremove -ff $vg ++ ++# Shorthand CLI (cache_pool exists, create origin w/ cache) ++#lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++#lvcreate -H -l 2 $vg/${lv}_cache_pool -n $lv1 ++#lvremove -ff $vg ++ ++# Create origin, then cache_pool and cache ++lvcreate -l 2 -n $lv1 $vg ++lvcreate --type cache -l 1 $vg/$lv1 ++dmsetup table ${vg}-$lv1 | grep cache # ensure it is loaded in kernel ++lvremove -ff $vg ++ ++# Shorthand CLI (origin exists, create cache_pool and cache) ++#lvcreate -l 1 -n $lv1 $vg ++#lvcreate --cache -l 2 $vg/$lv1 ++#lvremove -ff $vg ++ ++# Shorthand CLI (origin exists, create cache_pool and cache) ++#lvcreate -l 1 -n $lv1 $vg ++#lvcreate -H -l 2 $vg/$lv1 ++#lvremove -ff $vg ++ ++ ++################################################ ++# Repeat key tests with 'writethrough' cachemode ++################################################ ++# Create/remove cache_pool ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg --cachemode writethrough ++lvremove -ff $vg ++ ++# Create cache_pool, then origin with cache, then remove all ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvcreate --type cache -l 2 $vg/${lv}_cache_pool -n $lv1 --cachemode writethrough ++lvremove -ff $vg ++ ++# Create cache_pool, then origin with cache, then remove cache_pool/cache ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvcreate --type cache -l 2 $vg/${lv}_cache_pool -n $lv1 --cachemode writethrough ++lvremove -ff $vg/${lv}_cache_pool ++lvremove -ff $vg/$lv1 ++ ++# Create cache_pool, then origin with cache, then remove origin ++lvcreate --type cache-pool -l 1 -n ${lv}_cache_pool $vg ++lvcreate --type cache -l 2 $vg/${lv}_cache_pool -n $lv1 --cachemode writethrough ++lvremove -ff $vg/$lv1 ++lvremove -ff $vg/${lv}_cache_pool ++ ++# Create origin, then cache_pool and cache ++lvcreate -l 2 -n $lv1 $vg ++lvcreate --type cache -l 1 $vg/$lv1 --cachemode writethrough ++lvremove -ff $vg ++ ++ ++############################## ++# Test things that should fail ++############################## ++ ++# Attempt to create smaller cache than origin should fail ++lvcreate -l 1 -n $lv1 $vg ++not lvcreate --type cache -l 2 $vg/$lv1 ++lvremove -ff $vg ++ ++ ++# Option testing ++# --chunksize ++# --cachepolicy ++# --poolmetadatasize ++# --poolmetadataspare +diff --git a/test/shell/lvcreate-large-raid.sh b/test/shell/lvcreate-large-raid.sh +index 395cf2b..e5d883c 100644 +--- a/test/shell/lvcreate-large-raid.sh ++++ b/test/shell/lvcreate-large-raid.sh +@@ -31,8 +31,8 @@ lvcreate -s -l 20%FREE -n $lv5 $vg --virtualsize 256T + + aux extend_filter_LVMTEST + +-pvcreate $DM_DEV_DIR/$vg/$lv[12345] +-vgcreate $vg1 $DM_DEV_DIR/$vg/$lv[12345] ++pvcreate "$DM_DEV_DIR"/$vg/$lv[12345] ++vgcreate $vg1 "$DM_DEV_DIR"/$vg/$lv[12345] + + # bz837927 START + +diff --git a/test/shell/lvcreate-large-raid10.sh b/test/shell/lvcreate-large-raid10.sh +index 4f97997..3d02e81 100644 +--- a/test/shell/lvcreate-large-raid10.sh ++++ b/test/shell/lvcreate-large-raid10.sh +@@ -28,8 +28,8 @@ lvcreate -s -l 20%FREE -n $lv5 $vg --virtualsize 256T + + aux extend_filter_LVMTEST + +-pvcreate $DM_DEV_DIR/$vg/$lv[12345] +-vgcreate $vg1 $DM_DEV_DIR/$vg/$lv[12345] ++pvcreate "$DM_DEV_DIR"/$vg/$lv[12345] ++vgcreate $vg1 "$DM_DEV_DIR"/$vg/$lv[12345] + + # + # Create large RAID LVs +diff --git a/test/shell/lvcreate-large.sh b/test/shell/lvcreate-large.sh +index c8790f1..f3d3f30 100644 +--- a/test/shell/lvcreate-large.sh ++++ b/test/shell/lvcreate-large.sh +@@ -25,8 +25,8 @@ lvcreate -s -l 100%FREE -n $lv $vg --virtualsize 1024T + + aux extend_filter_LVMTEST + +-pvcreate $DM_DEV_DIR/$vg/$lv +-vgcreate $vg1 $DM_DEV_DIR/$vg/$lv ++pvcreate "$DM_DEV_DIR/$vg/$lv" ++vgcreate $vg1 "$DM_DEV_DIR/$vg/$lv" + + lvcreate -l 100%FREE -n $lv1 $vg1 + check lv_field $vg1/$lv1 size "1024.00t" +diff --git a/test/shell/lvcreate-pvtags.sh b/test/shell/lvcreate-pvtags.sh +index 20a241c..340a4f4 100644 +--- a/test/shell/lvcreate-pvtags.sh ++++ b/test/shell/lvcreate-pvtags.sh +@@ -28,7 +28,7 @@ lvcreate -l3 -i3 $vg @fast + not lvcreate -l4 -i4 $vg @fast + + # 2 stripes is too many with just one PV +-not lvcreate -l2 -i2 $vg $DM_DEV_DIR/mapper/pv1 ++not lvcreate -l2 -i2 $vg "$DM_DEV_DIR/mapper/pv1" + + # lvcreate mirror + lvcreate -aey -l1 --type mirror -m1 $vg @fast +diff --git a/test/shell/lvcreate-raid.sh b/test/shell/lvcreate-raid.sh +index 214b162..2053d04 100644 +--- a/test/shell/lvcreate-raid.sh ++++ b/test/shell/lvcreate-raid.sh +@@ -11,6 +11,14 @@ + + . lib/test + ++lv_devices() { ++ local local_vg=$1 ++ local local_lv=$2 ++ local count=$3 ++ ++ [ $count == `lvs --noheadings -o devices $local_vg/$local_lv | sed s/,/' '/g | wc -w` ] ++} ++ + ######################################################## + # MAIN + ######################################################## +@@ -70,3 +78,113 @@ for i in raid4 \ + aux wait_for_sync $vg $lv1 + lvremove -ff $vg + done ++ ++# Create RAID using 100%FREE ++############################ ++# 6 PVs with 18.5m in each PV. ++# 1 metadata LV = 1 extent = .5m ++# 1 image = 36+37+37 extents = 55.00m = lv_size ++lvcreate --type raid1 -m 1 -l 100%FREE -n raid1 $vg ++check lv_field $vg/raid1 size "55.00m" ++lvremove -ff $vg ++ ++# 1 metadata LV = 1 extent ++# 1 image = 36 extents ++# 5 images = 180 extents = 90.00m = lv_size ++lvcreate --type raid5 -i 5 -l 100%FREE -n raid5 $vg ++check lv_field $vg/raid5 size "90.00m" ++lvremove -ff $vg ++ ++# 1 image = 36+37 extents ++# 2 images = 146 extents = 73.00m = lv_size ++lvcreate --type raid5 -i 2 -l 100%FREE -n raid5 $vg ++check lv_field $vg/raid5 size "73.00m" ++lvremove -ff $vg ++ ++# 1 image = 36 extents ++# 4 images = 144 extents = 72.00m = lv_size ++lvcreate --type raid6 -i 4 -l 100%FREE -n raid6 $vg ++check lv_field $vg/raid6 size "72.00m" ++lvremove -ff $vg ++ ++# Eat 18 of 37 extents from dev1, leaving 19 ++lvcreate -l 18 -n lv $vg $dev1 ++# Using 100% free should take the rest of dev1 and equal from dev2 ++# 1 meta takes 1 extent ++# 1 image = 18 extents = 9.00m = lv_size ++lvcreate --type raid1 -m 1 -l 100%FREE -n raid1 $vg $dev1 $dev2 ++check lv_field $vg/raid1 size "9.00m" ++# Ensure image size is the same as the RAID1 size ++check lv_field $vg/raid1 size `lvs --noheadings -o size $vg/raid1_rimage_0` ++# Amount remaining in dev2 should equal the amount taken by 'lv' in dev1 ++check pv_field "$dev2" pv_free `lvs --noheadings -o size $vg/lv` ++lvremove -ff $vg ++ ++# Eat 18 of 37 extents from dev1, leaving 19 ++lvcreate -l 18 -n lv $vg $dev1 ++# Using 100% free should take the rest of dev1 and equal amount from the rest ++# 1 meta takes 1 extent ++# 1 image = 18 extents = 9.00m ++# 5 images = 90 extents = 45.00m = lv_size ++lvcreate --type raid5 -i 5 -l 100%FREE -n raid5 $vg ++check lv_field $vg/raid5 size "45.00m" ++# Amount remaining in dev6 should equal the amount taken by 'lv' in dev1 ++check pv_field "$dev6" pv_free `lvs --noheadings -o size $vg/lv` ++lvremove -ff $vg ++ ++# Eat 18 of 37 extents from dev1, leaving 19 ++lvcreate -l 18 -n lv $vg $dev1 ++# Using 100% free should take the rest of dev1, an equal amount ++# from 2 more devs, and all extents from 3 additional devs ++# 1 meta takes 1 extent ++# 1 image = 18+37 extents ++# 2 images = 110 extents = 55.00m = lv_size ++lvcreate --type raid5 -i 2 -l 100%FREE -n raid5 $vg ++check lv_field $vg/raid5 size "55.00m" ++lvremove -ff $vg ++ ++# Let's do some stripe tests too ++# Eat 18 of 37 extents from dev1, leaving 19 ++lvcreate -l 18 -n lv $vg $dev1 ++# Using 100% free should take the rest of dev1 and an equal amount from rest ++# 1 image = 19 extents ++# 6 images = 114 extents = 57.00m = lv_size ++lvcreate -i 6 -l 100%FREE -n stripe $vg ++check lv_field $vg/stripe size "57.00m" ++lvremove -ff $vg ++ ++# Eat 18 of 37 extents from dev1, leaving 19 ++lvcreate -l 18 -n lv $vg $dev1 ++# Using 100% free should take the rest of dev1, an equal amount from ++# one more dev, and all of the remaining 4 ++# 1 image = 19+37+37 extents ++# 2 images = 186 extents = 93.00m = lv_size ++lvcreate -i 2 -l 100%FREE -n stripe $vg ++check lv_field $vg/stripe size "93.00m" ++lvremove -ff $vg ++ ++# Create RAID (implicit stripe count based on PV count) ++####################################################### ++ ++# Not enough drives ++not lvcreate --type raid1 -l1 $vg $dev1 ++not lvcreate --type raid5 -l2 $vg $dev1 $dev2 ++not lvcreate --type raid6 -l3 $vg $dev1 $dev2 $dev3 $dev4 ++ ++# Implicit count comes from #PVs given (always 2 for mirror though) ++lvcreate --type raid1 -l1 -n raid1 $vg $dev1 $dev2 ++lv_devices $vg raid1 2 ++lvcreate --type raid5 -l2 -n raid5 $vg $dev1 $dev2 $dev3 ++lv_devices $vg raid5 3 ++lvcreate --type raid6 -l3 -n raid6 $vg $dev1 $dev2 $dev3 $dev4 $dev5 ++lv_devices $vg raid6 5 ++lvremove -ff $vg ++ ++# Implicit count comes from total #PVs in VG (always 2 for mirror though) ++lvcreate --type raid1 -l1 -n raid1 $vg ++lv_devices $vg raid1 2 ++lvcreate --type raid5 -l2 -n raid5 $vg ++lv_devices $vg raid5 6 ++lvcreate --type raid6 -l3 -n raid6 $vg ++lv_devices $vg raid6 6 ++lvremove -ff $vg +diff --git a/test/shell/lvcreate-raid10.sh b/test/shell/lvcreate-raid10.sh +index 143e869..ba35fdf 100644 +--- a/test/shell/lvcreate-raid10.sh ++++ b/test/shell/lvcreate-raid10.sh +@@ -11,6 +11,14 @@ + + . lib/test + ++lv_devices() { ++ local local_vg=$1 ++ local local_lv=$2 ++ local count=$3 ++ ++ [ $count == `lvs --noheadings -o devices $local_vg/$local_lv | sed s/,/' '/g | wc -w` ] ++} ++ + ######################################################## + # MAIN + ######################################################## +@@ -23,7 +31,6 @@ vgcreate -s 512k $vg $(cat DEVICES) + # Create RAID10: + # + +- + # Should not allow more than 2-way mirror + not lvcreate --type raid10 -m 2 -i 2 -l 2 -n $lv1 $vg + +@@ -46,6 +53,30 @@ aux wait_for_sync $vg $lv2 + + lvremove -ff $vg + ++# Test 100%FREE option ++# 37 extents / device ++# 1 image = 36 extents (1 for meta) ++# 3 images = 108 extents = 54.00m ++lvcreate --type raid10 -i 3 -l 100%FREE -n raid10 $vg ++check lv_field $vg/raid10 size "54.00m" ++lvremove -ff $vg ++ ++# Create RAID (implicit stripe count based on PV count) ++####################################################### ++ ++# Not enough drives ++not lvcreate --type raid10 -l2 $vg $dev1 $dev2 $dev3 ++ ++# Implicit count comes from #PVs given (always 2-way mirror) ++lvcreate --type raid10 -l2 -n raid10 $vg $dev1 $dev2 $dev3 $dev4 ++lv_devices $vg raid10 4 ++lvremove -ff $vg ++ ++# Implicit count comes from total #PVs in VG (always 2 for mirror though) ++lvcreate --type raid10 -l2 -n raid10 $vg ++lv_devices $vg raid10 6 ++lvremove -ff $vg ++ + # + # FIXME: Add tests that specify particular PVs to use for creation + # +diff --git a/test/shell/lvcreate-thin-external.sh b/test/shell/lvcreate-thin-external.sh +index c9aba6f..1e5b333 100644 +--- a/test/shell/lvcreate-thin-external.sh ++++ b/test/shell/lvcreate-thin-external.sh +@@ -26,11 +26,21 @@ aux prepare_pvs 2 64 + + vgcreate $vg -s 64K $(cat DEVICES) + ++# Test validation for external origin being multiple of thin pool chunk size ++lvcreate -L10M -T $vg/pool192 -c 192k ++lvcreate -an -pr -Zn -l1 -n $lv1 $vg ++not lvcreate -s $vg/$lv1 --thinpool $vg/pool192 ++ ++lvcreate -an -pr -Zn -l5 -n $lv2 $vg ++not lvcreate -s $vg/$lv2 --thinpool $vg/pool192 ++lvremove -f $vg ++ ++# Prepare pool and external origin with filesystem + lvcreate -L10M -V10M -T $vg/pool --name $lv1 +-mkfs.ext2 $DM_DEV_DIR/$vg/$lv1 ++mkfs.ext2 "$DM_DEV_DIR/$vg/$lv1" + + lvcreate -L4M -n $lv2 $vg +-mkfs.ext2 $DM_DEV_DIR/$vg/$lv2 ++mkfs.ext2 "$DM_DEV_DIR/$vg/$lv2" + + # Fail to create external origin snapshot of rw LV + not lvcreate -s $vg/$lv2 --thinpool $vg/pool +@@ -80,7 +90,7 @@ check active $vg $lv5 + check active $vg $lv6 + check active $vg $lv7 + +-fsck -n $DM_DEV_DIR/$vg/$lv1 +-fsck -n $DM_DEV_DIR/$vg/$lv7 ++fsck -n "$DM_DEV_DIR/$vg/$lv1" ++fsck -n "$DM_DEV_DIR/$vg/$lv7" + + vgremove -ff $vg +diff --git a/test/shell/lvcreate-thin-snap.sh b/test/shell/lvcreate-thin-snap.sh +index b7511e7..cfc801a 100644 +--- a/test/shell/lvcreate-thin-snap.sh ++++ b/test/shell/lvcreate-thin-snap.sh +@@ -33,11 +33,11 @@ aux prepare_pvs 2 64 + vgcreate $vg -s 64K $(cat DEVICES) + + lvcreate -L10M -V10M -T $vg/pool --name $lv1 +-mkfs.ext4 $DM_DEV_DIR/$vg/$lv1 ++mkfs.ext4 "$DM_DEV_DIR/$vg/$lv1" + # create thin snapshot of thin LV + lvcreate -K -s $vg/$lv1 --name snap + # check snapshot filesystem was properly frozen before snapping +-fsck -n $DM_DEV_DIR/$vg/snap ++fsck -n "$DM_DEV_DIR/$vg/snap" + lvcreate -K -s $vg/$lv1 --name $lv2 + lvcreate -K -s $vg/$lv1 --name $vg/$lv3 + lvcreate --type snapshot $vg/$lv1 +@@ -57,8 +57,8 @@ lvcreate -K -s --name sn4 $vg/sn3 + lvremove -ff $vg + + lvcreate -L10M --zero n -T $vg/pool -V10M --name $lv1 +-mkfs.ext4 $DM_DEV_DIR/$vg/$lv1 ++mkfs.ext4 "$DM_DEV_DIR/$vg/$lv1" + lvcreate -K -s $vg/$lv1 --name snap +-fsck -n $DM_DEV_DIR/$vg/snap ++fsck -n "$DM_DEV_DIR/$vg/snap" + + vgremove -ff $vg +diff --git a/test/shell/lvcreate-thin.sh b/test/shell/lvcreate-thin.sh +index 98ab7eb..a2811d2 100644 +--- a/test/shell/lvcreate-thin.sh ++++ b/test/shell/lvcreate-thin.sh +@@ -136,9 +136,9 @@ check vg_field $vg lv_count 0 + + # Create thin snapshot of thinLV + lvcreate -L10M -V10M -T $vg/pool --name lv1 +-mkfs.ext4 $DM_DEV_DIR/$vg/lv1 ++mkfs.ext4 "$DM_DEV_DIR/$vg/lv1" + lvcreate -K -s $vg/lv1 --name snap_lv1 +-fsck -n $DM_DEV_DIR/$vg/snap_lv1 ++fsck -n "$DM_DEV_DIR/$vg/snap_lv1" + lvcreate -s $vg/lv1 --name lv2 + lvcreate -s $vg/lv1 --name $vg/lv3 + lvcreate --type snapshot $vg/lv1 --name lv6 +diff --git a/test/shell/lvextend-thin-metadata-dmeventd.sh b/test/shell/lvextend-thin-metadata-dmeventd.sh +new file mode 100644 +index 0000000..cb5261c +--- /dev/null ++++ b/test/shell/lvextend-thin-metadata-dmeventd.sh +@@ -0,0 +1,93 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Test autoextension of thin metadata volume ++. lib/test ++ ++meta_percent_() { ++ get lv_field $vg/pool metadata_percent | cut -d. -f1 ++} ++ ++wait_for_change_() { ++ # dmeventd only checks every 10 seconds :( ++ for i in $(seq 1 15) ; do ++ test "$(meta_percent_)" != "$1" && return ++ sleep 1 ++ done ++ ++ return 1 # timeout ++} ++ ++# ++# Temporary solution to create some occupied thin metadata ++# This heavily depends on thin metadata output format to stay as is. ++# Currently it expects 2MB thin metadata and 200MB data volume size ++# Argument specifies how many devices should be created. ++fake_metadata_() { ++ echo '' ++ for i in $(seq 1 $1) ++ do ++ echo ' ' ++ echo ' ' ++ echo ' ' ++ done ++ echo "" ++} ++ ++aux have_thin 1 10 0 || skip ++ ++aux prepare_dmeventd ++ ++aux lvmconf "activation/thin_pool_autoextend_percent = 10" \ ++ "activation/thin_pool_autoextend_threshold = 70" ++ ++aux prepare_pvs 3 256 ++ ++vgcreate -s 1M $vg $(cat DEVICES) ++ ++# Testing dmeventd autoresize ++lvcreate -L200M -V1G -n thin -T $vg/pool ++lvcreate -L2M -n $lv1 $vg ++lvchange -an $vg/thin $vg/pool ++ ++# Prepare some fake metadata with unmatching id ++# Transaction_id is lower by 1 and there are no message -> ERROR ++fake_metadata_ 10 0 >data ++thin_restore -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1" ++lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1 ++not vgchange -ay $vg 2>&1 | tee out ++grep expected out ++ ++check inactive $vg pool_tmeta ++ ++# Transaction_id is higher by 1 ++fake_metadata_ 10 2 >data ++thin_restore -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1" ++lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1 ++not vgchange -ay $vg 2>&1 | tee out ++grep expected out ++ ++check inactive $vg pool_tmeta ++ ++# Prepare some fake metadata prefilled to ~81% (>70%) ++fake_metadata_ 400 1 >data ++thin_restore -i data -o "$DM_DEV_DIR/mapper/$vg-$lv1" ++ ++# Swap volume with restored fake metadata ++lvconvert -y --thinpool $vg/pool --poolmetadata $vg/$lv1 ++ ++vgchange -ay $vg ++ ++# Check dmeventd resizes metadata ++pre=$(meta_percent_) ++wait_for_change_ $pre ++ ++vgremove -f $vg +diff --git a/test/shell/lvmetad-disabled.sh b/test/shell/lvmetad-disabled.sh +index 41a3a19..c344d7f 100644 +--- a/test/shell/lvmetad-disabled.sh ++++ b/test/shell/lvmetad-disabled.sh +@@ -13,14 +13,17 @@ + + test -e LOCAL_LVMETAD || skip + kill $(cat LOCAL_LVMETAD) ++while test -e "$TESTDIR/lvmetad.socket"; do echo -n .; sleep .1; done # wait for the socket close ++test ! -e "$LVM_LVMETAD_PIDFILE" + +-test -e $LVMETAD_PIDFILE && skip + lvmetad +-test -e $LVMETAD_PIDFILE +-cp $LVMETAD_PIDFILE LOCAL_LVMETAD ++while ! test -e "$TESTDIR/lvmetad.socket"; do echo -n .; sleep .1; done # wait for the socket ++test -e "$LVM_LVMETAD_PIDFILE" ++cp "$LVM_LVMETAD_PIDFILE" LOCAL_LVMETAD ++ + pvs 2>&1 | not grep "lvmetad is running" + aux lvmconf "global/use_lvmetad = 0" + pvs 2>&1 | grep "lvmetad is running" + +-kill $(cat $LVMETAD_PIDFILE) +-not ls $LVMETAD_PIDFILE ++kill $(cat "$LVM_LVMETAD_PIDFILE") ++not ls "$LVM_LVMETAD_PIDFILE" +diff --git a/test/shell/lvresize-thin-external-origin.sh b/test/shell/lvresize-thin-external-origin.sh +new file mode 100644 +index 0000000..3499196 +--- /dev/null ++++ b/test/shell/lvresize-thin-external-origin.sh +@@ -0,0 +1,42 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Test resize of thin volume with external origin ++. lib/test ++ ++aux have_thin 1 2 0 || skip ++ ++# Pretend we miss the external_origin_extend feature ++aux lvmconf "global/thin_disabled_features = [ \"external_origin_extend\" ]" ++ ++aux prepare_pvs 2 ++ ++vgcreate -s 1M $vg $(cat DEVICES) ++ ++lvcreate -L10 -n $lv1 $vg ++ ++# Prepare thin pool ++lvcreate -L20 -T $vg/pool ++ ++# Convert $lv1 into thin LV with external origin ++lvconvert -T $vg/$lv1 --thinpool $vg/pool --originname ext ++ ++lvs -a $vg ++ ++# Bigger size is not supported without feature external_origin_extend ++not lvresize -L+10 $vg/$lv1 ++ ++# But reduction works ++lvresize -L-5 -f $vg/$lv1 ++not lvresize -L+15 -y $vg/$lv1 ++# Try to resize again back up to the size of external origin ++# But for now we do not support zeroing for rexetended areas. ++not lvresize -L+5 -f $vg/$lv1 +diff --git a/test/shell/lvresize-thin-metadata.sh b/test/shell/lvresize-thin-metadata.sh +index 16a7500..e62d8b6 100644 +--- a/test/shell/lvresize-thin-metadata.sh ++++ b/test/shell/lvresize-thin-metadata.sh +@@ -1,5 +1,5 @@ + #!/bin/sh +-# Copyright (C) 2013 Red Hat, Inc. All rights reserved. ++# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved. + # + # This copyrighted material is made available to anyone wishing to use, + # modify, copy, or redistribute it subject to the terms and conditions +@@ -11,31 +11,35 @@ + + . lib/test + +-aux have_thin 9 9 0 || skip ++aux have_thin 1 10 0 || skip + +-aux prepare_pvs 3 256 ++aux prepare_pvs 3 1256 + + vgcreate -s 1M $vg $(cat DEVICES) + + for deactivate in true false; do +- lvcreate -l1 -T $vg/pool +- +- test $deactivate && lvchange -an $vg +- ++# Create some thin volumes ++ lvcreate -L20 -V30 -n $lv1 -T $vg/pool ++ lvcreate -s $vg/$lv1 + # Confirm we have basic 2M metadata + check lv_field $vg/pool_tmeta size "2.00m" + +- lvresize --poolmetadata +2 $vg/pool ++ test $deactivate && lvchange -an $vg + ++ lvresize --poolmetadata +2M $vg/pool + # Test it's been resized to 4M + check lv_field $vg/pool_tmeta size "4.00m" + +-# TODO: Add more tests when kernel is fixed +- lvresize --alloc anywhere --poolmetadata +256 $vg/pool ++ lvresize --poolmetadata +256M $vg/pool ++ check lv_field $vg/pool_tmeta size "260.00m" ++ ++ lvresize --poolmetadata +3G $vg/pool ++ check lv_field $vg/pool_tmeta size "3.25g" + + vgchange -an $vg + vgchange -ay $vg + +-# TODO: Make a full metadata device and test dmeventd support ++# TODO: Add more tests ++ + lvremove -ff $vg + done +diff --git a/test/shell/lvresize-usage.sh b/test/shell/lvresize-usage.sh +index 51ef221..84431ed 100644 +--- a/test/shell/lvresize-usage.sh ++++ b/test/shell/lvresize-usage.sh +@@ -1,5 +1,5 @@ + #!/bin/sh +-# Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved. ++# Copyright (C) 2007-2014 Red Hat, Inc. All rights reserved. + # + # This copyrighted material is made available to anyone wishing to use, + # modify, copy, or redistribute it subject to the terms and conditions +@@ -11,7 +11,7 @@ + + . lib/test + +-aux prepare_vg 2 ++aux prepare_vg 2 80 + + lvcreate -L 10M -n lv -i2 $vg + lvresize -l +4 $vg/lv +@@ -19,3 +19,8 @@ lvremove -ff $vg + + lvcreate -L 64M -n $lv -i2 $vg + not lvresize -v -l +4 xxx/$lv ++ ++# Check stripe size is reduced to extent size when it's bigger ++ESIZE=$(get vg_field $vg vg_extent_size --units b) ++lvextend -L+64m -i 2 -I$(( ${ESIZE%%B} * 2 ))B $vg/$lv 2>&1 | tee err ++grep "Reducing stripe size" err +diff --git a/test/shell/name-mangling.sh b/test/shell/name-mangling.sh +index 5b92e60..76d90fa 100644 +--- a/test/shell/name-mangling.sh ++++ b/test/shell/name-mangling.sh +@@ -164,8 +164,8 @@ function check_mangle_cmd() + # check dmsetup can process path where the last component is not equal dm name (rhbz #797322) + r=0 + create_dm_dev auto "abc" +-ln -s ${DM_DEV_DIR}/mapper/${name_prefix}abc ${DM_DEV_DIR}/${name_prefix}xyz +-dmsetup status ${DM_DEV_DIR}/${name_prefix}xyz || r=1 ++ln -s "$DM_DEV_DIR/mapper/${name_prefix}abc" "$DM_DEV_DIR/${name_prefix}xyz" ++dmsetup status "$DM_DEV_DIR/${name_prefix}xyz" || r=1 + remove_dm_dev auto "abc" + if [ r = 1 ]; then + exit 1 +diff --git a/test/shell/process-each-lv.sh b/test/shell/process-each-lv.sh +index 0140c26..207f1ba 100644 +--- a/test/shell/process-each-lv.sh ++++ b/test/shell/process-each-lv.sh +@@ -36,139 +36,88 @@ aux prepare_devs 10 + # test lvremove vg|lv names + # + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 ++prepare_vgs_() { ++ # set up vgs/lvs that we will remove ++ vgcreate $vg1 "$dev1" "$dev2" ++ vgcreate $vg2 "$dev3" "$dev4" ++ vgcreate $vg3 "$dev5" "$dev6" ++ vgcreate $vg4 "$dev7" "$dev8" ++ vgcreate $vg5 "$dev9" "$dev10" ++ lvcreate -Zn -an -l 2 -n $lv1 $vg1 ++ lvcreate -Zn -an -l 2 -n $lv1 $vg2 ++ lvcreate -Zn -an -l 2 -n $lv2 $vg2 ++ lvcreate -Zn -an -l 2 -n $lv1 $vg3 ++ lvcreate -Zn -an -l 2 -n $lv2 $vg3 ++ lvcreate -Zn -an -l 2 -n $lv3 $vg3 ++ lvcreate -Zn -an -l 2 -n $lv1 $vg5 ++ lvcreate -Zn -an -l 2 -n $lv2 $vg5 ++ lvcreate -Zn -an -l 2 -n $lv3 $vg5 ++ lvcreate -Zn -an -l 2 -n $lv4 $vg5 ++ lvcreate -Zn -an -l 2 -n $lv5 $vg5 ++} ++ ++# ++# ++# ++prepare_vgs_ + + not lvremove + not lvremove garbage + not lvremove $vg1/garbage + + lvremove $vg1 +-lvs $vg1 +-not lvs $vg1/$lv1 ++check lv_exists $vg1 ++check lv_not_exists $vg1 $lv1 + vgremove $vg1 + + lvremove $vg2 +-lvs $vg2 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 ++check lv_exists $vg2 ++check lv_not_exists $vg2 $lv1 $lv2 + vgremove $vg2 + + lvremove $vg3/$lv1 + lvremove $vg3/$lv2 $vg3/$lv3 +-lvs $vg3 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg3/$lv3 ++check lv_exists $vg3 ++check lv_not_exists $vg3 $lv1 $lv2 $lv3 + vgremove $vg3 + + lvremove $vg4 +-lvs $vg4 ++check lv_exists $vg4 + vgremove $vg4 + + lvremove $vg5/$lv1 $vg5 $vg5/$lv3 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + vgremove $vg5 + + + # + # test lvremove vg|lv names from multiple vgs + # +- +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 ++prepare_vgs_ + + lvremove $vg2 $vg3/$lv3 $vg5/$lv1 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 +-not lvs $vg3/$lv3 +-not lvs $vg5/$lv1 ++check lv_not_exists $vg2 $lv1 $lv2 ++check lv_not_exists $vg3 $lv3 ++check lv_not_exists $vg5 $lv1 + + lvremove $vg2 $vg1 +-not lvs $vg1/$lv1 ++check lv_not_exists $vg1 $lv1 + + lvremove $vg3/$lv1 $vg3 $vg4 $vg5/$lv2 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg5/$lv2 ++check lv_not_exists $vg3 $lv1 $lv2 ++check lv_not_exists $vg5 $lv2 + + lvremove $vg5 $vg1 $vg5/$lv3 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg5 $lv3 $lv4 $lv5 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvremove @lvtags + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + lvchange --addtag V1L1 $vg1/$lv1 + lvchange --addtag V2L1 $vg2/$lv1 + lvchange --addtag V2L2 $vg2/$lv2 +@@ -185,99 +134,52 @@ lvchange --addtag V5L234 $vg5/$lv2 + lvchange --addtag V5L234 $vg5/$lv3 + lvchange --addtag V5L234 $vg5/$lv4 + lvchange --addtag V5L5 $vg5/$lv5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 ++vgchange -an $vg1 $vg2 $vg3 $vg4 $vg5 + + # verify all exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv2 +-lvs $vg3/$lv3 +-lvs $vg5/$lv1 +-lvs $vg5/$lv2 +-lvs $vg5/$lv3 +-lvs $vg5/$lv4 +-lvs $vg5/$lv5 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv2 $lv3 ++check lv_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + + lvremove @garbage + + lvremove @V3L3A +-not lvs $vg3/$lv3 ++check lv_not_exists $vg3 $lv3 + # verify unremoved still exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv2 +-lvs $vg5/$lv1 +-lvs $vg5/$lv2 +-lvs $vg5/$lv3 +-lvs $vg5/$lv4 +-lvs $vg5/$lv5 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv2 ++check lv_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + + lvremove @V5L234 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 ++check lv_not_exists $vg5 $lv2 $lv3 $lv4 + # verify unremoved still exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv2 +-lvs $vg5/$lv1 +-lvs $vg5/$lv5 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv2 ++check lv_exists $vg5 $lv1 $lv5 + + lvremove @V5L1 @V5L5 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg5 $lv1 $lv5 + # verify unremoved still exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv2 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv2 + + lvremove @V23 @V1L1 @V3L2 +-not lvs $vg1/$lv1 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 ++check lv_not_exists $vg1 $lv1 ++check lv_not_exists $vg2 $lv1 $lv2 ++check lv_not_exists $vg3 $lv1 $lv2 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvremove @vgtags + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + vgchange --addtag V1 $vg1 + vgchange --addtag V23 $vg2 + vgchange --addtag V23 $vg3 +@@ -285,71 +187,34 @@ vgchange --addtag V35 $vg3 + vgchange --addtag V4 $vg4 + vgchange --addtag V35 $vg5 + vgchange --addtag V5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 ++vgchange -an $vg1 $vg2 $vg3 $vg4 $vg5 + + lvremove @V4 + # verify unremoved exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv2 +-lvs $vg3/$lv3 +-lvs $vg5/$lv1 +-lvs $vg5/$lv2 +-lvs $vg5/$lv3 +-lvs $vg5/$lv4 +-lvs $vg5/$lv5 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv2 $lv3 ++check lv_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + + lvremove @V5 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + # verify unremoved exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv2 +-lvs $vg3/$lv3 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv2 $lv3 + + lvremove @V1 @V23 +-not lvs $vg1/$lv1 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg3/$lv3 ++check lv_not_exists $vg1 $lv1 ++check lv_not_exists $vg2 $lv1 $lv2 ++check lv_not_exists $vg3 $lv1 $lv2 $lv3 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 ++ ++# ++# ++# ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + vgchange --addtag V1 $vg1 + vgchange --addtag V23 $vg2 + vgchange --addtag V23 $vg3 +@@ -357,59 +222,26 @@ vgchange --addtag V35 $vg3 + vgchange --addtag V4 $vg4 + vgchange --addtag V35 $vg5 + vgchange --addtag V5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 + + lvremove @V35 @V5 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg3/$lv3 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg3 $lv1 $lv2 /$lv3 ++check lv_not_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + # verify unremoved exist +-lvs $vg1/$lv1 +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 ++check lv_exists $vg1 $lv1 ++check lv_exists $vg2 $lv1 $lv2 + + lvremove @V1 @V23 +-not lvs $vg1/$lv1 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 ++check lv_not_exists $vg1 $lv1 ++check lv_not_exists $vg2 $lv1 $lv2 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvremove vg|lv names and @lvtags + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + lvchange --addtag V1L1 $vg1/$lv1 + lvchange --addtag V2L1 $vg2/$lv1 + lvchange --addtag V2L2 $vg2/$lv2 +@@ -426,56 +258,27 @@ lvchange --addtag V5L234 $vg5/$lv2 + lvchange --addtag V5L234 $vg5/$lv3 + lvchange --addtag V5L234 $vg5/$lv4 + lvchange --addtag V5L5 $vg5/$lv5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 ++vgchange -an $vg1 $vg2 $vg3 $vg4 $vg5 + + lvremove $vg1/$lv1 @V3L2 @V5L234 +-not lvs $vg1/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 ++check lv_not_exists $vg1 $lv1 ++check lv_not_exists $vg3 $lv2 ++check lv_not_exists $vg5 $lv2 $lv3 $lv4 + # verify unremoved exist +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 +-lvs $vg3/$lv1 +-lvs $vg3/$lv3 +-lvs $vg5/$lv1 +-lvs $vg5/$lv5 ++check lv_exists $vg2 $lv1 $lv2 ++check lv_exists $vg3 $lv1 $lv3 ++check lv_exists $vg5 $lv1 $lv5 + + lvremove $vg2/$lv1 @V23 $vg5/$lv1 @V5L5 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvremove vg|lv names and @vgtags + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + vgchange --addtag V1 $vg1 + vgchange --addtag V23 $vg2 + vgchange --addtag V23 $vg3 +@@ -483,56 +286,24 @@ vgchange --addtag V35 $vg3 + vgchange --addtag V4 $vg4 + vgchange --addtag V35 $vg5 + vgchange --addtag V5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 + + lvremove $vg1/$lv1 @V35 +-not lvs $vg1/$lv1 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg3/$lv3 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg1 $lv1 ++check lv_not_exists $vg3 $lv1 $lv2 $lv3 ++check lv_not_exists $vg5 $lv1 $lv2 $lv3not $lv4 $lv5 + # verify unremoved exist +-lvs $vg2/$lv1 +-lvs $vg2/$lv2 ++check lv_exists $vg2 $lv1 $lv2 + + lvremove $vg2/$lv1 @V23 $vg2/$lv2 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvremove @lvtags and @vgtags + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + lvchange --addtag V1L1 $vg1/$lv1 + lvchange --addtag V2L1 $vg2/$lv1 + lvchange --addtag V2L2 $vg2/$lv2 +@@ -557,57 +328,25 @@ vgchange --addtag V35 $vg3 + vgchange --addtag V4 $vg4 + vgchange --addtag V35 $vg5 + vgchange --addtag V5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 + + lvremove @V23 @V35 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg3/$lv3 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg2 $lv1 $lv2 ++check lv_not_exists $vg3 $lv1 $lv2 $lv3 ++check lv_not_exists $vg5 $lv1 $lv2 $lv3 $lv4 $lv5 + # verify unremoved exist +-lvs $vg1/$lv1 ++check lv_exists $vg1 $lv1 + + lvremove @V1 @V1L1 +-not lvs $vg1/$lv1 ++check lv_not_exists $vg1 $lv1 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvremove vg|lv names and @lvtags and @vgtags + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + lvchange --addtag V1L1 $vg1/$lv1 + lvchange --addtag V2L1 $vg2/$lv1 + lvchange --addtag V2L2 $vg2/$lv2 +@@ -632,60 +371,26 @@ vgchange --addtag V35 $vg3 + vgchange --addtag V4 $vg4 + vgchange --addtag V35 $vg5 + vgchange --addtag V5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 + + lvremove $vg1/$lv1 @V23 @V5L5 +-not lvs $vg1/$lv1 +-not lvs $vg2/$lv1 +-not lvs $vg2/$lv2 +-not lvs $vg3/$lv1 +-not lvs $vg3/$lv2 +-not lvs $vg3/$lv3 +-not lvs $vg5/$lv5 ++check lv_not_exists $vg1 $lv1 ++check lv_not_exists $vg2 $lv1 $lv2 ++check lv_not_exists $vg3 $lv1 $lv2 $lv3 ++check lv_not_exists $vg5 $lv5 + # verify unremoved exist +-lvs $vg5/$lv1 +-lvs $vg5/$lv2 +-lvs $vg5/$lv3 +-lvs $vg5/$lv4 ++check lv_exists $vg5 $lv1 $lv2 $lv3 $lv4 + + lvremove $vg5/$lv2 @V5L234 @V5 +-not lvs $vg5/$lv1 +-not lvs $vg5/$lv2 +-not lvs $vg5/$lv3 +-not lvs $vg5/$lv4 ++check lv_not_exists $vg5 $lv1 $lv2 $lv3 $lv4 + +-vgremove $vg1 +-vgremove $vg2 +-vgremove $vg3 +-vgremove $vg4 +-vgremove $vg5 ++vgremove $vg1 $vg2 $vg3 $vg4 $vg5 + + + # + # test lvs: empty, vg(s), lv(s), vgtag(s), lvtag(s), garbage, combinations + # ++prepare_vgs_ + +-# set up vgs/lvs that we will remove +-vgcreate $vg1 "$dev1" "$dev2" +-vgcreate $vg2 "$dev3" "$dev4" +-vgcreate $vg3 "$dev5" "$dev6" +-vgcreate $vg4 "$dev7" "$dev8" +-vgcreate $vg5 "$dev9" "$dev10" +-lvcreate -l 2 -n $lv1 $vg1 +-lvcreate -l 2 -n $lv1 $vg2 +-lvcreate -l 2 -n $lv2 $vg2 +-lvcreate -l 2 -n $lv1 $vg3 +-lvcreate -l 2 -n $lv2 $vg3 +-lvcreate -l 2 -n $lv3 $vg3 +-lvcreate -l 2 -n $lv1 $vg5 +-lvcreate -l 2 -n $lv2 $vg5 +-lvcreate -l 2 -n $lv3 $vg5 +-lvcreate -l 2 -n $lv4 $vg5 +-lvcreate -l 2 -n $lv5 $vg5 + lvchange --addtag V1L1 $vg1/$lv1 + lvchange --addtag V2L1 $vg2/$lv1 + lvchange --addtag V2L2 $vg2/$lv2 +@@ -709,11 +414,6 @@ vgchange --addtag V35 $vg3 + vgchange --addtag V4 $vg4 + vgchange --addtag V35 $vg5 + vgchange --addtag V5 $vg5 +-vgchange -an $vg1 +-vgchange -an $vg2 +-vgchange -an $vg3 +-vgchange -an $vg4 +-vgchange -an $vg5 + + # empty + lvs -o vg_name,lv_name --separator '-' >err +@@ -951,4 +651,3 @@ not grep $vg5-$lv2 err + not grep $vg5-$lv3 err + not grep $vg5-$lv4 err + not grep $vg5-$lv5 err +- +diff --git a/test/shell/pvmove-all-segtypes.sh b/test/shell/pvmove-all-segtypes.sh +index fcd8913..5e1c71c 100644 +--- a/test/shell/pvmove-all-segtypes.sh ++++ b/test/shell/pvmove-all-segtypes.sh +@@ -13,12 +13,11 @@ test_description="ensure pvmove works with all common segment types" + + . lib/test + +-test -e LOCAL_CLVMD && skip + which mkfs.ext2 || skip + which md5sum || skip + + aux prepare_pvs 5 20 +-vgcreate -c n -s 256k $vg $(cat DEVICES) ++vgcreate -s 256k $vg $(cat DEVICES) + + # Each of the following tests does: + # 1) Create two LVs - one linear and one other segment type +@@ -27,8 +26,8 @@ vgcreate -c n -s 256k $vg $(cat DEVICES) + # 3) Move only the second LV by name + + # Testing pvmove of linear LV +-lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" +-lvcreate -l 2 -n $lv1 $vg "$dev1" ++lvcreate -aey -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate -aey -l 2 -n $lv1 $vg "$dev1" + check lv_tree_on $vg ${lv1}_foo "$dev1" + check lv_tree_on $vg $lv1 "$dev1" + aux mkdev_md5sum $vg $lv1 +@@ -43,8 +42,8 @@ check dev_md5sum $vg $lv1 + lvremove -ff $vg + + # Testing pvmove of stripe LV +-lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" +-lvcreate -l 4 -i 2 -n $lv1 $vg "$dev1" "$dev2" ++lvcreate -aey -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate -aey -l 4 -i 2 -n $lv1 $vg "$dev1" "$dev2" + check lv_tree_on $vg ${lv1}_foo "$dev1" + check lv_tree_on $vg $lv1 "$dev1" "$dev2" + aux mkdev_md5sum $vg $lv1 +@@ -58,9 +57,14 @@ check lv_tree_on $vg ${lv1}_foo "$dev5" + check dev_md5sum $vg $lv1 + lvremove -ff $vg + ++if test -e LOCAL_CLVMD ; then ++#FIXME these tests currently fail end require cmirrord ++echo "TEST WARNING, FIXME!!! pvmove in clustered VG not fully supported!" ++else ++ + # Testing pvmove of mirror LV +-lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" +-lvcreate -l 2 --type mirror -m 1 -n $lv1 $vg "$dev1" "$dev2" ++lvcreate -aey -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate -aey -l 2 --type mirror -m 1 -n $lv1 $vg "$dev1" "$dev2" + check lv_tree_on $vg ${lv1}_foo "$dev1" + check lv_tree_on $vg $lv1 "$dev1" "$dev2" + aux mkdev_md5sum $vg $lv1 +@@ -76,8 +80,8 @@ lvremove -ff $vg + + # Dummy LV and snap share dev1, while origin is on dev2 + # Testing pvmove of snapshot LV +-lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" +-lvcreate -l 2 -n $lv1 $vg "$dev2" ++lvcreate -aey -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate -aey -l 2 -n $lv1 $vg "$dev2" + lvcreate -s $vg/$lv1 -l 2 -n snap "$dev1" + check lv_tree_on $vg ${lv1}_foo "$dev1" + check lv_tree_on $vg snap "$dev1" +@@ -91,3 +95,4 @@ check lv_tree_on $vg snap "$dev4" + check lv_tree_on $vg ${lv1}_foo "$dev5" + check dev_md5sum $vg snap + lvremove -ff $vg ++fi +diff --git a/test/shell/pvmove-cache-segtypes.sh b/test/shell/pvmove-cache-segtypes.sh +new file mode 100644 +index 0000000..1c2423e +--- /dev/null ++++ b/test/shell/pvmove-cache-segtypes.sh +@@ -0,0 +1,178 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++test_description="ensure pvmove works with the cache segment types" ++ ++. lib/test ++ ++# pvmove fails when a RAID LV is the origin of a cache LV ++# pvmoving cache types is currently disabled in tools/pvmove.c ++# So, for now we set everything up and make sure pvmove /isn't/ allowed. ++# This allows us to ensure that it is disallowed even when there are ++# stacking complications to consider. ++ ++test -e LOCAL_CLVMD && skip ++which mkfs.ext2 || skip ++which md5sum || skip ++ ++aux target_at_least dm-cache 1 3 0 || skip ++# for stacking ++aux target_at_least dm-thin-pool 1 8 0 || skip ++aux target_at_least dm-raid 1 4 2 || skip ++ ++aux prepare_vg 5 80 ++ ++# Each of the following tests does: ++# 1) Create two LVs - one linear and one other segment type ++# The two LVs will share a PV. ++# 2) Move both LVs together ++# 3) Move only the second LV by name ++ ++# Testing pvmove of cache-pool LV (can't check contents though) ++############################################################### ++lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate --type cache-pool -n ${lv1}_pool -l 4 $vg "$dev1" ++check lv_tree_on $vg ${lv1}_foo "$dev1" ++check lv_tree_on $vg ${lv1}_pool "$dev1" ++ ++pvmove "$dev1" "$dev5" 2>&1 | tee out ++grep "Skipping cache-pool LV, ${lv1}_pool" out ++grep "Skipping cache-related LV, ${lv1}_pool_cmeta" out ++grep "Skipping cache-related LV, ${lv1}_pool_cdata" out ++check lv_tree_on $vg ${lv1}_foo "$dev5" ++not check lv_tree_on $vg ${lv1}_pool "$dev5" ++ ++#pvmove -n ${lv1}_pool "$dev5" "$dev4" ++#check lv_tree_on $vg ${lv1}_pool "$dev4" ++#check lv_tree_on $vg ${lv1}_foo "$dev5" ++ ++lvremove -ff $vg ++ ++# Testing pvmove of origin LV ++############################# ++lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate --type cache-pool -n ${lv1}_pool -l 4 $vg "$dev5" ++lvcreate --type cache -n $lv1 -l 8 $vg/${lv1}_pool "$dev1" ++ ++check lv_tree_on $vg ${lv1}_foo "$dev1" ++check lv_tree_on $vg ${lv1}_pool "$dev5" ++check lv_tree_on $vg ${lv1} "$dev1" ++ ++aux mkdev_md5sum $vg $lv1 ++pvmove "$dev1" "$dev3" 2>&1 | tee out ++grep "Skipping cache LV, ${lv1}" out ++check lv_tree_on $vg ${lv1}_foo "$dev3" ++#check lv_tree_on $vg ${lv1}_pool "$dev5" ++lvs -a -o name,attr,devices $vg ++not check lv_tree_on $vg ${lv1} "$dev3" ++#check dev_md5sum $vg $lv1 ++ ++#pvmove -n $lv1 "$dev3" "$dev1" ++#check lv_tree_on $vg ${lv1}_foo "$dev3" ++#check lv_tree_on $vg ${lv1}_pool "$dev5" ++#check lv_tree_on $vg ${lv1} "$dev1" ++#check dev_md5sum $vg $lv1 ++lvremove -ff $vg ++ ++# Testing pvmove of a RAID origin LV ++#################################### ++lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate --type raid1 -m 1 -l 8 -n $lv1 $vg "$dev1" "$dev2" ++lvcreate --type cache -l 4 -n ${lv1}_pool $vg/$lv1 "$dev5" ++check lv_tree_on $vg ${lv1}_foo "$dev1" ++check lv_tree_on $vg ${lv1} "$dev1" "$dev2" ++check lv_tree_on $vg ${lv1}_pool "$dev5" ++ ++aux mkdev_md5sum $vg $lv1 ++pvmove "$dev1" "$dev3" 2>&1 | tee out ++grep "Skipping cache LV, ${lv1}" out ++check lv_tree_on $vg ${lv1}_foo "$dev3" ++not check lv_tree_on $vg ${lv1} "$dev2" "$dev3" ++#check lv_tree_on $vg ${lv1}_pool "$dev5" ++#check dev_md5sum $vg $lv1 -- THIS IS WHERE THINGS FAIL IF PVMOVE NOT DISALLOWED ++ ++#pvmove -n $lv1 "$dev3" "$dev1" ++#check lv_tree_on $vg ${lv1}_foo "$dev3" ++#check lv_tree_on $vg ${lv1} "$dev1" "$dev2" ++#check lv_tree_on $vg ${lv1}_pool "$dev5" ++#check dev_md5sum $vg $lv1 ++lvremove -ff $vg ++ ++# Testing pvmove of a RAID cachepool (metadata and data) ++######################################################## ++lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" ++lvcreate --type raid1 -L 2M -n meta $vg "$dev1" "$dev2" ++lvcreate --type raid1 -L 4M -n ${lv1}_pool $vg "$dev1" "$dev2" ++lvconvert --type cache-pool $vg/${lv1}_pool --poolmetadata $vg/meta ++lvcreate --type cache -n $lv1 -L 8M $vg/${lv1}_pool "$dev5" ++ ++check lv_tree_on $vg ${lv1}_foo "$dev1" ++check lv_tree_on $vg ${lv1}_pool "$dev1" "$dev2" ++check lv_tree_on $vg ${lv1} "$dev5" ++ ++aux mkdev_md5sum $vg $lv1 ++# This will move ${lv1}_foo and the cache-pool data & meta ++# LVs, both of which contain a RAID1 _rimage & _rmeta LV - 5 total LVs ++pvmove "$dev1" "$dev3" 2>&1 | tee out ++grep "Skipping cache-pool LV, ${lv1}_pool" out ++grep "Skipping cache-related LV, ${lv1}_pool_cmeta" out ++grep "Skipping cache-related LV, ${lv1}_pool_cdata" out ++check lv_tree_on $vg ${lv1}_foo "$dev3" ++not check lv_tree_on $vg ${lv1}_pool "$dev2" "$dev3" ++#check lv_tree_on $vg ${lv1} "$dev5" ++#check dev_md5sum $vg $lv1 ++ ++#pvmove -n ${lv1}_pool "$dev3" "$dev1" ++#check lv_tree_on $vg ${lv1}_foo "$dev3" ++#check lv_tree_on $vg ${lv1}_pool "$dev1" "$dev2" ++#check lv_tree_on $vg ${lv1} "$dev5" ++#check dev_md5sum $vg $lv1 ++lvremove -ff $vg ++ ++# Testing pvmove of Thin-pool on cache LV on RAID ++################################################# ++lvcreate -l 2 -n ${lv1}_foo $vg "$dev1" ++# RAID for cachepool ++lvcreate --type raid1 -m 1 -L 2M -n meta $vg "$dev1" "$dev2" ++lvcreate --type raid1 -m 1 -L 4M -n cachepool $vg "$dev1" "$dev2" ++lvconvert --type cache-pool $vg/cachepool --poolmetadata $vg/meta ++# RAID for thin pool data LV ++lvcreate --type raid1 -m 1 -L 8 -n thinpool $vg "$dev3" "$dev4" ++# Convert thin pool data to a cached LV ++lvconvert --type cache $vg/thinpool --cachepool $vg/cachepool ++# Create simple thin pool meta ++lvcreate -L 2M -n meta $vg "$dev1" ++# Use thin pool data LV to build a thin pool ++lvconvert --thinpool $vg/thinpool --poolmetadata $vg/meta ++# Create a thin lv for fun ++lvcreate -T $vg/thinpool -V 20 -n thin_lv ++ ++check lv_tree_on $vg ${lv1}_foo "$dev1" ++check lv_tree_on $vg cachepool "$dev1" "$dev2" ++check lv_tree_on $vg thinpool "$dev1" "$dev3" "$dev4" ++ ++aux mkdev_md5sum $vg thin_lv ++lvs -a -o name,attr,devices $vg ++# Should move ${lv1}_foo and thinpool_tmeta from dev1 to dev5 ++pvmove "$dev1" "$dev5" 2>&1 | tee out ++lvs -a -o name,attr,devices $vg ++check lv_tree_on $vg ${lv1}_foo "$dev5" ++not check lv_tree_on $vg cachepool "$dev5" "$dev2" ++check lv_tree_on $vg thinpool "$dev3" "$dev4" "$dev5" # Move non-cache tmeta ++#check dev_md5sum $vg/thin_lv ++ ++#pvmove -n $vg/cachepool "$dev5" "$dev1" ++#check lv_tree_on $vg ${lv1}_foo "$dev5" ++#check lv_tree_on $vg $vg/cachepool "$dev1" "$dev2" ++#check lv_tree_on $vg $vg/thinpool "$dev3" "$dev4" ++#check dev_md5sum $vg/thin_lv ++ ++lvremove -ff $vg +\ No newline at end of file +diff --git a/test/shell/read-ahead.sh b/test/shell/read-ahead.sh +index 8c8f42c..264910c 100644 +--- a/test/shell/read-ahead.sh ++++ b/test/shell/read-ahead.sh +@@ -34,7 +34,7 @@ lvremove -ff $vg + blockdev --setra 768 "$dev1" + vgscan + lvcreate -n $lv -L4m $vg "$dev1" +-test $(blockdev --getra $DM_DEV_DIR/$vg/$lv) -eq 768 ++test $(blockdev --getra "$DM_DEV_DIR/$vg/$lv") -eq 768 + lvremove -ff $vg + + # Check default, active/inactive values for read_ahead / kernel_read_ahead +diff --git a/test/shell/snapshot-usage.sh b/test/shell/snapshot-usage.sh +index 9e6a14f..cb60556 100644 +--- a/test/shell/snapshot-usage.sh ++++ b/test/shell/snapshot-usage.sh +@@ -13,8 +13,11 @@ + + . lib/test + ++MKFS=mkfs.ext2 ++which $MKFS || skip ++ + fill() { +- dd if=/dev/zero of=$DM_DEV_DIR/$vg1/lvol0 bs=$1 count=1 ++ dd if=/dev/zero of="$DM_DEV_DIR/$vg1/lvol0" bs=$1 count=1 + } + + cleanup_tail() +@@ -37,8 +40,8 @@ aux lvmconf "activation/snapshot_autoextend_percent = 20" \ + "activation/snapshot_autoextend_threshold = 50" + + # Check usability with smallest extent size +-pvcreate --setphysicalvolumesize 4T $DM_DEV_DIR/$vg/$lv +-vgcreate -s 1K $vg1 $DM_DEV_DIR/$vg/$lv ++pvcreate --setphysicalvolumesize 4T "$DM_DEV_DIR/$vg/$lv" ++vgcreate -s 1K $vg1 "$DM_DEV_DIR/$vg/$lv" + + # Test removal of opened snapshot + lvcreate -V50 -L10 -n $lv1 -s $vg1 +@@ -52,9 +55,12 @@ lvs -a -o+lv_active $vg1 + + trap 'cleanup_tail' EXIT + # Keep device busy (but not mounted) for a while +-sleep 30 < $DM_DEV_DIR/$vg1/$lv1 & ++sleep 30 < "$DM_DEV_DIR/$vg1/$lv1" & + SLEEP_PID=$! + ++# give some short time to lock file above ++sleep 0.1 ++ + # Opened virtual snapshot device is not removable + # it should retry device removal for a few seconds + not lvremove -f $vg1/$lv1 +@@ -104,17 +110,46 @@ lvextend --use-policies $vg1/lvol1 + check lv_field $vg1/lvol1 size "18.00k" + + lvextend -l+33 $vg1/lvol1 +-check lv_field $vg1/lvol1 size "28.00k" ++check lv_field $vg1/lvol1 size "32.00k" + + fill 20K ++lvremove -f $vg1 ++ ++# Check snapshot really deletes COW header for read-only snapshot ++# Test needs special relation between chunk size and extent size ++# This test expects extent size 1K ++aux lvmconf "allocation/wipe_signatures_when_zeroing_new_lvs = 1" ++lvcreate -aey -L4 -n $lv $vg1 ++lvcreate -c 8 -s -L1 -n snap $vg1/$lv ++# Populate snapshot ++#dd if=/dev/urandom of="$DM_DEV_DIR/$vg1/$lv" bs=4096 count=10 ++$MKFS "$DM_DEV_DIR/$vg1/$lv" ++lvremove -f $vg1/snap ++ ++# Undeleted header would trigger attempt to access ++# beyond end of COW device ++# Fails to create when chunk size is different ++lvcreate -s -pr -l12 -n snap $vg1/$lv ++ ++# When header is undelete, fails to read snapshot without read errors ++#dd if="$DM_DEV_DIR/$vg1/snap" of=/dev/null bs=1M count=2 ++fsck -n "$DM_DEV_DIR/$vg1/snap" ++ ++# This test would trigger read of weird percentage for undeleted header ++# And since older snapshot target counts with metadata sectors ++# we have 2 valid results (unsure about correct version number) ++EXPECT="0.00" ++aux target_at_least dm-snapshot 1 10 0 || EXPECT="66.67" ++check lv_field $vg1/snap data_percent "$EXPECT" ++ + vgremove -ff $vg1 + + # Can't test >= 16T devices on 32bit + if test "$TSIZE" -eq 15P ; then + + # Check usability with largest extent size +-pvcreate $DM_DEV_DIR/$vg/$lv +-vgcreate -s 4G $vg1 $DM_DEV_DIR/$vg/$lv ++pvcreate "$DM_DEV_DIR/$vg/$lv" ++vgcreate -s 4G $vg1 "$DM_DEV_DIR/$vg/$lv" + + lvcreate -an -Zn -l50%FREE -n $lv1 $vg1 + lvcreate -s -l100%FREE -n $lv2 $vg1/$lv1 +diff --git a/test/shell/thin-merge.sh b/test/shell/thin-merge.sh +index 10a81f0..7eb137e 100644 +--- a/test/shell/thin-merge.sh ++++ b/test/shell/thin-merge.sh +@@ -13,7 +13,8 @@ + + . lib/test + +-which mkfs.ext2 || skip ++MKFS=mkfs.ext2 ++which $MKFS || skip + which fsck || skip + + # +@@ -27,14 +28,14 @@ lvcreate -T -L8M $vg/pool -V10M -n $lv1 + lvchange --addtag tagL $vg/$lv1 + + mkdir mnt +-mkfs.ext2 $DM_DEV_DIR/$vg/$lv1 +-mount $DM_DEV_DIR/$vg/$lv1 mnt ++$MKFS "$DM_DEV_DIR/$vg/$lv1" ++mount "$DM_DEV_DIR/$vg/$lv1" mnt + touch mnt/test + + lvcreate -K -s -n snap --addtag tagS $vg/$lv1 + mkdir mntsnap +-mkfs.ext2 $DM_DEV_DIR/$vg/snap +-mount $DM_DEV_DIR/$vg/snap mntsnap ++$MKFS "$DM_DEV_DIR/$vg/snap" ++mount "$DM_DEV_DIR/$vg/snap" mntsnap + touch mntsnap/test_snap + + lvs -o+tags,thin_id $vg +@@ -82,10 +83,10 @@ lvchange -ay $vg/$lv1 + check lv_exists $vg $lv1 + check lv_field $vg/$lv1 thin_id "2" + check lv_field $vg/$lv1 tags "tagL" +-not check lv_exists $vg snap ++check lv_not_exists $vg snap + +-fsck -n $DM_DEV_DIR/$vg/$lv1 +-mount $DM_DEV_DIR/$vg/$lv1 mnt ++fsck -n "$DM_DEV_DIR/$vg/$lv1" ++mount "$DM_DEV_DIR/$vg/$lv1" mnt + test -e mnt/test_snap + umount mnt + +@@ -97,8 +98,10 @@ lvcreate -s -n snap $vg/$lv1 + # Also add old snapshot to thin origin + lvcreate -s -L10 -n oldsnapof_${lv1} $vg/$lv1 + not lvconvert --merge $vg/snap +-lvremove -f $vg/oldsnapof_${lv1} +- ++$MKFS "$DM_DEV_DIR/$vg/oldsnapof_${lv1}" ++lvconvert --merge $vg/oldsnapof_${lv1} ++fsck -n "$DM_DEV_DIR/$vg/$lv1" ++check lv_not_exists $vg oldsnapof_${lv1} + # Add old snapshot to thin snapshot + lvcreate -s -L10 -n oldsnapof_snap $vg/snap + lvconvert --merge $vg/snap +diff --git a/test/shell/thin-vglock.sh b/test/shell/thin-vglock.sh +new file mode 100644 +index 0000000..083fcd8 +--- /dev/null ++++ b/test/shell/thin-vglock.sh +@@ -0,0 +1,51 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Test locking works and doesn't update metadata ++# RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1063542 ++ ++. lib/test ++ ++MKFS=mkfs.ext2 ++which $MKFS || skip ++ ++aux have_thin 1 0 0 || skip ++aux prepare_vg ++ ++lvcreate -L10 -T -V5 -n $lv1 $vg/pool ++lvcreate -an -V10 -T $vg/pool ++ ++$MKFS "$DM_DEV_DIR/$vg/$lv1" ++mkdir mnt ++mount "$DM_DEV_DIR/$vg/$lv1" mnt ++ ++lvcreate -s -n snap $vg/$lv1 ++check lv_field $vg/snap thin_id "3" ++ ++lvconvert --merge $vg/snap ++ ++umount mnt ++vgchange -an $vg ++ ++# Check reboot case ++vgchange -ay --sysinit $vg ++# Metadata are still not updated (--poll n) ++check lv_field $vg/$lv1 thin_id "1" ++check lv_field $vg/pool transaction_id "3" ++ ++# Check the metadata are updated after refresh ++vgchange --refresh $vg ++check lv_field $vg/$lv1 thin_id "3" ++check lv_field $vg/pool transaction_id "4" ++ ++#lvs -a -o+transaction_id,thin_id $vg ++ ++vgremove -f $vg +diff --git a/test/shell/vgsplit-stacked.sh b/test/shell/vgsplit-stacked.sh +index 3256191..c8b89b5 100644 +--- a/test/shell/vgsplit-stacked.sh ++++ b/test/shell/vgsplit-stacked.sh +@@ -18,8 +18,8 @@ vgcreate $vg1 "$dev1" "$dev2" + lvcreate -n $lv1 -l 100%FREE $vg1 + + #top VG +-pvcreate $DM_DEV_DIR/$vg1/$lv1 +-vgcreate $vg $DM_DEV_DIR/$vg1/$lv1 "$dev3" ++pvcreate "$DM_DEV_DIR/$vg1/$lv1" ++vgcreate $vg "$DM_DEV_DIR/$vg1/$lv1" "$dev3" + + vgchange -a n $vg $vg1 + +diff --git a/tools/Makefile.in b/tools/Makefile.in +index f18d3ea..253c404 100644 +--- a/tools/Makefile.in ++++ b/tools/Makefile.in +@@ -43,6 +43,7 @@ SOURCES =\ + pvscan.c \ + reporter.c \ + segtypes.c \ ++ tags.c \ + toollib.c \ + vgcfgbackup.c \ + vgcfgrestore.c \ +@@ -166,7 +167,7 @@ liblvm2cmd.$(LIB_SUFFIX).$(LIB_VERSION): liblvm2cmd.$(LIB_SUFFIX) + + .commands: $(srcdir)/commands.h $(srcdir)/cmdnames.h Makefile + $(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \ +- egrep -v '^ *(|#.*|devtypes|dumpconfig|formats|help|pvdata|segtypes|version) *$$' > .commands ++ egrep -v '^ *(|#.*|devtypes|dumpconfig|formats|help|pvdata|segtypes|tags|version) *$$' > .commands + + ifneq ("$(CFLOW_CMD)", "") + CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES)) +diff --git a/tools/args.h b/tools/args.h +index 1207189..f1a4ef4 100644 +--- a/tools/args.h ++++ b/tools/args.h +@@ -62,6 +62,8 @@ arg(monitor_ARG, '\0', "monitor", yes_no_arg, 0) + arg(config_ARG, '\0', "config", string_arg, 0) + arg(trustcache_ARG, '\0', "trustcache", NULL, 0) + arg(cache_ARG, '\0', "cache", NULL, 0) ++arg(cachemode_ARG, '\0', "cachemode", string_arg, 0) ++arg(cachepool_ARG, '\0', "cachepool", string_arg, 0) + arg(ignoremonitoring_ARG, '\0', "ignoremonitoring", NULL, 0) + arg(nameprefixes_ARG, '\0', "nameprefixes", NULL, 0) + arg(unquoted_ARG, '\0', "unquoted", NULL, 0) +diff --git a/tools/commands.h b/tools/commands.h +index 938898b..b0d608b 100644 +--- a/tools/commands.h ++++ b/tools/commands.h +@@ -212,13 +212,27 @@ xx(lvconvert, + "\t[-T|--thin ExternalLogicalVolume[Path]\n" + "\t [--originname NewExternalOriginVolumeName]]\n" + "\t[-Z|--zero {y|n}]\n" +- "\t[-d|--debug] [-h|-?|--help] [-v|--verbose]\n", ++ "\t[-d|--debug] [-h|-?|--help] [-v|--verbose]\n\n" + +- alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, discards_ARG, +- force_ARG, interval_ARG, merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, +- noudevsync_ARG, originname_ARG, poolmetadata_ARG, poolmetadatasize_ARG, +- poolmetadataspare_ARG, readahead_ARG, regionsize_ARG, repair_ARG, +- replace_ARG, snapshot_ARG, splitmirrors_ARG, splitsnapshot_ARG, ++ "lvconvert " ++ "--type cache_pool\n" ++ "\t[--cachemode CacheMode]\n" ++ "\t[--chunksize size]\n" ++ "\t[--poolmetadata CacheMetadataLogicalVolume[Path] |\n" ++ "\t [--poolmetadatasize size]\n" ++ "\t [--poolmetadataspare {y|n}]]\n" ++ "\tCacheDataLogicalVolume[Path]\n\n" ++ ++ "lvconvert " ++ "--type cache\n" ++ "\t--cachepool CachePoolLogicalVolume[Path]\n" ++ "\tLogicalVolume[Path]\n\n", ++ ++ alloc_ARG, background_ARG, cachemode_ARG, cachepool_ARG, chunksize_ARG, ++ corelog_ARG, discards_ARG, force_ARG, interval_ARG, merge_ARG, mirrorlog_ARG, ++ mirrors_ARG, name_ARG, noudevsync_ARG, originname_ARG, poolmetadata_ARG, ++ poolmetadatasize_ARG, poolmetadataspare_ARG, readahead_ARG, regionsize_ARG, ++ repair_ARG, replace_ARG, snapshot_ARG, splitmirrors_ARG, splitsnapshot_ARG, + stripes_long_ARG, stripesize_ARG, test_ARG, thin_ARG, thinpool_ARG, + trackchanges_ARG, type_ARG, use_policies_ARG, zero_ARG) + +@@ -230,6 +244,7 @@ xx(lvcreate, + "\t[-a|--activate [a|e|l]{y|n}]\n" + "\t[--addtag Tag]\n" + "\t[--alloc AllocationPolicy]\n" ++ "\t[--cachemode CacheMode]\n" + "\t[-C|--contiguous {y|n}]\n" + "\t[-d|--debug]\n" + "\t[-h|-?|--help]\n" +@@ -296,9 +311,9 @@ xx(lvcreate, + "\t[PhysicalVolumePath...]\n\n", + + addtag_ARG, alloc_ARG, autobackup_ARG, activate_ARG, available_ARG, +- chunksize_ARG, contiguous_ARG, corelog_ARG, discards_ARG, extents_ARG, +- ignoreactivationskip_ARG, ignoremonitoring_ARG, major_ARG, minor_ARG, +- mirrorlog_ARG, mirrors_ARG, monitor_ARG, minrecoveryrate_ARG, ++ cachemode_ARG, chunksize_ARG, contiguous_ARG, corelog_ARG, discards_ARG, ++ extents_ARG, ignoreactivationskip_ARG, ignoremonitoring_ARG, major_ARG, ++ minor_ARG, mirrorlog_ARG, mirrors_ARG, monitor_ARG, minrecoveryrate_ARG, + maxrecoveryrate_ARG, name_ARG, nosync_ARG, noudevsync_ARG, + permission_ARG, persistent_ARG, poolmetadatasize_ARG, poolmetadataspare_ARG, + raidminrecoveryrate_ARG, raidmaxrecoveryrate_ARG, readahead_ARG, +@@ -789,6 +804,11 @@ xx(segtypes, + PERMITTED_READ_ONLY, + "segtypes\n") + ++xx(tags, ++ "List tags defined on this host", ++ PERMITTED_READ_ONLY, ++ "tags\n") ++ + xx(vgcfgbackup, + "Backup volume group configuration(s)", + PERMITTED_READ_ONLY, +diff --git a/tools/dumpconfig.c b/tools/dumpconfig.c +index 18fba34..cb88bc6 100644 +--- a/tools/dumpconfig.c ++++ b/tools/dumpconfig.c +@@ -193,6 +193,12 @@ int dumpconfig(struct cmd_context *cmd, int argc, char **argv) + goto out; + } + ++ if (arg_count(cmd, withcomments_ARG)) ++ tree_spec.withcomments = 1; ++ ++ if (arg_count(cmd, withversions_ARG)) ++ tree_spec.withversions = 1; ++ + if (cft_check_handle) + tree_spec.check_status = cft_check_handle->status; + +@@ -202,9 +208,7 @@ int dumpconfig(struct cmd_context *cmd, int argc, char **argv) + goto_out; + } + +- if (!config_write(cft, arg_count(cmd, withcomments_ARG), +- arg_count(cmd, withversions_ARG), +- file, argc, argv)) { ++ if (!config_write(cft, &tree_spec, file, argc, argv)) { + stack; + r = ECMD_FAILED; + } +diff --git a/tools/lvchange.c b/tools/lvchange.c +index a0e350f..8ceacc5 100644 +--- a/tools/lvchange.c ++++ b/tools/lvchange.c +@@ -215,11 +215,8 @@ static int _lvchange_activate(struct cmd_context *cmd, struct logical_volume *lv + + activate = (activation_change_t) arg_uint_value(cmd, activate_ARG, CHANGE_AY); + +- if (lv_activation_skip(lv, activate, arg_count(cmd, ignoreactivationskip_ARG), 0)) { +- log_verbose("ACTIVATON_SKIP flag set for LV %s/%s, skipping activation.", +- lv->vg->name, lv->name); ++ if (lv_activation_skip(lv, activate, arg_count(cmd, ignoreactivationskip_ARG))) + return 1; +- } + + if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv))) + lv = origin_from_cow(lv); +diff --git a/tools/lvconvert.c b/tools/lvconvert.c +index 87eb643..09a64c7 100644 +--- a/tools/lvconvert.c ++++ b/tools/lvconvert.c +@@ -28,6 +28,7 @@ struct lvconvert_params { + int zero; + + const char *origin; ++ const char *cachepool; + const char *lv_name; + const char *lv_split_name; + const char *lv_name_full; +@@ -45,6 +46,7 @@ struct lvconvert_params { + uint32_t stripes; + uint32_t stripe_size; + uint32_t read_ahead; ++ uint32_t feature_flags; /* cache_pool */ + + const struct segment_type *segtype; + unsigned target_attr; +@@ -198,7 +200,9 @@ static int _check_conversion_type(struct cmd_context *cmd, const char *type_str) + } + + /* FIXME: Check thin-pool and thin more thoroughly! */ +- if (!strcmp(type_str, "snapshot") || !strncmp(type_str, "raid", 4) || ++ if (!strcmp(type_str, "snapshot") || ++ !strncmp(type_str, "raid", 4) || ++ !strcmp(type_str, "cache-pool") || !strcmp(type_str, "cache") || + !strcmp(type_str, "thin-pool") || !strcmp(type_str, "thin")) + return 1; + +@@ -217,6 +221,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + int argc, char **argv) + { + int i; ++ int cache_pool = 0; + const char *tmp_str; + struct arg_value_group_list *group; + int region_size; +@@ -250,6 +255,20 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + return 0; + } + ++ if (!strcmp(type_str, "cache-pool")) { ++ cache_pool = 1; ++ if ((tmp_str = arg_str_value(cmd, cachemode_ARG, NULL))) { ++ if (!strcmp(tmp_str, "writeback")) ++ lp->feature_flags |= DM_CACHE_FEATURE_WRITEBACK; ++ else if (!strcmp(tmp_str, "writethrough")) ++ lp->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; ++ else { ++ log_error("Unknown cachemode argument"); ++ return 0; ++ } ++ } ++ } ++ + if (!arg_count(cmd, background_ARG)) + lp->wait_completion = 1; + +@@ -270,28 +289,38 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + if (arg_count(cmd, thin_ARG)) + lp->thin = 1; + +- if (arg_count(cmd, thinpool_ARG)) { ++ if (arg_count(cmd, cachepool_ARG)) { ++ if (strcmp(type_str, "cache")) { ++ log_error("--cachepool argument is only valid with " ++ " the \"cache\" segment type"); ++ return 0; ++ } ++ lp->cachepool = arg_str_value(cmd, cachepool_ARG, NULL); ++ } else if (arg_count(cmd, thinpool_ARG) || cache_pool) { + if (arg_count(cmd, merge_ARG)) { +- log_error("--thinpool and --merge are mutually exlusive."); ++ log_error("--%spool and --merge are mutually exlusive.", ++ cache_pool ? "type cache_" : "thin"); + return 0; + } + if (mirror_or_raid_type_requested(cmd, type_str)) { +- log_error("--thinpool and --mirrors/--type mirror/--type raid* are mutually exlusive."); ++ log_error("--%spool and --mirrors/--type mirror/--type raid* are mutually exlusive.", cache_pool ? "type cache_" : "thin"); + return 0; + } + if (arg_count(cmd, repair_ARG)) { +- log_error("--thinpool and --repair are mutually exlusive."); ++ log_error("--%spool and --repair are mutually exlusive.", ++ cache_pool ? "type cache_" : "thin"); + return 0; + } + if (snapshot_type_requested(cmd, type_str)) { +- log_error("--thinpool and --snapshot/--type snapshot are mutually exlusive."); ++ log_error("--%spool and --snapshot/--type snapshot are mutually exlusive.", cache_pool ? "type cache_" : "thin"); + return 0; + } + if (arg_count(cmd, splitmirrors_ARG)) { +- log_error("--thinpool and --splitmirrors are mutually exlusive."); ++ log_error("--%spool and --splitmirrors are mutually exlusive.", cache_pool ? "type cache_" : "thin"); + return 0; + } +- lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN); ++ if (!cache_pool) ++ lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN); + } else if (lp->thin) { + log_error("--thin is only valid with --thinpool."); + return 0; +@@ -442,8 +471,15 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + tmp_str))) + return_0; + } +- } else if (arg_count(cmd, thinpool_ARG)) { +- if (!(lp->pool_data_lv_name = arg_str_value(cmd, thinpool_ARG, NULL))) { ++ } else if (arg_count(cmd, thinpool_ARG) || cache_pool) { ++ if (cache_pool) { ++ if (!argc) { ++ log_error("Please specify the pool data LV."); ++ return 0; ++ } ++ lp->pool_data_lv_name = argv[0]; ++ argv++, argc--; ++ } else if (!(lp->pool_data_lv_name = arg_str_value(cmd, thinpool_ARG, NULL))) { + log_error("Missing pool logical volume name."); + return 0; + } +@@ -469,7 +505,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + lp->read_ahead = arg_uint_value(cmd, readahead_ARG, + cmd->default_settings.read_ahead); + +- /* If --thinpool contains VG name, extract it. */ ++ /* If pool_data_lv_name contains VG name, extract it. */ + if ((tmp_str = strchr(lp->pool_data_lv_name, (int) '/'))) { + if (!(lp->vg_name = extract_vgname(cmd, lp->pool_data_lv_name))) + return 0; +@@ -484,7 +520,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + } + } + +- lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, "thin-pool")); ++ lp->segtype = get_segtype_from_string(cmd, arg_str_value(cmd, type_ARG, cache_pool ? "cache-pool" : "thin-pool")); + if (!lp->segtype) + return_0; + } else { /* Mirrors (and some RAID functions) */ +@@ -861,7 +897,7 @@ int lvconvert_poll(struct cmd_context *cmd, struct logical_volume *lv, + + if (lv_is_merging_origin(lv)) + return poll_daemon(cmd, lv_full_name, uuid, background, 0, +- lv_is_thin_volume(lv) ? ++ seg_is_thin_volume(find_snapshot(lv)) ? + &_lvconvert_thin_merge_fns : &_lvconvert_merge_fns, + "Merged"); + +@@ -2483,6 +2519,9 @@ static int _lvconvert_thinpool_external(struct cmd_context *cmd, + + dm_list_init(&lvc.tags); + ++ if (!pool_supports_external_origin(first_seg(pool_lv), external_lv)) ++ return_0; ++ + if (!(lvc.segtype = get_segtype_from_string(cmd, "thin"))) + return_0; + +@@ -2549,17 +2588,46 @@ revert_new_lv: + return 0; + } + ++static int _lvconvert_update_pool_params(struct logical_volume *pool_lv, ++ struct lvconvert_params *lp) ++{ ++ if (seg_is_cache_pool(lp)) ++ return update_cache_pool_params(pool_lv->vg, lp->target_attr, ++ lp->passed_args, ++ pool_lv->le_count, ++ pool_lv->vg->extent_size, ++ &lp->thin_chunk_size_calc_policy, ++ &lp->chunk_size, ++ &lp->discards, ++ &lp->poolmetadata_size, ++ &lp->zero); ++ ++ return update_thin_pool_params(pool_lv->vg, lp->target_attr, ++ lp->passed_args, ++ pool_lv->le_count, ++ pool_lv->vg->extent_size, ++ &lp->thin_chunk_size_calc_policy, ++ &lp->chunk_size, ++ &lp->discards, ++ &lp->poolmetadata_size, ++ &lp->zero); ++} ++ ++ ++ + /* + * Thin lvconvert version which + * rename metadata + * convert/layers thinpool over data + * attach metadata + */ +-static int _lvconvert_thinpool(struct cmd_context *cmd, +- struct logical_volume *pool_lv, +- struct lvconvert_params *lp) ++static int _lvconvert_to_pool(struct cmd_context *cmd, ++ struct logical_volume *pool_lv, ++ struct lvconvert_params *lp) + { + int r = 0; ++ uint64_t min_metadata_size; ++ uint64_t max_metadata_size; + const char *old_name; + struct lv_segment *seg; + struct logical_volume *data_lv; +@@ -2582,6 +2650,12 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + } + + if (lp->thin) { ++ if (strcmp(pool_lv->name, lp->pool_data_lv_name) == 0) { ++ log_error("Can't use same LV %s/%s for thin pool and thin volume.", ++ pool_lv->vg->name, pool_lv->name); ++ return 0; ++ } ++ + external_lv = pool_lv; + if (!(pool_lv = find_lv(external_lv->vg, lp->pool_data_lv_name))) { + log_error("Can't find pool LV %s/%s.", +@@ -2626,20 +2700,21 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + return 0; + } + +- if ((dm_snprintf(metadata_name, sizeof(metadata_name), "%s_tmeta", +- pool_lv->name) < 0) || +- (dm_snprintf(data_name, sizeof(data_name), "%s_tdata", +- pool_lv->name) < 0)) { ++ if ((dm_snprintf(metadata_name, sizeof(metadata_name), "%s%s", ++ pool_lv->name, ++ (segtype_is_cache_pool(lp->segtype)) ? ++ "_cmeta" : "_tmeta") < 0) || ++ (dm_snprintf(data_name, sizeof(data_name), "%s%s", ++ pool_lv->name, ++ (segtype_is_cache_pool(lp->segtype)) ? ++ "_cdata" : "_tdata") < 0)) { + log_error("Failed to create internal lv names, " +- "thin pool name is too long."); ++ "pool name is too long."); + return 0; + } + + if (!lp->pool_metadata_lv_name) { +- if (!update_pool_params(pool_lv->vg, lp->target_attr, lp->passed_args, +- pool_lv->le_count, pool_lv->vg->extent_size, +- &lp->thin_chunk_size_calc_policy, &lp->chunk_size, +- &lp->discards, &lp->poolmetadata_size, &lp->zero)) ++ if (!_lvconvert_update_pool_params(pool_lv, lp)) + return_0; + + if (!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size)) +@@ -2733,37 +2808,40 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + } + + lp->poolmetadata_size = metadata_lv->size; +- if (lp->poolmetadata_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { ++ max_metadata_size = (segtype_is_cache_pool(lp->segtype)) ? ++ DEFAULT_CACHE_POOL_MAX_METADATA_SIZE * 2 : ++ DEFAULT_THIN_POOL_MAX_METADATA_SIZE * 2; ++ min_metadata_size = (segtype_is_cache_pool(lp->segtype)) ? ++ DEFAULT_CACHE_POOL_MIN_METADATA_SIZE * 2 : ++ DEFAULT_THIN_POOL_MIN_METADATA_SIZE * 2; ++ ++ if (lp->poolmetadata_size > max_metadata_size) { + log_warn("WARNING: Maximum size used by metadata is %s, rest is unused.", +- display_size(cmd, 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)); +- lp->poolmetadata_size = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE; +- } else if (lp->poolmetadata_size < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { ++ display_size(cmd, max_metadata_size)); ++ lp->poolmetadata_size = max_metadata_size; ++ } else if (lp->poolmetadata_size < min_metadata_size) { + log_error("Logical volume %s/%s is too small (<%s) for metadata.", + metadata_lv->vg->name, metadata_lv->name, +- display_size(cmd, 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)); ++ display_size(cmd, min_metadata_size)); + return 0; + } +- if (!update_pool_params(pool_lv->vg, lp->target_attr, lp->passed_args, +- pool_lv->le_count, pool_lv->vg->extent_size, +- &lp->thin_chunk_size_calc_policy, &lp->chunk_size, +- &lp->discards, &lp->poolmetadata_size, &lp->zero)) ++ if (!_lvconvert_update_pool_params(pool_lv, lp)) + return_0; + + metadata_lv->status |= LV_TEMPORARY; + if (!activate_lv_local(cmd, metadata_lv)) { +- log_error("Aborting. Failed to activate thin metadata lv."); ++ log_error("Aborting. Failed to activate metadata lv."); + return 0; + } + +- + if (!wipe_lv(metadata_lv, (struct wipe_params) { .do_zero = 1 })) { +- log_error("Aborting. Failed to wipe thin metadata lv."); ++ log_error("Aborting. Failed to wipe metadata lv."); + return 0; + } + } + + if (!deactivate_lv(cmd, metadata_lv)) { +- log_error("Aborting. Failed to deactivate thin metadata lv. " ++ log_error("Aborting. Failed to deactivate metadata lv. " + "Manual intervention required."); + return 0; + } +@@ -2774,7 +2852,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + + old_name = data_lv->name; /* Use for pool name */ + /* +- * Since we wish to have underlaying devs to match _tdata ++ * Since we wish to have underlaying devs to match _[ct]data + * rename data LV to match pool LV subtree first, + * also checks for visible LV. + */ +@@ -2783,7 +2861,9 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + return_0; + + if (!(pool_lv = lv_create_empty(old_name, NULL, +- THIN_POOL | VISIBLE_LV | LVM_READ | LVM_WRITE, ++ ((segtype_is_cache_pool(lp->segtype)) ? ++ CACHE_POOL : THIN_POOL) | ++ VISIBLE_LV | LVM_READ | LVM_WRITE, + ALLOC_INHERIT, data_lv->vg))) { + log_error("Creation of pool LV failed."); + return 0; +@@ -2791,8 +2871,8 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + + /* Allocate a new linear segment */ + if (!(seg = alloc_lv_segment(lp->segtype, pool_lv, 0, data_lv->le_count, +- pool_lv->status, 0, NULL, NULL, 1, data_lv->le_count, +- 0, 0, 0, NULL))) ++ pool_lv->status, 0, NULL, NULL, 1, ++ data_lv->le_count, 0, 0, 0, NULL))) + return_0; + + /* Add the new segment to the layer LV */ +@@ -2838,8 +2918,10 @@ mda_write: + goto out; + } + +- log_print_unless_silent("Converted %s/%s to thin pool.", +- pool_lv->vg->name, pool_lv->name); ++ log_print_unless_silent("Converted %s/%s to %s pool.", ++ pool_lv->vg->name, pool_lv->name, ++ (segtype_is_cache_pool(lp->segtype)) ? ++ "cache" : "thin"); + + r = 1; + out: +@@ -2852,6 +2934,41 @@ out: + return r; + } + ++static int _lvconvert_cache(struct logical_volume *origin, ++ struct lvconvert_params *lp) ++{ ++ struct cmd_context *cmd = origin->vg->cmd; ++ struct logical_volume *cache_lv; ++ struct logical_volume *cachepool; ++ ++ if (!lp->cachepool) { ++ log_error("--cachepool argument is required."); ++ return 0; ++ } ++ ++ if (!(cachepool = find_lv(origin->vg, lp->cachepool))) { ++ log_error("Unable to find cache pool LV, %s", lp->cachepool); ++ return 0; ++ } ++ ++ if (!(cache_lv = lv_cache_create(cachepool, origin))) ++ return_0; ++ ++ if (!vg_write(cache_lv->vg)) ++ return_0; ++ if (!suspend_lv(cmd, cache_lv)) ++ return_0; ++ if (!vg_commit(cache_lv->vg)) ++ return_0; ++ if (!resume_lv(cmd, cache_lv)) ++ return_0; ++ ++ log_print_unless_silent("%s/%s is now cached.", ++ cache_lv->vg->name, cache_lv->name); ++ ++ return 1; ++} ++ + static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) + { +@@ -2884,7 +3001,9 @@ static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, + !(lv->status & MIRRORED) && !(lv->status & RAID)) { + if (arg_count(cmd, use_policies_ARG)) + return ECMD_PROCESSED; /* nothing to be done here */ +- log_error("Can't repair non-mirrored LV \"%s\".", lv->name); ++ log_error("Can't repair LV \"%s\" of segtype %s.", ++ lv->name, ++ first_seg(lv)->segtype->ops->name(first_seg(lv))); + return ECMD_FAILED; + } + +@@ -2920,11 +3039,25 @@ static int _lvconvert_single(struct cmd_context *cmd, struct logical_volume *lv, + if (!_lvconvert_snapshot(cmd, lv, lp)) + return_ECMD_FAILED; + ++ } else if (segtype_is_cache(lp->segtype)) { ++ if (!archive(lv->vg)) ++ return_ECMD_FAILED; ++ ++ if (!_lvconvert_cache(lv, lp)) ++ return_ECMD_FAILED; ++ ++ } else if (segtype_is_cache_pool(lp->segtype)) { ++ if (!archive(lv->vg)) ++ return_ECMD_FAILED; ++ ++ if (!_lvconvert_to_pool(cmd, lv, lp)) ++ return_ECMD_FAILED; ++ + } else if (arg_count(cmd, thinpool_ARG)) { + if (!archive(lv->vg)) + return_ECMD_FAILED; + +- if (!_lvconvert_thinpool(cmd, lv, lp)) ++ if (!_lvconvert_to_pool(cmd, lv, lp)) + return_ECMD_FAILED; + + } else if (segtype_is_raid(lp->segtype) || +@@ -3001,6 +3134,7 @@ static int _poll_logical_volume(struct cmd_context *cmd, struct logical_volume * + log_print_unless_silent("Conversion starts after activation."); + return ECMD_PROCESSED; + } ++ + return lvconvert_poll(cmd, lv, wait_completion ? 0 : 1U); + } + +@@ -3019,8 +3153,7 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp + if (!lv) + goto_out; + +- if (arg_count(cmd, thinpool_ARG) && +- !get_pool_params(cmd, lv_config_profile(lv), ++ if (!get_pool_params(cmd, lv_config_profile(lv), + &lp->passed_args, &lp->thin_chunk_size_calc_policy, + &lp->chunk_size, &lp->discards, + &lp->poolmetadata_size, &lp->zero)) +diff --git a/tools/lvcreate.c b/tools/lvcreate.c +index d0ca7bc..ad00327 100644 +--- a/tools/lvcreate.c ++++ b/tools/lvcreate.c +@@ -1,6 +1,6 @@ + /* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. +- * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * +@@ -21,7 +21,7 @@ struct lvcreate_cmdline_params { + percent_type_t percent; + uint64_t size; + char **pvs; +- int pv_count; ++ uint32_t pv_count; + }; + + static int _set_vg_name(struct lvcreate_params *lp, const char *vg_name) +@@ -73,8 +73,45 @@ static int _lvcreate_name_params(struct lvcreate_params *lp, + lp->lv_name = ptr + 1; + } + +- /* Need an origin? */ +- if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) { ++ if (seg_is_cache(lp)) { ++ /* ++ * We are looking for the origin or cache_pool LV. ++ * Could be in the form 'lv' or 'vg/lv' ++ * ++ * We store the lv name in 'lp->origin' for now, but ++ * it must be accessed later (when we can look-up the ++ * LV in the VG) whether it is truly the origin that ++ * was specified, or whether it is the cache_pool. ++ */ ++ if (!argc) { ++ log_error("Please specify a logical volume to act as " ++ "the origin or cache_pool."); ++ return 0; ++ } ++ ++ lp->origin = skip_dev_dir(cmd, argv[0], NULL); ++ if (strrchr(lp->origin, '/')) { ++ if (!_set_vg_name(lp, extract_vgname(cmd, lp->origin))) ++ return_0; ++ ++ /* Strip the volume group from the origin */ ++ if ((ptr = strrchr(lp->origin, (int) '/'))) ++ lp->origin = ptr + 1; ++ } ++ ++ if (!lp->vg_name && ++ !_set_vg_name(lp, extract_vgname(cmd, NULL))) ++ return_0; ++ ++ if (!lp->vg_name) { ++ log_error("The origin or cache_pool name should include" ++ " the volume group."); ++ return 0; ++ } ++ ++ lp->cache = 1; ++ (*pargv)++, (*pargc)--; ++ } else if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) { + /* argv[0] might be origin or vg/origin */ + if (!argc) { + log_error("Please specify a logical volume to act as " +@@ -227,6 +264,68 @@ static int _determine_snapshot_type(struct volume_group *vg, + return 1; + } + ++static int _lvcreate_update_pool_params(struct volume_group *vg, ++ struct lvcreate_params *lp) ++{ ++ if (seg_is_cache_pool(lp)) ++ return update_cache_pool_params(vg, lp->target_attr, ++ lp->passed_args, ++ lp->extents, vg->extent_size, ++ &lp->thin_chunk_size_calc_policy, ++ &lp->chunk_size, &lp->discards, ++ &lp->poolmetadatasize, ++ &lp->zero); ++ ++ return update_thin_pool_params(vg, lp->target_attr, lp->passed_args, ++ lp->extents, vg->extent_size, ++ &lp->thin_chunk_size_calc_policy, ++ &lp->chunk_size, &lp->discards, ++ &lp->poolmetadatasize, &lp->zero); ++} ++ ++/* ++ * _determine_cache_argument ++ * @vg ++ * @lp ++ * ++ * 'lp->origin' is set with an LV that could be either the origin ++ * or the cache_pool of the cached LV which is being created. This ++ * function determines which it is and sets 'lp->origin' or ++ * 'lp->pool' appropriately. ++ */ ++static int _determine_cache_argument(struct volume_group *vg, ++ struct lvcreate_params *lp) ++{ ++ struct lv_list *lvl; ++ ++ if (!seg_is_cache(lp)) { ++ log_error(INTERNAL_ERROR ++ "Unable to determine cache argument on %s segtype", ++ lp->segtype->name); ++ return 0; ++ } ++ ++ if (!(lvl = find_lv_in_vg(vg, lp->origin))) { ++ log_error("LV %s not found in Volume group %s.", ++ lp->origin, vg->name); ++ return 0; ++ } ++ ++ if (lv_is_cache_pool(lvl->lv)) { ++ lp->pool = lp->origin; ++ lp->origin = NULL; ++ } else { ++ lp->pool = NULL; ++ lp->create_pool = 1; ++ lp->poolmetadataspare = arg_int_value(vg->cmd, ++ poolmetadataspare_ARG, ++ DEFAULT_POOL_METADATA_SPARE); ++ lp->origin = lp->origin; ++ } ++ ++ return 1; ++} ++ + /* + * Update extents parameters based on other parameters which affect the size + * calculation. +@@ -264,19 +363,19 @@ static int _update_extents_params(struct volume_group *vg, + } else + lp->pvh = &vg->pvs; + +- switch(lcp->percent) { ++ switch (lcp->percent) { + case PERCENT_VG: +- lp->extents = percent_of_extents(lp->extents, vg->extent_count, 0); ++ extents = percent_of_extents(lp->extents, vg->extent_count, 0); + break; + case PERCENT_FREE: +- lp->extents = percent_of_extents(lp->extents, vg->free_count, 0); ++ extents = percent_of_extents(lp->extents, vg->free_count, 0); + break; + case PERCENT_PVS: + if (lcp->pv_count) { + pv_extent_count = pv_list_extents_free(lp->pvh); +- lp->extents = percent_of_extents(lp->extents, pv_extent_count, 0); ++ extents = percent_of_extents(lp->extents, pv_extent_count, 0); + } else +- lp->extents = percent_of_extents(lp->extents, vg->extent_count, 0); ++ extents = percent_of_extents(lp->extents, vg->extent_count, 0); + break; + case PERCENT_LV: + log_error("Please express size as %%VG, %%PVS, or " +@@ -293,10 +392,21 @@ static int _update_extents_params(struct volume_group *vg, + log_error(INTERNAL_ERROR "Couldn't find origin volume."); + return 0; + } +- lp->extents = percent_of_extents(lp->extents, origin->le_count, 0); ++ extents = percent_of_extents(lp->extents, origin->le_count, 0); + break; + case PERCENT_NONE: ++ extents = lp->extents; + break; ++ default: ++ log_error(INTERNAL_ERROR "Unsupported percent type %u.", lcp->percent); ++ return 0; ++ } ++ ++ if (lcp->percent) { ++ /* FIXME Don't do the adjustment for parallel allocation with PERCENT_ORIGIN! */ ++ lp->approx_alloc = 1; ++ log_verbose("Converted %" PRIu32 "%%%s into %" PRIu32 " extents.", lp->extents, get_percent_string(lcp->percent), extents); ++ lp->extents = extents; + } + + if (lp->snapshot && lp->origin && lp->extents) { +@@ -333,12 +443,8 @@ static int _update_extents_params(struct volume_group *vg, + lp->extents = lp->extents - size_rest; + } + +- if (lp->create_thin_pool) { +- if (!update_pool_params(vg, lp->target_attr, lp->passed_args, +- lp->extents, vg->extent_size, +- &lp->thin_chunk_size_calc_policy, +- &lp->chunk_size, &lp->discards, +- &lp->poolmetadatasize, &lp->zero)) ++ if (lp->create_pool) { ++ if (!_lvcreate_update_pool_params(vg, lp)) + return_0; + + if (!(lp->poolmetadataextents = +@@ -392,9 +498,9 @@ static int _read_size_params(struct lvcreate_params *lp, + + /* If size/extents given with thin, then we are creating a thin pool */ + if (seg_is_thin(lp) && (arg_count(cmd, size_ARG) || arg_count(cmd, extents_ARG))) +- lp->create_thin_pool = 1; ++ lp->create_pool = 1; + +- if (!lp->create_thin_pool && arg_count(cmd, poolmetadatasize_ARG)) { ++ if (!lp->create_pool && arg_count(cmd, poolmetadatasize_ARG)) { + log_error("--poolmetadatasize may only be specified when allocating the thin pool."); + return 0; + } +@@ -522,20 +628,7 @@ static int _read_raid_params(struct lvcreate_params *lp, + return 0; + } + +- /* +- * get_stripe_params is called before _read_raid_params +- * and already sets: +- * lp->stripes +- * lp->stripe_size +- * +- * For RAID 4/5/6/10, these values must be set. +- */ +- if (!segtype_is_mirrored(lp->segtype) && +- (lp->stripes <= lp->segtype->parity_devs)) { +- log_error("Number of stripes must be at least %d for %s", +- lp->segtype->parity_devs + 1, lp->segtype->name); +- return 0; +- } else if (!strcmp(lp->segtype->name, "raid10") && (lp->stripes < 2)) { ++ if (!strcmp(lp->segtype->name, "raid10") && (lp->stripes < 2)) { + if (arg_count(cmd, stripes_ARG)) { + /* User supplied the bad argument */ + log_error("Segment type 'raid10' requires 2 or more stripes."); +@@ -594,13 +687,45 @@ static int _read_raid_params(struct lvcreate_params *lp, + + if (lp->max_recovery_rate && + (lp->max_recovery_rate < lp->min_recovery_rate)) { +- log_error("Minumum recovery rate cannot be higher than maximum."); ++ log_error("Minimum recovery rate cannot be higher than maximum."); + return 0; + } + return 1; + } + +-static int _read_activation_params(struct lvcreate_params *lp, struct cmd_context *cmd, ++static int _read_cache_pool_params(struct lvcreate_params *lp, ++ struct cmd_context *cmd) ++{ ++ const char *str_arg; ++ ++ if (!segtype_is_cache_pool(lp->segtype)) ++ return 1; ++ ++ if (arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS) { ++ log_error("Negative chunk size is invalid."); ++ return 0; ++ } ++ ++ lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, ++ DEFAULT_CACHE_POOL_CHUNK_SIZE * 2); ++ ++ str_arg = arg_str_value(cmd, cachemode_ARG, NULL); ++ if (str_arg) { ++ if (!strcmp(str_arg, "writeback")) ++ lp->feature_flags |= DM_CACHE_FEATURE_WRITEBACK; ++ else if (!strcmp(str_arg, "writethrough")) ++ lp->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH; ++ else { ++ log_error("Unknown cachemode argument"); ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++static int _read_activation_params(struct lvcreate_params *lp, ++ struct cmd_context *cmd, + struct volume_group *vg) + { + unsigned pagesize; +@@ -608,7 +733,7 @@ static int _read_activation_params(struct lvcreate_params *lp, struct cmd_contex + lp->activate = (activation_change_t) + arg_uint_value(cmd, activate_ARG, CHANGE_AY); + +- if (lp->activate == CHANGE_AN || lp->activate == CHANGE_ALN) { ++ if (!is_change_activating(lp->activate)) { + if (lp->zero && !seg_is_thin(lp)) { + log_error("--activate n requires --zero n"); + return 0; +@@ -644,8 +769,12 @@ static int _read_activation_params(struct lvcreate_params *lp, struct cmd_contex + lp->permission = arg_uint_value(cmd, permission_ARG, + LVM_READ | LVM_WRITE); + +- /* Must not zero/wipe read only volume */ +- if (!(lp->permission & LVM_WRITE)) { ++ if (lp->snapshot) { ++ /* Snapshot has to zero COW header */ ++ lp->zero = 1; ++ lp->wipe_signatures = 0; ++ } else if (!(lp->permission & LVM_WRITE)) { ++ /* Must not zero/wipe read only volume */ + lp->zero = 0; + lp->wipe_signatures = 0; + } +@@ -665,7 +794,7 @@ static int _read_activation_params(struct lvcreate_params *lp, struct cmd_contex + + /* Persistent minor */ + if (arg_count(cmd, persistent_ARG)) { +- if (lp->create_thin_pool && !lp->thin) { ++ if (lp->create_pool && !lp->thin) { + log_error("--persistent is not permitted when creating a thin pool device."); + return 0; + } +@@ -774,12 +903,15 @@ static int _lvcreate_params(struct lvcreate_params *lp, + (!seg_is_thin(lp) && arg_count(cmd, virtualsize_ARG))) + lp->snapshot = 1; + ++ if (seg_is_cache_pool(lp)) ++ lp->create_pool = 1; ++ + if (seg_is_thin_pool(lp)) { + if (lp->snapshot) { + log_error("Snapshots are incompatible with thin_pool segment_type."); + return 0; + } +- lp->create_thin_pool = 1; ++ lp->create_pool = 1; + } + + if (seg_is_thin_volume(lp)) +@@ -853,19 +985,16 @@ static int _lvcreate_params(struct lvcreate_params *lp, + } + } + +- if (activation() && lp->segtype->ops->target_present && +- !lp->segtype->ops->target_present(cmd, NULL, &lp->target_attr)) { +- log_error("%s: Required device-mapper target(s) not " +- "detected in your kernel", lp->segtype->name); +- return 0; +- } else if (!strcmp(lp->segtype->name, "raid10")) { +- uint32_t maj, min, patchlevel; +- if (!target_version("raid", &maj, &min, &patchlevel)) { +- log_error("Failed to determine version of RAID kernel module"); ++ if (activation() && lp->segtype->ops->target_present) { ++ if (!lp->segtype->ops->target_present(cmd, NULL, &lp->target_attr)) { ++ log_error("%s: Required device-mapper target(s) not detected in your kernel.", ++ lp->segtype->name); + return 0; + } +- if ((maj != 1) || (min < 3)) { +- log_error("RAID module does not support RAID10"); ++ ++ if ((strcmp(lp->segtype->name, "raid10") == 0) && ++ !(lp->target_attr & RAID_FEATURE_RAID10)) { ++ log_error("RAID module does not support RAID10."); + return 0; + } + } +@@ -873,13 +1002,13 @@ static int _lvcreate_params(struct lvcreate_params *lp, + /* + * Should we zero/wipe signatures on the lv. + */ +- lp->zero = strcmp(arg_str_value(cmd, zero_ARG, +- (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n"); ++ lp->zero = (!(lp->segtype->flags & SEG_CANNOT_BE_ZEROED) && ++ (strcmp(arg_str_value(cmd, zero_ARG, "y"), "y") == 0)) ? 1 : 0; + + if (arg_count(cmd, wipesignatures_ARG)) { + /* If -W/--wipesignatures is given on command line directly, respect it. */ +- lp->wipe_signatures = strcmp(arg_str_value(cmd, wipesignatures_ARG, +- (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n"); ++ lp->wipe_signatures =(!(lp->segtype->flags & SEG_CANNOT_BE_ZEROED) && ++ (strcmp(arg_str_value(cmd, wipesignatures_ARG, "y"), "y") == 0)) ? 1 : 0; + } else { + /* + * If -W/--wipesignatures is not given on command line, +@@ -895,13 +1024,14 @@ static int _lvcreate_params(struct lvcreate_params *lp, + if (!_lvcreate_name_params(lp, cmd, &argc, &argv) || + !_read_size_params(lp, lcp, cmd) || + !get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) || +- (lp->create_thin_pool && ++ (lp->create_pool && + !get_pool_params(cmd, NULL, &lp->passed_args, + &lp->thin_chunk_size_calc_policy, + &lp->chunk_size, &lp->discards, + &lp->poolmetadatasize, &lp->zero)) || + !_read_mirror_params(lp, cmd) || +- !_read_raid_params(lp, cmd)) ++ !_read_raid_params(lp, cmd) || ++ !_read_cache_pool_params(lp, cmd)) + return_0; + + if (lp->snapshot && (lp->extents || lcp->size)) { +@@ -920,12 +1050,12 @@ static int _lvcreate_params(struct lvcreate_params *lp, + + if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot"))) + return_0; +- } else if (!lp->create_thin_pool && arg_count(cmd, chunksize_ARG)) { ++ } else if (!lp->create_pool && arg_count(cmd, chunksize_ARG)) { + log_error("--chunksize is only available with snapshots and thin pools."); + return 0; + } + +- if (lp->create_thin_pool) { ++ if (lp->create_pool) { + /* TODO: add lvm.conf default y|n */ + lp->poolmetadataspare = arg_int_value(cmd, poolmetadataspare_ARG, + DEFAULT_POOL_METADATA_SPARE); +@@ -974,7 +1104,7 @@ static int _check_thin_parameters(struct volume_group *vg, struct lvcreate_param + struct lv_list *lvl; + unsigned i; + +- if (!lp->thin && !lp->create_thin_pool && !lp->snapshot) { ++ if (!lp->thin && !lp->create_pool && !lp->snapshot) { + log_error("Please specify device size(s)."); + return 0; + } +@@ -984,7 +1114,7 @@ static int _check_thin_parameters(struct volume_group *vg, struct lvcreate_param + return 0; + } + +- if (!lp->create_thin_pool) { ++ if (!lp->create_pool) { + static const int _argname[] = { + alloc_ARG, + chunksize_ARG, +@@ -1051,6 +1181,45 @@ static int _check_thin_parameters(struct volume_group *vg, struct lvcreate_param + return 1; + } + ++static int _check_raid_parameters(struct volume_group *vg, ++ struct lvcreate_params *lp, ++ struct lvcreate_cmdline_params *lcp) ++{ ++ unsigned devs = lcp->pv_count ? : dm_list_size(&vg->pvs); ++ struct cmd_context *cmd = vg->cmd; ++ ++ /* ++ * If number of devices was not supplied, we can infer from ++ * the PVs given. ++ */ ++ if (!seg_is_mirrored(lp)) { ++ if (!arg_count(cmd, stripes_ARG) && ++ (devs > 2 * lp->segtype->parity_devs)) ++ lp->stripes = devs - lp->segtype->parity_devs; ++ ++ if (!lp->stripe_size) ++ lp->stripe_size = find_config_tree_int(cmd, metadata_stripesize_CFG, NULL) * 2; ++ ++ if (lp->stripes <= lp->segtype->parity_devs) { ++ log_error("Number of stripes must be at least %d for %s", ++ lp->segtype->parity_devs + 1, ++ lp->segtype->name); ++ return 0; ++ } ++ } else if (!strcmp(lp->segtype->name, "raid10")) { ++ if (!arg_count(cmd, stripes_ARG)) ++ lp->stripes = devs / lp->mirrors; ++ if (lp->stripes < 2) { ++ log_error("Unable to create RAID10 LV," ++ " insufficient number of devices."); ++ return 0; ++ } ++ } ++ /* 'mirrors' defaults to 2 - not the number of PVs supplied */ ++ ++ return 1; ++} ++ + /* + * Ensure the set of thin parameters extracted from the command line is consistent. + */ +@@ -1060,14 +1229,14 @@ static int _validate_internal_thin_processing(const struct lvcreate_params *lp) + + /* + The final state should be one of: +- thin create_thin_pool snapshot origin pool +- 1 1 0 0 y/n - create new pool and a thin LV in it +- 1 0 0 0 y - create new thin LV in existing pool +- 0 1 0 0 y/n - create new pool only +- 1 0 1 1 y - create thin snapshot of existing thin LV ++ thin create_pool snapshot origin pool ++ 1 1 0 0 y/n - create new pool and a thin LV in it ++ 1 0 0 0 y - create new thin LV in existing pool ++ 0 1 0 0 y/n - create new pool only ++ 1 0 1 1 y - create thin snapshot of existing thin LV + */ + +- if (!lp->create_thin_pool && !lp->pool) { ++ if (!lp->create_pool && !lp->pool) { + log_error(INTERNAL_ERROR "--thinpool not identified."); + r = 0; + } +@@ -1077,7 +1246,7 @@ static int _validate_internal_thin_processing(const struct lvcreate_params *lp) + r = 0; + } + +- if (!lp->thin && !lp->create_thin_pool && !lp->snapshot) { ++ if (!lp->thin && !lp->create_pool && !lp->snapshot) { + log_error(INTERNAL_ERROR "Failed to identify what type of thin target to use."); + r = 0; + } +@@ -1113,6 +1282,12 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv) + if (seg_is_thin(&lp) && !_check_thin_parameters(vg, &lp, &lcp)) + goto_out; + ++ if (seg_is_cache(&lp) && !_determine_cache_argument(vg, &lp)) ++ goto_out; ++ ++ if (seg_is_raid(&lp) && !_check_raid_parameters(vg, &lp, &lcp)) ++ goto_out; ++ + /* + * Check activation parameters to support inactive thin snapshot creation + * FIXME: anything else needs to be moved past _determine_snapshot_type()? +@@ -1126,7 +1301,7 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv) + if (seg_is_thin(&lp) && !_validate_internal_thin_processing(&lp)) + goto_out; + +- if (lp.create_thin_pool) { ++ if (lp.create_pool) { + if (!handle_pool_metadata_spare(vg, lp.poolmetadataextents, + lp.pvh, lp.poolmetadataspare)) + goto_out; +diff --git a/tools/pvmove.c b/tools/pvmove.c +index 19e1482..a7788ba 100644 +--- a/tools/pvmove.c ++++ b/tools/pvmove.c +@@ -233,6 +233,32 @@ static int sub_lv_of(struct logical_volume *lv, const char *lv_name) + return sub_lv_of(seg->lv, lv_name); + } + ++/* ++ * parent_lv_is_cache_type ++ * ++ * FIXME: This function can be removed when 'pvmove' is supported for ++ * cache types. ++ * ++ * If this LV is below a cache LV (at any depth), return 1. ++ */ ++static int parent_lv_is_cache_type(struct logical_volume *lv) ++{ ++ struct lv_segment *seg; ++ ++ /* Sub-LVs only ever have one segment using them */ ++ if (dm_list_size(&lv->segs_using_this_lv) != 1) ++ return 0; ++ ++ if (!(seg = get_only_segment_using_this_lv(lv))) ++ return_0; ++ ++ if (lv_is_cache_type(seg->lv)) ++ return 1; ++ ++ /* Continue up the tree */ ++ return parent_lv_is_cache_type(seg->lv); ++} ++ + /* Create new LV with mirror segments for the required copies */ + static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd, + struct volume_group *vg, +@@ -342,6 +368,23 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd, + if (!lv_is_on_pvs(lv, source_pvl)) + continue; + ++ if (lv_is_cache_type(lv)) { ++ log_print_unless_silent("Skipping %s LV, %s", ++ lv_is_cache(lv) ? "cache" : ++ lv_is_cache_pool(lv) ? ++ "cache-pool" : "cache-related", ++ lv->name); ++ lv_skipped = 1; ++ continue; ++ } ++ ++ if (parent_lv_is_cache_type(lv)) { ++ log_print_unless_silent("Skipping %s because a parent" ++ " is of cache type", lv->name); ++ lv_skipped = 1; ++ continue; ++ } ++ + /* + * If the VG is clustered, we are unable to handle + * snapshots, origins, thin types, RAID or mirror +diff --git a/tools/tags.c b/tools/tags.c +new file mode 100644 +index 0000000..bbb446a +--- /dev/null ++++ b/tools/tags.c +@@ -0,0 +1,23 @@ ++/* ++ * Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "tools.h" ++ ++int tags(struct cmd_context *cmd, int argc __attribute__((unused)), ++ char **argv __attribute__((unused))) ++{ ++ display_tags(cmd); ++ ++ return ECMD_PROCESSED; ++} +diff --git a/tools/toollib.c b/tools/toollib.c +index 61c14f1..91b0559 100644 +--- a/tools/toollib.c ++++ b/tools/toollib.c +@@ -184,7 +184,7 @@ int ignore_vg(struct volume_group *vg, const char *vg_name, int allow_inconsiste + int process_each_lv_in_vg(struct cmd_context *cmd, + struct volume_group *vg, + const struct dm_list *arg_lvnames, +- const struct dm_list *tags, ++ const struct dm_list *tagsl, + struct dm_list *failed_lvnames, + void *handle, + process_single_lv_fn_t process_single_lv) +@@ -201,7 +201,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd, + if (!vg_check_status(vg, EXPORTED_VG)) + return ECMD_FAILED; + +- if (tags && !dm_list_empty(tags)) ++ if (tagsl && !dm_list_empty(tagsl)) + tags_supplied = 1; + + if (arg_lvnames && !dm_list_empty(arg_lvnames)) +@@ -212,7 +212,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd, + process_all = 1; + /* Or if VG tags match */ + else if (tags_supplied && +- str_list_match_list(tags, &vg->tags, NULL)) ++ str_list_match_list(tagsl, &vg->tags, NULL)) + process_all = 1; + + /* +@@ -247,7 +247,7 @@ int process_each_lv_in_vg(struct cmd_context *cmd, + /* LV tag match? skip test, when process_all */ + else if (!process_all && + (!tags_supplied || +- !str_list_match_list(tags, &lvl->lv->tags, NULL))) ++ !str_list_match_list(tagsl, &lvl->lv->tags, NULL))) + continue; + + if (sigint_caught()) +@@ -296,7 +296,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, + struct cmd_vg *cvl_vg; + struct dm_list cmd_vgs; + struct dm_list failed_lvnames; +- struct dm_list tags, lvnames; ++ struct dm_list tagsl, lvnames; + struct dm_list arg_lvnames; /* Cmdline vgname or vgname/lvname */ + struct dm_list arg_vgnames; + char *vglv; +@@ -304,7 +304,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, + + const char *vgname; + +- dm_list_init(&tags); ++ dm_list_init(&tagsl); + dm_list_init(&arg_lvnames); + dm_list_init(&failed_lvnames); + +@@ -327,7 +327,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, + vgname); + continue; + } +- if (!str_list_add(cmd->mem, &tags, ++ if (!str_list_add(cmd->mem, &tagsl, + dm_pool_strdup(cmd->mem, + vgname + 1))) { + log_error("strlist allocation failed"); +@@ -395,7 +395,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, + vgnames = &arg_vgnames; + } + +- if (!argc || !dm_list_empty(&tags)) { ++ if (!argc || !dm_list_empty(&tagsl)) { + log_verbose("Finding all logical volumes"); + if (!lvmetad_vg_list_to_lvmcache(cmd)) + stack; +@@ -420,7 +420,7 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, + continue; + } + +- tags_arg = &tags; ++ tags_arg = &tagsl; + dm_list_init(&lvnames); /* LVs to be processed in this VG */ + dm_list_iterate_items(sll, &arg_lvnames) { + const char *vg_name = sll->str; +@@ -562,7 +562,7 @@ int process_each_segment_in_lv(struct cmd_context *cmd, + + static int _process_one_vg(struct cmd_context *cmd, const char *vg_name, + const char *vgid, +- struct dm_list *tags, struct dm_list *arg_vgnames, ++ struct dm_list *tagsl, struct dm_list *arg_vgnames, + uint32_t flags, void *handle, int ret_max, + process_single_vg_fn_t process_single_vg) + { +@@ -591,10 +591,10 @@ static int _process_one_vg(struct cmd_context *cmd, const char *vg_name, + } + } + +- if (!dm_list_empty(tags) && ++ if (!dm_list_empty(tagsl) && + /* Only process if a tag matches or it's on arg_vgnames */ + !str_list_match_item(arg_vgnames, vg_name) && +- !str_list_match_list(tags, &cvl_vg->vg->tags, NULL)) ++ !str_list_match_list(tagsl, &cvl_vg->vg->tags, NULL)) + break; + + ret = process_single_vg(cmd, vg_name, cvl_vg->vg, handle); +@@ -622,11 +622,11 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, + + struct str_list *sl; + struct dm_list *vgnames, *vgids; +- struct dm_list arg_vgnames, tags; ++ struct dm_list arg_vgnames, tagsl; + + const char *vg_name, *vgid; + +- dm_list_init(&tags); ++ dm_list_init(&tagsl); + dm_list_init(&arg_vgnames); + + if (argc) { +@@ -642,7 +642,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, + ret_max = EINVALID_CMD_LINE; + continue; + } +- if (!str_list_add(cmd->mem, &tags, ++ if (!str_list_add(cmd->mem, &tagsl, + dm_pool_strdup(cmd->mem, + vg_name + 1))) { + log_error("strlist allocation failed"); +@@ -669,7 +669,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, + vgnames = &arg_vgnames; + } + +- if (!argc || !dm_list_empty(&tags)) { ++ if (!argc || !dm_list_empty(&tagsl)) { + log_verbose("Finding all volume groups"); + if (!lvmetad_vg_list_to_lvmcache(cmd)) + stack; +@@ -681,9 +681,9 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, + if (sigint_caught()) + return_ECMD_FAILED; + vgid = sl->str; +- if (!(vgid) || !(vg_name = lvmcache_vgname_from_vgid(cmd->mem, vgid))) ++ if (!vgid || !(vg_name = lvmcache_vgname_from_vgid(cmd->mem, vgid))) + continue; +- ret_max = _process_one_vg(cmd, vg_name, vgid, &tags, ++ ret_max = _process_one_vg(cmd, vg_name, vgid, &tagsl, + &arg_vgnames, + flags, handle, + ret_max, process_single_vg); +@@ -695,7 +695,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, + vg_name = sl->str; + if (is_orphan_vg(vg_name)) + continue; /* FIXME Unnecessary? */ +- ret_max = _process_one_vg(cmd, vg_name, NULL, &tags, ++ ret_max = _process_one_vg(cmd, vg_name, NULL, &tagsl, + &arg_vgnames, + flags, handle, + ret_max, process_single_vg); +@@ -706,7 +706,7 @@ int process_each_vg(struct cmd_context *cmd, int argc, char **argv, + } + + int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, +- const struct dm_list *tags, void *handle, ++ const struct dm_list *tagsl, void *handle, + process_single_pv_fn_t process_single_pv) + { + int ret_max = ECMD_PROCESSED; +@@ -716,8 +716,8 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, + dm_list_iterate_items(pvl, &vg->pvs) { + if (sigint_caught()) + return_ECMD_FAILED; +- if (tags && !dm_list_empty(tags) && +- !str_list_match_list(tags, &pvl->pv->tags, NULL)) { ++ if (tagsl && !dm_list_empty(tagsl) && ++ !str_list_match_list(tagsl, &pvl->pv->tags, NULL)) { + continue; + } + if ((ret = process_single_pv(cmd, vg, pvl->pv, handle)) > ret_max) +@@ -801,12 +801,12 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, + struct pv_list *pvl; + struct physical_volume *pv; + struct dm_list *pvslist = NULL, *vgnames; +- struct dm_list tags; ++ struct dm_list tagsl; + struct str_list *sll; + char *at_sign, *tagname; + struct device *dev; + +- dm_list_init(&tags); ++ dm_list_init(&tagsl); + + if (lock_global && !lock_vol(cmd, VG_GLOBAL, LCK_VG_READ, NULL)) { + log_error("Unable to obtain global lock."); +@@ -831,7 +831,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, + ret_max = EINVALID_CMD_LINE; + continue; + } +- if (!str_list_add(cmd->mem, &tags, ++ if (!str_list_add(cmd->mem, &tagsl, + dm_pool_strdup(cmd->mem, + tagname))) { + log_error("strlist allocation failed"); +@@ -881,7 +881,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, + if (ret > ret_max) + ret_max = ret; + } +- if (!dm_list_empty(&tags) && (vgnames = get_vgnames(cmd, 1)) && ++ if (!dm_list_empty(&tagsl) && (vgnames = get_vgnames(cmd, 1)) && + !dm_list_empty(vgnames)) { + dm_list_iterate_items(sll, vgnames) { + if (sigint_caught()) { +@@ -895,7 +895,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, + continue; + } + +- ret = process_each_pv_in_vg(cmd, vg, &tags, ++ ret = process_each_pv_in_vg(cmd, vg, &tagsl, + handle, + process_single_pv); + if (ret > ret_max) +@@ -1215,7 +1215,7 @@ struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int + { + struct dm_list *r; + struct pv_list *pvl; +- struct dm_list tags, arg_pvnames; ++ struct dm_list tagsl, arg_pvnames; + char *pvname = NULL; + char *colon, *at_sign, *tagname; + int i; +@@ -1227,7 +1227,7 @@ struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int + } + dm_list_init(r); + +- dm_list_init(&tags); ++ dm_list_init(&tagsl); + dm_list_init(&arg_pvnames); + + for (i = 0; i < argc; i++) { +@@ -1404,7 +1404,7 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv, + * deactivation of origin, which is the only visible LV + */ + if (!deactivate_lv(cmd, find_snapshot(lv)->lv)) { +- if ((activate != CHANGE_AN) && (activate != CHANGE_ALN)) { ++ if (is_change_activating(activate)) { + log_error("Refusing to activate merging \"%s\" while snapshot \"%s\" is still active.", + lv->name, find_snapshot(lv)->lv->name); + return 0; +@@ -1420,8 +1420,7 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv, + return_0; + + if (background_polling() && +- (activate != CHANGE_AN) && +- (activate != CHANGE_ALN) && ++ is_change_activating(activate) && + (lv->status & (PVMOVE|CONVERTING|MERGING))) + lv_spawn_background_polling(cmd, lv); + +@@ -1661,14 +1660,28 @@ int get_pool_params(struct cmd_context *cmd, + uint64_t *pool_metadata_size, + int *zero) + { ++ int cache_pool = 0; ++ ++ if (!strcmp("cache-pool", arg_str_value(cmd, type_ARG, "none"))) ++ cache_pool = 1; ++ ++ if (!cache_pool && !arg_count(cmd, thinpool_ARG)) { ++ /* Check for arguments that should only go with pools */ ++ if (arg_count(cmd, poolmetadata_ARG)) { ++ log_error("'--poolmetadata' argument is only valid when" ++ " converting to pool LVs."); ++ return_0; ++ } ++ } ++ + *passed_args = 0; +- if (arg_count(cmd, zero_ARG)) { ++ if (!cache_pool && arg_count(cmd, zero_ARG)) { + *passed_args |= PASS_ARG_ZERO; + *zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n"); + log_very_verbose("Setting pool zeroing: %u", *zero); + } + +- if (arg_count(cmd, discards_ARG)) { ++ if (!cache_pool && arg_count(cmd, discards_ARG)) { + *passed_args |= PASS_ARG_DISCARDS; + *discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, 0); + log_very_verbose("Setting pool discards: %s", +@@ -1681,15 +1694,20 @@ int get_pool_params(struct cmd_context *cmd, + return 0; + } + *passed_args |= PASS_ARG_CHUNK_SIZE; +- *chunk_size = arg_uint_value(cmd, chunksize_ARG, ++ *chunk_size = arg_uint_value(cmd, chunksize_ARG, cache_pool ? ++ DM_CACHE_MIN_DATA_BLOCK_SIZE : + DM_THIN_MIN_DATA_BLOCK_SIZE); + log_very_verbose("Setting pool chunk size: %s", + display_size(cmd, *chunk_size)); + } + +- if (!update_profilable_pool_params(cmd, profile, *passed_args, +- chunk_size_calc_method, chunk_size, +- discards, zero)) ++ if (cache_pool) { ++ //FIXME: add cache_pool support to update_profilable_pool_params ++ if (!(*passed_args & PASS_ARG_CHUNK_SIZE)) ++ *chunk_size = DEFAULT_CACHE_POOL_CHUNK_SIZE * 2; ++ } else if (!update_profilable_pool_params(cmd, profile, *passed_args, ++ chunk_size_calc_method, ++ chunk_size, discards, zero)) + return_0; + + if (arg_count(cmd, poolmetadatasize_ARG)) { +@@ -1699,7 +1717,8 @@ int get_pool_params(struct cmd_context *cmd, + } + *passed_args |= PASS_ARG_POOL_METADATA_SIZE; + } +- *pool_metadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0)); ++ *pool_metadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, ++ UINT64_C(0)); + + return 1; + } +diff --git a/tools/toollib.h b/tools/toollib.h +index d809123..54636ab 100644 +--- a/tools/toollib.h ++++ b/tools/toollib.h +@@ -80,14 +80,14 @@ int process_each_segment_in_lv(struct cmd_context *cmd, + process_single_seg_fn_t process_single_seg); + + int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, +- const struct dm_list *tags, void *handle, ++ const struct dm_list *tagsl, void *handle, + process_single_pv_fn_t process_single_pv); + + + int process_each_lv_in_vg(struct cmd_context *cmd, + struct volume_group *vg, + const struct dm_list *arg_lvnames, +- const struct dm_list *tags, ++ const struct dm_list *tagsl, + struct dm_list *failed_lvnames, + void *handle, + process_single_lv_fn_t process_single_lv); +diff --git a/tools/vgchange.c b/tools/vgchange.c +index af4b002..e7b5e59 100644 +--- a/tools/vgchange.c ++++ b/tools/vgchange.c +@@ -117,15 +117,11 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, + + /* Can't deactivate a pvmove LV */ + /* FIXME There needs to be a controlled way of doing this */ +- if (((activate == CHANGE_AN) || (activate == CHANGE_ALN)) && +- ((lv->status & PVMOVE) )) ++ if ((lv->status & PVMOVE) && !is_change_activating(activate)) + continue; + +- if (lv_activation_skip(lv, activate, arg_count(cmd, ignoreactivationskip_ARG), 0)) { +- log_verbose("ACTIVATION_SKIP flag set for LV %s/%s, skipping activation.", +- lv->vg->name, lv->name); ++ if (lv_activation_skip(lv, activate, arg_count(cmd, ignoreactivationskip_ARG))) + continue; +- } + + if ((activate == CHANGE_AAY) && + !lv_passes_auto_activation_filter(cmd, lv)) +@@ -155,8 +151,8 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, struct volume_group *vg, + + if (expected_count) + log_verbose("%s %d logical volumes in volume group %s", +- (activate == CHANGE_AN || activate == CHANGE_ALN)? +- "Deactivated" : "Activated", count, vg->name); ++ is_change_activating(activate) ? ++ "Activated" : "Deactivated", count, vg->name); + + return (expected_count != count) ? 0 : 1; + } +@@ -196,12 +192,10 @@ static int _vgchange_background_polling(struct cmd_context *cmd, struct volume_g + int vgchange_activate(struct cmd_context *cmd, struct volume_group *vg, + activation_change_t activate) + { +- int lv_open, active, monitored = 0, r = 1, do_activate = 1; ++ int lv_open, active, monitored = 0, r = 1; + const struct lv_list *lvl; + struct lvinfo info; +- +- if ((activate == CHANGE_AN) || (activate == CHANGE_ALN)) +- do_activate = 0; ++ int do_activate = is_change_activating(activate); + + /* + * Safe, since we never write out new metadata here. Required for +@@ -592,8 +586,7 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) + + if (arg_count(cmd, activate_ARG) && + (arg_count(cmd, monitor_ARG) || arg_count(cmd, poll_ARG))) { +- int activate = arg_uint_value(cmd, activate_ARG, 0); +- if (activate == CHANGE_AN || activate == CHANGE_ALN) { ++ if (!is_change_activating(arg_uint_value(cmd, activate_ARG, 0))) { + log_error("Only -ay* allowed with --monitor or --poll."); + return EINVALID_CMD_LINE; + } +@@ -604,8 +597,8 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv) + return EINVALID_CMD_LINE; + } + +- if (arg_count(cmd, activate_ARG) == 1 +- && arg_count(cmd, autobackup_ARG)) { ++ if ((arg_count(cmd, activate_ARG) == 1) && ++ arg_count(cmd, autobackup_ARG)) { + log_error("-A option not necessary with -a option"); + return EINVALID_CMD_LINE; + } +diff --git a/tools/vgsplit.c b/tools/vgsplit.c +index dfcb120..150be1d 100644 +--- a/tools/vgsplit.c ++++ b/tools/vgsplit.c +@@ -14,6 +14,7 @@ + */ + + #include "tools.h" ++#include "metadata.h" /* for 'get_only_segment_using_this_lv' */ + + /* FIXME Why not (lv->vg == vg) ? */ + static int _lv_is_in_vg(struct volume_group *vg, struct logical_volume *lv) +@@ -72,6 +73,10 @@ static int _move_lvs(struct volume_group *vg_from, struct volume_group *vg_to) + lv_is_thin_volume(lv)) + continue; + ++ if (lv_is_cache(lv) || lv_is_cache_pool(lv)) ++ /* further checks by _move_cache() */ ++ continue; ++ + /* Ensure all the PVs used by this LV remain in the same */ + /* VG as each other */ + vg_with = NULL; +@@ -268,6 +273,77 @@ static int _move_thins(struct volume_group *vg_from, + return 1; + } + ++static int _move_cache(struct volume_group *vg_from, ++ struct volume_group *vg_to) ++{ ++ int is_moving; ++ struct dm_list *lvh, *lvht; ++ struct logical_volume *lv, *data, *meta, *orig; ++ struct lv_segment *seg, *cache_seg; ++ ++ dm_list_iterate_safe(lvh, lvht, &vg_from->lvs) { ++ lv = dm_list_item(lvh, struct lv_list)->lv; ++ data = meta = orig = NULL; ++ seg = first_seg(lv); ++ ++ if (!lv_is_cache(lv) && !lv_is_cache_pool(lv)) ++ continue; ++ ++ /* ++ * FIXME: The code seems to move cache LVs fine, but it ++ * hasn't been well tested and it causes problems ++ * when just splitting PVs that don't contain ++ * cache LVs. ++ * Waiting for next release before fixing and enabling. ++ */ ++ log_error("Unable to split VG while it contains cache LVs"); ++ return 0; ++ ++ if (lv_is_cache(lv)) { ++ orig = seg_lv(seg, 0); ++ data = seg_lv(first_seg(seg->pool_lv), 0); ++ meta = first_seg(seg->pool_lv)->metadata_lv; ++ /* Ensure all components are coming along */ ++ is_moving = !!_lv_is_in_vg(vg_to, orig); ++ } else { ++ if (!dm_list_empty(&seg->lv->segs_using_this_lv) && ++ !(cache_seg = get_only_segment_using_this_lv(seg->lv))) ++ return_0; ++ orig = seg_lv(cache_seg, 0); ++ data = seg_lv(seg, 0); ++ meta = seg->metadata_lv; ++ ++ if (_lv_is_in_vg(vg_to, data) || ++ _lv_is_in_vg(vg_to, meta)) ++ is_moving = 1; ++ } ++ ++ if (orig && (!!_lv_is_in_vg(vg_to, orig) != is_moving)) { ++ log_error("Can't split %s and its origin (%s)" ++ " into separate VGs", lv->name, orig->name); ++ return 0; ++ } ++ ++ if (data && (!!_lv_is_in_vg(vg_to, data) != is_moving)) { ++ log_error("Can't split %s and its cache pool" ++ " data LV (%s) into separate VGs", ++ lv->name, data->name); ++ return 0; ++ } ++ ++ if (meta && (!!_lv_is_in_vg(vg_to, meta) != is_moving)) { ++ log_error("Can't split %s and its cache pool" ++ " metadata LV (%s) into separate VGs", ++ lv->name, meta->name); ++ return 0; ++ } ++ if (!_move_one_lv(vg_from, vg_to, lvh)) ++ return_0; ++ } ++ ++ return 1; ++} ++ + /* + * Create or open the destination of the vgsplit operation. + * Returns +@@ -481,6 +557,9 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv) + if (!(_move_thins(vg_from, vg_to))) + goto_bad; + ++ if (!(_move_cache(vg_from, vg_to))) ++ goto_bad; ++ + /* Split metadata areas and check if both vgs have at least one area */ + if (!(vg_split_mdas(cmd, vg_from, vg_to)) && vg_from->pv_count) { + log_error("Cannot split: Nowhere to store metadata for new Volume Group"); +diff --git a/udev/10-dm.rules.in b/udev/10-dm.rules.in +index f7088f1..8d7a8ca 100644 +--- a/udev/10-dm.rules.in ++++ b/udev/10-dm.rules.in +@@ -120,12 +120,6 @@ ENV{DM_UDEV_RULES_VSN}="2" + + ENV{DM_UDEV_DISABLE_DM_RULES_FLAG}!="1", ENV{DM_NAME}=="?*", SYMLINK+="(DM_DIR)/$env{DM_NAME}" + +-# We have to ignore further rule application for inappropriate events +-# and devices. But still send the notification if cookie exists. +-ENV{DM_UUID}=="mpath-?*", ENV{DM_ACTION}=="PATH_FAILED", GOTO="dm_disable" +-ENV{DM_UUID}=="CRYPT-TEMP-?*", GOTO="dm_disable" +-ENV{DM_UUID}!="?*", ENV{DM_NAME}=="temporary-cryptsetup-?*", GOTO="dm_disable" +- + # Avoid processing and scanning a DM device in the other (foreign) + # rules if it is in suspended state. However, we still keep 'disk' + # and 'DM subsystem' related rules enabled in this case. +diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in +index e8304b5..bd75fc8 100644 +--- a/udev/69-dm-lvm-metad.rules.in ++++ b/udev/69-dm-lvm-metad.rules.in +@@ -34,6 +34,9 @@ ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="lvm_end" + # Inform lvmetad about any PV that is gone. + ACTION=="remove", GOTO="lvm_scan" + ++# Create /dev/disk/by-id/lvm-pv-uuid- symlink for each PV ++ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-id/lvm-pv-uuid-$env{ID_FS_UUID_ENC}" ++ + # If the PV is a special device listed below, scan only if the device is + # properly activated. These devices are not usable after an ADD event, + # but they require an extra setup and they are ready after a CHANGE event. diff --git a/SOURCES/lvm2-2_02_106-various-man-page-cleanups.patch b/SOURCES/lvm2-2_02_106-various-man-page-cleanups.patch new file mode 100644 index 0000000..7ef5269 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-various-man-page-cleanups.patch @@ -0,0 +1,188 @@ + man/lvchange.8.in | 6 +++--- + man/lvconvert.8.in | 36 +++++++++++++++++++++++------------- + man/lvcreate.8.in | 8 ++++---- + man/lvmetad.8.in | 11 ++++------- + 4 files changed, 34 insertions(+), 27 deletions(-) + +diff --git a/man/lvchange.8.in b/man/lvchange.8.in +index 64231c8..5de75e0 100644 +--- a/man/lvchange.8.in ++++ b/man/lvchange.8.in +@@ -41,7 +41,7 @@ lvchange \- change attributes of a logical volume + .RB [ \-\-[raid]writebehind + .IR IOCount ] + .RB [ \-\-[raid]writemostly +-.IR PhysicalVolume[:{t|n|y}] ] ++.IR PhysicalVolume [ : { t | n | y }]] + .RB [ \-\-sysinit ] + .RB [ \-\-noudevsync ] + .RB [ \-M | \-\-persistent +@@ -166,7 +166,7 @@ If \fIrepair\fP is used, the discrepancies will be corrected as they are + encountered. The 'lvs' command can be used to show the number of + discrepancies found or repaired. + .TP +-.BR \-\-[raid]writebehind " IOCount" ++.BR \-\-[raid]writebehind " " \fIIOCount + Specify the maximum number of outstanding writes that are allowed to + devices in a RAID1 logical volume that are marked as \fIwrite-mostly\fP. + Once this value is exceeded, writes become synchronous (i.e. all writes +@@ -174,7 +174,7 @@ to the constituent devices must complete before the array signals the + write has completed). Setting the value to zero clears the preference + and allows the system to choose the value arbitrarily. + .TP +-.BR \-\-[raid]writemostly " PhysicalVolume[:{t|y|n}]" ++.IR \fB\-\-[raid]writemostly " " PhysicalVolume [ : { t | y | n }] + Mark a device in a RAID1 logical volume as \fIwrite-mostly\fP. All reads + to these drives will be avoided unless absolutely necessary. This keeps + the number of I/Os to the drive to a minimum. The default behavior is to +diff --git a/man/lvconvert.8.in b/man/lvconvert.8.in +index 190e221..fe6525b 100644 +--- a/man/lvconvert.8.in ++++ b/man/lvconvert.8.in +@@ -91,11 +91,11 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot + .IR ChunkSize [ bBsSkKmMgG ]] + .RB [ \-\-discards + .RI { ignore | nopassdown | passdown }] +-.RB [[ \-\-poolmetadata +-.IR ThinPoolMetadataLogicalVolume { Name | Path }] ++.RB [{ \-\-poolmetadata ++.IR ThinPoolMetadataLogicalVolume { Name | Path } + | +-.RB [ \-\-poolmetadatasize +-.IR ThinPoolMetadataSize [ bBsSkKmMgG ]] ++.B \-\-poolmetadatasize ++.IR ThinPoolMetadataSize [ bBsSkKmMgG ]}] + .RB [ \-r | \-\-readahead + .RI { ReadAheadSectors | auto | none }] + .RB [ \-\-stripes +@@ -113,24 +113,24 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot + .RB [ \-v | \-\-verbose ] + .RB [ \-\-version ] + .sp +-.B lvconvert \-\-type cache-pool ++.B lvconvert \-\-type \fIcache-pool + .RB [ \-c | \-\-chunksize + .IR ChunkSize [ bBsSkKmMgG ]] + .RB [ \-\-cachemode + .RI { writeback | writethrough }] +-.RB [[ \-\-poolmetadata +-.IR CachePoolMetadataLogicalVolume { Name | Path }] ++.RB [{ \-\-poolmetadata ++.IR CachePoolMetadataLogicalVolume { Name | Path } + | +-.RB [ \-\-poolmetadatasize +-.IR CachePoolMetadataSize [ bBsSkKmMgG ]] ++.B \-\-poolmetadatasize ++.IR CachePoolMetadataSize [ bBsSkKmMgG ]}] + .IR LogicalVolume [ Path ] + .RI [ PhysicalVolume [ Path ][ :PE [ -PE ]]...] + .RB [ \-h | \-? | \-\-help ] + .RB [ \-v | \-\-verbose ] + .RB [ \-\-version ] + .sp +-.B lvconvert \-\-type cache +-.RB \-\-cachepool ++.B lvconvert \-\-type \fIcache ++.B \-\-cachepool + .IR CachePoolLV { Name | Path } + .IR LogicalVolume [ Path ] + .RB [ \-h | \-? | \-\-help ] +@@ -333,14 +333,23 @@ a suitable value automatically. + "None" is equivalent to specifying zero. + .TP + .B \-\-repair +-Repair a mirror after suffering a disk failure. The mirror will be brought back +-into a consistent state. By default, the original number of mirrors will be ++Repair a mirror after suffering a disk failure or try to fix thin pool metadata. ++ ++The mirror will be brought back into a consistent state. ++By default, the original number of mirrors will be + restored if possible. Specify \fB\-y\fP on the command line to skip + the prompts. Use \fB\-f\fP if you do not want any replacement. + Additionally, you may use \fB\-\-use\-policies\fP to use the device + replacement policy specified in \fBlvm.conf\fP(5), + viz. activation/mirror_log_fault_policy or + activation/mirror_device_fault_policy. ++ ++Thin pool repair automates the use of \fBthin_repair\fP(8) tool. ++Only inactive thin pool volumes can be repaired. ++There is no validation of metadata between kernel and lvm2. ++This requires further manual work. ++After successfull repair the old unmodified metadata are still ++available in \fB_tmeta\fP LV. + .TP + .B \-\-replace \fIPhysicalVolume + Remove the specified device (\fIPhysicalVolume\fP) and replace it with one +@@ -498,4 +507,5 @@ cache pool LV. + .BR lvscan (8), + .BR vgcreate (8), + .BR thin_dump (8), ++.BR thin_repair (8) + .BR thin_restore (8) +diff --git a/man/lvcreate.8.in b/man/lvcreate.8.in +index 325baad..445af80 100644 +--- a/man/lvcreate.8.in ++++ b/man/lvcreate.8.in +@@ -60,7 +60,7 @@ lvcreate \- create a logical volume in an existing volume group + .RB [ \-t | \-\-test ] + .RB [ \-T | \-\-thin + .RB [ \-\-cachemode +-.IR { writeback | writethrough } ++.RI { writeback | writethrough } + .RB [ \-c | \-\-chunksize + .IR ChunkSize [ bBsSkKmMgG ]] + .RB [ \-\-discards +@@ -145,7 +145,7 @@ be overridden. If the clustered locking is enabled, + .IR \fB\-a { a | l } y + will activate only on the local node. + .TP +-.IR \fB\-k ", " \fB\-\-setactivationskip " " { y | n } ++.IR \fB\-k ", " \fB\-\-setactivationskip " {" y | n } + Controls whether Logical Volumes are persistently flagged to be skipped during + activation. By default, thin snapshot volumes are flagged for activation skip. + To activate such volumes, an extra \fB\-K/\-\-ignoreactivationskip\fP option must +@@ -155,11 +155,11 @@ detach the flag for existing volumes. To see whether the flag is attached, + use \fBlvs\fP command where the state of the flag is reported within + \fBlv_attr\fP bits. + .TP +-.IR \fB\-K ", " \fB\-\-ignoreactivationskip ++.BR \-K ", " \-\-ignoreactivationskip + Ignore the flag to skip Logical Volumes during activation. + + .TP +-.BR \-\-cachemode " " { writeback | writethrough } ++.IR \fB\-\-cachemode " " { writeback | writethrough } + Specifying a cache mode determines when the writes to a cache LV + are considered complete. When \fIwriteback\fP is specified, a write is + considered complete as soon as it is stored in the cache pool LV. +diff --git a/man/lvmetad.8.in b/man/lvmetad.8.in +index 4956a3f..444c7c0 100644 +--- a/man/lvmetad.8.in ++++ b/man/lvmetad.8.in +@@ -4,14 +4,11 @@ lvmetad \- LVM metadata cache daemon + .SH SYNOPSIS + .B lvmetad + .RB [ \-l +-.RI {all|wire|debug} +-.RB ] ++.RI { all | wire | debug }] + .RB [ \-p +-.RI pidfile_path +-.RB ] ++.IR pidfile_path ] + .RB [ \-s +-.RI socket_path +-.RB ] ++.IR socket_path ] + .RB [ \-f ] + .RB [ \-h ] + .RB [ \-V ] +@@ -34,7 +31,7 @@ Don't fork, but run in the foreground. + .BR \-h ", " \-? + Show help information. + .TP +-.BR \-l " {" \fIall | \fIwire | \fIdebug } ++.IR \fB\-l " {" all | wire | debug } + Select the type of log messages to generate. + Messages are logged by syslog. + Additionally, when -f is given they are also sent to standard error. diff --git a/SOURCES/lvm2-2_02_106-various-thin-fixes.patch b/SOURCES/lvm2-2_02_106-various-thin-fixes.patch new file mode 100644 index 0000000..c4e7ef5 --- /dev/null +++ b/SOURCES/lvm2-2_02_106-various-thin-fixes.patch @@ -0,0 +1,633 @@ +commit 85fc38d398d1bc8acb4027da7acc376af2be0cc1 +Author: Peter Rajnoha +Date: Wed Mar 12 14:37:59 2014 +0100 + + thin fixes +--- + WHATS_NEW | 3 ++ + lib/activate/activate.c | 2 +- + lib/activate/dev_manager.c | 72 +++++++++++++++++++++++++-------------------- + lib/cache_segtype/cache.c | 6 ++-- + lib/metadata/lv_manip.c | 63 +++------------------------------------ + lib/metadata/pool_manip.c | 3 ++ + lib/metadata/thin_manip.c | 2 +- + lib/mirror/mirrored.c | 4 +-- + lib/misc/lvm-string.c | 8 ++++- + lib/misc/lvm-string.h | 3 +- + lib/thin/thin.c | 8 ++--- + test/shell/lvcreate-thin.sh | 4 ++- + test/shell/pvremove-thin.sh | 28 ++++++++++++++++++ + 13 files changed, 101 insertions(+), 105 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 3ee9585..a3d48f3 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,8 @@ + Version 2.02.106 - + ==================================== ++ Update API for internal function build_dm_uuid(). ++ Do not try to check empty pool with scheduled messages. ++ Fix return value in pool_has_message() when quering for any message. + Fix cache consistency in lvmetad when PV moves around. + Fix memleak when lvmetad discovers PV to appear on another device. + Fix invalid memory read in lvmetad that could cause a deadlock. +diff --git a/lib/activate/activate.c b/lib/activate/activate.c +index 26dc0e1..565634f 100644 +--- a/lib/activate/activate.c ++++ b/lib/activate/activate.c +@@ -1546,7 +1546,7 @@ static char *_build_target_uuid(struct cmd_context *cmd, struct logical_volume * + else + layer = NULL; + +- return build_dm_uuid(cmd->mem, lv->lvid.s, layer); ++ return build_dm_uuid(cmd->mem, lv, layer); + } + + int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso, +diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c +index 6b5f8c2..c723a61 100644 +--- a/lib/activate/dev_manager.c ++++ b/lib/activate/dev_manager.c +@@ -446,6 +446,12 @@ static int _device_is_usable(struct device *dev, int check_lv_names) + !dm_split_lvm_name(NULL, NULL, &vgname, &lvname, &layer)) + goto_out; + ++ if (strlen(uuid) > 68) { ++ log_debug_activation("%s: Reserved uuid %s on internal LV device %s/%s%s%s not usable.", ++ dev_name(dev), uuid, vgname, lvname, *layer ? "-" : "", layer); ++ goto out; ++ } ++ + if (lvname && (is_reserved_lvname(lvname) || *layer)) { + log_debug_activation("%s: Reserved internal LV device %s/%s%s%s not usable.", + dev_name(dev), vgname, lvname, *layer ? "-" : "", layer); +@@ -505,7 +511,7 @@ int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv, + return 0; + } + +- if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer))) { ++ if (!(dlid = build_dm_uuid(mem, lv, layer))) { + log_error("dlid build failed for %s", name); + r = 0; + goto out; +@@ -528,7 +534,7 @@ static const struct dm_info *_cached_info(struct dm_pool *mem, + const struct dm_tree_node *dnode; + const struct dm_info *dinfo = NULL; + +- if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer))) { ++ if (!(dlid = build_dm_uuid(mem, lv, layer))) { + log_error("Failed to build dlid for %s.", lv->name); + return NULL; + } +@@ -641,7 +647,7 @@ int lv_has_target_type(struct dm_pool *mem, struct logical_volume *lv, + char *type = NULL; + char *params = NULL; + +- if (!(dlid = build_dm_uuid(mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(mem, lv, layer))) + return_0; + + if (!(dmt = _setup_task(NULL, dlid, 0, +@@ -872,7 +878,7 @@ int dev_manager_transient(struct dev_manager *dm, struct logical_volume *lv) + const struct dm_list *segh = &lv->segments; + struct lv_segment *seg = NULL; + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + if (!(dmt = _setup_task(0, dlid, NULL, DM_DEVICE_STATUS, 0, 0))) +@@ -1015,7 +1021,7 @@ int dev_manager_snapshot_percent(struct dev_manager *dm, + if (!(name = dm_build_dm_name(dm->mem, snap_lv->vg->name, snap_lv->name, NULL))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, snap_lv->lvid.s, NULL))) ++ if (!(dlid = build_dm_uuid(dm->mem, snap_lv, NULL))) + return_0; + + /* +@@ -1047,7 +1053,7 @@ int dev_manager_mirror_percent(struct dev_manager *dm, + if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) { ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) { + log_error("dlid build failed for %s", lv->name); + return 0; + } +@@ -1074,7 +1080,7 @@ int dev_manager_raid_status(struct dev_manager *dm, + char *params = NULL; + const char *layer = lv_layer(lv); + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + log_debug_activation("Getting raid device status for %s.", lv->name); +@@ -1138,7 +1144,7 @@ int dev_manager_raid_message(struct dev_manager *dm, + return 0; + } + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TARGET_MSG, 0, 0))) +@@ -1173,7 +1179,7 @@ int dev_manager_cache_status(struct dev_manager *dm, + char *params = NULL; + const char *layer = lv_layer(lv); + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + log_debug_activation("Getting cache device status for %s.", lv->name); +@@ -1282,7 +1288,7 @@ int dev_manager_thin_pool_status(struct dev_manager *dm, + int r = 0; + + /* Build dlid for the thin pool layer */ +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, lv_layer(lv)))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv)))) + return_0; + + log_debug_activation("Getting thin pool device status for %s.", lv->name); +@@ -1328,7 +1334,7 @@ int dev_manager_thin_pool_percent(struct dev_manager *dm, + lv_layer(lv)))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, lv_layer(lv)))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv)))) + return_0; + + log_debug_activation("Getting device status percentage for %s", name); +@@ -1351,7 +1357,7 @@ int dev_manager_thin_percent(struct dev_manager *dm, + if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + log_debug_activation("Getting device status percentage for %s", name); +@@ -1374,7 +1380,7 @@ int dev_manager_thin_device_id(struct dev_manager *dm, + int r = 0; + + /* Build dlid for the thin layer */ +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, lv_layer(lv)))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv)))) + return_0; + + log_debug_activation("Getting device id for %s.", dlid); +@@ -1575,7 +1581,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + log_debug_activation("Getting device info for %s [%s]", name, dlid); +@@ -1648,7 +1654,7 @@ static int _add_partial_replicator_to_dtree(struct dev_manager *dm, + if (!_add_dev_to_dtree(dm, dtree, rlv, NULL)) + return_0; + +- if (!(uuid = build_dm_uuid(dm->mem, rlv->lvid.s, NULL))) ++ if (!(uuid = build_dm_uuid(dm->mem, rlv, NULL))) + return_0; + + rep_node = dm_tree_find_node_by_uuid(dtree, uuid); +@@ -1670,7 +1676,7 @@ static int _add_partial_replicator_to_dtree(struct dev_manager *dm, + /* If replicator exists - try connect existing heads */ + if (rep_node) { + uuid = build_dm_uuid(dm->mem, +- rdev->replicator_dev->lv->lvid.s, ++ rdev->replicator_dev->lv, + NULL); + if (!uuid) + return_0; +@@ -1790,7 +1796,9 @@ static int _thin_pool_register_callback(struct dev_manager *dm, + struct thin_cb_data *data; + + /* Skip metadata testing for unused pool. */ +- if (!first_seg(lv)->transaction_id) ++ if (!first_seg(lv)->transaction_id || ++ ((first_seg(lv)->transaction_id == 1) && ++ pool_has_message(first_seg(lv), NULL, 0))) + return 1; + + if (!(data = dm_pool_alloc(dm->mem, sizeof(*data)))) { +@@ -1836,7 +1844,7 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + #if 0 + /* ? Use origin_only to avoid 'deep' thin pool suspend ? */ + /* FIXME Implement dm_tree_node_skip_childrens optimisation */ +- if (!(uuid = build_dm_uuid(dm->mem, lv->lvid.s, lv_layer(lv)))) ++ if (!(uuid = build_dm_uuid(dm->mem, lv, lv_layer(lv)))) + return_0; + if ((thin_node = dm_tree_find_node_by_uuid(dtree, uuid))) + dm_tree_node_skip_childrens(thin_node, 1); +@@ -1866,7 +1874,7 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + /* Setup callback for non-activation partial tree */ + /* Activation gets own callback when needed */ + /* TODO: extend _cached_info() to return dnode */ +- if (!(uuid = build_dm_uuid(dm->mem, lv->lvid.s, lv_layer(lv)))) ++ if (!(uuid = build_dm_uuid(dm->mem, lv, lv_layer(lv)))) + return_0; + if ((thin_node = dm_tree_find_node_by_uuid(dtree, uuid)) && + !_thin_pool_register_callback(dm, thin_node, lv)) +@@ -1977,7 +1985,7 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree, + + sprintf(errid, "missing_%d_%d", segno, s); + +- if (!(dlid = build_dm_uuid(dm->mem, seg->lv->lvid.s, errid))) ++ if (!(dlid = build_dm_uuid(dm->mem, seg->lv, errid))) + return_NULL; + + if (!(name = dm_build_dm_name(dm->mem, seg->lv->vg->name, +@@ -2085,18 +2093,18 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg, + return_0; + continue; + } +- if (!(dlid = build_dm_uuid(dm->mem, seg_metalv(seg, s)->lvid.s, NULL))) ++ if (!(dlid = build_dm_uuid(dm->mem, seg_metalv(seg, s), NULL))) + return_0; + if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_metale(seg, s))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, seg_lv(seg, s)->lvid.s, NULL))) ++ if (!(dlid = build_dm_uuid(dm->mem, seg_lv(seg, s), NULL))) + return_0; + if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_le(seg, s))) + return_0; + } else if (seg_type(seg, s) == AREA_LV) { + +- if (!(dlid = build_dm_uuid(dm->mem, seg_lv(seg, s)->lvid.s, NULL))) ++ if (!(dlid = build_dm_uuid(dm->mem, seg_lv(seg, s), NULL))) + return_0; + if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_le(seg, s))) + return_0; +@@ -2125,7 +2133,7 @@ static int _add_layer_target_to_dtree(struct dev_manager *dm, + { + const char *layer_dlid; + +- if (!(layer_dlid = build_dm_uuid(dm->mem, lv->lvid.s, lv_layer(lv)))) ++ if (!(layer_dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv)))) + return_0; + + /* Add linear mapping over layered LV */ +@@ -2144,7 +2152,7 @@ static int _add_origin_target_to_dtree(struct dev_manager *dm, + { + const char *real_dlid; + +- if (!(real_dlid = build_dm_uuid(dm->mem, lv->lvid.s, "real"))) ++ if (!(real_dlid = build_dm_uuid(dm->mem, lv, "real"))) + return_0; + + if (!dm_tree_node_add_snapshot_origin_target(dnode, lv->size, real_dlid)) +@@ -2165,13 +2173,13 @@ static int _add_snapshot_merge_target_to_dtree(struct dev_manager *dm, + return 0; + } + +- if (!(origin_dlid = build_dm_uuid(dm->mem, lv->lvid.s, "real"))) ++ if (!(origin_dlid = build_dm_uuid(dm->mem, lv, "real"))) + return_0; + +- if (!(cow_dlid = build_dm_uuid(dm->mem, merging_snap_seg->cow->lvid.s, "cow"))) ++ if (!(cow_dlid = build_dm_uuid(dm->mem, merging_snap_seg->cow, "cow"))) + return_0; + +- if (!(merge_dlid = build_dm_uuid(dm->mem, merging_snap_seg->cow->lvid.s, NULL))) ++ if (!(merge_dlid = build_dm_uuid(dm->mem, merging_snap_seg->cow, NULL))) + return_0; + + if (!dm_tree_node_add_snapshot_merge_target(dnode, lv->size, origin_dlid, +@@ -2197,10 +2205,10 @@ static int _add_snapshot_target_to_dtree(struct dev_manager *dm, + return 0; + } + +- if (!(origin_dlid = build_dm_uuid(dm->mem, snap_seg->origin->lvid.s, "real"))) ++ if (!(origin_dlid = build_dm_uuid(dm->mem, snap_seg->origin, "real"))) + return_0; + +- if (!(cow_dlid = build_dm_uuid(dm->mem, snap_seg->cow->lvid.s, "cow"))) ++ if (!(cow_dlid = build_dm_uuid(dm->mem, snap_seg->cow, "cow"))) + return_0; + + size = (uint64_t) snap_seg->len * snap_seg->origin->vg->extent_size; +@@ -2514,7 +2522,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, + if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer))) + return_0; + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, layer))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, layer))) + return_0; + + /* We've already processed this node if it already has a context ptr */ +@@ -2774,7 +2782,7 @@ static int _tree_action(struct dev_manager *dm, struct logical_volume *lv, + /* Restore fs cookie */ + dm_tree_set_cookie(root, fs_get_cookie()); + +- if (!(dlid = build_dm_uuid(dm->mem, lv->lvid.s, laopts->origin_only ? lv_layer(lv) : NULL))) ++ if (!(dlid = build_dm_uuid(dm->mem, lv, laopts->origin_only ? lv_layer(lv) : NULL))) + goto_out; + + /* Only process nodes with uuid of "LVM-" plus VG id. */ +diff --git a/lib/cache_segtype/cache.c b/lib/cache_segtype/cache.c +index 57c7a5c..df521c4 100644 +--- a/lib/cache_segtype/cache.c ++++ b/lib/cache_segtype/cache.c +@@ -360,13 +360,13 @@ static int _cache_add_target_line(struct dev_manager *dm, + struct lv_segment *cache_pool_seg = first_seg(seg->pool_lv); + char *metadata_uuid, *data_uuid, *origin_uuid; + +- if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv->lvid.s, NULL))) ++ if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv, NULL))) + return_0; + +- if (!(data_uuid = build_dm_uuid(mem, seg_lv(cache_pool_seg, 0)->lvid.s, NULL))) ++ if (!(data_uuid = build_dm_uuid(mem, seg_lv(cache_pool_seg, 0), NULL))) + return_0; + +- if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0)->lvid.s, NULL))) ++ if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0), NULL))) + return_0; + + if (!dm_tree_node_add_cache_target(node, len, +diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c +index 57ce2d9..abbcbf8 100644 +--- a/lib/metadata/lv_manip.c ++++ b/lib/metadata/lv_manip.c +@@ -6291,19 +6291,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + if (lv_activation_skip(lv, lp->activate, lp->activation_skip & ACTIVATION_SKIP_IGNORE)) + lp->activate = CHANGE_AN; + +- /* +- * For thin pools - deactivate when inactive pool is requested or +- * for cluster give-up local lock and take proper exlusive lock +- */ +- if (lv_is_thin_pool(lv) && +- (!is_change_activating(lp->activate) || +- vg_is_clustered(lv->vg)) && +- /* Deactivates cleared metadata LV */ +- !deactivate_lv(lv->vg->cmd, lv)) { +- stack; +- goto deactivate_failed; +- } +- + /* store vg on disk(s) */ + if (!vg_write(vg) || !vg_commit(vg)) + return_NULL; +@@ -6322,50 +6309,7 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + if (lp->temporary) + lv->status |= LV_TEMPORARY; + +- if (lv_is_cache_type(lv)) { +- if (!lv_is_active(lv)) { +- if (!activate_lv_excl(cmd, lv)) { +- log_error("Failed to activate pool %s.", +- lv->name); +- goto deactivate_and_revert_new_lv; +- } +- } else { +- if (!suspend_lv(cmd, lv)) { +- log_error("Failed to suspend pool %s.", +- lv->name); +- goto deactivate_and_revert_new_lv; +- } +- if (!resume_lv(cmd, lv)) { +- log_error("Failed to resume pool %s.", lv->name); +- goto deactivate_and_revert_new_lv; +- } +- } +- } else if (lv_is_thin_pool(lv)) { +- if (is_change_activating(lp->activate)) { +- if (vg_is_clustered(lv->vg)) { +- if (!activate_lv_excl(cmd, lv)) { +- log_error("Failed to activate pool %s.", lv->name); +- goto deactivate_and_revert_new_lv; +- } +- } else { +- /* +- * Suspend cleared plain metadata LV +- * but now already commited as pool LV +- * and resume it as a pool LV. +- * +- * This trick avoids collision with udev watch rule. +- */ +- if (!suspend_lv(cmd, lv)) { +- log_error("Failed to suspend pool %s.", lv->name); +- goto deactivate_and_revert_new_lv; +- } +- if (!resume_lv(cmd, lv)) { +- log_error("Failed to resume pool %s.", lv->name); +- goto deactivate_and_revert_new_lv; +- } +- } +- } +- } else if (lv_is_thin_volume(lv)) { ++ if (lv_is_thin_volume(lv)) { + /* For snapshot, suspend active thin origin first */ + if (org && lv_is_active(org) && lv_is_thin_volume(org)) { + if (!suspend_lv_origin(cmd, org)) { +@@ -6403,7 +6347,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, + } + } else if (!lv_active_change(cmd, lv, lp->activate)) { + log_error("Failed to activate new LV."); +- if (lp->zero || lp->wipe_signatures) ++ if (lp->zero || lp->wipe_signatures || ++ lv_is_thin_pool(lv) || ++ lv_is_cache_type(lv)) + goto deactivate_and_revert_new_lv; + return NULL; + } +@@ -6497,7 +6443,6 @@ out: + + deactivate_and_revert_new_lv: + if (!deactivate_lv(cmd, lv)) { +-deactivate_failed: + log_error("Unable to deactivate failed new LV \"%s/%s\". " + "Manual intervention required.", lv->vg->name, lv->name); + return NULL; +diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c +index 0c7bf96..f414f3f 100644 +--- a/lib/metadata/pool_manip.c ++++ b/lib/metadata/pool_manip.c +@@ -267,6 +267,9 @@ int create_pool(struct logical_volume *pool_lv, + goto bad; + } + pool_lv->status &= ~LV_TEMPORARY; ++ /* Deactivates cleared metadata LV */ ++ if (!deactivate_lv_local(pool_lv->vg->cmd, pool_lv)) ++ goto_bad; + } + + if (dm_snprintf(name, sizeof(name), "%s_%s", pool_lv->name, +diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c +index b28f5a0..b94a4a8 100644 +--- a/lib/metadata/thin_manip.c ++++ b/lib/metadata/thin_manip.c +@@ -160,7 +160,7 @@ int pool_has_message(const struct lv_segment *seg, + } + + if (!lv && !device_id) +- return dm_list_empty(&seg->thin_messages); ++ return !dm_list_empty(&seg->thin_messages); + + dm_list_iterate_items(tmsg, &seg->thin_messages) { + switch (tmsg->type) { +diff --git a/lib/mirror/mirrored.c b/lib/mirror/mirrored.c +index 5088173..e35a372 100644 +--- a/lib/mirror/mirrored.c ++++ b/lib/mirror/mirrored.c +@@ -356,14 +356,14 @@ static int _add_log(struct dm_pool *mem, struct lv_segment *seg, + + if (seg->log_lv) { + /* If disk log, use its UUID */ +- if (!(log_dlid = build_dm_uuid(mem, seg->log_lv->lvid.s, NULL))) { ++ if (!(log_dlid = build_dm_uuid(mem, seg->log_lv, NULL))) { + log_error("Failed to build uuid for log LV %s.", + seg->log_lv->name); + return 0; + } + } else { + /* If core log, use mirror's UUID and set DM_CORELOG flag */ +- if (!(log_dlid = build_dm_uuid(mem, seg->lv->lvid.s, NULL))) { ++ if (!(log_dlid = build_dm_uuid(mem, seg->lv, NULL))) { + log_error("Failed to build uuid for mirror LV %s.", + seg->lv->name); + return 0; +diff --git a/lib/misc/lvm-string.c b/lib/misc/lvm-string.c +index 380fe81..b96b5cc 100644 +--- a/lib/misc/lvm-string.c ++++ b/lib/misc/lvm-string.c +@@ -15,6 +15,7 @@ + + #include "lib.h" + #include "lvm-string.h" ++#include "metadata-exported.h" + + #include + +@@ -160,8 +161,13 @@ int is_reserved_lvname(const char *name) + return rc; + } + +-char *build_dm_uuid(struct dm_pool *mem, const char *lvid, ++char *build_dm_uuid(struct dm_pool *mem, const struct logical_volume *lv, + const char *layer) + { ++ const char *lvid = lv->lvid.s; ++ ++ if (!layer && lv_is_thin_pool(lv)) ++ layer = "pool"; ++ + return dm_build_dm_uuid(mem, UUID_PREFIX, lvid, layer); + } +diff --git a/lib/misc/lvm-string.h b/lib/misc/lvm-string.h +index 13aacf8..82ebb12 100644 +--- a/lib/misc/lvm-string.h ++++ b/lib/misc/lvm-string.h +@@ -23,6 +23,7 @@ + #define UUID_PREFIX "LVM-" + + struct pool; ++struct logical_volume; + + typedef enum name_error { NAME_VALID = 0, NAME_INVALID_EMPTY = -1, + NAME_INVALID_HYPEN = -2, NAME_INVALID_DOTS = -3, +@@ -32,7 +33,7 @@ typedef enum name_error { NAME_VALID = 0, NAME_INVALID_EMPTY = -1, + int emit_to_buffer(char **buffer, size_t *size, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +-char *build_dm_uuid(struct dm_pool *mem, const char *lvid, ++char *build_dm_uuid(struct dm_pool *mem, const struct logical_volume *lvid, + const char *layer); + + int validate_name(const char *n); +diff --git a/lib/thin/thin.c b/lib/thin/thin.c +index 7c989f8..494fa92 100644 +--- a/lib/thin/thin.c ++++ b/lib/thin/thin.c +@@ -277,13 +277,13 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, + return 0; + } + +- if (!(metadata_dlid = build_dm_uuid(mem, seg->metadata_lv->lvid.s, NULL))) { ++ if (!(metadata_dlid = build_dm_uuid(mem, seg->metadata_lv, NULL))) { + log_error("Failed to build uuid for metadata LV %s.", + seg->metadata_lv->name); + return 0; + } + +- if (!(pool_dlid = build_dm_uuid(mem, seg_lv(seg, 0)->lvid.s, NULL))) { ++ if (!(pool_dlid = build_dm_uuid(mem, seg_lv(seg, 0), NULL))) { + log_error("Failed to build uuid for pool LV %s.", + seg_lv(seg, 0)->name); + return 0; +@@ -535,7 +535,7 @@ static int _thin_add_target_line(struct dev_manager *dm, + seg->lv->name); + return 0; + } +- if (!(pool_dlid = build_dm_uuid(mem, seg->pool_lv->lvid.s, lv_layer(seg->pool_lv)))) { ++ if (!(pool_dlid = build_dm_uuid(mem, seg->pool_lv, lv_layer(seg->pool_lv)))) { + log_error("Failed to build uuid for pool LV %s.", + seg->pool_lv->name); + return 0; +@@ -573,7 +573,7 @@ static int _thin_add_target_line(struct dev_manager *dm, + return 0; + } + } +- if (!(external_dlid = build_dm_uuid(mem, seg->external_lv->lvid.s, ++ if (!(external_dlid = build_dm_uuid(mem, seg->external_lv, + lv_layer(seg->external_lv)))) { + log_error("Failed to build uuid for external origin LV %s.", + seg->external_lv->name); +diff --git a/test/shell/lvcreate-thin.sh b/test/shell/lvcreate-thin.sh +index a2811d2..ab27dfb 100644 +--- a/test/shell/lvcreate-thin.sh ++++ b/test/shell/lvcreate-thin.sh +@@ -173,7 +173,9 @@ not lvcreate --chunksize 32 -l1 -T $vg/pool1 + # Too large chunk size (max is 1GB) + not lvcreate -L4M --chunksize 2G -T $vg/pool1 + +-lvcreate -L4M -V2G --name lv1 -T $vg/pool1 ++# Test creation of inactive pool ++lvcreate -an -L4M -T $vg/pool1 ++lvcreate -V2G --name lv1 -T $vg/pool1 + # Origin name is not accepted + not lvcreate -s $vg/lv1 -L4M -V2G --name $vg/lv4 + +diff --git a/test/shell/pvremove-thin.sh b/test/shell/pvremove-thin.sh +new file mode 100644 +index 0000000..03e6ca3 +--- /dev/null ++++ b/test/shell/pvremove-thin.sh +@@ -0,0 +1,28 @@ ++#!/bin/sh ++# Copyright (C) 2014 Red Hat, Inc. All rights reserved. ++# ++# 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 General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# Checks we are not reading our own devices ++# https://bugzilla.redhat.com/show_bug.cgi?id=1064374 ++ ++. lib/test ++ ++aux prepare_vg ++ ++aux target_at_least dm-thin-pool 1 8 0 || skip ++ ++aux extend_filter_LVMTEST ++ ++lvcreate -L10 -V10 -n $lv1 -T $vg/pool1 ++ ++pvcreate "$DM_DEV_DIR/$vg/$lv1" ++pvremove "$DM_DEV_DIR/$vg/$lv1" ++ ++vgremove -ff $vg diff --git a/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch b/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch new file mode 100644 index 0000000..b1cb821 --- /dev/null +++ b/SOURCES/lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch @@ -0,0 +1,106 @@ + configure | 20 ++++++++++---------- + configure.in | 4 ++-- + lib/device/dev-type.c | 3 +-- + 3 files changed, 13 insertions(+), 14 deletions(-) + +diff --git a/configure b/configure +index a156460..c96911c 100755 +--- a/configure ++++ b/configure +@@ -9484,12 +9484,12 @@ if test -n "$BLKID_CFLAGS"; then + pkg_cv_BLKID_CFLAGS="$BLKID_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ +- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"blkid >= 2.24\""; } >&5 +- ($PKG_CONFIG --exists --print-errors "blkid >= 2.24") 2>&5 ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"blkid >= 2.23\""; } >&5 ++ ($PKG_CONFIG --exists --print-errors "blkid >= 2.23") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then +- pkg_cv_BLKID_CFLAGS=`$PKG_CONFIG --cflags "blkid >= 2.24" 2>/dev/null` ++ pkg_cv_BLKID_CFLAGS=`$PKG_CONFIG --cflags "blkid >= 2.23" 2>/dev/null` + else + pkg_failed=yes + fi +@@ -9500,12 +9500,12 @@ if test -n "$BLKID_LIBS"; then + pkg_cv_BLKID_LIBS="$BLKID_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ +- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"blkid >= 2.24\""; } >&5 +- ($PKG_CONFIG --exists --print-errors "blkid >= 2.24") 2>&5 ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"blkid >= 2.23\""; } >&5 ++ ($PKG_CONFIG --exists --print-errors "blkid >= 2.23") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then +- pkg_cv_BLKID_LIBS=`$PKG_CONFIG --libs "blkid >= 2.24" 2>/dev/null` ++ pkg_cv_BLKID_LIBS=`$PKG_CONFIG --libs "blkid >= 2.23" 2>/dev/null` + else + pkg_failed=yes + fi +@@ -9525,9 +9525,9 @@ else + _pkg_short_errors_supported=no + fi + if test $_pkg_short_errors_supported = yes; then +- BLKID_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "blkid >= 2.24" 2>&1` ++ BLKID_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "blkid >= 2.23" 2>&1` + else +- BLKID_PKG_ERRORS=`$PKG_CONFIG --print-errors "blkid >= 2.24" 2>&1` ++ BLKID_PKG_ERRORS=`$PKG_CONFIG --print-errors "blkid >= 2.23" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$BLKID_PKG_ERRORS" >&5 +@@ -9535,7 +9535,7 @@ fi + if test x$BLKID_WIPING = xmaybe; then + BLKID_WIPING=no + else +- as_fn_error $? "bailing out... blkid library >= 2.24 is required" "$LINENO" 5 ++ as_fn_error $? "bailing out... blkid library >= 2.23 is required" "$LINENO" 5 + fi + + elif test $pkg_failed = untried; then +@@ -9544,7 +9544,7 @@ $as_echo "no" >&6; } + if test x$BLKID_WIPING = xmaybe; then + BLKID_WIPING=no + else +- as_fn_error $? "bailing out... blkid library >= 2.24 is required" "$LINENO" 5 ++ as_fn_error $? "bailing out... blkid library >= 2.23 is required" "$LINENO" 5 + fi + + else +diff --git a/configure.in b/configure.in +index 3e0e508..cff7e45 100644 +--- a/configure.in ++++ b/configure.in +@@ -981,14 +981,14 @@ if test x$BLKID_WIPING != xno; then + if test x$PKGCONFIG_INIT != x1; then + pkg_config_init + fi +- PKG_CHECK_MODULES(BLKID, blkid >= 2.24, ++ PKG_CHECK_MODULES(BLKID, blkid >= 2.23, + [if test x$BLKID_WIPING = xmaybe; then + BLKID_WIPING=yes + fi], + [if test x$BLKID_WIPING = xmaybe; then + BLKID_WIPING=no + else +- AC_MSG_ERROR([bailing out... blkid library >= 2.24 is required]) ++ AC_MSG_ERROR([bailing out... blkid library >= 2.23 is required]) + fi + ]) + if test x$BLKID_WIPING = xyes; then +diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c +index 78b093c..02bc99f 100644 +--- a/lib/device/dev-type.c ++++ b/lib/device/dev-type.c +@@ -521,8 +521,7 @@ static int _wipe_known_signatures_with_blkid(struct device *dev, const char *nam + BLKID_SUBLKS_TYPE | + BLKID_SUBLKS_USAGE | + BLKID_SUBLKS_VERSION | +- BLKID_SUBLKS_MAGIC | +- BLKID_SUBLKS_BADCSUM); ++ BLKID_SUBLKS_MAGIC); + + while (!blkid_do_probe(probe)) { + found++; diff --git a/SOURCES/lvm2-rhel7.patch b/SOURCES/lvm2-rhel7.patch index 0107b55..862e4aa 100644 --- a/SOURCES/lvm2-rhel7.patch +++ b/SOURCES/lvm2-rhel7.patch @@ -7,12 +7,12 @@ index dd4e60e..39d6c15 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ --2.02.103(2) (2013-10-04) -+2.02.103(2)-RHEL7 (2013-10-10) +-2.02.105(2) (2014-01-20) ++2.02.105(2)-RHEL7 (2014-03-26) diff --git a/VERSION_DM b/VERSION_DM index d53f47a..005fbd4 100644 --- a/VERSION_DM +++ b/VERSION_DM @@ -1 +1 @@ --1.02.82 (2013-10-04) -+1.02.82-RHEL7 (2013-10-10) +-1.02.84 (2014-01-20) ++1.02.84-RHEL7 (2014-03-26) diff --git a/SPECS/lvm2.spec b/SPECS/lvm2.spec index 8460e0c..1d5e3c6 100644 --- a/SPECS/lvm2.spec +++ b/SPECS/lvm2.spec @@ -1,16 +1,18 @@ -%define device_mapper_version 1.02.82 +%define device_mapper_version 1.02.84 -%define enable_thin 1 -%define enable_lvmetad 1 +%define enable_cache 1 %define enable_cluster 1 %define enable_cmirror 1 +%define enable_lvmetad 1 %define enable_python 1 +%define enable_thin 1 %define systemd_version 189-3 %define dracut_version 002-18 -%define util_linux_version 2.22.1 +%define util_linux_version 2.23 %define bash_version 4.0 %define corosync_version 1.99.9-1 +%define resource_agents_version 3.9.5-25 %define dlm_version 3.99.1-1 %define libselinux_version 1.30.19-4 %define persistent_data_version 0.2.7-1 @@ -38,8 +40,8 @@ Summary: Userland logical volume management tools Name: lvm2 Epoch: 7 -Version: 2.02.103 -Release: 6%{?dist} +Version: 2.02.105 +Release: 14%{?dist} License: GPLv2 Group: System Environment/Base URL: http://sources.redhat.com/lvm2 @@ -47,18 +49,21 @@ Source0: ftp://sources.redhat.com/pub/lvm2/releases/LVM2.%{version}.tgz Patch0: lvm2-rhel7.patch Patch1: lvm2-set-default-preferred_names.patch Patch2: lvm2-enable-lvmetad-by-default.patch -Patch3: lvm2-2_02_104-fix-lvconvert-when-converting-to-a-thin-pool-and-thin-lv-at-once.patch -Patch4: lvm2-2_02_104-improve-discards-when-pool-active-error.patch -Patch5: lvm2-2_02_104-udev-noscan-flag.patch -Patch6: lvm2-2_02_104-various-lvmetad-fixes.patch -Patch7: lvm2-2_02_104-udev-systemd-background-job-for-pvscan-cache-aay.patch -Patch8: lvm2-2_02_104-fix-lvconvert-swap-of-poolmetadata-volume-for-active-thin-pool.patch -Patch9: lvm2-2_02_104-add-dev-block-major-minor-device-systemd-alias-and-pvscan-cache-shortcut.patch -Patch10: lvm2-2_02_104-workaround-vg-refresh-during-autoactivation-by-retrying-the-refresh.patch -Patch11: lvm2-2_02_104-various-udev-support-fixes.patch -Patch12: lvm2-2_02_105-do-no-fail-the-whole-autoactivation-if-the-vg-refresh-done-before-fails.patch +Patch3: lvm2-drop-unavailable-libblkid-2_24-BLKID_SUBLKS_BADCSUM-for-signature-detection.patch +Patch4: lvm2-2_02_106-upstream-with-cache-support.patch +Patch5: lvm2-2_02_106-fix-incorrect-snapshot-calculation-of-cow-size.patch +Patch6: lvm2-2_02_106-additional-lvmetad-fixes.patch +Patch7: lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror.patch +Patch8: lvm2-2_02_106-fix-dmeventd-logging-with-parallel-wait-processing.patch +Patch9: lvm2-2_02_106-various-thin-fixes.patch +Patch10: lvm2-2_02_106-enahnce-lvmetad-protocol-for-pvscan-to-lock-vg-properly-and-run-refresh-only-when-needed.patch +Patch11: lvm2-2_02_106-cleanup-stray-warning-messages-for-cmirror-addendum.patch +Patch12: lvm2-2_02_106-reinitialise-lvmcache-properly-on-fork-to-fix-premature-polldaemon-exit.patch +Patch13: lvm2-2_02_106-fix-timeout-for-initial-lvmetad-scan-when-done-in-parallel.patch +Patch14: lvm2-2_02_106-various-man-page-cleanups.patch BuildRequires: libselinux-devel >= %{libselinux_version}, libsepol-devel +BuildRequires: libblkid-devel >= %{util_linux_version} BuildRequires: ncurses-devel BuildRequires: readline-devel %if %{enable_cluster} @@ -75,7 +80,7 @@ BuildRequires: python-setuptools %endif Requires: %{name}-libs = %{epoch}:%{version}-%{release} Requires: bash >= %{bash_version} -Requires(post): systemd-units >= %{systemd_version}, systemd-sysv +Requires(post): systemd-units >= %{systemd_version} Requires(preun): systemd-units >= %{systemd_version} Requires(postun): systemd-units >= %{systemd_version} Requires: module-init-tools @@ -96,16 +101,18 @@ or more physical volumes and creating one or more logical volumes %patch0 -p1 -b .rhel7 %patch1 -p1 -b .preferred_names %patch2 -p1 -b .enable_lvmetad -%patch3 -p1 -b .lvconvert_thin -%patch4 -p1 -b .pool_discards_msg -%patch5 -p1 -b .udev_noscan +%patch3 -p1 -b .blkid_sublks_badcsum +%patch4 -p1 -b .v106_upstream_cache +%patch5 -p1 -b .snap_cow_size_calc %patch6 -p1 -b .lvmetad_fixes -%patch7 -p1 -b .udev_systemd_background_job -%patch8 -p1 -b .lvconvert_swap_poolmetadata -%patch9 -p1 -b .major_minor_systemd_alias_pvscan -%patch10 -p1 -b .autoactivation_vg_refresh -%patch11 -p1 -b .udev_fixes -%patch12 -p1 -b .autoactivation_refresh_fail +%patch7 -p1 -b .cmirror_messages +%patch8 -p1 -b .dmeventd_parallel_wait +%patch9 -p1 -b .thin_fixes +%patch10 -p1 -b .pvscan_lvmetad +%patch11 -p1 -b .cmirror_messages_addendum +%patch12 -p1 -b .lvmcache_reinitialise +%patch13 -p1 -b .initial_lvmetad_scan_timeout +%patch14 -p1 -b .man_page_cleanups %build %define _default_pid_dir /run @@ -118,6 +125,10 @@ or more physical volumes and creating one or more logical volumes %define configure_udev --with-udevdir=%{_udevdir} --enable-udev_sync +%if %{enable_cache} +%define configure_cache --with-cache=internal +%endif + %if %{enable_thin} %define configure_thin --with-thin=internal --with-thin-check=%{_sbindir}/thin_check --with-thin-dump=%{_sbindir}/thin_dump --with-thin-repair=%{_sbindir}/thin_repair %endif @@ -130,7 +141,7 @@ or more physical volumes and creating one or more logical volumes %define configure_python --enable-python-bindings %endif -%configure --with-default-dm-run-dir=%{_default_dm_run_dir} --with-default-run-dir=%{_default_run_dir} --with-default-pid-dir=%{_default_pid_dir} --with-default-locking-dir=%{_default_locking_dir} --with-usrlibdir=%{_libdir} --enable-lvm1_fallback --enable-fsadm --with-pool=internal --with-user= --with-group= --with-device-uid=0 --with-device-gid=6 --with-device-mode=0660 --enable-pkgconfig --enable-applib --enable-cmdlib --enable-dmeventd %{?configure_python} %{?configure_cluster} %{?configure_cmirror} %{?configure_udev} %{?configure_thin} %{?configure_lvmetad} +%configure --with-default-dm-run-dir=%{_default_dm_run_dir} --with-default-run-dir=%{_default_run_dir} --with-default-pid-dir=%{_default_pid_dir} --with-default-locking-dir=%{_default_locking_dir} --with-usrlibdir=%{_libdir} --enable-lvm1_fallback --enable-fsadm --with-pool=internal --enable-write_install --with-user= --with-group= --with-device-uid=0 --with-device-gid=6 --with-device-mode=0660 --enable-pkgconfig --enable-applib --enable-cmdlib --enable-dmeventd --enable-blkid_wiping %{?configure_python} %{?configure_cluster} %{?configure_cmirror} %{?configure_udev} %{?configure_thin} %{?configure_lvmetad} %{?configure_cache} make %{?_smp_mflags} @@ -159,32 +170,37 @@ rm -rf $RPM_BUILD_ROOT %endif %postun -%systemd_postun_with_restart lvm2-monitor.service +%systemd_postun lvm2-monitor.service %if %{enable_lvmetad} %systemd_postun_with_restart lvm2-lvmetad.service %endif -%triggerun -- %{name} < 2.02.86-2 -%{_bindir}/systemd-sysv-convert --save lvm2-monitor >/dev/null 2>&1 || : -/bin/systemctl --no-reload enable lvm2-monitor.service > /dev/null 2>&1 || : -/sbin/chkconfig --del lvm2-monitor > /dev/null 2>&1 || : -/bin/systemctl try-restart lvm2-monitor.service > /dev/null 2>&1 || : - %files %defattr(-,root,root,-) %doc COPYING COPYING.LIB INSTALL README VERSION WHATS_NEW %doc doc/lvm_fault_handling.txt + +# Main binaries +%defattr(555,root,root,-) %{_sbindir}/blkdeactivate %{_sbindir}/fsadm +%{_sbindir}/lvm +%{_sbindir}/lvmconf +%{_sbindir}/lvmdump +%if %{enable_lvmetad} +%{_sbindir}/lvmetad +%endif +%{_sbindir}/vgimportclone + +# Other files +%defattr(444,root,root,-) %{_sbindir}/lvchange %{_sbindir}/lvconvert %{_sbindir}/lvcreate %{_sbindir}/lvdisplay %{_sbindir}/lvextend -%{_sbindir}/lvm %{_sbindir}/lvmchange %{_sbindir}/lvmdiskscan -%{_sbindir}/lvmdump %{_sbindir}/lvmsadc %{_sbindir}/lvmsar %{_sbindir}/lvreduce @@ -212,7 +228,6 @@ rm -rf $RPM_BUILD_ROOT %{_sbindir}/vgexport %{_sbindir}/vgextend %{_sbindir}/vgimport -%{_sbindir}/vgimportclone %{_sbindir}/vgmerge %{_sbindir}/vgmknodes %{_sbindir}/vgreduce @@ -221,10 +236,6 @@ rm -rf $RPM_BUILD_ROOT %{_sbindir}/vgs %{_sbindir}/vgscan %{_sbindir}/vgsplit -%{_sbindir}/lvmconf -%if %{enable_lvmetad} -%{_sbindir}/lvmetad -%endif %{_mandir}/man5/lvm.conf.5.gz %{_mandir}/man8/blkdeactivate.8.gz %{_mandir}/man8/fsadm.8.gz @@ -281,7 +292,7 @@ rm -rf $RPM_BUILD_ROOT %endif %dir %{_sysconfdir}/lvm %ghost %{_sysconfdir}/lvm/cache/.cache -%config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/lvm/lvm.conf +%attr(644, -, -) %config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/lvm/lvm.conf %dir %{_sysconfdir}/lvm/profile %{_sysconfdir}/lvm/profile/default.profile %{_sysconfdir}/lvm/profile/thin-performance.profile @@ -293,7 +304,7 @@ rm -rf $RPM_BUILD_ROOT %{_tmpfilesdir}/%{name}.conf %{_unitdir}/blk-availability.service %{_unitdir}/lvm2-monitor.service -%{_prefix}/lib/systemd/system-generators/lvm2-activation-generator +%attr(555, -, -) %{_prefix}/lib/systemd/system-generators/lvm2-activation-generator %if %{enable_lvmetad} %{_unitdir}/lvm2-lvmetad.socket %{_unitdir}/lvm2-lvmetad.service @@ -309,7 +320,7 @@ Group: Development/Libraries License: LGPLv2 Requires: %{name} = %{epoch}:%{version}-%{release} Requires: device-mapper-devel >= %{epoch}:%{device_mapper_version}-%{release} -Requires: device-mapper-event-devel >= %{epoch}:{device_mapper_version}-%{release} +Requires: device-mapper-event-devel >= %{epoch}:%{device_mapper_version}-%{release} Requires: pkgconfig %description devel @@ -317,13 +328,13 @@ This package contains files needed to develop applications that use the lvm2 libraries. %files devel -%defattr(-,root,root,-) +%defattr(444,root,root,-) %{_libdir}/liblvm2app.so %{_libdir}/liblvm2cmd.so +%{_libdir}/libdevmapper-event-lvm2.so %{_includedir}/lvm2app.h %{_includedir}/lvm2cmd.h %{_libdir}/pkgconfig/lvm2app.pc -%{_libdir}/libdevmapper-event-lvm2.so %package libs Summary: Shared libraries for lvm2 @@ -339,10 +350,10 @@ This package contains shared lvm2 libraries for applications. %postun libs -p /sbin/ldconfig %files libs -%defattr(-,root,root,-) -%attr(755,root,root) %{_libdir}/liblvm2app.so.* -%attr(755,root,root) %{_libdir}/liblvm2cmd.so.* -%attr(755,root,root) %{_libdir}/libdevmapper-event-lvm2.so.* +%defattr(555,root,root,-) +%{_libdir}/liblvm2app.so.* +%{_libdir}/liblvm2cmd.so.* +%{_libdir}/libdevmapper-event-lvm2.so.* %dir %{_libdir}/device-mapper %{_libdir}/device-mapper/libdevmapper-event-lvm2mirror.so %{_libdir}/device-mapper/libdevmapper-event-lvm2snapshot.so @@ -362,7 +373,7 @@ This package contains shared lvm2 libraries for applications. Summary: Python module to access LVM License: LGPLv2 Group: Development/Libraries -Provides: python-lvm = %{version}-%{release} +Provides: python-lvm = %{epoch}:%{version}-%{release} Obsoletes: python-lvm < 2.02.98-3 Requires: %{name}-libs = %{epoch}:%{version}-%{release} @@ -377,6 +388,7 @@ logical volumes, physical volumes, and volume groups. ############################################################################## # Cluster subpackage +# The OCF script to manage clvmd instance is part of resource-agents package. ############################################################################## %if %{enable_cluster} @@ -385,68 +397,113 @@ Summary: Cluster extensions for userland logical volume management tools License: GPLv2 Group: System Environment/Base Requires: lvm2 >= %{epoch}:%{version}-%{release} -Requires(post): chkconfig -Requires(preun): chkconfig Requires(preun): device-mapper >= %{epoch}:%{device_mapper_version} Requires(preun): lvm2 >= 2.02 Requires: corosync >= %{corosync_version} Requires: dlm >= %{dlm_version} +Requires: resource-agents >= %{resource_agents_version} %description cluster Extensions to LVM2 to support clusters. %post cluster -/sbin/chkconfig --add clvmd - -if [ "$1" -gt "1" ] ; then - /usr/sbin/clvmd -S >/dev/null 2>&1 || : +if [ -e %{_default_pid_dir}/clvmd.pid ]; then + /usr/sbin/clvmd -S || echo "Failed to restart clvmd daemon. Please, try manual restart." fi %preun cluster -if [ "$1" = 0 ]; then - /sbin/chkconfig --del clvmd +if [ "$1" = "0" ]; then /sbin/lvmconf --disable-cluster fi %files cluster -%defattr(-,root,root,-) -%attr(755,root,root) /usr/sbin/clvmd -%{_mandir}/man8/clvmd.8.gz -%{_sysconfdir}/rc.d/init.d/clvmd +%defattr(555,root,root,-) +%{_sbindir}/clvmd +%attr(444, -, -) %{_mandir}/man8/clvmd.8.gz + +############################################################################## +# Cluster-standalone subpackage +############################################################################## +%package cluster-standalone +Summary: Additional files to support clustered LVM2 in standalone mode +License: GPLv2 +Group: System Environment/Base +Requires: lvm2-cluster >= %{epoch}:%{version}-%{release} + +%description cluster-standalone + +Additional files needed to run clustered LVM2 daemon and clustered volume +activation in standalone mode as services without cluster resource manager +involvement (e.g. pacemaker). + +%post cluster-standalone +%systemd_post lvm2-clvmd.service lvm2-cluster-activation.service + +%preun cluster-standalone +%systemd_preun lvm2-clvmd.service lvm2-cluster-activation.service + +%postun cluster-standalone +%systemd_postun lvm2-clvmd.service lvm2-cluster-activation.service + +%files cluster-standalone +%defattr(555,root,root,-) +%{_prefix}/lib/systemd/lvm2-cluster-activation +%defattr(444,root,root,-) +%{_unitdir}/lvm2-clvmd.service +%{_unitdir}/lvm2-cluster-activation.service %endif -############################################################################## +################################################################################# # Cluster mirror subpackage -############################################################################## +# The OCF script to manage cmirrord instance is part of resource-agents package. +################################################################################# %if %{enable_cluster} %if %{enable_cmirror} %package -n cmirror Summary: Daemon for device-mapper-based clustered mirrors Group: System Environment/Base -Requires(post): chkconfig -Requires(preun): chkconfig Requires: corosync >= %{corosync_version} Requires: device-mapper >= %{epoch}:%{device_mapper_version}-%{release} +Requires: resource-agents >= %{resource_agents_version} %description -n cmirror Daemon providing device-mapper-based mirrors in a shared-storage cluster. -%post -n cmirror -/sbin/chkconfig --add cmirrord +%files -n cmirror +%defattr(555,root,root,-) +%{_sbindir}/cmirrord +%attr(444, -, -) %{_mandir}/man8/cmirrord.8.gz -%preun -n cmirror -if [ "$1" = 0 ]; then - /sbin/chkconfig --del cmirrord -fi +############################################################################## +# Cmirror-standalone subpackage +############################################################################## +%package -n cmirror-standalone +Summary: Additional files to support device-mapper-based clustered mirrors in standalone mode +License: GPLv2 +Group: System Environment/Base +Requires: cmirror >= %{epoch}:%{version}-%{release} -%files -n cmirror -%defattr(-,root,root,-) -%attr(755,root,root) /usr/sbin/cmirrord -%{_mandir}/man8/cmirrord.8.gz -%{_sysconfdir}/rc.d/init.d/cmirrord +%description -n cmirror-standalone + +Additional files needed to run daemon for device-mapper-based clustered +mirrors in standalone mode as a service without cluster resource manager +involvement (e.g. pacemaker). + +%post -n cmirror-standalone +%systemd_post lvm2-cmirrord.service + +%preun -n cmirror-standalone +%systemd_preun lvm2-cmirrord.service + +%postun -n cmirror-standalone +%systemd_postun lvm2-cmirrord.service + +%files -n cmirror-standalone +%defattr(444,root,root,-) +%{_unitdir}/lvm2-cmirrord.service %endif %endif @@ -465,8 +522,15 @@ SysV style init script for LVM2. It needs to be installed only if systemd is not used as the system init process. %files sysvinit +%defattr(555,root,root,-) %{_sysconfdir}/rc.d/init.d/blk-availability %{_sysconfdir}/rc.d/init.d/lvm2-monitor +%if %{enable_cluster} +%{_sysconfdir}/rc.d/init.d/clvmd +%if %{enable_cmirror} +%{_sysconfdir}/rc.d/init.d/cmirrord +%endif +%endif %if %{enable_lvmetad} %{_sysconfdir}/rc.d/init.d/lvm2-lvmetad %endif @@ -495,9 +559,10 @@ for the kernel device-mapper. %files -n device-mapper %defattr(-,root,root,-) %doc COPYING COPYING.LIB WHATS_NEW_DM VERSION_DM README INSTALL -%attr(755,root,root) %{_sbindir}/dmsetup -%{_mandir}/man8/dmsetup.8.gz %doc udev/12-dm-permissions.rules +%defattr(444,root,root,-) +%attr(555, -, -) %{_sbindir}/dmsetup +%{_mandir}/man8/dmsetup.8.gz %{_udevdir}/10-dm.rules %{_udevdir}/13-dm-disk.rules %{_udevdir}/95-dm-notify.rules @@ -516,7 +581,7 @@ This package contains files needed to develop applications that use the device-mapper libraries. %files -n device-mapper-devel -%defattr(-,root,root,-) +%defattr(444,root,root,-) %{_libdir}/libdevmapper.so %{_includedir}/libdevmapper.h %{_libdir}/pkgconfig/devmapper.pc @@ -537,7 +602,8 @@ This package contains the device-mapper shared library, libdevmapper. %postun -n device-mapper-libs -p /sbin/ldconfig %files -n device-mapper-libs -%attr(755,root,root) %{_libdir}/libdevmapper.so.* +%defattr(555,root,root,-) +%{_libdir}/libdevmapper.so.* %package -n device-mapper-event Summary: Device-mapper event daemon @@ -556,19 +622,16 @@ of device-mapper devices. %post -n device-mapper-event %systemd_post dm-event.socket +if [ -e %{_default_pid_dir}/dmeventd.pid ]; then + %{_sbindir}/dmeventd -R || echo "Failed to restart dmeventd daemon. Please, try manual restart." +fi %preun -n device-mapper-event %systemd_preun dm-event.service dm-event.socket -%postun -n device-mapper-event -/bin/systemctl daemon-reload > /dev/null 2>&1 || : -if [ $1 -ge 1 ]; then - /bin/systemctl reload dm-event.service > /dev/null 2>&1 || : -fi - %files -n device-mapper-event -%defattr(-,root,root,-) -%{_sbindir}/dmeventd +%defattr(444,root,root,-) +%attr(555, -, -) %{_sbindir}/dmeventd %{_mandir}/man8/dmeventd.8.gz %{_unitdir}/dm-event.socket %{_unitdir}/dm-event.service @@ -589,7 +652,8 @@ libdevmapper-event. %postun -n device-mapper-event-libs -p /sbin/ldconfig %files -n device-mapper-event-libs -%attr(755,root,root) %{_libdir}/libdevmapper-event.so.* +%defattr(555,root,root,-) +%{_libdir}/libdevmapper-event.so.* %package -n device-mapper-event-devel Summary: Development libraries and headers for the device-mapper event daemon @@ -605,12 +669,170 @@ This package contains files needed to develop applications that use the device-mapper event library. %files -n device-mapper-event-devel -%defattr(-,root,root,-) +%defattr(444,root,root,-) %{_libdir}/libdevmapper-event.so %{_includedir}/libdevmapper-event.h %{_libdir}/pkgconfig/devmapper-event.pc %changelog +* Wed Mar 26 2014 Peter Rajnoha - 7:2.02.105-14 +- Increase wait time for finishing initial lvmetad scan that's run in parallel. +- Reinitialise lvmcache properly on fork to fix polldaemon exiting prematurely. +- Add more cleanup for stray "cpg_dispatch_failed" cmirror messages. + +* Wed Mar 19 2014 Peter Rajnoha - 7:2.02.105-13 +- Use VG read lock during 'pvscan --cache -aay' autoactivation. +- Issue a VG refresh before autoactivation only if the PV has changed/is new. +- Add flag to lvmetad protocol to indicate the PV scanned has changed/is new. +- Also add vgname to lvmetad protocol when referencing VGs for PVs scanned. +- Use correct PATH_MAX for locking dir path. + +* Wed Mar 12 2014 Peter Rajnoha - 7:2.02.105-12 +- Do not try to check empty pool with scheduled messages. +- Fix return value in pool_has_message() when quering for any message. +- Fix dmeventd logging with parallel wait event processing. + +* Thu Mar 06 2014 Peter Rajnoha - 7:2.02.105-11 +- Cleanup stray "cpg_dispatch failed: CS_ERR_BAD_HANDLE" messages for cmirror. + +* Wed Mar 05 2014 Peter Rajnoha - 7:2.02.105-10 +- Fix cache consistency in lvmetad when PV moves around. +- Fix memleak when lvmetad discovers PV to appear on another device. +- Fix invalid memory read in lvmetad that could cause a deadlock. + +* Fri Feb 28 2014 Peter Rajnoha - 7:2.02.105-9 +- Use configure --with-cache=internal to enable cache support code. + +* Thu Feb 27 2014 Peter Rajnoha - 7:2.02.105-8 +- Fix incorrect calculation of snapshot cow size. + +* Wed Feb 26 2014 Peter Rajnoha - 7:2.02.105-7 +- Fix previous build that reinstated obsoleted multipath rule in 10-dm.rules. + +* Wed Feb 26 2014 Peter Rajnoha - 7:2.02.105-6 +- Add initial support for LVM cache and cachepool segment types. +- Include various bug fixes from upcoming lvm2 v2.02.106 release + detailed in WHATS_NEW and WHATS_NEW_DM file. + +* Fri Feb 21 2014 Peter Rajnoha - 7:2.02.105-5 +- Avoid a PV label scan while in a critical section. +- Add Requires: resource-agents to lvm2-cluster and cmirror subpackage. +- Add new lvm2-cluster-standalone and cmirror-standalone subpackages. +- Add systemd native service for clvmd, cmirrord and clustered LV activation. +- Move obsolete clvmd/cmirrord initscripts to lvm2-sysvinit subpackage. +- Use --ignoreskippedcluster in lvm2-monitor initscript/systemd unit. +- Use --ignoreskippedcluster in activation systemd units if use_lvmetad=0. + +* Wed Feb 19 2014 Peter Rajnoha - 7:2.02.105-4 +- Fix unwanted drop of hold flocks on forked children. +- Fix merging of old snapshot into thin volume origin. +- Exit dmeventd with pidfile cleanup instead of raising SIGKILL on DIE request. +- Add new DM_EVENT_GET_PARAMETERS request to dmeventd protocol. +- Do not use systemd's reload for dmeventd restart, use dmeventd -R instead. + +* Wed Feb 05 2014 Peter Rajnoha - 7:2.02.105-3 +- Drop obsolete multipath rules from 10-dm.rules. +- Drop cryptsetup rules from 10-dm.rules - cryptsetup >= 1.1.3 sets them. +- Avoid trying to convert single to thin pool and volume at the same time. +- Detect thin feature external_origin_extend and limit extend when missing. + +* Fri Jan 24 2014 Daniel Mach - 7:2.02.105-2 +- Mass rebuild 2014-01-24 + +* Wed Jan 22 2014 Peter Rajnoha - 7:2.02.105-1 +- Online thin pool metadata resize requires 1.10 kernel thin pool target. +- Fix thin LV flagging for udev to skip scanning only if the LV is wiped. +- Replace use of xfs_check with xfs_repair in fsadm. +- Mark lvm1 format metadata as FMT_OBSOLETE. Do not use it with lvmetad. +- Invalidate cached VG struct after a PV in it gets orphaned. (2.02.87) +- Mark pool format metadata as FMT_OBSOLETE. +- Syntax and spelling fixes in some man pages. +- Dependency scan counts with snapshots and external origins. +- Make sure VG extent size is always greater or equal to PV phys. block size. +- Optimize double call of stat() for cached devices. +- Enable support for thin provisioning for default configuration. +- Return success when inserting dirs and links into device cache. +- Test for remote exclusive activation after activation fails. +- Support lvconvert --merge for thin snapshots. +- Add support to read thin device id from table line entry. +- Extend lv_remove_single() to not print info about removed LV. +- Replace open_count check with lv_check_not_in_use() for snapshot open test. +- Add error messages with LV names for failing lv refresh. +- Add --splitsnapshot to lvconvert to separate out cow LV. +- Reinstate origin reload to complete lvconvert -s with active LVs. (2.02.98) +- Select only active volume groups if vgdisplay -A is used. +- Add -p and LVM_LVMETAD_PID env var to lvmetad to change pid file. +- Allow lvmetad to reuse stale socket. +- Only unlink lvmetad socket on error if created by the same process. +- Append missing newline to lvmetad missing socket path error message. +- Check for non-zero aligment in _text_pv_add_metadata_area() to not div by 0. +- Add allocation/use_blkid_wiping to lvm.conf to enable blkid wiping. +- Enable blkid_wiping by default if the blkid library is present. +- Add configure --disable-blkid_wiping to disable libblkid signature detection. +- Add -W/--wipesignatures lvcreate option to support wiping on new LVs. +- Add allocation/wipe_signatures_when_zeroing_new_lvs to lvm.conf. +- Do not connect to lvmetad on vg/lvchange --sysinit -aay and socket absent. +- Use lv_check_not_in_use() when testing device in use before merging. +- Check for failure of lvmcache_add_mda() when writing pv. +- Check for failure of dev_get_size() when reporting device size. +- Drop extra unneeded '/' when scanning sysfs directory. +- Fix undef value if skipped clustered VG ignored for toollib PV seg. (2.02.103) +- Support validation of VG/LV names in liblvm/python. +- Allow creation of PVs with arguments to liblvm/python. +- Ensure sufficient metadata copies retained in liblvm/python vgreduce. +- Fix installation of profiles from conf subdir when not building in srcdir. +- Show UUIDs for missing PVs in reports. +- Add reporting of thin_id device id for thin volumes. +- Fix reporting of empty numerical values for recently-added fields. +- Use _field_set_percent/value in reporting code. +- Return success when LV cannot be activated because of volume_list filter. +- Return proper error state for remote exclusive activation. +- Fix clvmd message verification to not reject REMOTE flag. (2.02.100) +- Compare equality of double values with DBL_EPSILON predefined constant. +- Use additional gcc warning flags by default. +- Add ignore_lvm_mirrors to config file to read/ignore labels on mirrors. +- Fix endless loop in blkdeactivate ... if unable to umount/deactivate. +- Remove 2>/dev/null from three lvm commands executed by vgimportclone. +- Check for open count with a timeout before removal/deactivation of an LV. +- Report RAID images split with tracking as out-of-sync ("I"). +- Add workaround for deactivation problem of opened virtual snapshot. +- Disable unsupported merge for virtual snapshot. +- Revert activation of activated nodes if a node preload callback fails. +- Avoid busy looping on CPU when dmeventd reads event DM_WAIT_RETRY. +- Ensure global mutex is held when working with dmeventd thread. +- Drop taking timeout mutex for un/registering dmeventd monitor. +- Allow section names in config file data to be quoted strings. +- Close fifos before exiting in dmeventd restart() error path. +- Catch invalid use of string sort values when reporting numerical fields. +- Consistently report on stderr when device is not found for dmsetup info. +- Skip race errors when non-udev dmsetup build runs on udev-enabled system. +- Skip error message when holders are not present in sysfs. +- Require libpthread to build now. +- Add BuildRequires: libblkid-devel for blkid wiping functionality. + +* Wed Jan 15 2014 Peter Rajnoha - 7:2.02.103-11 +- Do not drop SYSTEMD_READY for non-activating events (LVM on MD or loop). +- Add missing AC_TRY_CCFLAG defined in acinclude.m4 for configuration checks. + +* Tue Jan 07 2014 Peter Rajnoha - 7:2.02.103-10 +- Properly apply RELRO and PIE compiler and linker options. + +* Tue Jan 07 2014 Peter Rajnoha - 7:2.02.103-9 +- Consolidate file permissions for all packaged files. + +* Mon Jan 06 2014 Peter Rajnoha - 7:2.02.103-8 +- Use major:minor in lvm2-pvscan@.service for proper global_filter application. +- Fix SYSTEMD_READY assignment for foreign devices in lvmetad udev rules. +- Handle failures in temporary mirror used when adding images to mirrors. +- Compile/link executables with new RELRO and PIE options (non-static builds). +- Replace hardcoded sbin dir value with rpm variable for clvmd and cmirrord. +- Install all binary executables with 0555 permissions. +- Configure with --enable-write_install for writeable executables + during rpm processing of debug info. + +* Fri Dec 27 2013 Daniel Mach - 7:2.02.103-7 +- Mass rebuild 2013-12-27 + * Wed Nov 27 2013 Peter Rajnoha - 2.02.103-6 - Do not fail the whole autoactivation if the VG refresh done before fails.