From 41e4f25c4ea62fa99ba3f5ceb064e64cc95628df Mon Sep 17 00:00:00 2001 From: Peter Rajnoha Date: Tue, 14 May 2013 12:01:16 +0200 Subject: [PATCH] Add various fixes from upcoming lvm2 upstream release. --- ...-udev-env-var-and-fix-noudevsync-arg.patch | 252 ++ ...l-lock-in-pvs-when-lvmetad-is-in-use.patch | 34 + ...2_99-close-dmeventd-fifo-fds-on-exec.patch | 109 + ...-mountpoints-and-mangled-mount-paths.patch | 93 + ...an-cache-aay-triggered-by-non-mda-pv.patch | 175 ++ ...to-not-fail-if-used-without-c-switch.patch | 47 + ...2_02_99-fix-lv_is_active-in-lvcreate.patch | 44 + ...vmetad-for-parallel-update-and-query.patch | 35 + ...ible-race-in-lvmetad-remove_metadata.patch | 54 + ...caused-useless-mapper-control-access.patch | 182 ++ ...vmetad-fix-a-race-in-metadata-update.patch | 83 + ...9-various-thin-support-related-fixes.patch | 2066 +++++++++++++++++ lvm2.spec | 50 +- 13 files changed, 3223 insertions(+), 1 deletion(-) create mode 100644 lvm2-2_02_99-add-dm-disable-udev-env-var-and-fix-noudevsync-arg.patch create mode 100644 lvm2-2_02_99-avoid-global-lock-in-pvs-when-lvmetad-is-in-use.patch create mode 100644 lvm2-2_02_99-close-dmeventd-fifo-fds-on-exec.patch create mode 100644 lvm2-2_02_99-fix-blkdeactivate-handling-of-nested-mountpoints-and-mangled-mount-paths.patch create mode 100644 lvm2-2_02_99-fix-crash-in-pvscan-cache-aay-triggered-by-non-mda-pv.patch create mode 100644 lvm2-2_02_99-fix-dmsetup-splitname-to-not-fail-if-used-without-c-switch.patch create mode 100644 lvm2-2_02_99-fix-lv_is_active-in-lvcreate.patch create mode 100644 lvm2-2_02_99-fix-possible-deadlock-in-lvmetad-for-parallel-update-and-query.patch create mode 100644 lvm2-2_02_99-fix-possible-race-in-lvmetad-remove_metadata.patch create mode 100644 lvm2-2_02_99-fix-premature-dm-version-checking-which-caused-useless-mapper-control-access.patch create mode 100644 lvm2-2_02_99-lvmetad-fix-a-race-in-metadata-update.patch create mode 100644 lvm2-2_02_99-various-thin-support-related-fixes.patch diff --git a/lvm2-2_02_99-add-dm-disable-udev-env-var-and-fix-noudevsync-arg.patch b/lvm2-2_02_99-add-dm-disable-udev-env-var-and-fix-noudevsync-arg.patch new file mode 100644 index 0000000..d2f1570 --- /dev/null +++ b/lvm2-2_02_99-add-dm-disable-udev-env-var-and-fix-noudevsync-arg.patch @@ -0,0 +1,252 @@ +commit 03ed86585e1bfbaf5df1e3488b6268b8887ca427 +Author: Peter Rajnoha +Date: Tue May 14 11:17:52 2013 +0200 + + lvm2-2_02_99-add-dm-disable-udev-env-var-and-fix-noudevsync-arg.patch +--- + WHATS_NEW | 2 ++ + WHATS_NEW_DM | 1 + + lib/commands/toolcontext.c | 62 ++++++++++++++++++++++++++++++++++------------ + libdm/libdm-common.c | 43 ++++++++++++++++++++++++++------ + tools/lvmcmdline.c | 4 +-- + 5 files changed, 86 insertions(+), 26 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 1bdfeb0..8516f40 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,7 @@ + Version 2.02.99 - + =================================== ++ Recognize DM_DISABLE_UDEV environment variable for a complete fallback. ++ Do not verify udev operations if --noudevsync command option is used. + Fix blkdeactivate to handle nested mountpoints and mangled mount paths. + Fix a crash-inducing race condition in lvmetad while updating metadata. + Fix possible race while removing metadata from lvmetad. +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index e0b8d51..9574fdf 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.78 - + =================================== ++ Add DM_DISABLE_UDEV environment variable to manage dev nodes by libdm only. + Automatically deactivate failed preloaded dm tree node. + Fix dm_task_set_cookie to properly process udev flags if udev_sync disabled. + +diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c +index eb1a90b..80d0f3e 100644 +--- a/lib/commands/toolcontext.c ++++ b/lib/commands/toolcontext.c +@@ -211,6 +211,21 @@ static void _init_logging(struct cmd_context *cmd) + reset_lvm_errno(1); + } + ++static int _check_disable_udev(const char *msg) { ++ if (getenv("DM_DISABLE_UDEV")) { ++ log_very_verbose("DM_DISABLE_UDEV environment variable set. " ++ "Overriding configuration to use " ++ "udev_rules=0, udev_sync=0, verify_udev_operations=1."); ++ if (udev_is_running()) ++ log_warn("Udev is running and DM_DISABLE_UDEV environment variable is set. " ++ "Bypassing udev, LVM will %s.", msg); ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ + #ifdef UDEV_SYNC_SUPPORT + /* + * Until the DM_UEVENT_GENERATED_FLAG was introduced in kernel patch +@@ -237,6 +252,7 @@ static int _process_config(struct cmd_context *cmd) + const struct dm_config_value *cv; + int64_t pv_min_kb; + const char *lvmetad_socket; ++ int udev_disabled = 0; + + /* umask */ + cmd->default_settings.umask = find_config_tree_int(cmd, +@@ -310,13 +326,20 @@ static int _process_config(struct cmd_context *cmd) + return 0; + } + +- cmd->default_settings.udev_rules = find_config_tree_int(cmd, +- "activation/udev_rules", +- DEFAULT_UDEV_RULES); ++ /* ++ * If udev is disabled using DM_DISABLE_UDEV environment ++ * variable, override existing config and hardcode these: ++ * - udev_rules = 0 ++ * - udev_sync = 0 ++ * - udev_fallback = 1 ++ */ ++ udev_disabled = _check_disable_udev("manage logical volume symlinks in device directory"); + +- cmd->default_settings.udev_sync = find_config_tree_int(cmd, +- "activation/udev_sync", +- DEFAULT_UDEV_SYNC); ++ cmd->default_settings.udev_rules = udev_disabled ? 0 : ++ find_config_tree_int(cmd, "activation/udev_rules", DEFAULT_UDEV_RULES); ++ ++ cmd->default_settings.udev_sync = udev_disabled ? 0 : ++ find_config_tree_int(cmd, "activation/udev_sync", DEFAULT_UDEV_SYNC); + + init_retry_deactivation(find_config_tree_int(cmd, "activation/retry_deactivation", + DEFAULT_RETRY_DEACTIVATION)); +@@ -326,14 +349,12 @@ static int _process_config(struct cmd_context *cmd) + + #ifdef UDEV_SYNC_SUPPORT + /* +- * We need udev rules to be applied, otherwise we would end up with no +- * nodes and symlinks! However, we can disable the synchronization itself +- * in runtime and still have only udev to create the nodes and symlinks +- * without any fallback. ++ * Use udev fallback automatically in case udev ++ * is disabled via DM_DISABLE_UDEV environment ++ * variable or udev rules are switched off. + */ +- cmd->default_settings.udev_fallback = cmd->default_settings.udev_rules ? +- find_config_tree_int(cmd, "activation/verify_udev_operations", +- DEFAULT_VERIFY_UDEV_OPERATIONS) : 1; ++ cmd->default_settings.udev_fallback = !cmd->default_settings.udev_rules || udev_disabled ? 1 : ++ find_config_tree_int(cmd, "activation/verify_udev_operations", DEFAULT_VERIFY_UDEV_OPERATIONS); + + /* Do not rely fully on udev if the udev support is known to be incomplete. */ + if (!cmd->default_settings.udev_fallback && !_dm_driver_has_stable_udev_support()) { +@@ -693,9 +714,18 @@ static int _init_dev_cache(struct cmd_context *cmd) + if (!dev_cache_init(cmd)) + return_0; + +- device_list_from_udev = udev_is_running() ? +- find_config_tree_bool(cmd, "devices/obtain_device_list_from_udev", +- DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV) : 0; ++ /* ++ * Override existing config and hardcode device_list_from_udev = 0 if: ++ * - udev is not running ++ * - udev is disabled using DM_DISABLE_UDEV environment variable ++ */ ++ if (_check_disable_udev("obtain device list by scanning device directory")) ++ device_list_from_udev = 0; ++ else ++ device_list_from_udev = udev_is_running() ? ++ find_config_tree_bool(cmd, "devices/obtain_device_list_from_udev", ++ DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV) : 0; ++ + init_obtain_device_list_from_udev(device_list_from_udev); + + if (!(cn = find_config_tree_node(cmd, "devices/scan"))) { +diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c +index afdac89..075fba8 100644 +--- a/libdm/libdm-common.c ++++ b/libdm/libdm-common.c +@@ -74,6 +74,8 @@ static dm_string_mangling_t _name_mangling_mode = DEFAULT_DM_NAME_MANGLING; + static struct selabel_handle *_selabel_handle = NULL; + #endif + ++static int _udev_disabled = 0; ++ + #ifdef UDEV_SYNC_SUPPORT + static int _semaphore_supported = -1; + static int _udev_running = -1; +@@ -85,6 +87,9 @@ void dm_lib_init(void) + { + const char *env; + ++ if ((env = getenv("DM_DISABLE_UDEV"))) ++ _udev_disabled = 1; ++ + env = getenv(DM_DEFAULT_NAME_MANGLING_MODE_ENV_VAR_NAME); + if (env && *env) { + if (!strcasecmp(env, "none")) +@@ -1814,6 +1819,26 @@ out: + return r; + } + ++static void _set_cookie_flags(struct dm_task *dmt, uint16_t flags) ++{ ++ if (!dm_cookie_supported()) ++ return; ++ ++ if (_udev_disabled) { ++ /* ++ * If udev is disabled, hardcode this functionality: ++ * - we want libdm to create the nodes ++ * - we don't want the /dev/mapper and any subsystem ++ * related content to be created by udev if udev ++ * rules are installed ++ */ ++ flags &= ~DM_UDEV_DISABLE_LIBRARY_FALLBACK; ++ flags |= DM_UDEV_DISABLE_DM_RULES_FLAG | DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; ++ } ++ ++ dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT; ++} ++ + #ifndef UDEV_SYNC_SUPPORT + void dm_udev_set_sync_support(int sync_with_udev) + { +@@ -1835,8 +1860,8 @@ int dm_udev_get_checking(void) + + int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags) + { +- if (dm_cookie_supported()) +- dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT; ++ _set_cookie_flags(dmt, flags); ++ + *cookie = 0; + dmt->cookie_set = 1; + +@@ -1908,8 +1933,13 @@ static void _check_udev_sync_requirements_once(void) + if (_semaphore_supported < 0) + _semaphore_supported = _check_semaphore_is_supported(); + +- if (_udev_running < 0) ++ if (_udev_running < 0) { + _udev_running = _check_udev_is_running(); ++ if (_udev_disabled && _udev_running) ++ log_warn("Udev is running and DM_DISABLE_UDEV environment variable is set. " ++ "Bypassing udev, device-mapper library will manage device " ++ "nodes in device directory."); ++ } + } + + void dm_udev_set_sync_support(int sync_with_udev) +@@ -1922,8 +1952,8 @@ int dm_udev_get_sync_support(void) + { + _check_udev_sync_requirements_once(); + +- return _semaphore_supported && dm_cookie_supported() && +- _udev_running && _sync_with_udev; ++ return !_udev_disabled && _semaphore_supported && ++ dm_cookie_supported() &&_udev_running && _sync_with_udev; + } + + void dm_udev_set_checking(int checking) +@@ -2203,8 +2233,7 @@ int dm_task_set_cookie(struct dm_task *dmt, uint32_t *cookie, uint16_t flags) + { + int semid; + +- if (dm_cookie_supported()) +- dmt->event_nr = flags << DM_UDEV_FLAGS_SHIFT; ++ _set_cookie_flags(dmt, flags); + + if (!dm_udev_get_sync_support()) { + *cookie = 0; +diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c +index 39a8c58..652d57e 100644 +--- a/tools/lvmcmdline.c ++++ b/tools/lvmcmdline.c +@@ -901,10 +901,8 @@ static int _get_settings(struct cmd_context *cmd) + } else + init_trust_cache(0); + +- if (arg_count(cmd, noudevsync_ARG)) { ++ if (arg_count(cmd, noudevsync_ARG)) + cmd->current_settings.udev_sync = 0; +- cmd->current_settings.udev_fallback = 1; +- } + + /* Handle synonyms */ + if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) || diff --git a/lvm2-2_02_99-avoid-global-lock-in-pvs-when-lvmetad-is-in-use.patch b/lvm2-2_02_99-avoid-global-lock-in-pvs-when-lvmetad-is-in-use.patch new file mode 100644 index 0000000..e1ecef0 --- /dev/null +++ b/lvm2-2_02_99-avoid-global-lock-in-pvs-when-lvmetad-is-in-use.patch @@ -0,0 +1,34 @@ +commit 9cff3357bd69f15497af8c03774df07081d361dd +Author: Peter Rajnoha +Date: Tue May 14 11:04:08 2013 +0200 + + lvm2-2_02_99-avoid-global-lock-in-pvs-when-lvmetad-is-in-use.patch +--- + WHATS_NEW | 1 + + tools/toollib.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 62efb53..25e07ee 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Avoid a global lock in pvs when lvmetad is in use. + Fix crash in pvscan --cache -aay triggered by non-mda PV. + Fix lvm2app to return all property sizes in bytes. + Add lvm.conf option global/thin_disabled_features. +diff --git a/tools/toollib.c b/tools/toollib.c +index 5fe94e0..dce42f4 100644 +--- a/tools/toollib.c ++++ b/tools/toollib.c +@@ -701,7 +701,7 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, + int opt = 0; + int ret_max = ECMD_PROCESSED; + int ret = 0; +- int lock_global = !(flags & READ_WITHOUT_LOCK) && !(flags & READ_FOR_UPDATE); ++ int lock_global = !(flags & READ_WITHOUT_LOCK) && !(flags & READ_FOR_UPDATE) && !lvmetad_active(); + + struct pv_list *pvl; + struct physical_volume *pv; diff --git a/lvm2-2_02_99-close-dmeventd-fifo-fds-on-exec.patch b/lvm2-2_02_99-close-dmeventd-fifo-fds-on-exec.patch new file mode 100644 index 0000000..5fa9f6f --- /dev/null +++ b/lvm2-2_02_99-close-dmeventd-fifo-fds-on-exec.patch @@ -0,0 +1,109 @@ +commit b7e453f3821c4a896e00a29800351687d3365f64 +Author: Peter Rajnoha +Date: Tue May 14 11:26:40 2013 +0200 + + lvm2-2_02_99-close-dmeventd-fifo-fds-on-exec.patch +--- + WHATS_NEW_DM | 1 + + daemons/dmeventd/dmeventd.c | 37 ++++++++++++++++++++++++++++--------- + 2 files changed, 29 insertions(+), 9 deletions(-) + +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 9574fdf..30d01f9 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.78 - + =================================== ++ Close open dmeventd FIFO file descriptors on exec (FD_CLOEXEC). + Add DM_DISABLE_UDEV environment variable to manage dev nodes by libdm only. + Automatically deactivate failed preloaded dm tree node. + Fix dm_task_set_cookie to properly process udev flags if udev_sync disabled. +diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c +index 13148c3..5f2339f 100644 +--- a/daemons/dmeventd/dmeventd.c ++++ b/daemons/dmeventd/dmeventd.c +@@ -1237,7 +1237,8 @@ static int _get_timeout(struct message_data *message_data) + /* Initialize a fifos structure with path names. */ + static void _init_fifos(struct dm_event_fifos *fifos) + { +- memset(fifos, 0, sizeof(*fifos)); ++ fifos->client = -1; ++ fifos->server = -1; + + fifos->client_path = DM_EVENT_FIFO_CLIENT; + fifos->server_path = DM_EVENT_FIFO_SERVER; +@@ -1254,7 +1255,7 @@ static int _open_fifos(struct dm_event_fifos *fifos) + syslog(LOG_ERR, "%s: Failed to create client fifo %s: %m.\n", + __func__, fifos->client_path); + (void) dm_prepare_selinux_context(NULL, 0); +- return 0; ++ goto fail; + } + + /* Create server fifo. */ +@@ -1263,7 +1264,7 @@ static int _open_fifos(struct dm_event_fifos *fifos) + syslog(LOG_ERR, "%s: Failed to create server fifo %s: %m.\n", + __func__, fifos->server_path); + (void) dm_prepare_selinux_context(NULL, 0); +- return 0; ++ goto fail; + } + + (void) dm_prepare_selinux_context(NULL, 0); +@@ -1281,31 +1282,49 @@ static int _open_fifos(struct dm_event_fifos *fifos) + if (chmod(fifos->client_path, 0600)) { + syslog(LOG_ERR, "Unable to set correct file permissions on %s: %m.\n", + fifos->client_path); +- return 0; ++ goto fail; + } + + if (chmod(fifos->server_path, 0600)) { + syslog(LOG_ERR, "Unable to set correct file permissions on %s: %m.\n", + fifos->server_path); +- return 0; ++ goto fail; + } + + /* Need to open read+write or we will block or fail */ + if ((fifos->server = open(fifos->server_path, O_RDWR)) < 0) { + syslog(LOG_ERR, "Failed to open fifo server %s: %m.\n", + fifos->server_path); +- return 0; ++ goto fail; ++ } ++ ++ if (fcntl(fifos->server, F_SETFD, FD_CLOEXEC) < 0) { ++ syslog(LOG_ERR, "Failed to set FD_CLOEXEC for fifo server %s: %m.\n", ++ fifos->server_path); ++ goto fail; + } + + /* Need to open read+write for select() to work. */ + if ((fifos->client = open(fifos->client_path, O_RDWR)) < 0) { + syslog(LOG_ERR, "Failed to open fifo client %s: %m", fifos->client_path); +- if (close(fifos->server)) +- syslog(LOG_ERR, "Failed to close fifo server %s: %m", fifos->server_path); +- return 0; ++ goto fail; ++ } ++ ++ if (fcntl(fifos->client, F_SETFD, FD_CLOEXEC) < 0) { ++ syslog(LOG_ERR, "Failed to set FD_CLOEXEC for fifo client %s: %m.\n", ++ fifos->client_path); ++ goto fail; + } + + return 1; ++fail: ++ if (fifos->server >= 0 && close(fifos->server)) ++ syslog(LOG_ERR, "Failed to close fifo server %s: %m", fifos->server_path); ++ ++ if (fifos->client >= 0 && close(fifos->client)) ++ syslog(LOG_ERR, "Failed to close fifo client %s: %m", fifos->client_path); ++ ++ return 0; + } + + /* diff --git a/lvm2-2_02_99-fix-blkdeactivate-handling-of-nested-mountpoints-and-mangled-mount-paths.patch b/lvm2-2_02_99-fix-blkdeactivate-handling-of-nested-mountpoints-and-mangled-mount-paths.patch new file mode 100644 index 0000000..a9dab21 --- /dev/null +++ b/lvm2-2_02_99-fix-blkdeactivate-handling-of-nested-mountpoints-and-mangled-mount-paths.patch @@ -0,0 +1,93 @@ +commit 5025178f95b122c6862dc7d925a7c089f5fb61a8 +Author: Peter Rajnoha +Date: Tue May 14 11:05:50 2013 +0200 + + lvm2-2_02_99-fix-blkdeactivate-handling-of-nested-mountpoints-and-mangled-mount-paths.patch +--- + WHATS_NEW | 1 + + scripts/blkdeactivate.sh.in | 30 ++++++++++++++++++++++++------ + 2 files changed, 25 insertions(+), 6 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 35c5e43..1bdfeb0 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Fix blkdeactivate to handle nested mountpoints and mangled mount paths. + Fix a crash-inducing race condition in lvmetad while updating metadata. + Fix possible race while removing metadata from lvmetad. + Fix possible deadlock when querying and updating lvmetad at the same time. +diff --git a/scripts/blkdeactivate.sh.in b/scripts/blkdeactivate.sh.in +index 740bac5..be00c24 100644 +--- a/scripts/blkdeactivate.sh.in ++++ b/scripts/blkdeactivate.sh.in +@@ -39,6 +39,7 @@ LVM="@sbindir@/lvm" + LSBLK="/bin/lsblk -r --noheadings -o TYPE,KNAME,NAME,MOUNTPOINT" + LSBLK_VARS="local devtype local kname local name local mnt" + LSBLK_READ="read -r devtype kname name mnt" ++SORT_MNT="/bin/sort -r -u -k 4" + + # Do not unmount mounted devices by default. + DO_UMOUNT=0 +@@ -122,9 +123,11 @@ is_top_level_device() { + device_umount () { + test -z "$mnt" && return 0; + ++ test "$devtype" != "lvm" && test "${kname:0:3}" != "dm-" && return 0 ++ + if test -z "${SKIP_UMOUNT_LIST["$mnt"]}" -a "$DO_UMOUNT" -eq "1"; then + echo " UMOUNT: unmounting $name ($kname) mounted on $mnt" +- $UMOUNT "$mnt" || add_device_to_skip_list ++ $UMOUNT "$(printf $mnt)" || add_device_to_skip_list + else + echo " [SKIP]: unmount of $name ($kname) mounted on $mnt" + add_device_to_skip_list +@@ -142,9 +145,6 @@ deactivate_holders () { + # check if the device not on the skip list already + test -z ${SKIP_DEVICE_LIST["$kname"]} || return 1 + +- # try to unmount it if mounted +- device_umount || return 1 +- + # try to deactivate the holder + test $skip -eq 1 && skip=0 && continue + deactivate || return 1 +@@ -226,7 +226,16 @@ deactivate_all() { + echo "Deactivating block devices:" + + if test $# -eq 0; then +- # Deactivate all devices ++ ####################### ++ # Process all devices # ++ ####################### ++ ++ # Unmount all relevant mountpoints first ++ while $LSBLK_READ; do ++ device_umount ++ done <<< "`$LSBLK | $SORT_MNT`" ++ ++ # Do deactivate + while $LSBLK_READ; do + # 'disk' is at the bottom already and it's a real device + test "$devtype" = "disk" && continue +@@ -249,8 +258,17 @@ deactivate_all() { + deactivate || skip=1 + done <<< "`$LSBLK -s`" + else +- # Deactivate only specified devices ++ ################################## ++ # Process only specified devices # ++ ################################## ++ + while test $# -ne 0; do ++ # Unmount all relevant mountpoints first ++ while $LSBLK_READ; do ++ device_umount ++ done <<< "`$LSBLK $1 | $SORT_MNT`" ++ ++ # Do deactivate + # Single dm device tree deactivation. + if test -b "$1"; then + $LSBLK_READ <<< "`$LSBLK --nodeps $1`" diff --git a/lvm2-2_02_99-fix-crash-in-pvscan-cache-aay-triggered-by-non-mda-pv.patch b/lvm2-2_02_99-fix-crash-in-pvscan-cache-aay-triggered-by-non-mda-pv.patch new file mode 100644 index 0000000..491a1e5 --- /dev/null +++ b/lvm2-2_02_99-fix-crash-in-pvscan-cache-aay-triggered-by-non-mda-pv.patch @@ -0,0 +1,175 @@ +commit 5e247adf415942fcc0a5b888567f51a148619b65 +Author: Peter Rajnoha +Date: Tue May 14 11:03:46 2013 +0200 + + lvm2-2_02_99-fix-crash-in-pvscan-cache-aay-triggered-by-non-mda-pv.patch +--- + WHATS_NEW | 1 + + lib/cache/lvmetad.c | 12 ++++------ + lib/cache/lvmetad.h | 3 ++- + test/shell/lvmetad-pvscan-nomda.sh | 49 ++++++++++++++++++++++++++++++++++++++ + tools/pvscan.c | 22 +++++++++++++++-- + 5 files changed, 76 insertions(+), 11 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 5231745..62efb53 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Fix crash in pvscan --cache -aay triggered by non-mda PV. + Fix lvm2app to return all property sizes in bytes. + Add lvm.conf option global/thin_disabled_features. + Add lvconvert support to swap thin pool metadata volume. +diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c +index 72e07fd..a636f43 100644 +--- a/lib/cache/lvmetad.c ++++ b/lib/cache/lvmetad.c +@@ -675,7 +675,7 @@ int lvmetad_pv_found(const struct id *pvid, struct device *device, const struct + daemon_reply reply; + struct lvmcache_info *info; + struct dm_config_tree *pvmeta, *vgmeta; +- const char *status; ++ const char *status, *vgid; + int result; + + if (!lvmetad_active() || test_mode()) +@@ -724,11 +724,6 @@ int lvmetad_pv_found(const struct id *pvid, struct device *device, const struct + NULL); + dm_config_destroy(vgmeta); + } else { +- if (handler) { +- log_error(INTERNAL_ERROR "Handler needs existing VG."); +- dm_free(pvmeta); +- return 0; +- } + /* There are no MDAs on this PV. */ + reply = _lvmetad_send("pv_found", "pvmeta = %t", pvmeta, NULL); + } +@@ -744,10 +739,11 @@ int lvmetad_pv_found(const struct id *pvid, struct device *device, const struct + + if (result && handler) { + status = daemon_reply_str(reply, "status", ""); ++ vgid = daemon_reply_str(reply, "vgid", ""); + if (!strcmp(status, "partial")) +- handler(vg, 1, CHANGE_AAY); ++ handler(_lvmetad_cmd, vgid, 1, CHANGE_AAY); + else if (!strcmp(status, "complete")) +- handler(vg, 0, CHANGE_AAY); ++ handler(_lvmetad_cmd, vgid, 0, CHANGE_AAY); + else if (!strcmp(status, "orphan")) + ; + else +diff --git a/lib/cache/lvmetad.h b/lib/cache/lvmetad.h +index 5f0f552..c644069 100644 +--- a/lib/cache/lvmetad.h ++++ b/lib/cache/lvmetad.h +@@ -22,7 +22,8 @@ struct cmd_context; + struct dm_config_tree; + enum activation_change; + +-typedef int (*activation_handler) (struct volume_group *vg, int partial, ++typedef int (*activation_handler) (struct cmd_context *cmd, ++ const char *vgid, int partial, + enum activation_change activate); + + #ifdef LVMETAD_SUPPORT +diff --git a/test/shell/lvmetad-pvscan-nomda.sh b/test/shell/lvmetad-pvscan-nomda.sh +new file mode 100644 +index 0000000..c04a275 +--- /dev/null ++++ b/test/shell/lvmetad-pvscan-nomda.sh +@@ -0,0 +1,49 @@ ++#!/bin/sh ++# Copyright (C) 2012 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++. lib/test ++ ++test -e LOCAL_LVMETAD || skip ++kill $(cat LOCAL_LVMETAD) ++rm LOCAL_LVMETAD ++ ++aux prepare_devs 2 ++ ++pvcreate --metadatacopies 0 $dev1 ++pvcreate --metadatacopies 1 $dev2 ++vgcreate $vg1 $dev1 $dev2 ++lvcreate -n foo -l 1 -an --zero n $vg1 ++ ++# start lvmetad but make sure it doesn't know about $dev1 or $dev2 ++aux disable_dev $dev1 ++aux disable_dev $dev2 ++aux prepare_lvmetad ++lvs ++mv LOCAL_LVMETAD XXX ++aux enable_dev $dev2 ++aux enable_dev $dev1 ++mv XXX LOCAL_LVMETAD ++ ++aux lvmconf 'global/use_lvmetad = 0' ++check inactive $vg1 foo ++aux lvmconf 'global/use_lvmetad = 1' ++ ++pvscan --cache $dev2 -aay ++ ++aux lvmconf 'global/use_lvmetad = 0' ++check inactive $vg1 foo ++aux lvmconf 'global/use_lvmetad = 1' ++ ++pvscan --cache $dev1 -aay ++ ++aux lvmconf 'global/use_lvmetad = 0' ++check active $vg1 foo ++aux lvmconf 'global/use_lvmetad = 1' +diff --git a/tools/pvscan.c b/tools/pvscan.c +index fbd524b..1e844c5 100644 +--- a/tools/pvscan.c ++++ b/tools/pvscan.c +@@ -91,18 +91,36 @@ static void _pvscan_display_single(struct cmd_context *cmd, + display_size(cmd, (uint64_t) (pv_pe_count(pv) - pv_pe_alloc_count(pv)) * pv_pe_size(pv))); + } + +-static int _auto_activation_handler(struct volume_group *vg, int partial, ++static int _auto_activation_handler(struct cmd_context *cmd, ++ const char *vgid, int partial, + activation_change_t activate) + { ++ struct volume_group *vg; ++ int consistent = 0; ++ struct id vgid_raw; ++ + /* TODO: add support for partial and clustered VGs */ +- if (partial || vg_is_clustered(vg)) ++ if (partial) + return 1; + ++ id_read_format(&vgid_raw, vgid); ++ /* NB. This is safe because we know lvmetad is running and we won't hit ++ * disk. */ ++ if (!(vg = vg_read_internal(cmd, NULL, (const char *) &vgid_raw, 0, &consistent))) ++ return 1; ++ ++ if (vg_is_clustered(vg)) { ++ release_vg(vg); ++ return 1; ++ } ++ + if (!vgchange_activate(vg->cmd, vg, activate)) { + log_error("%s: autoactivation failed.", vg->name); ++ release_vg(vg); + return 0; + } + ++ release_vg(vg); + return 1; + } + diff --git a/lvm2-2_02_99-fix-dmsetup-splitname-to-not-fail-if-used-without-c-switch.patch b/lvm2-2_02_99-fix-dmsetup-splitname-to-not-fail-if-used-without-c-switch.patch new file mode 100644 index 0000000..2ad182b --- /dev/null +++ b/lvm2-2_02_99-fix-dmsetup-splitname-to-not-fail-if-used-without-c-switch.patch @@ -0,0 +1,47 @@ +commit f03877ecb56f32d91dd98e8e3331164f472f8b77 +Author: Peter Rajnoha +Date: Tue May 14 11:48:51 2013 +0200 + + lvm2-2_02_99-fix-dmsetup-splitname-to-not-fail-if-used-without-c-switch.patch +--- + WHATS_NEW_DM | 1 + + tools/dmsetup.c | 8 +++++--- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 30d01f9..34a0f70 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.78 - + =================================== ++ Fix 'dmsetup splitname -o' to not fail if used without '-c' switch (1.02.68). + Close open dmeventd FIFO file descriptors on exec (FD_CLOEXEC). + Add DM_DISABLE_UDEV environment variable to manage dev nodes by libdm only. + Automatically deactivate failed preloaded dm tree node. +diff --git a/tools/dmsetup.c b/tools/dmsetup.c +index 196c170..2dc3abd 100644 +--- a/tools/dmsetup.c ++++ b/tools/dmsetup.c +@@ -3749,9 +3749,6 @@ static int _process_switches(int *argc, char ***argv, const char *dev_dir) + return 0; + } + +- if (!_process_options(_string_args[OPTIONS_ARG])) +- return 0; +- + if (_switches[TABLE_ARG] && _switches[NOTABLE_ARG]) { + fprintf(stderr, "--table and --notable are incompatible.\n"); + return 0; +@@ -3824,6 +3821,11 @@ int main(int argc, char **argv) + if (!strcmp(cmd->name, "mangle")) + dm_set_name_mangling_mode(DM_STRING_MANGLING_NONE); + ++ if (!_process_options(_string_args[OPTIONS_ARG])) { ++ fprintf(stderr, "Couldn't process command line.\n"); ++ goto out; ++ } ++ + if (_switches[COLS_ARG]) { + if (!_report_init(cmd)) + goto out; diff --git a/lvm2-2_02_99-fix-lv_is_active-in-lvcreate.patch b/lvm2-2_02_99-fix-lv_is_active-in-lvcreate.patch new file mode 100644 index 0000000..3b2281b --- /dev/null +++ b/lvm2-2_02_99-fix-lv_is_active-in-lvcreate.patch @@ -0,0 +1,44 @@ +commit 191de7c40463b04ef80b424982221784df0a99ed +Author: Peter Rajnoha +Date: Tue May 14 11:02:39 2013 +0200 + + lvm2-2_02_99-fix-lv_is_active-in-lvcreate.patch +--- + lib/metadata/lv_manip.c | 10 ++-------- + test/shell/lvcreate-usage.sh | 2 ++ + 2 files changed, 4 insertions(+), 8 deletions(-) + +diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c +index a59e03f..557df58 100644 +--- a/lib/metadata/lv_manip.c ++++ b/lib/metadata/lv_manip.c +@@ -4348,14 +4348,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, struct l + log_warn("WARNING: See global/mirror_segtype_default in lvm.conf."); + } + +- if (!lv_is_active(org)) { +- log_error("Check for existence of active snapshot " +- "origin '%s' failed.", org->name); +- return NULL; +- } +- origin_active = 1; +- +- if (vg_is_clustered(vg) && ++ if ((origin_active = lv_is_active(org)) && ++ vg_is_clustered(vg) && + !lv_is_active_exclusive_locally(org)) { + log_error("%s must be active exclusively to" + " create snapshot", org->name); +diff --git a/test/shell/lvcreate-usage.sh b/test/shell/lvcreate-usage.sh +index ddde401..c9c906a 100644 +--- a/test/shell/lvcreate-usage.sh ++++ b/test/shell/lvcreate-usage.sh +@@ -64,6 +64,8 @@ lvcreate -l1 -n $lv3 $vg + not lvcreate -l1 -n $lv4 $vg + + lvremove -ff $vg/$lv3 ++# check snapshot of inactive origin ++lvchange -an $vg/$lv1 + lvcreate -l1 -s -n $lv3 $vg/$lv1 + not lvcreate -l1 -n $lv4 $vg + not lvcreate -l1 -m1 -n $lv4 $vg diff --git a/lvm2-2_02_99-fix-possible-deadlock-in-lvmetad-for-parallel-update-and-query.patch b/lvm2-2_02_99-fix-possible-deadlock-in-lvmetad-for-parallel-update-and-query.patch new file mode 100644 index 0000000..62f4307 --- /dev/null +++ b/lvm2-2_02_99-fix-possible-deadlock-in-lvmetad-for-parallel-update-and-query.patch @@ -0,0 +1,35 @@ +commit 509ba58eaa537a8db2e0bd41d8c56c52a68e8512 +Author: Peter Rajnoha +Date: Tue May 14 11:04:30 2013 +0200 + + lvm2-2_02_99-fix-possible-deadlock-in-lvmetad-for-parallel-update-and-query.patch +--- + WHATS_NEW | 1 + + daemons/lvmetad/lvmetad-core.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 25e07ee..4728466 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Fix possible deadlock when querying and updating lvmetad at the same time. + Avoid a global lock in pvs when lvmetad is in use. + Fix crash in pvscan --cache -aay triggered by non-mda PV. + Fix lvm2app to return all property sizes in bytes. +diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c +index 0a1c884..3f417da 100644 +--- a/daemons/lvmetad/lvmetad-core.c ++++ b/daemons/lvmetad/lvmetad-core.c +@@ -675,8 +675,8 @@ static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid + + lock_vgid_to_metadata(s); + old = dm_hash_lookup(s->vgid_to_metadata, _vgid); +- lock_vg(s, _vgid); + unlock_vgid_to_metadata(s); ++ lock_vg(s, _vgid); + + seq = dm_config_find_int(metadata, "metadata/seqno", -1); + diff --git a/lvm2-2_02_99-fix-possible-race-in-lvmetad-remove_metadata.patch b/lvm2-2_02_99-fix-possible-race-in-lvmetad-remove_metadata.patch new file mode 100644 index 0000000..5ca87f5 --- /dev/null +++ b/lvm2-2_02_99-fix-possible-race-in-lvmetad-remove_metadata.patch @@ -0,0 +1,54 @@ +commit 8f17dc8027f1d9fd188468c26eb8c0a9957cc9cb +Author: Peter Rajnoha +Date: Tue May 14 11:04:52 2013 +0200 + + lvm2-2_02_99-fix-possible-race-in-lvmetad-remove_metadata.patch +--- + WHATS_NEW | 1 + + daemons/lvmetad/lvmetad-core.c | 14 +++++++++----- + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 4728466..20a8125 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Fix possible race while removing metadata from lvmetad. + Fix possible deadlock when querying and updating lvmetad at the same time. + Avoid a global lock in pvs when lvmetad is in use. + Fix crash in pvscan --cache -aay triggered by non-mda PV. +diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c +index 3f417da..fed9296 100644 +--- a/daemons/lvmetad/lvmetad-core.c ++++ b/daemons/lvmetad/lvmetad-core.c +@@ -605,19 +605,23 @@ static int remove_metadata(lvmetad_state *s, const char *vgid, int update_pvids) + lock_vgid_to_metadata(s); + old = dm_hash_lookup(s->vgid_to_metadata, vgid); + oldname = dm_hash_lookup(s->vgid_to_vgname, vgid); +- unlock_vgid_to_metadata(s); + +- if (!old) ++ if (!old) { ++ unlock_vgid_to_metadata(s); + return 0; ++ } ++ + assert(oldname); + +- if (update_pvids) +- /* FIXME: What should happen when update fails */ +- update_pvid_to_vgid(s, old, "#orphan", 0); + /* need to update what we have since we found a newer version */ + dm_hash_remove(s->vgid_to_metadata, vgid); + dm_hash_remove(s->vgid_to_vgname, vgid); + dm_hash_remove(s->vgname_to_vgid, oldname); ++ unlock_vgid_to_metadata(s); ++ ++ if (update_pvids) ++ /* FIXME: What should happen when update fails */ ++ update_pvid_to_vgid(s, old, "#orphan", 0); + dm_config_destroy(old); + return 1; + } diff --git a/lvm2-2_02_99-fix-premature-dm-version-checking-which-caused-useless-mapper-control-access.patch b/lvm2-2_02_99-fix-premature-dm-version-checking-which-caused-useless-mapper-control-access.patch new file mode 100644 index 0000000..f565786 --- /dev/null +++ b/lvm2-2_02_99-fix-premature-dm-version-checking-which-caused-useless-mapper-control-access.patch @@ -0,0 +1,182 @@ +commit 811db05e50b042c7ce3dc3ca9cfd3eccac464caa +Author: Peter Rajnoha +Date: Tue May 14 11:22:58 2013 +0200 + + lvm2-2_02_99-fix-premature-dm-version-checking-which-caused-useless-mapper-control-access.patch +--- + WHATS_NEW | 1 + + lib/activate/dev_manager.c | 58 +++++++++++++++++++++++++++++++++++++++++++--- + lib/commands/toolcontext.c | 46 +++++++----------------------------- + 3 files changed, 64 insertions(+), 41 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 8516f40..eb7897d 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Fix premature DM version checking which caused useless mapper/control access. + Recognize DM_DISABLE_UDEV environment variable for a complete fallback. + Do not verify udev operations if --noudevsync command option is used. + Fix blkdeactivate to handle nested mountpoints and mangled mount paths. +diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c +index 31c1c27..7abd43b 100644 +--- a/lib/activate/dev_manager.c ++++ b/lib/activate/dev_manager.c +@@ -1016,6 +1016,58 @@ int dev_manager_mknodes(const struct logical_volume *lv) + return r; + } + ++#ifdef UDEV_SYNC_SUPPORT ++/* ++ * Until the DM_UEVENT_GENERATED_FLAG was introduced in kernel patch ++ * 856a6f1dbd8940e72755af145ebcd806408ecedd ++ * some operations could not be performed by udev, requiring our fallback code. ++ */ ++static int _dm_driver_has_stable_udev_support(void) ++{ ++ char vsn[80]; ++ unsigned maj, min, patchlevel; ++ ++ return driver_version(vsn, sizeof(vsn)) && ++ (sscanf(vsn, "%u.%u.%u", &maj, &min, &patchlevel) == 3) && ++ (maj == 4 ? min >= 18 : maj > 4); ++} ++ ++static int _check_udev_fallback(struct cmd_context *cmd) ++{ ++ struct config_info *settings = &cmd->current_settings; ++ ++ if (settings->udev_fallback != -1) ++ goto out; ++ ++ /* ++ * Use udev fallback automatically in case udev ++ * is disabled via DM_DISABLE_UDEV environment ++ * variable or udev rules are switched off. ++ */ ++ settings->udev_fallback = !settings->udev_rules ? 1 : ++ find_config_tree_bool(cmd, "activation/verify_udev_operations", ++ DEFAULT_VERIFY_UDEV_OPERATIONS); ++ ++ /* Do not rely fully on udev if the udev support is known to be incomplete. */ ++ if (!settings->udev_fallback && !_dm_driver_has_stable_udev_support()) { ++ log_very_verbose("Kernel driver has incomplete udev support so " ++ "LVM will check and perform some operations itself."); ++ settings->udev_fallback = 1; ++ } ++out: ++ return settings->udev_fallback; ++} ++ ++#else /* UDEV_SYNC_SUPPORT */ ++ ++static int _check_udev_fallback(struct cmd_context *cmd) ++{ ++ /* We must use old node/symlink creation code if not compiled with udev support at all! */ ++ return cmd->current_settings.udev_fallback = 1; ++} ++ ++#endif /* UDEV_SYNC_SUPPORT */ ++ + static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *lv, + const char *layer) + { +@@ -1025,7 +1077,7 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *l + * Instruct also libdevmapper to disable udev + * fallback in accordance to LVM2 settings. + */ +- if (!dm->cmd->current_settings.udev_fallback) ++ if (!_check_udev_fallback(dm->cmd)) + udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; + + /* +@@ -2036,7 +2088,7 @@ static int _create_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root + int r = 1; + + /* Nothing to do if udev fallback is disabled. */ +- if (!dm->cmd->current_settings.udev_fallback) { ++ if (!_check_udev_fallback(dm->cmd)) { + fs_set_create(); + return 1; + } +@@ -2084,7 +2136,7 @@ static int _remove_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root + int r = 1; + + /* Nothing to do if udev fallback is disabled. */ +- if (!dm->cmd->current_settings.udev_fallback) ++ if (!_check_udev_fallback(dm->cmd)) + return 1; + + while ((child = dm_tree_next_child(&handle, root, 0))) { +diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c +index 80d0f3e..61d5c26 100644 +--- a/lib/commands/toolcontext.c ++++ b/lib/commands/toolcontext.c +@@ -226,23 +226,6 @@ static int _check_disable_udev(const char *msg) { + return 0; + } + +-#ifdef UDEV_SYNC_SUPPORT +-/* +- * Until the DM_UEVENT_GENERATED_FLAG was introduced in kernel patch +- * 856a6f1dbd8940e72755af145ebcd806408ecedd +- * some operations could not be performed by udev, requiring our fallback code. +- */ +-static int _dm_driver_has_stable_udev_support(void) +-{ +- char vsn[80]; +- unsigned maj, min, patchlevel; +- +- return driver_version(vsn, sizeof(vsn)) && +- (sscanf(vsn, "%u.%u.%u", &maj, &min, &patchlevel) == 3) && +- (maj == 4 ? min >= 18 : maj > 4); +-} +-#endif +- + static int _process_config(struct cmd_context *cmd) + { + mode_t old_umask; +@@ -341,33 +324,20 @@ static int _process_config(struct cmd_context *cmd) + cmd->default_settings.udev_sync = udev_disabled ? 0 : + find_config_tree_int(cmd, "activation/udev_sync", DEFAULT_UDEV_SYNC); + ++ /* ++ * Set udev_fallback lazily on first use since it requires ++ * checking DM driver version which is an extra ioctl! ++ * This also prevents unnecessary use of mapper/control. ++ * If udev is disabled globally, set fallback mode immediately. ++ */ ++ cmd->default_settings.udev_fallback = udev_disabled ? 1 : -1; ++ + init_retry_deactivation(find_config_tree_int(cmd, "activation/retry_deactivation", + DEFAULT_RETRY_DEACTIVATION)); + + init_activation_checks(find_config_tree_int(cmd, "activation/checks", + DEFAULT_ACTIVATION_CHECKS)); + +-#ifdef UDEV_SYNC_SUPPORT +- /* +- * Use udev fallback automatically in case udev +- * is disabled via DM_DISABLE_UDEV environment +- * variable or udev rules are switched off. +- */ +- cmd->default_settings.udev_fallback = !cmd->default_settings.udev_rules || udev_disabled ? 1 : +- find_config_tree_int(cmd, "activation/verify_udev_operations", DEFAULT_VERIFY_UDEV_OPERATIONS); +- +- /* Do not rely fully on udev if the udev support is known to be incomplete. */ +- if (!cmd->default_settings.udev_fallback && !_dm_driver_has_stable_udev_support()) { +- log_very_verbose("Kernel driver has incomplete udev support so " +- "LVM will check and perform some operations itself."); +- cmd->default_settings.udev_fallback = 1; +- } +- +-#else +- /* We must use old node/symlink creation code if not compiled with udev support at all! */ +- cmd->default_settings.udev_fallback = 1; +-#endif +- + cmd->use_linear_target = find_config_tree_int(cmd, + "activation/use_linear_target", + DEFAULT_USE_LINEAR_TARGET); diff --git a/lvm2-2_02_99-lvmetad-fix-a-race-in-metadata-update.patch b/lvm2-2_02_99-lvmetad-fix-a-race-in-metadata-update.patch new file mode 100644 index 0000000..cd7f808 --- /dev/null +++ b/lvm2-2_02_99-lvmetad-fix-a-race-in-metadata-update.patch @@ -0,0 +1,83 @@ +commit 6283dc16f6ad54a08aae5f5d0c43bb4973e0ffc1 +Author: Peter Rajnoha +Date: Tue May 14 11:05:31 2013 +0200 + + lvm2-2_02_99-lvmetad-fix-a-race-in-metadata-update.patch +--- + WHATS_NEW | 1 + + daemons/lvmetad/lvmetad-core.c | 18 ++++++++++-------- + 2 files changed, 11 insertions(+), 8 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 20a8125..35c5e43 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,6 @@ + Version 2.02.99 - + =================================== ++ Fix a crash-inducing race condition in lvmetad while updating metadata. + Fix possible race while removing metadata from lvmetad. + Fix possible deadlock when querying and updating lvmetad at the same time. + Avoid a global lock in pvs when lvmetad is in use. +diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c +index fed9296..1e7470b 100644 +--- a/daemons/lvmetad/lvmetad-core.c ++++ b/daemons/lvmetad/lvmetad-core.c +@@ -450,7 +450,8 @@ static response vg_lookup(lvmetad_state *s, request r) + + DEBUGLOG(s, "vg_lookup: updated uuid = %s, name = %s", uuid, name); + +- if (!uuid) ++ /* Check the name here. */ ++ if (!uuid || !name) + return reply_unknown("VG not found"); + + cft = lock_vg(s, uuid); +@@ -679,16 +680,14 @@ static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid + + lock_vgid_to_metadata(s); + old = dm_hash_lookup(s->vgid_to_metadata, _vgid); ++ oldname = dm_hash_lookup(s->vgid_to_vgname, _vgid); + unlock_vgid_to_metadata(s); + lock_vg(s, _vgid); + + seq = dm_config_find_int(metadata, "metadata/seqno", -1); + +- if (old) { ++ if (old) + haveseq = dm_config_find_int(old->root, "metadata/seqno", -1); +- oldname = dm_hash_lookup(s->vgid_to_vgname, _vgid); +- assert(oldname); +- } + + if (seq < 0) + goto out; +@@ -740,7 +739,7 @@ static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid + if (haveseq >= 0 && haveseq < seq) { + INFO(s, "Updating metadata for %s at %d to %d", _vgid, haveseq, seq); + /* temporarily orphan all of our PVs */ +- remove_metadata(s, vgid, 1); ++ update_pvid_to_vgid(s, old, "#orphan", 0); + } + + lock_vgid_to_metadata(s); +@@ -750,14 +749,17 @@ static int update_metadata(lvmetad_state *s, const char *name, const char *_vgid + dm_hash_insert(s->vgid_to_metadata, vgid, cft) && + dm_hash_insert(s->vgid_to_vgname, vgid, cfgname) && + dm_hash_insert(s->vgname_to_vgid, name, (void*) vgid)) ? 1 : 0; ++ ++ if (retval && oldname && strcmp(name, oldname)) ++ dm_hash_remove(s->vgname_to_vgid, oldname); ++ + unlock_vgid_to_metadata(s); + + if (retval) +- /* FIXME: What should happen when update fails */ + retval = update_pvid_to_vgid(s, cft, vgid, 1); + + unlock_pvid_to_vgid(s); +-out: ++out: /* FIXME: We should probably abort() on partial failures. */ + if (!retval && cft) + dm_config_destroy(cft); + unlock_vg(s, _vgid); diff --git a/lvm2-2_02_99-various-thin-support-related-fixes.patch b/lvm2-2_02_99-various-thin-support-related-fixes.patch new file mode 100644 index 0000000..ff76e34 --- /dev/null +++ b/lvm2-2_02_99-various-thin-support-related-fixes.patch @@ -0,0 +1,2066 @@ +commit 426de775ff89de7fd9a01f3168e18a711615d23a +Author: Peter Rajnoha +Date: Tue May 14 11:02:19 2013 +0200 + + lvm2-2_02_99-various-thin-support-related-fixes.patch +--- + WHATS_NEW | 11 ++ + WHATS_NEW_DM | 1 + + doc/example.conf.in | 27 +++++ + lib/activate/activate.h | 1 + + lib/config/defaults.h | 3 + + lib/format_text/archiver.c | 24 ++-- + lib/format_text/archiver.h | 4 +- + lib/metadata/lv.c | 5 + + lib/metadata/lv.h | 1 + + lib/metadata/lv_manip.c | 11 +- + lib/metadata/metadata-exported.h | 5 +- + lib/metadata/metadata.h | 2 + + lib/metadata/mirror.c | 9 +- + lib/metadata/thin_manip.c | 18 +++ + lib/report/columns.h | 2 +- + lib/report/properties.c | 22 ++-- + lib/report/report.c | 3 +- + lib/thin/thin.c | 93 +++++++++++---- + libdm/libdm-deptree.c | 5 + + liblvm/lvm_lv.c | 8 +- + man/lvconvert.8.in | 114 +++++++++++++++--- + man/lvcreate.8.in | 18 +-- + man/vgcfgrestore.8.in | 9 ++ + test/api/thin_percent.c | 10 ++ + test/api/thin_percent.sh | 2 +- + test/shell/lvconvert-thin.sh | 81 ++++++++++--- + test/shell/thin-defaults.sh | 35 ++++++ + test/shell/thin-restore.sh | 34 ++++++ + tools/args.h | 1 + + tools/commands.h | 10 +- + tools/lvchange.c | 8 +- + tools/lvconvert.c | 245 +++++++++++++++++++++++++-------------- + tools/lvcreate.c | 117 +++++-------------- + tools/toollib.c | 195 +++++++++++++++++++++++++++++++ + tools/toollib.h | 14 +++ + tools/vgcfgrestore.c | 5 +- + 36 files changed, 860 insertions(+), 293 deletions(-) + +diff --git a/WHATS_NEW b/WHATS_NEW +index 03345d8..5231745 100644 +--- a/WHATS_NEW ++++ b/WHATS_NEW +@@ -1,5 +1,16 @@ + Version 2.02.99 - + =================================== ++ Fix lvm2app to return all property sizes in bytes. ++ Add lvm.conf option global/thin_disabled_features. ++ Add lvconvert support to swap thin pool metadata volume. ++ Implement internal function detach_pool_metadata_lv(). ++ Fix lvm2app and return lvseg discards property as string. ++ Allow forced vgcfgrestore of lvm2 metadata with thin volumes. ++ Add lvm.conf thin pool defs thin_pool_{chunk_size|discards|zero}. ++ Support discards for non-power-of-2 thin pool chunks. ++ Support allocation of pool metadata with lvconvert command. ++ Move common functionality for thin lvcreate and lvconvert to toollib. ++ Use lv_is_active() instead of lv_info() call. + Synchronize with udev in pvscan --cache and fix dangling udev_sync cookies. + Fix autoactivation to not autoactivate VG/LV on each change of the PVs used. + Skip mlocking [vectors] on arm architecture. +diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM +index 3f8d9c9..e0b8d51 100644 +--- a/WHATS_NEW_DM ++++ b/WHATS_NEW_DM +@@ -1,5 +1,6 @@ + Version 1.02.78 - + =================================== ++ Automatically deactivate failed preloaded dm tree node. + Fix dm_task_set_cookie to properly process udev flags if udev_sync disabled. + + Version 1.02.77 - 15th October 2012 +diff --git a/doc/example.conf.in b/doc/example.conf.in +index 442ad64..1bf89d3 100644 +--- a/doc/example.conf.in ++++ b/doc/example.conf.in +@@ -232,6 +232,23 @@ allocation { + # 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 ++ ++ # Specify the minimal chunk size (in KB) for thin pool volumes. ++ # Use of the larger chunk size may improve perfomance 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. ++ # Select one of "ignore", "nopassdown", "passdown" ++ # thin_pool_discards = "passdown" ++ ++ # Set to 0, to disable zeroing of thin pool data chunks before their ++ # first use. ++ # N.B. zeroing larger thin pool chunk size degrades performance. ++ # thin_pool_zero = 1 + } + + # This section that allows you to configure the nature of the +@@ -507,6 +524,16 @@ global { + # String with options passed with thin_check command. By default, + # option '-q' is for quiet output. + thin_check_options = [ "-q" ] ++ ++ # If set, given features are not used by thin driver. ++ # This can be helpful not just for testing, but i.e. allows to avoid ++ # using problematic implementation of some thin feature. ++ # Features: ++ # block_size ++ # discards ++ # discards_non_power_2 ++ # ++ # thin_disabled_features = [ "discards", "block_size" ] + } + + activation { +diff --git a/lib/activate/activate.h b/lib/activate/activate.h +index ba24d2a..0a0c97e 100644 +--- a/lib/activate/activate.h ++++ b/lib/activate/activate.h +@@ -51,6 +51,7 @@ enum { + THIN_FEATURE_EXTERNAL_ORIGIN = (1 << 1), + THIN_FEATURE_HELD_ROOT = (1 << 2), + THIN_FEATURE_BLOCK_SIZE = (1 << 3), ++ THIN_FEATURE_DISCARDS_NON_POWER_2 = (1 << 4), + }; + + void set_activation(int activation); +diff --git a/lib/config/defaults.h b/lib/config/defaults.h +index 9730a2d..348fa75 100644 +--- a/lib/config/defaults.h ++++ b/lib/config/defaults.h +@@ -69,6 +69,9 @@ + #define DEFAULT_THIN_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */ + #define DEFAULT_THIN_POOL_MIN_METADATA_SIZE 2048 /* KB */ + #define DEFAULT_THIN_POOL_OPTIMAL_SIZE (128 * 1024 * 1024) /* KB */ ++#define DEFAULT_THIN_POOL_CHUNK_SIZE 64 /* KB */ ++#define DEFAULT_THIN_POOL_DISCARDS "passdown" ++#define DEFAULT_THIN_POOL_ZERO 1 + + #define DEFAULT_UMASK 0077 + +diff --git a/lib/format_text/archiver.c b/lib/format_text/archiver.c +index ccefb4c..8599e3b 100644 +--- a/lib/format_text/archiver.c ++++ b/lib/format_text/archiver.c +@@ -347,7 +347,7 @@ int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg) + + /* ORPHAN and VG locks held before calling this */ + int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, +- const char *file) ++ const char *file, int force) + { + struct volume_group *vg; + int missing_pvs, r = 0; +@@ -360,14 +360,20 @@ int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, + return_0; + + /* FIXME: Restore support is missing for now */ +- dm_list_iterate_items(lvl, &vg->lvs) ++ dm_list_iterate_items(lvl, &vg->lvs) { + if (lv_is_thin_type(lvl->lv)) { +- log_error("Cannot restore Volume Group %s with " +- "thin logical volumes. " +- "(not yet supported).", vg->name); +- r = 0; +- goto out; ++ if (!force) { ++ log_error("Consider using option --force to restore " ++ "Volume Group %s with thin volumes.", ++ vg->name); ++ goto out; ++ } else { ++ log_warn("WARNING: Forced restore of Volume Group " ++ "%s with thin volumes.", vg->name); ++ break; ++ } + } ++ } + + missing_pvs = vg_missing_pv_count(vg); + if (missing_pvs == 0) +@@ -381,7 +387,7 @@ out: + return r; + } + +-int backup_restore(struct cmd_context *cmd, const char *vg_name) ++int backup_restore(struct cmd_context *cmd, const char *vg_name, int force) + { + char path[PATH_MAX]; + +@@ -391,7 +397,7 @@ int backup_restore(struct cmd_context *cmd, const char *vg_name) + return 0; + } + +- return backup_restore_from_file(cmd, vg_name, path); ++ return backup_restore_from_file(cmd, vg_name, path, force); + } + + int backup_to_file(const char *file, const char *desc, struct volume_group *vg) +diff --git a/lib/format_text/archiver.h b/lib/format_text/archiver.h +index 7346f93..ddff687 100644 +--- a/lib/format_text/archiver.h ++++ b/lib/format_text/archiver.h +@@ -53,8 +53,8 @@ struct volume_group *backup_read_vg(struct cmd_context *cmd, + const char *vg_name, const char *file); + int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg); + int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name, +- const char *file); +-int backup_restore(struct cmd_context *cmd, const char *vg_name); ++ const char *file, int force); ++int backup_restore(struct cmd_context *cmd, const char *vg_name, int force); + + int backup_to_file(const char *file, const char *desc, struct volume_group *vg); + +diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c +index 4032f34..34b428a 100644 +--- a/lib/metadata/lv.c ++++ b/lib/metadata/lv.c +@@ -123,6 +123,11 @@ char *lvseg_segtype_dup(struct dm_pool *mem, const struct lv_segment *seg) + return dm_pool_strdup(mem, seg->segtype->ops->name(seg)); + } + ++char *lvseg_discards_dup(struct dm_pool *mem, const struct lv_segment *seg) ++{ ++ return dm_pool_strdup(mem, get_pool_discards_name(seg->discards)); ++} ++ + uint64_t lvseg_chunksize(const struct lv_segment *seg) + { + uint64_t size; +diff --git a/lib/metadata/lv.h b/lib/metadata/lv.h +index 0daab62..838b002 100644 +--- a/lib/metadata/lv.h ++++ b/lib/metadata/lv.h +@@ -74,6 +74,7 @@ uint64_t lvseg_start(const struct lv_segment *seg); + uint64_t lvseg_size(const struct lv_segment *seg); + uint64_t lvseg_chunksize(const struct lv_segment *seg); + char *lvseg_segtype_dup(struct dm_pool *mem, const struct lv_segment *seg); ++char *lvseg_discards_dup(struct dm_pool *mem, const struct lv_segment *seg); + char *lvseg_tags_dup(const struct lv_segment *seg); + char *lvseg_devices(struct dm_pool *mem, const struct lv_segment *seg); + char *lvseg_seg_pe_ranges(struct dm_pool *mem, const struct lv_segment *seg); +diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c +index d469fe8..a59e03f 100644 +--- a/lib/metadata/lv_manip.c ++++ b/lib/metadata/lv_manip.c +@@ -4217,7 +4217,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, struct l + struct logical_volume *pool_lv; + struct lv_list *lvl; + int origin_active = 0; +- struct lvinfo info; + + if (new_lv_name && find_lv_in_vg(vg, new_lv_name)) { + log_error("Logical volume \"%s\" already exists in " +@@ -4349,12 +4348,12 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, struct l + log_warn("WARNING: See global/mirror_segtype_default in lvm.conf."); + } + +- if (!lv_info(cmd, org, 0, &info, 0, 0)) { ++ if (!lv_is_active(org)) { + log_error("Check for existence of active snapshot " + "origin '%s' failed.", org->name); + return NULL; + } +- origin_active = info.exists; ++ origin_active = 1; + + if (vg_is_clustered(vg) && + !lv_is_active_exclusive_locally(org)) { +@@ -4696,8 +4695,8 @@ revert_new_lv: + return NULL; + } + +-int lv_create_single(struct volume_group *vg, +- struct lvcreate_params *lp) ++struct logical_volume *lv_create_single(struct volume_group *vg, ++ struct lvcreate_params *lp) + { + struct logical_volume *lv; + +@@ -4725,5 +4724,5 @@ int lv_create_single(struct volume_group *vg, + out: + log_print_unless_silent("Logical volume \"%s\" created", lv->name); + +- return 1; ++ return lv; + } +diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h +index d149f95..11ca5be 100644 +--- a/lib/metadata/metadata-exported.h ++++ b/lib/metadata/metadata-exported.h +@@ -609,6 +609,7 @@ struct lvcreate_params { + uint32_t mirrors; /* mirror */ + + const struct segment_type *segtype; /* all */ ++ unsigned target_attr; /* all */ + + /* size */ + uint32_t extents; /* all */ +@@ -625,8 +626,8 @@ struct lvcreate_params { + struct dm_list tags; /* all */ + }; + +-int lv_create_single(struct volume_group *vg, +- struct lvcreate_params *lp); ++struct logical_volume *lv_create_single(struct volume_group *vg, ++ struct lvcreate_params *lp); + + /* + * Functions for layer manipulation +diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h +index 7bc7eaf..19bf742 100644 +--- a/lib/metadata/metadata.h ++++ b/lib/metadata/metadata.h +@@ -460,6 +460,8 @@ int fixup_imported_mirrors(struct volume_group *vg); + */ + int attach_pool_metadata_lv(struct lv_segment *pool_seg, + struct logical_volume *pool_metadata_lv); ++int detach_pool_metadata_lv(struct lv_segment *pool_seg, ++ struct logical_volume **pool_metadata_lv); + int attach_pool_data_lv(struct lv_segment *pool_seg, + struct logical_volume *pool_data_lv); + int attach_pool_lv(struct lv_segment *seg, struct logical_volume *pool_lv, +diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c +index c4683df..084c93a 100644 +--- a/lib/metadata/mirror.c ++++ b/lib/metadata/mirror.c +@@ -282,7 +282,6 @@ static int _init_mirror_log(struct cmd_context *cmd, + struct dm_list *tags, int remove_on_failure) + { + struct str_list *sl; +- struct lvinfo info; + uint64_t orig_status = log_lv->status; + int was_active = 0; + +@@ -298,14 +297,13 @@ static int _init_mirror_log(struct cmd_context *cmd, + } + + /* If the LV is active, deactivate it first. */ +- if (lv_info(cmd, log_lv, 0, &info, 0, 0) && info.exists) { +- (void)deactivate_lv(cmd, log_lv); ++ if (lv_is_active(log_lv)) { + /* + * FIXME: workaround to fail early + * Ensure that log is really deactivated because deactivate_lv + * on cluster do not fail if there is log_lv with different UUID. + */ +- if (lv_info(cmd, log_lv, 0, &info, 0, 0) && info.exists) { ++ if (!deactivate_lv(cmd, log_lv)) { + log_error("Aborting. Unable to deactivate mirror log."); + goto revert_new_lv; + } +@@ -1714,7 +1712,6 @@ int remove_mirror_log(struct cmd_context *cmd, + int force) + { + percent_t sync_percent; +- struct lvinfo info; + struct volume_group *vg = lv->vg; + + /* Unimplemented features */ +@@ -1724,7 +1721,7 @@ int remove_mirror_log(struct cmd_context *cmd, + } + + /* Had disk log, switch to core. */ +- if (lv_info(cmd, lv, 0, &info, 0, 0) && info.exists) { ++ if (lv_is_active(lv)) { + if (!lv_mirror_percent(cmd, lv, 0, &sync_percent, + NULL)) { + log_error("Unable to determine mirror sync status."); +diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c +index e7e96df..e2762a0 100644 +--- a/lib/metadata/thin_manip.c ++++ b/lib/metadata/thin_manip.c +@@ -30,6 +30,24 @@ int attach_pool_metadata_lv(struct lv_segment *pool_seg, struct logical_volume * + 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; ++ ++ if (!lv || !lv_is_thin_pool_metadata(lv) || ++ !remove_seg_from_segs_using_this_lv(lv, pool_seg)) { ++ log_error(INTERNAL_ERROR "LV %s is invalid thin pool.", pool_seg->lv->name); ++ return 0; ++ } ++ ++ lv_set_visible(lv); ++ lv->status &= ~THIN_POOL_METADATA; ++ *metadata_lv = lv; ++ pool_seg->metadata_lv = NULL; ++ ++ 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)) +diff --git a/lib/report/columns.h b/lib/report/columns.h +index 6299a2b..11b3f3f 100644 +--- a/lib/report/columns.h ++++ b/lib/report/columns.h +@@ -140,7 +140,7 @@ FIELD(SEGS, seg, NUM, "Region", region_size, 6, size32, region_size, "For mirror + FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, chunksize, "For snapshots, the unit of data used when tracking changes.", 0) + FIELD(SEGS, seg, NUM, "Chunk", list, 5, chunksize, chunk_size, "For snapshots, the unit of data used when tracking changes.", 0) + FIELD(SEGS, seg, NUM, "#Thins", list, 4, thincount, thin_count, "For thin pools, the number of thin volumes in this pool.", 0) +-FIELD(SEGS, seg, NUM, "Discards", list, 8, discards, discards, "For thin pools, how discards are handled.", 0) ++FIELD(SEGS, seg, STR, "Discards", list, 8, discards, discards, "For thin pools, how discards are handled.", 0) + FIELD(SEGS, seg, NUM, "Zero", list, 4, thinzero, zero, "For thin pools, if zeroing is enabled.", 0) + FIELD(SEGS, seg, NUM, "TransId", list, 4, transactionid, transaction_id, "For thin pools, the transaction id.", 0) + FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, seg_start, "Offset within the LV to the start of the segment in current units.", 0) +diff --git a/lib/report/properties.c b/lib/report/properties.c +index c4f6ab9..a871d66 100644 +--- a/lib/report/properties.c ++++ b/lib/report/properties.c +@@ -183,7 +183,7 @@ GET_LV_NUM_PROPERTY_FN(seg_count, dm_list_size(&lv->segments)) + #define _seg_count_set _not_implemented_set + GET_LV_STR_PROPERTY_FN(origin, lv_origin_dup(lv->vg->vgmem, lv)) + #define _origin_set _not_implemented_set +-GET_LV_NUM_PROPERTY_FN(origin_size, lv_origin_size(lv)) ++GET_LV_NUM_PROPERTY_FN(origin_size, (SECTOR_SIZE * lv_origin_size(lv))) + #define _origin_size_set _not_implemented_set + GET_LV_NUM_PROPERTY_FN(snap_percent, _snap_percent(lv)) + #define _snap_percent_set _not_implemented_set +@@ -231,7 +231,7 @@ GET_VG_NUM_PROPERTY_FN(vg_free, (SECTOR_SIZE * vg_free(vg))) + #define _vg_free_set _not_implemented_set + GET_VG_STR_PROPERTY_FN(vg_sysid, vg_system_id_dup(vg)) + #define _vg_sysid_set _not_implemented_set +-GET_VG_NUM_PROPERTY_FN(vg_extent_size, vg->extent_size) ++GET_VG_NUM_PROPERTY_FN(vg_extent_size, (SECTOR_SIZE * vg->extent_size)) + #define _vg_extent_size_set _not_implemented_set + GET_VG_NUM_PROPERTY_FN(vg_extent_count, vg->extent_count) + #define _vg_extent_count_set _not_implemented_set +@@ -267,17 +267,17 @@ GET_LVSEG_STR_PROPERTY_FN(segtype, lvseg_segtype_dup(lvseg->lv->vg->vgmem, lvseg + #define _segtype_set _not_implemented_set + GET_LVSEG_NUM_PROPERTY_FN(stripes, lvseg->area_count) + #define _stripes_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(stripesize, lvseg->stripe_size) ++GET_LVSEG_NUM_PROPERTY_FN(stripesize, (SECTOR_SIZE * lvseg->stripe_size)) + #define _stripesize_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(stripe_size, lvseg->stripe_size) ++GET_LVSEG_NUM_PROPERTY_FN(stripe_size, (SECTOR_SIZE * lvseg->stripe_size)) + #define _stripe_size_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(regionsize, lvseg->region_size) ++GET_LVSEG_NUM_PROPERTY_FN(regionsize, (SECTOR_SIZE * lvseg->region_size)) + #define _regionsize_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(region_size, lvseg->region_size) ++GET_LVSEG_NUM_PROPERTY_FN(region_size, (SECTOR_SIZE * lvseg->region_size)) + #define _region_size_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(chunksize, lvseg_chunksize(lvseg)) ++GET_LVSEG_NUM_PROPERTY_FN(chunksize, (SECTOR_SIZE * lvseg_chunksize(lvseg))) + #define _chunksize_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(chunk_size, lvseg_chunksize(lvseg)) ++GET_LVSEG_NUM_PROPERTY_FN(chunk_size, (SECTOR_SIZE * lvseg_chunksize(lvseg))) + #define _chunk_size_set _not_implemented_set + GET_LVSEG_NUM_PROPERTY_FN(thin_count, dm_list_size(&lvseg->lv->segs_using_this_lv)) + #define _thin_count_set _not_implemented_set +@@ -285,9 +285,9 @@ GET_LVSEG_NUM_PROPERTY_FN(zero, lvseg->zero_new_blocks) + #define _zero_set _not_implemented_set + GET_LVSEG_NUM_PROPERTY_FN(transaction_id, lvseg->transaction_id) + #define _transaction_id_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(discards, lvseg->discards) ++GET_LVSEG_STR_PROPERTY_FN(discards, lvseg_discards_dup(lvseg->lv->vg->vgmem, lvseg)) + #define _discards_set _not_implemented_set +-GET_LVSEG_NUM_PROPERTY_FN(seg_start, lvseg_start(lvseg)) ++GET_LVSEG_NUM_PROPERTY_FN(seg_start, (SECTOR_SIZE * lvseg_start(lvseg))) + #define _seg_start_set _not_implemented_set + GET_LVSEG_NUM_PROPERTY_FN(seg_start_pe, lvseg->le) + #define _seg_start_pe_set _not_implemented_set +@@ -305,7 +305,7 @@ GET_LVSEG_STR_PROPERTY_FN(devices, lvseg_devices(lvseg->lv->vg->vgmem, lvseg)) + /* PVSEG */ + GET_PVSEG_NUM_PROPERTY_FN(pvseg_start, pvseg->pe) + #define _pvseg_start_set _not_implemented_set +-GET_PVSEG_NUM_PROPERTY_FN(pvseg_size, pvseg->len) ++GET_PVSEG_NUM_PROPERTY_FN(pvseg_size, (SECTOR_SIZE * pvseg->len)) + #define _pvseg_size_set _not_implemented_set + + +diff --git a/lib/report/report.c b/lib/report/report.c +index eeca282..b1e2bc1 100644 +--- a/lib/report/report.c ++++ b/lib/report/report.c +@@ -830,7 +830,6 @@ static int _snpercent_disp(struct dm_report *rh __attribute__((unused)), struct + const void *data, void *private __attribute__((unused))) + { + const struct logical_volume *lv = (const struct logical_volume *) data; +- struct lvinfo info; + percent_t snap_percent; + uint64_t *sortval; + char *repstr; +@@ -847,7 +846,7 @@ static int _snpercent_disp(struct dm_report *rh __attribute__((unused)), struct + } + + if ((!lv_is_cow(lv) && !lv_is_merging_origin(lv)) || +- !lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) || !info.exists) { ++ !lv_is_active(lv)) { + *sortval = UINT64_C(0); + dm_report_field_set_value(field, "", sortval); + return 1; +diff --git a/lib/thin/thin.c b/lib/thin/thin.c +index 2b6c71f..d7c45ca 100644 +--- a/lib/thin/thin.c ++++ b/lib/thin/thin.c +@@ -37,6 +37,9 @@ + log_error(t " segment %s of logical volume %s.", ## p, \ + dm_config_parent_name(sn), seg->lv->name), 0; + ++/* TODO: using static field here, maybe should be a part of segment_type */ ++static unsigned _feature_mask; ++ + static int _thin_target_present(struct cmd_context *cmd, + const struct lv_segment *seg, + unsigned *attributes); +@@ -282,10 +285,15 @@ static int _thin_pool_add_target_line(struct dev_manager *dm, + return_0; + + if (attr & THIN_FEATURE_DISCARDS) { ++ /* Use ignore for discards ignore or non-power-of-2 chunk_size and <1.5 target */ + /* FIXME: Check whether underlying dev supports discards */ +- if (!dm_tree_node_set_thin_pool_discard(node, +- seg->discards == THIN_DISCARDS_IGNORE, +- seg->discards == THIN_DISCARDS_NO_PASSDOWN)) ++ if (((!(attr & THIN_FEATURE_DISCARDS_NON_POWER_2) && ++ (seg->chunk_size & (seg->chunk_size - 1))) || ++ (seg->discards == THIN_DISCARDS_IGNORE)) && ++ !dm_tree_node_set_thin_pool_discard(node, 1, 0)) ++ return_0; ++ else if (!dm_tree_node_set_thin_pool_discard(node, 0, ++ (seg->discards == THIN_DISCARDS_NO_PASSDOWN))) + return_0; + } else if (seg->discards != THIN_DISCARDS_IGNORE) + log_warn_suppress(_no_discards++, "WARNING: Thin pool target does " +@@ -534,10 +542,28 @@ static int _thin_target_present(struct cmd_context *cmd, + const struct lv_segment *seg, + unsigned *attributes) + { ++ /* List of features with their kernel target version */ ++ static const struct feature { ++ uint32_t maj; ++ uint32_t min; ++ unsigned thin_feature; ++ const char *feature; ++ } const _features[] = { ++ { 1, 1, THIN_FEATURE_DISCARDS, "discards" }, ++ { 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" }, ++ }; ++ ++ static const char _lvmconf[] = "global/thin_disabled_features"; + static int _checked = 0; + static int _present = 0; +- static int _attrs = 0; ++ static unsigned _attrs = 0; + uint32_t maj, min, patchlevel; ++ unsigned i; ++ const struct dm_config_node *cn; ++ const struct dm_config_value *cv; ++ const char *str; + + if (!_checked) { + _present = target_present(cmd, THIN_MODULE, 1); +@@ -547,29 +573,46 @@ static int _thin_target_present(struct cmd_context *cmd, + return 0; + } + +- if (maj >=1 && min >= 1) +- _attrs |= THIN_FEATURE_DISCARDS; +- else +- /* FIXME Log this as WARNING later only if the user asked for the feature to be used but it's not present */ +- log_debug("Target " THIN_MODULE " does not support discards."); +- +- if (maj >=1 && min >= 1) +- _attrs |= THIN_FEATURE_EXTERNAL_ORIGIN; +- else +- /* FIXME Log this as WARNING later only if the user asked for the feature to be used but it's not present */ +- log_debug("Target " THIN_MODULE " does not support external origins."); +- +- if (maj >=1 && min >= 4) +- _attrs |= THIN_FEATURE_BLOCK_SIZE; +- else +- /* FIXME Log this as WARNING later only if the user asked for the feature to be used but it's not present */ +- log_debug("Target " THIN_MODULE " does not support non power of 2 block sizes."); ++ for (i = 0; i < sizeof(_features)/sizeof(*_features); i++) ++ if (maj >= _features[i].maj && min >= _features[i].min) ++ _attrs |= _features[i].thin_feature; ++ else ++ log_very_verbose("Target " THIN_MODULE " does not support %s.", ++ _features[i].feature); + + _checked = 1; + } + +- if (attributes) +- *attributes = _attrs; ++ if (attributes) { ++ if (!_feature_mask) { ++ /* Support runtime lvm.conf changes, N.B. avoid 32 feature */ ++ if ((cn = find_config_tree_node(cmd, _lvmconf))) { ++ for (cv = cn->v; cv; cv = cv->next) { ++ if (cv->type != DM_CFG_STRING) { ++ log_error("Ignoring invalid string in config file %s.", ++ _lvmconf); ++ continue; ++ } ++ str = cv->v.str; ++ if (!*str) { ++ log_error("Ignoring empty string in config file %s.", ++ _lvmconf); ++ continue; ++ } ++ for (i = 0; i < sizeof(_features)/sizeof(*_features); i++) ++ if (strcasecmp(str, _features[i].feature) == 0) ++ _feature_mask |= _features[i].thin_feature; ++ } ++ } ++ _feature_mask = ~_feature_mask; ++ for (i = 0; i < sizeof(_features)/sizeof(*_features); i++) ++ if ((_attrs & _features[i].thin_feature) && ++ !(_feature_mask & _features[i].thin_feature)) ++ log_very_verbose("Target "THIN_MODULE " %s support disabled by %s", ++ _features[i].feature, _lvmconf); ++ } ++ *attributes = _attrs & _feature_mask; ++ } + + return _present; + } +@@ -671,5 +714,9 @@ int init_multiple_segtypes(struct cmd_context *cmd, struct segtype_library *segl + log_very_verbose("Initialised segtype: %s", segtype->name); + } + ++ ++ /* Reset mask for recalc */ ++ _feature_mask = 0; ++ + return 1; + } +diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c +index 096eba2..e4a574d 100644 +--- a/libdm/libdm-deptree.c ++++ b/libdm/libdm-deptree.c +@@ -2496,6 +2496,11 @@ int dm_tree_preload_children(struct dm_tree_node *dnode, + log_error("Unable to resume %s (%" PRIu32 + ":%" PRIu32 ")", child->name, child->info.major, + child->info.minor); ++ 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 ++ ":%" PRIu32 ")", child->name, child->info.major, ++ child->info.minor); + r = 0; + continue; + } +diff --git a/liblvm/lvm_lv.c b/liblvm/lvm_lv.c +index d47a857..b2a604d 100644 +--- a/liblvm/lvm_lv.c ++++ b/liblvm/lvm_lv.c +@@ -146,7 +146,7 @@ lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size) + { + struct lvcreate_params lp = { 0 }; + uint64_t extents; +- struct lv_list *lvl; ++ struct logical_volume *lv; + + if (vg_read_error(vg)) + return NULL; +@@ -162,11 +162,9 @@ lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size) + _lv_set_default_params(&lp, vg, name, extents); + if (!_lv_set_default_linear_params(vg->cmd, &lp)) + return_NULL; +- if (!lv_create_single(vg, &lp)) ++ if (!(lv = lv_create_single(vg, &lp))) + return_NULL; +- if (!(lvl = find_lv_in_vg(vg, name))) +- return NULL; +- return (lv_t) lvl->lv; ++ return (lv_t) lv; + } + + /* +diff --git a/man/lvconvert.8.in b/man/lvconvert.8.in +index 2659719..4fe9bdd 100644 +--- a/man/lvconvert.8.in ++++ b/man/lvconvert.8.in +@@ -40,7 +40,7 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot + .B lvconvert + .BR \-s | \-\-snapshot + .RB [ \-c | \-\-chunksize +-.IR ChunkSize ] ++.IR ChunkSize [ bBsSkK ]] + .RB [ \-h | \-? | \-\-help ] + .RB [ \-\-noudevsync ] + .RB [ \-v | \-\-verbose ] +@@ -59,17 +59,6 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot + .RB [ \-\-version ] + .IR LogicalVolume [ Path ]... + .sp +-.B lvconvert \-\-thinpool +-.IR ThinPoolLogicalVolume { Name | Path } +-.RB [ \-c | \-\-chunksize +-.IR ChunkSize ] +-.RB [ \-h | \-? | \-\-help ] +-.RB [ \-v | \-\-verbose ] +-.RB [ \-\-version ] +-.RB [ \-Z | \-\-zero +-.RI { y | n }] +-.IR ThinMetadataLogicalVolume { Name | Path } +-.sp + .B lvconvert \-\-repair + .RB [ \-h | \-? | \-\-help ] + .RB [ \-v | \-\-verbose ] +@@ -83,6 +72,31 @@ lvconvert \- convert a logical volume from linear to mirror or snapshot + .RB [ \-\-version ] + .IR LogicalVolume [ Path ] + .RI [ PhysicalVolume [ Path ]...] ++.sp ++.B lvconvert \-\-thinpool ++.IR ThinPoolLogicalVolume { Name | Path } ++.RB [ \-c | \-\-chunksize ++.IR ChunkSize [ bBsSkKmMgG ]] ++.RB [ \-\-discards ++.RI { ignore | nopassdown | passdown }] ++.RB [[ \-\-poolmetadata ++.IR ThinPoolMetadataLogicalVolume { Name | Path }] ++| ++.RB [ \-\-poolmetadatasize ++.IR ThinPoolMetadataSize [ bBsSkKmMgG ]] ++.RB [ \-r | \-\-readahead ++.RI { ReadAheadSectors | auto | none }] ++.RB [ \-\-stripes ++.I Stripes ++.RB [ \-I | \-\-stripesize ++.IR StripeSize ]]] ++.RB [ \-Z | \-\-zero ++.RI { y | n }] ++.RB [ \-h | \-? | \-\-help ] ++.RB [ \-v | \-\-verbose ] ++.RB [ \-\-version ] ++.RI [ PhysicalVolume [ Path ][ :PE [ -PE ]]...] ++.sp + + .SH DESCRIPTION + lvconvert is used to change the segment type (i.e. linear, mirror, etc) or +@@ -102,8 +116,9 @@ the freed extents come first from the specified PhysicalVolumes. + See \fBlvm\fP(8) for common options. + .br + Exactly one of +-.BR \-\-splitmirrors ", " \-\-mirrors ", " \-\-repair ", " \-\-snapshot +-or \fB\-\-merge\fP arguments is required. ++.BR \-\-merge ", " \-\-mirrors ", " \-\-repair ", " \-\-replace ++.RB ", " \-\-snapshot ", " \-\-splitmirrors " or " \-\-thinpool ++arguments is required. + .TP + .BR \-m ", " \-\-mirrors " " \fIMirrors + Specifies the degree of the mirror you wish to create. +@@ -170,12 +185,28 @@ implementation and not with the original device-mapper mirror implementation. + Create a snapshot from existing logical volume using another + existing logical volume as its origin. + .TP +-.BR \-c ", " \-\-chunksize " " \fIChunkSize +-Power of 2 chunk size for the snapshot logical volume between 4KiB and 512KiB. ++.BR \-c ", " \-\-chunksize " " \fIChunkSize [ \fIbBsSkKmMgG ] ++Gives the size of chunk for snapshot and thin pool logical volumes. ++For snapshots the value must be power of 2 between 4KiB and 512KiB ++and the default value is 4. ++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 128MB, ++if the pool metadata size is not specified. ++Older dm thin pool target version (<1.4) requires the value to be power of 2. ++The newer version requires to be the multiple of 64KiB, however discard is ++not supported for non power of 2 values. ++Default unit is in kilobytes. ++.TP ++.BR \-\-discards " {" \fIignore | \fInopassdown | \fIpassdown } ++Sets discards behavior for thin pool. ++Default is \fIpassdown\fP. + .TP + .BR \-Z ", " \-\-zero " {" \fIy | \fIn } + Controls zeroing of the first KB of data in the snapshot. + If the volume is read-only the snapshot will not be zeroed. ++For thin pool volumes it controls zeroing of provisioned blocks. ++Note: Provisioning of large zeroed chunks impacts performance. + .TP + .B \-\-merge + Merges a snapshot into its origin volume or merges a raid1 image that has +@@ -195,6 +226,35 @@ merge finishes, the merged snapshot is removed. Multiple snapshots may + be specified on the commandline or a @tag may be used to specify + multiple snapshots be merged to their respective origin. + .TP ++.BR \-\-poolmetadata " " \fIThinPoolMetadataLogicalVolume { \fIName | \fIPath } ++Specifies thin pool metadata logical volume. ++The size should be in between 2MiB and 16GiB. ++Thin pool is specified with the option ++\fB\-\-thinpool\fP. ++When the specified thin pool already exists, ++the thin pool's metadata volume will be swapped with the given LV. ++Properties of the thin pool like chunk size, discards or zero ++are preserved by default in this case. ++It can be useful for thin pool metadata repair or its offline resize, ++since the content of metadata becomes accessible for ++thin provisioning tools \fBthin_dump\fP(8) and \fBthin_restore\fP(8). ++.TP ++.BR \-\-poolmetadatasize " " \fIThinPoolMetadataSize [ \fIbBsSkKmMgG ] ++Sets the size of thin pool's metadata logical volume, ++if the pool metadata volume is undefined. ++Thin pool is specified with the option ++\fB\-\-thinpool\fP. ++Supported value is in the range between 2MiB and 16GiB. ++The default value is estimated with this formula ++(Pool_LV_size / Pool_LV_chunk_size * 64b). ++Default unit is megabytes. ++.TP ++.IR \fB\-r ", " \fB\-\-readahead " {" ReadAheadSectors | auto | none } ++Sets read ahead sector count of thin pool metadata logical volume. ++The default value is "auto" which allows the kernel to choose ++a suitable value automatically. ++"None" is equivalent to specifying zero. ++.TP + .B \-\-repair + Repair a mirror after suffering a disk failure. The mirror will be brought back + into a consistent state. By default, the original number of mirrors will be +@@ -210,6 +270,24 @@ Remove the specified device (\fIPhysicalVolume\fP) and replace it with one + that is available in the volume group or from the specific list provided. + This option is only available to RAID segment types + (e.g. "raid1", "raid5", etc). ++.TP ++.BR \-\-stripes " " \fIStripes ++Gives the number of stripes. ++This is equal to the number of physical volumes to scatter ++the logical volume. ++.TP ++.BR \-I ", " \-\-stripesize " " \fIStripeSize ++Gives the number of kilobytes for the granularity of the stripes. ++.br ++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 ++.IR \fB\-\-thinpool " " ThinPoolLogicalVolume { Name | Path } ++Changes logical volume into a thin pool volume. The volume ++will store the pool's data. ++Thin pool metadata logical volume can be specified with the option ++\fB\-\-poolmetadata\fP or allocated with \fB\-\-poolmetadatasize\fP. + + .SH Examples + Converts the linear logical volume "vg00/lvol1" to a two-way mirror +@@ -304,4 +382,6 @@ available in the volume group. + .BR lvextend (8), + .BR lvreduce (8), + .BR lvdisplay (8), +-.BR lvscan (8) ++.BR lvscan (8), ++.BR thin_dump(8), ++.BR thin_restore(8) +diff --git a/man/lvcreate.8.in b/man/lvcreate.8.in +index fb54cc6..0616150 100644 +--- a/man/lvcreate.8.in ++++ b/man/lvcreate.8.in +@@ -53,11 +53,11 @@ lvcreate \- create a logical volume in an existing volume group + .RB [ \-t | \-\-test ] + .RB [ \-T | \-\-thin + .RB [ \-c | \-\-chunksize +-.IR ChunkSize ] ++.IR ChunkSize [ bBsSkKmMgG ]] + .RB [ \-\-discards + .RI { ignore | nopassdown | passdown }] + .RB [ \-\-poolmetadatasize +-.IR MetadataSize [ bBsSkKmMgG ]]] ++.IR ThinPoolMetadataSize [ bBsSkKmMgG ]]] + .RB [ \-\-thinpool + .IR ThinPoolLogicalVolume { Name | Path }] + .RB [ \-\-type +@@ -76,7 +76,7 @@ lvcreate \- create a logical volume in an existing volume group + .BR \-L | \-\-size + .IR LogicalVolumeSize [ bBsSkKmMgGtTpPeE ]] + .RB [ \-c | \-\-chunksize +-.IR ChunkSize ] ++.IR ChunkSize [ bBsSkK ]] + .RB [ \-\-noudevsync ] + .RB [ \-\-ignoremonitoring ] + .RB [ \-\-monitor " {" \fIy | \fIn }] +@@ -125,14 +125,14 @@ always assumed and it can't be overridden. If clustered locking is enabled, + \fB\-a\fIey\fR will activate exclusively on one node and \fB\-a\fIly\fR will + activate only on the local node. + .TP +-.BR \-c ", " \-\-chunksize " " \fIChunkSize ++.BR \-c ", " \-\-chunksize " " \fIChunkSize [ \fIbBsSkKmMgG ] + Gives the size of chunk for snapshot and thin pool logical volumes. + For snapshots the value must be power of 2 between 4KiB and 512KiB + and the default value is 4. + For thin pools the value must be between 64KiB and +-1048576KiB and the default value starts with 64 and scales ++1GiB and the default value starts with 64 and scales + up to fit the pool metadata size within 128MB, +-if the poolmetadata size is not specified. ++if the pool metadata size is not specified. + Older dm thin pool target version (<1.4) requires the value to be power of 2. + The newer version requires to be the multiple of 64KiB, however discard is + not supported for non power of 2 values. +@@ -144,7 +144,7 @@ logical volumes. Default is no contiguous allocation based + on a next free principle. + .TP + .BR \-\-discards " {" \fIignore | \fInopassdown | \fIpassdown } +-Set discards behavior. ++Set discards behavior for thin pool. + Default is \fIpassdown\fP. + .TP + .BR \-i ", " \-\-stripes " " \fIStripes +@@ -236,7 +236,7 @@ Set access permissions to read only or read and write. + .br + Default is read and write. + .TP +-.IR \fB\-\-poolmetadatasize " " MetadataSize [ bBsSkKmMgG ] ++.IR \fB\-\-poolmetadatasize " " ThinPoolMetadataSize [ bBsSkKmMgG ] + Set the size of thin pool's metadata logical volume. + Supported value is in range between 2MiB and 16GiB. + Default value is (Pool_LV_size / Pool_LV_chunk_size * 64b). +@@ -364,7 +364,7 @@ a parity drive for a total of 4 devices) and a stripesize of 64KiB: + .B lvcreate \-\-type raid5 \-L 5G \-i 3 \-I 64 \-n my_lv vg00 + + Creates 100MiB pool logical volume for thin provisioning +-build with 2 stripes 64KiB and chunk size 128KiB together with ++build with 2 stripes 64KiB and chunk size 256KiB together with + 1TiB thin provisioned logical volume "vg00/thin_lv": + .sp + .B lvcreate \-i 2 \-I 64 \-c 256 \-L100M \-T vg00/pool \-V 1T \-\-name thin_lv +diff --git a/man/vgcfgrestore.8.in b/man/vgcfgrestore.8.in +index 3b7b038..7b8dd59 100644 +--- a/man/vgcfgrestore.8.in ++++ b/man/vgcfgrestore.8.in +@@ -6,6 +6,7 @@ vgcfgrestore \- restore volume group descriptor area + .RB [ \-d | \-\-debug ] + .RB [ \-f | \-\-file + .RI < filename >] ++.RB [ \-\-force ] + .RB [ \-l [ l ]| \-\-list ] + .RB [ \-h | \-\-help ] + .RB [ \-M | \-\-metadatatype +@@ -32,6 +33,14 @@ May be used with the \fB\-f\fP option. Does not restore \fIVolumeGroupName\fP. + Name of LVM metadata backup file + Specifies a metadata backup or archive file to be used for restoring + VolumeGroupName. Often this file has been created with \fBvgcfgbackup\fP. ++.TP ++.B \-\-force ++To restore metadata with thin pool volumes, user currently ++needs to use this flag. The tool DOES NOT make any validation. ++.br ++WARNING: Restoring lvm2 metadata that are not matching thin pool ++kernel metadata may lead to the destruction of the pool content. ++Use with extreme caution. + .SH REPLACING PHYSICAL VOLUMES + \fBvgdisplay \-\-partial \-\-verbose\fP will show you the UUIDs and sizes of + any PVs that are no longer present. +diff --git a/test/api/thin_percent.c b/test/api/thin_percent.c +index 2c8b19b..ae511df 100644 +--- a/test/api/thin_percent.c ++++ b/test/api/thin_percent.c +@@ -23,6 +23,8 @@ int main(int argc, char *argv[]) + vg_t vg; + lv_t lv; + struct lvm_property_value v; ++ struct dm_list *lvsegs; ++ struct lvm_lvseg_list *lvl; + + handle = lvm_init(NULL); + assert(handle); +@@ -33,6 +35,14 @@ int main(int argc, char *argv[]) + lv = lvm_lv_from_name(vg, "pool"); + assert(lv); + ++ lvsegs = lvm_lv_list_lvsegs(lv); ++ assert(lvsegs && (dm_list_size(lvsegs) == 1)); ++ dm_list_iterate_items(lvl, lvsegs) { ++ v = lvm_lvseg_get_property(lvl->lvseg, "discards"); ++ assert(v.is_valid && v.is_string); ++ assert(strcmp(v.value.string, "passdown") == 0); ++ } ++ + v = lvm_lv_get_property(lv, "data_percent"); + assert(v.is_valid); + assert(v.value.integer == 25 * PERCENT_1); +diff --git a/test/api/thin_percent.sh b/test/api/thin_percent.sh +index 9287cf3..e14e807 100644 +--- a/test/api/thin_percent.sh ++++ b/test/api/thin_percent.sh +@@ -30,7 +30,7 @@ dd if=/dev/urandom of="$DM_DEV_DIR/$vg/thin" count=2 bs=256K + lvcreate -s $vg/thin -n snap + dd if=/dev/urandom of="$DM_DEV_DIR/$vg/snap" count=3 bs=256K + +-lvs $vg ++lvs -o+discards $vg + + aux apitest thin_percent $vg + +diff --git a/test/shell/lvconvert-thin.sh b/test/shell/lvconvert-thin.sh +index 97ccc09..4634aa2 100644 +--- a/test/shell/lvconvert-thin.sh ++++ b/test/shell/lvconvert-thin.sh +@@ -12,6 +12,13 @@ + + . lib/test + ++prepare_lvs() ++{ ++ lvremove -f $vg ++ lvcreate -L10M -n $lv1 $vg ++ lvcreate -L8M -n $lv2 $vg ++} ++ + # + # Main + # +@@ -19,25 +26,73 @@ aux have_thin 1 0 0 || skip + + aux prepare_pvs 4 64 + +-vgcreate $vg -s 64K $(cat DEVICES) ++# build one large PV ++vgcreate $vg1 $(cut -d ' ' -f -3 DEVICES) ++lvcreate -s -l 100%FREE -n $lv $vg1 --virtualsize 64T ++aux lvmconf 'devices/filter = [ "a/dev\/mapper\/.*$/", "a/dev\/LVMTEST/", "r/.*/" ]' ++ ++pvcreate "$DM_DEV_DIR/$vg1/$lv" ++vgcreate $vg -s 64K $(cut -d ' ' -f 4 DEVICES) "$DM_DEV_DIR/$vg1/$lv" + + # create mirrored LVs for data and metadata volumes +-lvcreate -aey -l8 -m1 --mirrorlog core -n $lv1 $vg +-lvcreate -aey -l4 -m1 --mirrorlog core -n $lv2 $vg ++lvcreate -aey -L10M -m1 --mirrorlog core -n $lv1 $vg ++lvcreate -aey -L8M -m1 --mirrorlog core -n $lv2 $vg ++lvchange -an $vg/$lv1 ++ ++ ++# conversion fails for internal volumes ++not lvconvert --thinpool $vg/${lv1}_mimage_0 ++not lvconvert --thinpool $vg/$lv1 --poolmetadata $vg/${lv2}_mimage_0 ++# can't use --readahead with --poolmetadata ++not lvconvert --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 --readahead 512 ++ ++lvconvert --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 ++ ++prepare_lvs ++lvconvert -c 64 --stripes 2 --thinpool $vg/$lv1 --readahead 48 ++ ++lvremove -f $vg ++lvcreate -L1T -n $lv1 $vg ++lvconvert -c 8M --thinpool $vg/$lv1 ++ ++lvremove -f $vg ++# test with bigger sizes ++lvcreate -L1T -n $lv1 $vg ++lvcreate -L8M -n $lv2 $vg ++lvcreate -L1M -n $lv3 $vg + +-lvconvert -c 64K --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 ++# chunk size is bigger then size of thin pool data ++not lvconvert -c 1G --thinpool $vg/$lv3 ++# stripes can't be used with poolmetadata ++not lvconvert --stripes 2 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 ++# too small metadata (<2M) ++not lvconvert -c 64 --thinpool $vg/$lv1 --poolmetadata $vg/$lv3 ++# too small chunk size fails ++not lvconvert -c 4 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 ++# too big chunk size fails ++not lvconvert -c 2G --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 ++# negative chunk size fails ++not lvconvert -c -256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 ++# non power of 2 fails ++not lvconvert -c 88 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 + +-lvcreate -V10M -T $vg/$lv1 --name $lv3 ++# Warning about smaller then suggested ++lvconvert -c 256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 |& tee err ++grep "WARNING: Chunk size is smaller" err + +-# check lvrename work properly +-lvrename $vg/$lv1 $vg/pool +-check lv_field $vg/pool name "pool" ++lvremove -f $vg ++lvcreate -L1T -n $lv1 $vg ++lvcreate -L32G -n $lv2 $vg ++# Warning about bigger then needed ++lvconvert --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 |& tee err ++grep "WARNING: Maximum size" err + +-lvrename $vg/$lv3 $vg/$lv4 +-check lv_field $vg/$lv4 name "$lv4" ++lvremove -f $vg ++lvcreate -L24T -n $lv1 $vg ++# Warning about bigger then needed (24T data and 16G -> 128K chunk) ++lvconvert -c 64 --thinpool $vg/$lv1 |& tee err ++grep "WARNING: Chunk size is too small" err + +-# not yet supported conversions +-not lvconvert -m 1 $vg/pool +-not lvconvert -m 1 $vg/$lv3 ++#lvs -a -o+chunk_size,stripe_size,seg_pe_ranges + + vgremove -ff $vg +diff --git a/test/shell/thin-defaults.sh b/test/shell/thin-defaults.sh +new file mode 100644 +index 0000000..677d2f1 +--- /dev/null ++++ b/test/shell/thin-defaults.sh +@@ -0,0 +1,35 @@ ++#!/bin/bash ++# Copyright (C) 2012 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# test defaults entered through lvm.conf ++ ++. lib/test ++ ++# ++# Main ++# ++aux have_thin 1 0 0 || skip ++ ++aux prepare_vg 2 ++ ++lvcreate -T -L8M $vg/pool0 ++ ++aux lvmconf "allocation/thin_pool_chunk_size = 128" \ ++ "allocation/thin_pool_discards = \"ignore\"" \ ++ "allocation/thin_pool_zero = 0" ++ ++lvcreate -T -L8M $vg/pool1 ++ ++check lv_field $vg/pool1 chunksize "128.00k" ++check lv_field $vg/pool1 discards "ignore" ++check lv_field $vg/pool1 zero 0 ++ ++vgremove -f $vg +diff --git a/test/shell/thin-restore.sh b/test/shell/thin-restore.sh +new file mode 100644 +index 0000000..7580ae4 +--- /dev/null ++++ b/test/shell/thin-restore.sh +@@ -0,0 +1,34 @@ ++#!/bin/bash ++# Copyright (C) 2012 Red Hat, Inc. All rights reserved. ++# ++# This copyrighted material is made available to anyone wishing to use, ++# modify, copy, or redistribute it subject to the terms and conditions ++# of the GNU General Public License v.2. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software Foundation, ++# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++# test restore operation of thin pool metadata ++ ++. lib/test ++ ++# ++# Main ++# ++aux have_thin 1 0 0 || skip ++ ++aux prepare_vg 2 ++ ++lvcreate -T -L8M $vg/pool -V10M -n $lv1 ++ ++vgcfgbackup -f backup $vg ++ ++# use of --force is mandatory ++not vgcfgrestore -f backup $vg ++ ++vgcfgrestore -f backup --force $vg ++ ++check lv_field $vg/pool transaction_id 1 ++ ++vgremove -f $vg +diff --git a/tools/args.h b/tools/args.h +index 0d9605a..d4d6c40 100644 +--- a/tools/args.h ++++ b/tools/args.h +@@ -73,6 +73,7 @@ arg(poll_ARG, '\0', "poll", yes_no_arg, 0) + arg(poolmetadata_ARG, '\0', "poolmetadata", string_arg, 0) + arg(poolmetadatasize_ARG, '\0', "poolmetadatasize", size_mb_arg, 0) + arg(discards_ARG, '\0', "discards", discards_arg, 0) ++arg(force_long_ARG, '\0', "force", NULL, ARG_COUNTABLE) + arg(stripes_long_ARG, '\0', "stripes", int_arg, 0) + arg(sysinit_ARG, '\0', "sysinit", NULL, 0) + arg(thinpool_ARG, '\0', "thinpool", string_arg, 0) +diff --git a/tools/commands.h b/tools/commands.h +index 6415d34..986539e 100644 +--- a/tools/commands.h ++++ b/tools/commands.h +@@ -147,13 +147,16 @@ xx(lvconvert, + "--thinpool ThinPoolLogicalVolume[Path]\n" + "\t[--chunksize size]\n" + "\t[--discards {ignore|nopassdown|passdown}]\n" +- "\t[[--poolmetadatasize size] | --poolmetadata ThinMetadataLogicalVolume[Path]]\n" ++ "\t[--poolmetadata ThinMetadataLogicalVolume[Path] |\n" ++ "\t [--poolmetadatasize size]\n" ++ "\t [-r|--readahead ReadAheadSectors|auto|none]\n" ++ "\t [--stripes Stripes [-I|--stripesize StripeSize]]]\n" + "\t[-Z|--zero {y|n}]\n" + "\t[-d|--debug] [-h|-?|--help] [-v|--verbose]\n", + + alloc_ARG, background_ARG, chunksize_ARG, corelog_ARG, interval_ARG, + merge_ARG, mirrorlog_ARG, mirrors_ARG, name_ARG, noudevsync_ARG, +- regionsize_ARG, repair_ARG, replace_ARG, snapshot_ARG, splitmirrors_ARG, ++ readahead_ARG, regionsize_ARG, repair_ARG, replace_ARG, snapshot_ARG, splitmirrors_ARG, + trackchanges_ARG, type_ARG, stripes_long_ARG, stripesize_ARG, test_ARG, + chunksize_ARG, discards_ARG, poolmetadata_ARG, poolmetadatasize_ARG, thinpool_ARG, + use_policies_ARG, yes_ARG, force_ARG, zero_ARG) +@@ -722,6 +725,7 @@ xx(vgcfgrestore, + "vgcfgrestore " "\n" + "\t[-d|--debug] " "\n" + "\t[-f|--file filename] " "\n" ++ "\t[--force]\n" + "\t[-l[l]|--list [--list]]" "\n" + "\t[-M|--metadatatype 1|2]" "\n" + "\t[-h|--help]" "\n" +@@ -730,7 +734,7 @@ xx(vgcfgrestore, + "\t[--version] " "\n" + "\tVolumeGroupName", + +- file_ARG, list_ARG, metadatatype_ARG, test_ARG) ++ file_ARG, force_long_ARG, list_ARG, metadatatype_ARG, test_ARG) + + xx(vgchange, + "Change volume group attributes", +diff --git a/tools/lvchange.c b/tools/lvchange.c +index 04facdd..7156eeb 100644 +--- a/tools/lvchange.c ++++ b/tools/lvchange.c +@@ -111,13 +111,7 @@ static int lvchange_pool_update(struct cmd_context *cmd, + if (arg_count(cmd, discards_ARG)) { + discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_IGNORE); + if (discards != first_seg(lv)->discards) { +- if ((discards != THIN_DISCARDS_IGNORE) && +- (first_seg(lv)->chunk_size & +- (first_seg(lv)->chunk_size - 1))) +- log_error("Cannot change discards state for " +- "logical volume \"%s\" " +- "with non power of 2 chunk size.", lv->name); +- else if (((discards == THIN_DISCARDS_IGNORE) || ++ if (((discards == THIN_DISCARDS_IGNORE) || + (first_seg(lv)->discards == THIN_DISCARDS_IGNORE)) && + lv_is_active(lv)) + log_error("Cannot change discards state for active " +diff --git a/tools/lvconvert.c b/tools/lvconvert.c +index 132a69d..c584180 100644 +--- a/tools/lvconvert.c ++++ b/tools/lvconvert.c +@@ -39,8 +39,10 @@ struct lvconvert_params { + uint32_t keep_mimages; + uint32_t stripes; + uint32_t stripe_size; ++ uint32_t read_ahead; + + const struct segment_type *segtype; ++ unsigned target_attr; + + alloc_policy_t alloc; + +@@ -155,6 +157,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + int pagesize = lvm_getpagesize(); + + memset(lp, 0, sizeof(*lp)); ++ lp->target_attr = ~0; + + if ((arg_count(cmd, snapshot_ARG) || arg_count(cmd, merge_ARG)) && + (arg_count(cmd, mirrorlog_ARG) || arg_count(cmd, mirrors_ARG) || +@@ -268,6 +271,8 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + if (lp->merge) { /* Snapshot merge */ + if (arg_count(cmd, regionsize_ARG) || arg_count(cmd, chunksize_ARG) || + arg_count(cmd, zero_ARG) || arg_count(cmd, regionsize_ARG) || ++ arg_count(cmd, poolmetadata_ARG) || arg_count(cmd, poolmetadatasize_ARG) || ++ arg_count(cmd, readahead_ARG) || + arg_count(cmd, stripes_long_ARG) || arg_count(cmd, stripesize_ARG)) { + log_error("Only --background and --interval are valid " + "arguments for snapshot merge"); +@@ -335,53 +340,21 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + return 0; + } + ++ if (!get_pool_params(cmd, ++ &lp->chunk_size, ++ &lp->discards, ++ &lp->poolmetadata_size, ++ &lp->zero)) ++ return_0; ++ + if (arg_count(cmd, poolmetadata_ARG)) { +- lp->pool_metadata_lv_name = arg_str_value(cmd, poolmetadata_ARG, ""); +- } else if (arg_count(cmd, poolmetadatasize_ARG)) { +- if (arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { +- log_error("Negative pool metadata size is invalid."); ++ if (arg_count(cmd, poolmetadatasize_ARG)) { ++ log_error("--poolmetadatasize is invalid with --poolmetadata."); + return 0; + } +- lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0)); +- +- if (lp->poolmetadata_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { +- if (arg_count(cmd, poolmetadatasize_ARG)) +- log_warn("WARNING: Maximum supported pool metadata size is 16GB."); +- lp->poolmetadata_size = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE; +- } else if (lp->poolmetadata_size < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { +- if (arg_count(cmd, poolmetadatasize_ARG)) +- log_warn("WARNING: Minimum supported pool metadata size is 2M."); +- lp->poolmetadata_size = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE; +- } +- +- log_verbose("Setting pool metadata size to %" PRIu64 " sectors.", +- lp->poolmetadata_size); ++ lp->pool_metadata_lv_name = arg_str_value(cmd, poolmetadata_ARG, ""); + } + +- if (arg_count(cmd, chunksize_ARG)) { +- 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, +- DM_THIN_MIN_DATA_BLOCK_SIZE); +- +- if ((lp->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) || +- (lp->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE)) { +- log_error("Chunk size must be in the range %uK to %uK.", +- (DM_THIN_MIN_DATA_BLOCK_SIZE / 2), +- (DM_THIN_MAX_DATA_BLOCK_SIZE / 2)); +- return 0; +- } +- } else +- lp->chunk_size = DM_THIN_MIN_DATA_BLOCK_SIZE; +- +- log_verbose("Setting pool metadata chunk size to %u sectors.", +- lp->chunk_size); +- +- if (arg_count(cmd, zero_ARG)) +- lp->zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n"); +- + /* If --thinpool 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))) +@@ -460,7 +433,7 @@ static int _read_params(struct lvconvert_params *lp, struct cmd_context *cmd, + } + + if (activation() && lp->segtype && lp->segtype->ops->target_present && +- !lp->segtype->ops->target_present(cmd, NULL, NULL)) { ++ !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; +@@ -1826,12 +1799,20 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + { + int r = 0; + char *name; ++ const char *old_name; + int len; + struct lv_segment *seg; + struct logical_volume *data_lv; + struct logical_volume *metadata_lv; ++ struct logical_volume *pool_metadata_lv; ++ ++ if (!lv_is_visible(pool_lv)) { ++ log_error("Can't convert internal LV %s/%s.", ++ pool_lv->vg->name, pool_lv->name); ++ return 0; ++ } + +- if (lv_is_thin_type(pool_lv)) { ++ if (lv_is_thin_type(pool_lv) && !lp->pool_metadata_lv_name) { + log_error("Can't use thin logical volume %s/%s for thin pool data.", + pool_lv->vg->name, pool_lv->name); + return 0; +@@ -1839,19 +1820,48 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + + /* We are changing target type, so deactivate first */ + if (!deactivate_lv(cmd, pool_lv)) { +- log_error("Can't deactivate logical volume %s/%s.", ++ log_error("Aborting. Failed to deactivate logical volume %s/%s.", + pool_lv->vg->name, pool_lv->name); + return 0; + } + ++ len = strlen(pool_lv->name) + 16; ++ if (!(name = dm_pool_alloc(pool_lv->vg->vgmem, len))) { ++ log_error("Can't allocate new name."); ++ return 0; ++ } ++ ++ if (dm_snprintf(name, len, "%s_tmeta", pool_lv->name) < 0) { ++ log_error("Failed to create layer name."); ++ return 0; ++ } ++ + if (lp->pool_metadata_lv_name) { ++ if (arg_count(cmd, stripesize_ARG) || arg_count(cmd, stripes_long_ARG)) { ++ log_error("Can't use --stripes and --stripesize with --poolmetadata."); ++ return 0; ++ } ++ if (arg_count(cmd, readahead_ARG)) { ++ log_error("Can't use --readahead with --poolmetadata."); ++ return 0; ++ } + metadata_lv = find_lv(pool_lv->vg, lp->pool_metadata_lv_name); + if (!metadata_lv) { +- log_error("Unknown metadata LV %s", lp->pool_metadata_lv_name); ++ log_error("Unknown metadata LV %s.", lp->pool_metadata_lv_name); ++ return 0; ++ } ++ if (!lv_is_visible(metadata_lv)) { ++ log_error("Can't convert internal LV %s/%s.", ++ metadata_lv->vg->name, metadata_lv->name); ++ return 0; ++ } ++ if (metadata_lv->status & LOCKED) { ++ log_error("Can't convert locked LV %s/%s.", ++ metadata_lv->vg->name, metadata_lv->name); + return 0; + } + if (metadata_lv == pool_lv) { +- log_error("Can't use same LV for thin data and metadata LV %s", ++ log_error("Can't use same LV for thin pool data and metadata LV %s.", + lp->pool_metadata_lv_name); + return 0; + } +@@ -1861,37 +1871,95 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + metadata_lv->vg->name, metadata_lv->name); + return 0; + } +- } else if (arg_count(cmd, poolmetadatasize_ARG)) { +- /* FIXME: allocate metadata LV! */ +- metadata_lv = NULL; +- log_error("Uncreated metadata."); +- return 0; +- } else { +- log_error("Uknown metadata."); +- return 0; +- } + +- len = strlen(pool_lv->name) + 16; +- if (!(name = dm_pool_alloc(pool_lv->vg->vgmem, len))) { +- log_error("Cannot allocate new name."); +- return 0; +- } ++ /* Swap normal LV with pool's metadata LV ? */ ++ if (lv_is_thin_pool(pool_lv)) { ++ if (!deactivate_lv(cmd, metadata_lv)) { ++ log_error("Aborting. Failed to deactivate thin metadata lv."); ++ return 0; ++ } ++ if (!arg_count(cmd, yes_ARG) && ++ yes_no_prompt("Do you want to swap metadata of %s/%s pool with " ++ "volume %s/%s? [y/n]: ", ++ pool_lv->vg->name, pool_lv->name, ++ pool_lv->vg->name, metadata_lv->name) == 'n') { ++ log_error("Conversion aborted."); ++ return 0; ++ } ++ seg = first_seg(pool_lv); ++ /* Swap names between old and new metadata LV */ ++ if (!detach_pool_metadata_lv(seg, &pool_metadata_lv)) ++ return_0; ++ old_name = metadata_lv->name; ++ if (!lv_rename_update(cmd, metadata_lv, "pvmove_tmeta", 0)) ++ return_0; ++ if (!lv_rename_update(cmd, pool_metadata_lv, old_name, 0)) ++ return_0; + +- if (!lv_is_active(metadata_lv)) { +- if (!deactivate_lv(cmd, metadata_lv)) { +- log_error("Can't deactivate logical volume %s/%s.", +- metadata_lv->vg->name, metadata_lv->name); +- return 0; ++ if (!arg_count(cmd, chunksize_ARG)) ++ lp->chunk_size = seg->chunk_size; ++ else if ((lp->chunk_size != seg->chunk_size) && ++ !arg_count(cmd, force_ARG) && ++ yes_no_prompt("Do you really want to change chunk size %s to %s for %s/%s " ++ "pool volume? [y/n]: ", display_size(cmd, seg->chunk_size), ++ display_size(cmd, lp->chunk_size), ++ pool_lv->vg->name, pool_lv->name) == 'n') { ++ log_error("Conversion aborted."); ++ return 0; ++ } ++ if (!arg_count(cmd, discards_ARG)) ++ lp->discards = seg->discards; ++ if (!arg_count(cmd, zero_ARG)) ++ lp->zero = seg->zero_new_blocks; ++ ++ goto mda_write; + } +- if (!activate_lv_local(cmd, metadata_lv)) { ++ ++ if (!lv_is_active(metadata_lv) && ++ !activate_lv_local(cmd, metadata_lv)) { + log_error("Aborting. Failed to activate thin metadata lv."); + return 0; + } +- } ++ if (!set_lv(cmd, metadata_lv, UINT64_C(0), 0)) { ++ log_error("Aborting. Failed to wipe thin metadata lv."); ++ return 0; ++ } + +- if (!set_lv(cmd, metadata_lv, UINT64_C(0), 0)) { +- log_error("Aborting. Failed to wipe thin metadata lv."); +- return 0; ++ lp->poolmetadata_size = ++ (uint64_t) metadata_lv->le_count * metadata_lv->vg->extent_size; ++ if (lp->poolmetadata_size > (2 * DEFAULT_THIN_POOL_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)) { ++ 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)); ++ return 0; ++ } ++ if (!update_pool_params(cmd, lp->target_attr, ++ pool_lv->le_count, pool_lv->vg->extent_size, ++ &lp->chunk_size, &lp->discards, ++ &lp->poolmetadata_size)) ++ return_0; ++ } else { ++ if (!update_pool_params(cmd, lp->target_attr, ++ pool_lv->le_count, pool_lv->vg->extent_size, ++ &lp->chunk_size, &lp->discards, ++ &lp->poolmetadata_size)) ++ return_0; ++ ++ if (!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size)) ++ return_0; ++ /* Hmm _read_activation_params */ ++ lp->read_ahead = arg_uint_value(cmd, readahead_ARG, ++ cmd->default_settings.read_ahead); ++ ++ if (!(metadata_lv = alloc_pool_metadata(pool_lv, lp->alloc, name, ++ lp->pvh, lp->read_ahead, ++ lp->stripes, lp->stripe_size, ++ lp->poolmetadata_size))) ++ return_0; + } + + if (!deactivate_lv(cmd, metadata_lv)) { +@@ -1900,14 +1968,6 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + return 0; + } + +- if (dm_snprintf(name, len, "%s_tmeta", pool_lv->name) < 0) +- return_0; +- +- /* Rename deactivated metadata LV to have _tmeta suffix */ +- /* Implicit checks if metadata_lv is visible */ +- if (!lv_rename_update(cmd, metadata_lv, name, 0)) +- return_0; +- + /* + * Since we wish to have underlaying dev, to match _tdata + * rename data LV first, also checks for visible LV +@@ -1925,18 +1985,26 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + seg->segtype = lp->segtype; + seg->lv->status |= THIN_POOL; + +- seg->chunk_size = lp->chunk_size; +- seg->zero_new_blocks = lp->zero ? 1 : 0; +- seg->discards = lp->discards; ++ /* Drop reference as attach_pool_data_lv() takes it again */ ++ remove_seg_from_segs_using_this_lv(data_lv, seg); ++ if (!attach_pool_data_lv(seg, data_lv)) ++ return_0; ++ + seg->low_water_mark = 0; + seg->transaction_id = 0; + +- if (!attach_pool_metadata_lv(seg, metadata_lv)) ++mda_write: ++ seg->chunk_size = lp->chunk_size; ++ seg->discards = lp->discards; ++ seg->zero_new_blocks = lp->zero ? 1 : 0; ++ ++ /* Rename deactivated metadata LV to have _tmeta suffix */ ++ /* Implicit checks if metadata_lv is visible */ ++ if (strcmp(metadata_lv->name, name) && ++ !lv_rename_update(cmd, metadata_lv, name, 0)) + return_0; + +- /* Drop reference as attach_pool_data_lv() takes it again */ +- remove_seg_from_segs_using_this_lv(data_lv, seg); +- if (!attach_pool_data_lv(seg, data_lv)) ++ if (!attach_pool_metadata_lv(seg, metadata_lv)) + return_0; + + if (!vg_write(pool_lv->vg) || !vg_commit(pool_lv->vg)) +@@ -1945,6 +2013,11 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, + if (!activate_lv_excl(cmd, pool_lv)) { + log_error("Failed to activate pool logical volume %s/%s.", + pool_lv->vg->name, pool_lv->name); ++ /* Deactivate subvolumes */ ++ if (!deactivate_lv(cmd, seg_lv(seg, 0))) ++ log_error("Failed to deactivate pool data logical volume."); ++ if (!deactivate_lv(cmd, seg->metadata_lv)) ++ log_error("Failed to deactivate pool metadata logical volume."); + goto out; + } + +diff --git a/tools/lvcreate.c b/tools/lvcreate.c +index 3ea8f46..1fcbde3 100644 +--- a/tools/lvcreate.c ++++ b/tools/lvcreate.c +@@ -234,7 +234,6 @@ static int _update_extents_params(struct volume_group *vg, + { + uint32_t pv_extent_count; + struct logical_volume *origin = NULL; +- int changed = 0; + uint32_t size_rest; + uint32_t stripesize_extents; + +@@ -308,38 +307,11 @@ static int _update_extents_params(struct volume_group *vg, + } + + if (lp->create_thin_pool) { +- if (!arg_count(vg->cmd, poolmetadatasize_ARG)) { +- /* Defaults to nr_pool_blocks * 64b */ +- lp->poolmetadatasize = (uint64_t) lp->extents * vg->extent_size / +- (uint64_t) (lp->chunk_size * (SECTOR_SIZE / UINT64_C(64))); +- +- /* Check if we could eventually use bigger chunk size */ +- if (!arg_count(vg->cmd, chunksize_ARG)) { +- while ((lp->poolmetadatasize > +- (DEFAULT_THIN_POOL_OPTIMAL_SIZE / SECTOR_SIZE)) && +- (lp->chunk_size < DM_THIN_MAX_DATA_BLOCK_SIZE)) { +- lp->chunk_size <<= 1; +- lp->poolmetadatasize >>= 1; +- changed++; +- } +- if (changed) +- log_verbose("Changed chunksize to %u sectors.", +- lp->chunk_size); +- } +- } +- +- if (lp->poolmetadatasize > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { +- if (arg_count(vg->cmd, poolmetadatasize_ARG)) +- log_warn("WARNING: Maximum supported pool metadata size is 16GB."); +- lp->poolmetadatasize = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE; +- } else if (lp->poolmetadatasize < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { +- if (arg_count(vg->cmd, poolmetadatasize_ARG)) +- log_warn("WARNING: Minimum supported pool metadata size is 2M."); +- lp->poolmetadatasize = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE; +- } +- +- log_verbose("Setting pool metadata size to %" PRIu64 " sectors.", +- lp->poolmetadatasize); ++ if (!update_pool_params(vg->cmd, lp->target_attr, ++ lp->extents, vg->extent_size, ++ &lp->chunk_size, &lp->discards, ++ &lp->poolmetadatasize)) ++ return_0; + + if (!(lp->poolmetadataextents = + extents_from_size(vg->cmd, lp->poolmetadatasize, vg->extent_size))) +@@ -386,16 +358,9 @@ static int _read_size_params(struct lvcreate_params *lp, + if (lp->thin && (arg_count(cmd, size_ARG) || arg_count(cmd, extents_ARG))) + lp->create_thin_pool = 1; + +- if (arg_count(cmd, poolmetadatasize_ARG)) { +- if (!seg_is_thin(lp)) { +- log_error("--poolmetadatasize may only be specified when allocating the thin pool."); +- return 0; +- } +- if (arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { +- log_error("Negative poolmetadatasize is invalid."); +- return 0; +- } +- lp->poolmetadatasize = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0)); ++ if (arg_count(cmd, poolmetadatasize_ARG) && !seg_is_thin(lp)) { ++ log_error("--poolmetadatasize may only be specified when allocating the thin pool."); ++ return 0; + } + + /* Size returned in kilobyte units; held in sectors */ +@@ -679,11 +644,11 @@ static int _lvcreate_params(struct lvcreate_params *lp, + struct arg_value_group_list *current_group; + const char *segtype_str; + const char *tag; +- unsigned attr = 0; + + memset(lp, 0, sizeof(*lp)); + memset(lcp, 0, sizeof(*lcp)); + dm_list_init(&lp->tags); ++ lp->target_attr = ~0; + + /* + * Check selected options are compatible and determine segtype +@@ -796,7 +761,7 @@ static int _lvcreate_params(struct lvcreate_params *lp, + } + + if (activation() && lp->segtype->ops->target_present && +- !lp->segtype->ops->target_present(cmd, NULL, &attr)) { ++ !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; +@@ -812,16 +777,23 @@ static int _lvcreate_params(struct lvcreate_params *lp, + } + } + ++ /* ++ * Should we zero the lv. ++ */ ++ lp->zero = strcmp(arg_str_value(cmd, zero_ARG, ++ (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n"); ++ + 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 && ++ !get_pool_params(cmd, &lp->chunk_size, &lp->discards, ++ &lp->poolmetadatasize, &lp->zero)) || + !_read_mirror_params(lp, cmd) || + !_read_raid_params(lp, cmd)) + return_0; + +- if (lp->create_thin_pool) +- lp->discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, THIN_DISCARDS_PASSDOWN); +- else if (arg_count(cmd, discards_ARG)) { ++ if (!lp->create_thin_pool && arg_count(cmd, discards_ARG)) { + log_error("--discards is only available for thin pool creation."); + return 0; + } +@@ -831,58 +803,27 @@ static int _lvcreate_params(struct lvcreate_params *lp, + else if (lp->thin && !lp->create_thin_pool) { + if (arg_count(cmd, chunksize_ARG)) + log_warn("WARNING: Ignoring --chunksize when using an existing pool."); +- } else if (lp->snapshot || lp->create_thin_pool) { ++ } else if (lp->snapshot) { + if (arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS) { + log_error("Negative chunk size is invalid"); + return 0; + } +- if (lp->snapshot) { +- lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8); +- if (lp->chunk_size < 8 || lp->chunk_size > 1024 || +- (lp->chunk_size & (lp->chunk_size - 1))) { +- log_error("Chunk size must be a power of 2 in the " +- "range 4K to 512K"); +- return 0; +- } +- } else { +- lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, +- DM_THIN_MIN_DATA_BLOCK_SIZE); +- if ((lp->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) || +- (lp->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE)) { +- log_error("Chunk size must be in the range %uK to %uK", +- (DM_THIN_MIN_DATA_BLOCK_SIZE / 2), +- (DM_THIN_MAX_DATA_BLOCK_SIZE / 2)); +- return 0; +- } +- if (!(attr & THIN_FEATURE_BLOCK_SIZE) && +- (lp->chunk_size & (lp->chunk_size - 1))) { +- log_error("Chunk size must be a power of 2 for this thin target version."); +- return 0; +- } else if (lp->chunk_size & (DM_THIN_MIN_DATA_BLOCK_SIZE - 1)) { +- log_error("Chunk size must be multiple of %uK.", +- DM_THIN_MIN_DATA_BLOCK_SIZE / 2); +- return 0; +- } else if ((lp->discards != THIN_DISCARDS_IGNORE) && +- (lp->chunk_size & (lp->chunk_size - 1))) { +- log_warn("WARNING: Using discards ignore for chunk size non power of 2."); +- lp->discards = THIN_DISCARDS_IGNORE; +- } ++ lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8); ++ if (lp->chunk_size < 8 || lp->chunk_size > 1024 || ++ (lp->chunk_size & (lp->chunk_size - 1))) { ++ log_error("Chunk size must be a power of 2 in the " ++ "range 4K to 512K"); ++ return 0; + } +- log_verbose("Setting chunksize to %u sectors.", lp->chunk_size); ++ log_verbose("Setting chunksize to %s.", display_size(cmd, lp->chunk_size)); + + if (!lp->thin && lp->snapshot && !(lp->segtype = get_segtype_from_string(cmd, "snapshot"))) + return_0; +- } else if (arg_count(cmd, chunksize_ARG)) { ++ } else if (arg_count(cmd, chunksize_ARG) && !lp->create_thin_pool) { + log_error("-c is only available with snapshots and thin pools"); + return 0; + } + +- /* +- * Should we zero the lv. +- */ +- lp->zero = strcmp(arg_str_value(cmd, zero_ARG, +- (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? "n" : "y"), "n"); +- + if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) { + log_error("Only up to %d images in mirror supported currently.", + DEFAULT_MIRROR_MAX_IMAGES); +diff --git a/tools/toollib.c b/tools/toollib.c +index 3fe1c14..5fe94e0 100644 +--- a/tools/toollib.c ++++ b/tools/toollib.c +@@ -15,6 +15,7 @@ + + #include "tools.h" + #include ++#include + + const char *command_name(struct cmd_context *cmd) + { +@@ -1521,6 +1522,200 @@ int get_activation_monitoring_mode(struct cmd_context *cmd, + return 1; + } + ++int get_pool_params(struct cmd_context *cmd, ++ uint32_t *chunk_size, ++ thin_discards_t *discards, ++ uint64_t *pool_metadata_size, ++ int *zero) ++{ ++ const char *dstr; ++ ++ if (arg_count(cmd, zero_ARG)) { ++ *zero = strcmp(arg_str_value(cmd, zero_ARG, "y"), "n"); ++ log_very_verbose("Setting pool zeroing: %u", *zero); ++ } else ++ *zero = find_config_tree_int(cmd, ++ "allocation/thin_pool_zero", ++ DEFAULT_THIN_POOL_ZERO); ++ ++ if (arg_count(cmd, discards_ARG)) { ++ *discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, 0); ++ log_very_verbose("Setting pool discards: %s", ++ get_pool_discards_name(*discards)); ++ } else { ++ dstr = find_config_tree_str(cmd, ++ "allocation/thin_pool_discards", ++ DEFAULT_THIN_POOL_DISCARDS); ++ if (!get_pool_discards(dstr, discards)) ++ return_0; ++ } ++ ++ if (arg_count(cmd, chunksize_ARG)) { ++ if (arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS) { ++ log_error("Negative chunk size is invalid."); ++ return 0; ++ } ++ *chunk_size = arg_uint_value(cmd, chunksize_ARG, ++ DM_THIN_MIN_DATA_BLOCK_SIZE); ++ log_very_verbose("Setting pool chunk size: %s", ++ display_size(cmd, *chunk_size)); ++ } else ++ *chunk_size = find_config_tree_int(cmd, ++ "allocation/thin_pool_chunk_size", ++ DEFAULT_THIN_POOL_CHUNK_SIZE) * 2; ++ ++ if ((*chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) || ++ (*chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE)) { ++ log_error("Chunk size must be in the range %s to %s.", ++ display_size(cmd, DM_THIN_MIN_DATA_BLOCK_SIZE), ++ display_size(cmd, DM_THIN_MAX_DATA_BLOCK_SIZE)); ++ return 0; ++ } ++ ++ if (arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE) == SIGN_MINUS) { ++ log_error("Negative pool metadata size is invalid."); ++ return 0; ++ } ++ *pool_metadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, UINT64_C(0)); ++ ++ return 1; ++} ++ ++int update_pool_params(struct cmd_context *cmd, unsigned attr, ++ uint32_t data_extents, uint32_t extent_size, ++ uint32_t *chunk_size, thin_discards_t *discards, ++ uint64_t *pool_metadata_size) ++{ ++ size_t estimate_chunk_size; ++ ++ if (!(attr & THIN_FEATURE_BLOCK_SIZE) && ++ (*chunk_size & (*chunk_size - 1))) { ++ log_error("Chunk size must be a power of 2 for this thin target version."); ++ return 0; ++ } else if (*chunk_size & (DM_THIN_MIN_DATA_BLOCK_SIZE - 1)) { ++ log_error("Chunk size must be multiple of %s.", ++ display_size(cmd, DM_THIN_MIN_DATA_BLOCK_SIZE)); ++ return 0; ++ } else if ((*discards != THIN_DISCARDS_IGNORE) && ++ (*chunk_size & (*chunk_size - 1))) { ++ log_warn("WARNING: Using discards ignore for chunk size non power of 2."); ++ *discards = THIN_DISCARDS_IGNORE; ++ } ++ ++ if (!*pool_metadata_size) { ++ /* Defaults to nr_pool_blocks * 64b converted to size in sectors */ ++ *pool_metadata_size = (uint64_t) data_extents * extent_size / ++ (*chunk_size * (SECTOR_SIZE / UINT64_C(64))); ++ /* Check if we could eventually use bigger chunk size */ ++ if (!arg_count(cmd, chunksize_ARG)) { ++ while ((*pool_metadata_size > ++ (DEFAULT_THIN_POOL_OPTIMAL_SIZE / SECTOR_SIZE)) && ++ (*chunk_size < DM_THIN_MAX_DATA_BLOCK_SIZE)) { ++ *chunk_size <<= 1; ++ *pool_metadata_size >>= 1; ++ } ++ log_verbose("Setting chunk size to %s.", ++ display_size(cmd, *chunk_size)); ++ } else if (*pool_metadata_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { ++ /* Suggest bigger chunk size */ ++ estimate_chunk_size = (uint64_t) data_extents * extent_size / ++ (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE * ++ (SECTOR_SIZE / UINT64_C(64))); ++ log_warn("WARNING: Chunk size is too small for pool, suggested minimum is %s.", ++ display_size(cmd, 1 << (ffs(estimate_chunk_size) + 1))); ++ } ++ ++ /* Round up to extent size */ ++ if (*pool_metadata_size % extent_size) ++ *pool_metadata_size += extent_size - *pool_metadata_size % extent_size; ++ } else { ++ estimate_chunk_size = (uint64_t) data_extents * extent_size / ++ (*pool_metadata_size * (SECTOR_SIZE / UINT64_C(64))); ++ /* Check to eventually use bigger chunk size */ ++ if (!arg_count(cmd, chunksize_ARG)) { ++ *chunk_size = estimate_chunk_size; ++ ++ if (*chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) ++ *chunk_size = DM_THIN_MIN_DATA_BLOCK_SIZE; ++ else if (*chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE) ++ *chunk_size = DM_THIN_MAX_DATA_BLOCK_SIZE; ++ ++ log_verbose("Setting chunk size %s.", ++ display_size(cmd, *chunk_size)); ++ } else if (*chunk_size < estimate_chunk_size) { ++ /* Suggest bigger chunk size */ ++ log_warn("WARNING: Chunk size is smaller then suggested minimum size %s.", ++ display_size(cmd, estimate_chunk_size)); ++ } ++ } ++ ++ if ((uint64_t) *chunk_size > (uint64_t) data_extents * extent_size) { ++ log_error("Chunk size is bigger then pool data size."); ++ return 0; ++ } ++ ++ if (*pool_metadata_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { ++ if (arg_count(cmd, poolmetadatasize_ARG)) ++ log_warn("WARNING: Maximum supported pool metadata size is %s.", ++ display_size(cmd, 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)); ++ *pool_metadata_size = 2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE; ++ } else if (*pool_metadata_size < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { ++ if (arg_count(cmd, poolmetadatasize_ARG)) ++ log_warn("WARNING: Minimum supported pool metadata size is %s.", ++ display_size(cmd, 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)); ++ *pool_metadata_size = 2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE; ++ } ++ ++ log_verbose("Setting pool metadata size to %s.", ++ display_size(cmd, *pool_metadata_size)); ++ ++ return 1; ++} ++ ++struct logical_volume *alloc_pool_metadata(struct logical_volume *pool_lv, ++ alloc_policy_t alloc, ++ const char *name, ++ struct dm_list *pvh, ++ uint32_t read_ahead, ++ uint32_t stripes, ++ uint32_t stripe_size, ++ uint64_t size) ++{ ++ struct logical_volume *metadata_lv; ++ struct lvcreate_params lvc; ++ ++ /* FIXME: Make lvm2api usable */ ++ memset(&lvc, 0, sizeof(lvc)); ++ ++ if (!(lvc.extents = extents_from_size(pool_lv->vg->cmd, size, ++ pool_lv->vg->extent_size))) ++ return_0; ++ ++ if (!(lvc.segtype = get_segtype_from_string(pool_lv->vg->cmd, "striped"))) ++ return_0; ++ ++ dm_list_init(&lvc.tags); ++ ++ /* FIXME: allocate properly space for metadata_lv */ ++ lvc.activate = CHANGE_ALY; ++ lvc.alloc = alloc; ++ lvc.lv_name = name; ++ lvc.major = -1; ++ lvc.minor = -1; ++ lvc.permission = LVM_READ | LVM_WRITE; ++ lvc.pvh = pvh; ++ lvc.read_ahead = read_ahead; ++ lvc.stripe_size = stripe_size; ++ lvc.stripes = stripes; ++ lvc.vg_name = pool_lv->vg->name; ++ lvc.zero = 1; ++ ++ if (!(metadata_lv = lv_create_single(pool_lv->vg, &lvc))) ++ return_0; ++ ++ return metadata_lv; ++} ++ + /* + * Generic stripe parameter checks. + */ +diff --git a/tools/toollib.h b/tools/toollib.h +index b3b0a7c..80c01fd 100644 +--- a/tools/toollib.h ++++ b/tools/toollib.h +@@ -111,6 +111,20 @@ int pvcreate_params_validate(struct cmd_context *cmd, + + int get_activation_monitoring_mode(struct cmd_context *cmd, + int *monitoring_mode); ++int get_pool_params(struct cmd_context *cmd, ++ uint32_t *chunk_size, ++ thin_discards_t *discards, ++ uint64_t *pool_metadata_size, ++ int *zero); ++int update_pool_params(struct cmd_context *cmd, unsigned attr, ++ uint32_t data_extents, uint32_t extent_size, ++ uint32_t *chunk_size, thin_discards_t *discards, ++ uint64_t *pool_metadata_size); ++struct logical_volume *alloc_pool_metadata(struct logical_volume *pool_lv, ++ alloc_policy_t alloc, const char *name, ++ struct dm_list *pvh, uint32_t read_ahead, ++ uint32_t stripes, uint32_t stripe_size, ++ uint64_t size); + int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, + uint32_t *stripe_size); + +diff --git a/tools/vgcfgrestore.c b/tools/vgcfgrestore.c +index d62df99..20ca16b 100644 +--- a/tools/vgcfgrestore.c ++++ b/tools/vgcfgrestore.c +@@ -62,8 +62,9 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv) + + if (!(arg_count(cmd, file_ARG) ? + backup_restore_from_file(cmd, vg_name, +- arg_str_value(cmd, file_ARG, "")) : +- backup_restore(cmd, vg_name))) { ++ arg_str_value(cmd, file_ARG, ""), ++ arg_count(cmd, force_long_ARG)) : ++ backup_restore(cmd, vg_name, arg_count(cmd, force_long_ARG)))) { + unlock_vg(cmd, VG_ORPHANS); + unlock_vg(cmd, vg_name); + log_error("Restore failed."); diff --git a/lvm2.spec b/lvm2.spec index 34f642b..7c3a510 100644 --- a/lvm2.spec +++ b/lvm2.spec @@ -37,7 +37,7 @@ Summary: Userland logical volume management tools Name: lvm2 Version: 2.02.98 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv2 Group: System Environment/Base URL: http://sources.redhat.com/lvm2 @@ -58,6 +58,18 @@ Patch12: lvm2-2_02_99-fix-autoactivation-to-not-autoactivate-vg-lv-on-each-pv-ch Patch13: lvm2-2_02_99-synchronize-with-udev-in-pvscan-cache-and-fix-dangling-udev_sync-cookies.patch Patch14: lvm2-2_02_99-also-autoactivate-on-coldplug.patch Patch15: lvm2-2_02_99-fire-pvscan-cache-on-change-event-for-md-devs.patch +Patch16: lvm2-2_02_99-various-thin-support-related-fixes.patch +Patch17: lvm2-2_02_99-fix-lv_is_active-in-lvcreate.patch +Patch18: lvm2-2_02_99-fix-crash-in-pvscan-cache-aay-triggered-by-non-mda-pv.patch +Patch19: lvm2-2_02_99-avoid-global-lock-in-pvs-when-lvmetad-is-in-use.patch +Patch20: lvm2-2_02_99-fix-possible-deadlock-in-lvmetad-for-parallel-update-and-query.patch +Patch21: lvm2-2_02_99-fix-possible-race-in-lvmetad-remove_metadata.patch +Patch22: lvm2-2_02_99-lvmetad-fix-a-race-in-metadata-update.patch +Patch23: lvm2-2_02_99-fix-blkdeactivate-handling-of-nested-mountpoints-and-mangled-mount-paths.patch +Patch24: lvm2-2_02_99-add-dm-disable-udev-env-var-and-fix-noudevsync-arg.patch +Patch25: lvm2-2_02_99-fix-premature-dm-version-checking-which-caused-useless-mapper-control-access.patch +Patch26: lvm2-2_02_99-close-dmeventd-fifo-fds-on-exec.patch +Patch27: lvm2-2_02_99-fix-dmsetup-splitname-to-not-fail-if-used-without-c-switch.patch BuildRequires: libselinux-devel >= %{libselinux_version}, libsepol-devel BuildRequires: ncurses-devel @@ -108,6 +120,18 @@ or more physical volumes and creating one or more logical volumes %patch13 -p1 -b .dangling_cookies %patch14 -p1 -b .coldplug %patch15 -p1 -b .autoactivation_on_md +%patch16 -p1 -b .various_thin_fixes +%patch17 -p1 -b .lv_is_active +%patch18 -p1 -b .pvscan_cache_aay_crash +%patch19 -p1 -b .avoid_global_lock +%patch20 -p1 -b .lvmetad_deadlock +%patch21 -p1 -b .lvmetad_race_on_remove +%patch22 -p1 -b .lvmetad_race_on_update +%patch23 -p1 -b .blkdeactivate +%patch24 -p1 -b .dm_disable_udev +%patch25 -p1 -b .premature_dm_version +%patch26 -p1 -b .dmeventd_fd_cloexec +%patch27 -p1 -b .dmsetup_splitname %build %define _default_pid_dir /run @@ -604,6 +628,30 @@ the device-mapper event library. %{_libdir}/pkgconfig/devmapper-event.pc %changelog +* Tue May 14 2013 Peter Rajnoha - 2.02.98-9 +- Fix 'dmsetup splitname -o' to not fail if used without '-c' switch (1.02.68). +- Close open dmeventd FIFO file descriptors on exec (FD_CLOEXEC). +- Fix premature DM version checking which caused useless mapper/control access. +- Recognize DM_DISABLE_UDEV environment variable for a complete fallback. +- Do not verify udev operations if --noudevsync command option is used. +- Fix blkdeactivate to handle nested mountpoints and mangled mount paths. +- Fix a crash-inducing race condition in lvmetad while updating metadata. +- Fix possible race while removing metadata from lvmetad. +- Fix possible deadlock when querying and updating lvmetad at the same time. +- Avoid a global lock in pvs when lvmetad is in use. +- Fix crash in pvscan --cache -aay triggered by non-mda PV. +- Fix lvm2app to return all property sizes in bytes. +- Add lvm.conf option global/thin_disabled_features. +- Add lvconvert support to swap thin pool metadata volume. +- Implement internal function detach_pool_metadata_lv(). +- Fix lvm2app and return lvseg discards property as string. +- Allow forced vgcfgrestore of lvm2 metadata with thin volumes. +- Add lvm.conf thin pool defs thin_pool_{chunk_size|discards|zero}. +- Support discards for non-power-of-2 thin pool chunks. +- Support allocation of pool metadata with lvconvert command. +- Move common functionality for thin lvcreate and lvconvert to toollib. +- Use lv_is_active() instead of lv_info() call. + * Fri May 03 2013 Peter Rajnoha - 2.02.98-8 - Fix non-functional autoactivation of LVM volumes on top of MD devices.