This commit add function which treat link status structure
and format it to text representation.

Signed-off-by: Ivan Dyukov <i.dyu...@samsung.com>
---
 MAINTAINERS                    |   1 +
 app/test/Makefile              |   3 +
 app/test/meson.build           |   2 +
 app/test/test_ethdev_link.c    | 253 +++++++++++++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.c | 163 +++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.h |  52 +++++++
 6 files changed, 474 insertions(+)
 create mode 100644 app/test/test_ethdev_link.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 6a14622a0..94c5cd58e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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 <or...@mellanox.com>
diff --git a/app/test/Makefile b/app/test/Makefile
index 5b119aa61..14552073d 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -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
diff --git a/app/test/meson.build b/app/test/meson.build
index 1715ddbcb..c5b742c15 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -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],
diff --git a/app/test/test_ethdev_link.c b/app/test/test_ethdev_link.c
new file mode 100644
index 000000000..9d04dfb81
--- /dev/null
+++ b/app/test/test_ethdev_link.c
@@ -0,0 +1,253 @@
+/* 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");
+       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);
+       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);
+       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';
+
+
+       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");
+
+       ret = rte_eth_link_format(text, 2, "%S",
+               &link_status);
+       RTE_TEST_ASSERT(ret < 0, "Status string should fail, but it's ok\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';
+       ret = rte_eth_link_format(text, 128, "status = %S, duplex = %D\n",
+               &link_status);
+       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);
+       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);
+       RTE_TEST_ASSERT(ret > 0, "Failed to format string\n");
+       TEST_ASSERT_BUFFERS_ARE_EQUAL("40.0",
+               text, strlen(text), "Invalid status string3.");
+       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';
+       ret = rte_eth_link_format(text, 128, "status = %S, ",
+               &link_status);
+       ret += rte_eth_link_format(text + ret, 128 - ret,
+               "%A",
+               &link_status);
+       ret += rte_eth_link_format(text + ret, 128 - ret,
+               ", duplex = %D\n",
+               &link_status);
+       ret += rte_eth_link_format(text + ret, 128 - ret,
+               "%M Mbits/s\n",
+               &link_status);
+       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);
+       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);
+       RTE_TEST_ASSERT(ret < 0, "It should fail. No space for "
+                                "zero terminator\n");
+       ret = rte_eth_link_format(text, 7, "%A", &link_status);
+       RTE_TEST_ASSERT(ret < 0, "It should fail. No space for "
+                                "zero terminator\n");
+       ret = rte_eth_link_format(text, 8, "%A", &link_status);
+       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);
diff --git a/lib/librte_ethdev/rte_ethdev.c b/lib/librte_ethdev/rte_ethdev.c
index 8e10a6fc3..108be1902 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2385,6 +2385,169 @@ rte_eth_link_get_nowait(uint16_t port_id, struct 
rte_eth_link *eth_link)
        return 0;
 }
 
+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);
+       printf("%s", text);
+       return ret;
+}
+
+int
+rte_eth_link_format(char *str, int32_t len, const char *const fmt,
+                   struct rte_eth_link *link)
+{
+       int offset = 0;
+       int32_t clen = len;
+       const char *fmt_cur = fmt;
+       double gbits = (double)link->link_speed / 1000.;
+       /* 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";
+       static const char UNKNOWN_STR[]       = "Unknown";
+       static const char UP_STR[]            = "Up";
+       static const char DOWN_STR[]          = "Down";
+       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)
+                       return snprintf(str, (size_t)clen, "%s", LINK_DOWN_STR);
+
+               offset = snprintf(str, (size_t)clen, "%s", LINK_UP_STR);
+               if (offset < 0 || (clen - offset) <= 0)
+                       return -1;
+               clen -= offset;
+               str += offset;
+               if (link->link_speed == ETH_SPEED_NUM_UNKNOWN) {
+                       offset = snprintf(str, clen, "%s",
+                                         UNKNOWN_SPEED_STR);
+                       if (offset < 0 || (clen - offset) <= 0)
+                               return -1;
+                       clen -= offset;
+                       str += offset;
+               } else {
+                       if (link->link_speed < ETH_SPEED_NUM_1G) {
+                               offset = snprintf(str, clen,
+                                                 "%u %s",
+                                                 link->link_speed,
+                                                 MBITS_STR);
+                               if (offset < 0 || (clen - offset) <= 0)
+                                       return -1;
+                               clen -= offset;
+                               str += offset;
+
+                       } else {
+                               offset = snprintf(str, clen,
+                                                 "%.1f %s",
+                                                 gbits,
+                                                 GBITS_STR);
+                               if (offset < 0 || (clen - offset) <= 0)
+                                       return -1;
+                               clen -= offset;
+                               str += offset;
+                       }
+               }
+               offset = snprintf(str, clen, " %s", link->link_duplex ?
+                              FDX_STR : HDX_STR);
+               if (offset < 0 || (clen - offset) <= 0)
+                       return -1;
+               clen -= offset;
+               str += offset;
+               offset = snprintf(str, clen, " %s", link->link_autoneg ?
+                              AUTONEG_STR : FIXED_STR);
+               if (offset < 0 || (clen - offset) <= 0)
+                       return -1;
+               clen -= offset;
+               str += offset;
+       /* Formated status */
+       } else {
+               char c = *fmt_cur;
+               while (c) {
+                       if (clen <= 0)
+                               return -1;
+                       if (c == '%') {
+                               c = *++fmt_cur;
+                               switch (c) {
+                               /* Speed in Mbits/s */
+                               case 'M':
+                                       if (link->link_speed ==
+                                           ETH_SPEED_NUM_UNKNOWN)
+                                               offset = snprintf(str,
+                                                 clen, "%s",
+                                                 UNKNOWN_STR);
+                                       else
+                                               offset = snprintf(str,
+                                                 clen, "%u",
+                                                 link->link_speed);
+                                       break;
+                               /* Speed in Gbits/s */
+                               case 'G':
+                                       if (link->link_speed ==
+                                           ETH_SPEED_NUM_UNKNOWN)
+                                               offset = snprintf(str,
+                                                 clen, "%s",
+                                                 UNKNOWN_STR);
+                                       else {
+                                               offset = snprintf(str,
+                                                 clen, "%.1f",
+                                                 gbits);
+                                       }
+                                       break;
+                               /* Link status */
+                               case 'S':
+                                       offset = snprintf(str, clen,
+                                               "%s",
+                                               link->link_status ?
+                                               UP_STR : DOWN_STR);
+                                       break;
+                               /* Link autoneg */
+                               case 'A':
+                                       offset = snprintf(str, clen,
+                                               "%s",
+                                               link->link_autoneg ?
+                                               AUTONEG_STR :
+                                               FIXED_STR);
+                                       break;
+                               /* Link duplex */
+                               case 'D':
+                                       offset = snprintf(str, clen,
+                                               "%s",
+                                               link->link_duplex ?
+                                               FDX_STR : HDX_STR);
+                                       break;
+                               /* Error cases */
+                               default:
+                                       return -1;
+
+                               }
+                               if (offset < 0 || (clen - offset) <= 0)
+                                       return -1;
+                               clen -= offset;
+                               str += offset;
+                       } else {
+                               *str++ = c;
+                               clen--;
+                       }
+                       c = *++fmt_cur;
+               }
+       }
+       /* teminate string */
+       clen = len - clen;
+       *str = 0;
+       return clen;
+}
+
 int
 rte_eth_stats_get(uint16_t port_id, struct rte_eth_stats *stats)
 {
diff --git a/lib/librte_ethdev/rte_ethdev.h b/lib/librte_ethdev/rte_ethdev.h
index 2090af501..83291e656 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -2295,6 +2295,58 @@ 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.
+ *
+ */
+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.
+ *
+ */
+int rte_eth_link_format(char *str, int32_t len, const char *const fmt,
+                       struct rte_eth_link *eth_link);
+
 /**
  * Retrieve the general I/O statistics of an Ethernet device.
  *
-- 
2.17.1

Reply via email to