X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=app%2Ftest%2Ftest_hash.c;h=b99e8de1db71ef180e7f254a7840f457c65fbca2;hb=80da61198b6be66ab16964eb0944c79c0325c40e;hp=390fbef87f42b7bc1bf6748fbdc63769653ee1c9;hpb=54e5545d33eff911c1024479eb52d68dbf45139b;p=dpdk.git diff --git a/app/test/test_hash.c b/app/test/test_hash.c index 390fbef87f..b99e8de1db 100644 --- a/app/test/test_hash.c +++ b/app/test/test_hash.c @@ -60,32 +60,45 @@ static uint32_t hashtest_key_lens[] = {0, 2, 4, 5, 6, 7, 8, 10, 11, 15, 16, 21, } \ } while(0) -/* 5-tuple key type */ +#define RETURN_IF_ERROR_RCU_QSBR(cond, str, ...) do { \ + if (cond) { \ + printf("ERROR line %d: " str "\n", __LINE__, ##__VA_ARGS__); \ + if (rcu_cfg.mode == RTE_HASH_QSBR_MODE_SYNC) { \ + writer_done = 1; \ + /* Wait until reader exited. */ \ + rte_eal_mp_wait_lcore(); \ + } \ + rte_hash_free(g_handle); \ + rte_free(g_qsv); \ + return -1; \ + } \ +} while (0) + +/* + * 5-tuple key type. + * Should be packed to avoid holes with potentially + * undefined content in the middle. + */ struct flow_key { uint32_t ip_src; uint32_t ip_dst; uint16_t port_src; uint16_t port_dst; - uint8_t proto; -} __attribute__((packed)); - -int hash_logtype_test; + uint32_t proto; +} __rte_packed; /* * Hash function that always returns the same value, to easily test what * happens when a bucket is full. */ -static uint32_t pseudo_hash(__attribute__((unused)) const void *keys, - __attribute__((unused)) uint32_t key_len, - __attribute__((unused)) uint32_t init_val) +static uint32_t pseudo_hash(__rte_unused const void *keys, + __rte_unused uint32_t key_len, + __rte_unused uint32_t init_val) { return 3; } -RTE_INIT(test_hash_init_log) -{ - hash_logtype_test = rte_log_register("test.hash"); -} +RTE_LOG_REGISTER(hash_logtype_test, test.hash, INFO); /* * Print out result of unit test hash operation. @@ -104,32 +117,32 @@ static void print_key_info(const char *msg, const struct flow_key *key, /* Keys used by unit test functions */ static struct flow_key keys[5] = { { - .ip_src = IPv4(0x03, 0x02, 0x01, 0x00), - .ip_dst = IPv4(0x07, 0x06, 0x05, 0x04), + .ip_src = RTE_IPV4(0x03, 0x02, 0x01, 0x00), + .ip_dst = RTE_IPV4(0x07, 0x06, 0x05, 0x04), .port_src = 0x0908, .port_dst = 0x0b0a, .proto = 0x0c, }, { - .ip_src = IPv4(0x13, 0x12, 0x11, 0x10), - .ip_dst = IPv4(0x17, 0x16, 0x15, 0x14), + .ip_src = RTE_IPV4(0x13, 0x12, 0x11, 0x10), + .ip_dst = RTE_IPV4(0x17, 0x16, 0x15, 0x14), .port_src = 0x1918, .port_dst = 0x1b1a, .proto = 0x1c, }, { - .ip_src = IPv4(0x23, 0x22, 0x21, 0x20), - .ip_dst = IPv4(0x27, 0x26, 0x25, 0x24), + .ip_src = RTE_IPV4(0x23, 0x22, 0x21, 0x20), + .ip_dst = RTE_IPV4(0x27, 0x26, 0x25, 0x24), .port_src = 0x2928, .port_dst = 0x2b2a, .proto = 0x2c, }, { - .ip_src = IPv4(0x33, 0x32, 0x31, 0x30), - .ip_dst = IPv4(0x37, 0x36, 0x35, 0x34), + .ip_src = RTE_IPV4(0x33, 0x32, 0x31, 0x30), + .ip_dst = RTE_IPV4(0x37, 0x36, 0x35, 0x34), .port_src = 0x3938, .port_dst = 0x3b3a, .proto = 0x3c, }, { - .ip_src = IPv4(0x43, 0x42, 0x41, 0x40), - .ip_dst = IPv4(0x47, 0x46, 0x45, 0x44), + .ip_src = RTE_IPV4(0x43, 0x42, 0x41, 0x40), + .ip_dst = RTE_IPV4(0x47, 0x46, 0x45, 0x44), .port_src = 0x4948, .port_dst = 0x4b4a, .proto = 0x4c, @@ -138,7 +151,7 @@ static struct flow_key keys[5] = { { /* Parameters used for hash table in unit test functions. Name set later. */ static struct rte_hash_parameters ut_params = { .entries = 64, - .key_len = sizeof(struct flow_key), /* 13 */ + .key_len = sizeof(struct flow_key), .hash_func = rte_jhash, .hash_func_init_val = 0, .socket_id = 0, @@ -235,15 +248,9 @@ static void run_hash_func_tests(void) { unsigned i, j, k; - for (i = 0; - i < sizeof(hashtest_funcs) / sizeof(rte_hash_function); - i++) { - for (j = 0; - j < sizeof(hashtest_initvals) / sizeof(uint32_t); - j++) { - for (k = 0; - k < sizeof(hashtest_key_lens) / sizeof(uint32_t); - k++) { + for (i = 0; i < RTE_DIM(hashtest_funcs); i++) { + for (j = 0; j < RTE_DIM(hashtest_initvals); j++) { + for (k = 0; k < RTE_DIM(hashtest_key_lens); k++) { run_hash_func_test(hashtest_funcs[i], hashtest_initvals[j], hashtest_key_lens[k]); @@ -481,6 +488,98 @@ static int test_add_update_delete_free(void) return 0; } +/* + * Sequence of operations for a single key with 'rw concurrency lock free' set: + * - add + * - delete: hit + * - free: hit + * Repeat the test case when 'multi writer add' is enabled. + * - add + * - delete: hit + * - free: hit + */ +static int test_add_delete_free_lf(void) +{ +/* Should match the #define LCORE_CACHE_SIZE value in rte_cuckoo_hash.h */ +#define LCORE_CACHE_SIZE 64 + struct rte_hash *handle; + hash_sig_t hash_value; + int pos, expectedPos, delPos; + uint8_t extra_flag; + uint32_t i, ip_src; + + extra_flag = ut_params.extra_flag; + ut_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF; + handle = rte_hash_create(&ut_params); + RETURN_IF_ERROR(handle == NULL, "hash creation failed"); + ut_params.extra_flag = extra_flag; + + /* + * The number of iterations is at least the same as the number of slots + * rte_hash allocates internally. This is to reveal potential issues of + * not freeing keys successfully. + */ + for (i = 0; i < ut_params.entries + 1; i++) { + hash_value = rte_hash_hash(handle, &keys[0]); + pos = rte_hash_add_key_with_hash(handle, &keys[0], hash_value); + print_key_info("Add", &keys[0], pos); + RETURN_IF_ERROR(pos < 0, "failed to add key (pos=%d)", pos); + expectedPos = pos; + + pos = rte_hash_del_key_with_hash(handle, &keys[0], hash_value); + print_key_info("Del", &keys[0], pos); + RETURN_IF_ERROR(pos != expectedPos, + "failed to delete key (pos=%d)", pos); + delPos = pos; + + pos = rte_hash_free_key_with_position(handle, delPos); + print_key_info("Free", &keys[0], delPos); + RETURN_IF_ERROR(pos != 0, + "failed to free key (pos=%d)", delPos); + } + + rte_hash_free(handle); + + extra_flag = ut_params.extra_flag; + ut_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF | + RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD; + handle = rte_hash_create(&ut_params); + RETURN_IF_ERROR(handle == NULL, "hash creation failed"); + ut_params.extra_flag = extra_flag; + + ip_src = keys[0].ip_src; + /* + * The number of iterations is at least the same as the number of slots + * rte_hash allocates internally. This is to reveal potential issues of + * not freeing keys successfully. + */ + for (i = 0; i < ut_params.entries + (RTE_MAX_LCORE - 1) * + (LCORE_CACHE_SIZE - 1) + 1; i++) { + keys[0].ip_src++; + hash_value = rte_hash_hash(handle, &keys[0]); + pos = rte_hash_add_key_with_hash(handle, &keys[0], hash_value); + print_key_info("Add", &keys[0], pos); + RETURN_IF_ERROR(pos < 0, "failed to add key (pos=%d)", pos); + expectedPos = pos; + + pos = rte_hash_del_key_with_hash(handle, &keys[0], hash_value); + print_key_info("Del", &keys[0], pos); + RETURN_IF_ERROR(pos != expectedPos, + "failed to delete key (pos=%d)", pos); + delPos = pos; + + pos = rte_hash_free_key_with_position(handle, delPos); + print_key_info("Free", &keys[0], delPos); + RETURN_IF_ERROR(pos != 0, + "failed to free key (pos=%d)", delPos); + } + keys[0].ip_src = ip_src; + + rte_hash_free(handle); + + return 0; +} + /* * Sequence of operations for retrieving a key with its position * @@ -697,7 +796,7 @@ static int test_full_bucket(void) struct rte_hash_parameters params_pseudo_hash = { .name = "test4", .entries = 64, - .key_len = sizeof(struct flow_key), /* 13 */ + .key_len = sizeof(struct flow_key), .hash_func = pseudo_hash, .hash_func_init_val = 0, .socket_id = 0, @@ -800,7 +899,7 @@ static int test_extendable_bucket(void) struct rte_hash_parameters params_pseudo_hash = { .name = "test5", .entries = 64, - .key_len = sizeof(struct flow_key), /* 13 */ + .key_len = sizeof(struct flow_key), .hash_func = pseudo_hash, .hash_func_init_val = 0, .socket_id = 0, @@ -1050,8 +1149,11 @@ fbk_hash_unit_test(void) handle = rte_fbk_hash_create(&invalid_params_7); RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed"); - handle = rte_fbk_hash_create(&invalid_params_8); - RETURN_IF_ERROR_FBK(handle != NULL, "fbk hash creation should have failed"); + if (rte_eal_has_hugepages()) { + handle = rte_fbk_hash_create(&invalid_params_8); + RETURN_IF_ERROR_FBK(handle != NULL, + "fbk hash creation should have failed"); + } handle = rte_fbk_hash_create(&invalid_params_same_name_1); RETURN_IF_ERROR_FBK(handle == NULL, "fbk hash creation should have succeeded"); @@ -1508,6 +1610,17 @@ static struct rte_hash_parameters hash_params_ex = { .socket_id = 0, }; +/* + * Wrapper function around rte_jhash_32b. + * It is required because rte_jhash_32b() accepts the length + * as size of 4-byte units. + */ +static inline uint32_t +test_jhash_32b(const void *k, uint32_t length, uint32_t initval) +{ + return rte_jhash_32b(k, length >> 2, initval); +} + /* * add/delete key with jhash2 */ @@ -1520,7 +1633,7 @@ test_hash_add_delete_jhash2(void) hash_params_ex.name = "hash_test_jhash2"; hash_params_ex.key_len = 4; - hash_params_ex.hash_func = (rte_hash_function)rte_jhash_32b; + hash_params_ex.hash_func = (rte_hash_function)test_jhash_32b; handle = rte_hash_create(&hash_params_ex); if (handle == NULL) { @@ -1559,7 +1672,7 @@ test_hash_add_delete_2_jhash2(void) hash_params_ex.name = "hash_test_2_jhash2"; hash_params_ex.key_len = 8; - hash_params_ex.hash_func = (rte_hash_function)rte_jhash_32b; + hash_params_ex.hash_func = (rte_hash_function)test_jhash_32b; handle = rte_hash_create(&hash_params_ex); if (handle == NULL) @@ -1717,12 +1830,373 @@ fail_jhash_3word: return ret; } +static struct rte_hash *g_handle; +static struct rte_rcu_qsbr *g_qsv; +static volatile uint8_t writer_done; +struct flow_key g_rand_keys[9]; + +/* + * rte_hash_rcu_qsbr_add positive and negative tests. + * - Add RCU QSBR variable to Hash + * - Add another RCU QSBR variable to Hash + * - Check returns + */ +static int +test_hash_rcu_qsbr_add(void) +{ + size_t sz; + struct rte_rcu_qsbr *qsv2 = NULL; + int32_t status; + struct rte_hash_rcu_config rcu_cfg = {0}; + struct rte_hash_parameters params; + + printf("\n# Running RCU QSBR add tests\n"); + memcpy(¶ms, &ut_params, sizeof(params)); + params.name = "test_hash_rcu_qsbr_add"; + params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF | + RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD; + g_handle = rte_hash_create(¶ms); + RETURN_IF_ERROR_RCU_QSBR(g_handle == NULL, "Hash creation failed"); + + /* Create RCU QSBR variable */ + sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE); + g_qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, + RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY); + RETURN_IF_ERROR_RCU_QSBR(g_qsv == NULL, + "RCU QSBR variable creation failed"); + + status = rte_rcu_qsbr_init(g_qsv, RTE_MAX_LCORE); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "RCU QSBR variable initialization failed"); + + rcu_cfg.v = g_qsv; + /* Invalid QSBR mode */ + rcu_cfg.mode = 0xff; + status = rte_hash_rcu_qsbr_add(g_handle, &rcu_cfg); + RETURN_IF_ERROR_RCU_QSBR(status == 0, "Invalid QSBR mode test failed"); + + rcu_cfg.mode = RTE_HASH_QSBR_MODE_DQ; + /* Attach RCU QSBR to hash table */ + status = rte_hash_rcu_qsbr_add(g_handle, &rcu_cfg); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "Attach RCU QSBR to hash table failed"); + + /* Create and attach another RCU QSBR to hash table */ + qsv2 = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, + RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY); + RETURN_IF_ERROR_RCU_QSBR(qsv2 == NULL, + "RCU QSBR variable creation failed"); + + rcu_cfg.v = qsv2; + rcu_cfg.mode = RTE_HASH_QSBR_MODE_SYNC; + status = rte_hash_rcu_qsbr_add(g_handle, &rcu_cfg); + rte_free(qsv2); + RETURN_IF_ERROR_RCU_QSBR(status == 0, + "Attach RCU QSBR to hash table succeeded where failure" + " is expected"); + + rte_hash_free(g_handle); + rte_free(g_qsv); + + return 0; +} + +/* + * rte_hash_rcu_qsbr_add DQ mode functional test. + * Reader and writer are in the same thread in this test. + * - Create hash which supports maximum 8 (9 if ext bkt is enabled) entries + * - Add RCU QSBR variable to hash + * - Add 8 hash entries and fill the bucket + * - If ext bkt is enabled, add 1 extra entry that is available in the ext bkt + * - Register a reader thread (not a real thread) + * - Reader lookup existing entry + * - Writer deletes the entry + * - Reader lookup the entry + * - Writer re-add the entry (no available free index) + * - Reader report quiescent state and unregister + * - Writer re-add the entry + * - Reader lookup the entry + */ +static int +test_hash_rcu_qsbr_dq_mode(uint8_t ext_bkt) +{ + uint32_t total_entries = (ext_bkt == 0) ? 8 : 9; + + uint8_t hash_extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF; + + if (ext_bkt) + hash_extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + + struct rte_hash_parameters params_pseudo_hash = { + .name = "test_hash_rcu_qsbr_dq_mode", + .entries = total_entries, + .key_len = sizeof(struct flow_key), + .hash_func = pseudo_hash, + .hash_func_init_val = 0, + .socket_id = 0, + .extra_flag = hash_extra_flag, + }; + int pos[total_entries]; + int expected_pos[total_entries]; + unsigned int i; + size_t sz; + int32_t status; + struct rte_hash_rcu_config rcu_cfg = {0}; + + g_qsv = NULL; + g_handle = NULL; + + for (i = 0; i < total_entries; i++) { + g_rand_keys[i].port_dst = i; + g_rand_keys[i].port_src = i+1; + } + + if (ext_bkt) + printf("\n# Running RCU QSBR DQ mode functional test with" + " ext bkt\n"); + else + printf("\n# Running RCU QSBR DQ mode functional test\n"); + + g_handle = rte_hash_create(¶ms_pseudo_hash); + RETURN_IF_ERROR_RCU_QSBR(g_handle == NULL, "Hash creation failed"); + + /* Create RCU QSBR variable */ + sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE); + g_qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, + RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY); + RETURN_IF_ERROR_RCU_QSBR(g_qsv == NULL, + "RCU QSBR variable creation failed"); + + status = rte_rcu_qsbr_init(g_qsv, RTE_MAX_LCORE); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "RCU QSBR variable initialization failed"); + + rcu_cfg.v = g_qsv; + rcu_cfg.mode = RTE_HASH_QSBR_MODE_DQ; + /* Attach RCU QSBR to hash table */ + status = rte_hash_rcu_qsbr_add(g_handle, &rcu_cfg); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "Attach RCU QSBR to hash table failed"); + + /* Fill bucket */ + for (i = 0; i < total_entries; i++) { + pos[i] = rte_hash_add_key(g_handle, &g_rand_keys[i]); + print_key_info("Add", &g_rand_keys[i], pos[i]); + RETURN_IF_ERROR_RCU_QSBR(pos[i] < 0, + "failed to add key (pos[%u]=%d)", i, + pos[i]); + expected_pos[i] = pos[i]; + } + + /* Register pseudo reader */ + status = rte_rcu_qsbr_thread_register(g_qsv, 0); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "RCU QSBR thread registration failed"); + rte_rcu_qsbr_thread_online(g_qsv, 0); + + /* Lookup */ + pos[0] = rte_hash_lookup(g_handle, &g_rand_keys[0]); + print_key_info("Lkp", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] != expected_pos[0], + "failed to find correct key (pos[%u]=%d)", 0, + pos[0]); + + /* Writer update */ + pos[0] = rte_hash_del_key(g_handle, &g_rand_keys[0]); + print_key_info("Del", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] != expected_pos[0], + "failed to del correct key (pos[%u]=%d)", 0, + pos[0]); + + /* Lookup */ + pos[0] = rte_hash_lookup(g_handle, &g_rand_keys[0]); + print_key_info("Lkp", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] != -ENOENT, + "found deleted key (pos[%u]=%d)", 0, pos[0]); + + /* Fill bucket */ + pos[0] = rte_hash_add_key(g_handle, &g_rand_keys[0]); + print_key_info("Add", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] != -ENOSPC, + "Added key successfully (pos[%u]=%d)", 0, pos[0]); + + /* Reader quiescent */ + rte_rcu_qsbr_quiescent(g_qsv, 0); + + /* Fill bucket */ + pos[0] = rte_hash_add_key(g_handle, &g_rand_keys[0]); + print_key_info("Add", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] < 0, + "failed to add key (pos[%u]=%d)", 0, pos[0]); + expected_pos[0] = pos[0]; + + rte_rcu_qsbr_thread_offline(g_qsv, 0); + (void)rte_rcu_qsbr_thread_unregister(g_qsv, 0); + + /* Lookup */ + pos[0] = rte_hash_lookup(g_handle, &g_rand_keys[0]); + print_key_info("Lkp", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] != expected_pos[0], + "failed to find correct key (pos[%u]=%d)", 0, + pos[0]); + + rte_hash_free(g_handle); + rte_free(g_qsv); + return 0; + +} + +/* Report quiescent state interval every 1024 lookups. Larger critical + * sections in reader will result in writer polling multiple times. + */ +#define QSBR_REPORTING_INTERVAL 1024 +#define WRITER_ITERATIONS 512 + +/* + * Reader thread using rte_hash data structure with RCU. + */ +static int +test_hash_rcu_qsbr_reader(void *arg) +{ + int i; + + RTE_SET_USED(arg); + /* Register this thread to report quiescent state */ + (void)rte_rcu_qsbr_thread_register(g_qsv, 0); + rte_rcu_qsbr_thread_online(g_qsv, 0); + + do { + for (i = 0; i < QSBR_REPORTING_INTERVAL; i++) + rte_hash_lookup(g_handle, &g_rand_keys[0]); + + /* Update quiescent state */ + rte_rcu_qsbr_quiescent(g_qsv, 0); + } while (!writer_done); + + rte_rcu_qsbr_thread_offline(g_qsv, 0); + (void)rte_rcu_qsbr_thread_unregister(g_qsv, 0); + + return 0; +} + +/* + * rte_hash_rcu_qsbr_add sync mode functional test. + * 1 Reader and 1 writer. They cannot be in the same thread in this test. + * - Create hash which supports maximum 8 (9 if ext bkt is enabled) entries + * - Add RCU QSBR variable to hash + * - Register a reader thread. Reader keeps looking up a specific key. + * - Writer keeps adding and deleting a specific key. + */ +static int +test_hash_rcu_qsbr_sync_mode(uint8_t ext_bkt) +{ + uint32_t total_entries = (ext_bkt == 0) ? 8 : 9; + + uint8_t hash_extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF; + + if (ext_bkt) + hash_extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + + struct rte_hash_parameters params_pseudo_hash = { + .name = "test_hash_rcu_qsbr_sync_mode", + .entries = total_entries, + .key_len = sizeof(struct flow_key), + .hash_func = pseudo_hash, + .hash_func_init_val = 0, + .socket_id = 0, + .extra_flag = hash_extra_flag, + }; + int pos[total_entries]; + int expected_pos[total_entries]; + unsigned int i; + size_t sz; + int32_t status; + struct rte_hash_rcu_config rcu_cfg = {0}; + + g_qsv = NULL; + g_handle = NULL; + + for (i = 0; i < total_entries; i++) { + g_rand_keys[i].port_dst = i; + g_rand_keys[i].port_src = i+1; + } + + if (ext_bkt) + printf("\n# Running RCU QSBR sync mode functional test with" + " ext bkt\n"); + else + printf("\n# Running RCU QSBR sync mode functional test\n"); + + g_handle = rte_hash_create(¶ms_pseudo_hash); + RETURN_IF_ERROR_RCU_QSBR(g_handle == NULL, "Hash creation failed"); + + /* Create RCU QSBR variable */ + sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE); + g_qsv = (struct rte_rcu_qsbr *)rte_zmalloc_socket(NULL, sz, + RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY); + RETURN_IF_ERROR_RCU_QSBR(g_qsv == NULL, + "RCU QSBR variable creation failed"); + + status = rte_rcu_qsbr_init(g_qsv, RTE_MAX_LCORE); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "RCU QSBR variable initialization failed"); + + rcu_cfg.v = g_qsv; + rcu_cfg.mode = RTE_HASH_QSBR_MODE_SYNC; + /* Attach RCU QSBR to hash table */ + status = rte_hash_rcu_qsbr_add(g_handle, &rcu_cfg); + RETURN_IF_ERROR_RCU_QSBR(status != 0, + "Attach RCU QSBR to hash table failed"); + + /* Launch reader thread */ + rte_eal_remote_launch(test_hash_rcu_qsbr_reader, NULL, + rte_get_next_lcore(-1, 1, 0)); + + /* Fill bucket */ + for (i = 0; i < total_entries; i++) { + pos[i] = rte_hash_add_key(g_handle, &g_rand_keys[i]); + print_key_info("Add", &g_rand_keys[i], pos[i]); + RETURN_IF_ERROR_RCU_QSBR(pos[i] < 0, + "failed to add key (pos[%u]=%d)", i, pos[i]); + expected_pos[i] = pos[i]; + } + writer_done = 0; + + /* Writer Update */ + for (i = 0; i < WRITER_ITERATIONS; i++) { + expected_pos[0] = pos[0]; + pos[0] = rte_hash_del_key(g_handle, &g_rand_keys[0]); + print_key_info("Del", &g_rand_keys[0], status); + RETURN_IF_ERROR_RCU_QSBR(pos[0] != expected_pos[0], + "failed to del correct key (pos[%u]=%d)" + , 0, pos[0]); + + pos[0] = rte_hash_add_key(g_handle, &g_rand_keys[0]); + print_key_info("Add", &g_rand_keys[0], pos[0]); + RETURN_IF_ERROR_RCU_QSBR(pos[0] < 0, + "failed to add key (pos[%u]=%d)", 0, + pos[0]); + } + + writer_done = 1; + /* Wait until reader exited. */ + rte_eal_mp_wait_lcore(); + + rte_hash_free(g_handle); + rte_free(g_qsv); + + return 0; + +} + /* * Do all unit and performance tests. */ static int test_hash(void) { + RTE_BUILD_BUG_ON(sizeof(struct flow_key) % sizeof(uint32_t) != 0); + if (test_add_delete() < 0) return -1; if (test_hash_add_delete_jhash2() < 0) @@ -1743,6 +2217,8 @@ test_hash(void) return -1; if (test_add_update_delete_free() < 0) return -1; + if (test_add_delete_free_lf() < 0) + return -1; if (test_five_keys() < 0) return -1; if (test_full_bucket() < 0) @@ -1776,6 +2252,21 @@ test_hash(void) if (test_crc32_hash_alg_equiv() < 0) return -1; + if (test_hash_rcu_qsbr_add() < 0) + return -1; + + if (test_hash_rcu_qsbr_dq_mode(0) < 0) + return -1; + + if (test_hash_rcu_qsbr_dq_mode(1) < 0) + return -1; + + if (test_hash_rcu_qsbr_sync_mode(0) < 0) + return -1; + + if (test_hash_rcu_qsbr_sync_mode(1) < 0) + return -1; + return 0; }