Files
scoutfs/kmod/dlm/dlmtest.c
Mark Fasheh 08bf1fea79 dlm: Give fs/dlm the notion of ranges
Using the new interval tree code we add a tree for each lock status list to
efficiently track ranged requests. Internally, most operations on a
resources lock status list (granted, waiting, converting) then are turned
into operations within a given range.

There is no API change other than a new call, dlm_lock_range() and a new
structure, 'struct dlm_key' to define our range endpoints. Keys can have
arbitrary lengths and are compared via memcmp. A ranged blocking ast type is
defined so that users of dlm_lock_range() can know which range they are
blocking.

A rudimentary test, dlmtest.ko is included.

TODO:
 - Update userspace entry points, need to add one for new lock call
 - Manage backwards compatibility with network protocol

Signed-off-by: Mark Fasheh <mfasheh@versity.com>
2017-06-23 15:07:10 -05:00

308 lines
6.0 KiB
C

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include "include/linux/dlm.h"
static atomic_t granted;
static atomic_t blocking;
static int val = 1;
static dlm_lockspace_t *ls;
static char *lockres_name = "test_resource";
struct lockinfo {
char *lockname;
int unlocking;
u64 start;
u64 end;
struct dlm_key startkey;
struct dlm_key endkey;
struct dlm_lksb lksb;
};
static inline void set_lock_endpoints(struct lockinfo *lock, u64 start, u64 end)
{
lock->start = cpu_to_be64(start);
lock->startkey.val = &lock->start;
lock->startkey.len = sizeof(lock->start);
lock->end = cpu_to_be64(end);
lock->endkey.val = &lock->end;
lock->endkey.len = sizeof(lock->end);
}
#define NUM_LOCKS 3
static struct lockinfo locks[NUM_LOCKS] = {
{ "lock0", },
{ "lock1", },
{ "lock2", },
};
static int glbl_exmode = 0;
module_param(glbl_exmode, int, 0);
MODULE_PARM_DESC(glbl_exmode, "Take global lock exclusively.");
static void init_counters(void)
{
atomic_set(&granted, 0);
atomic_set(&blocking, 0);
val = 1;
}
static void wait_for_blocking_asts(int count)
{
printk("wait for %d blocking asts\n", count);
while (atomic_read(&blocking) != count) {
printk("blocking: %d\n", atomic_read(&blocking));
msleep_interruptible(2000);
}
}
static void wait_for_lock_grants(int count)
{
printk("wait for %d grants\n", count);
while (atomic_read(&granted) != count) {
printk("granted: %d\n", atomic_read(&granted));
msleep_interruptible(2000);
}
}
static void grant_function(void *arg)
{
char *name = arg;
printk("lock %s granted\n", name);
atomic_add(val, &granted);
}
static void blocking_function(void *arg, int mode, struct dlm_key *start,
struct dlm_key *end)
{
char *name = arg;
BUG_ON(!start);
BUG_ON(!end);
printk("lock %s blocking mode %d, range (%llu, %llu)\n", name, mode,
be64_to_cpu(*((u64 *)start->val)), be64_to_cpu(*((u64 *)end->val)));
atomic_inc(&blocking);
}
static int _test_lock(unsigned int lockidx, unsigned int mode,
unsigned long long start, unsigned long long end,
unsigned int flags)
{
struct lockinfo *lock = &locks[lockidx];
BUG_ON(lockidx > NUM_LOCKS);
set_lock_endpoints(lock, start, end);
printk("lock %s (%u, %llu, %llu)\n", lock->lockname, mode, start, end);
return dlm_lock_range(ls, mode, &lock->startkey, &lock->endkey,
&lock->lksb, flags, lockres_name,
strlen(lockres_name), 0, grant_function,
lock->lockname, blocking_function);
}
static inline int test_lock(unsigned int lockidx, unsigned int mode,
unsigned long long start, unsigned long long end)
{
return _test_lock(lockidx, mode, start, end, 0);
}
static inline int test_convert(unsigned int lockidx, unsigned int mode,
unsigned long long start,
unsigned long long end)
{
return _test_lock(lockidx, mode, start, end, DLM_LKF_CONVERT);
}
static int test_unlock(int lockidx)
{
struct lockinfo *lock = &locks[lockidx];
printk("unlock %s (%llu, %llu)\n", lock->lockname,
be64_to_cpu(lock->start), be64_to_cpu(lock->end));
return dlm_unlock(ls, lock->lksb.sb_lkid, 0, &lock->lksb, lock->lockname);
}
static int test_locking(void)
{
int ret;
printk("Test basic lock/unlock.\n");
init_counters();
ret = test_lock(0, DLM_LOCK_EX, 0, 16384);
if (ret)
goto out;
ret = test_lock(1, DLM_LOCK_EX, 16385, 32768);
if (ret)
goto out;
wait_for_lock_grants(2);
ret = test_lock(2, DLM_LOCK_EX, 0, 32768);
if (ret)
goto out;
wait_for_blocking_asts(2);
val = -1;
ret = test_unlock(0);
if (ret)
goto out;
ret = test_unlock(1);
if (ret)
goto out;
wait_for_lock_grants(-1);
ret = test_unlock(2);
if (ret)
goto out;
wait_for_lock_grants(-2);
out:
return ret;
}
static int test_lock_conversions(void)
{
int ret;
printk("Test lock conversions\n");
init_counters();
ret = test_lock(0, DLM_LOCK_PR, 0, 16384);
if (ret)
goto out;
ret = test_lock(1, DLM_LOCK_PR, 16385, 32768);
if (ret)
goto out;
ret = test_lock(2, DLM_LOCK_PR, 0, 32768);
if (ret)
goto out;
wait_for_lock_grants(3);
ret = test_convert(0, DLM_LOCK_EX, 0, 16384);
if (ret)
goto out;
wait_for_blocking_asts(2);
init_counters();
ret = test_convert(1, DLM_LOCK_NL, 16385, 32768);
if (ret)
goto out;
ret = test_convert(2, DLM_LOCK_NL, 0, 32768);
if (ret)
goto out;
wait_for_lock_grants(3);
init_counters();
ret = test_unlock(1);
if (ret)
goto out;
ret = test_unlock(2);
if (ret)
goto out;
ret = test_unlock(0);
if (ret)
goto out;
wait_for_lock_grants(3);
out:
return ret;
}
#define glbl_res "global"
#define glbl_res_len strlen(glbl_res)
static atomic_t glbl_grants;
static struct dlm_lksb glbl_lksb;
static void glbl_granted(void *arg)
{
printk("Got global lock at %d mode\n", *((int *) arg));
atomic_set(&glbl_grants, 1);
}
static void glbl_blocking(void *arg, int mode)
{
printk("Global lock at %d mode blocking %d lock\n", *((int *)arg), mode);
}
static int test_multinode(void)
{
int ret;
int mode = DLM_LOCK_EX;
printk("Test a global lock\n");
ret = dlm_lock(ls, mode, &glbl_lksb, 0, glbl_res, glbl_res_len, 0,
glbl_granted, &mode, glbl_blocking);
if (ret)
return ret;
while (!atomic_read(&glbl_grants))
msleep_interruptible(5000);
mode = 0;
ret = dlm_unlock(ls, glbl_lksb.sb_lkid, 0, &glbl_lksb, &mode);
return ret;
}
static int __init init_dlm_test(void)
{
int ret;
printk("dlmtest loaded!\n");
ret = dlm_new_lockspace("lockspace", "scoutfs",
DLM_LSFL_FS|DLM_LSFL_NEWEXCL, 8, NULL, NULL,
NULL, &ls);
if (ret) {
printk("new_lockspace returns %d\n", ret);
return ret;
}
ret = test_multinode();
if (!ret)
ret = test_locking();
if (!ret)
ret = test_lock_conversions();
if (ret)
printk("FAILURE: Locking test returns %d\n", ret);
else
printk("Locking test completed with no errors.\n");
return 0;
}
static void __exit exit_dlm_test(void)
{
int ret;
ret = dlm_release_lockspace(ls, 1);
printk("dlmtest unloaded (ret=%d)!\n", ret);
}
module_init(init_dlm_test);
module_exit(exit_dlm_test);
MODULE_DESCRIPTION("dlmtest");
MODULE_AUTHOR("Mark Fasheh");
MODULE_LICENSE("GPL");