summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2014-11-26 17:26:21 -0600
committerDavid Teigland <teigland@redhat.com>2015-03-05 14:25:09 -0600
commit7fddc54dc2b562bff24b54609c9ff428d1e3c88a (patch)
tree3504d4a673f2747e373f57b12f9964b302278ff8
parent479445a5a26587118e4701be3739a85460ef162b (diff)
downloadlvm2-7fddc54dc2b562bff24b54609c9ff428d1e3c88a.tar.gz
lvmlockd: LV locking
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.c398
-rw-r--r--lib/locking/lvmlockd.h24
-rw-r--r--lib/metadata/lv.c22
-rw-r--r--lib/metadata/lv_manip.c29
-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, 571 insertions, 5 deletions
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index cd0ec0ede..219de8b4d 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -101,6 +101,7 @@ struct cmd_context {
unsigned include_active_foreign_vgs:1;
unsigned error_foreign_vgs:1;
unsigned lockd_vg_disable:1;
+ unsigned lockd_lv_disable:1;
unsigned lockd_vg_default_sh:1;
struct dev_types *dev_types;
@@ -147,6 +148,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 618219cb2..c6698fb57 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -1487,3 +1487,401 @@ int lockd_vg_update(struct volume_group *vg)
return ret;
}
+/*
+ * When this is called directly (as opposed to being called from
+ * lockd_lv), the caller knows that the LV has a lock.
+ */
+
+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 (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 (cmd->lock_lv_mode && def_mode && strcmp(cmd->lock_lv_mode, "na") &&
+ 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 (def_mode && !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);
+}
+
+/*
+ * If the VG has no lock_type, then this function can return immediately.
+ * The LV itself may have no lock (NULL lv->lock_type), but the lock request
+ * may be directed to another lock, e.g. the pool LV lock in _lockd_lv_thin.
+ * If the lock request is not directed to another LV, and the LV has no
+ * lock_type set, it means that the LV has no lock, and no locking is done
+ * for it.
+ */
+
+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);
+
+ if (!is_lockd_type(lv->lock_type))
+ return 1;
+
+ /*
+ * 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
+ *
+ * lvcreate sets lp lock_type to the vg lock_type, so any lv
+ * created in a lockd vg will inherit the lock_type of the vg.
+ * In some cases, e.g. thin lvs, this function may decide
+ * that the lv should not be given a lock, in which case it
+ * sets lp lock_type to NULL, which will cause the lv to not
+ * have a lock_type set in its metadata. A lockd_lv() request
+ * on an lv with no lock_type will do nothing (unless the lv
+ * type causes the lock request to be directed to another lv
+ * with a lock, e.g. to the thin pool LV for thin LVs.)
+ *
+ * Current limitations:
+ * - cache-type LV's in a lockd VG must be created with lvconvert.
+ * - 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);
+
+ if (cmd->lock_lv_mode && !strcmp(cmd->lock_lv_mode, "na"))
+ return 1;
+
+ 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) {
+ struct logical_volume *origin_lv;
+
+ /*
+ * COW snapshots are associated with their origin LV,
+ * and only the origin LV needs its own lock, which
+ * represents itself and all associated cow snapshots.
+ */
+
+ if (!(origin_lv = find_lv(vg, lp->origin_name))) {
+ log_error("Failed to find origin LV %s/%s", vg->name, lp->origin_name);
+ return 0;
+ }
+ if (!lockd_lv(cmd, origin_lv, "ex", LDLV_PERSISTENT)) {
+ log_error("Failed to lock origin LV %s/%s", vg->name, lp->origin_name);
+ return 0;
+ }
+ lp->lock_type = NULL;
+ return 1;
+
+ } 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 an lv does not
+ * use its own lock, its lock_type is set to NULL.
+ */
+
+ 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;
+ }
+ lp->lock_type = NULL;
+ 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 be353e396..b368aa6e0 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(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 10ce9063f..6316cd867 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>
@@ -911,6 +912,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:
@@ -953,7 +967,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);
@@ -961,6 +977,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 154166bec..ed58130ac 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,
@@ -5036,6 +5037,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;
@@ -5382,6 +5390,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;
@@ -5428,14 +5437,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);
@@ -5610,6 +5624,10 @@ 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);
+ if (lv->lock_type)
+ 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);
@@ -7325,6 +7343,10 @@ struct logical_volume *lv_create_single(struct volume_group *vg,
const struct segment_type *segtype;
struct logical_volume *lv;
+ /* lockd_init_lv clears lock_type if this LV does not use its own lock. */
+ 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;
@@ -7366,8 +7388,11 @@ 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))) {
+ if (lp->lock_type)
+ 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 80acd1594..4355d1404 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -809,7 +809,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 b13dce0cd..7a9c4b31f 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -50,6 +50,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 d81e11419..054175b94 100644
--- a/tools/lvchange.c
+++ b/tools/lvchange.c
@@ -588,6 +588,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;
@@ -966,6 +969,22 @@ static int _lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
return ECMD_FAILED;
}
+ 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 4176eb12c..6ef0fe1f3 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -2691,6 +2691,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))
@@ -2803,6 +2809,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;
@@ -3141,6 +3149,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() ? */
@@ -3198,6 +3236,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:
@@ -3460,6 +3514,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_bad;
+
+ /*
* 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 816433dc1..864e36d9e 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -1512,6 +1512,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 ba0bdc810..571fc1d45 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -286,6 +286,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;
@@ -765,7 +771,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
@@ -1060,6 +1066,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;