summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2014-11-26 17:26:21 -0600
committerDavid Teigland <teigland@redhat.com>2014-12-01 15:14:08 -0600
commit745c7d25aedb536574395ad1791b8e8f0f575f2f (patch)
treeb0bb3af8cff0f290fc1833a0db52bb6864d640c0
parent87fefe4822e97b2a53dae14bdd4c8b5e15a5698e (diff)
downloadlvm2-dev-dct-lvmlockd-C.tar.gz
lvmlockd: LV lockingdev-dct-lvmlockd-C
An lv lock is a simple ex/sh lock that is: - acquired before the LV is activated - released after the LV is deactivated If the lock cannot be acquired, the LV is not activated. Unlike gl and vg locks, lv locks are persistent; they remain after a command acquiring them ends. They are held by the lvmlockd process. lvmlockd uses persistent locks in the lock manager so if lvmlockd exits, the lv locks are not dropped, and the LVs remain protected. The lvchange/vgchange -a option gains a new "s" arg to represent shared locks ("e" already means exclusive): -aey = activate using an ex lock -asy = activate using a sh lock An unspecified activation mode, e.g. -ay, defaults to the ex mode in lvmlockd VGs. If one host has an LV activated with an ex lock, any other host that attempts to activate the LV (in any mode) will fail to acquire the lock and will report the error. If a host has an LV activated with a sh lock, any other host can also activate that LV with a sh lock, but any attempt to activate the LV with an ex lock will fail. Commands that modify an LV (as opposed to changing the activation state) also use lv locks. These are lvconvert and other variations of lvchange. When the LV is already active (and locked), the lock requests do nothing, but if the LV is not active, these commands acquire the LV lock prior to changing the LV. If the LV is active and locked on another host, the lvchange/lvconvert command will fail when the LV lock request fails. Removing an LV with lvremove must also acquire the lv lock prior to removing the LV. If the LV is active and locked on another host, lvremove will fail. A thin pool LV has an lv lock that applies to all usage of that pool. Thin LVs do not have individual LV locks. A cache pool LV does not have an independent lv lock. When the cache pool LV is linked to an origin LV, the lock of the origin LV will protect the combined origin + cache pool.
-rw-r--r--lib/commands/toolcontext.h2
-rw-r--r--lib/locking/lvmlockd.c360
-rw-r--r--lib/locking/lvmlockd.h24
-rw-r--r--lib/metadata/lv.c22
-rw-r--r--lib/metadata/lv_manip.c26
-rw-r--r--lib/metadata/metadata-exported.h3
-rw-r--r--tools/args.h1
-rw-r--r--tools/lvchange.c19
-rw-r--r--tools/lvconvert.c65
-rw-r--r--tools/lvcreate.c3
-rw-r--r--tools/lvmcmdline.c10
11 files changed, 530 insertions, 5 deletions
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index 4bebbfd5c..c27907b6f 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -99,6 +99,7 @@ struct cmd_context {
unsigned unknown_system_id:1;
unsigned include_foreign_vgs:1;
unsigned lockd_vg_disable:1;
+ unsigned lockd_lv_disable:1;
unsigned lockd_vg_default_sh:1;
struct dev_types *dev_types;
@@ -145,6 +146,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 *lock_lv_mode; /* lv mode, from --lock-lv */
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 927244e00..8e47af5fe 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -1478,3 +1478,363 @@ int lockd_vg_update(struct volume_group *vg)
return ret;
}
+int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg,
+ const char *lv_name, const char *lock_args,
+ const char *def_mode, uint32_t flags)
+{
+ const char *mode = NULL;
+ const char *opts = NULL;
+ uint32_t result_flags;
+ int result;
+
+ if (!is_lockd_type(vg->lock_type))
+ return 1;
+
+ if (cmd->lockd_lv_disable)
+ return 1;
+
+ /*
+ * For lvchange/vgchange activation, def_mode is "sh" or "ex"
+ * according to the specific -a{e,s}y mode designation.
+ * No e,s designation gives NULL def_mode.
+ *
+ * The --lock-lv option is saved in cmd->lock_lv_mode.
+ *
+ * If both -a{e,s}y and --lock-lv mode are set, they should agree.
+ */
+
+ if (cmd->lock_lv_mode && def_mode && strcmp(cmd->lock_lv_mode, def_mode)) {
+ log_error("Different LV lock modes from activation %s and lock-lv %s",
+ def_mode, cmd->lock_lv_mode);
+ return 0;
+ }
+
+ if (cmd->lock_lv_mode && (_mode_compare(cmd->lock_lv_mode, "sh") < 0) &&
+ !find_config_tree_bool(cmd, global_allow_override_lock_modes_CFG, NULL)) {
+ log_error("Disallowed lock-lv mode \"%s\"", cmd->lock_lv_mode);
+ return 0;
+ }
+
+ if (cmd->lock_lv_mode)
+ mode = cmd->lock_lv_mode;
+ else if (def_mode)
+ mode = def_mode;
+
+ if (mode && !strcmp(mode, "sh") && (flags & LDLV_MODE_NO_SH)) {
+ log_error("Shared activation not compatible with LV type: %s/%s",
+ vg->name, lv_name);
+ return 0;
+ }
+
+ if (!mode)
+ mode = "ex";
+
+ if (flags & LDLV_PERSISTENT)
+ opts = "persistent";
+
+ if (!_lockd_request(cmd, "lock_lv",
+ vg->name, vg->lock_type, vg->lock_args,
+ lv_name, lock_args, mode, opts,
+ &result, &result_flags)) {
+ /* No result from lvmlockd, it is probably not running. */
+ log_error("Locking failed for LV %s/%s", vg->name, lv_name);
+ return 0;
+ }
+
+ /* The lv was not active/locked. */
+ if (result == -ENOENT && !strcmp(mode, "un"))
+ return 1;
+
+ if (result == -EALREADY)
+ return 1;
+
+ if (result == -EAGAIN) {
+ log_error("LV locked by other host: %s/%s", vg->name, lv_name);
+ return 0;
+ }
+
+ if (result < 0) {
+ log_error("LV lock %s error %d: %s/%s", mode, result, vg->name, lv_name);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Direct the lock request to the pool LV.
+ * For a thin pool and all its thin volumes, one ex lock is used.
+ * It is the one specified in metadata of the pool data lv.
+ */
+
+static int _lockd_lv_thin(struct cmd_context *cmd, struct logical_volume *lv,
+ const char *def_mode, uint32_t flags)
+{
+ struct logical_volume *pool_lv;
+
+ if (lv_is_thin_volume(lv)) {
+ struct lv_segment *pool_seg = first_seg(lv);
+ pool_lv = pool_seg ? pool_seg->pool_lv : NULL;
+
+ } else if (lv_is_thin_pool(lv)) {
+ pool_lv = lv;
+
+ } else {
+ /* This should not happen AFAIK. */
+ log_error("Lock on incorrect thin lv type %s/%s",
+ lv->vg->name, lv->name);
+ return 0;
+ }
+
+ if (!pool_lv) {
+ /* This should not happen. */
+ log_error("Cannot find thin pool for %s/%s",
+ lv->vg->name, lv->name);
+ return 0;
+ }
+
+ /*
+ * Locking a locked lv (pool in this case) is a no-op.
+ * Unlock when the pool is no longer active.
+ */
+
+ if (!strcmp(def_mode, "un") && pool_is_active(pool_lv))
+ return 1;
+
+ flags |= LDLV_MODE_NO_SH;
+
+ return lockd_lv_name(cmd, pool_lv->vg, pool_lv->name, pool_lv->lock_args,
+ def_mode, flags);
+}
+
+int lockd_lv(struct cmd_context *cmd, struct logical_volume *lv,
+ const char *def_mode, uint32_t flags)
+{
+ if (!is_lockd_type(lv->vg->lock_type))
+ return 1;
+
+ if (lv_is_thin_type(lv))
+ return _lockd_lv_thin(cmd, lv, def_mode, flags);
+
+ /*
+ * LV type cannot be active concurrently on multiple hosts,
+ * so shared mode activation is not allowed.
+ */
+ if (lv_is_external_origin(lv) ||
+ lv_is_thin_type(lv) ||
+ lv_is_mirror_type(lv) ||
+ lv_is_raid_type(lv) ||
+ lv_is_cache_type(lv)) {
+ flags |= LDLV_MODE_NO_SH;
+ }
+
+ return lockd_lv_name(cmd, lv->vg, lv->name, lv->lock_args, def_mode, flags);
+}
+
+static int _init_lv_sanlock(struct cmd_context *cmd, struct volume_group *vg,
+ const char *lv_name, const char **lock_args_ret)
+{
+ daemon_reply reply;
+ const char *reply_str;
+ const char *lv_lock_args = NULL;
+ int result;
+ int ret;
+
+ if (!_lvmlockd_active)
+ return 1;
+ if (!lvmlockd_connected())
+ return 0;
+
+ reply = _lockd_send("init_lv",
+ "pid = %d", getpid(),
+ "vg_name = %s", vg->name,
+ "lv_name = %s", lv_name,
+ "vg_lock_type = %s", "sanlock",
+ "vg_lock_args = %s", vg->lock_args,
+ NULL);
+
+ if (!_lockd_result(reply, &result, NULL)) {
+ ret = 0;
+ } else {
+ ret = (result < 0) ? 0 : 1;
+ }
+
+ if (result == -EEXIST) {
+ log_error("Lock already exists for LV %s/%s", vg->name, lv_name);
+ goto out;
+ }
+
+ if (!ret) {
+ log_error("_init_lv_sanlock lvmlockd result %d", result);
+ goto out;
+ }
+
+ reply_str = daemon_reply_str(reply, "lv_lock_args", NULL);
+ if (!reply_str) {
+ log_error("lv_lock_args not returned");
+ ret = 0;
+ goto out;
+ }
+
+ lv_lock_args = dm_pool_strdup(cmd->mem, reply_str);
+ if (!lv_lock_args) {
+ log_error("lv_lock_args allocation failed");
+ ret = 0;
+ }
+out:
+ daemon_reply_destroy(reply);
+
+ *lock_args_ret = lv_lock_args;
+ return ret;
+}
+
+static int _free_lv_sanlock(struct cmd_context *cmd, struct volume_group *vg,
+ const char *lv_name, const char *lock_args)
+{
+ daemon_reply reply;
+ int result;
+ int ret;
+
+ if (!_lvmlockd_active)
+ return 1;
+ if (!lvmlockd_connected())
+ return 0;
+
+ reply = _lockd_send("free_lv",
+ "pid = %d", getpid(),
+ "vg_name = %s", vg->name,
+ "lv_name = %s", lv_name,
+ "vg_lock_type = %s", "sanlock",
+ "vg_lock_args = %s", vg->lock_args,
+ "lv_lock_args = %s", lock_args ?: "none",
+ NULL);
+
+ if (!_lockd_result(reply, &result, NULL)) {
+ ret = 0;
+ } else {
+ ret = (result < 0) ? 0 : 1;
+ }
+
+ if (!ret) {
+ log_error("_free_lv_sanlock lvmlockd result %d", result);
+ }
+
+ daemon_reply_destroy(reply);
+
+ return ret;
+}
+
+/*
+ * lvcreate
+ *
+ * This can handle only a subset of LV types currently:
+ * - normal LV's
+ * - thin pool LV's (by themselves)
+ * - thin volumes/snapshots (under existing thin pool)
+ *
+ * Current limitations:
+ * - cache-type LV's in a lockd VG must be created with lvconvert.
+ * - old cow snapshots are not handled.
+ * - creating a thin pool and thin lv in one command is not allowed.
+ */
+
+int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg,
+ struct lvcreate_params *lp)
+{
+ const char *lv_name;
+ int lock_type_num = lock_type_to_num(lp->lock_type);
+
+ switch (lock_type_num) {
+ case LOCK_TYPE_NONE:
+ case LOCK_TYPE_CLVM:
+ return 1;
+ case LOCK_TYPE_SANLOCK:
+ case LOCK_TYPE_DLM:
+ break;
+ default:
+ log_error("lockd_init_lv: unknown lock_type.");
+ return 0;
+ }
+
+ if (seg_is_cache(lp) || seg_is_cache_pool(lp)) {
+ log_error("Use lvconvert for cache with lock type %s", lp->lock_type);
+ return 0;
+
+ } else if (!seg_is_thin_volume(lp) && lp->snapshot) {
+ log_error("Only thin snapshots are allowed with lock type %s", lp->lock_type);
+ return 0;
+
+ } else if (seg_is_thin(lp)) {
+ if ((seg_is_thin_volume(lp) && !lp->create_pool) ||
+ (!seg_is_thin_volume(lp) && lp->snapshot)) {
+ struct lv_list *lvl;
+
+ /*
+ * Creating a new thin lv or snapshot. These lvs do not get
+ * their own lock but use the pool lock.
+ */
+
+ if (!(lvl = find_lv_in_vg(vg, lp->pool_name))) {
+ log_error("Failed to find thin pool %s/%s", vg->name, lp->pool_name);
+ return 0;
+ }
+ if (!lockd_lv(cmd, lvl->lv, "ex", LDLV_PERSISTENT)) {
+ log_error("Failed to lock thin pool %s/%s", vg->name, lp->pool_name);
+ return 0;
+ }
+ return 1;
+
+ } else if (seg_is_thin_volume(lp) && lp->create_pool) {
+ /*
+ * Creating a thin pool and a thin lv in it. We could
+ * probably make this work by setting lp->lock_type and
+ * lp->lock_args to NULL in lv_create_single after
+ * creating the pool lv. Then we would just set
+ * lv_name = lp->pool_name here. Stop it at least for now
+ * to try to slow down some of the unnecessary complexity.
+ */
+ log_error("Create thin pool and thin lv separately with lock type %s",
+ lp->lock_type);
+ return 0;
+
+ } else if (!seg_is_thin_volume(lp) && lp->create_pool) {
+ /* Creating a thin pool only. */
+ lv_name = lp->pool_name;
+
+ } else {
+ log_error("Unknown thin options for lock init.");
+ return 0;
+ }
+
+ } else {
+ /* Creating a normal lv. */
+ lv_name = lp->lv_name;
+ }
+
+ if (lock_type_num == LOCK_TYPE_SANLOCK)
+ return _init_lv_sanlock(cmd, vg, lv_name, &lp->lock_args);
+
+ return 1;
+}
+
+/* lvremove */
+
+int lockd_free_lv(struct cmd_context *cmd, struct volume_group *vg,
+ const char *lv_name, const char *lock_args)
+{
+ if (cmd->lock_lv_mode && !strcmp(cmd->lock_lv_mode, "na"))
+ return 1;
+
+ switch (lock_type_to_num(vg->lock_type)) {
+ case LOCK_TYPE_NONE:
+ case LOCK_TYPE_CLVM:
+ case LOCK_TYPE_DLM:
+ return 1;
+ case LOCK_TYPE_SANLOCK:
+ return _free_lv_sanlock(cmd, vg, lv_name, lock_args);
+ default:
+ log_error("lockd_free_lv: unknown lock_type.");
+ return 0;
+ }
+}
+
diff --git a/lib/locking/lvmlockd.h b/lib/locking/lvmlockd.h
index 5641949ff..60f1173a5 100644
--- a/lib/locking/lvmlockd.h
+++ b/lib/locking/lvmlockd.h
@@ -27,6 +27,11 @@
/* lockd_vg flags */
#define LDVG_MODE_NOARG 0x00000001
+/* lockd_lv flags */
+#define LDLV_MODE_NOARG 0x00000001
+#define LDLV_MODE_NO_SH 0x00000002
+#define LDLV_PERSISTENT 0x00000004
+
/* lvmlockd result flags */
#define LD_RF_NO_LOCKSPACES 0x00000001
#define LD_RF_NO_GL_LS 0x00000002
@@ -108,6 +113,19 @@ 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);
+int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg,
+ const char *lv_name, const char *lock_args,
+ const char *def_mode, uint32_t flags);
+int lockd_lv(struct cmd_context *cmd, struct logical_volume *lv,
+ const char *def_mode, uint32_t flags);
+
+/* lvcreate/lvremove use init/free */
+
+int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg,
+ struct lvcreate_params *lp);
+int lockd_free_lv(struct cmd_context *cmd, struct volume_group *vg,
+ const char *lv_name, const char *lock_args);
+
#else /* LVMLOCKD_SUPPORT */
#define lvmlockd_init(cmd) do { } while (0)
@@ -130,6 +148,12 @@ int lockd_vg_update(struct volume_group *vg);
#define lockd_gl_vg(cmd, vg_name, def_gl_mode, def_vg_mode, flags) (1)
#define lockd_vg_update(struct volume_group *vg) (1)
+#define lockd_lv_name(cmd, vg, lv_name, lock_args, def_mode, flags) (1)
+#define lockd_lv(cmd, lv, def_mode, flags) (1)
+
+#define lockd_init_lv(cmd, vg, lp) (1)
+#define lockd_free_lv(cmd, vg, lv_name, lock_args) (1)
+
#endif /* LVMLOCKD_SUPPORT */
#endif
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index fb1cd7823..e8a50d154 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -20,6 +20,7 @@
#include "toolcontext.h"
#include "segtype.h"
#include "str_list.h"
+#include "lvmlockd.h"
#include <time.h>
#include <sys/utsname.h>
@@ -880,6 +881,19 @@ static int _lv_is_exclusive(struct logical_volume *lv)
int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv,
enum activation_change activate, int needs_exclusive)
{
+ const char *ay_with_mode = NULL;
+
+ if (activate == CHANGE_ASY)
+ ay_with_mode = "sh";
+ if (activate == CHANGE_AEY)
+ ay_with_mode = "ex";
+
+ if (is_change_activating(activate) &&
+ !lockd_lv(cmd, lv, ay_with_mode, LDLV_PERSISTENT)) {
+ log_error("Failed to lock logical volume %s/%s", lv->vg->name, lv->name);
+ return 0;
+ }
+
switch (activate) {
case CHANGE_AN:
deactivate:
@@ -922,7 +936,9 @@ exclusive:
if (!activate_lv_excl(cmd, lv))
return_0;
break;
- default: /* CHANGE_AY */
+ case CHANGE_ASY:
+ case CHANGE_AY:
+ default:
if (needs_exclusive || _lv_is_exclusive(lv))
goto exclusive;
log_verbose("Activating logical volume \"%s\".", lv->name);
@@ -930,6 +946,10 @@ exclusive:
return_0;
}
+ if (!is_change_activating(activate) &&
+ !lockd_lv(cmd, lv, "un", LDLV_PERSISTENT))
+ log_error("Failed to unlock logical volume %s/%s", lv->vg->name, lv->name);
+
return 1;
}
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index bf33e263b..2b218c2f2 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -30,6 +30,7 @@
#include "lvm-exec.h"
#include "lvm-signal.h"
#include "memlock.h"
+#include "lvmlockd.h"
typedef enum {
PREFERRED,
@@ -4989,6 +4990,13 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
return 0;
}
+ /*
+ * If the LV is locked from activation, this lock call is a no-op.
+ * Otherwise, this acquires a transient lock on the lv (not PERSISTENT).
+ */
+ if (!lockd_lv(cmd, lv, "ex", 0))
+ return_0;
+
if (lp->sizeargs &&
!(lock_lv = _lvresize_volume(cmd, lv, lp, pvh)))
return_0;
@@ -5335,6 +5343,7 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
int format1_reload_required = 0;
int visible;
struct logical_volume *pool_lv = NULL;
+ struct logical_volume *lock_lv = lv;
struct lv_segment *cache_seg = NULL;
int ask_discard;
struct lv_list *lvl;
@@ -5381,14 +5390,19 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
log_error("Can't remove logical volume %s used by a pool.",
lv->name);
return 0;
- } else if (lv_is_thin_volume(lv))
+ } else if (lv_is_thin_volume(lv)) {
pool_lv = first_seg(lv)->pool_lv;
+ lock_lv = pool_lv;
+ }
if (lv_is_locked(lv)) {
log_error("Can't remove locked LV %s", lv->name);
return 0;
}
+ if (!lockd_lv(cmd, lock_lv, "ex", LDLV_PERSISTENT))
+ return_0;
+
/* FIXME Ensure not referred to by another existing LVs */
ask_discard = find_config_tree_bool(cmd, devices_issue_discards_CFG, NULL);
@@ -5563,6 +5577,9 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
backup(vg);
+ lockd_lv(cmd, lock_lv, "un", LDLV_PERSISTENT | LDLV_MODE_NOARG);
+ lockd_free_lv(cmd, vg, lv->name, lv->lock_args);
+
if (!suppress_remove_message && visible)
log_print_unless_silent("Logical volume \"%s\" successfully removed", lv->name);
@@ -7257,6 +7274,9 @@ struct logical_volume *lv_create_single(struct volume_group *vg,
const struct segment_type *segtype;
struct logical_volume *lv;
+ if (lp->lock_type && !lockd_init_lv(vg->cmd, vg, lp))
+ return_NULL;
+
/* Create pool first if necessary */
if (lp->create_pool && !seg_is_pool(lp)) {
segtype = lp->segtype;
@@ -7298,8 +7318,10 @@ struct logical_volume *lv_create_single(struct volume_group *vg,
lp->segtype = segtype;
}
- if (!(lv = _lv_create_an_lv(vg, lp, lp->lv_name)))
+ if (!(lv = _lv_create_an_lv(vg, lp, lp->lv_name))) {
+ lockd_free_lv(vg->cmd, vg, lp->lv_name, lp->lock_args);
return_NULL;
+ }
if (lp->temporary)
log_verbose("Temporary logical volume \"%s\" created.", lv->name);
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 4fa1d9ed5..833b1244a 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -800,7 +800,8 @@ typedef enum activation_change {
CHANGE_AEY = 2, /* activate exclusively */
CHANGE_ALY = 3, /* activate locally */
CHANGE_ALN = 4, /* deactivate locally */
- CHANGE_AAY = 5 /* automatic activation */
+ CHANGE_AAY = 5, /* automatic activation */
+ CHANGE_ASY = 6 /* activate shared */
} activation_change_t;
/* Returns true, when change activates device */
diff --git a/tools/args.h b/tools/args.h
index 02ef25197..fa750bc88 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -49,6 +49,7 @@ 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(locklv_ARG, '\0', "lock-lv", 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/lvchange.c b/tools/lvchange.c
index ba974388d..7b9a55352 100644
--- a/tools/lvchange.c
+++ b/tools/lvchange.c
@@ -537,6 +537,9 @@ static int lvchange_persistent(struct cmd_context *cmd,
{
enum activation_change activate = CHANGE_AN;
+ /* The LV lock in lvmlockd should remain as it is. */
+ cmd->lockd_lv_disable = 1;
+
if (!get_and_validate_major_minor(cmd, lv->vg->fid->fmt,
&lv->major, &lv->minor))
return_0;
@@ -910,6 +913,22 @@ static int _lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
}
}
+ if (!arg_count(cmd, activate_ARG) && !arg_count(cmd, refresh_ARG)) {
+ /*
+ * If a persistent lv lock already exists from activation
+ * (with the needed mode or higher), this will be a no-op.
+ * Otherwise, the lv lock will be taken as non-persistent
+ * and released when this command exits.
+ *
+ * TODO: use "sh" if the options imply that the lvchange
+ * operation does not modify the LV.
+ */
+ if (!lockd_lv(cmd, lv, "ex", 0)) {
+ stack;
+ return ECMD_FAILED;
+ }
+ }
+
/*
* FIXME: DEFAULT_BACKGROUND_POLLING should be "unspecified".
* If --poll is explicitly provided use it; otherwise polling
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 213b0da95..e3a4ae635 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -2689,6 +2689,12 @@ static int _lvconvert_thin(struct cmd_context *cmd,
return 0;
}
+ if (is_lockd_type(lv->vg->lock_type)) {
+ log_error("Can't use lock_type %s LV as external origin.",
+ lv->vg->lock_type);
+ return 0;
+ }
+
dm_list_init(&lvc.tags);
if (!pool_supports_external_origin(first_seg(pool_lv), lv))
@@ -2801,6 +2807,8 @@ static int _lvconvert_pool(struct cmd_context *cmd,
{
int r = 0;
const char *old_name;
+ const char *lock_free_meta_name = NULL;
+ const char *lock_free_data_name = NULL;
struct lv_segment *seg;
struct volume_group *vg = pool_lv->vg;
struct logical_volume *data_lv;
@@ -3138,6 +3146,36 @@ static int _lvconvert_pool(struct cmd_context *cmd,
if (!attach_pool_data_lv(seg, data_lv))
return_0;
+ /*
+ * A thin pool lv adopts the lock from the data lv, and the lock for
+ * the meta lv is unlocked and freed. A cache pool lv has no lock, and
+ * the existing lock for both data and meta lvs are unlocked and freed.
+ */
+ if (is_lockd_type(pool_lv->vg->lock_type)) {
+ if (lp->pool_metadata_name) {
+ char *c;
+ if ((c = strchr(lp->pool_metadata_name, '/')))
+ lock_free_meta_name = c + 1;
+ else
+ lock_free_meta_name = lp->pool_metadata_name;
+ }
+
+ if (segtype_is_cache_pool(lp->segtype)) {
+ lock_free_data_name = pool_lv->name;
+ pool_lv->lock_type = NULL;
+ pool_lv->lock_args = NULL;
+ } else {
+ if (data_lv->lock_type)
+ pool_lv->lock_type = dm_pool_strdup(cmd->mem, data_lv->lock_type);
+ if (data_lv->lock_args)
+ pool_lv->lock_args = dm_pool_strdup(cmd->mem, data_lv->lock_args);
+ }
+ data_lv->lock_type = NULL;
+ data_lv->lock_args = NULL;
+ metadata_lv->lock_type = NULL;
+ metadata_lv->lock_args = NULL;
+ }
+
/* FIXME: revert renamed LVs in fail path? */
/* FIXME: any common code with metadata/thin_manip.c extend_pool() ? */
@@ -3195,6 +3233,22 @@ out:
(segtype_is_cache_pool(lp->segtype)) ?
"cache" : "thin");
+ if (lock_free_meta_name) {
+ if (!lockd_lv_name(cmd, pool_lv->vg, lock_free_meta_name, NULL, "un", LDLV_PERSISTENT)) {
+ log_error("Failed to unlock pool metadata LV %s/%s",
+ pool_lv->vg->name, lock_free_meta_name);
+ }
+ lockd_free_lv(cmd, pool_lv->vg, lock_free_meta_name, NULL);
+ }
+
+ if (lock_free_data_name) {
+ if (!lockd_lv_name(cmd, pool_lv->vg, lock_free_data_name, NULL, "un", LDLV_PERSISTENT)) {
+ log_error("Failed to unlock pool data LV %s/%s",
+ pool_lv->vg->name, lock_free_data_name);
+ }
+ lockd_free_lv(cmd, pool_lv->vg, lock_free_data_name, NULL);
+ }
+
return r;
#if 0
revert_new_lv:
@@ -3457,6 +3511,17 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
goto_out;
/*
+ * If the lv is inactive before and after the command, the
+ * use of PERSISTENT here means the lv will remain locked as
+ * an effect of running the lvconvert.
+ * To unlock it, it would need to be activated+deactivated.
+ * Or, we could identify the commands for which the lv remains
+ * inactive, and not use PERSISTENT here for those cases.
+ */
+ if (!lockd_lv(cmd, lv, "ex", LDLV_PERSISTENT))
+ goto_out;
+
+ /*
* lp->pvh holds the list of PVs available for allocation or removal
*/
if (lp->pv_count) {
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
index 7d65b520f..ad452b2e7 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -1480,6 +1480,9 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
lp.snapshot ? " as snapshot of " : "",
lp.snapshot ? lp.origin_name : "", lp.segtype->name);
+ if (vg->lock_type && !(lp.lock_type = dm_pool_strdup(cmd->mem, vg->lock_type)))
+ goto_out;
+
if (!lv_create_single(vg, &lp))
goto_out;
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 9f80de4cb..96b3ac974 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -282,6 +282,12 @@ int activation_arg(struct cmd_context *cmd __attribute__((unused)), struct arg_v
av->ui_value = CHANGE_AEY;
}
+ else if (!strcmp(av->value, "s") || !strcmp(av->value, "sy") ||
+ !strcmp(av->value, "ys")) {
+ av->i_value = CHANGE_ASY;
+ av->ui_value = CHANGE_ASY;
+ }
+
else if (!strcmp(av->value, "y")) {
av->i_value = CHANGE_AY;
av->ui_value = CHANGE_AY;
@@ -761,7 +767,7 @@ void lvm_register_commands(void)
yes_ARG, \
quiet_ARG, config_ARG, \
commandprofile_ARG, \
- lockgl_ARG, lockvg_ARG, \
+ lockgl_ARG, lockvg_ARG, locklv_ARG, \
profile_ARG, -1);
#include "commands.h"
#undef xx
@@ -1047,6 +1053,8 @@ static int _get_settings(struct cmd_context *cmd)
cmd->lock_vg_mode = arg_str_value(cmd, lockvg_ARG, NULL);
if (cmd->command->flags & LOCKD_VG_SH)
cmd->lockd_vg_default_sh = 1;
+ if (arg_is_set(cmd, locklv_ARG))
+ cmd->lock_lv_mode = arg_str_value(cmd, locklv_ARG, NULL);
cmd->partial_activation = 0;
cmd->degraded_activation = 0;