summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2014-11-21 15:30:17 -0600
committerDavid Teigland <teigland@redhat.com>2014-11-21 16:22:56 -0600
commit4b34a78114bc05448f877c3eae10046757c1a0ec (patch)
treebb26403c51b02e0e2d7257876c67ae6d44afeab5
parent615bef7cb363567ecd62b47973f06772f390e5b4 (diff)
downloadlvm2-dev-dct-lvmlockd7-lockvg.tar.gz
lvmlockd: vg locksdev-dct-lvmlockd7-lockvg
The VG lock is a simple read/write lock that protects a VG's metadata. The VG lock is used in shared mode to read the VG and in exclusive mode to modify the VG. The function to acquire/release the VG lock is dlock_vg(): . To acquire the vg lock in ex mode for writing: dlock_vg(cmd, vg_name, "ex", 0); . To acquire the vg lock in sh mode for reading: dlock_vg(cmd, vg_name, "sh", 0); . To release the vg lock: dlock_vg(cmd, vg_name, "un", 0); The dlock_vg() function sends a message to lvmlockd, asking for the lock in the specified mode. lvmlockd acquires the lock from the underlying lock manager, and sends the result back to the command. When the command exits, or calls dlock_vg("un"), lvmlockd releases the lock in the underlying lock manager. When lvm is compiled without lvmlockd support, all the dlock_vg calls simply compile to success (1). Using the vg lock in commands is simple: . A command that wants to read the VG should acquire the vg lock in the sh mode, then read the VG: dlock_vg(cmd, vg_name, "sh", 0); vg = vg_read(cmd, vg_name); dlock_vg(cmd, vg_name, "un", 0); . A command that wants to write the VG should acquire the vg lock in the ex mode, then make changes to the VG and write it: dlock_vg(cmd, vg_name, "ex", 0); vg = vg_read(cmd, vg_name); ... vg_write(vg); vg_commit(vg); dlock_vg(cmd, vg_name, "un", 0); When a command processes multiple VGs, e.g. using toollib process_each, then VG lock should be explicitly unlocked when the command is done processing the VG, i.e. dlock_vg(cmd, vg_name, "un", 0) as shown above. When a command processes a single VG or pair of VGs, then the command can simply exit and lvmlockd will automatically unlock the VG lock(s) that the command had acquired. Locking conflicts: When a command calls dlock_vg(), the lock request is passed to lvmlockd. lvmlockd makes the corresponding lock request in the lock manager using a non-blocking request. If another command on another host holds the vg lock in a conflicting mode, the lock request fails, and lvmlockd returns the failure to the command. The command reports the lock conflict and fails. A future option may enable lvmlockd to automatically retry lock requests that fail due to conflicts with locks of commands running concurrently on other hosts. (These retries could be disabled or limited to a certain number of a command or config option.) This way, simple, transient command locking conflicts would be hidden. Caching: lvmlockd uses the lvb in the VG lock to hold the VG seqno. When a command writes a VG with a new seqno (under an ex lock), it sends lvmlockd the new VG seqno by calling lvmlockd_vg_update(vg). When lvmlockd unlocks the ex VG lock, it saves the latest seqno in the vg lock's lvb. When other hosts next acquire the VG lock, they will read the lvb, see that the seqno is higher than the last seqno they saw, and know that their cached copy of the VG is stale. When lvmlockd sees this, it invalidates the cached copy of the VG in lvmetad. When a command next reads the VG from lvmetad, it will see that it's stale, will reread the latest VG from disk, and update the cached copy in lvmetad. These commands do not yet work with lvmlockd lock_types (sanlock|dlm): . vgsplit . vgmerge . vgrename . lvrename
-rw-r--r--lib/locking/lvmlockd.c30
-rw-r--r--lib/locking/lvmlockd.h5
-rw-r--r--lib/metadata/metadata.c4
-rw-r--r--tools/args.h1
-rw-r--r--tools/dlock.c245
-rw-r--r--tools/dlock.h11
-rw-r--r--tools/lvconvert.c3
-rw-r--r--tools/lvcreate.c3
-rw-r--r--tools/lvmcmdline.c2
-rw-r--r--tools/lvrename.c5
-rw-r--r--tools/lvresize.c3
-rw-r--r--tools/toollib.c17
-rw-r--r--tools/tools.h2
-rw-r--r--tools/vgchange.c37
-rw-r--r--tools/vgmerge.c7
-rw-r--r--tools/vgreduce.c2
-rw-r--r--tools/vgremove.c7
-rw-r--r--tools/vgrename.c6
-rw-r--r--tools/vgsplit.c7
19 files changed, 391 insertions, 6 deletions
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
index ec8f1c40e..b9b684d4d 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -881,3 +881,33 @@ out:
return ret;
}
+int lvmlockd_vg_update(struct volume_group *vg)
+{
+ daemon_reply reply;
+ int result;
+ int ret;
+
+ if (!is_dlock_type(vg->lock_type))
+ return 1;
+
+ if (!_lvmlockd_active)
+ return 1;
+ if (!lvmlockd_connected())
+ return 0;
+
+ reply = _lvmlockd_send("vg_update",
+ "pid = %d", getpid(),
+ "vg_name = %s", vg->name,
+ "version = %d", (int64_t)vg->seqno,
+ NULL);
+
+ if (!_lvmlockd_result(reply, &result, NULL)) {
+ ret = 0;
+ } else {
+ ret = (result < 0) ? 0 : 1;
+ }
+
+ daemon_reply_destroy(reply);
+ return ret;
+}
+
diff --git a/lib/locking/lvmlockd.h b/lib/locking/lvmlockd.h
index 87b49ccfe..9ee8f9acb 100644
--- a/lib/locking/lvmlockd.h
+++ b/lib/locking/lvmlockd.h
@@ -104,6 +104,9 @@ void lvmlockd_free_vg_final(struct cmd_context *cmd, struct volume_group *vg);
int lvmlockd_start_vg(struct cmd_context *cmd, struct volume_group *vg);
int lvmlockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg);
+/* notify lvmlockd of a new VG seqno */
+
+int lvmlockd_vg_update(struct volume_group *vg);
#else /* LVMLOCKD_SUPPORT */
@@ -123,6 +126,8 @@ int lvmlockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg);
#define lvmlockd_start_vg(cmd, vg) (1)
#define lvmlockd_stop_vg(cmd, vg) (1)
+#define lvmlockd_vg_update(vg) (1)
+
#endif /* LVMLOCKD_SUPPORT */
#endif
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 9e1bb9e5f..220de162f 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -605,6 +605,8 @@ int vg_remove(struct volume_group *vg)
if (!lvmetad_vg_remove(vg))
stack;
+ lvmlockd_vg_update(vg);
+
if (!backup_remove(vg->cmd, vg->name))
stack;
@@ -2903,6 +2905,8 @@ int vg_commit(struct volume_group *vg)
if ((vg->fid->fmt->features & FMT_PRECOMMIT) && !lvmetad_vg_update(vg))
return_0;
+ lvmlockd_vg_update(vg);
+
cache_updated = _vg_commit_mdas(vg);
if (cache_updated) {
diff --git a/tools/args.h b/tools/args.h
index 72f592dd5..12c9ce934 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -48,6 +48,7 @@ arg(ignoreskippedcluster_ARG, '\0', "ignoreskippedcluster", NULL, 0)
arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", NULL, 0)
arg(labelsector_ARG, '\0', "labelsector", int_arg, 0)
arg(lockgl_ARG, '\0', "lock-gl", string_arg, 0)
+arg(lockvg_ARG, '\0', "lock-vg", string_arg, 0)
arg(lockstart_ARG, '\0', "lock-start", string_arg, 0)
arg(lockstop_ARG, '\0', "lock-stop", string_arg, 0)
arg(locktype_ARG, '\0', "lock-type", string_arg, 0)
diff --git a/tools/dlock.c b/tools/dlock.c
index cefa54d7c..ab2a47291 100644
--- a/tools/dlock.c
+++ b/tools/dlock.c
@@ -135,11 +135,12 @@ int dlock_gl_create(struct cmd_context *cmd, const char *def_mode, uint32_t flag
/*
* result and result_flags were returned from lvmlockd.
* In lvmlockd, result 0 is success, and error is < 0.
+ *
+ * ENOLS: no lockspace was found with a global lock.
+ * It may not exist (perhaps this command is creating the first),
+ * or it may not be visible or started on the system yet.
*/
- /*
- * No lockspace was found with a global lock.
- */
if (result == -ENOLS) {
/*
* It's not a problem if an unlock happens after the
@@ -319,7 +320,14 @@ int dlock_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
/*
* result and result_flags were returned from lvmlockd.
- * in lvmlockd, result 0 is success, and error is < 0.
+ * In lvmlockd, result 0 is success, and error is < 0.
+ *
+ * ENOLS: no lockspace was found with a global lock.
+ * The VG with the global lock may not be visible or started yet,
+ * this should be a temporary condition.
+ *
+ * ESTARTING: the lockspace with the gl is starting.
+ * The VG with the global lock is starting and should finish shortly.
*/
if (result == -ENOLS || result == -ESTARTING) {
@@ -378,3 +386,232 @@ int dlock_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
return 1;
}
+int dlock_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *def_mode, uint32_t flags)
+{
+ const char *mode = NULL;
+ const char *opts = NULL;
+ uint32_t result_flags;
+ int result;
+ int ret;
+
+ /*
+ * Only real vgs have locks; orphans are covered by global lock.
+ */
+ if (!is_real_vg(vg_name))
+ return 1;
+
+ /*
+ * DLOCK_VG_NA is used in special cases to disable the vg lock.
+ */
+ if (cmd->command->flags & DLOCK_VG_NA)
+ return 1;
+
+ /*
+ * DL_VG_MODE_NOARG disables getting the mode from --lock-vg arg.
+ */
+ if (!(flags & DL_VG_MODE_NOARG)) {
+ mode = arg_str_value(cmd, lockvg_ARG, NULL);
+ if (mode && def_mode &&
+ (_mode_compare(mode, def_mode) < 0) &&
+ !find_config_tree_bool(cmd, global_allow_override_lock_modes_CFG, NULL)) {
+ log_error("Disallowed lock-vg mode \"%s\"", mode);
+ return 0;
+ }
+ }
+
+ if (!mode)
+ mode = def_mode;
+
+ if (!mode) {
+ /*
+ * Default mode is needed, but was not provided
+ * in the function args. This happens when dlock_vg
+ * is called from a process_each function that handles
+ * different commands. Commands that only
+ * read/check/report/display the vg have DLOCK_VG_SH
+ * set in commands.h. All other commands modify the vg.
+ */
+ if (cmd->command->flags & DLOCK_VG_SH)
+ mode = "sh";
+ else
+ mode = "ex";
+ }
+
+ if (!strcmp(mode, "ex") && find_config_tree_bool(cmd, global_read_only_lock_modes_CFG, NULL)) {
+ log_error("Disallow lock-vg ex with read_only_lock_modes");
+ return 0;
+ }
+
+ ret = lvmlockd_send(cmd, command_name(cmd), "lock_vg",
+ vg_name, NULL, NULL, NULL, NULL, mode, opts,
+ &result, &result_flags);
+ if (!ret) {
+ /* No result from lvmlockd, it is probably not running. */
+
+ /* See comment in dlock_gl() about this case. */
+
+ if (arg_count(cmd, sysinit_ARG) || arg_count(cmd, ignorelockingfailure_ARG)) {
+ log_debug("Ignore failed locking for VG %s: option %s", vg_name,
+ arg_count(cmd, sysinit_ARG) ? "sysinit" : "ignorelockingfailure");
+ return 1;
+ }
+
+ /* See comment in dlock_gl() about these cases. */
+
+ if (!strcmp(mode, "un"))
+ return 1;
+
+ if (!strcmp(mode, "sh")) {
+ log_warn("Reading VG %s without shared lock.", vg_name);
+ return 1;
+ }
+
+ log_error("Locking failed for VG %s", vg_name);
+ return 0;
+ }
+
+ /*
+ * result and result_flags were returned from lvmlockd.
+ * In lvmlockd, result 0 is success, and error is < 0.
+ *
+ * ELOCALVG: the VG is local and does not need a dlock.
+ *
+ * EOTHERVG: the VG is local and belongs to another system_id.
+ *
+ * ENOLS: no lockspace for the VG was found, the VG may not
+ * be started yet. The VG should be started manually or by system,
+ * e.g. vgchange --lock-start
+ *
+ * ESTARTING: the lockspace for the VG is starting and should
+ * finish shortly.
+ */
+
+ if (result == -ELOCALVG)
+ return 1;
+
+ if (result == -EOTHERVG) {
+ /*
+ * The VG is local and owned by another system_id.
+ * lvmlockd knows this because it caches the identity of
+ * local VGs (for the ELOCALVG case above).
+ *
+ * . By returning 0 (failure) here we can skip this VG
+ * before vg_read() is even called.
+ *
+ * . By returning 1 (success) here we will continue through
+ * vg_read(), after which we will see the foreign
+ * system_id in the metadata, and return FAILED_SYSTEMID.
+ * This VG would then be skipped by ignore_vg() instead
+ * of being skipped here.
+ *
+ * Skipping the foreign VG here is more efficient, but not
+ * consistent with the way foreign VGs are ignored when
+ * lvmlockd is not used, so we return success instead.
+ * If we decide to skip the VG here (return 0), then we
+ * would need to override this when cmd->include_foreign_vgs
+ * is set (the command wants to read/report foreign VGs.)
+ */
+ log_debug("local VG %s owned by other system_id", vg_name);
+ return 0;
+ }
+
+ if (result == -ENOLS || result == -ESTARTING) {
+
+ /* It doesn't matter if an unlock operation fails. */
+ if (!strcmp(mode, "un"))
+ return 1;
+
+ if (strcmp(mode, "sh")) {
+ /*
+ * An ex lock request always fails here. Based on the
+ * result number and result flags we can often print a
+ * reason for the failure.
+ *
+ * (In the future we might want to continue through
+ * vg_read with the lock and fail after the vg_read.
+ * Doing the vg_read may provide more information about
+ * the failure here.)
+ */
+ if ((result == -ENOLS) && (result_flags & LD_RF_INACTIVE_LS)) {
+ if (result_flags & LD_RF_ADD_LS_ERROR)
+ log_error("VG %s lock failed: lock start error", vg_name);
+ else
+ log_error("VG %s lock failed: locking stopped", vg_name);
+
+ } else if (result == -ENOLS) {
+ log_error("VG %s lock failed: lock start required", vg_name);
+
+ } else if (result == -ESTARTING) {
+ log_error("VG %s lock failed: lock start in progress", vg_name);
+
+ } else {
+ log_error("VG %s lock failed: %d", vg_name, result);
+ }
+ return 0;
+ }
+
+ /*
+ * When a sh lock failed we will allow the command to proceed
+ * because there's little harm that it could do. See the
+ * reasoning above for proceeding after a failed gl sh lock.
+ * Do we want to invalidate the cached VG in these cases to
+ * force rereading from disk?
+ */
+
+ if (result == -ESTARTING) {
+ log_warn("Skipping lock for VG %s: lock start in progress", vg_name);
+ return 1;
+ }
+
+ if ((result == -ENOLS) && (result_flags & LD_RF_ADD_LS_ERROR)) {
+ log_warn("Skipping lock for VG %s: lock start error", vg_name);
+ return 1;
+ }
+
+ if ((result == -ENOLS) && (result_flags & LD_RF_INACTIVE_LS)) {
+ log_warn("Skipping lock for VG %s: locking stopped", vg_name);
+ return 1;
+ }
+
+ if (result == -ENOLS) {
+ log_warn("Skipping lock for VG %s: lock start required", vg_name);
+ return 1;
+ }
+
+ log_error("VG %s shared lock error %d", vg_name, result);
+ return 0;
+ }
+
+ /*
+ * A notice from lvmlockd that duplicate gl locks have been found.
+ * It would be good for the user to disable one of them.
+ */
+ if ((result_flags & LD_RF_DUP_GL_LS) && strcmp(mode, "un"))
+ log_warn("Duplicate sanlock global lock in VG %s", vg_name);
+
+ if (result < 0) {
+ log_error("VG %s lock error: %d", vg_name, result);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* A shortcut for back to back dlock_gl() + dlock_vg() */
+
+int dlock_gl_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *def_gl_mode, const char *def_vg_mode,
+ uint32_t flags)
+{
+ if (!dlock_gl(cmd, def_gl_mode, flags))
+ return 0;
+
+ if (!dlock_vg(cmd, vg_name, def_vg_mode, flags)) {
+ dlock_gl(cmd, "un", DL_GL_MODE_NOARG);
+ return 0;
+ }
+
+ return 1;
+}
+
diff --git a/tools/dlock.h b/tools/dlock.h
index 255c90080..2a6ebe183 100644
--- a/tools/dlock.h
+++ b/tools/dlock.h
@@ -15,16 +15,27 @@
#define DL_GL_SKIP_CACHE_VALIDATE 0x00000002
#define DL_GL_UPDATE_NAMES 0x00000004
+#define DL_VG_MODE_NOARG 0x00000001
+
#ifdef LVMLOCKD_SUPPORT
int dlock_gl_create(struct cmd_context *cmd, const char *def_mode, uint32_t flags,
const char *vg_lock_type);
int dlock_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags);
+int dlock_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *def_mode, uint32_t flags);
+
+int dlock_gl_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *def_gl_mode, const char *def_vg_mode,
+ uint32_t flags);
+
#else /* LVMLOCKD_SUPPORT */
#define dlock_gl_create(cmd, def_mode, flags, vg_lock_type) (1)
#define dlock_gl(cmd, def_mode, flags) (1)
+#define dlock_vg(cmd, vg_name, def_mode, flags) (1)
+#define dlock_gl_vg(cmd, vg_name, def_gl_mode, def_vg_mode, flags) (1)
#endif /* LVMLOCKD_SUPPORT */
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index fcec8e176..b95e20b82 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -3418,6 +3418,9 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
cmd->handles_missing_pvs = 1;
}
+ if (!dlock_vg(cmd, lp->vg_name, "ex", 0))
+ goto_out;
+
if (!(lv = get_vg_lock_and_logical_volume(cmd, lp->vg_name, lp->lv_name)))
goto_out;
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
index cf2a07cb8..333b9c113 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -1424,6 +1424,9 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
+ if (!dlock_vg(cmd, lp.vg_name, "ex", 0))
+ return_ECMD_FAILED;
+
log_verbose("Finding volume group \"%s\"", lp.vg_name);
vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
if (vg_read_error(vg)) {
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 07427f941..337dc4416 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -761,7 +761,7 @@ void lvm_register_commands(void)
yes_ARG, \
quiet_ARG, config_ARG, \
commandprofile_ARG, \
- lockgl_ARG, \
+ lockgl_ARG, lockvg_ARG, \
profile_ARG, -1);
#include "commands.h"
#undef xx
diff --git a/tools/lvrename.c b/tools/lvrename.c
index eeff76da2..2f34f1dd2 100644
--- a/tools/lvrename.c
+++ b/tools/lvrename.c
@@ -124,6 +124,11 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv)
goto bad;
}
+ if (is_dlock_type(vg->lock_type)) {
+ log_error("Cannot rename LV with lock_type %s", vg->lock_type);
+ goto bad;
+ }
+
if (!lv_rename(cmd, lvl->lv, lv_name_new))
goto_bad;
diff --git a/tools/lvresize.c b/tools/lvresize.c
index 08248bbec..f747d4ccf 100644
--- a/tools/lvresize.c
+++ b/tools/lvresize.c
@@ -174,6 +174,9 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv)
if (!_lvresize_params(cmd, argc, argv, &lp))
return EINVALID_CMD_LINE;
+ if (!dlock_vg(cmd, lp.vg_name, "ex", 0))
+ return_ECMD_FAILED;
+
log_verbose("Finding volume group %s", lp.vg_name);
vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
if (vg_read_error(vg)) {
diff --git a/tools/toollib.c b/tools/toollib.c
index 8a7c6cc3e..6b6c0db9b 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1695,12 +1695,16 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
vg_uuid = vgnl->vgid;
ret = 0;
+ if (!dlock_vg(cmd, vg_name, NULL, 0))
+ continue;
+
vg = vg_read(cmd, vg_name, vg_uuid, flags);
if (ignore_vg(cmd, vg, vg_name, arg_vgnames, flags & READ_ALLOW_INCONSISTENT, &ret)) {
if (ret > ret_max)
ret_max = ret;
release_vg(vg);
+ dlock_vg(cmd, vg_name, "un", 0);
stack;
continue;
}
@@ -1716,6 +1720,8 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
else
unlock_and_release_vg(cmd, vg, vg_name);
+ dlock_vg(cmd, vg_name, "un", 0);
+
if (ret > ret_max)
ret_max = ret;
if (sigint_caught())
@@ -2070,11 +2076,15 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
}
}
+ if (!dlock_vg(cmd, vg_name, NULL, 0))
+ continue;
+
vg = vg_read(cmd, vg_name, vg_uuid, flags);
if (ignore_vg(cmd, vg, vg_name, arg_vgnames, flags & READ_ALLOW_INCONSISTENT, &ret)) {
if (ret > ret_max)
ret_max = ret;
release_vg(vg);
+ dlock_vg(cmd, vg_name, "un", 0);
stack;
continue;
}
@@ -2083,6 +2093,8 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
handle, process_single_lv);
unlock_and_release_vg(cmd, vg, vg_name);
+ dlock_vg(cmd, vg_name, "un", 0);
+
if (ret > ret_max)
ret_max = ret;
@@ -2386,6 +2398,9 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t flags,
ret = 0;
skip = 0;
+ if (!dlock_vg(cmd, vg_name, NULL, 0))
+ continue;
+
vg = vg_read(cmd, vg_name, vg_uuid, flags | READ_WARN_INCONSISTENT);
if (ignore_vg(cmd, vg, vg_name, NULL, flags & READ_ALLOW_INCONSISTENT, &ret)) {
if (ret > ret_max)
@@ -2404,6 +2419,8 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t flags,
else
unlock_and_release_vg(cmd, vg, vg->name);
+ dlock_vg(cmd, vg_name, "un", 0);
+
if (sigint_caught())
return_ECMD_FAILED;
diff --git a/tools/tools.h b/tools/tools.h
index 0f0495e0c..af554cc0b 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -110,6 +110,8 @@ struct arg_value_group_list {
#define ENABLE_ALL_DEVS 0x00000008
/* Use only the first free arg as the vg name. */
#define ONLY_FIRST_NAME 0x00000010
+#define DLOCK_VG_SH 0x00000020 /* cmd never modifies a vg */
+#define DLOCK_VG_NA 0x00000040 /* vg lock does not apply to cmd */
/* a register of the lvm commands */
struct command {
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 80d91c647..67f9d43d0 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -665,8 +665,45 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
return ret;
}
+/*
+ * vgchange can do a different things that require different
+ * locking, so look at each of those things here.
+ *
+ * Set up overrides for the default VG locking for various special cases.
+ * The VG lock will be acquired in process_each_vg.
+ *
+ * Acquire the gl lock according to which kind of vgchange command this is.
+ */
+
static int dlock_vgchange(struct cmd_context *cmd, int argc, char **argv)
{
+ /* The default vg lock mode is ex, but these options only need sh. */
+
+ if (arg_is_set(cmd, activate_ARG) || arg_is_set(cmd, refresh_ARG))
+ cmd->command->flags |= DLOCK_VG_SH;
+
+ /* Starting a vg lockspace means there are no locks available yet. */
+
+ if (arg_is_set(cmd, lockstart_ARG))
+ cmd->command->flags |= DLOCK_VG_NA;
+
+ /*
+ * In most cases, dlock_vg does not apply when changing lock type.
+ * (We don't generally allow changing *from* dlock type yet.)
+ * dlock_vg could be called within _vgchange_locktype as needed.
+ */
+
+ if (arg_is_set(cmd, locktype_ARG))
+ cmd->command->flags |= DLOCK_VG_NA;
+
+ /*
+ * Changing system_id or lock_type must only be done on explicitly
+ * named vgs.
+ */
+
+ if (arg_is_set(cmd, systemid_ARG) || arg_is_set(cmd, locktype_ARG))
+ cmd->command->flags &= ~ALL_VGS_IS_DEFAULT;
+
if (!argc || arg_tag_count(argc, argv) || arg_is_set(cmd, lockstart_ARG)) {
/*
* The first two standard conditions want the current
diff --git a/tools/vgmerge.c b/tools/vgmerge.c
index 41658af9a..92b49ad30 100644
--- a/tools/vgmerge.c
+++ b/tools/vgmerge.c
@@ -25,6 +25,13 @@ static struct volume_group *_vgmerge_vg_read(struct cmd_context *cmd,
release_vg(vg);
return NULL;
}
+
+ if (is_dlock_type(vg->lock_type)) {
+ log_error("vgmerge not allowed for lock_type %s", vg->lock_type);
+ unlock_and_release_vg(cmd, vg, vg_name);
+ return NULL;
+ }
+
return vg;
}
diff --git a/tools/vgreduce.c b/tools/vgreduce.c
index 95d0cca50..fb11a0c08 100644
--- a/tools/vgreduce.c
+++ b/tools/vgreduce.c
@@ -195,7 +195,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
init_ignore_suspended_devices(1);
cmd->handles_missing_pvs = 1;
- if (!dlock_gl(cmd, "ex", 0))
+ if (!dlock_gl_vg(cmd, vg_name, "ex", "ex", 0))
return_ECMD_FAILED;
vg = vg_read_for_update(cmd, vg_name, NULL, READ_ALLOW_EXPORTED);
diff --git a/tools/vgremove.c b/tools/vgremove.c
index 6c31c9a3e..9941d04b1 100644
--- a/tools/vgremove.c
+++ b/tools/vgremove.c
@@ -67,6 +67,13 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name,
lvmlockd_free_vg_final(cmd, vg);
+ /*
+ * The vg lock no longer exists, so don't bother unlocking it.
+ * (This would be nice to do in lvmlockd_free_vg_final(), but
+ * cmd->command is not accessible lib.)
+ */
+ cmd->command->flags |= DLOCK_VG_NA;
+
return ECMD_PROCESSED;
}
diff --git a/tools/vgrename.c b/tools/vgrename.c
index 8dc413920..a287a21f9 100644
--- a/tools/vgrename.c
+++ b/tools/vgrename.c
@@ -29,6 +29,12 @@ static struct volume_group *_get_old_vg_for_rename(struct cmd_context *cmd,
return_NULL;
}
+ if (is_dlock_type(vg->lock_type)) {
+ log_error("vgrename not allowed for lock_type %s", vg->lock_type);
+ unlock_and_release_vg(cmd, vg, vg_name_old);
+ return NULL;
+ }
+
return vg;
}
diff --git a/tools/vgsplit.c b/tools/vgsplit.c
index 339bb52aa..40b6e67a6 100644
--- a/tools/vgsplit.c
+++ b/tools/vgsplit.c
@@ -453,6 +453,13 @@ static struct volume_group *_vgsplit_from(struct cmd_context *cmd,
release_vg(vg_from);
return NULL;
}
+
+ if (is_dlock_type(vg_from->lock_type)) {
+ log_error("vgsplit not allowed for lock_type %s", vg_from->lock_type);
+ unlock_and_release_vg(cmd, vg_from, vg_name_from);
+ return NULL;
+ }
+
return vg_from;
}