summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2014-11-24 14:27:30 -0600
committerDavid Teigland <teigland@redhat.com>2014-11-24 15:33:47 -0600
commit530660b6d7ca34411598406cabdbccc06423333b (patch)
treec1579b29f0551072a4959a0cde6e953d08c96366
parent8220c139fafed4d0990eff4fbacda74902a35c25 (diff)
downloadlvm2-dev-dct-lvmlockd.tar.gz
lvmlockd: use dlock_vg to acquire the VG lockdev-dct-lvmlockd
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 via a command or config option.) This way, simple, transient locking conflicts between commands on different hosts would be hidden. Caching: lvmlockd uses the lvb in the VG lock to hold the VG seqno. When a command writes VG metadata 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 this new seqno in the vg lock's lvb. When other hosts next acquire the VG lock, they will read the lvb, see the new 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 then 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/commands/toolcontext.h1
-rw-r--r--lib/locking/lvmlockd.c189
-rw-r--r--lib/locking/lvmlockd.h15
-rw-r--r--lib/metadata/metadata.c7
-rw-r--r--tools/args.h1
-rw-r--r--tools/commands.h26
-rw-r--r--tools/dlock.c83
-rw-r--r--tools/dlock.h9
-rw-r--r--tools/lvchange.c8
-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.c53
-rw-r--r--tools/tools.h1
-rw-r--r--tools/vgchange.c37
-rw-r--r--tools/vgmerge.c7
-rw-r--r--tools/vgreduce.c2
-rw-r--r--tools/vgrename.c6
-rw-r--r--tools/vgsplit.c7
21 files changed, 430 insertions, 38 deletions
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index a3aaf7c28..07a432c45 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -98,6 +98,7 @@ struct cmd_context {
unsigned independent_metadata_areas:1; /* Active formats have MDAs outside PVs */
unsigned unknown_system_id:1;
unsigned include_foreign_vgs:1;
+ unsigned disable_dlock_vg:1;
struct dev_types *dev_types;
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
index d6eaa34c2..fad164e03 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -531,6 +531,162 @@ int lvmlockd_gl(struct cmd_context *cmd, const char *cmd_name, const char *mode,
return 1;
}
+int lvmlockd_vg(struct cmd_context *cmd, const char *cmd_name, const char *vg_name,
+ const char *mode, uint32_t flags)
+{
+ int result;
+ uint32_t result_flags;
+
+ if (!is_real_vg(vg_name))
+ return 1;
+
+ if (!_lvmlockd_request(cmd, cmd_name, "lock_vg",
+ vg_name, NULL, NULL, NULL, NULL, mode, NULL,
+ &result, &result_flags)) {
+ /* No result from lvmlockd, it is probably not running. */
+
+ /*
+ * See comment in lvmlockd_gl() about these cases
+ * where we keep going if the mode is "un" or "sh".
+ */
+
+ 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.
+ *
+ * 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) {
+ 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 without the lock, and add something after
+ * vg_read to check if the lock request failed, and
+ * fail at that point. Going through the vg_read
+ * before failing may provide more information about
+ * the failure and the VG.)
+ */
+ 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;
+}
/* The name of the internal lv created to hold sanlock locks. */
#define LVMLOCKD_SANLOCK_LV_NAME "lvmlock"
@@ -943,6 +1099,9 @@ void lvmlockd_free_vg_final(struct cmd_context *cmd, struct volume_group *vg)
default:
log_error("Unknown lock_type.");
}
+
+ /* The vg lock no longer exists, so don't bother trying to unlock. */
+ cmd->disable_dlock_vg = 1;
}
/*
@@ -1104,3 +1263,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 d8996f972..2fbabc30a 100644
--- a/lib/locking/lvmlockd.h
+++ b/lib/locking/lvmlockd.h
@@ -24,6 +24,9 @@
#define DL_GL_SKIP_CACHE_VALIDATE 0x00000002
#define DL_GL_UPDATE_NAMES 0x00000004
+/* dlock_vg flags */
+#define DL_VG_MODE_NOARG 0x00000001
+
/* lvmlockd_send result flags */
#define LD_RF_NO_LOCKSPACES 0x00000001
#define LD_RF_NO_GL_LS 0x00000002
@@ -108,6 +111,15 @@ int lvmlockd_gl(struct cmd_context *cmd,
const char *mode,
uint32_t flags);
+int lvmlockd_vg(struct cmd_context *cmd,
+ const char *cmd_name,
+ const char *vg_name,
+ const char *mode,
+ uint32_t flags);
+
+/* notify lvmlockd of a new VG seqno */
+
+int lvmlockd_vg_update(struct volume_group *vg);
#else /* LVMLOCKD_SUPPORT */
@@ -127,6 +139,9 @@ int lvmlockd_gl(struct cmd_context *cmd,
int lvmlockd_gl_create(cmd, cmd_name, mode, vg_lock_type) (1)
int lvmlockd_gl(cmd, cmd_name, mode, flags) (1)
+int lvmlockd_vg(cmd, cmd_name, vg_name, mode, flags) (1)
+
+#define lvmlockd_vg_update(vg) (1)
#endif /* LVMLOCKD_SUPPORT */
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 1d5459417..db2a552a2 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) {
@@ -4331,6 +4335,9 @@ static int _access_vg_lock_type(struct cmd_context *cmd, struct volume_group *vg
if (!is_real_vg(vg->name))
return 1;
+ if (cmd->disable_dlock_vg)
+ return 1;
+
if (is_dlock_type(vg->lock_type) && !find_config_tree_bool(cmd, global_use_lvmlockd_CFG, NULL)) {
log_warn("Cannot access VG %s which requires lvmlockd (lock_type %s).",
vg->name, vg->lock_type);
diff --git a/tools/args.h b/tools/args.h
index 97771e897..02ef25197 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/commands.h b/tools/commands.h
index b579420bd..014ddc951 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -352,7 +352,7 @@ xx(lvcreate,
xx(lvdisplay,
"Display information about a logical volume",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"lvdisplay\n"
"\t[-a|--all]\n"
"\t[-c|--colon]\n"
@@ -568,7 +568,7 @@ xx(lvresize,
xx(lvs,
"Display information about logical volumes",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"lvs\n"
"\t[-a|--all]\n"
"\t[--aligned]\n"
@@ -605,7 +605,7 @@ xx(lvs,
xx(lvscan,
"List all logical volumes in all volume groups",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"lvscan\n"
"\t[-a|--all]\n"
"\t[-b|--blockdevice]\n"
@@ -662,7 +662,7 @@ xx(pvresize,
xx(pvck,
"Check the consistency of physical volume(s)",
- 0,
+ DLOCK_VG_SH,
"pvck "
"\t[--commandprofile ProfileName]\n"
"\t[-d|--debug]\n"
@@ -728,7 +728,7 @@ xx(pvdata,
xx(pvdisplay,
"Display various attributes of physical volume(s)",
- CACHE_VGMETADATA | PERMITTED_READ_ONLY | ENABLE_ALL_DEVS,
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY | ENABLE_ALL_DEVS | DLOCK_VG_SH,
"pvdisplay\n"
"\t[-c|--colon]\n"
"\t[--commandprofile ProfileName]\n"
@@ -814,7 +814,7 @@ xx(pvremove,
xx(pvs,
"Display information about physical volumes",
- CACHE_VGMETADATA | PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | ENABLE_ALL_DEVS,
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | ENABLE_ALL_DEVS | DLOCK_VG_SH,
"pvs\n"
"\t[-a|--all]\n"
"\t[--aligned]\n"
@@ -851,7 +851,7 @@ xx(pvs,
xx(pvscan,
"List all physical volumes",
- PERMITTED_READ_ONLY,
+ PERMITTED_READ_ONLY | DLOCK_VG_SH,
"pvscan\n"
"\t[-b|--background]\n"
"\t[--cache [-a|--activate ay] [ DevicePath | -j|--major major --minor minor]...]\n"
@@ -883,7 +883,7 @@ xx(tags,
xx(vgcfgbackup,
"Backup volume group configuration(s)",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"vgcfgbackup\n"
"\t[--commandprofile ProfileName]\n"
"\t[-d|--debug]\n"
@@ -965,7 +965,7 @@ xx(vgchange,
xx(vgck,
"Check the consistency of volume group(s)",
- ALL_VGS_IS_DEFAULT,
+ ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"vgck "
"\t[--commandprofile ProfileName]\n"
"\t[-d|--debug]\n"
@@ -1028,7 +1028,7 @@ xx(vgcreate,
xx(vgdisplay,
"Display volume group information",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"vgdisplay\n"
"\t[-A|--activevolumegroups]\n"
"\t[-c|--colon | -s|--short | -v|--verbose]\n"
@@ -1072,7 +1072,7 @@ xx(vgdisplay,
xx(vgexport,
"Unregister volume group(s) from the system",
- ALL_VGS_IS_DEFAULT,
+ ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"vgexport\n"
"\t[-a|--all]\n"
"\t[--commandprofile ProfileName]\n"
@@ -1209,7 +1209,7 @@ xx(vgrename,
xx(vgs,
"Display information about volume groups",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"vgs\n"
"\t[--aligned]\n"
"\t[--binary]\n"
@@ -1245,7 +1245,7 @@ xx(vgs,
xx(vgscan,
"Search for all volume groups",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | DLOCK_VG_SH,
"vgscan "
"\t[--cache]\n"
"\t[--commandprofile ProfileName]\n"
diff --git a/tools/dlock.c b/tools/dlock.c
index 62bc283c1..40907fb88 100644
--- a/tools/dlock.c
+++ b/tools/dlock.c
@@ -167,3 +167,86 @@ 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;
+
+ /*
+ * Only real vgs have locks; orphans are covered by global lock.
+ */
+ if (!is_real_vg(vg_name))
+ return 1;
+
+ /*
+ * Some special cases need to disable the vg lock.
+ */
+ if (cmd->disable_dlock_vg)
+ 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;
+ }
+
+ if (!lvmlockd_vg(cmd, command_name(cmd), vg_name, mode, flags)) {
+ if (arg_is_set(cmd, sysinit_ARG) || arg_is_set(cmd, ignorelockingfailure_ARG)) {
+ log_debug("Ignore failed locking for global lock: option %s",
+ arg_is_set(cmd, sysinit_ARG) ? "sysinit" : "ignorelockingfailure");
+ return 1;
+ }
+
+ 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 f24fe1981..cab605e45 100644
--- a/tools/dlock.h
+++ b/tools/dlock.h
@@ -17,10 +17,19 @@ int dlock_gl_create(struct cmd_context *cmd, const char *def_mode, uint32_t flag
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/lvchange.c b/tools/lvchange.c
index 5f0f9c512..ee54e5aa7 100644
--- a/tools/lvchange.c
+++ b/tools/lvchange.c
@@ -1171,6 +1171,14 @@ int lvchange(struct cmd_context *cmd, int argc, char **argv)
}
}
+ /*
+ * The default vg lock mode for lvchange is ex, but these options
+ * are cases where lvchange does not modify the vg, so they can use
+ * the sh lock mode.
+ */
+ if (arg_count(cmd, activate_ARG) || arg_count(cmd, refresh_ARG))
+ cmd->command->flags |= DLOCK_VG_SH;
+
if (arg_tag_count(argc, argv) && !dlock_gl(cmd, "sh", 0))
return_ECMD_FAILED;
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index c7acd5d16..7010a5787 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -3450,6 +3450,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 b0186d338..f57bc681c 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 5aab83105..ad7c2f78b 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 158725d37..4d8987605 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1792,17 +1792,17 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
vg_uuid = vgnl->vgid;
skip = 0;
+ if (!dlock_vg(cmd, vg_name, NULL, 0))
+ continue;
+
vg = vg_read(cmd, vg_name, vg_uuid, flags);
if (ignore_vg(vg, vg_name, arg_vgnames, flags & READ_ALLOW_INCONSISTENT, &skip)) {
stack;
ret_max = ECMD_FAILED;
- release_vg(vg);
- continue;
- }
- if (skip) {
- release_vg(vg);
- continue;
+ goto endvg;
}
+ if (skip)
+ goto endvg;
/* Process this VG? */
if (process_all ||
@@ -1815,10 +1815,11 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
ret_max = ret;
}
- if (vg_read_error(vg))
- release_vg(vg);
- else
- unlock_and_release_vg(cmd, vg, vg_name);
+ if (!vg_read_error(vg))
+ unlock_vg(cmd, vg_name);
+endvg:
+ release_vg(vg);
+ dlock_vg(cmd, vg_name, "un", 0);
}
return ret_max;
@@ -2174,18 +2175,18 @@ 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(vg, vg_name, arg_vgnames, flags & READ_ALLOW_INCONSISTENT, &skip)) {
stack;
ret_max = ECMD_FAILED;
- release_vg(vg);
- continue;
+ goto endvg;
}
- if (skip) {
- release_vg(vg);
- continue;
- }
+ if (skip)
+ goto endvg;
ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, 0,
handle, process_single_lv);
@@ -2194,7 +2195,10 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
if (ret > ret_max)
ret_max = ret;
- unlock_and_release_vg(cmd, vg, vg_name);
+ unlock_vg(cmd, vg_name);
+endvg:
+ release_vg(vg);
+ dlock_vg(cmd, vg_name, "un", 0);
}
return ret_max;
@@ -2499,12 +2503,14 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t flags,
vg_uuid = vgnl->vgid;
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(vg, vg_name, NULL, flags & READ_ALLOW_INCONSISTENT, &skip)) {
stack;
ret_max = ECMD_FAILED;
- release_vg(vg);
- continue;
+ goto endvg;
}
/*
@@ -2519,10 +2525,11 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t flags,
if (ret > ret_max)
ret_max = ret;
- if (skip)
- release_vg(vg);
- else
- unlock_and_release_vg(cmd, vg, vg->name);
+ if (!skip)
+ unlock_vg(cmd, vg->name);
+endvg:
+ release_vg(vg);
+ dlock_vg(cmd, vg_name, "un", 0);
/* Quit early when possible. */
if (!process_all && dm_list_empty(arg_tags) && dm_list_empty(arg_pvnames))
diff --git a/tools/tools.h b/tools/tools.h
index 0f0495e0c..3b5ccff50 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -110,6 +110,7 @@ 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 */
/* a register of the lvm commands */
struct command {
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 80d91c647..3e22b09df 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->disable_dlock_vg = 1;
+
+ /*
+ * 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->disable_dlock_vg = 1;
+
+ /*
+ * 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/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;
}