mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
6bc3d72a65
This applies the same rule/logic to dlm VGs that has always existed for sanlock VGs. Allowing a dlm VG to be removed while its lockspace was still running on other hosts largely worked, but there were difficult problems if another VG with the same name was recreated. Forcing the VG lockspace to be stopped, gives both sanlock and dlm VGs the same behavior.
768 lines
18 KiB
C
768 lines
18 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
#define _XOPEN_SOURCE 500 /* pthread */
|
|
#define _ISOC99_SOURCE
|
|
|
|
#include "tool.h"
|
|
|
|
#include "daemon-server.h"
|
|
#include "daemon-log.h"
|
|
#include "xlate.h"
|
|
|
|
#include "lvmlockd-internal.h"
|
|
#include "lvmlockd-client.h"
|
|
|
|
/*
|
|
* Using synchronous _wait dlm apis so do not define _REENTRANT and
|
|
* link with non-threaded version of library, libdlm_lt.
|
|
*/
|
|
#include "libdlm.h"
|
|
|
|
#include <pthread.h>
|
|
#include <stddef.h>
|
|
#include <poll.h>
|
|
#include <errno.h>
|
|
#include <endian.h>
|
|
#include <fcntl.h>
|
|
#include <byteswap.h>
|
|
#include <syslog.h>
|
|
#include <dirent.h>
|
|
#include <sys/socket.h>
|
|
|
|
struct lm_dlm {
|
|
dlm_lshandle_t *dh;
|
|
};
|
|
|
|
struct rd_dlm {
|
|
struct dlm_lksb lksb;
|
|
struct val_blk *vb;
|
|
};
|
|
|
|
int lm_data_size_dlm(void)
|
|
{
|
|
return sizeof(struct rd_dlm);
|
|
}
|
|
|
|
/*
|
|
* lock_args format
|
|
*
|
|
* vg_lock_args format for dlm is
|
|
* vg_version_string:undefined:cluster_name
|
|
*
|
|
* lv_lock_args are not used for dlm
|
|
*
|
|
* version_string is MAJOR.MINOR.PATCH
|
|
* undefined may contain ":"
|
|
*/
|
|
|
|
#define VG_LOCK_ARGS_MAJOR 1
|
|
#define VG_LOCK_ARGS_MINOR 0
|
|
#define VG_LOCK_ARGS_PATCH 0
|
|
|
|
static int dlm_has_lvb_bug;
|
|
|
|
static int cluster_name_from_args(char *vg_args, char *clustername)
|
|
{
|
|
return last_string_from_args(vg_args, clustername);
|
|
}
|
|
|
|
static int check_args_version(char *vg_args)
|
|
{
|
|
unsigned int major = 0;
|
|
int rv;
|
|
|
|
rv = version_from_args(vg_args, &major, NULL, NULL);
|
|
if (rv < 0) {
|
|
log_error("check_args_version %s error %d", vg_args, rv);
|
|
return rv;
|
|
}
|
|
|
|
if (major > VG_LOCK_ARGS_MAJOR) {
|
|
log_error("check_args_version %s major %d %d", vg_args, major, VG_LOCK_ARGS_MAJOR);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This will be set after dlm_controld is started. */
|
|
#define DLM_CLUSTER_NAME_PATH "/sys/kernel/config/dlm/cluster/cluster_name"
|
|
|
|
static int read_cluster_name(char *clustername)
|
|
{
|
|
static const char close_error_msg[] = "read_cluster_name: close_error %d";
|
|
char *n;
|
|
int fd;
|
|
int rv;
|
|
|
|
if (daemon_test) {
|
|
sprintf(clustername, "%s", "test");
|
|
return 0;
|
|
}
|
|
|
|
fd = open(DLM_CLUSTER_NAME_PATH, O_RDONLY);
|
|
if (fd < 0) {
|
|
log_debug("read_cluster_name: open error %d, check dlm_controld", fd);
|
|
return fd;
|
|
}
|
|
|
|
rv = read(fd, clustername, MAX_ARGS);
|
|
if (rv < 0) {
|
|
log_error("read_cluster_name: cluster name read error %d, check dlm_controld", fd);
|
|
if (close(fd))
|
|
log_error(close_error_msg, fd);
|
|
return rv;
|
|
}
|
|
|
|
n = strstr(clustername, "\n");
|
|
if (n)
|
|
*n = '\0';
|
|
if (close(fd))
|
|
log_error(close_error_msg, fd);
|
|
return 0;
|
|
}
|
|
|
|
int lm_init_vg_dlm(char *ls_name, char *vg_name, uint32_t flags, char *vg_args)
|
|
{
|
|
char clustername[MAX_ARGS+1];
|
|
char lock_args_version[MAX_ARGS+1];
|
|
int rv;
|
|
|
|
memset(clustername, 0, sizeof(clustername));
|
|
memset(lock_args_version, 0, sizeof(lock_args_version));
|
|
|
|
snprintf(lock_args_version, MAX_ARGS, "%u.%u.%u",
|
|
VG_LOCK_ARGS_MAJOR, VG_LOCK_ARGS_MINOR, VG_LOCK_ARGS_PATCH);
|
|
|
|
rv = read_cluster_name(clustername);
|
|
if (rv < 0)
|
|
return -EMANAGER;
|
|
|
|
if (strlen(clustername) + strlen(lock_args_version) + 2 > MAX_ARGS) {
|
|
log_error("init_vg_dlm args too long");
|
|
return -EARGS;
|
|
}
|
|
|
|
snprintf(vg_args, MAX_ARGS, "%s:%s", lock_args_version, clustername);
|
|
rv = 0;
|
|
|
|
log_debug("init_vg_dlm done %s vg_args %s", ls_name, vg_args);
|
|
return rv;
|
|
}
|
|
|
|
int lm_prepare_lockspace_dlm(struct lockspace *ls)
|
|
{
|
|
char sys_clustername[MAX_ARGS+1];
|
|
char arg_clustername[MAX_ARGS+1];
|
|
uint32_t major = 0, minor = 0, patch = 0;
|
|
struct lm_dlm *lmd;
|
|
int rv;
|
|
|
|
memset(sys_clustername, 0, sizeof(sys_clustername));
|
|
memset(arg_clustername, 0, sizeof(arg_clustername));
|
|
|
|
rv = read_cluster_name(sys_clustername);
|
|
if (rv < 0)
|
|
return -EMANAGER;
|
|
|
|
rv = dlm_kernel_version(&major, &minor, &patch);
|
|
if (rv < 0) {
|
|
log_error("prepare_lockspace_dlm kernel_version not detected %d", rv);
|
|
dlm_has_lvb_bug = 1;
|
|
}
|
|
|
|
if ((major == 6) && (minor == 0) && (patch == 1)) {
|
|
log_debug("dlm kernel version %u.%u.%u has lvb bug", major, minor, patch);
|
|
dlm_has_lvb_bug = 1;
|
|
}
|
|
|
|
if (!ls->vg_args[0]) {
|
|
/* global lockspace has no vg args */
|
|
goto skip_args;
|
|
}
|
|
|
|
rv = check_args_version(ls->vg_args);
|
|
if (rv < 0)
|
|
return -EARGS;
|
|
|
|
rv = cluster_name_from_args(ls->vg_args, arg_clustername);
|
|
if (rv < 0) {
|
|
log_error("prepare_lockspace_dlm %s no cluster name from args %s", ls->name, ls->vg_args);
|
|
return -EARGS;
|
|
}
|
|
|
|
if (strcmp(sys_clustername, arg_clustername)) {
|
|
log_error("prepare_lockspace_dlm %s mismatching cluster names sys %s arg %s",
|
|
ls->name, sys_clustername, arg_clustername);
|
|
return -EARGS;
|
|
}
|
|
|
|
skip_args:
|
|
lmd = malloc(sizeof(struct lm_dlm));
|
|
if (!lmd)
|
|
return -ENOMEM;
|
|
|
|
ls->lm_data = lmd;
|
|
return 0;
|
|
}
|
|
|
|
int lm_add_lockspace_dlm(struct lockspace *ls, int adopt)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
|
|
if (daemon_test)
|
|
return 0;
|
|
|
|
if (adopt)
|
|
lmd->dh = dlm_open_lockspace(ls->name);
|
|
else
|
|
lmd->dh = dlm_new_lockspace(ls->name, 0600, DLM_LSFL_NEWEXCL);
|
|
|
|
if (!lmd->dh) {
|
|
log_error("add_lockspace_dlm %s adopt %d error", ls->name, adopt);
|
|
free(lmd);
|
|
ls->lm_data = NULL;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int lm_rem_lockspace_dlm(struct lockspace *ls, int free_vg)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
int rv;
|
|
|
|
if (daemon_test)
|
|
goto out;
|
|
|
|
/*
|
|
* If free_vg is set, it means we are doing vgremove, and we may want
|
|
* to tell any other nodes to leave the lockspace. This is not really
|
|
* necessary since there should be no harm in having an unused
|
|
* lockspace sitting around. A new "notification lock" would need to
|
|
* be added with a callback to signal this.
|
|
*/
|
|
|
|
rv = dlm_release_lockspace(ls->name, lmd->dh, 1);
|
|
if (rv < 0) {
|
|
log_error("rem_lockspace_dlm error %d", rv);
|
|
return rv;
|
|
}
|
|
out:
|
|
free(lmd);
|
|
ls->lm_data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int lm_add_resource_dlm(struct lockspace *ls, struct resource *r, int with_lock_nl)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
|
uint32_t flags = 0;
|
|
char *buf;
|
|
int rv;
|
|
|
|
if (r->type == LD_RT_GL || r->type == LD_RT_VG) {
|
|
buf = malloc(sizeof(struct val_blk) + DLM_LVB_LEN);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
memset(buf, 0, sizeof(struct val_blk) + DLM_LVB_LEN);
|
|
|
|
rdd->vb = (struct val_blk *)buf;
|
|
rdd->lksb.sb_lvbptr = buf + sizeof(struct val_blk);
|
|
|
|
flags |= LKF_VALBLK;
|
|
}
|
|
|
|
if (!with_lock_nl)
|
|
goto out;
|
|
|
|
/* because this is a new NL lock request */
|
|
flags |= LKF_EXPEDITE;
|
|
|
|
if (daemon_test)
|
|
goto out;
|
|
|
|
rv = dlm_ls_lock_wait(lmd->dh, LKM_NLMODE, &rdd->lksb, flags,
|
|
r->name, strlen(r->name),
|
|
0, NULL, NULL, NULL);
|
|
if (rv < 0) {
|
|
log_error("S %s R %s add_resource_dlm lock error %d", ls->name, r->name, rv);
|
|
return rv;
|
|
}
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
int lm_rem_resource_dlm(struct lockspace *ls, struct resource *r)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
|
struct dlm_lksb *lksb;
|
|
int rv = 0;
|
|
|
|
if (daemon_test)
|
|
goto out;
|
|
|
|
lksb = &rdd->lksb;
|
|
|
|
if (!lksb->sb_lkid)
|
|
goto out;
|
|
|
|
rv = dlm_ls_unlock_wait(lmd->dh, lksb->sb_lkid, 0, lksb);
|
|
if (rv < 0) {
|
|
log_error("S %s R %s rem_resource_dlm unlock error %d", ls->name, r->name, rv);
|
|
}
|
|
out:
|
|
if (rdd->vb)
|
|
free(rdd->vb);
|
|
|
|
memset(rdd, 0, sizeof(struct rd_dlm));
|
|
r->lm_init = 0;
|
|
return rv;
|
|
}
|
|
|
|
static int to_dlm_mode(int ld_mode)
|
|
{
|
|
switch (ld_mode) {
|
|
case LD_LK_EX:
|
|
return LKM_EXMODE;
|
|
case LD_LK_SH:
|
|
return LKM_PRMODE;
|
|
};
|
|
return -1;
|
|
}
|
|
|
|
static int lm_adopt_dlm(struct lockspace *ls, struct resource *r, int ld_mode,
|
|
struct val_blk *vb_out)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
|
struct dlm_lksb *lksb;
|
|
uint32_t flags = 0;
|
|
int mode;
|
|
int rv;
|
|
|
|
memset(vb_out, 0, sizeof(struct val_blk));
|
|
|
|
if (!r->lm_init) {
|
|
rv = lm_add_resource_dlm(ls, r, 0);
|
|
if (rv < 0)
|
|
return rv;
|
|
r->lm_init = 1;
|
|
}
|
|
|
|
lksb = &rdd->lksb;
|
|
|
|
flags |= LKF_PERSISTENT;
|
|
flags |= LKF_ORPHAN;
|
|
|
|
if (rdd->vb)
|
|
flags |= LKF_VALBLK;
|
|
|
|
mode = to_dlm_mode(ld_mode);
|
|
if (mode < 0) {
|
|
log_error("adopt_dlm invalid mode %d", ld_mode);
|
|
rv = -EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
log_debug("S %s R %s adopt_dlm", ls->name, r->name);
|
|
|
|
if (daemon_test)
|
|
return 0;
|
|
|
|
/*
|
|
* dlm returns 0 for success, -EAGAIN if an orphan is
|
|
* found with another mode, and -ENOENT if no orphan.
|
|
*
|
|
* cast/bast/param are (void *)1 because the kernel
|
|
* returns errors if some are null.
|
|
*/
|
|
|
|
rv = dlm_ls_lockx(lmd->dh, mode, lksb, flags,
|
|
r->name, strlen(r->name), 0,
|
|
(void *)1, (void *)1, (void *)1,
|
|
NULL, NULL);
|
|
|
|
if (rv == -1 && errno == -EAGAIN) {
|
|
log_debug("S %s R %s adopt_dlm adopt mode %d try other mode",
|
|
ls->name, r->name, ld_mode);
|
|
rv = -EUCLEAN;
|
|
goto fail;
|
|
}
|
|
if (rv < 0) {
|
|
log_debug("S %s R %s adopt_dlm mode %d flags %x error %d errno %d",
|
|
ls->name, r->name, mode, flags, rv, errno);
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* FIXME: For GL/VG locks we probably want to read the lvb,
|
|
* especially if adopting an ex lock, because when we
|
|
* release this adopted ex lock we may want to write new
|
|
* lvb values based on the current lvb values (at lease
|
|
* in the GL case where we increment the current values.)
|
|
*
|
|
* It should be possible to read the lvb by requesting
|
|
* this lock in the same mode it's already in.
|
|
*/
|
|
|
|
return rv;
|
|
|
|
fail:
|
|
lm_rem_resource_dlm(ls, r);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Use PERSISTENT so that if lvmlockd exits while holding locks,
|
|
* the locks will remain orphaned in the dlm, still protecting what
|
|
* they were acquired to protect.
|
|
*/
|
|
|
|
int lm_lock_dlm(struct lockspace *ls, struct resource *r, int ld_mode,
|
|
struct val_blk *vb_out, int adopt)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
|
struct dlm_lksb *lksb;
|
|
struct val_blk vb;
|
|
uint32_t flags = 0;
|
|
int mode;
|
|
int rv;
|
|
|
|
if (adopt) {
|
|
/* When adopting, we don't follow the normal method
|
|
of acquiring a NL lock then converting it to the
|
|
desired mode. */
|
|
return lm_adopt_dlm(ls, r, ld_mode, vb_out);
|
|
}
|
|
|
|
if (!r->lm_init) {
|
|
rv = lm_add_resource_dlm(ls, r, 1);
|
|
if (rv < 0)
|
|
return rv;
|
|
r->lm_init = 1;
|
|
}
|
|
|
|
lksb = &rdd->lksb;
|
|
|
|
flags |= LKF_CONVERT;
|
|
flags |= LKF_NOQUEUE;
|
|
flags |= LKF_PERSISTENT;
|
|
|
|
if (rdd->vb)
|
|
flags |= LKF_VALBLK;
|
|
|
|
mode = to_dlm_mode(ld_mode);
|
|
if (mode < 0) {
|
|
log_error("lock_dlm invalid mode %d", ld_mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
log_debug("S %s R %s lock_dlm", ls->name, r->name);
|
|
|
|
if (daemon_test) {
|
|
memset(vb_out, 0, sizeof(struct val_blk));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The dlm lvb bug means that converting NL->EX will not return
|
|
* the latest lvb, so we have to convert NL->PR->EX to reread it.
|
|
*/
|
|
if (dlm_has_lvb_bug && (ld_mode == LD_LK_EX)) {
|
|
rv = dlm_ls_lock_wait(lmd->dh, LKM_PRMODE, lksb, flags,
|
|
r->name, strlen(r->name),
|
|
0, NULL, NULL, NULL);
|
|
if (rv == -1) {
|
|
log_debug("S %s R %s lock_dlm acquire mode PR for %d rv %d",
|
|
ls->name, r->name, mode, rv);
|
|
goto lockrv;
|
|
}
|
|
|
|
/* Fall through to request EX. */
|
|
}
|
|
|
|
rv = dlm_ls_lock_wait(lmd->dh, mode, lksb, flags,
|
|
r->name, strlen(r->name),
|
|
0, NULL, NULL, NULL);
|
|
lockrv:
|
|
if (rv == -1 && errno == EAGAIN) {
|
|
log_debug("S %s R %s lock_dlm acquire mode %d rv EAGAIN", ls->name, r->name, mode);
|
|
return -EAGAIN;
|
|
}
|
|
if (rv < 0) {
|
|
log_error("S %s R %s lock_dlm acquire error %d errno %d", ls->name, r->name, rv, errno);
|
|
return rv;
|
|
}
|
|
|
|
if (rdd->vb) {
|
|
if (lksb->sb_flags & DLM_SBF_VALNOTVALID) {
|
|
log_debug("S %s R %s lock_dlm VALNOTVALID", ls->name, r->name);
|
|
memset(rdd->vb, 0, sizeof(struct val_blk));
|
|
memset(vb_out, 0, sizeof(struct val_blk));
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* 'vb' contains disk endian values, not host endian.
|
|
* It is copied directly to rdd->vb which is also kept
|
|
* in disk endian form.
|
|
* vb_out is returned to the caller in host endian form.
|
|
*/
|
|
memcpy(&vb, lksb->sb_lvbptr, sizeof(struct val_blk));
|
|
memcpy(rdd->vb, &vb, sizeof(vb));
|
|
|
|
vb_out->version = le16_to_cpu(vb.version);
|
|
vb_out->flags = le16_to_cpu(vb.flags);
|
|
vb_out->r_version = le32_to_cpu(vb.r_version);
|
|
}
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
int lm_convert_dlm(struct lockspace *ls, struct resource *r,
|
|
int ld_mode, uint32_t r_version)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
|
struct dlm_lksb *lksb = &rdd->lksb;
|
|
uint32_t mode;
|
|
uint32_t flags = 0;
|
|
int rv;
|
|
|
|
log_debug("S %s R %s convert_dlm", ls->name, r->name);
|
|
|
|
flags |= LKF_CONVERT;
|
|
flags |= LKF_NOQUEUE;
|
|
flags |= LKF_PERSISTENT;
|
|
|
|
if (rdd->vb && r_version && (r->mode == LD_LK_EX)) {
|
|
if (!rdd->vb->version) {
|
|
/* first time vb has been written */
|
|
rdd->vb->version = cpu_to_le16(VAL_BLK_VERSION);
|
|
}
|
|
rdd->vb->r_version = cpu_to_le32(r_version);
|
|
memcpy(lksb->sb_lvbptr, rdd->vb, sizeof(struct val_blk));
|
|
|
|
log_debug("S %s R %s convert_dlm set r_version %u",
|
|
ls->name, r->name, r_version);
|
|
|
|
flags |= LKF_VALBLK;
|
|
}
|
|
|
|
mode = to_dlm_mode(ld_mode);
|
|
|
|
if (daemon_test)
|
|
return 0;
|
|
|
|
rv = dlm_ls_lock_wait(lmd->dh, mode, lksb, flags,
|
|
r->name, strlen(r->name),
|
|
0, NULL, NULL, NULL);
|
|
if (rv == -1 && errno == EAGAIN) {
|
|
/* FIXME: When does this happen? Should something different be done? */
|
|
log_error("S %s R %s convert_dlm mode %d rv EAGAIN", ls->name, r->name, mode);
|
|
return -EAGAIN;
|
|
}
|
|
if (rv < 0) {
|
|
log_error("S %s R %s convert_dlm error %d", ls->name, r->name, rv);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int lm_unlock_dlm(struct lockspace *ls, struct resource *r,
|
|
uint32_t r_version, uint32_t lmu_flags)
|
|
{
|
|
struct lm_dlm *lmd = (struct lm_dlm *)ls->lm_data;
|
|
struct rd_dlm *rdd = (struct rd_dlm *)r->lm_data;
|
|
struct dlm_lksb *lksb = &rdd->lksb;
|
|
struct val_blk vb_prev;
|
|
struct val_blk vb_next;
|
|
uint32_t flags = 0;
|
|
int new_vb = 0;
|
|
int rv;
|
|
|
|
/*
|
|
* Do not set PERSISTENT, because we don't need an orphan
|
|
* NL lock to protect anything.
|
|
*/
|
|
|
|
flags |= LKF_CONVERT;
|
|
|
|
if (rdd->vb && (r->mode == LD_LK_EX)) {
|
|
|
|
/* vb_prev and vb_next are in disk endian form */
|
|
memcpy(&vb_prev, rdd->vb, sizeof(struct val_blk));
|
|
memcpy(&vb_next, rdd->vb, sizeof(struct val_blk));
|
|
|
|
if (!vb_prev.version) {
|
|
vb_next.version = cpu_to_le16(VAL_BLK_VERSION);
|
|
new_vb = 1;
|
|
}
|
|
|
|
if ((lmu_flags & LMUF_FREE_VG) && (r->type == LD_RT_VG)) {
|
|
vb_next.flags = cpu_to_le16(VBF_REMOVED);
|
|
new_vb = 1;
|
|
}
|
|
|
|
if (r_version) {
|
|
vb_next.r_version = cpu_to_le32(r_version);
|
|
new_vb = 1;
|
|
}
|
|
|
|
if (new_vb) {
|
|
memcpy(rdd->vb, &vb_next, sizeof(struct val_blk));
|
|
memcpy(lksb->sb_lvbptr, &vb_next, sizeof(struct val_blk));
|
|
|
|
log_debug("S %s R %s unlock_dlm vb old %x %x %u new %x %x %u",
|
|
ls->name, r->name,
|
|
le16_to_cpu(vb_prev.version),
|
|
le16_to_cpu(vb_prev.flags),
|
|
le32_to_cpu(vb_prev.r_version),
|
|
le16_to_cpu(vb_next.version),
|
|
le16_to_cpu(vb_next.flags),
|
|
le32_to_cpu(vb_next.r_version));
|
|
} else {
|
|
log_debug("S %s R %s unlock_dlm vb unchanged", ls->name, r->name);
|
|
}
|
|
|
|
flags |= LKF_VALBLK;
|
|
} else {
|
|
log_debug("S %s R %s unlock_dlm", ls->name, r->name);
|
|
}
|
|
|
|
if (daemon_test)
|
|
return 0;
|
|
|
|
rv = dlm_ls_lock_wait(lmd->dh, LKM_NLMODE, lksb, flags,
|
|
r->name, strlen(r->name),
|
|
0, NULL, NULL, NULL);
|
|
if (rv < 0) {
|
|
log_error("S %s R %s unlock_dlm error %d", ls->name, r->name, rv);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* This list could be read from dlm_controld via libdlmcontrol,
|
|
* but it's simpler to get it from sysfs.
|
|
*/
|
|
|
|
#define DLM_LOCKSPACES_PATH "/sys/kernel/config/dlm/cluster/spaces"
|
|
|
|
/*
|
|
* FIXME: this should be implemented differently.
|
|
* It's not nice to use an aspect of the dlm clustering
|
|
* implementation, which could change. It would be
|
|
* better to do something like use a special lock in the
|
|
* lockspace that was held PR by all nodes, and then an
|
|
* EX request on it could check if it's started (and
|
|
* possibly also notify others to stop it automatically).
|
|
* Or, possibly an enhancement to libdlm that would give
|
|
* info about lockspace members.
|
|
*
|
|
* (We could let the VG be removed while others still
|
|
* have the lockspace running, which largely works, but
|
|
* introduces problems if another VG with the same name is
|
|
* recreated while others still have the lockspace running
|
|
* for the previous VG. We'd also want a way to clean up
|
|
* the stale lockspaces on the others eventually.)
|
|
*/
|
|
|
|
int lm_hosts_dlm(struct lockspace *ls, int notify)
|
|
{
|
|
static const char closedir_err_msg[] = "lm_hosts_dlm: closedir failed";
|
|
char ls_nodes_path[PATH_MAX];
|
|
struct dirent *de;
|
|
DIR *ls_dir;
|
|
int count = 0;
|
|
|
|
memset(ls_nodes_path, 0, sizeof(ls_nodes_path));
|
|
snprintf(ls_nodes_path, PATH_MAX-1, "%s/%s/nodes",
|
|
DLM_LOCKSPACES_PATH, ls->name);
|
|
|
|
if (!(ls_dir = opendir(ls_nodes_path)))
|
|
return -ECONNREFUSED;
|
|
|
|
while ((de = readdir(ls_dir))) {
|
|
if (de->d_name[0] == '.')
|
|
continue;
|
|
count++;
|
|
}
|
|
|
|
if (closedir(ls_dir))
|
|
log_error(closedir_err_msg);
|
|
|
|
if (!count) {
|
|
log_error("lm_hosts_dlm found no nodes in %s", ls_nodes_path);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Assume that a count of one node represents ourself,
|
|
* and any value over one represents other nodes.
|
|
*/
|
|
|
|
return count - 1;
|
|
}
|
|
|
|
int lm_get_lockspaces_dlm(struct list_head *ls_rejoin)
|
|
{
|
|
static const char closedir_err_msg[] = "lm_get_lockspace_dlm: closedir failed";
|
|
struct lockspace *ls;
|
|
struct dirent *de;
|
|
DIR *ls_dir;
|
|
|
|
if (!(ls_dir = opendir(DLM_LOCKSPACES_PATH)))
|
|
return -ECONNREFUSED;
|
|
|
|
while ((de = readdir(ls_dir))) {
|
|
if (de->d_name[0] == '.')
|
|
continue;
|
|
|
|
if (strncmp(de->d_name, LVM_LS_PREFIX, strlen(LVM_LS_PREFIX)))
|
|
continue;
|
|
|
|
if (!(ls = alloc_lockspace())) {
|
|
if (closedir(ls_dir))
|
|
log_error(closedir_err_msg);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ls->lm_type = LD_LM_DLM;
|
|
strncpy(ls->name, de->d_name, MAX_NAME);
|
|
strncpy(ls->vg_name, ls->name + strlen(LVM_LS_PREFIX), MAX_NAME);
|
|
list_add_tail(&ls->list, ls_rejoin);
|
|
}
|
|
|
|
if (closedir(ls_dir))
|
|
log_error(closedir_err_msg);
|
|
return 0;
|
|
}
|
|
|
|
int lm_is_running_dlm(void)
|
|
{
|
|
char sys_clustername[MAX_ARGS+1];
|
|
int rv;
|
|
|
|
memset(sys_clustername, 0, sizeof(sys_clustername));
|
|
|
|
rv = read_cluster_name(sys_clustername);
|
|
if (rv < 0)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|