DLM user API for lock value block

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

-- 
saffroy@xxxxxxxxx

$ 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
Linux-cluster@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/linux-cluster

[Index of Archives]     [Corosync Cluster Engine]     [GFS]     [Linux Virtualization]     [Centos Virtualization]     [Centos]     [Linux RAID]     [Fedora Users]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite Camping]

  Powered by Linux