summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2014-11-26 16:42:12 -0600
committerDavid Teigland <teigland@redhat.com>2014-12-08 15:32:53 -0600
commitfbe817838a83768d82acae0d8856f78e22066eae (patch)
tree3e5650f2e0ebeb70346a627e05e0684c04c07eae
parent5f642565289fda446c7af6d807991889150e45e3 (diff)
downloadlvm2-fbe817838a83768d82acae0d8856f78e22066eae.tar.gz
lvmlockd: VG locking
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 lockd_vg(): . To acquire the vg lock in ex mode for writing: lockd_vg(cmd, vg_name, "ex", 0); . To acquire the vg lock in sh mode for reading: lockd_vg(cmd, vg_name, "sh", 0); . To release the vg lock: lockd_vg(cmd, vg_name, "un", 0); The lockd_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 lockd_vg("un"), lvmlockd releases the lock in the underlying lock manager. When lvm is compiled without lvmlockd support, all the lockd_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: lockd_vg(cmd, vg_name, "sh", 0); vg = vg_read(cmd, vg_name); lockd_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: lockd_vg(cmd, vg_name, "ex", 0); vg = vg_read(cmd, vg_name); ... vg_write(vg); vg_commit(vg); lockd_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. lockd_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 lockd_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 lockd_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.h3
-rw-r--r--lib/locking/lvmlockd.c259
-rw-r--r--lib/locking/lvmlockd.h10
-rw-r--r--lib/metadata/metadata.c7
-rw-r--r--tools/args.h1
-rw-r--r--tools/commands.h26
-rw-r--r--tools/lvchange.c8
-rw-r--r--tools/lvconvert.c3
-rw-r--r--tools/lvcreate.c3
-rw-r--r--tools/lvmcmdline.c6
-rw-r--r--tools/lvrename.c5
-rw-r--r--tools/lvresize.c3
-rw-r--r--tools/toollib.c53
-rw-r--r--tools/tools.h2
-rw-r--r--tools/vgchange.c39
-rw-r--r--tools/vgmerge.c7
-rw-r--r--tools/vgreduce.c2
-rw-r--r--tools/vgrename.c6
-rw-r--r--tools/vgsplit.c7
19 files changed, 408 insertions, 42 deletions
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index 6fc95e711..4bebbfd5c 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -98,6 +98,8 @@ 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 lockd_vg_disable:1;
+ unsigned lockd_vg_default_sh:1;
struct dev_types *dev_types;
@@ -142,6 +144,7 @@ struct cmd_context {
/* Locking */
const char *lock_gl_mode; /* gl mode, from --lock-gl */
+ const char *lock_vg_mode; /* vg mode, from --lock-vg */
const char *lib_dir; /* Cache value global/library_dir */
char system_dir[PATH_MAX];
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
index a1e6928d6..f3e644e12 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -679,6 +679,9 @@ int lockd_init_vg(struct cmd_context *cmd, struct volume_group *vg)
int lockd_free_vg_before(struct cmd_context *cmd, struct volume_group *vg)
{
+ if (cmd->lock_vg_mode && !strcmp(cmd->lock_vg_mode, "na"))
+ return 1;
+
switch (lock_type_to_num(vg->lock_type)) {
case LOCK_TYPE_NONE:
case LOCK_TYPE_CLVM:
@@ -697,6 +700,9 @@ int lockd_free_vg_before(struct cmd_context *cmd, struct volume_group *vg)
void lockd_free_vg_final(struct cmd_context *cmd, struct volume_group *vg)
{
+ if (cmd->lock_vg_mode && !strcmp(cmd->lock_vg_mode, "na"))
+ return;
+
switch (lock_type_to_num(vg->lock_type)) {
case LOCK_TYPE_NONE:
_free_vg_local(cmd, vg);
@@ -710,6 +716,9 @@ void lockd_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->lockd_vg_disable = 1;
}
/*
@@ -751,11 +760,8 @@ int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg)
/* Skip starting the vg lockspace when the vg lock is skipped. */
- /* Added in a later patch. */
- /*
- if (cmd_mode && !strcmp(cmd_mode, "na"))
+ if (cmd->lock_vg_mode && !strcmp(cmd->lock_vg_mode, "na"))
return 1;
- */
log_debug("lockd_start_vg %s lock_type %s", vg->name,
vg->lock_type ? vg->lock_type : "empty");
@@ -1233,4 +1239,249 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
return 1;
}
+int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode, uint32_t flags)
+{
+ const char *mode = NULL;
+ uint32_t result_flags;
+ int result;
+
+ if (!is_real_vg(vg_name))
+ return 1;
+
+ /*
+ * Some special cases need to disable the vg lock.
+ */
+ if (cmd->lockd_vg_disable)
+ return 1;
+
+ /*
+ * LDVG_MODE_NOARG disables getting the mode from --lock-vg arg.
+ */
+ if (!(flags & LDVG_MODE_NOARG) && cmd->lock_vg_mode) {
+ mode = cmd->lock_vg_mode;
+ 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;
+ }
+ }
+
+ /*
+ * The default mode may not have been provided in the
+ * function args. This happens when lockd_vg is called
+ * from a process_each function that handles different
+ * commands. Commands that only read/check/report/display
+ * the vg have LOCKD_VG_SH set in commands.h, which is
+ * copied to lockd_vg_default_sh. Commands without this
+ * set modify the vg and need ex.
+ */
+ if (!mode)
+ mode = def_mode;
+ if (!mode)
+ mode = cmd->lockd_vg_default_sh ? "sh" : "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 (!_lockd_request(cmd, "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 lockd_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.
+ *
+ * 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.
+ *
+ * ELOCALVG: the VG is local and does not need locking.
+ *
+ * EOTHERVG:
+ *
+ * The VG is local and its system_id does not match
+ * the local system_id saved in lvmlockd (lvmlockd
+ * caches the names/system_ids of local VGs so it
+ * can quickly avoid any distributed locking on them.)
+ *
+ * In many cases we could simply return 0 (a failure)
+ * here, causing this VG to be skipped by the command
+ * before it's even read. But there are a couple of
+ * reasons why we want to return 1 (success) here and
+ * proceed through vg_read and get to access_vg_systemid:
+ *
+ * . The command may allow reading foreign VG's,
+ * i.e. vgs --foreign, which would not work if we
+ * skipped the VG here.
+ *
+ * . The local host may accept multiple system_ids,
+ * i.e. allow_system_id(), and lvmlockd does not
+ * know about all the allowed system_ids.
+ *
+ * If neither of these are true, then access_vg_systemid
+ * will deny access, and the VG will be skipped at that
+ * point. So, to allow the two exceptions above, we
+ * return success here and allow VG access to be
+ * decided later in access_vg_systemid.
+ */
+
+ if (result == -ELOCALVG || result == -EOTHERVG)
+ return 1;
+
+ 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) {
+ if (ignorelockingfailure()) {
+ log_debug("Ignore failed locking for VG %s", vg_name);
+ return 1;
+ } else {
+ log_error("VG %s lock error: %d", vg_name, result);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* A shortcut for back to back lockd_gl() + lockd_vg() */
+
+int lockd_gl_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *def_gl_mode, const char *def_vg_mode,
+ uint32_t flags)
+{
+ if (!lockd_gl(cmd, def_gl_mode, flags))
+ return 0;
+
+ if (!lockd_vg(cmd, vg_name, def_vg_mode, flags)) {
+ lockd_gl(cmd, "un", LDGL_MODE_NOARG);
+ return 0;
+ }
+
+ return 1;
+}
+
+int lockd_vg_update(struct volume_group *vg)
+{
+ daemon_reply reply;
+ int result;
+ int ret;
+
+ if (!is_lockd_type(vg->lock_type))
+ return 1;
+
+ if (!_lvmlockd_active)
+ return 1;
+ if (!lvmlockd_connected())
+ return 0;
+
+ reply = _lockd_send("vg_update",
+ "pid = %d", getpid(),
+ "vg_name = %s", vg->name,
+ "version = %d", (int64_t)vg->seqno,
+ NULL);
+
+ if (!_lockd_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 8a612511c..18980ead7 100644
--- a/lib/locking/lvmlockd.h
+++ b/lib/locking/lvmlockd.h
@@ -24,6 +24,9 @@
#define LDGL_SKIP_CACHE_VALIDATE 0x00000002
#define LDGL_UPDATE_NAMES 0x00000004
+/* lockd_vg flags */
+#define LDVG_MODE_NOARG 0x00000001
+
/* lvmlockd result flags */
#define LD_RF_NO_LOCKSPACES 0x00000001
#define LD_RF_NO_GL_LS 0x00000002
@@ -100,6 +103,10 @@ int lockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg);
int lockd_gl_create(struct cmd_context *cmd, const char *def_mode, const char *vg_lock_type);
int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags);
+int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode, uint32_t flags);
+int lockd_gl_vg(struct cmd_context *cmd, const char *vg_name,
+ const char *def_gl_mode, const char *def_vg_mode, uint32_t flags);
+int lockd_vg_update(struct volume_group *vg);
#else /* LVMLOCKD_SUPPORT */
@@ -119,6 +126,9 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags);
#define lockd_gl_create(cmd, def_mode, vg_lock_type) (1)
#define lockd_gl(cmd, def_mode, flags) (1)
+#define lockd_vg(cmd, vg_name, def_mode, flags) (1)
+#define lockd_gl_vg(cmd, vg_name, def_gl_mode, def_vg_mode, flags) (1)
+#define lockd_vg_update(vg) (1)
#endif /* LVMLOCKD_SUPPORT */
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 3a6545ae5..71ddd8e00 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;
+ lockd_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;
+ lockd_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->lockd_vg_disable)
+ return 1;
+
if (is_lockd_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 0272e12e5..f2a38c92b 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -353,7 +353,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 | LOCKD_VG_SH,
"lvdisplay\n"
"\t[-a|--all]\n"
"\t[-c|--colon]\n"
@@ -569,7 +569,7 @@ xx(lvresize,
xx(lvs,
"Display information about logical volumes",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"lvs\n"
"\t[-a|--all]\n"
"\t[--aligned]\n"
@@ -606,7 +606,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 | LOCKD_VG_SH,
"lvscan\n"
"\t[-a|--all]\n"
"\t[-b|--blockdevice]\n"
@@ -663,7 +663,7 @@ xx(pvresize,
xx(pvck,
"Check the consistency of physical volume(s)",
- 0,
+ LOCKD_VG_SH,
"pvck "
"\t[--commandprofile ProfileName]\n"
"\t[-d|--debug]\n"
@@ -729,7 +729,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 | LOCKD_VG_SH,
"pvdisplay\n"
"\t[-c|--colon]\n"
"\t[--commandprofile ProfileName]\n"
@@ -815,7 +815,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 | LOCKD_VG_SH,
"pvs\n"
"\t[-a|--all]\n"
"\t[--aligned]\n"
@@ -852,7 +852,7 @@ xx(pvs,
xx(pvscan,
"List all physical volumes",
- PERMITTED_READ_ONLY,
+ PERMITTED_READ_ONLY | LOCKD_VG_SH,
"pvscan\n"
"\t[-b|--background]\n"
"\t[--cache [-a|--activate ay] [ DevicePath | -j|--major major --minor minor]...]\n"
@@ -884,7 +884,7 @@ xx(tags,
xx(vgcfgbackup,
"Backup volume group configuration(s)",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"vgcfgbackup\n"
"\t[--commandprofile ProfileName]\n"
"\t[-d|--debug]\n"
@@ -966,7 +966,7 @@ xx(vgchange,
xx(vgck,
"Check the consistency of volume group(s)",
- ALL_VGS_IS_DEFAULT,
+ ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"vgck "
"\t[--commandprofile ProfileName]\n"
"\t[-d|--debug]\n"
@@ -1029,7 +1029,7 @@ xx(vgcreate,
xx(vgdisplay,
"Display volume group information",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"vgdisplay\n"
"\t[-A|--activevolumegroups]\n"
"\t[-c|--colon | -s|--short | -v|--verbose]\n"
@@ -1073,7 +1073,7 @@ xx(vgdisplay,
xx(vgexport,
"Unregister volume group(s) from the system",
- ALL_VGS_IS_DEFAULT,
+ ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"vgexport\n"
"\t[-a|--all]\n"
"\t[--commandprofile ProfileName]\n"
@@ -1210,7 +1210,7 @@ xx(vgrename,
xx(vgs,
"Display information about volume groups",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"vgs\n"
"\t[--aligned]\n"
"\t[--binary]\n"
@@ -1246,7 +1246,7 @@ xx(vgs,
xx(vgscan,
"Search for all volume groups",
- PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT,
+ PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | LOCKD_VG_SH,
"vgscan "
"\t[--cache]\n"
"\t[--commandprofile ProfileName]\n"
diff --git a/tools/lvchange.c b/tools/lvchange.c
index 2e8af272b..ba974388d 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->lockd_vg_default_sh = 1;
+
if (arg_tag_count(argc, argv) && !lockd_gl(cmd, "sh", 0))
return_ECMD_FAILED;
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index c7acd5d16..213b0da95 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 (!lockd_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 7236ffca5..ef185a255 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -1440,6 +1440,9 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
+ if (!lockd_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 0ffd8b446..9f80de4cb 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
@@ -1043,6 +1043,10 @@ static int _get_settings(struct cmd_context *cmd)
if (arg_is_set(cmd, lockgl_ARG))
cmd->lock_gl_mode = arg_str_value(cmd, lockgl_ARG, NULL);
+ if (arg_is_set(cmd, lockvg_ARG))
+ cmd->lock_vg_mode = arg_str_value(cmd, lockvg_ARG, NULL);
+ if (cmd->command->flags & LOCKD_VG_SH)
+ cmd->lockd_vg_default_sh = 1;
cmd->partial_activation = 0;
cmd->degraded_activation = 0;
diff --git a/tools/lvrename.c b/tools/lvrename.c
index eeff76da2..98559202b 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_lockd_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..704b41697 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 (!lockd_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 3cfafaa0e..6f30f368b 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1793,17 +1793,17 @@ static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
vg_uuid = vgnl->vgid;
skip = 0;
+ if (!lockd_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 ||
@@ -1816,10 +1816,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);
+ lockd_vg(cmd, vg_name, "un", 0);
}
return ret_max;
@@ -2175,18 +2176,18 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
}
}
+ if (!lockd_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);
@@ -2195,7 +2196,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);
+ lockd_vg(cmd, vg_name, "un", 0);
}
return ret_max;
@@ -2500,12 +2504,14 @@ static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t flags,
vg_uuid = vgnl->vgid;
skip = 0;
+ if (!lockd_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;
}
/*
@@ -2520,10 +2526,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);
+ lockd_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 9f5982d12..f7ccfff0c 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -109,6 +109,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
+/* Command does not modify a VG, this is propagated to lockd_vg_disable. */
+#define LOCKD_VG_SH 0x00000020
/* a register of the lvm commands */
struct command {
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 66e4fb095..e8f46831d 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -536,6 +536,8 @@ static int _vgchange_lock_start(struct cmd_context *cmd, struct volume_group *vg
static int _vgchange_lock_stop(struct cmd_context *cmd, struct volume_group *vg)
{
+ /* Disable the unlock in toollib because it's pointless after the stop. */
+ cmd->lockd_vg_disable = 1;
return lockd_stop_vg(cmd, vg);
}
@@ -665,8 +667,45 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
return ret;
}
+/*
+ * vgchange can do 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 lockd_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->lockd_vg_default_sh = 1;
+
+ /* Starting a vg lockspace means there are no locks available yet. */
+
+ if (arg_is_set(cmd, lockstart_ARG))
+ cmd->lockd_vg_disable = 1;
+
+ /*
+ * In most cases, lockd_vg does not apply when changing lock type.
+ * (We don't generally allow changing *from* lockd type yet.)
+ * lockd_vg could be called within _vgchange_locktype as needed.
+ */
+
+ if (arg_is_set(cmd, locktype_ARG))
+ cmd->lockd_vg_disable = 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 4f5121232..e60346c75 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_lockd_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 e3b0910cf..026fce477 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 (!lockd_gl(cmd, "ex", 0))
+ if (!lockd_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 835c7acee..438cbc63f 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_lockd_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 67c9fae2e..b2df7fd3c 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_lockd_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;
}