diff options
Diffstat (limited to 'lib/locking/lvmlockd.c')
-rw-r--r-- | lib/locking/lvmlockd.c | 2604 |
1 files changed, 2604 insertions, 0 deletions
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c new file mode 100644 index 000000000..5918de347 --- /dev/null +++ b/lib/locking/lvmlockd.c @@ -0,0 +1,2604 @@ +/* + * Copyright (C) 2014-2015 Red Hat, Inc. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + */ + +#include "lib.h" +#include "toolcontext.h" +#include "metadata.h" +#include "segtype.h" +#include "activate.h" +#include "lvmetad.h" +#include "lvmlockd.h" +#include "lvmcache.h" +#include "lvmlockd-client.h" + +static daemon_handle _lvmlockd; +static const char *_lvmlockd_socket = NULL; +static struct cmd_context *_lvmlockd_cmd = NULL; +static int _use_lvmlockd = 0; /* is 1 if command is configured to use lvmlockd */ +static int _lvmlockd_connected = 0; /* is 1 if command is connected to lvmlockd */ +static int _lvmlockd_init_failed = 0; /* used to suppress further warnings */ + +void lvmlockd_set_socket(const char *sock) +{ + _lvmlockd_socket = sock; +} + +/* + * Set directly from global/use_lvmlockd + */ +void lvmlockd_set_use(int use) +{ + _use_lvmlockd = use; +} + +/* + * Returns the value of global/use_lvmlockd being used by the command. + */ +int lvmlockd_use(void) +{ + return _use_lvmlockd; +} + +/* + * The command continues even if init and/or connect fail, + * because the command is allowed to use local VGs without lvmlockd, + * and is allowed to read lockd VGs without locks from lvmlockd. + */ +void lvmlockd_init(struct cmd_context *cmd) +{ + if (!_use_lvmlockd) { + /* Should never happen, don't call init when not using lvmlockd. */ + log_error("Should not initialize lvmlockd with use_lvmlockd=0."); + } + + if (!_lvmlockd_socket) { + log_warn("WARNING: lvmlockd socket location is not configured."); + _lvmlockd_init_failed = 1; + } + + if (!!access(LVMLOCKD_PIDFILE, F_OK)) { + log_warn("WARNING: lvmlockd process is not running."); + _lvmlockd_init_failed = 1; + } else { + _lvmlockd_init_failed = 0; + } + + _lvmlockd_cmd = cmd; +} + +void lvmlockd_connect(void) +{ + if (!_use_lvmlockd) { + /* Should never happen, don't call connect when not using lvmlockd. */ + log_error("Should not connect to lvmlockd with use_lvmlockd=0."); + } + + if (_lvmlockd_connected) { + /* Should never happen, only call connect once. */ + log_error("lvmlockd is already connected."); + } + + if (_lvmlockd_init_failed) + return; + + _lvmlockd = lvmlockd_open(_lvmlockd_socket); + + if (_lvmlockd.socket_fd >= 0 && !_lvmlockd.error) { + log_debug("Successfully connected to lvmlockd on fd %d.", _lvmlockd.socket_fd); + _lvmlockd_connected = 1; + } else { + log_warn("WARNING: lvmlockd connect failed."); + } +} + +void lvmlockd_disconnect(void) +{ + if (_lvmlockd_connected) + daemon_close(_lvmlockd); + _lvmlockd_connected = 0; + _lvmlockd_cmd = NULL; +} + +/* Translate the result strings from lvmlockd to bit flags. */ +static void _flags_str_to_lockd_flags(const char *flags_str, uint32_t *lockd_flags) +{ + if (strstr(flags_str, "NO_LOCKSPACES")) + *lockd_flags |= LD_RF_NO_LOCKSPACES; + + if (strstr(flags_str, "NO_GL_LS")) + *lockd_flags |= LD_RF_NO_GL_LS; + + if (strstr(flags_str, "DUP_GL_LS")) + *lockd_flags |= LD_RF_DUP_GL_LS; + + if (strstr(flags_str, "INACTIVE_LS")) + *lockd_flags |= LD_RF_INACTIVE_LS; + + if (strstr(flags_str, "ADD_LS_ERROR")) + *lockd_flags |= LD_RF_ADD_LS_ERROR; + + if (strstr(flags_str, "WARN_GL_REMOVED")) + *lockd_flags |= LD_RF_WARN_GL_REMOVED; +} + +/* + * evaluate the reply from lvmlockd, check for errors, extract + * the result and lockd_flags returned by lvmlockd. + * 0 failure (no result/lockd_flags set) + * 1 success (result/lockd_flags set) + */ + +/* + * This is an arbitrary number that we know lvmlockd + * will not return. daemon_reply_int reverts to this + * value if it finds no result value. + */ +#define NO_LOCKD_RESULT -1000 + +static int _lockd_result(daemon_reply reply, int *result, uint32_t *lockd_flags) +{ + int reply_result; + const char *flags_str = NULL; + const char *lock_type = NULL; + + if (reply.error) { + log_error("lockd_result reply error %d", reply.error); + return 0; + } + + if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) { + log_error("lockd_result bad response"); + return 0; + } + + reply_result = daemon_reply_int(reply, "op_result", NO_LOCKD_RESULT); + if (reply_result == NO_LOCKD_RESULT) { + log_error("lockd_result no op_result"); + return 0; + } + + /* The lock_type that lvmlockd used for locking. */ + lock_type = daemon_reply_str(reply, "lock_type", "none"); + + *result = reply_result; + + if (lockd_flags) { + if ((flags_str = daemon_reply_str(reply, "result_flags", NULL))) + _flags_str_to_lockd_flags(flags_str, lockd_flags); + } + + log_debug("lockd_result %d flags %s lm %s", reply_result, + flags_str ? flags_str : "none", lock_type); + return 1; +} + +static daemon_reply _lockd_send(const char *req_name, ...) +{ + va_list ap; + daemon_reply repl; + daemon_request req; + + req = daemon_request_make(req_name); + + va_start(ap, req_name); + daemon_request_extend_v(req, ap); + va_end(ap); + + repl = daemon_send(_lvmlockd, req); + + daemon_request_destroy(req); + + return repl; +} + +/* + * result/lockd_flags are values returned from lvmlockd. + * + * return 0 (failure) + * return 1 (result/lockd_flags indicate success/failure) + * + * return 1 result 0 (success) + * return 1 result < 0 (failure) + * + * caller may ignore result < 0 failure depending on + * lockd_flags and the specific command/mode. + * + * When this function returns 0 (failure), no result/lockd_flags + * were obtained from lvmlockd. + * + * When this function returns 1 (success), result/lockd_flags may + * have been obtained from lvmlockd. This lvmlockd result may + * indicate a locking failure. + */ + +static int _lockd_request(struct cmd_context *cmd, + const char *req_name, + const char *vg_name, + const char *vg_lock_type, + const char *vg_lock_args, + const char *lv_name, + const char *lv_uuid, + const char *lv_lock_args, + const char *mode, + const char *opts, + int *result, + uint32_t *lockd_flags) +{ + const char *cmd_name = get_cmd_name(); + daemon_reply reply; + int pid = getpid(); + + *result = 0; + *lockd_flags = 0; + + if (!strcmp(mode, "na")) + return 1; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + /* cmd and pid are passed for informational and debugging purposes */ + + if (!cmd_name || !cmd_name[0]) + cmd_name = "none"; + + if (vg_name && lv_name) { + reply = _lockd_send(req_name, + "cmd = %s", cmd_name, + "pid = %d", pid, + "mode = %s", mode, + "opts = %s", opts ?: "none", + "vg_name = %s", vg_name, + "lv_name = %s", lv_name, + "lv_uuid = %s", lv_uuid, + "vg_lock_type = %s", vg_lock_type ?: "none", + "vg_lock_args = %s", vg_lock_args ?: "none", + "lv_lock_args = %s", lv_lock_args ?: "none", + NULL); + + if (!_lockd_result(reply, result, lockd_flags)) + goto fail; + + log_debug("lvmlockd %s %s vg %s lv %s result %d %x", + req_name, mode, vg_name, lv_name, *result, *lockd_flags); + + } else if (vg_name) { + reply = _lockd_send(req_name, + "cmd = %s", cmd_name, + "pid = %d", pid, + "mode = %s", mode, + "opts = %s", opts ?: "none", + "vg_name = %s", vg_name, + "vg_lock_type = %s", vg_lock_type ?: "none", + "vg_lock_args = %s", vg_lock_args ?: "none", + NULL); + + if (!_lockd_result(reply, result, lockd_flags)) + goto fail; + + log_debug("lvmlockd %s %s vg %s result %d %x", + req_name, mode, vg_name, *result, *lockd_flags); + + } else { + reply = _lockd_send(req_name, + "cmd = %s", cmd_name, + "pid = %d", pid, + "mode = %s", mode, + "opts = %s", opts ?: "none", + "vg_lock_type = %s", vg_lock_type ?: "none", + NULL); + + if (!_lockd_result(reply, result, lockd_flags)) + goto fail; + + log_debug("lvmlockd %s %s result %d %x", + req_name, mode, *result, *lockd_flags); + } + + daemon_reply_destroy(reply); + + /* result/lockd_flags have lvmlockd result */ + return 1; + + fail: + /* no result was obtained from lvmlockd */ + + log_error("lvmlockd %s %s failed no result", req_name, mode); + + daemon_reply_destroy(reply); + return 0; +} + +/* + * Eventually add an option to specify which pv the lvmlock lv should be placed on. + */ + +static int _create_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg, + const char *lock_lv_name, int extend_mb) +{ + struct logical_volume *lv; + struct lvcreate_params lp = { + .activate = CHANGE_ALY, + .alloc = ALLOC_INHERIT, + .extents = (extend_mb * 1024 * 1024) / (vg->extent_size * SECTOR_SIZE), + .major = -1, + .minor = -1, + .permission = LVM_READ | LVM_WRITE, + .pvh = &vg->pvs, + .read_ahead = DM_READ_AHEAD_NONE, + .stripes = 1, + .vg_name = vg->name, + .lv_name = dm_pool_strdup(cmd->mem, lock_lv_name), + .zero = 1, + }; + + dm_list_init(&lp.tags); + + if (!(lp.segtype = get_segtype_from_string(vg->cmd, "striped"))) + return_0; + + lv = lv_create_single(vg, &lp); + if (!lv) { + log_error("Failed to create sanlock lv %s in vg %s", lock_lv_name, vg->name); + return 0; + } + + vg->sanlock_lv = lv; + + return 1; +} + +static int _remove_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) +{ + if (!lv_remove(vg->sanlock_lv)) { + log_error("Failed to remove sanlock LV %s/%s", vg->name, vg->sanlock_lv->name); + return 0; + } + + return 1; +} + +static int _extend_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg, int extend_mb) +{ + struct logical_volume *lv = vg->sanlock_lv; + struct lvresize_params lp = { + .lv_name = vg->sanlock_lv->name, + .sign = SIGN_NONE, + .percent = PERCENT_NONE, + .resize = LV_EXTEND, + .ac_force = 1, + .sizeargs = 1, + }; + + lp.size = lv->size + ((extend_mb * 1024 * 1024) / SECTOR_SIZE); + + if (!lv_resize_prepare(cmd, lv, &lp, &vg->pvs) || + !lv_resize(cmd, lv, &lp, &vg->pvs)) { + log_error("Extend LV %s/%s to size %llu failed.", + vg->name, lv->name, (unsigned long long)lp.size); + return 0; + } + + return 1; +} + +/* When one host does _extend_sanlock_lv, the others need to refresh the size. */ + +static int _refresh_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) +{ + if (!lv_refresh_suspend_resume(cmd, vg->sanlock_lv)) { + log_error("Failed to refresh %s.", vg->sanlock_lv->name); + return 0; + } + + return 1; +} + +/* + * Called at the beginning of lvcreate in a sanlock VG to ensure + * that there is space in the sanlock LV for a new lock. If it's + * full, then this extends it. + */ + +int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) +{ + daemon_reply reply; + int extend_mb; + int result; + int ret; + + if (!_use_lvmlockd) + return 1; + if (!_lvmlockd_connected) + return 0; + + extend_mb = find_config_tree_int(cmd, global_sanlock_lv_extend_CFG, NULL); + + /* + * User can choose to not automatically extend the lvmlock LV + * so they can manually extend it. + */ + if (!extend_mb) + return 1; + + /* + * Another host may have extended the lvmlock LV already. + * Refresh so that we'll find the new space they added + * when we search for new space. + */ + if (!_refresh_sanlock_lv(cmd, vg)) + return 0; + + /* + * Ask lvmlockd/sanlock to look for an unused lock. + */ + reply = _lockd_send("find_free_lock", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + } else { + ret = (result < 0) ? 0 : 1; + } + + /* No space on the lvmlock lv for a new lease. */ + if (result == -EMSGSIZE) + ret = _extend_sanlock_lv(cmd, vg, extend_mb); + + daemon_reply_destroy(reply); + + return ret; +} + +static int _activate_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) +{ + if (!activate_lv(cmd, vg->sanlock_lv)) { + log_error("Failed to activate sanlock lv %s/%s", vg->name, vg->sanlock_lv->name); + return 0; + } + + return 1; +} + +static int _deactivate_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) +{ + if (!deactivate_lv(cmd, vg->sanlock_lv)) { + log_error("Failed to deactivate sanlock lv %s/%s", vg->name, vg->sanlock_lv->name); + return 0; + } + + return 1; +} + +static int _init_vg_dlm(struct cmd_context *cmd, struct volume_group *vg) +{ + daemon_reply reply; + const char *reply_str; + const char *vg_lock_args = NULL; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + reply = _lockd_send("init_vg", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "vg_lock_type = %s", "dlm", + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + result = -ELOCKD; + } else { + ret = (result < 0) ? 0 : 1; + } + + switch (result) { + case 0: + break; + case -ELOCKD: + log_error("VG %s init failed: lvmlockd not available", vg->name); + break; + case -EARGS: + log_error("VG %s init failed: invalid parameters for dlm", vg->name); + break; + case -EMANAGER: + log_error("VG %s init failed: lock manager dlm is not running", vg->name); + break; + case -EPROTONOSUPPORT: + log_error("VG %s init failed: lock manager dlm is not supported by lvmlockd", vg->name); + break; + default: + log_error("VG %s init failed: %d", vg->name, result); + } + + if (!ret) + goto out; + + if (!(reply_str = daemon_reply_str(reply, "vg_lock_args", NULL))) { + log_error("VG %s init failed: lock_args not returned", vg->name); + ret = 0; + goto out; + } + + if (!(vg_lock_args = dm_pool_strdup(cmd->mem, reply_str))) { + log_error("VG %s init failed: lock_args alloc failed", vg->name); + ret = 0; + goto out; + } + + vg->lock_type = "dlm"; + vg->lock_args = vg_lock_args; + + if (!vg_write(vg) || !vg_commit(vg)) { + log_error("VG %s init failed: vg_write vg_commit", vg->name); + ret = 0; + goto out; + } + + ret = 1; +out: + daemon_reply_destroy(reply); + return ret; +} + +static int _init_vg_sanlock(struct cmd_context *cmd, struct volume_group *vg, int lv_lock_count) +{ + daemon_reply reply; + const char *reply_str; + const char *vg_lock_args = NULL; + const char *opts = NULL; + int extend_mb; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + /* + * Automatic extension of the sanlock lv is disabled by + * setting sanlock_lv_extend to 0. Zero won't work as + * an initial size, so in this case, use the default as + * the initial size. + */ + if (!(extend_mb = find_config_tree_int(cmd, global_sanlock_lv_extend_CFG, NULL))) + extend_mb = DEFAULT_SANLOCK_LV_EXTEND_MB; + + /* + * Creating the sanlock LV writes the VG containing the new lvmlock + * LV, then activates the lvmlock LV. The lvmlock LV must be active + * before we ask lvmlockd to initialize the VG because sanlock needs + * to initialize leases on the lvmlock LV. + * + * When converting an existing VG to sanlock, the sanlock lv needs to + * be large enough to hold leases for all existing lvs needing locks. + * One sanlock lease uses 1MB/8MB for 512/4K sector size devices, so + * increase the initial size by 1MB/8MB for each existing lv. + * FIXME: we don't know what sector size the pv will have, so we + * multiply by 8 (MB) unnecessarily when the sector size is 512. + */ + + if (lv_lock_count) + extend_mb += (lv_lock_count * 8); + + if (!_create_sanlock_lv(cmd, vg, LOCKD_SANLOCK_LV_NAME, extend_mb)) { + log_error("Failed to create internal lv."); + return 0; + } + + /* + * N.B. this passes the sanlock lv name as vg_lock_args + * even though it is only part of the final args string + * which will be returned from lvmlockd. + */ + + reply = _lockd_send("init_vg", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "vg_lock_type = %s", "sanlock", + "vg_lock_args = %s", vg->sanlock_lv->name, + "opts = %s", opts ?: "none", + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + result = -ELOCKD; + } else { + ret = (result < 0) ? 0 : 1; + } + + switch (result) { + case 0: + break; + case -ELOCKD: + log_error("VG %s init failed: lvmlockd not available", vg->name); + break; + case -EARGS: + log_error("VG %s init failed: invalid parameters for sanlock", vg->name); + break; + case -EMANAGER: + log_error("VG %s init failed: lock manager sanlock is not running", vg->name); + break; + case -EPROTONOSUPPORT: + log_error("VG %s init failed: lock manager sanlock is not supported by lvmlockd", vg->name); + break; + case -EMSGSIZE: + log_error("VG %s init failed: no disk space for leases", vg->name); + break; + default: + log_error("VG %s init failed: %d", vg->name, result); + } + + if (!ret) + goto out; + + if (!(reply_str = daemon_reply_str(reply, "vg_lock_args", NULL))) { + log_error("VG %s init failed: lock_args not returned", vg->name); + ret = 0; + goto out; + } + + if (!(vg_lock_args = dm_pool_strdup(cmd->mem, reply_str))) { + log_error("VG %s init failed: lock_args alloc failed", vg->name); + ret = 0; + goto out; + } + + lv_set_hidden(vg->sanlock_lv); + vg->sanlock_lv->status |= LOCKD_SANLOCK_LV; + + vg->lock_type = "sanlock"; + vg->lock_args = vg_lock_args; + + if (!vg_write(vg) || !vg_commit(vg)) { + log_error("VG %s init failed: vg_write vg_commit", vg->name); + ret = 0; + goto out; + } + + ret = 1; +out: + if (!ret) { + /* + * The usleep delay gives sanlock time to close the lock lv, + * and usually avoids having an annoying error printed. + */ + usleep(1000000); + _deactivate_sanlock_lv(cmd, vg); + _remove_sanlock_lv(cmd, vg); + if (!vg_write(vg) || !vg_commit(vg)) + stack; + } + + daemon_reply_destroy(reply); + return ret; +} + +/* called after vg_remove on disk */ + +static int _free_vg_dlm(struct cmd_context *cmd, struct volume_group *vg) +{ + uint32_t lockd_flags; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + /* + * Unlocking the vg lock here preempts the lvmlockd unlock in + * toollib.c which happens too late since the lockspace is + * left here. + */ + + /* Equivalent to a standard unlock. */ + ret = _lockd_request(cmd, "lock_vg", + vg->name, NULL, NULL, NULL, NULL, NULL, "un", NULL, + &result, &lockd_flags); + + if (!ret || result < 0) { + log_error("_free_vg_dlm lvmlockd result %d", result); + return 0; + } + + /* Leave the dlm lockspace. */ + lockd_stop_vg(cmd, vg); + + return 1; +} + +/* called before vg_remove on disk */ + +static int _free_vg_sanlock(struct cmd_context *cmd, struct volume_group *vg) +{ + daemon_reply reply; + uint32_t lockd_flags = 0; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + /* + * vgremove originally held the global lock, but lost it because the + * vgremove command is removing multiple VGs, and removed the VG + * holding the global lock before attempting to remove this VG. + * To avoid this situation, the user should remove the VG holding + * the global lock in a command by itself, or as the last arg in a + * vgremove command that removes multiple VGs. + */ + if (cmd->lockd_gl_removed) { + log_error("Global lock failed: global lock was lost by removing a previous VG."); + return 0; + } + + if (!vg->lock_args || !strlen(vg->lock_args)) { + /* Shouldn't happen in general, but maybe in some error cases? */ + log_debug("_free_vg_sanlock %s no lock_args", vg->name); + return 1; + } + + reply = _lockd_send("free_vg", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "vg_lock_type = %s", vg->lock_type, + "vg_lock_args = %s", vg->lock_args, + NULL); + + if (!_lockd_result(reply, &result, &lockd_flags)) { + ret = 0; + } else { + ret = (result < 0) ? 0 : 1; + } + + /* + * Other hosts could still be joined to the lockspace, which means they + * are using the internal sanlock LV, which means we cannot remove the + * VG. Once other hosts stop using the VG it can be removed. + */ + if (result == -EBUSY) { + log_error("Lockspace for \"%s\" not stopped on other hosts", vg->name); + goto out; + } + + if (!ret) { + log_error("_free_vg_sanlock lvmlockd result %d", result); + goto out; + } + + /* + * If the global lock was been removed by removing this VG, then: + * + * Print a warning indicating that the global lock should be enabled + * in another remaining sanlock VG. + * + * Do not allow any more VGs to be removed by this command, e.g. + * if a command removes two sanlock VGs, like vgremove foo bar, + * and the global lock existed in foo, do not continue to remove + * VG bar without the global lock. See the corresponding check above. + */ + if (lockd_flags & LD_RF_WARN_GL_REMOVED) { + log_warn("VG %s held the sanlock global lock, enable global lock in another VG.", vg->name); + cmd->lockd_gl_removed = 1; + } + + /* + * The usleep delay gives sanlock time to close the lock lv, + * and usually avoids having an annoying error printed. + */ + usleep(1000000); + + _deactivate_sanlock_lv(cmd, vg); + _remove_sanlock_lv(cmd, vg); + out: + daemon_reply_destroy(reply); + + return ret; +} + +/* + * Tell lvmlockd to forget about an old VG name. + * lvmlockd remembers previous lockd VGs so that it can provide more + * informative error messages (see INACTIVE_LS, ADD_LS_ERROR). + * + * If a new local VG is created with the same name as a previous lockd VG, + * lvmlockd's memory of the previous lockd VG interferes (causes incorrect + * lockd_vg failures). + * + * We could also remove the list of inactive (old) VG names from lvmlockd, + * and then this function would not be needed, but this would also reduce + * the ability to have helpful error messages. + */ + +static void _forget_vg_name(struct cmd_context *cmd, struct volume_group *vg) +{ + daemon_reply reply; + + if (!_use_lvmlockd) + return; + if (!_lvmlockd_connected) + return; + + reply = _lockd_send("forget_vg_name", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + NULL); + + daemon_reply_destroy(reply); +} + +/* vgcreate */ + +int lockd_init_vg(struct cmd_context *cmd, struct volume_group *vg, + const char *lock_type, int lv_lock_count) +{ + switch (get_lock_type_from_string(lock_type)) { + case LOCK_TYPE_NONE: + _forget_vg_name(cmd, vg); + return 1; + case LOCK_TYPE_CLVM: + return 1; + case LOCK_TYPE_DLM: + return _init_vg_dlm(cmd, vg); + case LOCK_TYPE_SANLOCK: + return _init_vg_sanlock(cmd, vg, lv_lock_count); + default: + log_error("Unknown lock_type."); + return 0; + } +} + +/* vgremove before the vg is removed */ + +int lockd_free_vg_before(struct cmd_context *cmd, struct volume_group *vg) +{ + switch (get_lock_type_from_string(vg->lock_type)) { + case LOCK_TYPE_NONE: + case LOCK_TYPE_CLVM: + case LOCK_TYPE_DLM: + return 1; + case LOCK_TYPE_SANLOCK: + /* returning an error will prevent vg_remove() */ + return _free_vg_sanlock(cmd, vg); + default: + log_error("Unknown lock_type."); + return 0; + } +} + +/* vgremove after the vg is removed */ + +void lockd_free_vg_final(struct cmd_context *cmd, struct volume_group *vg) +{ + switch (get_lock_type_from_string(vg->lock_type)) { + case LOCK_TYPE_NONE: + case LOCK_TYPE_CLVM: + case LOCK_TYPE_SANLOCK: + break; + case LOCK_TYPE_DLM: + _free_vg_dlm(cmd, vg); + break; + default: + log_error("Unknown lock_type."); + } +} + +/* + * Starting a vg involves: + * 1. reading the vg without a lock + * 2. getting the lock_type/lock_args from the vg metadata + * 3. doing start_vg in lvmlockd for the lock_type; + * this means joining the lockspace + * + * The vg read in step 1 should not be used for anything + * other than getting the lock_type/lock_args/uuid necessary + * for starting the lockspace. To use the vg after starting + * the lockspace, follow the standard method which is: + * lock the vg, read/use/write the vg, unlock the vg. + */ + +int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg) +{ + char uuid[64] __attribute__((aligned(8))); + daemon_reply reply; + int host_id = 0; + int result; + int ret; + + memset(uuid, 0, sizeof(uuid)); + + if (!is_lockd_type(vg->lock_type)) + return 1; + + if (!_use_lvmlockd) { + log_error("VG %s start failed: lvmlockd is not enabled", vg->name); + return 0; + } + if (!_lvmlockd_connected) { + log_error("VG %s start failed: lvmlockd is not running", vg->name); + return 0; + } + + log_debug("lockd start VG %s lock_type %s", + vg->name, vg->lock_type ? vg->lock_type : "empty"); + + if (!id_write_format(&vg->id, uuid, sizeof(uuid))) + return_0; + + if (vg->lock_type && !strcmp(vg->lock_type, "sanlock")) { + /* + * This is the big difference between starting + * sanlock vgs vs starting dlm vgs: the internal + * sanlock lv needs to be activated before lvmlockd + * does the start because sanlock needs to use the lv + * to access locks. + */ + if (!_activate_sanlock_lv(cmd, vg)) + return 0; + + host_id = find_config_tree_int(cmd, local_host_id_CFG, NULL); + } + + reply = _lockd_send("start_vg", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "vg_lock_type = %s", vg->lock_type, + "vg_lock_args = %s", vg->lock_args ?: "none", + "vg_uuid = %s", uuid[0] ? uuid : "none", + "version = %d", (int64_t)vg->seqno, + "host_id = %d", host_id, + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + result = -ELOCKD; + } else { + ret = (result < 0) ? 0 : 1; + } + + switch (result) { + case 0: + log_print_unless_silent("VG %s starting %s lockspace", vg->name, vg->lock_type); + break; + case -ELOCKD: + log_error("VG %s start failed: lvmlockd not available", vg->name); + break; + case -EEXIST: + log_debug("VG %s start error: already started", vg->name); + ret = 1; + break; + case -EARGS: + log_error("VG %s start failed: invalid parameters for %s", vg->name, vg->lock_type); + break; + case -EHOSTID: + log_error("VG %s start failed: invalid sanlock host_id, set in lvmlocal.conf", vg->name); + break; + case -EMANAGER: + log_error("VG %s start failed: lock manager %s is not running", vg->name, vg->lock_type); + break; + case -EPROTONOSUPPORT: + log_error("VG %s start failed: lock manager %s is not supported by lvmlockd", vg->name, vg->lock_type); + break; + default: + log_error("VG %s start failed: %d", vg->name, result); + } + + daemon_reply_destroy(reply); + + return ret; +} + +int lockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg) +{ + daemon_reply reply; + int result; + int ret; + + if (!is_lockd_type(vg->lock_type)) + return 1; + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + log_debug("lockd stop VG %s lock_type %s", + vg->name, vg->lock_type ? vg->lock_type : "empty"); + + reply = _lockd_send("stop_vg", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + } else { + ret = (result < 0) ? 0 : 1; + } + + if (result == -ENOLS) { + ret = 1; + goto out; + } + + if (result == -EBUSY) { + log_error("VG %s stop failed: LVs must first be deactivated", vg->name); + goto out; + } + + if (!ret) { + log_error("VG %s stop failed: %d", vg->name, result); + goto out; + } + + if (!strcmp(vg->lock_type, "sanlock")) { + log_debug("lockd_stop_vg deactivate sanlock lv"); + _deactivate_sanlock_lv(cmd, vg); + } +out: + daemon_reply_destroy(reply); + + return ret; +} + +int lockd_start_wait(struct cmd_context *cmd) +{ + daemon_reply reply; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + reply = _lockd_send("start_wait", + "pid = %d", getpid(), + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + } else { + ret = (result < 0) ? 0 : 1; + } + + if (!ret) + log_error("Lock start failed"); + + /* + * FIXME: get a list of vgs that started so we can + * better report what worked and what didn't? + */ + + daemon_reply_destroy(reply); + + return ret; +} + +/* + * lockd_gl_create() is a variation of lockd_gl() used only by vgcreate. + * It handles the case that when using sanlock, the global lock does + * not exist until after the first vgcreate is complete, since the global + * lock exists on storage within an actual VG. So, the first vgcreate + * needs special logic to detect this bootstrap case. + * + * When the vgcreate is not creating the first VG, then lockd_gl_create() + * behaves the same as lockd_gl(). + * + * vgcreate will have a lock_type for the new VG which lockd_gl_create() + * can provide in the lock-gl call. + * + * lockd_gl() and lockd_gl_create() differ in the specific cases where + * ENOLS (no lockspace found) is overriden. In the vgcreate case, the + * override cases are related to sanlock bootstrap, and the lock_type of + * the vg being created is needed. + * + * 1. vgcreate of the first lockd-type vg calls lockd_gl_create() + * to acquire the global lock. + * + * 2. vgcreate/lockd_gl_create passes gl lock request to lvmlockd, + * along with lock_type of the new vg. + * + * 3. lvmlockd finds no global lockspace/lock. + * + * 4. dlm: + * If the lock_type from vgcreate is dlm, lvmlockd creates the + * dlm global lockspace, and queues the global lock request + * for vgcreate. lockd_gl_create returns sucess with the gl held. + * + * sanlock: + * If the lock_type from vgcreate is sanlock, lvmlockd returns -ENOLS + * with the NO_GL_LS flag. lvmlockd cannot create or acquire a sanlock + * global lock until the VG exists on disk (the locks live within the VG). + * + * lockd_gl_create sees sanlock/ENOLS/NO_GL_LS (and optionally the + * "enable" lock-gl arg), determines that this is the sanlock + * bootstrap special case, and returns success without the global lock. + * + * vgcreate creates the VG on disk, and calls lockd_init_vg() which + * initializes/enables a global lock on the new VG's internal sanlock lv. + * Future lockd_gl/lockd_gl_create calls will acquire the existing gl. + */ + +int lockd_gl_create(struct cmd_context *cmd, const char *def_mode, const char *vg_lock_type) +{ + const char *mode = NULL; + uint32_t lockd_flags; + int retries = 0; + int result; + + /* + * There are four variations of creating a local/lockd VG + * with/without use_lvmlockd set. + * + * use_lvmlockd=1, lockd VG: + * This function should acquire or create the global lock. + * + * use_lvmlockd=0, local VG: + * This function is a no-op, just returns 1. + * + * use_lvmlockd=0, lockd VG + * An error is returned in vgcreate_params_set_from_args (before this is called). + * + * use_lvmlockd=1, local VG + * This function should acquire the global lock. + */ + if (!_use_lvmlockd) { + if (!is_lockd_type(vg_lock_type)) + return 1; + log_error("Cannot create VG with lock_type %s without lvmlockd.", vg_lock_type); + return 0; + } + + log_debug("lockd global lock_type %s", vg_lock_type); + + if (!mode) + mode = def_mode; + if (!mode) { + log_error("Unknown lock-gl mode"); + return 0; + } + + req: + if (!_lockd_request(cmd, "lock_gl", + NULL, vg_lock_type, NULL, NULL, NULL, NULL, mode, NULL, + &result, &lockd_flags)) { + /* No result from lvmlockd, it is probably not running. */ + log_error("Global lock failed: check that lvmlockd is running."); + return 0; + } + + if (result == -EAGAIN) { + if (retries < find_config_tree_int(cmd, global_lvmlockd_lock_retries_CFG, NULL)) { + log_warn("Retrying %s global lock", mode); + sleep(1); + retries++; + goto req; + } + } + + /* + * ENOLS: no lockspace was found with a global lock. + * It may not exist (perhaps this command is creating the first), + * or it may not be visible or started on the system yet. + */ + + if (result == -ENOLS) { + if (!strcmp(mode, "un")) + return 1; + + /* + * This is the sanlock bootstrap condition for proceding + * without the global lock: a chicken/egg case for the first + * sanlock VG that is created. When creating the first + * sanlock VG, there is no global lock to acquire because + * the gl will exist in the VG being created. So, we + * skip acquiring the global lock when creating this initial + * VG, and enable the global lock in this VG. + * + * This initial bootstrap condition is identified based on + * two things: + * + * 1. No sanlock VGs have been started in lvmlockd, causing + * lvmlockd to return NO_GL_LS/NO_LOCKSPACES. + * + * 2. No sanlock VGs are seen in lvmcache after the disk + * scan performed in lvmetad_validate_global_cache(). + * + * If both of those are true, we go ahead and create this new + * VG which will have the global lock enabled. However, this + * has a shortcoming: another sanlock VG may exist that hasn't + * appeared to the system yet. If that VG has its global lock + * enabled, then when it appears later, duplicate global locks + * will be seen, and a warning will indicate that one of them + * should be disabled. + * + * The two bootstrap conditions have another shortcoming to the + * opposite effect: other sanlock VGs may be visible to the + * system, but none of them have a global lock enabled. + * In that case, it would make sense to create this new VG with + * an enabled global lock. (FIXME: we could detect that none + * of the existing sanlock VGs have a gl enabled and allow this + * vgcreate to go ahead.) Enabling the global lock in one of + * the existing sanlock VGs is currently the simplest solution. + */ + + if ((lockd_flags & LD_RF_NO_GL_LS) && + (lockd_flags & LD_RF_NO_LOCKSPACES) && + !strcmp(vg_lock_type, "sanlock")) { + lvmetad_validate_global_cache(cmd, 1); + /* + * lvmcache holds provisional VG lock_type info because + * lvmetad_validate_global_cache did a disk scan. + */ + if (lvmcache_contains_lock_type_sanlock(cmd)) { + /* FIXME: we could check that all are started, and then check that none have gl enabled. */ + log_error("Global lock failed: start existing sanlock VGs to access global lock."); + log_error("(If all sanlock VGs are started, enable global lock with lvmlockctl.)"); + return 0; + } + log_print_unless_silent("Enabling sanlock global lock"); + return 1; + } + + if (!strcmp(vg_lock_type, "sanlock")) + log_error("Global lock failed: check that VG holding global lock exists and is started."); + else + log_error("Global lock failed: check that global lockspace is started."); + return 0; + } + + /* + * Check for each specific error that can be returned so a helpful + * message can be printed for it. + */ + if (result < 0) { + if (result == -ESTARTING) + log_error("Global lock failed: lockspace is starting."); + else if (result == -EAGAIN) + log_error("Global lock failed: held by other host."); + else if (result == -EPROTONOSUPPORT) + log_error("VG create failed: lock manager %s is not supported by lvmlockd.", vg_lock_type); + else + log_error("Global lock failed: error %d", result); + return 0; + } + + lvmetad_validate_global_cache(cmd, 1); + + return 1; +} + +/* + * The global lock protects: + * + * - The global VG namespace. Two VGs cannot have the same name. + * Used by any command that creates or removes a VG name, + * e.g. vgcreate, vgremove, vgrename, vgsplit, vgmerge. + * + * - The set of orphan PVs. + * Used by any command that changes a non-PV device into an orphan PV, + * an orphan PV into a device, a non-orphan PV (in a VG) into an orphan PV + * (not in a VG), or an orphan PV into a non-orphan PV, + * e.g. pvcreate, pvremove, vgcreate, vgremove, vgextend, vgreduce. + * + * - The properties of orphan PVs. It is possible to make changes to the + * properties of an orphan PV, e.g. pvresize, pvchange. + * + * These are things that cannot be protected by a VG lock alone, since + * orphan PVs do not belong to a real VG (an artificial VG does not + * apply since a sanlock lock only exists on real storage.) + * + * If a command will change any of the things above, it must first acquire + * the global lock in exclusive mode. + * + * If command is reading any of the things above, it must acquire the global + * lock in shared mode. A number of commands read the things above, including: + * + * - Reporting/display commands which show all VGs. Any command that + * will iterate through the entire VG namespace must first acquire the + * global lock shared so that it has an accurate view of the namespace. + * + * - A command where a tag name is used to identify what to process. + * A tag requires reading all VGs to check if they match the tag. + * + * In these cases, the global lock must be acquired before the list of + * all VGs is created. + * + * The global lock is not generally unlocked explicitly in the code. + * When the command disconnects from lvmlockd, lvmlockd automatically + * releases the locks held by the command. The exception is if a command + * will continue running for a long time while not needing the global lock, + * e.g. commands that poll to report progress. + * + * Acquiring the global lock also updates the local lvmetad cache if + * necessary. lockd_gl() first acquires the lock via lvmlockd, then + * before returning to the caller, it checks that the global information + * (e.g. VG namespace, set of orphans) is up to date in lvmetad. If + * not, it scans disks and updates the lvmetad cache before returning + * to the caller. It does this checking using a version number associated + * with the global lock. The version number is incremented each time + * a change is made to the state associated with the global lock, and + * if the local version number is lower than the version number in the + * lock, then the local lvmetad state must be updated. + * + * There are two cases where the global lock can be taken in shared mode, + * and then later converted to ex. pvchange and pvresize use process_each_pv + * which does lockd_gl("sh") to get the list of VGs. Later, in the "_single" + * function called within process_each_pv, the PV may be an orphan, in which + * case the ex global lock is needed, so it's converted to ex at that point. + * + * Effects of misconfiguring use_lvmlockd. + * + * - Setting use_lvmlockd=1 tells lvm commands to use the global lock. + * This should not be set unless a lock manager and lockd VGs will + * be used. Setting use_lvmlockd=1 without setting up a lock manager + * or using lockd VGs will cause lvm commands to fail when they attempt + * to change any global state (requiring the ex global lock), and will + * cause warnings when the commands read global state (requiring the sh + * global lock). In this condition, lvm is nominally useful, and existing + * local VGs can continue to be used mostly as usual. But, the + * warnings/errors should lead a user to either set up a lock manager + * and lockd VGs, or set use_lvmlockd to 0. + * + * - Setting use_lvmlockd=0 tells lvm commands to not use the global lock. + * If use_lvmlockd=0 when lockd VGs exist which require lvmlockd, the + * lockd_gl() calls become no-ops, but the lockd_vg() calls for the lockd + * VGs will fail. The warnings/errors from accessing the lockd VGs + * should lead the user to set use_lvmlockd to 1 and run the necessary + * lock manager. In this condition, lvm reverts to the behavior of + * the following case, in which system ID largely protects shared + * devices, but has limitations. + * + * - Setting use_lvmlockd=0 with shared devices, no lockd VGs and + * no lock manager is a recognized mode of operation that is + * described in the lvmsystemid man page. Using lvm on shared + * devices this way is made safe by using system IDs to assign + * ownership of VGs to single hosts. The main limitation of this + * mode (among others outlined in the man page), is that orphan PVs + * are unprotected. + */ + +int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags) +{ + const char *mode = NULL; + const char *opts = NULL; + uint32_t lockd_flags; + int force_cache_update = 0; + int retries = 0; + int result; + + if (!_use_lvmlockd) + return 1; + + /* + * Verify that when --readonly is used, no ex locks should be used. + */ + if (cmd->metadata_read_only && def_mode && !strcmp(def_mode, "ex")) { + log_error("Exclusive locks are not allowed with readonly option."); + return 0; + } + + if (cmd->lockd_gl_disable) + return 1; + + if (def_mode && !strcmp(def_mode, "un")) { + mode = "un"; + goto req; + } + + if (!mode) + mode = def_mode; + if (!mode) { + log_error("Unknown lock-gl mode"); + return 0; + } + + req: + log_debug("lockd global mode %s", mode); + + if (!_lockd_request(cmd, "lock_gl", + NULL, NULL, NULL, NULL, NULL, NULL, mode, opts, + &result, &lockd_flags)) { + /* No result from lvmlockd, it is probably not running. */ + + /* We don't care if an unlock fails. */ + if (!strcmp(mode, "un")) + return 1; + + /* We can continue reading if a shared lock fails. */ + if (!strcmp(mode, "sh")) { + log_warn("Reading without shared global lock."); + force_cache_update = 1; + goto allow; + } + + log_error("Global lock failed: check that lvmlockd is running."); + return 0; + } + + if (result == -EAGAIN) { + if (retries < find_config_tree_int(cmd, global_lvmlockd_lock_retries_CFG, NULL)) { + log_warn("Retrying %s global lock", mode); + sleep(1); + retries++; + goto req; + } + } + + /* + * ENOLS: no lockspace was found with a global lock. + * The VG with the global lock may not be visible or started yet, + * this should be a temporary condition. + * + * ESTARTING: the lockspace with the gl is starting. + * The VG with the global lock is starting and should finish shortly. + * + * ELOCKIO: sanlock gets i/o errors when trying to read/write leases + * (This can progress to EVGKILLED.) + * + * EVGKILLED: the sanlock lockspace is being killed after losing + * access to lease storage. + */ + + if (result == -ENOLS || + result == -ESTARTING || + result == -EVGKILLED || + result == -ELOCKIO) { + + if (!strcmp(mode, "un")) + return 1; + + /* + * If an ex global lock fails, then the command fails. + */ + if (strcmp(mode, "sh")) { + if (result == -ESTARTING) + log_error("Global lock failed: lockspace is starting"); + else if (result == -ENOLS) + log_error("Global lock failed: check that global lockspace is started"); + else if (result == -ELOCKIO) + log_error("Global lock failed: storage errors for sanlock leases"); + else if (result == -EVGKILLED) + log_error("Global lock failed: storage failed for sanlock leases"); + else + log_error("Global lock failed: error %d", result); + return 0; + } + + /* + * If a sh global lock fails, then the command can continue + * reading without it, but force a global cache validation, + * and print a warning. + */ + + if (result == -ESTARTING) { + log_warn("Skipping global lock: lockspace is starting"); + force_cache_update = 1; + goto allow; + } + + if (result == -ELOCKIO || result == -EVGKILLED) { + log_warn("Skipping global lock: storage %s for sanlock leases", + result == -ELOCKIO ? "errors" : "failed"); + force_cache_update = 1; + goto allow; + } + + if ((lockd_flags & LD_RF_NO_GL_LS) || (lockd_flags & LD_RF_NO_LOCKSPACES)) { + log_warn("Skipping global lock: lockspace not found or started"); + force_cache_update = 1; + goto allow; + } + + /* + * This is for completeness. If we reach here, then + * a specific check for the error should be added above + * with a more helpful message. + */ + log_error("Global lock failed: error %d", result); + return 0; + } + + if ((lockd_flags & LD_RF_DUP_GL_LS) && strcmp(mode, "un")) + log_warn("Duplicate sanlock global locks should be corrected"); + + if (result < 0) { + if (result == -EAGAIN) { + /* + * Most of the time, retries should avoid this case. + */ + log_error("Global lock failed: held by other host."); + return 0; + } else { + /* + * We don't intend to reach this. We should check + * any known/possible error specifically and print + * a more helpful message. This is for completeness. + */ + log_error("Global lock failed: error %d.", result); + return 0; + } + } + + allow: + lvmetad_validate_global_cache(cmd, force_cache_update); + return 1; +} + +/* + * VG lock + * + * Return 1: continue, lockd_state may still indicate an error + * Return 0: failure, do not continue + * + * lvmlockd could also return the lock_type that it used for the VG, + * and we could encode that in lockd_state, and verify later that it + * matches vg->lock_type. + * + * The result of the VG lock operation needs to be saved in lockd_state + * because the result needs to be passed into vg_read so it can be + * assessed in combination with vg->lock_type. + * + * The VG lock protects the VG metadata on disk from concurrent access + * among hosts. The VG lock also ensures that the local lvmetad cache + * contains the latest version of the VG metadata from disk. (Since + * another host may have changed the VG since it was last read.) + * + * The VG lock must be acquired before the VG is read, i.e. before vg_read(). + * The result from lockd_vg() is saved in the "lockd_state" variable, and + * this result is passed into vg_read(). After vg_read() reads the VG, + * it checks if the VG lock_type (sanlock or dlm) requires a lock to be + * held, and if so, it verifies that the lock was correctly acquired by + * looking at lockd_state. If vg_read() sees that the VG is a local VG, + * i.e. lock_type is not sanlock or dlm, then no lock is required, and it + * ignores lockd_state (which would indicate no lock was found.) + * + * When acquiring the VG lock, lvmlockd checks if the local cached copy + * of the VG metadata in lvmetad is up to date. If not, it invalidates + * the VG cached in lvmetad. This would happen if another host changed + * the VG since it was last read. When lvm commands read the VG from + * lvmetad, they will check if the metadata is invalid, and if so they + * will reread it from disk, and update the copy in lvmetad. + */ + +int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode, + uint32_t flags, uint32_t *lockd_state) +{ + const char *mode = NULL; + uint32_t lockd_flags; + uint32_t prev_state = *lockd_state; + int retries = 0; + int result; + int ret; + + *lockd_state = 0; + + if (!is_real_vg(vg_name)) + return 1; + + /* + * Verify that when --readonly is used, no ex locks should be used. + */ + if (cmd->metadata_read_only && + ((def_mode && !strcmp(def_mode, "ex")) || + (!def_mode && !cmd->lockd_vg_default_sh))) { + log_error("Exclusive locks are not allowed with readonly option."); + return 0; + } + + /* + * Some special cases need to disable the vg lock. + */ + if (cmd->lockd_vg_disable) + return 1; + + /* + * An unlock is simply sent or skipped without any need + * for the mode checking for sh/ex. + * + * Look at lockd_state from the sh/ex lock, and if it failed, + * don't bother sending the unlock to lvmlockd. The main + * purpose of this is to avoid sending an unnecessary unlock + * for local VGs (the lockd_state from sh/ex on the local VG + * will be failed.) This implies that the lockd_state value + * should be preserved from the sh/ex lockd_vg() call and + * passed back to lockd_vg() for the corresponding unlock. + */ + if (def_mode && !strcmp(def_mode, "un")) { + if (prev_state & LDST_FAIL) { + log_debug("VG %s unlock skipped: lockd_state is failed", vg_name); + return 1; + } + + mode = "un"; + goto req; + } + + /* + * 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")) + *lockd_state |= LDST_EX; + + req: + /* + * This check is not at the top of the function so that + * we can first set LDST_EX which will be used later to + * decide whether a failure can be ignored or not. + * + * We do not know if this is a local VG or lockd VG yet, + * so we must return success, go ahead and read the VG, + * then check if the lock_type required lvmlockd or not. + */ + if (!_use_lvmlockd) { + *lockd_state |= LDST_FAIL_REQUEST; + return 1; + } + + log_debug("lockd VG %s mode %s", vg_name, mode); + + if (!_lockd_request(cmd, "lock_vg", + vg_name, NULL, NULL, NULL, NULL, NULL, mode, NULL, + &result, &lockd_flags)) { + /* + * No result from lvmlockd, it is probably not running. + * Decide if it is ok to continue without a lock in + * access_vg_lock_type() after the VG has been read and + * the lock_type can be checked. We don't care about + * this error for local VGs, but we do care for lockd VGs. + */ + *lockd_state |= LDST_FAIL_REQUEST; + return 1; + } + + if (result == -EAGAIN) { + if (retries < find_config_tree_int(cmd, global_lvmlockd_lock_retries_CFG, NULL)) { + log_warn("Retrying %s lock on VG %s", mode, vg_name); + sleep(1); + retries++; + goto req; + } + } + + switch (result) { + case 0: + /* success */ + break; + case -ENOLS: + *lockd_state |= LDST_FAIL_NOLS; + break; + case -ESTARTING: + *lockd_state |= LDST_FAIL_STARTING; + break; + default: + *lockd_state |= LDST_FAIL_OTHER; + } + + /* + * Normal success. + */ + if (!result) { + ret = 1; + goto out; + } + + /* + * The lockspace for the VG is starting (the VG must not + * be local), and is not yet ready to do locking. Allow + * reading without a sh lock during this period. + */ + if (result == -ESTARTING) { + if (!strcmp(mode, "un")) { + ret = 1; + goto out; + } else if (!strcmp(mode, "sh")) { + log_warn("VG %s lock skipped: lock start in progress", vg_name); + ret = 1; + goto out; + } else { + log_error("VG %s lock failed: lock start in progress", vg_name); + ret = 0; + goto out; + } + } + + /* + * sanlock is getting i/o errors while reading/writing leases, or the + * lockspace/VG is being killed after failing to renew its lease for + * too long. + */ + if (result == -EVGKILLED || result == -ELOCKIO) { + const char *problem = (result == -ELOCKIO ? "errors" : "failed"); + + if (!strcmp(mode, "un")) { + ret = 1; + goto out; + } else if (!strcmp(mode, "sh")) { + log_warn("VG %s lock skipped: storage %s for sanlock leases", vg_name, problem); + ret = 1; + goto out; + } else { + log_error("VG %s lock failed: storage %s for sanlock leases", vg_name, problem); + ret = 0; + goto out; + } + } + + /* + * An unused/previous lockspace for the VG was found. + * This means it must be a lockd VG, not local. The + * lockspace needs to be started to be used. + */ + if ((result == -ENOLS) && (lockd_flags & LD_RF_INACTIVE_LS)) { + if (!strcmp(mode, "un")) { + ret = 1; + goto out; + } else if (!strcmp(mode, "sh")) { + log_warn("VG %s lock skipped: lockspace is inactive", vg_name); + ret = 1; + goto out; + } else { + log_error("VG %s lock failed: lockspace is inactive", vg_name); + ret = 0; + goto out; + } + } + + /* + * An unused lockspace for the VG was found. The previous + * start of the lockspace failed, so we can print a more useful + * error message. + */ + if ((result == -ENOLS) && (lockd_flags & LD_RF_ADD_LS_ERROR)) { + if (!strcmp(mode, "un")) { + ret = 1; + goto out; + } else if (!strcmp(mode, "sh")) { + log_warn("VG %s lock skipped: lockspace start error", vg_name); + ret = 1; + goto out; + } else { + log_error("VG %s lock failed: lockspace start error", vg_name); + ret = 0; + goto out; + } + } + + /* + * No lockspace for the VG was found. It may be a local + * VG that lvmlockd doesn't keep track of, or it may be + * a lockd VG that lvmlockd doesn't yet know about (it hasn't + * been started yet.) Decide what to do after the VG is + * read and we can see the lock_type. + */ + if (result == -ENOLS) { + ret = 1; + goto out; + } + + /* + * Another error. We don't intend to reach here, but + * want to check for each specific error above so that + * a helpful message can be printed. + */ + if (result) { + if (!strcmp(mode, "un")) { + ret = 1; + goto out; + } else if (!strcmp(mode, "sh")) { + log_warn("VG %s lock skipped: error %d", vg_name, result); + ret = 1; + goto out; + } else { + log_error("VG %s lock failed: error %d", vg_name, result); + ret = 0; + goto out; + } + } + +out: + /* + * A notice from lvmlockd that duplicate gl locks have been found. + * It would be good for the user to disable one of them. + */ + if ((lockd_flags & LD_RF_DUP_GL_LS) && strcmp(mode, "un")) + log_warn("Duplicate sanlock global lock in VG %s", vg_name); + + return ret; +} + +/* + * This must be called before a new version of the VG metadata is + * written to disk. For local VGs, this is a no-op, but for lockd + * VGs, this notifies lvmlockd of the new VG seqno. lvmlockd must + * know the latest VG seqno so that it can save it within the lock's + * LVB. The VG seqno in the VG lock's LVB is used by other hosts to + * detect when their cached copy of the VG metadata is stale, i.e. + * the cached VG metadata has a lower seqno than the seqno seen in + * the VG lock. + */ + +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 (!_use_lvmlockd) + return 0; + 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; +} + +/* + * 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, struct id *lv_id, + const char *lock_args, const char *def_mode, uint32_t flags) +{ + char lv_uuid[64] __attribute__((aligned(8))); + const char *mode = NULL; + const char *opts = NULL; + uint32_t lockd_flags; + int refreshed = 0; + int result; + + /* + * Verify that when --readonly is used, no LVs should be activated or used. + */ + if (cmd->metadata_read_only) { + log_error("LV locks are not allowed with readonly option."); + return 0; + } + + if (cmd->lockd_lv_disable) + return 1; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + if (!id_write_format(lv_id, lv_uuid, sizeof(lv_uuid))) + return_0; + + /* + * 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. + */ + + 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"; + + retry: + log_debug("lockd LV %s/%s mode %s uuid %s", vg->name, lv_name, mode, lv_uuid); + + if (!_lockd_request(cmd, "lock_lv", + vg->name, vg->lock_type, vg->lock_args, + lv_name, lv_uuid, lock_args, mode, opts, + &result, &lockd_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 == -EMSGSIZE) { + /* Another host probably extended lvmlock. */ + if (!refreshed++) { + log_debug("Refresh lvmlock"); + _refresh_sanlock_lv(cmd, vg); + goto retry; + } + } + + if (result == -ENOLS) { + log_error("LV %s/%s lock failed: lockspace is inactive", vg->name, lv_name); + return 0; + } + + if (result == -EVGKILLED || result == -ELOCKIO) { + const char *problem = (result == -ELOCKIO ? "errors" : "failed"); + log_error("LV %s/%s lock failed: storage %s for sanlock leases", vg->name, lv_name, problem); + return 0; + } + + if (result < 0) { + log_error("LV %s/%s lock failed: error %d", vg->name, lv_name, result); + 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->lvid.id[1], + 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_args), 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. + * + * An LV lock is acquired before the LV is activated, and released + * after the LV is deactivated. If the LV lock cannot be acquired, + * it means that the LV is active on another host and the activation + * fails. Commands that modify an inactive LV also acquire the LV lock. + * + * In non-lockd VGs, this is a no-op. + * + * In lockd VGs, normal LVs each have their own lock, but other + * LVs do not have their own lock, e.g. the lock for a thin LV is + * acquired on the thin pool LV, and a thin LV does not have a lock + * of its own. A cache pool LV does not have a lock of its own. + * When the cache pool LV is linked to an origin LV, the lock of + * the orgin LV protects the combined origin + cache pool. + */ + +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 (!_use_lvmlockd) { + log_error("LV in VG %s with lock_type %s requires lvmlockd.", + lv->vg->name, lv->vg->lock_type); + return 0; + } + + if (!_lvmlockd_connected) + return 0; + + if (lv_is_thin_type(lv)) + return _lockd_lv_thin(cmd, lv, def_mode, flags); + + /* + * An LV with NULL lock_args does not have a lock of its own. + */ + if (!lv->lock_args) + 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->lvid.id[1], + lv->lock_args, def_mode, flags); +} + +static int _init_lv_sanlock(struct cmd_context *cmd, struct volume_group *vg, + const char *lv_name, struct id *lv_id, + const char **lock_args_ret) +{ + char lv_uuid[64] __attribute__((aligned(8))); + daemon_reply reply; + const char *reply_str; + const char *lv_lock_args = NULL; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + if (!id_write_format(lv_id, lv_uuid, sizeof(lv_uuid))) + return_0; + + reply = _lockd_send("init_lv", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "lv_name = %s", lv_name, + "lv_uuid = %s", lv_uuid, + "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 (result == -EMSGSIZE) { + /* + * No space on the lvmlock lv for a new lease, this should be + * detected by handle_sanlock_lv() called before. + */ + log_error("No sanlock space for lock for LV %s/%s", vg->name, lv_name); + goto out; + } + + if (!ret) { + log_error("_init_lv_sanlock lvmlockd result %d", result); + goto out; + } + + if (!(reply_str = daemon_reply_str(reply, "lv_lock_args", NULL))) { + log_error("lv_lock_args not returned"); + ret = 0; + goto out; + } + + if (!(lv_lock_args = dm_pool_strdup(cmd->mem, reply_str))) { + 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(struct cmd_context *cmd, struct volume_group *vg, + const char *lv_name, struct id *lv_id, const char *lock_args) +{ + char lv_uuid[64] __attribute__((aligned(8))); + daemon_reply reply; + int result; + int ret; + + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + if (!id_write_format(lv_id, lv_uuid, sizeof(lv_uuid))) + return_0; + + reply = _lockd_send("free_lv", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "lv_name = %s", lv_name, + "lv_uuid = %s", lv_uuid, + "vg_lock_type = %s", vg->lock_type, + "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 lvmlockd result %d", result); + + daemon_reply_destroy(reply); + + return ret; +} + +int lockd_init_lv_args(struct cmd_context *cmd, struct volume_group *vg, + struct logical_volume *lv, + const char *lock_type, const char **lock_args) +{ + /* sanlock is the only lock type that sets per-LV lock_args. */ + if (!strcmp(lock_type, "sanlock")) + return _init_lv_sanlock(cmd, vg, lv->name, &lv->lvid.id[1], lock_args); + return 1; +} + +/* + * lvcreate + * + * An LV created in a lockd VG inherits 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_args to NULL, which will cause + * the LV to not have lock_args set in its metadata. A lockd_lv() request on + * an LV with no lock_args 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 logical_volume *lv, + struct lvcreate_params *lp) +{ + int lock_type_num = get_lock_type_from_string(vg->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 (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + if (!lp->needs_lockd_init) { + /* needs_lock_init is set for LVs that need a lockd lock. */ + return 1; + + } else if (seg_is_cache(lp) || seg_is_cache_pool(lp)) { + log_error("Use lvconvert for cache with lock type %s", vg->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; + } + lv->lock_args = 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_args 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; + } + lv->lock_args = 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. + */ + log_error("Create thin pool and thin LV separately with lock type %s", + vg->lock_type); + return 0; + + } else if (!seg_is_thin_volume(lp) && lp->create_pool) { + /* Creating a thin pool only. */ + /* lv_name_lock = lp->pool_name; */ + + } else { + log_error("Unknown thin options for lock init."); + return 0; + } + + } else { + /* Creating a normal lv. */ + /* lv_name_lock = lv_name; */ + } + + /* + * The LV gets its own lock, so set lock_args to non-NULL. + * + * lockd_init_lv_args() will be called during vg_write() + * to complete the sanlock LV lock initialization, where + * actual space on disk is allocated. Waiting to do this + * last step until vg_write() avoids the need to revert + * the sanlock allocation if the lvcreate function isn't + * completed. + * + * This works, but would leave the sanlock lease allocated + * unless the lease was freed on each early exit path from + * lvcreate: + * + * return lockd_init_lv_args(cmd, vg, lv_name_lock, lv_id, + * vg->lock_type, &lv->lock_args); + */ + + if (!strcmp(vg->lock_type, "sanlock")) + lv->lock_args = "pending"; + else if (!strcmp(vg->lock_type, "dlm")) + lv->lock_args = "dlm"; + + return 1; +} + +/* lvremove */ + +int lockd_free_lv(struct cmd_context *cmd, struct volume_group *vg, + const char *lv_name, struct id *lv_id, const char *lock_args) +{ + switch (get_lock_type_from_string(vg->lock_type)) { + case LOCK_TYPE_NONE: + case LOCK_TYPE_CLVM: + return 1; + case LOCK_TYPE_DLM: + case LOCK_TYPE_SANLOCK: + if (!lock_args) + return 1; + return _free_lv(cmd, vg, lv_name, lv_id, lock_args); + default: + log_error("lockd_free_lv: unknown lock_type."); + return 0; + } +} + +int lockd_rename_vg_before(struct cmd_context *cmd, struct volume_group *vg) +{ + struct lv_list *lvl; + daemon_reply reply; + int result; + int ret; + + if (!is_lockd_type(vg->lock_type)) + return 1; + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + if (lvs_in_vg_activated(vg)) { + log_error("LVs must be inactive before vgrename."); + return 0; + } + + /* Check that no LVs are active on other hosts. */ + + dm_list_iterate_items(lvl, &vg->lvs) { + if (!lockd_lv(cmd, lvl->lv, "ex", 0)) { + log_error("LV %s/%s must be inactive on all hosts before vgrename.", + vg->name, lvl->lv->name); + return 0; + } + + if (!lockd_lv(cmd, lvl->lv, "un", 0)) { + log_error("Failed to unlock LV %s/%s.", vg->name, lvl->lv->name); + return 0; + } + } + + /* + * lvmlockd: + * checks for other hosts in lockspace + * leaves the lockspace + */ + + reply = _lockd_send("rename_vg_before", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "vg_lock_type = %s", vg->lock_type, + "vg_lock_args = %s", vg->lock_args, + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + } else { + ret = (result < 0) ? 0 : 1; + } + + daemon_reply_destroy(reply); + + if (!ret) { + log_error("lockd_rename_vg_before lvmlockd result %d", result); + return 0; + } + + if (!strcmp(vg->lock_type, "sanlock")) { + log_debug("lockd_rename_vg_before deactivate sanlock lv"); + _deactivate_sanlock_lv(cmd, vg); + } + + return 1; +} + +int lockd_rename_vg_final(struct cmd_context *cmd, struct volume_group *vg, int success) +{ + daemon_reply reply; + int result; + int ret; + + if (!is_lockd_type(vg->lock_type)) + return 1; + if (!_use_lvmlockd) + return 0; + if (!_lvmlockd_connected) + return 0; + + if (!success) { + /* + * Depending on the problem that caused the rename to + * fail, it may make sense to not restart the VG here. + */ + if (!lockd_start_vg(cmd, vg)) + log_error("Failed to restart VG %s lockspace.", vg->name); + return 1; + } + + if (!strcmp(vg->lock_type, "sanlock")) { + if (!_activate_sanlock_lv(cmd, vg)) + return 0; + + /* + * lvmlockd needs to rewrite the leases on disk + * with the new VG (lockspace) name. + */ + reply = _lockd_send("rename_vg_final", + "pid = %d", getpid(), + "vg_name = %s", vg->name, + "vg_lock_type = %s", vg->lock_type, + "vg_lock_args = %s", vg->lock_args, + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + ret = 0; + } else { + ret = (result < 0) ? 0 : 1; + } + + daemon_reply_destroy(reply); + + if (!ret) { + /* + * The VG has been renamed on disk, but renaming the + * sanlock leases failed. Cleaning this up can + * probably be done by converting the VG to lock_type + * none, then converting back to sanlock. + */ + log_error("lockd_rename_vg_final lvmlockd result %d", result); + return 0; + } + } + + if (!lockd_start_vg(cmd, vg)) + log_error("Failed to start VG %s lockspace.", vg->name); + + return 1; +} + +const char *lockd_running_lock_type(struct cmd_context *cmd) +{ + daemon_reply reply; + const char *lock_type = NULL; + int result; + + if (!_use_lvmlockd) + return NULL; + if (!_lvmlockd_connected) + return NULL; + + reply = _lockd_send("running_lm", + "pid = %d", getpid(), + NULL); + + if (!_lockd_result(reply, &result, NULL)) { + log_error("Failed to get result from lvmlockd"); + goto out; + } + + switch (result) { + case -EXFULL: + log_error("lvmlockd found multiple lock managers, use --lock-type to select one."); + break; + case -ENOLCK: + log_error("lvmlockd found no lock manager running."); + break; + case LOCK_TYPE_SANLOCK: + log_debug("lvmlockd found sanlock"); + lock_type = "sanlock"; + break; + case LOCK_TYPE_DLM: + log_debug("lvmlockd found dlm"); + lock_type = "dlm"; + break; + default: + log_error("Failed to find a running lock manager."); + break; + } +out: + daemon_reply_destroy(reply); + + return lock_type; +} + +/* Some LV types have no lock. */ + +int lockd_lv_uses_lock(struct logical_volume *lv) +{ + if (lv_is_thin_volume(lv)) + return 0; + + if (lv_is_thin_pool_data(lv)) + return 0; + + if (lv_is_thin_pool_metadata(lv)) + return 0; + + if (lv_is_pool_metadata_spare(lv)) + return 0; + + if (lv_is_cache_pool(lv)) + return 0; + + if (lv_is_cache_pool_data(lv)) + return 0; + + if (lv_is_cache_pool_metadata(lv)) + return 0; + + if (lv_is_cow(lv)) + return 0; + + if (lv->status & SNAPSHOT) + return 0; + + /* FIXME: lv_is_virtual_origin ? */ + + if (lv_is_lockd_sanlock_lv(lv)) + return 0; + + if (lv_is_mirror_image(lv)) + return 0; + + if (lv_is_mirror_log(lv)) + return 0; + + if (lv_is_raid_image(lv)) + return 0; + + if (lv_is_raid_metadata(lv)) + return 0; + + if (!lv_is_visible(lv)) + return 0; + + return 1; +} |