@@ -2183,6 +2183,91 @@ test_hash_rcu_qsbr_sync_mode(uint8_t ext_bkt)
}
+/*
+ * rte_hash_rcu_qsbr_dq_reclaim unit test.
+ */
+static int
+test_hash_rcu_qsbr_dq_reclaim(void)
+{
+ size_t sz;
+ int32_t status;
+ unsigned int total_entries = 8;
+ unsigned int freed, pending, available;
+ uint32_t reclaim_keys[8] = {10, 11, 12, 13, 14, 15, 16, 17};
+ struct rte_hash_rcu_config rcu_cfg = {0};
+ struct rte_hash_parameters hash_params = {
+ .name = "test_hash_rcu_qsbr_dq_reclaim",
+ .entries = total_entries,
+ .key_len = sizeof(uint32_t),
+ .hash_func = NULL,
+ .hash_func_init_val = 0,
+ .socket_id = 0,
+ };
+
+ hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF;
+
+ g_qsv = NULL;
+ g_handle = NULL;
+
+ printf("\n# Running RCU QSBR DQ mode, reclaim defer queue functional test\n");
+
+ g_handle = rte_hash_create(&hash_params);
+ 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.dq_size = total_entries;
+ 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");
+
+ /* 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);
+
+ /* Fill half of the hash table */
+ for (size_t i = 0; i < total_entries / 2; i++)
+ status = rte_hash_add_key(g_handle, &reclaim_keys[i]);
+
+ /* Try to put these elements into the defer queue*/
+ for (size_t i = 0; i < total_entries / 2; i++)
+ rte_hash_del_key(g_handle, &reclaim_keys[i]);
+
+ /* Reader quiescent */
+ rte_rcu_qsbr_quiescent(g_qsv, 0);
+
+ status = rte_hash_add_key(g_handle, &reclaim_keys[0]);
+ RETURN_IF_ERROR_RCU_QSBR(status < 0, "failed to add key (pos[%u]=%d)", 0, status);
+
+ /* This should be (total_entries / 2) + 1 (last add) */
+ unsigned int hash_size = rte_hash_count(g_handle);
+
+ /* Freed size should be (total_entries / 2) */
+ rte_hash_rcu_qsbr_dq_reclaim(g_handle, &freed, &pending, &available);
+
+ rte_hash_free(g_handle);
+ rte_free(g_qsv);
+
+ if (hash_size != (total_entries / 2 + 1) || freed != (total_entries / 2)) {
+ printf("Failed to reclaim defer queue\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
/*
* Do all unit and performance tests.
*/
@@ -2261,6 +2346,9 @@ test_hash(void)
if (test_hash_rcu_qsbr_sync_mode(1) < 0)
return -1;
+ if (test_hash_rcu_qsbr_dq_reclaim() < 0)
+ return -1;
+
return 0;
}
@@ -1588,6 +1588,29 @@ rte_hash_rcu_qsbr_add(struct rte_hash *h, struct rte_hash_rcu_config *cfg)
return 0;
}
+int
+rte_hash_rcu_qsbr_dq_reclaim(struct rte_hash *h, unsigned int *freed,
+ unsigned int *pending, unsigned int *available)
+{
+ int ret;
+
+ if (h == NULL || h->hash_rcu_cfg == NULL) {
+ rte_errno = EINVAL;
+ return 1;
+ }
+
+ ret = rte_rcu_qsbr_dq_reclaim(h->dq, h->hash_rcu_cfg->max_reclaim_size,
+ freed, pending, available);
+ if (ret != 0) {
+ HASH_LOG(ERR,
+ "%s: could not reclaim the defer queue in hash table",
+ __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
static inline void
remove_entry(const struct rte_hash *h, struct rte_hash_bucket *bkt,
unsigned int i)
@@ -674,6 +674,30 @@ rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32
*/
int rte_hash_rcu_qsbr_add(struct rte_hash *h, struct rte_hash_rcu_config *cfg);
+/**
+ * Reclaim resources from the defer queue.
+ * This API reclaim the resources from the defer queue if rcu is enabled.
+ *
+ * @param h
+ * The hash object to reclaim resources.
+ * @param freed
+ * Number of resources that were freed.
+ * @param pending
+ * Number of resources pending on the defer queue.
+ * This number might not be accurate if multi-thread safety is configured.
+ * @param available
+ * Number of resources that can be added to the defer queue.
+ * This number might not be accurate if multi-thread safety is configured.
+ * @return
+ * On success - 0
+ * On error - 1 with error code set in rte_errno.
+ * Possible rte_errno codes are:
+ * - EINVAL - invalid pointer
+ */
+__rte_experimental
+int rte_hash_rcu_qsbr_dq_reclaim(struct rte_hash *h, unsigned int *freed,
+ unsigned int *pending, unsigned int *available);
+
#ifdef __cplusplus
}
#endif
@@ -53,3 +53,10 @@ INTERNAL {
rte_thash_gfni_stub;
rte_thash_gfni_bulk_stub;
};
+
+EXPERIMENTAL {
+ global:
+
+ # added in 24.07
+ rte_hash_rcu_qsbr_dq_reclaim;
+};