commit 11388698bad4336c156b11f8e1f51b664b34f57c Author: Peter Rajnoha Date: Fri Oct 25 08:22:33 2013 +0200 Additional fixes from upcoming v104. --- WHATS_NEW | 29 ++++++ WHATS_NEW_DM | 6 ++ configure | 26 +++++- configure.in | 17 +++- daemons/clvmd/clvmd.c | 2 +- daemons/clvmd/lvm-functions.c | 2 +- daemons/lvmetad/lvmetad-core.c | 25 +++-- lib/activate/activate.c | 90 ++++++++++++------ lib/activate/activate.h | 19 +++- lib/activate/dev_manager.c | 19 +++- lib/cache/lvmetad.c | 2 - lib/device/device-types.h | 1 + lib/format_text/flags.c | 6 +- lib/locking/file_locking.c | 6 +- lib/locking/no_locking.c | 6 +- lib/metadata/lv.c | 8 +- lib/metadata/lv_manip.c | 17 +++- lib/metadata/metadata-exported.h | 7 ++ lib/metadata/metadata.c | 76 ++++++++++------ lib/metadata/metadata.h | 3 - lib/metadata/snapshot_manip.c | 10 ++ lib/metadata/thin_manip.c | 1 + lib/snapshot/snapshot.c | 59 ++++++------ libdaemon/client/daemon-io.c | 2 +- libdaemon/server/daemon-server.c | 6 +- libdm/Makefile.in | 2 +- libdm/libdevmapper.h | 2 +- libdm/libdm-common.c | 13 ++- libdm/mm/pool-fast.c | 4 + libdm/mm/pool.c | 9 +- man/pvscan.8.in | 4 +- scripts/Makefile.in | 2 + scripts/blkdeactivate.sh.in | 5 +- scripts/lvm2_pvscan_systemd_red_hat@.service.in | 14 +++ scripts/vgimportclone.sh | 8 +- test/Makefile.in | 12 ++- test/lib/aux.sh | 2 +- test/lib/get.sh | 2 +- test/lib/harness.c | 30 +++--- test/shell/lvconvert-repair-raid.sh | 2 +- test/shell/lvconvert-repair-thin.sh | 116 ++++++++++++++++++++++++ test/shell/lvconvert-thin-external.sh | 8 ++ test/shell/lvcreate-usage.sh | 18 ++-- test/shell/lvmcache-exercise.sh | 17 +++- test/shell/snapshot-usage.sh | 37 +++++++- test/shell/vgrename-usage.sh | 15 +++ tools/dmsetup.c | 2 +- tools/lvchange.c | 3 +- tools/lvconvert.c | 23 ++++- tools/lvremove.c | 8 -- tools/pvscan.c | 61 ++++++++++--- tools/vgrename.c | 2 + udev/11-dm-lvm.rules.in | 15 +++ udev/13-dm-disk.rules.in | 3 +- udev/69-dm-lvm-metad.rules.in | 4 +- udev/Makefile.in | 8 +- 56 files changed, 697 insertions(+), 199 deletions(-) diff --git a/WHATS_NEW b/WHATS_NEW index d69e74e..49f37a4 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,3 +1,32 @@ +Version 2.02.104 +=================================== + Add internal flag for temporary LVs to properly direct udev to not interfere. + Fix endless loop in blkdeactivate ... if unable to umount/deactivate. + Add dev-block-:.device systemd alias for complete PV tracking. + Use major:minor as short form of --major and --minor arg for pvscan --cache. + Remove 2>/dev/null from three lvm commands executed by vgimportclone. + Add configure --enable-udev-systemd-background-jobs. + Add lvm2-pvscan@.service to run pvscan as a service for lvmetad/autoactivation. + Fix lvconvert swap of poolmetadata volume for active thin pool. + Check for open count with a timeout before removal/deactivation of an LV. + Report RAID images split with tracking as out-of-sync ("I"). + Improve parsing of snapshot lv segment. + Add workaround for deactivation problem of opened virtual snapshot. + Disable unsupported merge for virtual snapshot. + Move code to remove virtual snapshot from tools to lib for lvm2app. + Fix possible race during daemon worker thread creation (lvmetad). + Fix possible deadlock while clearing lvmetad cache for full rescan. + Fix possible race while creating/destroying memory pools. + Recognise NVM Express devices in filter. + Fix failing metadata repair when lvmetad is used. + Fix incorrect memory handling when reading messages from lvmetad. + Fix locking in lvmetad when handling the PV which is gone. + Recognize new flag to skip udev scanning in udev rules and act appropriately. + Add support for flagging an LV to skip udev scanning during activation. + Improve message when unable to change discards setting on active thin pool. + Run full scan before vgrename operation to avoid any cache name collision. + Fix lvconvert when converting to a thin pool and thin LV at once. + Version 2.02.103 - 4th October 2013 =================================== Ensure vgid matches before removing vgname entry from lvmetad cache. diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM index 9dfd9bf..6742ad4 100644 --- a/WHATS_NEW_DM +++ b/WHATS_NEW_DM @@ -1,3 +1,9 @@ +Version 1.02.83 +================================== + Skip race errors when non-udev dmsetup build runs on udev-enabled system. + Skip error message when holders are not present in sysfs. + Use __linux__ instead of linux define to make libdevmapper.h C compliant. + Version 1.02.82 - 4th October 2013 ================================== Define symbolic names for subsystem udev flags in libdevmapper for easier use. diff --git a/configure b/configure index 20a706a..002a7d2 100755 --- a/configure +++ b/configure @@ -613,6 +613,7 @@ DMEVENTD_PIDFILE WRITE_INSTALL UDEV_HAS_BUILTIN_BLKID UDEV_RULE_EXEC_DETECTION +UDEV_SYSTEMD_BACKGROUND_JOBS UDEV_SYNC UDEV_RULES UDEV_PC @@ -849,6 +850,7 @@ enable_valgrind_pool enable_devmapper enable_lvmetad with_lvmetad_pidfile +enable_udev_systemd_background_jobs enable_udev_sync enable_udev_rules enable_udev_rule_exec_detection @@ -1552,6 +1554,9 @@ Optional Features: --enable-valgrind-pool enable valgrind awareness of pools --disable-devmapper disable LVM2 device-mapper interaction --enable-lvmetad enable the LVM Metadata Daemon + --enable-udev-systemd-background-jobs + enable udev-systemd protocol to instantiate a + service for background job --enable-udev_sync enable synchronisation with udev processing --enable-udev_rules install rule files needed for udev synchronisation --enable-udev-rule-exec-detection @@ -9090,6 +9095,19 @@ _ACEOF fi ################################################################################ +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use udev-systemd protocol for jobs in background" >&5 +$as_echo_n "checking whether to use udev-systemd protocol for jobs in background... " >&6; } +# Check whether --enable-udev-systemd-background-jobs was given. +if test "${enable_udev_systemd_background_jobs+set}" = set; then : + enableval=$enable_udev_systemd_background_jobs; UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval +else + UDEV_SYSTEMD_BACKGROUND_JOBS=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $UDEV_SYSTEMD_BACKGROUND_JOBS" >&5 +$as_echo "$UDEV_SYSTEMD_BACKGROUND_JOBS" >&6; } + +################################################################################ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable synchronisation with udev processing" >&5 $as_echo_n "checking whether to enable synchronisation with udev processing... " >&6; } # Check whether --enable-udev_sync was given. @@ -9668,8 +9686,7 @@ if [ \( "x$LVM1" = xshared -o "x$POOL" = xshared -o "x$CLUSTER" = xshared \ fi ################################################################################ -if [ "$DMEVENTD" = yes -o "$CLVMD" != none ] ; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_lock in -lpthread" >&5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_lock in -lpthread" >&5 $as_echo_n "checking for pthread_mutex_lock in -lpthread... " >&6; } if test "${ac_cv_lib_pthread_pthread_mutex_lock+set}" = set; then : $as_echo_n "(cached) " >&6 @@ -9711,7 +9728,6 @@ else hard_bailout fi -fi ################################################################################ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable selinux support" >&5 @@ -10933,8 +10949,9 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'` + ################################################################################ -ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/default.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" +ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmetad/Makefile conf/Makefile conf/example.conf conf/default.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile lib/misc/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -11671,6 +11688,7 @@ do "scripts/lvm2_lvmetad_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_init_red_hat" ;; "scripts/lvm2_lvmetad_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.socket" ;; "scripts/lvm2_lvmetad_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_lvmetad_systemd_red_hat.service" ;; + "scripts/lvm2_pvscan_systemd_red_hat@.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_pvscan_systemd_red_hat@.service" ;; "scripts/lvm2_monitoring_init_red_hat") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_init_red_hat" ;; "scripts/dm_event_systemd_red_hat.socket") CONFIG_FILES="$CONFIG_FILES scripts/dm_event_systemd_red_hat.socket" ;; "scripts/dm_event_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/dm_event_systemd_red_hat.service" ;; diff --git a/configure.in b/configure.in index 611ab37..07b0afc 100644 --- a/configure.in +++ b/configure.in @@ -940,6 +940,15 @@ if test x$BUILD_LVMETAD = xyes; then fi ################################################################################ +dnl -- Enable udev-systemd protocol to instantiate a service for background jobs +AC_MSG_CHECKING(whether to use udev-systemd protocol for jobs in background) +AC_ARG_ENABLE(udev-systemd-background-jobs, + AC_HELP_STRING([--enable-udev-systemd-background-jobs], + [enable udev-systemd protocol to instantiate a service for background job]), + UDEV_SYSTEMD_BACKGROUND_JOBS=$enableval, UDEV_SYSTEMD_BACKGROUND_JOBS=no) +AC_MSG_RESULT($UDEV_SYSTEMD_BACKGROUND_JOBS) + +################################################################################ dnl -- Enable udev synchronisation AC_MSG_CHECKING(whether to enable synchronisation with udev processing) AC_ARG_ENABLE(udev_sync, @@ -1181,10 +1190,8 @@ Features cannot be 'shared' when building statically fi ################################################################################ -if [[ "$DMEVENTD" = yes -o "$CLVMD" != none ]] ; then - AC_CHECK_LIB([pthread], [pthread_mutex_lock], - [PTHREAD_LIBS="-lpthread"], hard_bailout) -fi +AC_CHECK_LIB([pthread], [pthread_mutex_lock], + [PTHREAD_LIBS="-lpthread"], hard_bailout) ################################################################################ dnl -- Disable selinux @@ -1628,6 +1635,7 @@ AC_SUBST(UDEV_LIBS) AC_SUBST(UDEV_PC) AC_SUBST(UDEV_RULES) AC_SUBST(UDEV_SYNC) +AC_SUBST(UDEV_SYSTEMD_BACKGROUND_JOBS) AC_SUBST(UDEV_RULE_EXEC_DETECTION) AC_SUBST(UDEV_HAS_BUILTIN_BLKID) AC_SUBST(CUNIT_LIBS) @@ -1702,6 +1710,7 @@ scripts/cmirrord_init_red_hat scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmetad_systemd_red_hat.service +scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_monitoring_init_red_hat scripts/dm_event_systemd_red_hat.socket scripts/dm_event_systemd_red_hat.service diff --git a/daemons/clvmd/clvmd.c b/daemons/clvmd/clvmd.c index bed8e68..d57c0fd 100644 --- a/daemons/clvmd/clvmd.c +++ b/daemons/clvmd/clvmd.c @@ -1124,7 +1124,7 @@ static int verify_message(char *buf, int len) /* TODO: we may be able to narrow len/flags/clientid/arglen checks based on cmd */ - if (h->flags & ~(CLVMD_FLAG_LOCAL | CLVMD_FLAG_SYSTEMLV | CLVMD_FLAG_NODEERRS)) { + if (h->flags & ~(CLVMD_FLAG_LOCAL | CLVMD_FLAG_SYSTEMLV | CLVMD_FLAG_NODEERRS | CLVMD_FLAG_REMOTE)) { log_error("verify_message bad flags %x", h->flags); return -1; } diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c index da7d335..b15732f 100644 --- a/daemons/clvmd/lvm-functions.c +++ b/daemons/clvmd/lvm-functions.c @@ -401,7 +401,7 @@ static int do_activate_lv(char *resource, unsigned char command, unsigned char l } /* Now activate it */ - if (!lv_activate(cmd, resource, exclusive, NULL)) + if (!lv_activate(cmd, resource, exclusive, 0, 0, NULL)) goto error; return 0; diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c index e1ec5a8..285c8cc 100644 --- a/daemons/lvmetad/lvmetad-core.c +++ b/daemons/lvmetad/lvmetad-core.c @@ -551,7 +551,7 @@ static int compare_config(struct dm_config_node *a, struct dm_config_node *b) return result; } -static int vg_remove_if_missing(lvmetad_state *s, const char *vgid); +static int vg_remove_if_missing(lvmetad_state *s, const char *vgid, int update_pvids); /* You need to be holding the pvid_to_vgid lock already to call this. */ static int update_pvid_to_vgid(lvmetad_state *s, struct dm_config_tree *vg, @@ -590,7 +590,7 @@ static int update_pvid_to_vgid(lvmetad_state *s, struct dm_config_tree *vg, n = dm_hash_get_next(to_check, n)) { check_vgid = dm_hash_get_key(to_check, n); lock_vg(s, check_vgid); - vg_remove_if_missing(s, check_vgid); + vg_remove_if_missing(s, check_vgid, 0); unlock_vg(s, check_vgid); } @@ -631,7 +631,7 @@ static int remove_metadata(lvmetad_state *s, const char *vgid, int update_pvids) } /* The VG must be locked. */ -static int vg_remove_if_missing(lvmetad_state *s, const char *vgid) +static int vg_remove_if_missing(lvmetad_state *s, const char *vgid, int update_pvids) { struct dm_config_tree *vg; struct dm_config_node *pv; @@ -658,7 +658,7 @@ static int vg_remove_if_missing(lvmetad_state *s, const char *vgid) if (missing) { DEBUGLOG(s, "removing empty VG %s", vgid); - remove_metadata(s, vgid, 0); + remove_metadata(s, vgid, update_pvids); } unlock_pvid_to_pvmeta(s); @@ -796,11 +796,24 @@ static response pv_gone(lvmetad_state *s, request r) pvmeta = dm_hash_lookup(s->pvid_to_pvmeta, pvid); pvid_old = dm_hash_lookup_binary(s->device_to_pvid, &device, sizeof(device)); + char *vgid = dm_hash_lookup(s->pvid_to_vgid, pvid); + + if (vgid && !(vgid = dm_strdup(vgid))) { + unlock_pvid_to_pvmeta(s); + return reply_fail("out of memory"); + } + dm_hash_remove_binary(s->device_to_pvid, &device, sizeof(device)); dm_hash_remove(s->pvid_to_pvmeta, pvid); - vg_remove_if_missing(s, dm_hash_lookup(s->pvid_to_vgid, pvid)); unlock_pvid_to_pvmeta(s); + if (vgid) { + lock_vg(s, vgid); + vg_remove_if_missing(s, vgid, 1); + unlock_vg(s, vgid); + dm_free(vgid); + } + if (pvid_old) dm_free(pvid_old); @@ -816,8 +829,8 @@ static response pv_clear_all(lvmetad_state *s, request r) DEBUGLOG(s, "pv_clear_all"); lock_pvid_to_pvmeta(s); - lock_vgid_to_metadata(s); lock_pvid_to_vgid(s); + lock_vgid_to_metadata(s); destroy_metadata_hashes(s); create_metadata_hashes(s); diff --git a/lib/activate/activate.c b/lib/activate/activate.c index 28549fc..006681e 100644 --- a/lib/activate/activate.c +++ b/lib/activate/activate.c @@ -251,8 +251,8 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer, { return 0; } -int lv_check_not_in_use(struct cmd_context *cmd __attribute__((unused)), - struct logical_volume *lv, struct lvinfo *info) +int lv_check_not_in_use(struct cmd_context *cmd, struct logical_volume *lv, + struct lvinfo *info) { return 0; } @@ -337,12 +337,13 @@ int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s, { return 1; } -int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv) +int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan, + int temporary, struct logical_volume *lv) { return 1; } int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, - struct logical_volume *lv) + int noscan, int temporary, struct logical_volume *lv) { return 1; } @@ -675,33 +676,48 @@ int lv_info_by_lvid(struct cmd_context *cmd, const char *lvid_s, int use_layer, return r; } -int lv_check_not_in_use(struct cmd_context *cmd __attribute__((unused)), - struct logical_volume *lv, struct lvinfo *info) +#define OPEN_COUNT_CHECK_RETRIES 25 +#define OPEN_COUNT_CHECK_USLEEP_DELAY 200000 + +int lv_check_not_in_use(struct cmd_context *cmd, struct logical_volume *lv, + struct lvinfo *info) { + unsigned int open_count_check_retries; + if (!info->exists) return 1; /* If sysfs is not used, use open_count information only. */ - if (!*dm_sysfs_dir()) { - if (info->open_count) { - log_error("Logical volume %s/%s in use.", + if (dm_sysfs_dir()) { + if (dm_device_has_holders(info->major, info->minor)) { + log_error("Logical volume %s/%s is used by another device.", lv->vg->name, lv->name); return 0; } - return 1; - } - - if (dm_device_has_holders(info->major, info->minor)) { - log_error("Logical volume %s/%s is used by another device.", - lv->vg->name, lv->name); - return 0; + if (dm_device_has_mounted_fs(info->major, info->minor)) { + log_error("Logical volume %s/%s contains a filesystem in use.", + lv->vg->name, lv->name); + return 0; + } } - if (dm_device_has_mounted_fs(info->major, info->minor)) { - log_error("Logical volume %s/%s contains a filesystem in use.", - lv->vg->name, lv->name); - return 0; + open_count_check_retries = retry_deactivation() ? OPEN_COUNT_CHECK_RETRIES : 1; + while (open_count_check_retries--) { + if (info->open_count > 0) { + if (open_count_check_retries) { + usleep(OPEN_COUNT_CHECK_USLEEP_DELAY); + log_debug_activation("Retrying open_count check for %s/%s.", + lv->vg->name, lv->name); + if (!lv_info(cmd, lv, 0, info, 1, 0)) + return -1; + continue; + } + log_error("Logical volume %s/%s in use.", + lv->vg->name, lv->name); + return 0; + } else + break; } return 1; @@ -887,6 +903,18 @@ int lv_raid_message(const struct logical_volume *lv, const char *msg) struct dm_status_raid *status; if (!seg_is_raid(first_seg(lv))) { + /* + * Make it easier for user to know what to do when + * they are using thinpool. + */ + if (lv_is_thin_pool(lv) && + (lv_is_raid(seg_lv(first_seg(lv), 0)) || + lv_is_raid(first_seg(lv)->metadata_lv))) { + log_error("Thinpool data or metadata volume" + " must be specified. (e.g. \"%s/%s_tdata\")", + lv->vg->name, lv->name); + return 0; + } log_error("%s/%s must be a RAID logical volume to" " perform this action.", lv->vg->name, lv->name); return 0; @@ -1928,7 +1956,7 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, struct logical_vo goto out; } - if (lv_is_visible(lv)) { + if (lv_is_visible(lv) || lv_is_virtual_origin(lv)) { if (!lv_check_not_in_use(cmd, lv, &info)) goto_out; @@ -2027,9 +2055,11 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s, if (filter) laopts->read_only = _passes_readonly_filter(cmd, lv); - log_debug_activation("Activating %s/%s%s%s.", lv->vg->name, lv->name, + log_debug_activation("Activating %s/%s%s%s%s%s.", lv->vg->name, lv->name, laopts->exclusive ? " exclusively" : "", - laopts->read_only ? " read-only" : ""); + laopts->read_only ? " read-only" : "", + laopts->noscan ? " noscan" : "", + laopts->temporary ? " temporary" : ""); if (!lv_info(cmd, lv, 0, &info, 0, 0)) goto_out; @@ -2066,9 +2096,12 @@ out: } /* Activate LV */ -int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv) +int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, + int noscan, int temporary, struct logical_volume *lv) { - struct lv_activate_opts laopts = { .exclusive = exclusive }; + struct lv_activate_opts laopts = { .exclusive = exclusive, + .noscan = noscan, + .temporary = temporary }; if (!_lv_activate(cmd, lvid_s, &laopts, 0, lv)) return_0; @@ -2077,9 +2110,12 @@ int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, stru } /* Activate LV only if it passes filter */ -int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv) +int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, + int noscan, int temporary, struct logical_volume *lv) { - struct lv_activate_opts laopts = { .exclusive = exclusive }; + struct lv_activate_opts laopts = { .exclusive = exclusive, + .noscan = noscan, + .temporary = temporary }; if (!_lv_activate(cmd, lvid_s, &laopts, 1, lv)) return_0; diff --git a/lib/activate/activate.h b/lib/activate/activate.h index f34d376..df888cd 100644 --- a/lib/activate/activate.h +++ b/lib/activate/activate.h @@ -38,6 +38,18 @@ struct lv_activate_opts { int skip_in_use; unsigned revert; unsigned read_only; + unsigned noscan; /* Mark this LV to avoid its scanning. This also + directs udev to use proper udev flag to avoid + any scanning in udev. This udev flag is automatically + dropped in udev db on any spurious event that follows. */ + unsigned temporary; /* Mark this LV as temporary. It means, the LV + * is created, used and deactivated within single + * LVM command execution. Such LVs are mostly helper + * LVs to do some action or cleanup before the proper + * LV is created. This also directs udev to use proper + * set of flags to avoid any scanning in udev. These udev + * flags are persistent in udev db for any spurious event + * that follows. */ }; /* target attribute flags */ @@ -80,9 +92,10 @@ int lv_suspend_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned o int lv_resume(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only, struct logical_volume *lv); int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned origin_only, unsigned exclusive, unsigned revert, struct logical_volume *lv); -int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, struct logical_volume *lv); -int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, - int exclusive, struct logical_volume *lv); +int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, + int noscan, int temporary, struct logical_volume *lv); +int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive, + int noscan, int temporary, struct logical_volume *lv); int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, struct logical_volume *lv); int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv); diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c index b8233bf..0b911f2 100644 --- a/lib/activate/dev_manager.c +++ b/lib/activate/dev_manager.c @@ -30,6 +30,7 @@ #include #define MAX_TARGET_PARAMSIZE 50000 +#define LVM_UDEV_NOSCAN_FLAG DM_SUBSYSTEM_UDEV_FLAG0 typedef enum { PRELOAD, @@ -1399,7 +1400,7 @@ static int _check_udev_fallback(struct cmd_context *cmd) #endif /* UDEV_SYNC_SUPPORT */ static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *lv, - const char *layer) + const char *layer, int noscan, int temporary) { uint16_t udev_flags = 0; @@ -1447,6 +1448,16 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, struct logical_volume *l udev_flags |= DM_UDEV_DISABLE_DM_RULES_FLAG | DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG; + /* + * LVM subsystem specific flags. + */ + if (noscan) + udev_flags |= DM_SUBSYSTEM_UDEV_FLAG0; + + if (temporary) + udev_flags |= DM_UDEV_DISABLE_DISK_RULES_FLAG | + DM_UDEV_DISABLE_OTHER_RULES_FLAG; + return udev_flags; } @@ -1493,7 +1504,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, } if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor, - _get_udev_flags(dm, lv, layer))) { + _get_udev_flags(dm, lv, layer, 0, 0))) { log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree", info.major, info.minor); return 0; @@ -2334,7 +2345,7 @@ static int _set_udev_flags_for_children(struct dev_manager *dm, } dm_tree_node_set_udev_flags(child, - _get_udev_flags(dm, lvl->lv, NULL)); + _get_udev_flags(dm, lvl->lv, NULL, 0, 0)); } return 1; @@ -2411,7 +2422,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree, read_only_lv(lv, laopts), ((lv->vg->status & PRECOMMITTED) | laopts->revert) ? 1 : 0, lvlayer, - _get_udev_flags(dm, lv, layer)))) + _get_udev_flags(dm, lv, layer, laopts->noscan, laopts->temporary)))) return_0; /* Store existing name so we can do rename later */ diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c index f43283f..8940704 100644 --- a/lib/cache/lvmetad.c +++ b/lib/cache/lvmetad.c @@ -392,8 +392,6 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna pvl->pv->dev = lvmcache_device(info); if (!pvl->pv->dev) pvl->pv->status |= MISSING_PV; - else - check_reappeared_pv(vg, pvl->pv); if (!lvmcache_fid_add_mdas_pv(info, fid)) { vg = NULL; goto_out; /* FIXME error path */ diff --git a/lib/device/device-types.h b/lib/device/device-types.h index d25c2f0..d716878 100644 --- a/lib/device/device-types.h +++ b/lib/device/device-types.h @@ -61,5 +61,6 @@ static const dev_known_type_t _dev_known_types[] = { {"skd", 16, "STEC"}, {"scm", 8, "Storage Class Memory (IBM S/390)"}, {"bcache", 1, "bcache block device cache"}, + {"nvme", 64, "NVM Express"}, {"", 0, ""} }; diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c index 5077576..e31429e 100644 --- a/lib/format_text/flags.c +++ b/lib/format_text/flags.c @@ -61,6 +61,8 @@ static const struct flag _lv_flags[] = { {LV_REBUILD, "REBUILD", STATUS_FLAG}, {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG}, {LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG}, + {LV_NOSCAN, NULL, 0}, + {LV_TEMPORARY, NULL, 0}, {POOL_METADATA_SPARE, NULL, 0}, {RAID, NULL, 0}, {RAID_META, NULL, 0}, @@ -144,8 +146,8 @@ int print_flags(uint64_t status, int type, char *buffer, size_t size) return 0; if (status) - log_warn("Metadata inconsistency: Not all flags successfully " - "exported."); + log_warn(INTERNAL_ERROR "Metadata inconsistency: " + "Not all flags successfully exported."); return 1; } diff --git a/lib/locking/file_locking.c b/lib/locking/file_locking.c index 5e49bc4..ab3dabd 100644 --- a/lib/locking/file_locking.c +++ b/lib/locking/file_locking.c @@ -305,7 +305,8 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource, break; case LCK_READ: log_very_verbose("Locking LV %s (R)", resource); - if (!lv_activate_with_filter(cmd, resource, 0, lv_ondisk(lv))) + if (!lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, + lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv))) return 0; break; case LCK_PREAD: @@ -318,7 +319,8 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource, break; case LCK_EXCL: log_very_verbose("Locking LV %s (EX)", resource); - if (!lv_activate_with_filter(cmd, resource, 1, lv_ondisk(lv))) + if (!lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, + lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv))) return 0; break; default: diff --git a/lib/locking/no_locking.c b/lib/locking/no_locking.c index 5f3f0b6..dac2f80 100644 --- a/lib/locking/no_locking.c +++ b/lib/locking/no_locking.c @@ -48,11 +48,13 @@ static int _no_lock_resource(struct cmd_context *cmd, const char *resource, case LCK_UNLOCK: return lv_resume_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1: 0, 0, (flags & LCK_REVERT) ? 1 : 0, lv_ondisk(lv)); case LCK_READ: - return lv_activate_with_filter(cmd, resource, 0, lv_ondisk(lv)); + return lv_activate_with_filter(cmd, resource, 0, lv->status & LV_NOSCAN ? 1 : 0, + lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv)); case LCK_WRITE: return lv_suspend_if_active(cmd, resource, (flags & LCK_ORIGIN_ONLY) ? 1 : 0, 0, lv_ondisk(lv), lv); case LCK_EXCL: - return lv_activate_with_filter(cmd, resource, 1, lv_ondisk(lv)); + return lv_activate_with_filter(cmd, resource, 1, lv->status & LV_NOSCAN ? 1 : 0, + lv->status & LV_TEMPORARY ? 1 : 0, lv_ondisk(lv)); default: break; } diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c index e3fda18..e59bd8e 100644 --- a/lib/metadata/lv.c +++ b/lib/metadata/lv.c @@ -565,7 +565,13 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv) else if (lv->status & MIRROR_IMAGE) repstr[0] = (_lv_mimage_in_sync(lv)) ? 'i' : 'I'; else if (lv->status & RAID_IMAGE) - repstr[0] = (_lv_raid_image_in_sync(lv)) ? 'i' : 'I'; + /* + * Visible RAID_IMAGES are sub-LVs that have been exposed for + * top-level use by being split from the RAID array with + * '--splitmirrors 1 --trackchanges'. They always report 'I'. + */ + repstr[0] = (!lv_is_visible(lv) && _lv_raid_image_in_sync(lv)) ? + 'i' : 'I'; else if (lv->status & MIRROR_LOG) repstr[0] = 'l'; else if (lv_is_cow(lv)) diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index f42db1d..88fceb2 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -89,7 +89,7 @@ static int _lv_is_on_pv(struct cmd_context *cmd, struct physical_volume *pv2; struct lv_segment *seg; - if (!lv || !(seg = first_seg(lv))) + if (!lv || !(first_seg(lv))) return_0; /* @@ -4685,6 +4685,7 @@ int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume * struct dm_list *snh, *snht; struct lv_list *lvl; struct lvinfo info; + struct logical_volume *origin; int is_last_pool; if (lv_is_cow(lv)) { @@ -4713,7 +4714,10 @@ int lv_remove_with_dependencies(struct cmd_context *cmd, struct logical_volume * return 0; } } - } + } else if (!level && lv_is_virtual_origin(origin = origin_from_cow(lv))) + /* If this is a sparse device, remove its origin too. */ + /* Stacking is not supported */ + lv = origin; } if (lv_is_origin(lv)) { @@ -5421,6 +5425,8 @@ int set_lv(struct cmd_context *cmd, struct logical_volume *lv, if (!dev_close_immediate(dev)) stack; + lv->status &= ~LV_NOSCAN; + return 1; } @@ -5977,6 +5983,13 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, goto out; } + /* Do not scan this LV until properly zeroed. */ + if (lp->zero) + lv->status |= LV_NOSCAN; + + if (lp->temporary) + lv->status |= LV_TEMPORARY; + if (lv_is_thin_pool(lv)) { if (is_change_activating(lp->activate)) { if (vg_is_clustered(lv->vg)) { diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 308dcfe..c00e4e5 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -102,6 +102,12 @@ #define LV_WRITEMOSTLY UINT64_C(0x0000020000000000) /* LV (RAID1) */ #define LV_ACTIVATION_SKIP UINT64_C(0x0000040000000000) /* LV */ +#define LV_NOSCAN UINT64_C(0x0000080000000000) /* LV - internal use only - the LV + should not be scanned */ +#define LV_TEMPORARY UINT64_C(0x0000100000000000) /* LV - internal use only - the LV + is supposed to be created and + removed during single LVM + command execution. */ /* Format features flags */ #define FMT_SEGMENTS 0x00000001U /* Arbitrary segment params? */ @@ -720,6 +726,7 @@ struct lvcreate_params { int log_count; /* mirror */ int nosync; /* mirror */ int poolmetadataspare; /* thin pool */ + int temporary; /* temporary LV */ #define ACTIVATION_SKIP_SET 0x01 /* request to set LV activation skip flag state */ #define ACTIVATION_SKIP_SET_ENABLED 0x02 /* set the LV activation skip flag state to 'enabled' */ #define ACTIVATION_SKIP_IGNORE 0x04 /* request to ignore LV activation skip flag (if any) */ diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index 4ffd502..8571e0a 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -2883,10 +2883,11 @@ int vg_missing_pv_count(const struct volume_group *vg) return ret; } -void check_reappeared_pv(struct volume_group *correct_vg, - struct physical_volume *pv) +static int _check_reappeared_pv(struct volume_group *correct_vg, + struct physical_volume *pv, int act) { struct pv_list *pvl; + int rv = 0; /* * Skip these checks in case the tool is going to deal with missing @@ -2894,21 +2895,47 @@ void check_reappeared_pv(struct volume_group *correct_vg, * confusing. */ if (correct_vg->cmd->handles_missing_pvs) - return; + return rv; dm_list_iterate_items(pvl, &correct_vg->pvs) if (pv->dev == pvl->pv->dev && is_missing_pv(pvl->pv)) { - log_warn("Missing device %s reappeared, updating " - "metadata for VG %s to version %u.", - pv_dev_name(pvl->pv), pv_vg_name(pvl->pv), - correct_vg->seqno); + if (act) + log_warn("Missing device %s reappeared, updating " + "metadata for VG %s to version %u.", + pv_dev_name(pvl->pv), pv_vg_name(pvl->pv), + correct_vg->seqno); if (pvl->pv->pe_alloc_count == 0) { - pv->status &= ~MISSING_PV; - pvl->pv->status &= ~MISSING_PV; - } else + if (act) { + pv->status &= ~MISSING_PV; + pvl->pv->status &= ~MISSING_PV; + } + ++ rv; + } else if (act) log_warn("Device still marked missing because of allocated data " "on it, remove volumes and consider vgreduce --removemissing."); } + return rv; +} + +static int _repair_inconsistent_vg(struct volume_group *vg) +{ + unsigned saved_handles_missing_pvs = vg->cmd->handles_missing_pvs; + + vg->cmd->handles_missing_pvs = 1; + if (!vg_write(vg)) { + log_error("Automatic metadata correction failed"); + vg->cmd->handles_missing_pvs = saved_handles_missing_pvs; + return 0; + } + + vg->cmd->handles_missing_pvs = saved_handles_missing_pvs; + + if (!vg_commit(vg)) { + log_error("Automatic metadata correction commit failed"); + return 0; + } + + return 1; } static int _check_mda_in_use(struct metadata_area *mda, void *_in_use) @@ -2951,12 +2978,12 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, int inconsistent_mdas = 0; int inconsistent_mda_count = 0; unsigned use_precommitted = precommitted; - unsigned saved_handles_missing_pvs = cmd->handles_missing_pvs; struct dm_list *pvids; struct pv_list *pvl, *pvl2; struct dm_list all_pvs; char uuid[64] __attribute__((aligned(8))); unsigned seqno = 0; + int reappeared = 0; if (is_orphan_vg(vgname)) { if (use_precommitted) { @@ -2969,8 +2996,16 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, } if (lvmetad_active() && !use_precommitted) { - *consistent = 1; - return lvmcache_get_vg(cmd, vgname, vgid, precommitted); + if ((correct_vg = lvmcache_get_vg(cmd, vgname, vgid, precommitted))) { + dm_list_iterate_items(pvl, &correct_vg->pvs) + if (pvl->pv->dev) + reappeared += _check_reappeared_pv(correct_vg, pvl->pv, *consistent); + if (reappeared && *consistent) + *consistent = _repair_inconsistent_vg(correct_vg); + else + *consistent = !reappeared; + } + return correct_vg; } /* @@ -3339,22 +3374,11 @@ static struct volume_group *_vg_read(struct cmd_context *cmd, * update metadata and remove MISSING flag */ dm_list_iterate_items(pvl, &all_pvs) - check_reappeared_pv(correct_vg, pvl->pv); + _check_reappeared_pv(correct_vg, pvl->pv, 1); - cmd->handles_missing_pvs = 1; - if (!vg_write(correct_vg)) { - log_error("Automatic metadata correction failed"); + if (!_repair_inconsistent_vg(correct_vg)) { _free_pv_list(&all_pvs); release_vg(correct_vg); - cmd->handles_missing_pvs = saved_handles_missing_pvs; - return NULL; - } - cmd->handles_missing_pvs = saved_handles_missing_pvs; - - if (!vg_commit(correct_vg)) { - log_error("Automatic metadata correction commit " - "failed"); - release_vg(correct_vg); return NULL; } diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h index 2408c23..21ac204 100644 --- a/lib/metadata/metadata.h +++ b/lib/metadata/metadata.h @@ -486,7 +486,4 @@ int add_pv_to_vg(struct volume_group *vg, const char *pv_name, uint64_t find_min_mda_size(struct dm_list *mdas); char *tags_format_and_copy(struct dm_pool *mem, const struct dm_list *tags); -void check_reappeared_pv(struct volume_group *correct_vg, - struct physical_volume *pv); - #endif diff --git a/lib/metadata/snapshot_manip.c b/lib/metadata/snapshot_manip.c index 325a4e8..fc1273e 100644 --- a/lib/metadata/snapshot_manip.c +++ b/lib/metadata/snapshot_manip.c @@ -227,6 +227,16 @@ int vg_remove_snapshot(struct logical_volume *cow) struct logical_volume *origin = origin_from_cow(cow); int is_origin_active = lv_is_active(origin); + if (is_origin_active && + lv_is_virtual_origin(origin)) { + if (!deactivate_lv(origin->vg->cmd, origin)) { + log_error("Failed to deactivate logical volume \"%s\"", + origin->name); + return 0; + } + is_origin_active = 0; + } + dm_list_del(&cow->snapshot->origin_list); origin->origin_count--; diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c index a6e0fc2..bd5b117 100644 --- a/lib/metadata/thin_manip.c +++ b/lib/metadata/thin_manip.c @@ -809,6 +809,7 @@ static struct logical_volume *_alloc_pool_metadata_spare(struct volume_group *vg .stripes = 1, .vg_name = vg->name, .zero = 1, + .temporary = 1, }; dm_list_init(&lp.tags); diff --git a/lib/snapshot/snapshot.c b/lib/snapshot/snapshot.c index 506d618..91e778f 100644 --- a/lib/snapshot/snapshot.c +++ b/lib/snapshot/snapshot.c @@ -23,6 +23,10 @@ #include "str_list.h" #include "defaults.h" +#define SEG_LOG_ERROR(t, p...) \ + log_error(t " segment %s of logical volume %s.", ## p, \ + dm_config_parent_name(sn), seg->lv->name), 0; + static const char *_snap_name(const struct lv_segment *seg) { return seg->segtype->name; @@ -41,50 +45,45 @@ static int _snap_text_import(struct lv_segment *seg, const struct dm_config_node struct dm_hash_table *pv_hash __attribute__((unused))) { uint32_t chunk_size; - const char *org_name, *cow_name; struct logical_volume *org, *cow; - int old_suppress, merge = 0; + const char *org_name = NULL, *cow_name = NULL; + int merge = 0; if (!dm_config_get_uint32(sn, "chunk_size", &chunk_size)) { log_error("Couldn't read chunk size for snapshot."); return 0; } - old_suppress = log_suppress(1); - - if ((cow_name = dm_config_find_str(sn, "merging_store", NULL))) { - if (dm_config_find_str(sn, "cow_store", NULL)) { - log_suppress(old_suppress); - log_error("Both snapshot cow and merging storage were specified."); - return 0; - } + if (dm_config_has_node(sn, "merging_store")) { + if (!(cow_name = dm_config_find_str(sn, "merging_store", NULL))) + return SEG_LOG_ERROR("Merging store must be a string in"); merge = 1; } - else if (!(cow_name = dm_config_find_str(sn, "cow_store", NULL))) { - log_suppress(old_suppress); - log_error("Snapshot cow storage not specified."); - return 0; - } - if (!(org_name = dm_config_find_str(sn, "origin", NULL))) { - log_suppress(old_suppress); - log_error("Snapshot origin not specified."); - return 0; + if (dm_config_has_node(sn, "cow_store")) { + if (cow_name) + return SEG_LOG_ERROR("Both snapshot cow and merging storage were specified in"); + + if (!(cow_name = dm_config_find_str(sn, "cow_store", NULL))) + return SEG_LOG_ERROR("Cow store must be a string in"); } - log_suppress(old_suppress); + if (!cow_name) + return SEG_LOG_ERROR("Snapshot cow storage not specified in"); - if (!(cow = find_lv(seg->lv->vg, cow_name))) { - log_error("Unknown logical volume specified for " - "snapshot cow store."); - return 0; - } + if (!dm_config_has_node(sn, "origin")) + return SEG_LOG_ERROR("Snapshot origin not specified in"); - if (!(org = find_lv(seg->lv->vg, org_name))) { - log_error("Unknown logical volume specified for " - "snapshot origin."); - return 0; - } + if (!(org_name = dm_config_find_str(sn, "origin", NULL))) + return SEG_LOG_ERROR("Snapshot origin must be a string in"); + + if (!(cow = find_lv(seg->lv->vg, cow_name))) + return SEG_LOG_ERROR("Unknown logical volume %s specified for " + "snapshot cow store in", cow_name); + + if (!(org = find_lv(seg->lv->vg, org_name))) + return SEG_LOG_ERROR("Unknown logical volume %s specified for " + "snapshot origin in", org_name); init_snapshot_seg(seg, org, cow, chunk_size, merge); diff --git a/libdaemon/client/daemon-io.c b/libdaemon/client/daemon-io.c index 906f375..e2c5180 100644 --- a/libdaemon/client/daemon-io.c +++ b/libdaemon/client/daemon-io.c @@ -38,7 +38,7 @@ int buffer_read(int fd, struct buffer *buffer) { result = read(fd, buffer->mem + buffer->used, buffer->allocated - buffer->used); if (result > 0) { buffer->used += result; - if (!strncmp((buffer->mem) + buffer->used - 4, "\n##\n", 4)) { + if (buffer->used >= 4 && !strncmp((buffer->mem) + buffer->used - 4, "\n##\n", 4)) { buffer->used -= 4; buffer->mem[buffer->used] = 0; break; /* success, we have the full message now */ diff --git a/libdaemon/server/daemon-server.c b/libdaemon/server/daemon-server.c index df2c852..b114b9f 100644 --- a/libdaemon/server/daemon-server.c +++ b/libdaemon/server/daemon-server.c @@ -381,6 +381,7 @@ static void *client_thread(void *baton) request req; response res; + b->client.thread_id = pthread_self(); buffer_init(&req.buffer); while (1) { @@ -431,6 +432,7 @@ static int handle_connect(daemon_state s) struct sockaddr_un sockaddr; client_handle client = { .thread_id = 0 }; socklen_t sl = sizeof(sockaddr); + pthread_t tid; client.socket_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl); if (client.socket_fd < 0) @@ -446,10 +448,10 @@ static int handle_connect(daemon_state s) baton->s = s; baton->client = client; - if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton)) + if (pthread_create(&tid, NULL, client_thread, baton)) return 0; - pthread_detach(baton->client.thread_id); + pthread_detach(tid); return 1; } diff --git a/libdm/Makefile.in b/libdm/Makefile.in index bddb0a0..2aa70d4 100644 --- a/libdm/Makefile.in +++ b/libdm/Makefile.in @@ -57,7 +57,7 @@ include $(top_builddir)/make.tmpl DEFS += -DDM_DEVICE_UID=@DM_DEVICE_UID@ -DDM_DEVICE_GID=@DM_DEVICE_GID@ \ -DDM_DEVICE_MODE=@DM_DEVICE_MODE@ -LIBS += $(SELINUX_LIBS) $(UDEV_LIBS) +LIBS += $(SELINUX_LIBS) $(UDEV_LIBS) $(PTHREAD_LIBS) device-mapper: all diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h index aaf00b2..adfbb76 100644 --- a/libdm/libdevmapper.h +++ b/libdm/libdevmapper.h @@ -21,7 +21,7 @@ #include #include -#ifdef linux +#ifdef __linux__ # include #endif diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c index b66911c..9a52f2e 100644 --- a/libdm/libdm-common.c +++ b/libdm/libdm-common.c @@ -966,7 +966,9 @@ static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor, (void) dm_prepare_selinux_context(path, S_IFBLK); old_mask = umask(0); - if (mknod(path, S_IFBLK | mode, dev) < 0) { + + /* The node may already have been created by udev. So ignore EEXIST. */ + if (mknod(path, S_IFBLK | mode, dev) < 0 && errno != EEXIST) { log_error("%s: mknod for %s failed: %s", path, dev_name, strerror(errno)); umask(old_mask); (void) dm_prepare_selinux_context(NULL, 0); @@ -998,7 +1000,8 @@ static int _rm_dev_node(const char *dev_name, int warn_if_udev_failed) log_warn("Node %s was not removed by udev. " "Falling back to direct node removal.", path); - if (unlink(path) < 0) { + /* udev may already have deleted the node. Ignore ENOENT. */ + if (unlink(path) < 0 && errno != ENOENT) { log_error("Unable to unlink device node for '%s'", dev_name); return 0; } @@ -1054,7 +1057,8 @@ static int _rename_dev_node(const char *old_name, const char *new_name, "Falling back to direct node rename.", oldpath, newpath); - if (rename(oldpath, newpath) < 0) { + /* udev may already have renamed the node. Ignore ENOENT. */ + if (rename(oldpath, newpath) < 0 && errno != ENOENT) { log_error("Unable to rename device node from '%s' to '%s'", old_name, new_name); return 0; @@ -1795,7 +1799,8 @@ int dm_device_has_holders(uint32_t major, uint32_t minor) } if (stat(sysfs_path, &st)) { - log_sys_error("stat", sysfs_path); + if (errno != ENOENT) + log_sys_error("stat", sysfs_path); return 0; } diff --git a/libdm/mm/pool-fast.c b/libdm/mm/pool-fast.c index 2b494d6..edb31a0 100644 --- a/libdm/mm/pool-fast.c +++ b/libdm/mm/pool-fast.c @@ -62,7 +62,9 @@ struct dm_pool *dm_pool_create(const char *name, size_t chunk_hint) while (new_size < p->chunk_size) new_size <<= 1; p->chunk_size = new_size; + pthread_mutex_lock(&_dm_pools_mutex); dm_list_add(&_dm_pools, &p->list); + pthread_mutex_unlock(&_dm_pools_mutex); return p; } @@ -77,7 +79,9 @@ void dm_pool_destroy(struct dm_pool *p) c = pr; } + pthread_mutex_lock(&_dm_pools_mutex); dm_list_del(&p->list); + pthread_mutex_unlock(&_dm_pools_mutex); dm_free(p); } diff --git a/libdm/mm/pool.c b/libdm/mm/pool.c index fd08307..ef006a4 100644 --- a/libdm/mm/pool.c +++ b/libdm/mm/pool.c @@ -15,9 +15,10 @@ #include "dmlib.h" #include +#include -/* FIXME: thread unsafe */ static DM_LIST_INIT(_dm_pools); +static pthread_mutex_t _dm_pools_mutex = PTHREAD_MUTEX_INITIALIZER; void dm_pools_check_leaks(void); #ifdef DEBUG_ENFORCE_POOL_LOCKING @@ -81,8 +82,11 @@ void dm_pools_check_leaks(void) { struct dm_pool *p; - if (dm_list_empty(&_dm_pools)) + pthread_mutex_lock(&_dm_pools_mutex); + if (dm_list_empty(&_dm_pools)) { + pthread_mutex_unlock(&_dm_pools_mutex); return; + } log_error("You have a memory leak (not released memory pool):"); dm_list_iterate_items(p, &_dm_pools) { @@ -94,6 +98,7 @@ void dm_pools_check_leaks(void) log_error(" [%p] %s", p, p->name); #endif } + pthread_mutex_unlock(&_dm_pools_mutex); log_error(INTERNAL_ERROR "Unreleased memory pool(s) found."); } diff --git a/man/pvscan.8.in b/man/pvscan.8.in index 211c82b..37ecaaf 100644 --- a/man/pvscan.8.in +++ b/man/pvscan.8.in @@ -25,7 +25,9 @@ pvscan \- scan all disks for physical volumes .B \-\-minor .I minor | -.IR DevicePath ]... +.IR DevicePath +| +.IR major:minor ]... .SH DESCRIPTION pvscan scans all supported LVM block devices in the system for physical volumes. diff --git a/scripts/Makefile.in b/scripts/Makefile.in index a658903..3616afa 100644 --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -107,6 +107,7 @@ endif ifeq ("@BUILD_LVMETAD@", "yes") $(INSTALL_DATA) lvm2_lvmetad_systemd_red_hat.socket $(systemd_unit_dir)/lvm2-lvmetad.socket $(INSTALL_DATA) lvm2_lvmetad_systemd_red_hat.service $(systemd_unit_dir)/lvm2-lvmetad.service + $(INSTALL_DATA) lvm2_pvscan_systemd_red_hat@.service $(systemd_unit_dir)/lvm2-pvscan@.service endif install_tmpfiles_configuration: @@ -118,6 +119,7 @@ DISTCLEAN_TARGETS += clvmd_init_red_hat cmirrord_init_red_hat \ dm_event_systemd_red_hat.socket dm_event_systemd_red_hat.service \ lvm2_monitoring_systemd_red_hat.service \ lvm2_lvmetad_systemd_red_hat.socket lvm2_lvmetad_systemd_red_hat.service \ + lvm2_pvscan_systemd_red_hat@.service \ lvm2_tmpfiles_red_hat.conf blk_availability_init_red_hat \ blk_availability_systemd_red_hat.service \ blkdeactivate.sh diff --git a/scripts/blkdeactivate.sh.in b/scripts/blkdeactivate.sh.in index b6d0117..f454154 100644 --- a/scripts/blkdeactivate.sh.in +++ b/scripts/blkdeactivate.sh.in @@ -323,7 +323,10 @@ deactivate_all() { $LSBLK_READ <<< "`$LSBLK --nodeps $1`" # check if the device is not on the skip list already - test -z ${SKIP_DEVICE_LIST["$kname"]} || continue + test -z ${SKIP_DEVICE_LIST["$kname"]} || { + shift + continue + } deactivate else diff --git a/scripts/lvm2_pvscan_systemd_red_hat@.service.in b/scripts/lvm2_pvscan_systemd_red_hat@.service.in new file mode 100644 index 0000000..4225982 --- /dev/null +++ b/scripts/lvm2_pvscan_systemd_red_hat@.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=LVM2 PV scan on device %i +Documentation=man:pvscan(8) +DefaultDependencies=no +BindsTo=dev-block-%i.device +After=lvm2-lvmetad.socket +Before=shutdown.target +Conflicts=shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@sbindir@/pvscan --cache --activate ay /dev/block/%i +ExecStop=@sbindir@/pvscan --cache %i diff --git a/scripts/vgimportclone.sh b/scripts/vgimportclone.sh index d6ad75d..7087557 100755 --- a/scripts/vgimportclone.sh +++ b/scripts/vgimportclone.sh @@ -204,8 +204,8 @@ for ARG do if [ -b "$ARG" ] then - PVS_OUT=`"${LVM}" pvs ${LVM_OPTS} --noheadings -o vg_name "$ARG" 2>/dev/null` - checkvalue $? "$ARG is not a PV." + PVS_OUT=`"${LVM}" pvs ${LVM_OPTS} --noheadings -o vg_name "$ARG"` + checkvalue $? "$ARG could not be verified to be a PV without errors." PV_VGNAME=$(echo $PVS_OUT | $GREP -v '[[:space:]]+$') [ -z "$PV_VGNAME" ] && die 3 "$ARG is not in a VG." @@ -227,7 +227,7 @@ fi ### Get the existing state so we can use it later ##################################################################### -OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings 2>/dev/null` +OLDVGS=`"${LVM}" vgs ${LVM_OPTS} -o name --noheadings` checkvalue $? "Current VG names could not be collected without errors" ##################################################################### @@ -280,7 +280,7 @@ export LVM_SYSTEM_DIR=${TMP_LVM_SYSTEM_DIR} ### Rename the VG(s) and change the VG and PV UUIDs. ##################################################################### -PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator : 2>/dev/null` +PVINFO=`"${LVM}" pvs ${LVM_OPTS} -o pv_name,vg_name,vg_attr --noheadings --separator :` checkvalue $? "PV info could not be collected without errors" # output VG info so each line looks like: name:exported?:disk1,disk2,... diff --git a/test/Makefile.in b/test/Makefile.in index 1b9789f..943a4ce 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -25,6 +25,10 @@ abs_top_builddir = "@abs_top_builddir@" abs_top_srcdir = "@abs_top_srcdir@" LVM_TEST_RESULTS ?= results +export LVM_TEST_THIN_CHECK_CMD?=@THIN_CHECK_CMD@ +export LVM_TEST_THIN_DUMP_CMD?=@THIN_DUMP_CMD@ +export LVM_TEST_THIN_REPAIR_CMD?=@THIN_REPAIR_CMD@ + SUBDIRS = api unit SOURCES = lib/not.c lib/harness.c @@ -34,7 +38,8 @@ T ?= . S ?= @ # never match anything by default VERBOSE ?= 0 ALL = $(shell find $(srcdir) \( -path \*/shell/\*.sh -or -path \*/api/\*.sh \) | sort) -RUN = $(shell find $(srcdir) -regextype posix-egrep \( -path \*/shell/\*.sh -or -path \*/api/\*.sh \) -and -regex "$(srcdir)/.*($(T)).*" -and -not -regex "$(srcdir)/.*($(S)).*" | sort) +comma = , +RUN = $(shell find $(srcdir) -regextype posix-egrep \( -path \*/shell/\*.sh -or -path \*/api/\*.sh \) -and -regex "$(srcdir)/.*($(subst $(comma),|,$(T))).*" -and -not -regex "$(srcdir)/.*($(subst $(comma),|,$(S))).*" | sort) RUN_BASE = $(subst $(srcdir)/,,$(RUN)) # Shell quote; @@ -57,12 +62,15 @@ help: @echo " help Display callable targets." @echo -e "\nSupported variables:" @echo " LVM_TEST_DEVDIR Set to '/dev' to run on real /dev." - @echo " LVM_TEST_DIR Where to create test files [TMPDIR]." + @echo " LVM_TEST_DIR Where to create test files [$(LVM_TEST_DIR)]." @echo " LVM_TEST_LOCKING Normal (1), Cluster (3)." @echo " LVM_TEST_LVMETAD Start lvmetad (1)." @echo " LVM_TEST_NODEBUG Do not debug lvm commands." @echo " LVM_TEST_PARALLEL May skip agresive wipe of LVMTEST resources." @echo " LVM_TEST_RESULTS Where to create result files [results]." + @echo " LVM_TEST_THIN_CHECK_CMD Command for thin_check [$(LVM_TEST_THIN_CHECK_CMD)]." + @echo " LVM_TEST_THIN_DUMP_CMD Command for thin_dump [$(LVM_TEST_THIN_DUMP_CMD)]." + @echo " LVM_TEST_THIN_REPAIR_CMD Command for thin_repair [$(LVM_TEST_THIN_REPAIR_CMD)]." @echo " LVM_TEST_UNLIMITED Set to get unlimited test log (>32MB)" @echo " LVM_VERIFY_UDEV Default verify state for lvm.conf." @echo " S Skip given test (regex)." diff --git a/test/lib/aux.sh b/test/lib/aux.sh index d27d263..f51fc0d 100644 --- a/test/lib/aux.sh +++ b/test/lib/aux.sh @@ -162,7 +162,7 @@ teardown_devs() { local stray_loops=( $(losetup -a | grep "$COMMON_PREFIX" | cut -d: -f1) ) test ${#stray_loops[@]} -eq 0 || { echo "Removing stray loop devices containing $COMMON_PREFIX: ${stray_loops[@]}" - losetup -d "${stray_loops[@]}" + for i in "${stray_loops[@]}" ; do losetup -d $i ; done } } } diff --git a/test/lib/get.sh b/test/lib/get.sh index 7b97c06..5dd5451 100644 --- a/test/lib/get.sh +++ b/test/lib/get.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Copyright (C) 2011-2013 Red Hat, Inc. All rights reserved. # # This copyrighted material is made available to anyone wishing to use, diff --git a/test/lib/harness.c b/test/lib/harness.c index 0036502..8c9f4a7 100644 --- a/test/lib/harness.c +++ b/test/lib/harness.c @@ -173,7 +173,7 @@ static void _append_buf(const char *buf, size_t len) kill(-pid, SIGINT); return; } - readbuf_sz = readbuf_sz ? 2 * readbuf_sz : 4096; + readbuf_sz = readbuf_sz ? 2 * readbuf_sz : 16384; readbuf = realloc(readbuf, readbuf_sz); } @@ -221,7 +221,7 @@ static const char *_append_with_stamp(const char *buf, int stamp) static void drain(int fd) { - char buf[4096]; + char buf[4096 + 1]; const char *bp; int stamp = 0; int sz; @@ -322,7 +322,7 @@ static void failed(int i, char *f, int st) { ++ s.nfailed; s.status[i] = FAILED; - printf("FAILED.\n"); + printf("FAILED (status %d).\n", WEXITSTATUS(st)); if (!verbose && readbuf) { printf("-- FAILED %s ------------------------------------\n", f); dump(); @@ -340,12 +340,12 @@ static void run(int i, char *f) { exit(201); } else if (pid == 0) { if (!interactive) { - close(0); - dup2(fds[0], 1); - dup2(fds[0], 2); - close(fds[0]); + close(STDIN_FILENO); + dup2(fds[1], STDOUT_FILENO); + dup2(fds[1], STDERR_FILENO); close(fds[1]); } + close(fds[0]); if (strchr(f, ':')) { strcpy(flavour, f); *strchr(flavour, ':') = 0; @@ -372,6 +372,7 @@ static void run(int i, char *f) { FILE *varlogmsg; int fd_vlm = -1; + //close(fds[1]); snprintf(buf, sizeof(buf), "%s ...", f); printf("Running %-60s ", buf); fflush(stdout); @@ -404,14 +405,14 @@ static void run(int i, char *f) { } FD_ZERO(&set); - FD_SET(fds[1], &set); + FD_SET(fds[0], &set); selectwait.tv_sec = 0; selectwait.tv_usec = 500000; /* timeout 0.5s */ - if (select(fds[1] + 1, &set, NULL, NULL, &selectwait) <= 0) { + if (select(fds[0] + 1, &set, NULL, NULL, &selectwait) <= 0) { no_write++; continue; } - drain(fds[1]); + drain(fds[0]); no_write = 0; if (fd_vlm >= 0) drain(fd_vlm); @@ -420,7 +421,7 @@ static void run(int i, char *f) { perror("waitpid"); exit(206); } - drain(fds[1]); + drain(fds[0]); if (fd_vlm >= 0) drain(fd_vlm); if (die == 2) @@ -472,12 +473,13 @@ int main(int argc, char **argv) { results = getenv("LVM_TEST_RESULTS") ? : "results"; (void) snprintf(results_list, sizeof(results_list), "%s/list", results); + //if (pipe(fds)) { if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) { perror("socketpair"); return 201; } - if (fcntl(fds[1], F_SETFL, O_NONBLOCK ) == -1) { + if (fcntl(fds[0], F_SETFL, O_NONBLOCK ) == -1) { perror("fcntl on socket"); return 202; } @@ -537,10 +539,10 @@ int main(int argc, char **argv) { printf("skipped: %s\n", argv[i]); break; case INTERRUPTED: - printf("interrupted: %s\n", argv[i]); + printf("INTERRUPTED: %s\n", argv[i]); break; case TIMEOUT: - printf("timeout: %s\n", argv[i]); + printf("TIMEOUT: %s\n", argv[i]); break; default: /* do nothing */ ; } diff --git a/test/shell/lvconvert-repair-raid.sh b/test/shell/lvconvert-repair-raid.sh index 84e247d..71c84c1 100644 --- a/test/shell/lvconvert-repair-raid.sh +++ b/test/shell/lvconvert-repair-raid.sh @@ -29,7 +29,7 @@ vgextend $vg "$dev3" lvremove -ff $vg # RAID6 double replace -lvcreate --type raid5 -i 3 -l 2 -n $lv1 $vg \ +lvcreate --type raid6 -i 3 -l 2 -n $lv1 $vg \ "$dev1" "$dev2" "$dev3" "$dev4" "$dev5" aux wait_for_sync $vg $lv1 aux disable_dev "$dev4" "$dev5" diff --git a/test/shell/lvconvert-repair-thin.sh b/test/shell/lvconvert-repair-thin.sh new file mode 100644 index 0000000..aa301d6 --- /dev/null +++ b/test/shell/lvconvert-repair-thin.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +# Copyright (C) 2013 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# 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 repairing of broken thin pool metadata + +. lib/test + +which mkfs.ext2 || skip + +# By default use tools from configuration (exported through Makefile) +# Allow user to override location of binaries to take tools from different laces +# Maybe check also version of the tools here? +test -n "$LVM_TEST_THIN_CHECK_CMD" || LVM_TEST_THIN_CHECK_CMD=$(which thin_check) || skip +test -n "$LVM_TEST_THIN_DUMP_CMD" || LVM_TEST_THIN_DUMP_CMD=$(which thin_dump) || skip +test -n "$LVM_TEST_THIN_REPAIR_CMD" || LVM_TEST_THIN_REPAIR_CMD=$(which thin_repair) || skip + +# +# Main +# +aux have_thin 1 0 0 || skip + +aux prepare_vg 4 + +# Create LV +lvcreate -T -L20 -V10 -n $lv1 $vg/pool "$dev1" "$dev2" +lvcreate -T -V10 -n $lv2 $vg/pool + +mkfs.ext2 $DM_DEV_DIR/$vg/$lv1 +mkfs.ext2 $DM_DEV_DIR/$vg/$lv2 + +lvcreate -L20 -n repair $vg +lvcreate -L2 -n fixed $vg + +lvs -a -o+seg_pe_ranges $vg +#aux error_dev "$dev2" 2050:1 + +# Make some repairable metadata damage?? +vgchange -an $vg + +lvconvert --repair $vg/pool + +lvs -a $vg + +# Test swapping - swap out thin-pool's metadata with our repair volume +lvconvert -y -f --poolmetadata $vg/repair --thinpool $vg/pool + +lvchange -aey $vg/repair $vg/fixed + +#dd if=$DM_DEV_DIR/$vg/repair of=back bs=1M + +# Make some 'repairable' damage?? +dd if=/dev/zero of=$DM_DEV_DIR/$vg/repair bs=1 seek=40960 count=1 + +#dd if=$DM_DEV_DIR/$vg/repair of=back_trashed bs=1M +#not vgchange -ay $vg + +#lvconvert --repair $vg/pool + +# Using now SHOULD - since thin tools currently do not seem to work +should not $THIN_CHECK $DM_DEV_DIR/$vg/repair + +should not $LVM_TEST_THIN_DUMP_CMD $DM_DEV_DIR/$vg/repair | tee dump + +should $LVM_TEST_THIN_REPAIR_CMD -i $DM_DEV_DIR/$vg/repair -o $DM_DEV_DIR/$vg/fixed + +should $LVM_TEST_THIN_DUMP_CMD --repair $DM_DEV_DIR/$vg/repair | tee repaired_xml + +should $LVM_TEST_THIN_CHECK_CMD $DM_DEV_DIR/$vg/fixed + +# Swap repaired metadata back +lvconvert -y -f --poolmetadata $vg/fixed --thinpool $vg/pool +lvs -a $vg + +# Activate pool - this should now work +should vgchange -ay $vg + +lvs -a -o+devices $vg +dmsetup table +dmsetup info -c +dmsetup ls --tree + +lvchange -an $vg + +# FIXME: Currently in deep troubles - we can't remove thin volume from broken pool +should lvremove -ff $vg + +# let's not block PVs with openned _tdata/_tmeta devices +dmsetup remove $vg-pool_tdata || true +dmsetup remove $vg-pool_tmeta || true + +dmsetup table + +# FIXME: needs also --yes with double force +pvremove --yes -ff "$dev1" +pvremove --yes -ff "$dev2" + +# FIXME: pv1 & pv2 are removed so pv3 & pv4 have no real LVs, +# yet vgremove is refusing to do its jobs and suggest --partial?? +should vgremove -ff $vg + +# FIXME: stressing even more - there are no pool PV, we do not pass... +should vgreduce --removemissing -f $vg +should vgremove -ff $vg + +# Let's do a final forced cleanup +pvremove --yes -ff "$dev3" +pvremove --yes -ff "$dev4" diff --git a/test/shell/lvconvert-thin-external.sh b/test/shell/lvconvert-thin-external.sh index a700b37..d9d4d19 100644 --- a/test/shell/lvconvert-thin-external.sh +++ b/test/shell/lvconvert-thin-external.sh @@ -144,5 +144,13 @@ lvs -a -o+origin_size,seg_size,segtype $vg lvremove -f $vg/extorg2 # Only pool is left check vg_field $vg lv_count 1 +lvremove -ff $vg + +# Test conversion to the pool and thin external at the same time (rhbz #1003461) +lvcreate -l50 -n pool $vg +lvcreate -l100 -n thin $vg +lvconvert --thin --thinpool $vg/pool $vg/thin --originname thin-origin +check lv_field $vg/thin segtype thin +check lv_field $vg/thin-origin segtype linear vgremove -ff $vg diff --git a/test/shell/lvcreate-usage.sh b/test/shell/lvcreate-usage.sh index 68a15a9..105fbce 100644 --- a/test/shell/lvcreate-usage.sh +++ b/test/shell/lvcreate-usage.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (C) 2008-2011 Red Hat, Inc. All rights reserved. +# Copyright (C) 2008-2013 Red Hat, Inc. All rights reserved. # # This copyrighted material is made available to anyone wishing to use, # modify, copy, or redistribute it subject to the terms and conditions @@ -15,7 +15,7 @@ aux prepare_pvs 4 aux pvcreate --metadatacopies 0 "$dev1" -vgcreate -cn $vg $(cat DEVICES) +vgcreate $vg $(cat DEVICES) # "lvcreate rejects repeated invocation (run 2 times) (bz178216)" lvcreate -n $lv -l 4 $vg @@ -58,7 +58,7 @@ test -z "$(lvdisplay $vg)" # Setting max_lv works. (bz490298) lvremove -ff $vg vgchange -l 3 $vg -lvcreate -l1 -n $lv1 $vg +lvcreate -aey -l1 -n $lv1 $vg lvcreate -l1 -s -n $lv2 $vg/$lv1 lvcreate -l1 -n $lv3 $vg not lvcreate -l1 -n $lv4 $vg @@ -71,13 +71,13 @@ not lvcreate -l1 -n $lv4 $vg not lvcreate -l1 --type mirror -m1 -n $lv4 $vg lvremove -ff $vg/$lv3 -lvcreate -l1 --type mirror -m1 -n $lv3 $vg +lvcreate -aey -l1 --type mirror -m1 -n $lv3 $vg vgs -o +max_lv $vg not lvcreate -l1 -n $lv4 $vg not lvcreate -l1 --type mirror -m1 -n $lv4 $vg lvconvert -m0 $vg/$lv3 -lvconvert -m2 -i 1 $vg/$lv3 +lvconvert -m2 --type mirror -i 1 $vg/$lv3 lvconvert -m1 $vg/$lv3 not vgchange -l 2 @@ -90,8 +90,8 @@ vgchange -l 0 $vg # lvcreate rejects invalid chunksize, accepts between 4K and 512K # validate origin_size vgremove -ff $vg -vgcreate -cn $vg $(cat DEVICES) -lvcreate -L 32m -n $lv1 $vg +vgcreate $vg $(cat DEVICES) +lvcreate -aey -L 32m -n $lv1 $vg not lvcreate -L 8m -n $lv2 -s --chunksize 3k $vg/$lv1 not lvcreate -L 8m -n $lv2 -s --chunksize 1024k $vg/$lv1 lvcreate -L 8m -n $lv2 -s --chunksize 4k $vg/$lv1 @@ -111,10 +111,10 @@ not lvcreate -L 32m -n $lv -R0 $vg 2>err grep "Non-zero region size must be supplied." err not lvcreate -L 32m -n $lv -R 11k $vg not lvcreate -L 32m -n $lv -R 1k $vg -lvcreate -L 32m -n $lv --regionsize 128m --type mirror -m 1 $vg +lvcreate -aey -L 32m -n $lv --regionsize 128m --type mirror -m 1 $vg check lv_field $vg/$lv regionsize "32.00m" lvremove -ff $vg -lvcreate -L 32m -n $lv --regionsize 4m --type mirror -m 1 $vg +lvcreate -aey -L 32m -n $lv --regionsize 4m --type mirror -m 1 $vg check lv_field $vg/$lv regionsize "4.00m" lvremove -ff $vg diff --git a/test/shell/lvmcache-exercise.sh b/test/shell/lvmcache-exercise.sh index b1e2b92..6e8efda 100644 --- a/test/shell/lvmcache-exercise.sh +++ b/test/shell/lvmcache-exercise.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (C) 2008 Red Hat, Inc. All rights reserved. +# Copyright (C) 2008-2013 Red Hat, Inc. All rights reserved. # # This copyrighted material is made available to anyone wishing to use, # modify, copy, or redistribute it subject to the terms and conditions @@ -14,10 +14,23 @@ aux prepare_pvs 5 vgcreate $vg1 "$dev1" -vgcreate $vg2 "$dev3" +vgcreate $vg2 "$dev3" "$dev4" "$dev5" aux disable_dev "$dev1" pvscan vgcreate $vg1 "$dev2" aux enable_dev "$dev1" pvs + +# reappearing device (rhbz 995440) +lvcreate -aey -m2 --type mirror -l4 --alloc anywhere --corelog -n $lv1 $vg2 + +aux disable_dev "$dev3" +lvconvert --yes --repair $vg2/$lv1 +aux enable_dev "$dev3" + +# here it should fix any reappeared devices +lvs + +lvs -a $vg2 -o+devices 2>&1 | tee out +not grep reappeared out diff --git a/test/shell/snapshot-usage.sh b/test/shell/snapshot-usage.sh index 9c1c2f6..3d4d308 100644 --- a/test/shell/snapshot-usage.sh +++ b/test/shell/snapshot-usage.sh @@ -17,6 +17,13 @@ fill() { dd if=/dev/zero of=$DM_DEV_DIR/$vg1/lvol0 bs=$1 count=1 } +cleanup_tail() +{ + test -z "$SLEEP_PID" || kill $SLEEP_PID + wait + aux teardown +} + aux prepare_pvs 1 vgcreate -s 4M $vg $(cat DEVICES) @@ -30,6 +37,34 @@ aux lvmconf "activation/snapshot_autoextend_percent = 20" \ pvcreate --setphysicalvolumesize 4T $DM_DEV_DIR/$vg/$lv vgcreate -s 1K $vg1 $DM_DEV_DIR/$vg/$lv +# Test removal of opened snapshot +lvcreate -V50 -L10 -n $lv1 -s $vg1 + +lvs -a -o+lv_active $vg1 +lvchange -an $vg1 + +# Snapshot get exclusive activation +lvchange -ay $vg1 +lvs -a -o+lv_active $vg1 + +trap 'cleanup_tail' EXIT +# Keep device busy (but not mounted) for a while +sleep 30 < $DM_DEV_DIR/$vg1/$lv1 & +SLEEP_PID=$! + +# Opened virtual snapshot device is not removable +# it should retry device removal for a few seconds +not lvremove -f $vg1/$lv1 + +kill $SLEEP_PID +SLEEP_PID= +# Wait for killed task, so there is no device holder +wait + +lvremove -f $vg1/$lv1 +not dmsetup info $vg1-$lv1 >/dev/null || \ + die "$vg1/$lv1 expected to be removed, but there are mappings!" + # Check border size lvcreate -aey -L4095G $vg1 lvcreate -s -L100K $vg1/lvol0 @@ -79,7 +114,7 @@ lvcreate -an -Zn -l50%FREE -n $lv1 $vg1 lvcreate -s -l100%FREE -n $lv2 $vg1/$lv1 check lv_field $vg1/$lv2 size "7.50p" lvremove -ff $vg1 - + lvcreate -aey -V15E -l1 -n $lv1 -s $vg1 check lv_field $vg1/$lv1 origin_size "15.00e" diff --git a/test/shell/vgrename-usage.sh b/test/shell/vgrename-usage.sh index 2b8ac5a..59576ac 100644 --- a/test/shell/vgrename-usage.sh +++ b/test/shell/vgrename-usage.sh @@ -38,3 +38,18 @@ vgcreate $vg1 "$dev1" vgcreate $vg2 "$dev2" not vgrename $vg1 $vg2 vgremove $vg1 $vg2 + +# vgrename duplicate name +vgcreate $vg1 "$dev1" +aux disable_dev "$dev1" +vgcreate $vg1 "$dev2" +UUID=$(vgs --noheading -o vg_uuid $vg1) +aux enable_dev "$dev1" + +not vgrename $vg1 $vg2 +vgrename $UUID $vg2 +not vgrename $UUID $vg1 + +vgs + +vgremove $vg1 $vg2 diff --git a/tools/dmsetup.c b/tools/dmsetup.c index 517e8aa..a0ee23e 100644 --- a/tools/dmsetup.c +++ b/tools/dmsetup.c @@ -1147,7 +1147,7 @@ static int _udevcomplete_all(CMD_ARGS) unsigned age = 0; time_t t; - if (argc == 2 && (sscanf(argv[1], "%i", &age) != 1)) { + if (argc == 2 && (sscanf(argv[1], "%u", &age) != 1)) { log_error("Failed to read age_in_minutes parameter."); return 0; } diff --git a/tools/lvchange.c b/tools/lvchange.c index 1d4f0a5..6ae9720 100644 --- a/tools/lvchange.c +++ b/tools/lvchange.c @@ -120,8 +120,7 @@ static int lvchange_pool_update(struct cmd_context *cmd, if (((discards == THIN_DISCARDS_IGNORE) || (first_seg(lv)->discards == THIN_DISCARDS_IGNORE)) && pool_is_active(lv)) - log_error("Cannot change discards state for active " - "pool volume \"%s\".", lv->name); + log_error("Cannot change support for discards while pool volume \"%s\" is active.", lv->name); else { first_seg(lv)->discards = discards; update++; diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 49881fa..a6c1187 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -1891,6 +1891,10 @@ static int lvconvert_merge(struct cmd_context *cmd, find_merging_snapshot(origin)->cow->name); return 0; } + if (lv_is_virtual_origin(origin)) { + log_error("Snapshot %s has virtual origin.", lv->name); + return 0; + } /* * Prevent merge with open device(s) as it would likely lead @@ -2266,11 +2270,12 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, int r = 0; const char *old_name; struct lv_segment *seg; - struct logical_volume *data_lv = pool_lv; + struct logical_volume *data_lv; struct logical_volume *metadata_lv; struct logical_volume *pool_metadata_lv; struct logical_volume *external_lv = NULL; char metadata_name[NAME_LEN], data_name[NAME_LEN]; + int activate_pool; if (!lv_is_visible(pool_lv)) { log_error("Can't convert internal LV %s/%s.", @@ -2299,17 +2304,22 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, } if (lv_is_thin_pool(pool_lv)) { + activate_pool = lv_is_active(pool_lv); r = 1; /* Already existing thin pool */ goto out; } } + data_lv = pool_lv; if (lv_is_thin_type(pool_lv) && !lp->pool_metadata_lv_name) { log_error("Can't use thin logical volume %s/%s for thin pool data.", pool_lv->vg->name, pool_lv->name); return 0; } + /* Allow to have only thinpool active and restore it's active state */ + activate_pool = lv_is_active(pool_lv); + /* We are changing target type, so deactivate first */ if (!deactivate_lv(cmd, pool_lv)) { log_error("Aborting. Failed to deactivate logical volume %s/%s.", @@ -2317,6 +2327,13 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, return 0; } + if (lv_is_thin_pool(pool_lv) && pool_is_active(pool_lv)) { + /* If any thin volume is also active - abort here */ + log_error("Cannot convert pool %s/%s with active thin volumes.", + pool_lv->vg->name, pool_lv->name); + return 0; + } + if ((dm_snprintf(metadata_name, sizeof(metadata_name), "%s_tmeta", pool_lv->name) < 0) || (dm_snprintf(data_name, sizeof(data_name), "%s_tdata", @@ -2418,6 +2435,7 @@ static int _lvconvert_thinpool(struct cmd_context *cmd, goto mda_write; } + metadata_lv->status |= LV_NOSCAN; if (!lv_is_active(metadata_lv) && !activate_lv_local(cmd, metadata_lv)) { log_error("Aborting. Failed to activate thin metadata lv."); @@ -2510,7 +2528,8 @@ mda_write: if (!vg_write(pool_lv->vg) || !vg_commit(pool_lv->vg)) return_0; - if (!activate_lv_excl(cmd, pool_lv)) { + if (activate_pool && + !activate_lv_excl(cmd, pool_lv)) { log_error("Failed to activate pool logical volume %s/%s.", pool_lv->vg->name, pool_lv->name); /* Deactivate subvolumes */ diff --git a/tools/lvremove.c b/tools/lvremove.c index 4f48746..dfc435c 100644 --- a/tools/lvremove.c +++ b/tools/lvremove.c @@ -18,14 +18,6 @@ static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv, void *handle __attribute__((unused))) { - struct logical_volume *origin; - - /* - * If this is a sparse device, remove its origin too. - */ - if (lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv))) - lv = origin; - if (!lv_remove_with_dependencies(cmd, lv, (force_t) arg_count(cmd, force_ARG), 0)) return_ECMD_FAILED; diff --git a/tools/pvscan.c b/tools/pvscan.c index 3f16b05..b6a07bd 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -132,6 +132,27 @@ out: return r; } +static int _clear_dev_from_lvmetad_cache(dev_t devno, int32_t major, int32_t minor, + activation_handler handler) +{ + char *buf; + + if (!dm_asprintf(&buf, "%" PRIi32 ":%" PRIi32, major, minor)) + stack; + if (!lvmetad_pv_gone(devno, buf ? : "", handler)) { + if (buf) + dm_free(buf); + return 0; + } + + log_print_unless_silent("Device %s not found. " + "Cleared from lvmetad cache.", buf ? : ""); + if (buf) + dm_free(buf); + + return 1; +} + static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) { int ret = ECMD_PROCESSED; @@ -142,7 +163,6 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) int devno_args = 0; struct arg_value_group_list *current_group; dev_t devno; - char *buf; activation_handler handler = NULL; /* @@ -193,11 +213,30 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) /* Process any command line PVs first. */ while (argc--) { pv_name = *argv++; - dev = dev_cache_get(pv_name, cmd->lvmetad_filter); - if (!dev) { - log_error("Physical Volume %s not found.", pv_name); - ret = ECMD_FAILED; - continue; + if (pv_name[0] == '/') { + /* device path */ + if (!(dev = dev_cache_get(pv_name, cmd->lvmetad_filter))) { + log_error("Physical Volume %s not found.", pv_name); + ret = ECMD_FAILED; + continue; + } + } + else { + /* device major:minor */ + if (sscanf(pv_name, "%d:%d", &major, &minor) != 2) { + log_error("Failed to parse major:minor from %s", pv_name); + ret = ECMD_FAILED; + continue; + } + devno = MKDEV((dev_t)major, minor); + if (!(dev = dev_cache_get_by_devt(devno, cmd->lvmetad_filter))) { + if (!(_clear_dev_from_lvmetad_cache(devno, major, minor, handler))) { + stack; + ret = ECMD_FAILED; + break; + } + continue; + } } if (sigint_caught()) { ret = ECMD_FAILED; @@ -225,19 +264,11 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv) devno = MKDEV((dev_t)major, minor); if (!(dev = dev_cache_get_by_devt(devno, cmd->lvmetad_filter))) { - if (!dm_asprintf(&buf, "%" PRIi32 ":%" PRIi32, major, minor)) + if (!(_clear_dev_from_lvmetad_cache(devno, major, minor, handler))) { stack; - if (!lvmetad_pv_gone(devno, buf ? : "", handler)) { ret = ECMD_FAILED; - if (buf) - dm_free(buf); break; } - - log_print_unless_silent("Device %s not found. " - "Cleared from lvmetad cache.", buf ? : ""); - if (buf) - dm_free(buf); continue; } if (sigint_caught()) { diff --git a/tools/vgrename.c b/tools/vgrename.c index 154a6f3..b5e778f 100644 --- a/tools/vgrename.c +++ b/tools/vgrename.c @@ -83,6 +83,8 @@ static int vg_rename_path(struct cmd_context *cmd, const char *old_vg_path, if (!lvmetad_vg_list_to_lvmcache(cmd)) stack; + lvmcache_label_scan(cmd, 2); + /* Avoid duplicates */ if (!(vgids = get_vgids(cmd, 0)) || dm_list_empty(vgids)) { log_error("No complete volume groups found"); diff --git a/udev/11-dm-lvm.rules.in b/udev/11-dm-lvm.rules.in index f21d0aa..5032280 100644 --- a/udev/11-dm-lvm.rules.in +++ b/udev/11-dm-lvm.rules.in @@ -20,6 +20,21 @@ ENV{DM_UUID}!="LVM-?*", GOTO="lvm_end" # Use DM name and split it up into its VG/LV/layer constituents. IMPORT{program}="(DM_EXEC)/dmsetup splitname --nameprefixes --noheadings --rows $env{DM_NAME}" +# DM_SUBSYSTEM_UDEV_FLAG0 is the 'NOSCAN' flag for LVM subsystem. +# This flag is used to temporarily disable selected rules to prevent any +# processing or scanning done on the LVM volume before LVM has any chance +# to zero any stale metadata found within the LV data area. Such stale +# metadata could cause false claim of the LV device, keeping it open etc. +# +# If the NOSCAN flag is present, backup selected existing flags used to +# disable rules, then set them firmly so those selected rules are surely skipped. +# Restore these flags once the NOSCAN flag is dropped (which is normally any +# uevent that follows for this LV, even an artificially generated one). +ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_NOSCAN}="1", ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" +ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", IMPORT{db}="DM_NOSCAN", IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" +ENV{DM_SUBSYSTEM_UDEV_FLAG0}!="1", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}", \ + ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG_OLD}="", ENV{DM_NOSCAN}="" + ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}=="1", GOTO="lvm_end" OPTIONS+="event_timeout=180" diff --git a/udev/13-dm-disk.rules.in b/udev/13-dm-disk.rules.in index 1920260..4b64dd6 100644 --- a/udev/13-dm-disk.rules.in +++ b/udev/13-dm-disk.rules.in @@ -18,6 +18,7 @@ SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}" ENV{DM_UUID}=="?*", SYMLINK+="disk/by-id/dm-uuid-$env{DM_UUID}" ENV{DM_SUSPENDED}=="1", GOTO="dm_end" +ENV{DM_NOSCAN}=="1", GOTO="dm_watch" (BLKID_RULE) ENV{DM_UDEV_LOW_PRIORITY_FLAG}=="1", OPTIONS="link_priority=-100" @@ -32,7 +33,7 @@ ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk # (like creating a filesystem, changing filesystem label etc.). # # But let's use this until we have something better... - +LABEL="dm_watch" OPTIONS+="watch" LABEL="dm_end" diff --git a/udev/69-dm-lvm-metad.rules.in b/udev/69-dm-lvm-metad.rules.in index ba43396..3e303b1 100644 --- a/udev/69-dm-lvm-metad.rules.in +++ b/udev/69-dm-lvm-metad.rules.in @@ -17,6 +17,8 @@ SUBSYSTEM!="block", GOTO="lvm_end" (LVM_EXEC_RULE) +ENV{DM_NOSCAN}=="1", GOTO="lvm_end" + # If the PV label got lost, inform lvmetad immediately. # Detect the lost PV label by comparing previous ID_FS_TYPE value with current one. ENV{.ID_FS_TYPE_NEW}="$env{ID_FS_TYPE}" @@ -77,6 +79,6 @@ LABEL="lvm_scan" # MD | | X | X* | | # loop | | X | X* | | # other | X | | X | | X -RUN+="(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major $major --minor $minor", ENV{LVM_SCANNED}="1" +(PVSCAN_RULE) LABEL="lvm_end" diff --git a/udev/Makefile.in b/udev/Makefile.in index 5c15bdb..40a4671 100644 --- a/udev/Makefile.in +++ b/udev/Makefile.in @@ -46,8 +46,14 @@ else BLKID_RULE=IMPORT{program}=\"${SBIN}\/blkid -o udev -p \$$tempnode\" endif +ifeq ("@UDEV_SYSTEMD_BACKGROUND_JOBS@", "yes") +PVSCAN_RULE=ENV{SYSTEMD_ALIAS}=\"\/dev\/block\/\$$major:\$$minor\"\nENV{ID_MODEL}=\"LVM PV \$$env{ID_FS_UUID_ENC} on \/dev\/\$$name\"\nENV{SYSTEMD_WANTS}=\"lvm2-pvscan@\$$major:\$$minor.service\" +else +PVSCAN_RULE=RUN\+\=\"$(LVM_EXEC)/lvm pvscan --background --cache --activate ay --major \$$major --minor \$$minor\", ENV{LVM_SCANNED}=\"1\" +endif + %.rules: %.rules.in - $(SED) -e "s+(DM_DIR)+$(DM_DIR)+;s+(BLKID_RULE)+$(BLKID_RULE)+;s+(DM_EXEC_RULE)+$(DM_EXEC_RULE)+;s+(DM_EXEC)+$(DM_EXEC)+;s+(LVM_EXEC_RULE)+$(LVM_EXEC_RULE)+;s+(LVM_EXEC)+$(LVM_EXEC)+;" $< >$@ + $(SED) -e "s+(DM_DIR)+$(DM_DIR)+;s+(BLKID_RULE)+$(BLKID_RULE)+;s+(PVSCAN_RULE)+$(PVSCAN_RULE)+;s+(DM_EXEC_RULE)+$(DM_EXEC_RULE)+;s+(DM_EXEC)+$(DM_EXEC)+;s+(LVM_EXEC_RULE)+$(LVM_EXEC_RULE)+;s+(LVM_EXEC)+$(LVM_EXEC)+;" $< >$@ %_install: %.rules $(INSTALL_DATA) -D $< $(udevdir)/$(