[dpdk-dev] [RFC 2/3] tqs: add thread quiescent state library

Honnappa Nagarahalli honnappa.nagarahalli at arm.com
Thu Nov 22 04:30:54 CET 2018


Add Thread Quiescent State (TQS) library. This library helps identify
the quiescent state of the reader threads so that the writers
can free the memory associated with the lock less data structures.

Signed-off-by: Honnappa Nagarahalli <honnappa.nagarahalli at arm.com>
Reviewed-by: Steve Capper <steve.capper at arm.com>
Reviewed-by: Gavin Hu <gavin.hu at arm.com>
---
 config/common_base                 |   6 +
 lib/Makefile                       |   2 +
 lib/librte_tqs/Makefile            |  23 ++
 lib/librte_tqs/meson.build         |   5 +
 lib/librte_tqs/rte_tqs.c           | 249 ++++++++++++++++++++
 lib/librte_tqs/rte_tqs.h           | 352 +++++++++++++++++++++++++++++
 lib/librte_tqs/rte_tqs_version.map |  16 ++
 lib/meson.build                    |   2 +-
 mk/rte.app.mk                      |   1 +
 9 files changed, 655 insertions(+), 1 deletion(-)
 create mode 100644 lib/librte_tqs/Makefile
 create mode 100644 lib/librte_tqs/meson.build
 create mode 100644 lib/librte_tqs/rte_tqs.c
 create mode 100644 lib/librte_tqs/rte_tqs.h
 create mode 100644 lib/librte_tqs/rte_tqs_version.map

diff --git a/config/common_base b/config/common_base
index d12ae98bc..af40a9f81 100644
--- a/config/common_base
+++ b/config/common_base
@@ -792,6 +792,12 @@ CONFIG_RTE_LIBRTE_LATENCY_STATS=y
 #
 CONFIG_RTE_LIBRTE_TELEMETRY=n
 
+#
+# Compile librte_tqs
+#
+CONFIG_RTE_LIBRTE_TQS=y
+CONFIG_RTE_LIBRTE_TQS_DEBUG=n
+
 #
 # Compile librte_lpm
 #
diff --git a/lib/Makefile b/lib/Makefile
index b7370ef97..7095eac88 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -108,6 +108,8 @@ DIRS-$(CONFIG_RTE_LIBRTE_BPF) += librte_bpf
 DEPDIRS-librte_bpf := librte_eal librte_mempool librte_mbuf librte_ethdev
 DIRS-$(CONFIG_RTE_LIBRTE_TELEMETRY) += librte_telemetry
 DEPDIRS-librte_telemetry := librte_eal librte_metrics librte_ethdev
