[dpdk-dev] [RFC] latencystats: added new library for latency stats

Reshma Pattan reshma.pattan at intel.com
Mon Sep 26 18:39:40 CEST 2016


Library is designed to calculate latency stats and report them to the
application when queried. Library measures minimum, average, maximum
latencies and jitter in nano seconds.
Current implementation supports global latency stats, i.e. per application
stats.

APIs:

Added APIs to initialize and un initialize latency stats
calculation.
Added API to retrieve latency stats.

Functionality:

*Library will register ethdev Rx/Tx callbacks for each active port,
queue combinations.
*Library will register latency stats names with new stats library, which is under
design for now.
*Rx packets will be marked with time stamp on each sampling interval.
*On Tx side, packets with time stamp will be considered for calculating
the minimum, maximum, average latencies and jitter.
*Average latency is calculated by summing all the latencies measured for each
time stamped packet and dividing that by total timestamped packets.
*Minimum and maximum latencies will be low and high latency values observed
so far.
*Jitter calculation is kept simple which is variation in latest
two consecutive latencies.
*Measured stats can be retrieved via get API of the libray (or)
by calling generic get API of the new stats library, in this case
callback is provided to update the stats  into new stats library.

Signed-off-by: Reshma Pattan <reshma.pattan at intel.com>
---
 config/common_base                         |   5 +
 examples/skeleton/basicfwd.c               |  33 ++++-
 lib/Makefile                               |   1 +
 lib/librte_latencystats/Makefile           |  55 +++++++
 lib/librte_latencystats/rte_latencystats.c | 226 +++++++++++++++++++++++++++++
 lib/librte_latencystats/rte_latencystats.h | 109 ++++++++++++++
 lib/librte_mbuf/rte_mbuf.h                 |   3 +
 mk/rte.app.mk                              |   1 +
 8 files changed, 430 insertions(+), 3 deletions(-)
 create mode 100644 lib/librte_latencystats/Makefile
 create mode 100644 lib/librte_latencystats/rte_latencystats.c
 create mode 100644 lib/librte_latencystats/rte_latencystats.h

diff --git a/config/common_base b/config/common_base
index 01f94ab..0edd888 100644
--- a/config/common_base
+++ b/config/common_base
@@ -546,6 +546,11 @@ CONFIG_RTE_KNI_VHOST_DEBUG_TX=n
 CONFIG_RTE_LIBRTE_PDUMP=y
 
 #
+# Compile the xstats library
+#
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+
+#
 # Compile vhost library
 # fuse-devel is needed to run vhost-cuse.
 # fuse-devel enables user space char driver development
diff --git a/examples/skeleton/basicfwd.c b/examples/skeleton/basicfwd.c
index c89822c..9f7514a 100644
--- a/examples/skeleton/basicfwd.c
+++ b/examples/skeleton/basicfwd.c
@@ -33,11 +33,15 @@
 
 #include <stdint.h>
 #include <inttypes.h>
+#include <stdbool.h>
+#include <signal.h>
+
 #include <rte_eal.h>
 #include <rte_ethdev.h>
 #include <rte_cycles.h>
 #include <rte_lcore.h>
 #include <rte_mbuf.h>
+#include <rte_latencystats.h>
 
 #define RX_RING_SIZE 128
 #define TX_RING_SIZE 512
@@ -46,10 +50,22 @@
 #define MBUF_CACHE_SIZE 250
 #define BURST_SIZE 32
 
+static volatile bool force_quit;
 static const struct rte_eth_conf port_conf_default = {
 	.rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
 };
 
+static void
+signal_handler(int signum)
+{
+        if (signum == SIGINT || signum == SIGTERM) {
+                printf("\n\nSignal %d received, preparing to exit...\n",
+                                signum);
+                force_quit = true;
+        }
+}
+
+
 /* basicfwd.c: Basic DPDK skeleton forwarding example. */
 
 /*
@@ -113,7 +129,7 @@ port_init(uint8_t port, struct rte_mempool *mbuf_pool)
  * The lcore main. This is the main thread that does the work, reading from
  * an input port and writing to an output port.
  */
