diff --git a/WHATS_NEW b/WHATS_NEW index 54f05f068..a43433f03 100644 --- a/WHATS_NEW +++ b/WHATS_NEW @@ -1,5 +1,6 @@ Version 2.02.83 - =================================== + Allow exclusive activation of snapshots in a cluster. Add --addnodeonresume, --addnodeoncreate options for dmsetup create. Use cluster-wide message to request device name sync. Fix operation node stacking for consecutive dm ops. diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c index 1fa234837..81e8b884f 100644 --- a/daemons/clvmd/lvm-functions.c +++ b/daemons/clvmd/lvm-functions.c @@ -240,9 +240,17 @@ static int hold_lock(char *resource, int mode, int flags) lvi = lookup_info(resource); - if (lvi && lvi->lock_mode == mode) { - DEBUGLOG("hold_lock, lock mode %d already held\n", mode); - return 0; + if (lvi) { + if (lvi->lock_mode == mode) { + DEBUGLOG("hold_lock, lock mode %d already held\n", + mode); + return 0; + } + if ((lvi->lock_mode == LCK_EXCL) && (mode == LCK_WRITE)) { + DEBUGLOG("hold_lock, lock already held LCK_EXCL, " + "ignoring LCK_WRITE request"); + return 0; + } } /* Only allow explicit conversions */ diff --git a/lib/activate/activate.c b/lib/activate/activate.c index d8f31bab1..9edb0114f 100644 --- a/lib/activate/activate.c +++ b/lib/activate/activate.c @@ -708,39 +708,108 @@ int lvs_in_vg_opened(const struct volume_group *vg) } /* + * _lv_is_active + * @lv: logical volume being queried + * @locally: set if active locally (when provided) + * @exclusive: set if active exclusively (when provided) + * * Determine whether an LV is active locally or in a cluster. - * Assumes vg lock held. - * Returns: - * 0 - not active locally or on any node in cluster - * 1 - active either locally or some node in the cluster + * In addition to the return code which indicates whether or + * not the LV is active somewhere, two other values are set + * to yield more information about the status of the activation: + * return locally exclusively status + * ====== ======= =========== ====== + * 0 0 0 not active + * 1 0 0 active remotely + * 1 0 1 exclusive remotely + * 1 1 0 active locally and possibly remotely + * 1 1 1 exclusive locally (or local && !cluster) + * The VG lock must be held to call this function. + * + * Returns: 0 or 1 */ -int lv_is_active(struct logical_volume *lv) +static int _lv_is_active(struct logical_volume *lv, + int *locally, int *exclusive) { - int ret; + int r, l, e; /* remote, local, and exclusive */ + + r = l = e = 0; if (_lv_active(lv->vg->cmd, lv)) - return 1; + l = 1; - if (!vg_is_clustered(lv->vg)) - return 0; + if (!vg_is_clustered(lv->vg)) { + e = 1; /* exclusive by definition */ + goto out; + } - if ((ret = remote_lock_held(lv->lvid.s)) >= 0) - return ret; + /* Active locally, and the caller doesn't care about exclusive */ + if (l && !exclusive) + goto out; + + if ((r = remote_lock_held(lv->lvid.s, &e)) >= 0) + goto out; /* - * Old compatibility code if locking doesn't support lock query - * FIXME: check status to not deactivate already activate device + * If lock query is not supported (due to interfacing with old + * code), then we cannot evaluate exclusivity properly. + * + * Old users of this function will never be affected by this, + * since they are only concerned about active vs. not active. + * New users of this function who specifically ask for 'exclusive' + * will be given an error message. */ + if (l) { + if (exclusive) + log_error("Unable to determine exclusivity of %s", + lv->name); + goto out; + } + if (activate_lv_excl(lv->vg->cmd, lv)) { if (!deactivate_lv(lv->vg->cmd, lv)) stack; return 0; } - /* - * Exclusive local activation failed so assume it is active elsewhere. - */ - return 1; +out: + if (locally) + *locally = l; + if (exclusive) + *exclusive = e; + + log_very_verbose("%s/%s is %sactive%s%s", + lv->vg->name, lv->name, + (r || l) ? "" : "not ", + (exclusive && e) ? " exclusive" : "", + e ? (l ? " locally" : " remotely") : ""); + + return r || l; +} + +int lv_is_active(struct logical_volume *lv) +{ + return _lv_is_active(lv, NULL, NULL); +} + +/* +int lv_is_active_locally(struct logical_volume *lv) +{ + int l; + return _lv_is_active(lv, &l, NULL) && l; +} +*/ + +int lv_is_active_exclusive_locally(struct logical_volume *lv) +{ + int l, e; + return _lv_is_active(lv, &l, &e) && l && e; +} + +int lv_is_active_exclusive_remotely(struct logical_volume *lv) +{ + int l, e; + return _lv_is_active(lv, &l, &e) && !l && e; } #ifdef DMEVENTD diff --git a/lib/activate/activate.h b/lib/activate/activate.h index 2bc73dd81..c054c6db7 100644 --- a/lib/activate/activate.h +++ b/lib/activate/activate.h @@ -94,6 +94,8 @@ int lvs_in_vg_activated(struct volume_group *vg); int lvs_in_vg_opened(const struct volume_group *vg); int lv_is_active(struct logical_volume *lv); +int lv_is_active_exclusive_locally(struct logical_volume *lv); +int lv_is_active_exclusive_remotely(struct logical_volume *lv); int lv_has_target_type(struct dm_pool *mem, struct logical_volume *lv, const char *layer, const char *target_type); diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c index 1fbefc4ed..b48540481 100644 --- a/lib/activate/dev_manager.c +++ b/lib/activate/dev_manager.c @@ -1390,10 +1390,6 @@ static int _add_segment_to_dtree(struct dev_manager *dm, /* If this is a snapshot origin, add real LV */ /* If this is a snapshot origin + merging snapshot, add cow + real LV */ } else if (lv_is_origin(seg->lv) && !layer) { - if (vg_is_clustered(seg->lv->vg)) { - log_error("Clustered snapshots are not yet supported"); - return 0; - } if (lv_is_merging_origin(seg->lv)) { if (!_add_new_lv_to_dtree(dm, dtree, find_merging_cow(seg->lv)->cow, "cow")) diff --git a/lib/locking/locking.c b/lib/locking/locking.c index 645f25d57..520b25eeb 100644 --- a/lib/locking/locking.c +++ b/lib/locking/locking.c @@ -545,7 +545,7 @@ int locking_is_clustered(void) return (_locking.flags & LCK_CLUSTERED) ? 1 : 0; } -int remote_lock_held(const char *vol) +int remote_lock_held(const char *vol, int *exclusive) { int mode = LCK_NULL; @@ -563,5 +563,8 @@ int remote_lock_held(const char *vol) return 1; } + if (exclusive) + *exclusive = (mode == LCK_EXCL); + return mode == LCK_NULL ? 0 : 1; } diff --git a/lib/locking/locking.h b/lib/locking/locking.h index 51a7198c1..f30a76ce7 100644 --- a/lib/locking/locking.h +++ b/lib/locking/locking.h @@ -25,7 +25,7 @@ void reset_locking(void); int vg_write_lock_held(void); int locking_is_clustered(void); -int remote_lock_held(const char *vol); +int remote_lock_held(const char *vol, int *exclusive); /* * LCK_VG: diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c index f6bb68c27..eb2a3e4a3 100644 --- a/lib/metadata/lv_manip.c +++ b/lib/metadata/lv_manip.c @@ -3164,11 +3164,6 @@ int lv_create_single(struct volume_group *vg, "device-mapper kernel driver"); return 0; } - /* FIXME Allow exclusive activation. */ - if (vg_is_clustered(vg)) { - log_error("Clustered snapshots are not yet supported."); - return 0; - } /* Must zero cow */ status |= LVM_WRITE; @@ -3217,6 +3212,13 @@ int lv_create_single(struct volume_group *vg, return 0; } origin_active = info.exists; + + if (vg_is_clustered(vg) && + !lv_is_active_exclusive_locally(org)) { + log_error("%s must be active exclusively to" + " create snapshot", org->name); + return 0; + } } } diff --git a/tools/vgchange.c b/tools/vgchange.c index f2d399c4e..4266d6333 100644 --- a/tools/vgchange.c +++ b/tools/vgchange.c @@ -115,6 +115,16 @@ static int _activate_lvs_in_vg(struct cmd_context *cmd, ((lv->status & PVMOVE) )) continue; + /* + * If the LV is active exclusive remotely, + * then ignore it here + */ + if (lv_is_active_exclusive_remotely(lv)) { + log_verbose("%s/%s is exclusively active on" + " a remote node", vg->name, lv->name); + continue; + } + expected_count++; if (activate == CHANGE_AN) {