Skip to content

Commit

Permalink
Merge fixes/NFSE-4054
Browse files Browse the repository at this point in the history
  • Loading branch information
Neelima Premsankar committed Jul 11, 2020
2 parents 378c317 + d4d2ed9 commit 6756cfc
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 20 deletions.
5 changes: 2 additions & 3 deletions src/util/include/hse_util/bonsai_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,16 +249,15 @@ struct bonsai_client {
* @br_root: pointer to the root of bonsai_tree
* @br_kv: a circular k/v list, next=head, prev=tail
* @br_lcp: longest common prefix between min/max keys
* @br_chkbounds: safe to perfom a bounds check
* @br_bounds: indicates bounds are established and lcp
* @br_client: bonsai client instance
*
* There is one such structure for every bonsai tree.
*/
struct bonsai_root {
struct bonsai_node * br_root;
struct bonsai_kv br_kv;
unsigned int br_lcp;
bool br_chkbounds;
atomic_t br_bounds;
struct bonsai_client br_client;
};

Expand Down
38 changes: 24 additions & 14 deletions src/util/src/bonsai_tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ bn_find_impl(struct bonsai_root *tree, const struct bonsai_skey *skey, enum bons
const void * key;

uint klen;
uint lcp;
uint lcp, bounds;
s32 res;

ki = &skey->bsk_key_imm;
Expand All @@ -211,19 +211,25 @@ bn_find_impl(struct bonsai_root *tree, const struct bonsai_skey *skey, enum bons
* lcp to potentially minimize the amount of work required
* to search for the key in this tree.
*/
if (tree->br_chkbounds) {
bounds = atomic_read(&tree->br_bounds);
if (bounds) {
struct bonsai_kv *bkv = tree->br_kv.bkv_prev; /* max key */

if (tree->br_lcp > KI_DLEN_MAX) {
lcp = min_t(uint, klen, tree->br_lcp);
/* br_bounds is set to 1 + the lcp to use. */
lcp = min_t(uint, klen, bounds - 1);

if (lcp > KI_DLEN_MAX &&
key_immediate_index(ki) == key_immediate_index(&bkv->bkv_key_imm)) {

lcp = memlcpq(key, bkv->bkv_key, lcp);
if (lcp > KI_DLEN_MAX)
if (lcp > KI_DLEN_MAX) {
assert(key_immediate_cmp(ki, &bkv->bkv_key_imm) == S32_MIN);
goto search;

lcp = KI_DLEN_MAX;
}
}

lcp = KI_DLEN_MAX;

/*
* If search key > max, then a miss for both GE and EQ get.
* Return the max key for a LE get and a NULL for EQ get.
Expand Down Expand Up @@ -524,8 +530,7 @@ bn_reset(struct bonsai_root *tree)
tree->br_kv.bkv_tomb = NULL;
tree->br_kv.bkv_flags = 0;

tree->br_lcp = 0;
tree->br_chkbounds = false;
atomic_set(&tree->br_bounds, 0);

client->bc_slab_cur = NULL;
client->bc_slab_end = NULL;
Expand Down Expand Up @@ -585,8 +590,7 @@ void
bn_finalize(struct bonsai_root *tree)
{
const struct bonsai_kv *kmin, *kmax;

uint lcp;
uint lcp, set_lcp = 0;

kmin = tree->br_kv.bkv_next;
kmax = tree->br_kv.bkv_prev;
Expand All @@ -599,11 +603,17 @@ bn_finalize(struct bonsai_root *tree)
if (kmin != kmax) {
lcp = min_t(uint, kmin->bkv_key_imm.ki_klen, kmax->bkv_key_imm.ki_klen);

if (lcp > KI_DLEN_MAX) {
if (lcp > KI_DLEN_MAX &&
key_immediate_index(&kmin->bkv_key_imm) == key_immediate_index(&kmax->bkv_key_imm)) {

lcp = memlcpq(kmin->bkv_key, kmax->bkv_key, lcp);
tree->br_lcp = lcp;
if (lcp > KI_DLEN_MAX) {
assert(key_immediate_cmp(&kmin->bkv_key_imm, &kmax->bkv_key_imm) == S32_MIN);
set_lcp = lcp;
}
}

tree->br_chkbounds = true;
/* Indicate that the bounds have been established and the lcp to use. */
atomic_set(&tree->br_bounds, set_lcp + 1);
}
}
248 changes: 245 additions & 3 deletions src/util/test/bonsai_tree_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,145 @@ bonsai_client_producer(void *arg)
pthread_exit((void *)(long)merr_errno(err));
}

struct lcp_test_arg {
pthread_barrier_t *fbarrier;
uint tid;
};

static void *
bonsai_client_lcp_test(void *arg)
{
int i;
uint tid;
merr_t err = 0;
char key[KI_DLEN_MAX + 36];
unsigned long val;
pthread_barrier_t *fbarrier;

struct lcp_test_arg *p = (struct lcp_test_arg *)arg;

fbarrier = p->fbarrier;
tid = p->tid;

struct bonsai_skey skey = { 0 };
struct bonsai_sval sval = { 0 };

#ifdef BONSAI_TREE_CLIENT_VERIFY
uint lcp, bounds;
#endif

/*
* Register is not required for BP. For QSBR, it is required only for
* clients.
*/
#ifndef LIBURCU_QSBR
#ifndef LIBURCU_BP
BONSAI_RCU_REGISTER();
#endif
#endif

memset(key, 'a', KI_DLEN_MAX + 27);

/*
* Insert keys of the same length (KI_DLEN_MAX + 27).
* The last byte is replaced with a..z.
* Each key is inserted with a unique value to identify the keynum, skidx.
*/
for (i = 0; i < 26; i++) {
val = (u64)i << 32 | tid;

key[KI_DLEN_MAX + 26] = 'a' + i;

bn_skey_init(key, KI_DLEN_MAX + 27, tid, &skey);
bn_sval_init(&val, sizeof(val), val, &sval);

pthread_mutex_lock(&mtx);
err = bn_insert_or_replace(broot, &skey, &sval, false);
pthread_mutex_unlock(&mtx);

key[KI_DLEN_MAX + 26] = 'a';

if (err) {
hse_elog(HSE_ERR "lcp_test bn_insert %u result @@e", err, i);
break;
}
}

pthread_barrier_wait(fbarrier);

while (!stop_producer_threads)
usleep(1000);

#ifdef BONSAI_TREE_CLIENT_VERIFY
bounds = atomic_read(&broot->br_bounds);
if (bounds)
lcp = bounds - 1;

for (i = 0; i < 26; i++) {
struct bonsai_skey skey = { 0 };
struct bonsai_kv * kv = NULL;
struct bonsai_val * v;
unsigned long val;
bool found;
const struct key_immediate *ki;

key[KI_DLEN_MAX + 26] = 'a' + i;

bn_skey_init(key, KI_DLEN_MAX + 27, tid, &skey);
ki = &skey.bsk_key_imm;

rcu_read_lock();
found = bn_find(broot, &skey, &kv);
assert(found);

v = kv->bkv_values;
memcpy((char *)&val, v->bv_value, sizeof(val));
assert(val == ((u64)i << 32 | tid));

rcu_read_unlock();

key[KI_DLEN_MAX + 26] = 'a';

if (lcp > 0) {
assert(key_immediate_cmp(ki, &kv->bkv_key_imm) == S32_MIN);
assert(memcmp(kv->bkv_key, &kv->bkv_key, lcp) == 0);
}
}

for (i = 1; i < KI_DLEN_MAX + 27; i++) {
struct bonsai_skey skey = { 0 };
struct bonsai_kv * kv = NULL;
bool found;

bn_skey_init(key, i, tid, &skey);

rcu_read_lock();
found = bn_find(broot, &skey, &kv);
assert(!found);
}

for (i = KI_DLEN_MAX + 28; i < sizeof(key); i++) {
struct bonsai_skey skey = { 0 };
struct bonsai_kv * kv = NULL;
bool found;

bn_skey_init(key, i, tid, &skey);

rcu_read_lock();
found = bn_find(broot, &skey, &kv);
assert(!found);
}
#endif

#ifndef LIBURCU_QSBR
#ifndef LIBURCU_BP
BONSAI_RCU_UNREGISTER();
#endif
#endif

pthread_exit((void *)(long)merr_errno(err));
}

static void *
bonsai_client_consumer(void *arg)
{
Expand Down Expand Up @@ -609,9 +748,9 @@ bonsai_client_singlethread_test(enum bonsai_alloc_mode allocm)
4, 5, 99, 299, 301, 1, 2, 3, 4, 5, 99, 7,
8, 9, 13, 14, 15, 99, 20, 30, 40, 50, 101, 150,
500, 100, 600, 5, 99, 200, 1, 2, 3, 4, 5, 99 };
struct bonsai_skey skey = { 0 };
struct bonsai_sval sval = { 0 };
struct bonsai_kv * kv = NULL;
struct bonsai_skey skey = { 0 };
struct bonsai_sval sval = { 0 };
struct bonsai_kv * kv = NULL;

if (allocm == HSE_ALLOC_CURSOR) {
cheap = cheap_create(8, 64 * MB);
Expand Down Expand Up @@ -769,6 +908,109 @@ MTF_DEFINE_UTEST_PREPOST(bonsai_tree_test, producer_test, no_fail_pre, no_fail_p
#endif
}

MTF_DEFINE_UTEST_PREPOST(bonsai_tree_test, lcp_test, no_fail_pre, no_fail_post)
{
int rc;
int i;
const int num_skidx = 64;
pthread_t *producer_tids;

void * ret;
pthread_barrier_t final_barrier;
struct lcp_test_arg args[num_skidx];
merr_t err;

cheap = cheap_create(8, 64 * MB);
ASSERT_NE(cheap, NULL);

err = bn_create(cheap, 32 * MB, bonsai_client_insert_callback, NULL, &broot);
ASSERT_EQ(err, 0);

rc = pthread_mutex_init(&mtx, NULL);
ASSERT_EQ(rc, 0);

producer_tids = malloc(num_skidx * sizeof(*producer_tids));
ASSERT_NE(producer_tids, NULL);

rc = create_all_cpu_call_rcu_data(0);
ASSERT_EQ(rc, 0);

stop_producer_threads = 0;

pthread_barrier_init(&final_barrier, NULL, num_skidx + 1);

for (i = 0; i < num_skidx; i++) {
args[i].tid = i;
args[i].fbarrier = &final_barrier;
rc = pthread_create(&producer_tids[i], NULL, bonsai_client_lcp_test, &args[i]);
ASSERT_EQ(rc, 0);
}

/* Wait until all the skidx threads are done inserting their keys */
pthread_barrier_wait(&final_barrier);

bn_finalize(broot);

/* lcp must be zero since the keys have different skidx values */
ASSERT_EQ(atomic_read(&broot->br_bounds), 1);

stop_producer_threads = 1;
for (i = 0; i < num_producers; i++) {
rc = pthread_join(producer_tids[i], &ret);
ASSERT_EQ(rc, 0);
}

BONSAI_RCU_BARRIER();

bn_destroy(broot);

BONSAI_RCU_BARRIER();

cheap_destroy(cheap);
cheap = NULL;
cheap = cheap_create(8, 64 * MB);
ASSERT_NE(cheap, NULL);

err = bn_create(cheap, 32 * MB, bonsai_client_insert_callback, NULL, &broot);
ASSERT_EQ(err, 0);

stop_producer_threads = 0;

/* Repeat the test with a bonsai tree containing keys for just one skidx. */
pthread_barrier_init(&final_barrier, NULL, 2);

args[0].tid = num_skidx + 1;
args[0].fbarrier = &final_barrier;
rc = pthread_create(&producer_tids[0], NULL, bonsai_client_lcp_test, &args[0]);
ASSERT_EQ(rc, 0);

pthread_barrier_wait(&final_barrier);

bn_finalize(broot);

/* lcp must be set this time around */
ASSERT_GT(atomic_read(&broot->br_bounds), 1 + KI_DLEN_MAX);

stop_producer_threads = 1;
rc = pthread_join(producer_tids[0], &ret);
ASSERT_EQ(rc, 0);

BONSAI_RCU_BARRIER();

bn_destroy(broot);

BONSAI_RCU_BARRIER();

free_all_cpu_call_rcu_data();

cheap_destroy(cheap);
cheap = NULL;

pthread_mutex_destroy(&mtx);

free(producer_tids);
}

MTF_DEFINE_UTEST_PREPOST(bonsai_tree_test, producer_manyconsumer_test, no_fail_pre, no_fail_post)
{
key_begin = 1;
Expand Down

0 comments on commit 6756cfc

Please sign in to comment.