-static __attribute__((noreturn)) void
+static void
 lcore_main(void)
 {
 	const uint8_t nb_ports = rte_eth_dev_count();
@@ -135,7 +151,7 @@ lcore_main(void)
 			rte_lcore_id());
 
 	/* Run until the application is quit or killed. */
-	for (;;) {
+	while (!force_quit) {
 		/*
 		 * Receive packets on a port and forward them on the paired
 		 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
@@ -183,6 +199,9 @@ main(int argc, char *argv[])
 	argc -= ret;
 	argv += ret;
 
+	force_quit = false;
+	signal(SIGINT, signal_handler);
+	signal(SIGTERM, signal_handler);
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count();
 	if (nb_ports < 2 || (nb_ports & 1))
@@ -194,7 +213,6 @@ main(int argc, char *argv[])
 
 	if (mbuf_pool == NULL)
 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
-
 	/* Initialize all ports. */
 	for (portid = 0; portid < nb_ports; portid++)
 		if (port_init(portid, mbuf_pool) != 0)
@@ -204,8 +222,17 @@ main(int argc, char *argv[])
 	if (rte_lcore_count() > 1)
 		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
 
+	rte_latencystats_init(1,NULL);
 	/* Call lcore_main on the master core only. */
 	lcore_main();
+	rte_latencystats_uninit();
+	struct rte_latency_stats lat_stats;
+	memset(&lat_stats, 0, sizeof(struct rte_latency_stats));
+	rte_latencystats_get(&lat_stats);
+	printf("min_latency_ns=%.4f, avg_latency_ns=%.4f, max_latency_ns=%.4f\n, jitter_ns=%.4f",
+		lat_stats.min_latency, lat_stats.avg_latency,
+		lat_stats.max_latency, lat_stats.jitter);
+
 
 	return 0;
 }
diff --git a/lib/Makefile b/lib/Makefile
index cbaeefa..7af7e1c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -59,6 +59,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline
 DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder
 DIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += librte_pdump
 DIRS-$(CONFIG_RTE_LIBRTE_STATS) += librte_stats
+DIRS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) += librte_latencystats
 
 ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y)
 DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni
