@@ -378,6 +378,7 @@ T: git://dpdk.org/next/dpdk-next-net
F: lib/librte_ethdev/
F: devtools/test-null.sh
F: doc/guides/prog_guide/switch_representation.rst
+F: app/test/test_ethdev*
Flow API
M: Ori Kam <orika@mellanox.com>
@@ -249,6 +249,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_SECURITY) += test_security.c
SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec.c test_ipsec_perf.c
SRCS-$(CONFIG_RTE_LIBRTE_IPSEC) += test_ipsec_sad.c
+
+SRCS-$(CONFIG_RTE_LIBRTE_ETHER) += test_ethdev_link.c
+
ifeq ($(CONFIG_RTE_LIBRTE_IPSEC),y)
LDLIBS += -lrte_ipsec
endif
@@ -38,6 +38,7 @@ test_sources = files('commands.c',
'test_efd.c',
'test_efd_perf.c',
'test_errno.c',
+ 'test_ethdev_link.c',
'test_event_crypto_adapter.c',
'test_event_eth_rx_adapter.c',
'test_event_ring.c',
@@ -196,6 +197,7 @@ fast_tests = [
['eal_flags_misc_autotest', false],
['eal_fs_autotest', true],
['errno_autotest', true],
+ ['ethdev_link_status' true],
['event_ring_autotest', true],
['fib_autotest', true],
['fib6_autotest', true],
new file mode 100644
@@ -0,0 +1,278 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ */
+
+#include <rte_log.h>
+#include <rte_ethdev.h>
+
+#include <rte_test.h>
+#include "test.h"
+
+
+static int32_t
+test_link_status_up_default(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_2_5G,
+ .link_status = ETH_LINK_UP,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_FULL_DUPLEX
+ };
+ char text[128];
+ ret = rte_eth_link_format(text, 128, NULL, &link_status);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format default string\n");
+ printf("Default link up #1: %s\n", text);
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("Link up at 2.5 Gbit/s FDX Autoneg",
+ text, strlen(text), "Invalid default link status string");
+
+ link_status.link_duplex = ETH_LINK_HALF_DUPLEX;
+ link_status.link_autoneg = ETH_LINK_FIXED;
+ link_status.link_speed = ETH_SPEED_NUM_10M,
+ ret = rte_eth_link_format(text, 128, NULL, &link_status);
+ printf("Default link up #2: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format default string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("Link up at 10 Mbit/s HDX Fixed",
+ text, strlen(text), "Invalid default link status "
+ "string with HDX");
+
+ link_status.link_speed = ETH_SPEED_NUM_UNKNOWN,
+ ret = rte_eth_link_format(text, 128, NULL, &link_status);
+ printf("Default link up #3: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format default string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("Link up at Unknown speed HDX Fixed",
+ text, strlen(text), "Invalid default link status "
+ "string with HDX");
+ return TEST_SUCCESS;
+}
+
+static int32_t
+test_link_status_down_default(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_2_5G,
+ .link_status = ETH_LINK_DOWN,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_FULL_DUPLEX
+ };
+ char text[128];
+ ret = rte_eth_link_format(text, 128, NULL, &link_status);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format default string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("Link down",
+ text, strlen(text), "Invalid default link status string");
+
+ return TEST_SUCCESS;
+}
+
+static int32_t
+test_link_status_string_overflow(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_2_5G,
+ .link_status = ETH_LINK_UP,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_FULL_DUPLEX
+ };
+ char text[128];
+ int i = 0;
+ for (i = 0; i < 128; i++)
+ text[i] = 'Y';
+ text[127] = '\0';
+
+ ret = rte_eth_link_format(NULL, 2, "status %S, %G Gbits/s",
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Format string should fail, but it's ok\n");
+
+ ret = rte_eth_link_format(text, 2, "status %S, %G Gbits/s",
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Format string should fail, but it's ok\n");
+ RTE_TEST_ASSERT(text[2] == 'Y', "String1 overflow\n");
+
+ ret = rte_eth_link_format(text, 8, NULL,
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Default format string should fail,"
+ " but it's ok\n");
+ RTE_TEST_ASSERT(text[8] == 'Y', "String1 overflow\n");
+
+ ret = rte_eth_link_format(text, 10, NULL,
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Default format string should fail,"
+ " but it's ok\n");
+ RTE_TEST_ASSERT(text[10] == 'Y', "String1 overflow\n");
+
+ text[1] = 'Y';
+ ret = rte_eth_link_format(text, 1, "%S",
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Status string should fail, but it's ok\n");
+ RTE_TEST_ASSERT(text[1] == 'Y', "String1 overflow\n");
+
+ return TEST_SUCCESS;
+}
+
+static int32_t
+test_link_status_format(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_40G,
+ .link_status = ETH_LINK_UP,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_FULL_DUPLEX
+ };
+ char text[128];
+ int i = 0;
+ for (i = 0; i < 128; i++)
+ text[i] = 'Y';
+ text[127] = '\0';
+ printf("status format #1: %s\n", text);
+ ret = rte_eth_link_format(text, 128, "status = %S, duplex = %D\n",
+ &link_status);
+ printf("status format #2: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("status = Up, duplex = FDX\n",
+ text, strlen(text), "Invalid status string1.");
+
+ ret = rte_eth_link_format(text, 128, "%A", &link_status);
+ printf("status format #3: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("Autoneg",
+ text, strlen(text), "Invalid status string2.");
+
+ ret = rte_eth_link_format(text, 128,
+ "%G",
+ &link_status);
+ printf("status format #4: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("40.0",
+ text, strlen(text), "Invalid status string3.");
+
+ ret = rte_eth_link_format(text, 128,
+ "%M",
+ &link_status);
+ printf("status format #5: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("40000",
+ text, strlen(text), "Invalid status string4.");
+ return TEST_SUCCESS;
+}
+
+static int32_t
+test_link_status_return_value(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_40G,
+ .link_status = ETH_LINK_UP,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_FULL_DUPLEX
+ };
+ char text[128];
+ int i = 0;
+ for (i = 0; i < 128; i++)
+ text[i] = 'Y';
+ text[127] = '\0';
+ ret = rte_eth_link_format(text, 128, "status = %S, ",
+ &link_status);
+ printf("return value #1:ret=%u, text=%s\n", ret, text);
+ ret += rte_eth_link_format(text + ret, 128 - ret,
+ "%A",
+ &link_status);
+ printf("return value #2:ret=%u, text=%s\n", ret, text);
+ ret += rte_eth_link_format(text + ret, 128 - ret,
+ ", duplex = %D\n",
+ &link_status);
+ printf("return value #3:ret=%u, text=%s\n", ret, text);
+ ret += rte_eth_link_format(text + ret, 128 - ret,
+ "%M Mbits/s\n",
+ &link_status);
+ printf("return value #4:ret=%u, text=%s\n", ret, text);
+ RTE_TEST_ASSERT(ret > 0, "Failed to format string\n");
+ TEST_ASSERT_BUFFERS_ARE_EQUAL("status = Up, Autoneg, duplex = FDX\n"
+ "40000 Mbits/s\n",
+ text, strlen(text), "Invalid status string");
+
+ return TEST_SUCCESS;
+}
+
+static int32_t
+test_link_status_invalid_fmt(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_40G,
+ .link_status = ETH_LINK_UP,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_FULL_DUPLEX
+ };
+ char text[128];
+ ret = rte_eth_link_format(text, 128, "status = %",
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Status string1 should fail, but it's ok\n");
+ ret = rte_eth_link_format(text, 128,
+ ", duplex = %d\n",
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Status string2 should fail, but it's ok\n");
+ ret = rte_eth_link_format(text, 128,
+ "% Mbits/s\n",
+ &link_status);
+ RTE_TEST_ASSERT(ret < 0, "Status string3 should fail, but it's ok\n");
+
+ return TEST_SUCCESS;
+}
+
+static int32_t
+test_link_status_format_edges(void)
+{
+ int ret = 0;
+ struct rte_eth_link link_status = {
+ .link_speed = ETH_SPEED_NUM_UNKNOWN,
+ .link_status = ETH_LINK_DOWN,
+ .link_autoneg = ETH_LINK_AUTONEG,
+ .link_duplex = ETH_LINK_HALF_DUPLEX
+ };
+ char text[128];
+ ret = rte_eth_link_format(text, 4, "%S", &link_status);
+ printf("format edges #1: %s\n", text);
+ RTE_TEST_ASSERT(ret < 0, "It should fail. No space for "
+ "zero terminator\n");
+ ret = rte_eth_link_format(text, 6, "123%D", &link_status);
+ printf("format edges #2: %s\n", text);
+ RTE_TEST_ASSERT(ret < 0, "It should fail. No space for "
+ "zero terminator\n");
+ ret = rte_eth_link_format(text, 7, "%A", &link_status);
+ printf("format edges #3: %s\n", text);
+ RTE_TEST_ASSERT(ret < 0, "It should fail. No space for "
+ "zero terminator\n");
+ ret = rte_eth_link_format(text, 8, "%A", &link_status);
+ printf("format edges #4: %s\n", text);
+ RTE_TEST_ASSERT(ret > 0, "It should ok, but it fails\n");
+ return TEST_SUCCESS;
+}
+static struct unit_test_suite link_status_testsuite = {
+ .suite_name = "link status formating",
+ .setup = NULL,
+ .teardown = NULL,
+ .unit_test_cases = {
+ TEST_CASE(test_link_status_up_default),
+ TEST_CASE(test_link_status_down_default),
+ TEST_CASE(test_link_status_string_overflow),
+ TEST_CASE(test_link_status_format),
+ TEST_CASE(test_link_status_format_edges),
+ TEST_CASE(test_link_status_invalid_fmt),
+ TEST_CASE(test_link_status_return_value),
+ TEST_CASES_END() /**< NULL terminate unit test array */
+ }
+};
+
+static int
+test_link_status(void)
+{
+ rte_log_set_global_level(RTE_LOG_DEBUG);
+ rte_log_set_level(RTE_LOGTYPE_EAL, RTE_LOG_DEBUG);
+
+ return unit_test_suite_runner(&link_status_testsuite);
+}
+
+REGISTER_TEST_COMMAND(ethdev_link_status, test_link_status);
@@ -2385,6 +2385,174 @@ rte_eth_link_get_nowait(uint16_t port_id, struct rte_eth_link *eth_link)
return 0;
}
+static int
+rte_eth_link_format_parser(char *str, size_t len, const char *const fmt,
+ struct rte_eth_link *link)
+{
+ size_t offset = 0;
+ const char *fmt_cur = fmt;
+ char *str_cur = str;
+ double gbits = (double)link->link_speed / 1000.;
+ static const char AUTONEG_STR[] = "Autoneg";
+ static const char FIXED_STR[] = "Fixed";
+ static const char FDX_STR[] = "FDX";
+ static const char HDX_STR[] = "HDX";
+ static const char UNKNOWN_STR[] = "Unknown";
+ static const char UP_STR[] = "Up";
+ static const char DOWN_STR[] = "Down";
+
+ char gbits_str[20];
+ char mbits_str[20];
+ /* preformat complex formating to easily concatinate it further */
+ snprintf(mbits_str, 20, "%u", link->link_speed);
+ snprintf(gbits_str, 20, "%.1f", gbits);
+ /* init str before formating */
+ str[0] = 0;
+ while (*fmt_cur) {
+ /* check str bounds */
+ if (offset > (len - 1)) {
+ str[len - 1] = '\0';
+ return -1;
+ }
+ if (*fmt_cur == '%') {
+ /* set null terminator to current position,
+ * it's required for strlcat
+ */
+ *str_cur = '\0';
+ switch (*++fmt_cur) {
+ /* Speed in Mbits/s */
+ case 'M':
+ if (link->link_speed ==
+ ETH_SPEED_NUM_UNKNOWN)
+ offset = strlcat(str, UNKNOWN_STR,
+ len);
+ else
+ offset = strlcat(str, mbits_str, len);
+ break;
+ /* Speed in Gbits/s */
+ case 'G':
+ if (link->link_speed ==
+ ETH_SPEED_NUM_UNKNOWN)
+ offset = strlcat(str, UNKNOWN_STR,
+ len);
+ else
+ offset = strlcat(str, gbits_str, len);
+ break;
+ /* Link status */
+ case 'S':
+ offset = strlcat(str, link->link_status ?
+ UP_STR : DOWN_STR, len);
+ break;
+ /* Link autoneg */
+ case 'A':
+ offset = strlcat(str, link->link_autoneg ?
+ AUTONEG_STR : FIXED_STR, len);
+ break;
+ /* Link duplex */
+ case 'D':
+ offset = strlcat(str, link->link_duplex ?
+ FDX_STR : HDX_STR, len);
+ break;
+ /* Error cases */
+ default:
+ return -1;
+
+ }
+ if (offset > (len - 1))
+ return -1;
+
+ str_cur = str + offset;
+ } else {
+ *str_cur++ = *fmt_cur;
+ offset++;
+ }
+ fmt_cur++;
+ }
+ *str_cur = '\0';
+ return offset;
+}
+
+int
+rte_eth_link_printf(const char *const fmt,
+ struct rte_eth_link *link)
+{
+ char text[200];
+ int ret;
+ ret = rte_eth_link_format(text, 200, fmt, link);
+ if (ret > 0)
+ printf("%s", text);
+ return ret;
+}
+
+int
+rte_eth_link_format(char *str, size_t len, const char *const fmt,
+ struct rte_eth_link *link)
+{
+ size_t offset = 0;
+ double gbits = (double)link->link_speed / 1000.;
+ char gbits_str[20];
+ char mbits_str[20];
+ /* TBD: make it international? */
+ static const char LINK_DOWN_STR[] = "Link down";
+ static const char LINK_UP_STR[] = "Link up at ";
+ static const char UNKNOWN_SPEED_STR[] = "Unknown speed ";
+ static const char MBITS_STR[] = "Mbit/s";
+ static const char GBITS_STR[] = "Gbit/s";
+ static const char AUTONEG_STR[] = "Autoneg";
+ static const char FIXED_STR[] = "Fixed";
+ static const char FDX_STR[] = "FDX ";
+ static const char HDX_STR[] = "HDX ";
+ if (str == NULL || len == 0)
+ return -1;
+ /* default format string, if no fmt is specified */
+ if (fmt == NULL) {
+ if (link->link_status == ETH_LINK_DOWN) {
+ if (sizeof(LINK_DOWN_STR) > len)
+ return -1;
+ return strlcpy(str, LINK_DOWN_STR, len);
+ }
+
+ /* preformat complex strings to easily concatinate it further */
+ snprintf(mbits_str, 20, "%u %s ", link->link_speed, MBITS_STR);
+ snprintf(gbits_str, 20, "%.1f %s ", gbits, GBITS_STR);
+
+ offset = strlcpy(str, LINK_UP_STR, len);
+ /* reserve one byte to null terminator */
+ if (offset > (len - 1))
+ return -1;
+ /* link speed */
+ if (link->link_speed == ETH_SPEED_NUM_UNKNOWN) {
+ offset = strlcat(str, UNKNOWN_SPEED_STR, len);
+ if (offset > (len - 1))
+ return -1;
+ } else {
+ if (link->link_speed < ETH_SPEED_NUM_1G) {
+ offset = strlcat(str, mbits_str, len);
+ if (offset > (len - 1))
+ return -1;
+ } else {
+ offset = strlcat(str, gbits_str, len);
+ if (offset > (len - 1))
+ return -1;
+ }
+ }
+ /* link duplex */
+ offset = strlcat(str, link->link_duplex ?
+ FDX_STR : HDX_STR, len);
+ if (offset > (len - 1))
+ return -1;
+ /* link autonegotiation */
+ offset = strlcat(str, link->link_autoneg ?
+ AUTONEG_STR : FIXED_STR, len);
+ if (offset > (len - 1))
+ return -1;
+ /* Formated status */
+ } else
+ offset = rte_eth_link_format_parser(str, len, fmt, link);
+
+ return offset;
+}
+
int
rte_eth_stats_get(uint16_t port_id, struct rte_eth_stats *stats)
{
@@ -2295,6 +2295,60 @@ int rte_eth_link_get(uint16_t port_id, struct rte_eth_link *link);
*/
int rte_eth_link_get_nowait(uint16_t port_id, struct rte_eth_link *link);
+
+/**
+ * print formated link status to stdout. This function threats all
+ * special values like ETH_SPEED_NUM_UNKNOWN, ETH_LINK_DOWN etc. and convert
+ * them to textual representation.
+ *
+ * @param fmt
+ * Format string which allow to format link status. If NULL is provided
+ * , default formating will be applied.
+ * Following specifiers are available:
+ * - '%M' link speed in Mbits/s
+ * - '%G' link speed in Gbits/s
+ * - '%S' link status. e.g. Up or Down
+ * - '%A' link autonegotiation state
+ * - '%D' link duplex state
+ * @param link
+ * Link status provided by rte_eth_link_get function
+ * @return
+ * - Number of bytes written to stdout. In case of error, -1 is returned.
+ *
+ */
+__rte_experimental
+int rte_eth_link_printf(const char *const fmt,
+ struct rte_eth_link *link);
+
+/**
+ * Format link status to textual representation. This function threats all
+ * special values like ETH_SPEED_NUM_UNKNOWN, ETH_LINK_DOWN etc. and convert
+ * them to textual representation.
+ *
+ * @param str
+ * A pointer to a string to be filled with textual representation of
+ * device status.
+ * @param len
+ * Length of available memory at 'str' string.
+ * @param fmt
+ * Format string which allow to format link status. If NULL is provided
+ * , default formating will be applied.
+ * Following specifiers are available:
+ * - '%M' link speed in Mbits/s
+ * - '%G' link speed in Gbits/s
+ * - '%S' link status. e.g. Up or Down
+ * - '%A' link autonegotiation state
+ * - '%D' link duplex state
+ * @param link
+ * Link status provided by rte_eth_link_get function
+ * @return
+ * - Number of bytes written to str array. In case of error, -1 is returned.
+ *
+ */
+__rte_experimental
+int rte_eth_link_format(char *str, size_t len, const char *const fmt,
+ struct rte_eth_link *eth_link);
+
/**
* Retrieve the general I/O statistics of an Ethernet device.
*
@@ -241,4 +241,8 @@ EXPERIMENTAL {
__rte_ethdev_trace_rx_burst;
__rte_ethdev_trace_tx_burst;
rte_flow_get_aged_flows;
+
+ # added in 20.08
+ rte_eth_link_format;
+ rte_eth_link_printf;
};