Hi,
I am trying to use the DLM userland API (libdlm3), and while I was able to
do plain lock acquisitions and conversions, I am stuck trying to update
and then read the lock value block.
Does anyone have working examples of this? I did look at the rhdlmbook
doc, but couldn't fine one.
Attached is a messy test I wrote, which fails because it looks like
up-converting a lock with the LKF_VALBLK set doesn't seem to overwrite the
buffer I provide for the lock value block (and with strace it looks like
the kernel device returns the LVB on a down-conversion! weird). Example
output below.
Cheers,
Jean-Marc
--
[email protected]
$ make D=1
gcc -D_REENTRANT -Wall -Werror -O0 -g locklvb.c -pthread -ldlm
-lpthread -o locklvb
$ ./locklvb
dlm_kernel_version 6.0.1
create_lockspace
create_lockspace: Operation not permitted
open_lockspace
dlm_pthread_init
acquiring NL on MyLock...
LOCK mode -> NL convert 0
read_lvb 0 write_lvb 0
completion ast
entering loop on lock #1
count 0
LOCK mode -> PW convert 1
read_lvb 0 write_lvb 0
completion ast
init lvb => 51
lvb cache => 52
LOCK mode -> CR convert 1
read_lvb 0 write_lvb 1
completion ast
count 1
LOCK mode -> PW convert 1
read_lvb 1 write_lvb 0
completion ast
read lvb -1
locklvb: locklvb.c:177: do_lock: Assertion `lvb_lock.val >= 0' failed.
Aborted (core dumped)
/* compile with:
gcc -o locklvb locklvb.c -pthread -ldlm
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>
#include <libdlm.h>
#define LOG(fmt, ...) \
do { \
printf(fmt "\n", ## __VA_ARGS__); \
} while (0)
#define DEBUG LOG
#if 1
#define MODE_LO LKM_CRMODE
#define MODE_HI LKM_PWMODE
#else
#define MODE_LO LKM_NLMODE
#define MODE_HI LKM_EXMODE
#endif
#define RESOURCE "MyLock"
#define LOCKSPACE "default"
/* state for completion ast */
static const char *resource = RESOURCE;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int done = 0;
static int seq = 0; /* sanity check */
static dlm_lshandle_t ls;
struct lvb_desc {
int valid;
union {
char buf[DLM_LVB_LEN]; /* 32 bytes */
long val;
};
};
static struct lvb_desc lvb_cache = { .valid = false };
static const char const *mode_str(int mode) {
switch(mode) {
#define MAP(_m) case LKM_ ## _m ## MODE: return # _m
MAP(NL);
MAP(CR);
MAP(CW);
MAP(PR);
MAP(PW);
MAP(EX);
#undef MAP
default: return "??";
}
}
static void astcb(void *arg)
{
LOG("completion ast");
pthread_mutex_lock(&mutex);
assert((intptr_t)arg == seq); /* sanity check */
done = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
static void ast_wait(void)
{
pthread_mutex_lock(&mutex);
while (done == 0)
pthread_cond_wait(&cond, &mutex);
done = 0;
pthread_mutex_unlock(&mutex);
}
static void
do_lock(int mode, int flags, int *lockid,
const struct lvb_desc *out_lvb, /* write new lvb from this */
struct lvb_desc *in_lvb) /* read lvb and save it there */
{
int convert = (flags & LKF_CONVERT) != 0;
struct dlm_lksb lksb = {};
struct lvb_desc lvb_lock = {};
static int written = 0;
DEBUG("LOCK mode -> %s convert %d", mode_str(mode), convert);
if (convert)
lksb.sb_lkid = *lockid;
const int read_lvb =
written &&
convert && (mode == MODE_HI); /* up-convert */
const int write_lvb =
convert && (mode == MODE_LO); /* down-convert */
assert(!(read_lvb && write_lvb));
DEBUG("read_lvb %d write_lvb %d", read_lvb, write_lvb);
lvb_lock.val = -1; /* poison */
if (read_lvb || write_lvb) {
flags |= LKF_VALBLK;
lksb.sb_lvbptr = (char*)&lvb_lock.val;
}
if (read_lvb)
assert(out_lvb == NULL);
if (write_lvb) {
assert(in_lvb == NULL);
if (out_lvb->valid) {
assert(out_lvb->val > 0);
lvb_lock.val = out_lvb->val;
} else {
DEBUG("lvb invalidate");
flags |= LKF_IVVALBLK;
}
}
void *ast_arg = (void*)(intptr_t)++seq;
int rc = dlm_ls_lock(ls,
mode, &lksb, flags,
convert ? NULL : resource,
convert ? 0 : strlen(resource),
0, /* parent */
astcb, ast_arg, /* ast, ast arg */
NULL, NULL); /* bast, range */
if (rc) {
perror("dlm_lock");
exit(1);
}
ast_wait();
errno = lksb.sb_status;
if (lksb.sb_status) {
perror("dlm_lock post wait");
exit(1);
}
if (lksb.sb_flags)
DEBUG("sb_flags %d", lksb.sb_flags);
if ((lksb.sb_flags & DLM_SBF_VALNOTVALID) != 0)
DEBUG("DLM_SBF_VALNOTVALID");
if (read_lvb || write_lvb)
assert(lksb.sb_lvbptr == (char*)&lvb_lock.val);
if (read_lvb) {
/* "An LVB is valid when the lock manager first
creates the lock resource, in response to the first
lock request, before any client can assign a value
to the LVB."
So we never write 0 to the LVB, and always ignore 0
in the LVB.
*/
/* after comp ast runs, and despite poisoning, we
* shouldn't see a negative here! */
DEBUG("read lvb %ld", lvb_lock.val);
assert(lvb_lock.val >= 0);
in_lvb->valid =
((lksb.sb_flags & DLM_SBF_VALNOTVALID) == 0)
&& lvb_lock.val > 0;
if (in_lvb->valid)
in_lvb->val = lvb_lock.val;
}
if (write_lvb)
written = 1;
*lockid = lksb.sb_lkid;
}
static void
do_unlock(int lockid)
{
struct dlm_lksb lksb = {};
int rc = dlm_ls_unlock(ls, lockid, 0, &lksb, NULL);
if (rc) {
perror("dlm_unlock");
exit(1);
}
ast_wait();
errno = lksb.sb_status;
if (lksb.sb_status != 0
&& lksb.sb_status != EUNLOCK) {
perror("dlm_unlock post wait");
exit(1);
}
}
static int do_init(void)
{
int rc;
uint32_t major, minor, patch;
rc = dlm_kernel_version(&major, &minor, &patch);
if (rc != 0) {
perror("dlm_kernel_version");
return 1;
}
LOG("dlm_kernel_version %d.%d.%d", major, minor, patch);
LOG("create_lockspace");
ls = dlm_create_lockspace(LOCKSPACE, 0777); /* requires CAP_SYSADMIN */
if (ls == NULL) {
perror("create_lockspace");
if (errno != EEXIST && errno != EPERM)
return 1;
LOG("open_lockspace");
ls = dlm_open_lockspace(LOCKSPACE);
if (ls == NULL) {
perror("open_lockspace");
return 1;
}
}
LOG("dlm_pthread_init");
rc = dlm_ls_pthread_init(ls);
if(rc != 0) {
perror("dlm_pthread_init");
return 1;
}
return 0;
}
int main(int argc, char **argv) {
long count = 0;
int rc;
int lockid;
rc = do_init();
if (rc != 0)
return 1;
LOG("acquiring NL on %s...", resource);
do_lock(LKM_NLMODE, LKF_EXPEDITE, &lockid, NULL, NULL);
LOG("entering loop on lock #%d", lockid);
while (count < 10) {
DEBUG("count %ld", count);
struct lvb_desc const old_lvb = lvb_cache;
/* up-convert and read lvb */
do_lock(MODE_HI, LKF_CONVERT, &lockid, NULL, &lvb_cache);
if (old_lvb.valid != lvb_cache.valid) {
LOG("lvb valid => %s", lvb_cache.valid ? "true" : "false");
if (lvb_cache.valid)
LOG("lvb = %ld", lvb_cache.val);
} else if (old_lvb.valid && lvb_cache.valid) {
if (old_lvb.val != lvb_cache.val)
LOG("lvb %ld -> %ld", old_lvb.val, lvb_cache.val);
}
if (!lvb_cache.valid) {
lvb_cache.valid = true;
lvb_cache.val = 51;
LOG("init lvb => %ld", lvb_cache.val);
}
lvb_cache.val++;
DEBUG("lvb cache => %ld", lvb_cache.val);
/* down-convert and write lvb */
do_lock(MODE_LO, LKF_CONVERT, &lockid, &lvb_cache, NULL);
count++;
}
LOG("unlock");
do_unlock(lockid);
return 0;
}
--
Linux-cluster mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-cluster