diff --git a/lib/librte_latencystats/Makefile b/lib/librte_latencystats/Makefile
new file mode 100644
index 0000000..8ab8ba2
--- /dev/null
+++ b/lib/librte_latencystats/Makefile
@@ -0,0 +1,55 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016 Intel Corporation. All rights reserved.
+#   All rights reserved.
+#
+#   Redistribution and use in source and binary forms, with or without
+#   modification, are permitted provided that the following conditions
+#   are met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above copyright
+#       notice, this list of conditions and the following disclaimer in
+#       the documentation and/or other materials provided with the
+#       distribution.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# library name
+LIB = librte_latencystats.a
+
+CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3
+
+EXPORT_MAP := rte_latencystats_version.map
+
+LIBABIVER := 1
+
+# all source are stored in SRCS-y
+SRCS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) := rte_latencystats.c
+
+# install this header file
+SYMLINK-$(CONFIG_RTE_LIBRTE_LATENCY_STATS)-include := rte_latencystats.h
+
+# this lib depends upon:
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) += lib/librte_mbuf
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) += lib/librte_eal
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) += lib/librte_ether
+DEPDIRS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS) += lib/librte_stats
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/lib/librte_latencystats/rte_latencystats.c b/lib/librte_latencystats/rte_latencystats.c
new file mode 100644
index 0000000..71dbfa8
--- /dev/null
+++ b/lib/librte_latencystats/rte_latencystats.c
@@ -0,0 +1,226 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <math.h>
+
+#include <rte_mbuf.h>
+#include <rte_log.h>
+#include <rte_cycles.h>
+#include <rte_ethdev.h>
+#include <rte_stats.h>
+
+#include "rte_latencystats.h"
+
+/** Nano seconds per second */
+#define NS_PER_SEC 1E9
+
+/** Clock cycles per nano second */
+#define CYCLES_PER_NS (rte_get_timer_hz() / NS_PER_SEC)
+
+/* Macros for printing using RTE_LOG */
+#define RTE_LOGTYPE_LATENCY_STATS RTE_LOGTYPE_USER1
+
+struct rte_latency_stats glob_stats;
+static uint64_t sampIntvl;
+static uint64_t timer_tsc;
+static uint64_t prev_tsc;
+static uint64_t total_sampl_pkts;
+
+static struct rxtx_cbs {
+	struct rte_eth_rxtx_callback *cb;
+} rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT],
+	tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];
+
+struct latency_stats_nameoff {
+	char name[RTE_ETH_XSTATS_NAME_SIZE];
+	unsigned offset;
+};
+
+static const struct latency_stats_nameoff lat_stats_strings[] = {
+	{"min_latency_ns", offsetof(struct rte_latency_stats, min_latency)},
+	{"avg_latency_ns", offsetof(struct rte_latency_stats, avg_latency)},
+	{"max_latency_ns", offsetof(struct rte_latency_stats, max_latency)},
+	{"jitter_ns", offsetof(struct rte_latency_stats, jitter)},
+};
+
+#define NUM_LATENCY_STATS (sizeof(lat_stats_strings) / sizeof (lat_stats_strings[0]))
+
+static uint16_t
+add_time_stamps(uint8_t pid __rte_unused,
+		uint16_t qid __rte_unused,
+		struct rte_mbuf **pkts,
+		uint16_t nb_pkts,
+		uint16_t max_pkts __rte_unused,
+		void *user_cb __rte_unused)
+{
+	unsigned int i;
+	uint64_t diff_tsc, now;
+
+	/* for every sample interval,
+	* time stamp is marked on one received packet
+	*/
+	now = rte_rdtsc();
+	for (i = 0; i < nb_pkts; i++) {
+		diff_tsc = now - prev_tsc;
+		timer_tsc += diff_tsc;
+		if (timer_tsc >= sampIntvl) {
+			pkts[i]->time_arrived = now;
+			timer_tsc = 0;
+		}
+		prev_tsc = now;
+		now = rte_rdtsc();
+	}
+
+	return nb_pkts;
+}
+
+static uint16_t
+calc_latency(uint8_t pid __rte_unused,
+		uint16_t qid __rte_unused,
+		struct rte_mbuf **pkts,
+		uint16_t nb_pkts,
+		void *_ __rte_unused)
+{
+	unsigned int i;
+	float latency = 0, prev_latency = 0;
+	uint64_t now;
+
+	now = rte_rdtsc();
+	for (i = 0; i < nb_pkts; i++) {
+		if (pkts[i]->time_arrived) {
+			latency = now - pkts[i]->time_arrived;
+			/** Calculate jitter */
+			glob_stats.jitter = abs(latency - prev_latency);
+			prev_latency = latency;
+			/** Calculate min, avg and max latency */
+			if (glob_stats.min_latency == 0)
+				glob_stats.min_latency = latency;
+			else if (latency < glob_stats.min_latency)
+				glob_stats.min_latency = latency;
+			else if (latency > glob_stats.max_latency)
+				glob_stats.max_latency = latency;
+			glob_stats.avg_latency += latency;
+			total_sampl_pkts++;
+		}
+	}
+
+	return nb_pkts;
+}
+
+static int
+rte_get_latency_stats_cbfn(int pid __rte_unused, struct rte_stat_value *values,
+			uint16_t max_values, void *data __rte_unused)
+{
+	unsigned int i;
+	float *stats_ptr;
+
+	if (max_values > NUM_LATENCY_STATS)
+		return -ERANGE;
+
+	for (i = 0; i < NUM_LATENCY_STATS; i++) {
+		stats_ptr = RTE_PTR_ADD(&glob_stats, lat_stats_strings[i].offset);
+		values[i].key = i;
+		values[i].value = (int)floor(*stats_ptr);
+	}
+
+	return NUM_LATENCY_STATS;
+}
+
+void
+rte_latencystats_init(uint64_t samp_intvl,
+				rte_latency_stats_flow_type_fn user_cb)
+{
+	unsigned int i;
+	uint8_t pid;
+	uint16_t qid;
+	struct rxtx_cbs *cbs = NULL;
+	const uint8_t nb_ports = rte_eth_dev_count();
+
+	samp_intvl *= CYCLES_PER_NS;
+
+	/** Register latency metrics with stats library */
+	rte_stats_init();
+	for (i = 0; i < NUM_LATENCY_STATS; i++)
+		rte_stats_reg_metric(lat_stats_strings[i].name,
+					&rte_get_latency_stats_cbfn,
+					NULL);
+
+	/** Register Rx/Tx callbacks */
+	for (pid = 0; pid < nb_ports; pid++) {
+		struct rte_eth_dev_info dev_info;
+		rte_eth_dev_info_get(pid, &dev_info);
+		for (qid = 0; qid < dev_info.nb_rx_queues; qid++) {
+			cbs = &rx_cbs[pid][qid];
+			cbs->cb = rte_eth_add_first_rx_callback(pid, qid,
+					add_time_stamps, user_cb);
+			if (!cbs->cb)
+				RTE_LOG(INFO, LATENCY_STATS, "failed to "
+					"register Rx callback for pid=%d, "
+					"qid=%d\n", pid, qid);
+		}
+		for (qid = 0; qid < dev_info.nb_tx_queues; qid++) {
+			cbs = &tx_cbs[pid][qid];
+			cbs->cb =  rte_eth_add_tx_callback(pid, qid,
+					calc_latency, user_cb);
+			if (!cbs->cb)
+				RTE_LOG(INFO, LATENCY_STATS, "failed to "
+					"register Tx callback for pid=%d, "
+					"qid=%d\n", pid, qid);
+		}
+	}
+}
+
+void
+rte_latencystats_uninit(void)
+{
+	uint8_t pid;
+	uint16_t qid;
+	int ret = 0;
+	struct rxtx_cbs *cbs = NULL;
+	const uint8_t nb_ports = rte_eth_dev_count();
+
+	/** De register Rx/Tx callbacks */
+	for (pid = 0; pid < nb_ports; pid++) {
+		struct rte_eth_dev_info dev_info;
+		rte_eth_dev_info_get(pid, &dev_info);
+		for (qid = 0; qid < dev_info.nb_rx_queues; qid++) {
+			cbs = &rx_cbs[pid][qid];
+			ret = rte_eth_remove_rx_callback(pid, qid, cbs->cb);
+			if (ret)
+				RTE_LOG(INFO, LATENCY_STATS, "failed to "
+						"remove Rx callback for pid=%d, "
+						"qid=%d\n", pid, qid);
+		}
+		for (qid = 0; qid < dev_info.nb_tx_queues; qid++) {
+			cbs = &tx_cbs[pid][qid];
+			ret = rte_eth_remove_tx_callback(pid, qid, cbs->cb);
+			if (ret)
+				RTE_LOG(INFO, LATENCY_STATS, "failed to "
+						"remove Tx callback for pid=%d, "
+						"qid=%d\n", pid, qid);
+		}
+	}
+}
+
+int
+rte_latencystats_get(struct rte_latency_stats *stats)
+{
+	unsigned int i;
+	float *gstats_ptr = NULL;
+	float *stats_ptr = NULL;
+
+	if (stats == NULL)
+		return -EINVAL;
+
+	/* Retrieve latency statistics */
+	for (i = 0; i < NUM_LATENCY_STATS; i++) {
+		gstats_ptr = RTE_PTR_ADD(&glob_stats, lat_stats_strings[i].offset);
+		stats_ptr = RTE_PTR_ADD(stats, lat_stats_strings[i].offset);
+		if (i == 1)
+			*stats_ptr = (*gstats_ptr)/(CYCLES_PER_NS*total_sampl_pkts);
+		else
+			*stats_ptr = (*gstats_ptr)/(CYCLES_PER_NS);
+	}
+
+	return 0;
+}
diff --git a/lib/librte_latencystats/rte_latencystats.h b/lib/librte_latencystats/rte_latencystats.h
new file mode 100644
index 0000000..f77faa0
--- /dev/null
+++ b/lib/librte_latencystats/rte_latencystats.h
@@ -0,0 +1,109 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_LATENCYSTATS_H_
+#define _RTE_LATENCYSTATS_H_
+
+/**
+ * @file
+ * RTE latency stats
+ *
+ * library to provide application and flow based latency stats.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rte_latency_stats {
+	float min_latency; /**< Minimum latency in nano seconds */
+	float avg_latency; /**< Average latency in nano seconds */
+	float max_latency; /**< Maximum latency in nano seconds */
+	float jitter; /** Latency variation between to packets */
+};
+
+/**
+ * Function type used for identifting flow types of a Rx packet.
+ *
+ * The callback function is called on Rx for each packet.
+ * This function is used for flow based latency calculations.
+ *
+ * @param pkt
+ *   Packet that has to be identified with its flow types.
+ * @param user_param
+ *   The arbitrary user parameter passed in by the application when the callback
+ *   was originally configured.
+ * @return
+ *   The flow_mask, representing the multiple flow types of a packet.
+ */
+typedef uint16_t (*rte_latency_stats_flow_type_fn)(struct rte_mbuf *pkt,
+							void *user_param);
+
+/**
+ * @internal
+ *  Registers Rx/Tx callbacks for each active port, queue.
+ *
+ * @param sampIntvl
+ *  Sampling time period at which packet should be marked with time stamp.
+ * @param userCb
+ *  User callback to be called to get flow types of a packet.
+ *  Used for flow based latency calculation.
+ *  If the value is NULL, global stats will be calculated,
+ *  else flow based stats will be calculated.
+ */
+void rte_latencystats_init(uint64_t samp_intvl,
+				rte_latency_stats_flow_type_fn user_cb);
+
+/**
+ * @internal
+ *  Removes registered Rx/Tx callbacks for each active port, queue.
+ */
+void rte_latencystats_uninit(void);
+
+/**
+ * @internal
+ *  Retrieves latency stats.
+ *
+ * @param stats
+ *  A pointer to rte_latency_stats type to be filled with latency stats.
+ *  @return
+ *  -EINVAL on failure.
+ *  0 on success.
+ */
+int rte_latencystats_get(struct rte_latency_stats *stats);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_LATENCYSTATS_H_ */
diff --git a/lib/librte_mbuf/rte_mbuf.h b/lib/librte_mbuf/rte_mbuf.h
index 23b7bf8..68bf6c4 100644
--- a/lib/librte_mbuf/rte_mbuf.h
+++ b/lib/librte_mbuf/rte_mbuf.h
@@ -891,6 +891,9 @@ struct rte_mbuf {
 
 	/** Timesync flags for use with IEEE1588. */
 	uint16_t timesync;
+
+	/** Timestamp for measuring latency stats. */
+	uint64_t time_arrived;
 } __rte_cache_aligned;
 
 /**
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 0c92af0..44116bb 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -69,6 +69,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_TABLE)          += -lrte_table
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PORT)           += -lrte_port
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP)          += -lrte_pdump
+_LDLIBS-$(CONFIG_RTE_LIBRTE_LATENCY_STATS)   += -lrte_latencystats
 _LDLIBS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR)    += -lrte_distributor
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 _LDLIBS-$(CONFIG_RTE_LIBRTE_IP_FRAG)        += -lrte_ip_frag
-- 
2.7.4



More information about the dev mailing list