mrc0mmand / rpms / lvm2

Forked from rpms/lvm2 2 years ago
Clone
Blob Blame History Raw
commit 8cb42e4d9a3f46998defef78bb5bc38971ef3870
Author: Peter Rajnoha <prajnoha@redhat.com>
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-<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 <http://www.gnu.org/licenses/>.
 #
 # 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 <config-patches@gnu.org> 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 <features.h>
+	#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 <stdlib.h>
-              #include <unistd.h>
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
 
-              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 <Richard.M.Bartel@ccMail.Census.GOV>
-        echo i586-unisys-sysv4
-        exit ;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	echo i586-unisys-sysv4
+	exit ;;
     *:UNIX_System_V:4*:FTX*)
 	# From Gerald Hewes <hewes@openmarket.com>.
 	# 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 <<EOF
 #ifdef _SEQUENT_
@@ -1321,11 +1388,11 @@ main ()
 #include <sys/param.h>
   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 <http://www.gnu.org/licenses/>.
 #
 # 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 <config-patches@gnu.org>.  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 <config-patches@gnu.org>."
 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 <raid10|mirror>' 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=<pid> daemon=<no/yes> exec_method=<direct/systemd>'
+		 * 	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, &params);
+
+	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 = "[<key> <value>]..."
+	 *
+	 *   '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_name>"
+	 *   policy_argc = <# args>
+	 *   policy_argv = "[<key> <value>]..."
+	 *
+	 *   '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 <unistd.h>
 #include <sys/param.h>
@@ -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 <sys/stat.h>
 #include <fcntl.h>
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 <name>_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 <unistd.h>
 #include <sys/wait.h>
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 "
+				  "<key> <value> 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(&params, 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, &params);
 }
 
-
 /*
  * 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;
+}
+
+/*
+ * <metadata block size> <#used metadata blocks>/<#total metadata blocks>
+ * <cache block size> <#used cache blocks>/<#total cache blocks>
+ * <#read hits> <#read misses> <#write hits> <#write misses>
+ * <#demotions> <#promotions> <#dirty> <#features> <features>*
+ * <#core args> <core args>* <policy name> <#policy 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, &params->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, &params->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 <VG> <LV>
 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 <VG> <LV>
@@ -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 <VG> <LV>
 #   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 <VG> <LV>
@@ -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 <VG> <LV> <"-"|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 '<superblock uuid="" time="1" transaction="'$2'" data_block_size="128" nr_data_blocks="3200">'
+	for i in $(seq 1 $1)
+	do
+		echo ' <device dev_id="'$i'" mapped_blocks="785" transaction="0" creation_time="0" snap_time="1">'
+		echo '  <range_mapping origin_begin="0" data_begin="0" length="37" time="0"/>'
+		echo ' </device>'
+	done
+	echo "</superblock>"
+}
+
+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-<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.