diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h index ffdd0ee93..fc5a66fe3 100644 --- a/lib/commands/toolcontext.h +++ b/lib/commands/toolcontext.h @@ -163,6 +163,10 @@ struct cmd_context { unsigned vg_read_print_access_error:1; /* print access errors from vg_read */ unsigned allow_mixed_block_sizes:1; unsigned force_access_clustered:1; + unsigned lockd_creating_thin_pool:1; + unsigned lockd_creating_thin_volume:1; + unsigned lockd_created_thin_pool:1; + unsigned lockd_created_thin_volume:1; unsigned lockd_gl_disable:1; unsigned lockd_vg_disable:1; unsigned lockd_lv_disable:1; diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c index 7e297d9cd..6ed864b39 100644 --- a/lib/locking/lvmlockd.c +++ b/lib/locking/lvmlockd.c @@ -145,33 +145,29 @@ static void _flags_str_to_lockd_flags(const char *flags_str, uint32_t *lockd_fla */ #define NO_LOCKD_RESULT (-1000) -static int _lockd_result(daemon_reply reply, int *result, uint32_t *lockd_flags) +static int _lockd_result(const char *req_name, daemon_reply reply, int *result, uint32_t *lockd_flags) { int reply_result; const char *flags_str = NULL; - const char *lock_type = NULL; *result = -1; if (reply.error) { - log_error("lockd_result reply error %d", reply.error); + log_error("lockd %s result: reply error %d", req_name, reply.error); return 0; } if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) { - log_error("lockd_result bad response"); + log_error("lockd %s result: bad response", req_name); 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"); + log_error("lockd %s result: no op_result", req_name); 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) { @@ -179,8 +175,7 @@ static int _lockd_result(daemon_reply reply, int *result, uint32_t *lockd_flags) _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); + log_debug("lockd %s result: %d", req_name, reply_result); return 1; } @@ -430,11 +425,13 @@ static int _lockd_request(struct cmd_context *cmd, "lv_lock_args = %s", lv_lock_args ?: "none", NULL); - if (!_lockd_result(reply, result, lockd_flags)) + if (!_lockd_result(req_name, reply, result, lockd_flags)) goto fail; - log_debug("lvmlockd %s %s vg %s lv %s result %d %x", + /* + log_debug("lockd %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_with_pvs(req_name, @@ -448,11 +445,13 @@ static int _lockd_request(struct cmd_context *cmd, "vg_lock_args = %s", vg_lock_args ?: "none", NULL); - if (!_lockd_result(reply, result, lockd_flags)) + if (!_lockd_result(req_name, reply, result, lockd_flags)) goto fail; - log_debug("lvmlockd %s %s vg %s result %d %x", + /* + log_debug("lockd %s %s vg %s result %d %x", req_name, mode, vg_name, *result, *lockd_flags); + */ } else { reply = _lockd_send_with_pvs(req_name, @@ -464,10 +463,10 @@ static int _lockd_request(struct cmd_context *cmd, "vg_lock_type = %s", vg_lock_type ?: "none", NULL); - if (!_lockd_result(reply, result, lockd_flags)) + if (!_lockd_result(req_name, reply, result, lockd_flags)) goto fail; - log_debug("lvmlockd %s %s result %d %x", + log_debug("lockd %s %s result %d %x", req_name, mode, *result, *lockd_flags); } @@ -651,7 +650,7 @@ static int _refresh_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) * full, then this extends it. */ -int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) +static int _handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) { struct logical_volume *lv = vg->sanlock_lv; daemon_reply reply; @@ -724,6 +723,8 @@ int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) return 0; } + log_debug("lockd find_free_lock %s", vg->name); + /* * Ask lvmlockd/sanlock to look for an unused lock. */ @@ -733,7 +734,7 @@ int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) "lv_size_bytes = " FMTd64, (int64_t) lv_size_bytes, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("find_free_lock", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -788,7 +789,7 @@ static int _init_vg(struct cmd_context *cmd, struct volume_group *vg, "vg_lock_type = %s", lock_type, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("init_vg", reply, &result, NULL)) { ret = 0; result = -ELOCKD; } else { @@ -985,7 +986,7 @@ static int _init_vg_sanlock(struct cmd_context *cmd, struct volume_group *vg, in "opts = %s", opts ?: "none", NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("init_vg", reply, &result, NULL)) { ret = 0; result = -ELOCKD; } else { @@ -1087,7 +1088,7 @@ static int _free_vg(struct cmd_context *cmd, struct volume_group *vg) "vg_lock_args = %s", vg->lock_args, NULL); - if (!_lockd_result(reply, &result, &lockd_flags)) { + if (!_lockd_result("free_vg", reply, &result, &lockd_flags)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -1141,7 +1142,7 @@ static int _busy_vg(struct cmd_context *cmd, struct volume_group *vg) "vg_lock_args = %s", vg->lock_args, NULL); - if (!_lockd_result(reply, &result, &lockd_flags)) { + if (!_lockd_result("busy_vg", reply, &result, &lockd_flags)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -1215,7 +1216,7 @@ static int _free_vg_sanlock(struct cmd_context *cmd, struct volume_group *vg) "vg_lock_args = %s", vg->lock_args, NULL); - if (!_lockd_result(reply, &result, &lockd_flags)) { + if (!_lockd_result("free_vg", reply, &result, &lockd_flags)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -1492,7 +1493,7 @@ int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg, int *exists NULL); } - if (!_lockd_result(reply, &result, &lockd_flags)) { + if (!_lockd_result("start_vg", reply, &result, &lockd_flags)) { ret = 0; result = -ELOCKD; } else { @@ -1561,7 +1562,7 @@ int lockd_stop_vg(struct cmd_context *cmd, struct volume_group *vg) "vg_name = %s", vg->name, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("stop_vg", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -1607,7 +1608,7 @@ int lockd_start_wait(struct cmd_context *cmd) "pid = " FMTd64, (int64_t) getpid(), NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("start_wait", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -1955,7 +1956,7 @@ int lockd_global(struct cmd_context *cmd, const char *def_mode) cmd->lockd_global_ex = 0; req: - log_debug("lockd global mode %s", mode); + log_debug("lockd global %s", mode); if (!_lockd_request(cmd, "lock_gl", NULL, NULL, NULL, NULL, NULL, NULL, mode, opts, @@ -1992,7 +1993,7 @@ int lockd_global(struct cmd_context *cmd, const char *def_mode) * missed which causes the command to request the gl when it's already * held, it's not a problem, so let it go. */ - log_debug("lockd global mode %s already held.", mode); + log_debug("lockd global %s already held.", mode); return 1; } @@ -2259,7 +2260,7 @@ int lockd_vg(struct cmd_context *cmd, const char *vg_name, const char *def_mode, return 1; } - log_debug("lockd VG %s mode %s", vg_name, mode); + log_debug("lockd VG %s %s", vg_name, mode); if (!_lockd_request(cmd, "lock_vg", vg_name, NULL, NULL, NULL, NULL, NULL, mode, opts, @@ -2488,6 +2489,7 @@ int lockd_vg_update(struct volume_group *vg) if (!strcmp(vg->lock_type, "sanlock")) return 1; #endif + log_debug("lockd_vg_update %s", vg->name); reply = _lockd_send("vg_update", "pid = " FMTd64, (int64_t) getpid(), @@ -2495,7 +2497,7 @@ int lockd_vg_update(struct volume_group *vg) "version = " FMTd64, (int64_t) vg->seqno, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("vg_update", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -2527,7 +2529,7 @@ static int _query_lv(struct cmd_context *cmd, struct volume_group *vg, "lv_lock_args = %s", lock_args ?: "none", NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("query_lock_lv", reply, &result, NULL)) { /* No result from lvmlockd, it is probably not running. */ log_error("Lock query failed for LV %s/%s", vg->name, lv_name); return 0; @@ -2672,7 +2674,7 @@ int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg, } retry: - log_debug("lockd LV %s/%s mode %s uuid %s %s", vg->name, lv_name, mode, lv_uuid, opts ?: ""); + log_debug("lockd_lv %s %s/%s %s %s", mode, vg->name, lv_name, lv_uuid, opts ?: ""); /* Pass PV list for IDM lock type */ if (!strcmp(vg->lock_type, "idm")) { @@ -2772,6 +2774,128 @@ int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg, return 1; } +int lockd_lvcreate_thin_setup(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp, + int creating_thin_pool, int creating_thin_volume) +{ + log_debug("lockd_lvcreate_thin_setup creating pool %d volume %d created pool %d volume %d", + creating_thin_pool, creating_thin_volume, + cmd->lockd_created_thin_pool, cmd->lockd_created_thin_volume); + + if ((creating_thin_pool && cmd->lockd_created_thin_pool) || + (creating_thin_volume && cmd->lockd_created_thin_volume) || + (creating_thin_pool && cmd->lockd_creating_thin_pool) || + (creating_thin_volume && cmd->lockd_creating_thin_volume)) { + /* shouldn't happen */ + log_error("lockd_lvcreate_thin_setup invalid lockd transition creating p %d v %d created p %d v %d", + creating_thin_pool, creating_thin_volume, cmd->lockd_created_thin_pool, cmd->lockd_created_thin_volume); + return 0; + } + + /* + * cmd->lockd_creating_thin_pool and LDLV_CREATING_THIN_POOL, or + * cmd->lockd_creating_thin_volume and LDLV_CREATING_THIN_VOLUME + * enable lockd_lv(). + */ + cmd->lockd_creating_thin_pool = creating_thin_pool; + cmd->lockd_creating_thin_volume = creating_thin_volume; + + /* + * If a thin pool was just created, then it's already locked. + * If a thin pool was not just created, then we need to lock + * the thin pool before creating a thin volume. + */ + if (creating_thin_volume && !cmd->lockd_created_thin_pool) { + struct logical_volume *pool_lv; + + log_debug("lockd_lvcreate_thin_setup creating_thin_volume locking thin pool %s", lp->pool_name); + + if (!(pool_lv = find_lv(vg, lp->pool_name))) { + log_error("Couldn't find thin pool %s for creating thin volume.", lp->pool_name); + return 0; + } + + if (!lockd_lv(cmd, pool_lv, "ex", LDLV_PERSISTENT | LDLV_CREATING_THIN_VOLUME)) { + log_error("Failed to lock thin pool for creating thin volume."); + return 0; + } + + /* Save pool info to use in lockd_lvcreate_done() */ + lp->lockd_name = dm_pool_strdup(cmd->mem, pool_lv->name); + } + + return 1; +} + +int lockd_lvcreate_prepare(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp) +{ + if (!vg_is_shared(vg)) + return 1; + + if (cmd->command_enum == lvcreate_thin_vol_with_thinpool_or_sparse_snapshot_CMD) { + log_error("Use lvconvert to create thin pools and cache pools in a shared VG."); + return 0; + } + + if (!strcmp(vg->lock_type, "sanlock")) { + if (segtype_is_thin_volume(lp->segtype) && !lp->create_pool) + log_debug("lockd_lvcreate_prepare find_free_lock skipped for thin volume"); + else { + /* Ensure there is space on disk for a new sanlock lease. */ + if (!_handle_sanlock_lv(cmd, vg)) { + log_error("No space for sanlock lock, extend the internal lvmlock LV."); + return 0; + } + } + } + + /* + * The primary LV requested by the user begins with the + * expectation that a lock is needed for the new LV. + * This will be cleared for some cases that do not need + * a lock. The lp struct for an internal LV that the + * command creates will not have this set, so those + * internal LVs will not by default have locks allocated. + */ + lp->needs_lockd_init = 1; + + return 1; +} + +void lockd_lvcreate_done(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp) +{ + struct logical_volume *pool_lv; + uint32_t flags = LDLV_PERSISTENT; + + if (!vg_is_shared(vg)) + return; + + if (!cmd->lockd_created_thin_volume && !cmd->lockd_created_thin_pool) + return; + + if (!lp->lockd_name) { + log_error("lockd_lvcreate_done missing name %s", lp->lockd_name ?: "-"); + return; + } + + if (!(pool_lv = find_lv(vg, lp->lockd_name))) { + log_error("lockd_lvcreate_done cannot find thin pool %s", lp->lockd_name); + return; + } + + if (thin_pool_is_active(pool_lv)) { + log_debug("lockd_lvcreate_done hold lock for active thin pool"); + return; + } + + if (cmd->lockd_creating_thin_pool) + flags |= LDLV_CREATING_THIN_POOL; + else if (cmd->lockd_creating_thin_volume) + flags |= LDLV_CREATING_THIN_VOLUME; + + if (!lockd_lv_name(cmd, vg, pool_lv->name, &pool_lv->lvid.id[1], pool_lv->lock_args, "un", flags)) + log_error("Failed to unlock thin pool %s", lp->lockd_name); +} + /* * Direct the lock request to the pool LV. * For a thin pool and all its thin volumes, one ex lock is used. @@ -2782,22 +2906,35 @@ 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 = NULL; + int pool_is_active = 0; + int locking = 0; + int unlocking = 0; + int result; + + if (def_mode && !strcmp(def_mode, "un")) + unlocking = 1; + else + locking = 1; if (lv_is_thin_volume(lv)) { struct lv_segment *pool_seg = first_seg(lv); pool_lv = pool_seg ? pool_seg->pool_lv : NULL; + log_debug("lockd_lv_thin thin_volume"); } else if (lv_is_thin_pool(lv)) { pool_lv = lv; + log_debug("lockd_lv_thin thin_pool"); } else if (lv_is_thin_pool_data(lv)) { /* FIXME: there should be a function to get pool lv from data lv. */ pool_lv = lv_parent(lv); + log_debug("lockd_lv_thin thin_data"); } else if (lv_is_thin_pool_metadata(lv)) { struct lv_segment *pool_seg = get_only_segment_using_this_lv(lv); if (pool_seg) pool_lv = pool_seg->lv; + log_debug("lockd_lv_thin thin_metadata"); } else { /* This should not happen AFAIK. */ @@ -2813,18 +2950,78 @@ static int _lockd_lv_thin(struct cmd_context *cmd, struct logical_volume *lv, return 0; } + if (cmd->lockd_creating_thin_pool && (flags & LDLV_CREATING_THIN_POOL)) { + /* do it */ + log_debug("lockd_lv_thin creating_thin_pool"); + } else if (cmd->lockd_creating_thin_volume && (flags & LDLV_CREATING_THIN_VOLUME)) { + /* do it */ + log_debug("lockd_lv_thin creating_thin_volume"); + } else if (cmd->lockd_creating_thin_pool && !(flags & LDLV_CREATING_THIN_POOL)) { + /* skip it, this lockd_lv is intentionally suppressed for lvcreate */ + log_debug("lockd_lv_thin creating_thin_pool skip without LDLV_CREATING_THIN_POOL"); + return 1; + } else if (cmd->lockd_creating_thin_volume && !(flags & LDLV_CREATING_THIN_VOLUME)) { + /* skip it, this lockd_lv is intentionally suppressed for lvcreate */ + log_debug("lockd_lv_thin creating_thin_volume skip without LDLV_CREATING_THIN_VOLUME"); + return 1; + } else if (flags & LDLV_CREATING_THIN_POOL) { + /* flags used in wrong context */ + log_error("lockd_lv_thin invalid use of LDLV_CREATING_THIN_POOL"); + return 0; + } else if (flags & LDLV_CREATING_THIN_VOLUME) { + /* flags used in wrong context */ + log_error("lockd_lv_thin invalid use of LDLV_CREATING_THIN_VOLUME"); + return 0; + } else { + /* non-lvcreate thin locking */ + if (!strcmp(cmd->name, "lvcreate")) { + log_error("lockd_lv_thin from lvcreate undefined case."); + return 0; + } + log_debug("lockd_lv_thin for %s", cmd->name); + } + + pool_is_active = thin_pool_is_active(pool_lv); + /* * 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") && thin_pool_is_active(pool_lv)) + if (unlocking && pool_is_active) { + log_debug("lockd_lv_thin skip unlock for active pool %s", pool_lv->name); return 1; + } + + /* + * Optimization for "lvchange -a n|y" of all LVs in the VG, which + * means this function is called for a thin pool and all thin volumes + * in it (and the meta/data sublvs of the pool due to the component + * activation special case in process_each_lv_in_vg.) + * + * Remember when a thin pool has been unlocked or unlocked by the + * command already, to avoid sending repeated unlock|lock requests + * to lvmlockd for the same thin pool. + */ + if (unlocking && !pool_is_active && pool_lv->lockd_thin_pool_unlocked) { + log_debug("lockd_lv_thin skip repeated unlock for inactive pool %s", pool_lv->name); + return 1; + } + if (locking && pool_is_active && pool_lv->lockd_thin_pool_locked) { + log_debug("lockd_lv_thin skip repeated lock for active pool %s", pool_lv->name); + 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); + result = lockd_lv_name(cmd, pool_lv->vg, pool_lv->name, &pool_lv->lvid.id[1], + pool_lv->lock_args, def_mode, flags); + + if (result && unlocking) + pool_lv->lockd_thin_pool_unlocked = 1; + if (result && locking) + pool_lv->lockd_thin_pool_locked = 1; + + return result; } static int _lockd_lv_vdo(struct cmd_context *cmd, struct logical_volume *lv, @@ -2899,6 +3096,8 @@ int lockd_lv(struct cmd_context *cmd, struct logical_volume *lv, if (!vg_is_shared(lv->vg)) return 1; + log_debug("lockd_lv %s %s", def_mode ?: "no_mode", display_lvname(lv)); + if (!_use_lvmlockd) { log_error("LV in VG %s with lock_type %s requires lvmlockd.", lv->vg->name, lv->vg->lock_type); @@ -3091,6 +3290,8 @@ static int _init_lv_sanlock(struct cmd_context *cmd, struct volume_group *vg, if (!id_write_format(lv_id, lv_uuid, sizeof(lv_uuid))) return_0; + log_debug("lockd init_lv %s %s", lv_name, lv_uuid); + reply = _lockd_send("init_lv", "pid = " FMTd64, (int64_t) getpid(), "vg_name = %s", vg->name, @@ -3101,7 +3302,7 @@ static int _init_lv_sanlock(struct cmd_context *cmd, struct volume_group *vg, "vg_lock_args = %s", vg->lock_args, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("init_lv", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -3167,7 +3368,7 @@ static int _free_lv(struct cmd_context *cmd, struct volume_group *vg, "lv_lock_args = %s", lock_args ?: "none", NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("free_lv", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -3234,7 +3435,7 @@ int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logic return 0; if (!lp->needs_lockd_init) { - /* needs_lock_init is set for LVs that need a lockd lock. */ + lv->lock_args = NULL; return 1; } else if (seg_is_cache_pool(lp)) { @@ -3274,49 +3475,20 @@ int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logic return 1; } else if (seg_is_thin(lp)) { - if ((seg_is_thin_volume(lp) && !lp->create_pool) || - (!seg_is_thin_volume(lp) && lp->origin_name)) { - struct logical_volume *thin_pool_lv; - - /* - * 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. - */ - log_debug("lockd_init_lv thin %s locking thin pool", display_lvname(lv)); - - if (!(thin_pool_lv = find_lv(vg, lp->pool_name))) { - log_error("Failed to find thin pool %s/%s", vg->name, lp->pool_name); - return 0; - } - if (!lockd_lv(cmd, thin_pool_lv, "ex", 0)) { - log_error("Failed to lock thin pool %s/%s", vg->name, lp->pool_name); - return 0; - } - lv->lock_args = NULL; + if (cmd->lockd_creating_thin_volume) { + /* do nothing */ 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. - * - * This should not happen because the command defs are - * checked and excluded for shared VGs early in lvcreate. - */ - 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."); + if (!cmd->lockd_creating_thin_pool) { + /* can this happen? */ + log_error("lockd_init_lv thin invalid without lockd_creating_thin_pool"); return 0; } + /* create a new lock for a new thin pool */ + log_debug("lockd_init_lv creating new lock for thin pool"); + } else if (seg_is_vdo(lp)) { struct logical_volume *vdo_pool_lv; @@ -3340,35 +3512,17 @@ int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logic } 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 + * 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"; - else if (!strcmp(vg->lock_type, "idm")) - lv->lock_args = "idm"; - - return 1; + return lockd_init_lv_args(cmd, vg, lv, vg->lock_type, NULL, &lv->lock_args); } /* lvremove */ @@ -3502,7 +3656,7 @@ int lockd_rename_vg_before(struct cmd_context *cmd, struct volume_group *vg) "vg_lock_args = %s", vg->lock_args, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("rename_vg_before", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -3567,7 +3721,7 @@ int lockd_rename_vg_final(struct cmd_context *cmd, struct volume_group *vg, int "vg_lock_args = %s", vg->lock_args, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("rename_vg_final", reply, &result, NULL)) { ret = 0; } else { ret = (result < 0) ? 0 : 1; @@ -3608,7 +3762,7 @@ const char *lockd_running_lock_type(struct cmd_context *cmd, int *found_multiple "pid = " FMTd64, (int64_t) getpid(), NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("running_lm", reply, &result, NULL)) { log_error("Failed to get result from lvmlockd"); goto out; } @@ -3729,7 +3883,7 @@ int lockd_lv_refresh(struct cmd_context *cmd, struct lvresize_params *lp) "path = %s", path, NULL); - if (!_lockd_result(reply, &result, NULL)) { + if (!_lockd_result("refresh_lv", reply, &result, NULL)) { /* No result from lvmlockd, it is probably not running. */ log_error("LV refresh failed for LV %s", path); return 0; diff --git a/lib/locking/lvmlockd.h b/lib/locking/lvmlockd.h index f6f059f28..95336f48a 100644 --- a/lib/locking/lvmlockd.h +++ b/lib/locking/lvmlockd.h @@ -21,6 +21,8 @@ #define LDLV_MODE_NO_SH 0x00000001 #define LDLV_PERSISTENT 0x00000002 #define LDLV_SH_EXISTS_OK 0x00000004 +#define LDLV_CREATING_THIN_VOLUME 0x00000008 +#define LDLV_CREATING_THIN_POOL 0x00000010 /* lvmlockd result flags */ #define LD_RF_NO_LOCKSPACES 0x00000001 @@ -108,8 +110,7 @@ int lockd_lv_resize(struct cmd_context *cmd, struct logical_volume *lv, /* lvcreate/lvremove use init/free */ -int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv, - struct lvcreate_params *lp); +int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv, struct lvcreate_params *lp); int lockd_init_lv_args(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv, const char *lock_type, const char *last_args, const char **lock_args); int lockd_free_lv(struct cmd_context *cmd, struct volume_group *vg, @@ -120,14 +121,17 @@ void lockd_free_removed_lvs(struct cmd_context *cmd, struct volume_group *vg, in const char *lockd_running_lock_type(struct cmd_context *cmd, int *found_multiple); -int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg); - int lockd_lv_uses_lock(struct logical_volume *lv); int lockd_lv_refresh(struct cmd_context *cmd, struct lvresize_params *lp); int lockd_query_lv(struct cmd_context *cmd, struct logical_volume *lv, int *ex, int *sh); +int lockd_lvcreate_prepare(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp); +void lockd_lvcreate_done(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp); +int lockd_lvcreate_thin_setup(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp, + int creating_thin_pool, int creating_thin_volume); + #else /* LVMLOCKD_SUPPORT */ static inline void lockd_lockopt_get_flags(const char *str, uint32_t *flags) @@ -248,14 +252,13 @@ static inline int lockd_lv_resize(struct cmd_context *cmd, struct logical_volume return 1; } -static inline int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, - struct logical_volume *lv, struct lvcreate_params *lp) +static inline int lockd_init_lv(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv, struct lvcreate_params *lp) { return 1; } static inline int lockd_init_lv_args(struct cmd_context *cmd, struct volume_group *vg, - struct logical_volume *lv, const char *lock_type, const char *last_args, const char **lock_args) + struct logical_volume *lv, const char *lock_type, const char *last_args, const char **lock_args) { return 1; } @@ -282,11 +285,6 @@ static inline const char *lockd_running_lock_type(struct cmd_context *cmd, int * return NULL; } -static inline int handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg) -{ - return 0; -} - static inline int lockd_lv_uses_lock(struct logical_volume *lv) { return 0; @@ -302,6 +300,21 @@ static inline int lockd_query_lv(struct cmd_context *cmd, struct logical_volume return 0; } +static inline int lockd_lvcreate_prepare(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp) +{ + return 1; +} + +static inline void lockd_lvcreate_done(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp) +{ +} + +static inline int lockd_lvcreate_thin_setup(struct cmd_context *cmd, struct volume_group *vg, struct lvcreate_params *lp, + int creating_thin_pool, int creating_thin_volume) +{ + return 1; +} + #endif /* LVMLOCKD_SUPPORT */ #endif /* _LVMLOCKD_H */ diff --git a/lib/metadata/lv.h b/lib/metadata/lv.h index 78bfcd3be..0596ed86b 100644 --- a/lib/metadata/lv.h +++ b/lib/metadata/lv.h @@ -65,6 +65,8 @@ struct logical_volume { uint64_t timestamp; unsigned new_lock_args:1; unsigned to_remove:1; /* set when LV is known to be removed */ + unsigned lockd_thin_pool_locked:1; /* set after locking thin pool in lvmlockd */ + unsigned lockd_thin_pool_unlocked:1; /* set after unlocking thin pool in lvmlockd */ const char *hostname; const char *lock_args; }; diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index 2218e1311..6b0a85219 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -7647,8 +7647,13 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, lockd_pool = first_seg(lv)->pool_lv; else if (lv_is_thin_pool(lv)) lockd_pool = lv; - if (!lockd_lv(cmd, lock_lv, "ex", LDLV_PERSISTENT)) - return_0; + if (!lockd_pool->lockd_thin_pool_locked) { + if (!lockd_lv(cmd, lock_lv, "ex", LDLV_PERSISTENT)) + return_0; + lockd_pool->lockd_thin_pool_locked = 1; + } else { + log_debug("lockd_thin_pool_locked skip repeat lockd_lv ex"); + } } else { if (!lockd_lv(cmd, lock_lv, "ex", LDLV_PERSISTENT)) return_0; @@ -7803,8 +7808,14 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv, } if (lockd_pool && !thin_pool_is_active(lockd_pool)) { - if (!lockd_lv_name(cmd, vg, lockd_pool->name, &lockd_pool->lvid.id[1], lockd_pool->lock_args, "un", LDLV_PERSISTENT)) - log_warn("WARNING: Failed to unlock %s.", display_lvname(lockd_pool)); + if (lockd_pool->lockd_thin_pool_locked && !lockd_pool->lockd_thin_pool_unlocked) { + if (!lockd_lv_name(cmd, vg, lockd_pool->name, &lockd_pool->lvid.id[1], lockd_pool->lock_args, "un", LDLV_PERSISTENT)) + log_warn("WARNING: Failed to unlock %s.", display_lvname(lockd_pool)); + else + lockd_pool->lockd_thin_pool_unlocked = 1; + } else { + log_debug("lockd_thin_pool_unlocked skip repeat lockd_lv un"); + } } else { if (!lockd_lv(cmd, lv, "un", LDLV_PERSISTENT)) log_warn("WARNING: Failed to unlock %s.", display_lvname(lv)); @@ -9158,6 +9169,9 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, int thin_pool_was_active = -1; /* not scanned, inactive, active */ int historical; uint64_t transaction_id; + uint32_t flags = 0; + int creating_thin_pool = 0; + int creating_thin_volume = 0; int ret; if (new_lv_name && lv_name_is_used_in_vg(vg, new_lv_name, &historical)) { @@ -9239,6 +9253,21 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, return NULL; } + /* + * TODO: do this for each type, and use these + * creating_foo variables in the code below in + * place of the seg_is_ calls. + */ + if ((creating_thin_pool = seg_is_thin_pool(lp))) + log_debug("Creating LV: thin pool"); + if ((creating_thin_volume = seg_is_thin_volume(lp))) + log_debug("Creating LV: thin volume"); + + if (vg_is_shared(vg) && (creating_thin_pool || creating_thin_volume)) { + if (!lockd_lvcreate_thin_setup(cmd, vg, lp, creating_thin_pool, creating_thin_volume)) + return NULL; + } + if (seg_is_pool(lp)) status |= LVM_WRITE; /* Pool is always writable */ else if (seg_is_cache(lp) || seg_is_thin_volume(lp) || seg_is_vdo(lp)) { @@ -9288,11 +9317,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, return_NULL; /* New pool is now inactive */ } else { - if (!lockd_lv(cmd, pool_lv, "ex", LDLV_PERSISTENT)) { - log_error("Failed to lock thin pool."); - return NULL; - } - if (!activate_lv(cmd, pool_lv)) { log_error("Aborting. Failed to locally activate thin pool %s.", display_lvname(pool_lv)); @@ -9457,14 +9481,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, lv->major, lv->minor); } - /* - * The specific LV may not use a lock. lockd_init_lv() sets - * lv->lock_args to NULL if this LV does not use its own lock. - */ - - if (!lockd_init_lv(vg->cmd, vg, lv, lp)) - return_NULL; - dm_list_splice(&lv->tags, &lp->tags); if (!lv_extend(lv, create_segtype, @@ -9574,16 +9590,47 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, if (lv_activation_skip(lv, lp->activate, lp->activation_skip & ACTIVATION_SKIP_IGNORE)) lp->activate = CHANGE_AN; - /* store vg on disk(s) */ - if (!vg_write(vg) || !vg_commit(vg)) - /* Pool created metadata LV, but better avoid recover when vg_write/commit fails */ + /* + * Allocate a lock for the LV, if it needs one. + * (With sanlock this is an on disk allocation.) + */ + if (!lockd_init_lv(cmd, vg, lv, lp)) return_NULL; + /* store vg on disk(s) */ + if (!vg_write(vg) || !vg_commit(vg)) { + /* Pool created metadata LV, but better avoid recover when vg_write/commit fails */ + + /* Reverse the lockd_init_lv. */ + if (lv->lock_args) + lockd_free_lv(cmd, vg, lv->name, &lv->lvid.id[1], lv->lock_args); + + return_NULL; + } + if (test_mode()) { log_verbose("Test mode: Skipping activation, zeroing and signature wiping."); goto out; } + /* + * The lock for the new thin pool was created during vg_write, + * so we can now acquire it. + */ + if (cmd->lockd_creating_thin_pool) { + log_debug("lockd_creating_thin_pool lockd_lv ex for new thin pool."); + if (!lockd_lv(cmd, lv, "ex", LDLV_PERSISTENT | LDLV_CREATING_THIN_POOL)) { + log_error("Failed to lock thin pool after creating it."); + goto out; + } + cmd->lockd_created_thin_pool = 1; + /* Save pool info to use in lockd_lvcreate_done() */ + if (!(lp->lockd_name = dm_pool_strdup(cmd->mem, lv->name))) + stack; + } + if (cmd->lockd_creating_thin_volume) + cmd->lockd_created_thin_volume = 1; + if (seg_is_raid(lp) && lp->raidintegrity) { log_debug("Adding integrity to new LV"); @@ -9674,10 +9721,13 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, /* Avoid multiple thin-pool activations in this case */ if (thin_pool_was_active < 0) thin_pool_was_active = 0; - if (!lockd_lv(cmd, pool_lv, "ex", LDLV_PERSISTENT)) { - log_error("Failed to lock thin pool."); - return NULL; + + if (vg_is_shared(vg) && !pool_lv->lockd_thin_pool_locked) { + /* sanity check, shouldn't happen */ + log_error(INTERNAL_ERROR "thin pool not locked"); + goto revert_new_lv; } + if (!activate_lv(cmd, pool_lv)) { log_error("Failed to activate thin pool %s.", display_lvname(pool_lv)); @@ -9707,10 +9757,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg, log_error("Failed to deactivate thin pool %s.", display_lvname(pool_lv)); return NULL; } - if (!lockd_lv(cmd, pool_lv, "un", LDLV_PERSISTENT)) { - log_error("Failed to unlock thin pool."); - return NULL; - } } } else if (lp->snapshot) { lv->status |= LV_TEMPORARY; @@ -9883,6 +9929,7 @@ out: return lv; deactivate_and_revert_new_lv: + log_debug("deactivating to revert new lv"); if (!sync_local_dev_names(lv->vg->cmd)) log_error("Failed to sync local devices before reverting %s.", display_lvname(lv)); @@ -9893,7 +9940,13 @@ deactivate_and_revert_new_lv: } revert_new_lv: - if (!lockd_lv(cmd, lv, "un", LDLV_PERSISTENT)) + log_debug("reverting new lv"); + flags = LDLV_PERSISTENT; + if (cmd->lockd_creating_thin_pool) + flags |= LDLV_CREATING_THIN_POOL; + else if (cmd->lockd_creating_thin_volume) + flags |= LDLV_CREATING_THIN_VOLUME; + if (!lockd_lv(cmd, lv, "un", flags)) log_warn("WARNING: Failed to unlock %s.", display_lvname(lv)); lockd_free_lv(vg->cmd, vg, lv->name, &lv->lvid.id[1], lv->lock_args); @@ -9921,8 +9974,6 @@ struct logical_volume *lv_create_single(struct volume_group *vg, if (!(lp->segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_THIN_POOL))) return_NULL; - /* We want a lockd lock for the new thin pool, but not the thin lv. */ - lp->needs_lockd_init = 1; /* When creating thin volume with new thin-pool avoid activating * new empty pool so it's not necessary to reactivate is as used thin-pool */ tmp = lp->activate; @@ -9930,6 +9981,8 @@ struct logical_volume *lv_create_single(struct volume_group *vg, if (!(lv = _lv_create_an_lv(vg, lp, lp->pool_name))) return_NULL; lp->activate = tmp; /* restore activation */ + + /* The thin pool had a lock created, but thin volumes do not. */ lp->needs_lockd_init = 0; } else if (seg_is_cache(lp)) { @@ -9960,13 +10013,11 @@ struct logical_volume *lv_create_single(struct volume_group *vg, if (!(lp->segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_VDO_POOL))) return_NULL; - /* We want a lockd lock for the new vdo pool, but not the vdo lv. */ - lp->needs_lockd_init = 1; - /* Use vpool names for vdo-pool */ if (!(lv = _lv_create_an_lv(vg, lp, lp->pool_name ? : "vpool%d"))) return_NULL; + /* The vdo pool had a lock created, but vdo volumes do not. */ lp->needs_lockd_init = 0; } else { log_error(INTERNAL_ERROR "Creation of pool for unsupported segment type %s.", diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index fbd801198..4dc011d50 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -1018,8 +1018,7 @@ struct lvcreate_params { const char *lv_name; /* all */ const char *origin_name; /* snap */ const char *pool_name; /* thin */ - - const char *lock_args; + const char *lockd_name; uint32_t stripes; /* striped/RAID */ uint32_t stripe_size; /* striped/RAID */ diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c index 511ebd8ae..b7232751a 100644 --- a/lib/metadata/metadata.c +++ b/lib/metadata/metadata.c @@ -2934,7 +2934,6 @@ int vg_write(struct volume_group *vg) struct dm_list *mdah; struct pv_list *pvl, *pvl_safe, *new_pvl; struct metadata_area *mda; - struct lv_list *lvl; struct device *mda_dev; int revert = 0, wrote = 0; @@ -2943,20 +2942,6 @@ int vg_write(struct volume_group *vg) log_debug("Writing metadata for VG %s.", vg->name); - if (vg_is_shared(vg)) { - const char *last_args = NULL; - dm_list_iterate_items(lvl, &vg->lvs) { - if (lvl->lv->lock_args && !strcmp(lvl->lv->lock_args, "pending")) { - if (!lockd_init_lv_args(vg->cmd, vg, lvl->lv, vg->lock_type, last_args, &lvl->lv->lock_args)) { - log_error("Cannot allocate lock for new LV."); - return 0; - } - last_args = lvl->lv->lock_args; - lvl->lv->new_lock_args = 1; - } - } - } - if (!_handle_historical_lvs(vg)) { log_error("Failed to handle historical LVs in VG %s.", vg->name); return 0; @@ -3197,14 +3182,6 @@ int vg_commit(struct volume_group *vg) void vg_revert(struct volume_group *vg) { struct metadata_area *mda; - struct lv_list *lvl; - - dm_list_iterate_items(lvl, &vg->lvs) { - if (lvl->lv->new_lock_args) { - lockd_free_lv(vg->cmd, vg, lvl->lv->name, &lvl->lv->lvid.id[1], lvl->lv->lock_args); - lvl->lv->new_lock_args = 0; - } - } _vg_wipe_cached_precommitted(vg); /* VG is no longer needed */ diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 110867c4a..f5d7d2ccc 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -1803,13 +1803,6 @@ static int _lvcreate_single(struct cmd_context *cmd, const char *vg_name, lp->pool_name ? : "with generated name", lp->vg_name, lp->segtype->name); } - if (vg->lock_type && !strcmp(vg->lock_type, "sanlock")) { - if (!handle_sanlock_lv(cmd, vg)) { - log_error("No space for sanlock lock, extend the internal lvmlock LV."); - goto out; - } - } - if (seg_is_thin_volume(lp)) log_verbose("Making thin LV %s in pool %s in VG %s%s%s using segtype %s.", lp->lv_name ? : "with generated name", @@ -1817,14 +1810,8 @@ static int _lvcreate_single(struct cmd_context *cmd, const char *vg_name, lp->snapshot ? " as snapshot of " : "", lp->snapshot ? lp->origin_name : "", lp->segtype->name); - if (vg_is_shared(vg)) { - if (cmd->command->command_enum == lvcreate_thin_vol_with_thinpool_or_sparse_snapshot_CMD) { - log_error("Use lvconvert to create thin pools and cache pools in a shared VG."); - goto out; - } - - lp->needs_lockd_init = 1; - } + if (!lockd_lvcreate_prepare(cmd, vg, lp)) + goto_out; if (!(lv = lv_create_single(vg, lp))) goto_out; @@ -1840,6 +1827,8 @@ out: log_error("Removal of created spare volume failed. " "Manual intervention required."); + lockd_lvcreate_done(cmd, vg, lp); + return ret; } diff --git a/tools/vgchange.c b/tools/vgchange.c index 39a8b736f..077659d1d 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -1273,17 +1273,27 @@ static int _vgchange_locktype(struct cmd_context *cmd, struct volume_group *vg, * then sets lock_type, sets lock_args, and clears system_id. * * Stage 2: - * We get here, and can now set LV lock_args. This uses - * the standard code path for allocating LV locks in - * vg_write() by setting LV lock_args to "pending", - * which tells vg_write() to call lockd_init_lv() - * and sets the lv->lock_args value before writing the VG. + * We get here, and can now set LV lock_args. + * + * lockd_init_lv() uses "lp" settings for the new LV to check if + * it should have a lock, and if so calls lockd_init_lv_args to + * do the allocation. + * Here there is no "lp", so here we use lockd_lv_uses_lock() to + * check if the LV should have a lock, if if so call lockd_init_lv_args + * to do the allocation. */ if (!strcmp(lock_type, "sanlock")) { + const char *last_args = NULL; + dm_list_iterate_items(lvl, &vg->lvs) { lv = lvl->lv; - if (lockd_lv_uses_lock(lv)) - lv->lock_args = "pending"; + + if (lockd_lv_uses_lock(lv) && + !lockd_init_lv_args(cmd, vg, lv, vg->lock_type, last_args, &lv->lock_args)) { + log_error("Failed to allocate LV lock on disk"); + return 0; + } + last_args = lv->lock_args; } vg->skip_validate_lock_args = 0;