+DIRS-$(CONFIG_RTE_LIBRTE_TQS) += librte_tqs
+DEPDIRS-librte_tqs := librte_eal
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_tqs/Makefile b/lib/librte_tqs/Makefile
new file mode 100644
index 000000000..059de53e2
--- /dev/null
+++ b/lib/librte_tqs/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Arm Limited
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_tqs.a
+
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
+LDLIBS += -lrte_eal
+
+EXPORT_MAP := rte_tqs_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_RING) := rte_tqs.c
+
+# install includes
+SYMLINK-$(CONFIG_RTE_LIBRTE_RING)-include := rte_tqs.h
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_tqs/meson.build b/lib/librte_tqs/meson.build
new file mode 100644
index 000000000..dd696ab07
--- /dev/null
+++ b/lib/librte_tqs/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Arm Limited
+
+sources = files('rte_tqs.c')
+headers = files('rte_tqs.h')
diff --git a/lib/librte_tqs/rte_tqs.c b/lib/librte_tqs/rte_tqs.c
new file mode 100644
index 000000000..cbc36864e
--- /dev/null
+++ b/lib/librte_tqs/rte_tqs.c
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 2018 Arm Limited
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <rte_common.h>
+#include <rte_log.h>
+#include <rte_memory.h>
+#include <rte_malloc.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_atomic.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_errno.h>
+
+#include "rte_tqs.h"
+
+TAILQ_HEAD(rte_tqs_list, rte_tailq_entry);
+
+static struct rte_tailq_elem rte_tqs_tailq = {
+	.name = RTE_TAILQ_TQS_NAME,
+};
+EAL_REGISTER_TAILQ(rte_tqs_tailq)
+
+/* Allocate a new TQS variable with the name *name* in memory. */
+struct rte_tqs * __rte_experimental
+rte_tqs_alloc(const char *name, int socket_id, uint64_t lcore_mask)
+{
+	char tqs_name[RTE_TQS_NAMESIZE];
+	struct rte_tailq_entry *te, *tmp_te;
+	struct rte_tqs_list *tqs_list;
+	struct rte_tqs *v, *tmp_v;
+	int ret;
+
+	if (name == NULL) {
+		RTE_LOG(ERR, TQS, "Invalid input parameters\n");
+		rte_errno = -EINVAL;
+		return NULL;
+	}
+
+	te = rte_zmalloc("TQS_TAILQ_ENTRY", sizeof(*te), 0);
+	if (te == NULL) {
+		RTE_LOG(ERR, TQS, "Cannot reserve memory for tailq\n");
+		rte_errno = -ENOMEM;
+		return NULL;
+	}
+
+	snprintf(tqs_name, sizeof(tqs_name), "%s", name);
+	v = rte_zmalloc_socket(tqs_name, sizeof(struct rte_tqs),
+				RTE_CACHE_LINE_SIZE, socket_id);
+	if (v == NULL) {
+		RTE_LOG(ERR, TQS, "Cannot reserve memory for TQS variable\n");
+		rte_errno = -ENOMEM;
+		goto alloc_error;
+	}
+
+	ret = snprintf(v->name, sizeof(v->name), "%s", name);
+	if (ret < 0 || ret >= (int)sizeof(v->name)) {
+		rte_errno = -ENAMETOOLONG;
+		goto alloc_error;
+	}
+
+	te->data = (void *) v;
+	v->lcore_mask = lcore_mask;
+
+	rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	tqs_list = RTE_TAILQ_CAST(rte_tqs_tailq.head, rte_tqs_list);
+
+	/* Search if a TQS variable with the same name exists already */
+	TAILQ_FOREACH(tmp_te, tqs_list, next) {
+		tmp_v = (struct rte_tqs *) tmp_te->data;
+		if (strncmp(name, tmp_v->name, RTE_TQS_NAMESIZE) == 0)
+			break;
+	}
+
+	if (tmp_te != NULL) {
+		rte_errno = -EEXIST;
+		goto tqs_exist;
+	}
+
+	TAILQ_INSERT_TAIL(tqs_list, te, next);
+
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	return v;
+
+tqs_exist:
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+alloc_error:
+	rte_free(te);
+	rte_free(v);
+	return NULL;
+}
+
+/* De-allocate all the memory used by a TQS variable. */
+void __rte_experimental
+rte_tqs_free(struct rte_tqs *v)
+{
+	struct rte_tqs_list *tqs_list;
+	struct rte_tailq_entry *te;
+
+	/* Search for the TQS variable in tailq */
+	tqs_list = RTE_TAILQ_CAST(rte_tqs_tailq.head, rte_tqs_list);
+	rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	TAILQ_FOREACH(te, tqs_list, next) {
+		if (te->data == (void *) v)
+			break;
+	}
+
+	if (te != NULL)
+		TAILQ_REMOVE(tqs_list, te, next);
+	else
+		RTE_LOG(ERR, TQS, "TQS variable %s not found\n", v->name);
+
+	rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	rte_free(te);
+	rte_free(v);
+}
+
+/* Add a reader thread, running on an lcore, to the list of threads
+ * reporting their quiescent state on a TQS variable.
+ */
+int __rte_experimental
+rte_tqs_register_lcore(struct rte_tqs *v, unsigned int lcore_id)
+{
+	TQS_RETURN_IF_TRUE((v == NULL || lcore_id >= RTE_TQS_MAX_LCORE),
+				-EINVAL);
+
+	/* Worker thread has to count the quiescent states
+	 * only from the current value of token.
+	 */
+	v->w[lcore_id].cnt = v->token;
+
+	/* Release the store to initial TQS count so that workers
+	 * can use it immediately after this function returns.
+	 */
+	__atomic_fetch_or(&v->lcore_mask, (1UL << lcore_id), __ATOMIC_RELEASE);
+
+	return 0;
+}
+
+/* Remove a reader thread, running on an lcore, from the list of threads
+ * reporting their quiescent state on a TQS variable.
+ */
+int __rte_experimental
+rte_tqs_unregister_lcore(struct rte_tqs *v, unsigned int lcore_id)
+{
+	TQS_RETURN_IF_TRUE((v == NULL ||
+				lcore_id >= RTE_TQS_MAX_LCORE), -EINVAL);
+
+	/* This can be a relaxed store. Since this is an API, make sure
+	 * the store is not reordered with other memory operations.
+	 */
+	__atomic_fetch_and(&v->lcore_mask,
+				~(1UL << lcore_id), __ATOMIC_RELEASE);
+
+	return 0;
+}
+
+/* Search a TQS variable, given its name. */
+struct rte_tqs * __rte_experimental
+rte_tqs_lookup(const char *name)
+{
+	struct rte_tqs_list *tqs_list;
+	struct rte_tailq_entry *te;
+	struct rte_tqs *v;
+
+	if (name == NULL) {
+		RTE_LOG(ERR, TQS, "Invalid input parameters\n");
+		rte_errno = -EINVAL;
+		return NULL;
+	}
+
+	v = NULL;
+	tqs_list = RTE_TAILQ_CAST(rte_tqs_tailq.head, rte_tqs_list);
+
+	rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	/* find out tailq entry */
+	TAILQ_FOREACH(te, tqs_list, next) {
+		v = (struct rte_tqs *) te->data;
+		if (strncmp(name, v->name, RTE_TQS_NAMESIZE) == 0)
+			break;
+	}
+
+	if (te == NULL) {
+		rte_errno = -ENOENT;
+		v = NULL;
+	}
+
+	rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK);
+
+	return v;
+}
+
+/* Dump the details of a single TQS variables to a file. */
+void __rte_experimental
+rte_tqs_dump(FILE *f, struct rte_tqs *v)
+{
+	uint64_t tmp_mask;
+	uint32_t i;
+
+	TQS_ERR_LOG_IF_TRUE(v == NULL || f == NULL);
+
+	fprintf(f, "\nTQS <%s>@%p\n", v->name, v);
+	fprintf(f, "  lcore mask = 0x%lx\n", v->lcore_mask);
+	fprintf(f, "  token = %u\n", v->token);
+
+	if (v->lcore_mask != 0) {
+		fprintf(f, "Quiescent State Counts for readers:\n");
+		tmp_mask = v->lcore_mask;
+		while (tmp_mask) {
+			i = __builtin_ctz(tmp_mask);
+			fprintf(f, "lcore # = %d, count = %u\n",
+				i, v->w[i].cnt);
+			tmp_mask &= ~(1UL << i);
+		}
+	}
+}
+
+/* Dump the details of all the TQS variables to a file. */
+void __rte_experimental
+rte_tqs_list_dump(FILE *f)
+{
+	const struct rte_tailq_entry *te;
+	struct rte_tqs_list *tqs_list;
+
+	TQS_ERR_LOG_IF_TRUE(f == NULL);
+
+	tqs_list = RTE_TAILQ_CAST(rte_tqs_tailq.head, rte_tqs_list);
+
+	rte_rwlock_read_lock(RTE_EAL_TAILQ_RWLOCK);
+
+	TAILQ_FOREACH(te, tqs_list, next) {
+		rte_tqs_dump(f, (struct rte_tqs *) te->data);
+	}
+
+	rte_rwlock_read_unlock(RTE_EAL_TAILQ_RWLOCK);
+}
diff --git a/lib/librte_tqs/rte_tqs.h b/lib/librte_tqs/rte_tqs.h
new file mode 100644
index 000000000..9136418d2
--- /dev/null
+++ b/lib/librte_tqs/rte_tqs.h
@@ -0,0 +1,352 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2018 Arm Limited
+ */
+
+#ifndef _RTE_TQS_H_
+#define _RTE_TQS_H_
+
+/**
+ * @file
+ * RTE Thread Quiescent State
+ *
+ * Thread Quiescent State (TQS) is any point in the thread execution
+ * where the thread does not hold a reference to shared memory, i.e.
+ * a non-critical section. A critical section for a data structure can
+ * be a quiescent state for another data structure.
+ *
+ * An application can identify the quiescent state according to its
+ * needs. It can identify 1 quiescent state for each data structure or
+ * 1 quiescent state for a group/all of data structures.
+ *
+ * This library provides the flexibility for these use cases.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_lcore.h>
+
+#define RTE_TAILQ_TQS_NAME "RTE_TQS"
+
+/**< The maximum length of a TQS variable name. */
+#define RTE_TQS_NAMESIZE 32
+
+/**< Maximum number of lcores supported. */
+#if (RTE_MAX_LCORE > 64)
+#define RTE_TQS_MAX_LCORE 64
+#else
+#define RTE_TQS_MAX_LCORE RTE_MAX_LCORE
+#endif
+
+/* Macro for run-time checking of function parameters */
+#if defined(RTE_LIBRTE_TQS_DEBUG)
+#define TQS_RETURN_IF_TRUE(cond, retval) do { \
+	if ((cond)) \
+		return retval; \
+} while (0)
+#else
+#define TQS_RETURN_IF_TRUE(cond, retval)
+#endif
+
+/* Macro to log error message */
+#define TQS_ERR_LOG_IF_TRUE(cond) do { \
+	if ((cond)) { \
+		RTE_LOG(ERR, TQS, "Invalid parameters\n"); \
+		return; \
+	} \
+} while (0)
+
+/* Worker thread counter */
+struct rte_tqs_cnt {
+	volatile uint32_t cnt; /**< Quiescent state counter. */
+} __rte_cache_aligned;
+
+/**
+ * RTE Thread Quiescent State structure.
+ */
+struct rte_tqs {
+	char name[RTE_TQS_NAMESIZE];
+	/**< Name of the ring. */
+	uint64_t lcore_mask;
+	/**< Worker lcores reporting on this TQS */
+
+	uint32_t token __rte_cache_aligned;
+	/**< Counter to allow for multiple simultaneous TQS queries */
+
+	struct rte_tqs_cnt w[RTE_TQS_MAX_LCORE] __rte_cache_aligned;
+	/**< TQS counter for each worker thread, counts upto
+	 * current value of token.
+	 */
+} __rte_cache_aligned;
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Allocate a new TQS variable with the name *name* in memory.
+ *
+ * The TQS variable is added in RTE_TAILQ_TQS list.
+ *
+ * @param name
+ *   The name of the TQS variable.
+ * @param socket_id
+ *   The *socket_id* argument is the socket identifier in case of
+ *   NUMA. The value can be *SOCKET_ID_ANY* if there is no NUMA
+ *   constraint for the reserved zone.
+ * @param lcore_mask
+ *   Data plane reader threads in this mask will report their quiescent
+ *   state on this TQS variable.
+ * @return
+ *   On success, the pointer to the new allocated TQS variable. NULL on
+ *   error with rte_errno set appropriately. Possible errno values include:
+ *    - EINVAL - invalid input parameters
+ *    - ENAMETOOLONG - TQS variable name is longer than RTE_TQS_NAMESIZE
+ *    - EEXIST - a TQS variable with the same name already exists
+ *    - ENOMEM - no appropriate memory area found in which to create memzone
+ */
+struct rte_tqs * __rte_experimental
+rte_tqs_alloc(const char *name, int socket_id, uint64_t lcore_mask);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * De-allocate all the memory used by a TQS variable. It is the
+ * application's responsibility to make sure that no other thread
+ * is using the TQS variable.
+ *
+ * The TQS variable is removed from RTE_TAILQ_TQS list.
+ *
+ * @param v
+ *   TQS variable to free.
+ */
+void __rte_experimental rte_tqs_free(struct rte_tqs *v);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Add a worker thread, running on an lcore, to the list of threads
+ * reporting their quiescent state on a TQS variable.
+ *
+ * This is implemented as a lock-free function. It is multi-thread
+ * safe. This API can be called from the worker threads during
+ * initialization. Any ongoing TQS queries may wait for the
+ * status from this registered worker thread.
+ *
+ * @param v
+ *   TQS variable
+ * @param lcore_id
+ *   Worker thread on this lcore will report its quiescent state on
+ *   this TQS variable.
+ * @return
+ *   - 0 if registered successfully.
+ *   - -EINVAL if the parameters are invalid (debug mode compilation only).
+ */
+int __rte_experimental
+rte_tqs_register_lcore(struct rte_tqs *v, unsigned int lcore_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Remove a worker thread, running on an lcore, from the list of threads
+ * reporting their quiescent state on a TQS variable.
+ *
+ * This is implemented as a lock-free function. It is multi-thread safe.
+ * This API can be called from the worker threads during shutdown.
+ * Any ongoing TQS queries may stop waiting for the status from this
+ * unregistered worker thread.
+ *
+ * @param v
+ *   TQS variable
+ * @param lcore_id
+ *   Worker thread on this lcore will stop reporting its quiescent state
+ *   on this TQS variable.
+ * @return
+ *   - 0 if un-registered successfully.
+ *   - -EINVAL if the parameters are invalid (debug mode compilation only).
+ */
+int __rte_experimental
+rte_tqs_unregister_lcore(struct rte_tqs *v, unsigned int lcore_id);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Trigger the worker threads to report the quiescent state
+ * status.
+ *
+ * This is implemented as a lock-free function. It is multi-thread
+ * safe and can be called from the worker threads as well.
+ *
+ * @param v
+ *   TQS variable
+ * @param n
+ *   Expected number of times the quiescent state is entered
+ * @param t
+ *   - If successful, this is the token for this call of the API.
+ *     This should be passed to rte_tqs_check API.
+ * @return
+ *   - -EINVAL if the parameters are invalid (debug mode compilation only).
+ *   - 0 Otherwise and always (non-debug mode compilation).
+ */
+static __rte_always_inline int __rte_experimental
+rte_tqs_start(struct rte_tqs *v, unsigned int n, uint32_t *t)
+{
+	TQS_RETURN_IF_TRUE((v == NULL || t == NULL), -EINVAL);
+
+	/* This store release will ensure that changes to any data
+	 * structure are visible to the workers before the token
+	 * update is visible.
+	 */
+	*t = __atomic_add_fetch(&v->token, n, __ATOMIC_RELEASE);
+
+	return 0;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Update quiescent state for the worker thread on a lcore.
+ *
+ * This is implemented as a lock-free function. It is multi-thread safe.
+ * All the worker threads registered to report their quiescent state
+ * on the TQS variable must call this API.
+ *
+ * @param v
+ *   TQS variable
+ */
+static __rte_always_inline void __rte_experimental
+rte_tqs_update(struct rte_tqs *v, unsigned int lcore_id)
+{
+	uint32_t t;
+
+	TQS_ERR_LOG_IF_TRUE(v == NULL || lcore_id >= RTE_TQS_MAX_LCORE);
+
+	/* Load the token before the worker thread loads any other
+	 * (lock-free) data structure. This ensures that updates
+	 * to the data structures are visible if the update
+	 * to token is visible.
+	 */
+	t = __atomic_load_n(&v->token, __ATOMIC_ACQUIRE);
+	if (v->w[lcore_id].cnt != t)
+		v->w[lcore_id].cnt++;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Checks if all the worker threads have entered the quiescent state
+ * 'n' number of times. 'n' is provided in rte_tqs_start API.
+ *
+ * This is implemented as a lock-free function. It is multi-thread
+ * safe and can be called from the worker threads as well.
+ *
+ * @param v
+ *   TQS variable
+ * @param t
+ *   Token returned by rte_tqs_start API
+ * @param wait
+ *   If true, block till all the worker threads have completed entering
+ *   the quiescent state 'n' number of times
+ * @return
+ *   - 0 if all worker threads have NOT passed through specified number
+ *     of quiescent states.
+ *   - 1 if all worker threads have passed through specified number
+ *     of quiescent states.
+ *   - -EINVAL if the parameters are invalid (debug mode compilation only).
+ */
+static __rte_always_inline int __rte_experimental
+rte_tqs_check(struct rte_tqs *v, uint32_t t, bool wait)
+{
+	uint64_t l;
+	uint64_t lcore_mask;
+
+	TQS_RETURN_IF_TRUE((v == NULL), -EINVAL);
+
+	do {
+		/* Load the current lcore_mask before loading the
+		 * worker thread quiescent state counters.
+		 */
+		lcore_mask = __atomic_load_n(&v->lcore_mask, __ATOMIC_ACQUIRE);
+
+		while (lcore_mask) {
+			l = __builtin_ctz(lcore_mask);
+			if (v->w[l].cnt != t)
+				break;
+
+			lcore_mask &= ~(1UL << l);
+		}
+
+		if (lcore_mask == 0)
+			return 1;
+
+		rte_pause();
+	} while (wait);
+
+	return 0;
+}
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Search a TQS variable, given its name.
+ *
+ * It is multi-thread safe.
+ *
+ * @param name
+ *   The name of the TQS variable.
+ * @return
+ *   On success, the pointer to the TQS variable. NULL on
+ *   error with rte_errno set appropriately. Possible errno values include:
+ *    - EINVAL - invalid input parameters.
+ *    - ENOENT - entry not found.
+ */
+struct rte_tqs * __rte_experimental
+rte_tqs_lookup(const char *name);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump the details of a single TQS variables to a file.
+ *
+ * It is NOT multi-thread safe.
+ *
+ * @param f
+ *   A pointer to a file for output
+ * @param tqs
+ *   TQS variable
+ */
+void __rte_experimental
+rte_tqs_dump(FILE *f, struct rte_tqs *v);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Dump the details of all the TQS variables to a file.
+ *
+ * It is multi-thread safe.
+ *
+ * @param f
+ *   A pointer to a file for output
+ */
+void __rte_experimental
+rte_tqs_list_dump(FILE *f);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_TQS_H_ */
diff --git a/lib/librte_tqs/rte_tqs_version.map b/lib/librte_tqs/rte_tqs_version.map
new file mode 100644
index 000000000..2e4d5c094
--- /dev/null
+++ b/lib/librte_tqs/rte_tqs_version.map
@@ -0,0 +1,16 @@
+EXPERIMENTAL {
+	global:
+
+	rte_tqs_alloc;
+	rte_tqs_free;
+	rte_tqs_register_lcore;
+	rte_tqs_unregister_lcore;
+	rte_tqs_start;
+	rte_tqs_update;
+	rte_tqs_check;
+	rte_tqs_lookup;
+	rte_tqs_list_dump;
+	rte_tqs_dump;
+
+	local: *;
+};
diff --git a/lib/meson.build b/lib/meson.build
index bb7f443f9..ee19c483e 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -21,7 +21,7 @@ libraries = [ 'compat', # just a header, used for versioning
 	'gro', 'gso', 'ip_frag', 'jobstats',
 	'kni', 'latencystats', 'lpm', 'member',
 	'meter', 'power', 'pdump', 'rawdev',
-	'reorder', 'sched', 'security', 'vhost',
+	'reorder', 'sched', 'security', 'tqs', 'vhost',
 	# add pkt framework libs which use other libs from above
 	'port', 'table', 'pipeline',
 	# flow_classify lib depends on pkt framework table lib
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 3ebc4e64c..6e19e669a 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -92,6 +92,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
 _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_SCHED)          += -lrte_sched
+_LDLIBS-$(CONFIG_RTE_LIBRTE_TQS)            += -lrte_tqs
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
 _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI)            += -lrte_kni
-- 
2.17.1



More information about the dev mailing list