} \
} 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.
/* 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,
/* 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,
{
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]);
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
*
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,
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,
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");
.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
*/
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) {
ret = 0;
fail_jhash2:
- if (handle != NULL)
- rte_hash_free(handle);
+ rte_hash_free(handle);
return ret;
}
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)
ret = 0;
fail_2_jhash2:
- if (handle != NULL)
- rte_hash_free(handle);
+ rte_hash_free(handle);
return ret;
}
ret = 0;
fail_jhash_1word:
- if (handle != NULL)
- rte_hash_free(handle);
+ rte_hash_free(handle);
return ret;
}
ret = 0;
fail_jhash_2word:
- if (handle != NULL)
- rte_hash_free(handle);
+ rte_hash_free(handle);
return ret;
}
ret = 0;
fail_jhash_3word:
- if (handle != NULL)
- rte_hash_free(handle);
+ rte_hash_free(handle);
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)
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)
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;
}