From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from dpdk.org (dpdk.org [92.243.14.124])
	by inbox.dpdk.org (Postfix) with ESMTP id C3482A04B5;
	Thu, 10 Sep 2020 21:34:28 +0200 (CEST)
Received: from [92.243.14.124] (localhost [127.0.0.1])
	by dpdk.org (Postfix) with ESMTP id 1AD501C0D0;
	Thu, 10 Sep 2020 21:34:20 +0200 (CEST)
Received: from mailout2.w1.samsung.com (mailout2.w1.samsung.com
 [210.118.77.12]) by dpdk.org (Postfix) with ESMTP id 3B2171C0CF
 for <dev@dpdk.org>; Thu, 10 Sep 2020 21:34:19 +0200 (CEST)
Received: from eucas1p1.samsung.com (unknown [182.198.249.206])
 by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id
 20200910193418euoutp02978d330f90fdbe080b8b56ac198021fe~zgx5Xg0SF0501005010euoutp02Q;
 Thu, 10 Sep 2020 19:34:18 +0000 (GMT)
DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com
 20200910193418euoutp02978d330f90fdbe080b8b56ac198021fe~zgx5Xg0SF0501005010euoutp02Q
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com;
 s=mail20170921; t=1599766459;
 bh=iZ2rhieyH+SHo++rrlWrL6I7iDEjuhhlRYa1cnWCSW8=;
 h=From:To:Subject:Date:In-Reply-To:Reply-To:References:From;
 b=WPVr4GJP2SY22da9XIpObTbjpqbfnyEphlnRh4K5mjmXTIwOf4Yt/ovBlAoTMHulw
 V9EUfNo1sUX63FDBPtVZP+2wqMf8IKzOnb3mcFb60IxF5kpEHpEmGk21qjY0Fen3e0
 LdGPmCWlBJSzV2uzTASSWd1BmucCLi2Z4l76D804=
Received: from eusmges1new.samsung.com (unknown [203.254.199.242]) by
 eucas1p2.samsung.com (KnoxPortal) with ESMTP id
 20200910193418eucas1p2dc420133c9867ea1dc64373e306eac1d~zgx5DKPO72490124901eucas1p2k;
 Thu, 10 Sep 2020 19:34:18 +0000 (GMT)
Received: from eucas1p2.samsung.com ( [182.198.249.207]) by
 eusmges1new.samsung.com (EUCPMTA) with SMTP id 76.0D.06456.ABF7A5F5; Thu, 10
 Sep 2020 20:34:18 +0100 (BST)
Received: from eusmtrp1.samsung.com (unknown [182.198.249.138]) by
 eucas1p2.samsung.com (KnoxPortal) with ESMTPA id
 20200910193417eucas1p22204d717d003dba6eb7cb52d60e6571b~zgx4T0rAU2394823948eucas1p2j;
 Thu, 10 Sep 2020 19:34:17 +0000 (GMT)
Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by
 eusmtrp1.samsung.com (KnoxPortal) with ESMTP id
 20200910193417eusmtrp19eb0b4a7209d8e81ca6b55e1bebb86c5~zgx4S5GJ13165331653eusmtrp1t;
 Thu, 10 Sep 2020 19:34:17 +0000 (GMT)
X-AuditID: cbfec7f2-809ff70000001938-7d-5f5a7fbaf71d
Received: from eusmtip2.samsung.com ( [203.254.199.222]) by
 eusmgms2.samsung.com (EUCPMTA) with SMTP id F3.8A.06017.9BF7A5F5; Thu, 10
 Sep 2020 20:34:17 +0100 (BST)
Received: from idyukov.rnd.samsung.ru (unknown [106.109.129.29]) by
 eusmtip2.samsung.com (KnoxPortal) with ESMTPA id
 20200910193415eusmtip247250d85f75a8a1df35765d2b8f84d1e~zgx2gG2sT1532615326eusmtip2M;
 Thu, 10 Sep 2020 19:34:15 +0000 (GMT)
From: Ivan Dyukov <i.dyukov@samsung.com>
To: dev@dpdk.org, i.dyukov@samsung.com, v.kuramshin@samsung.com,
 thomas@monjalon.net, david.marchand@redhat.com, ferruh.yigit@intel.com,
 arybchenko@solarflare.com, wei.zhao1@intel.com, jia.guo@intel.com,
 beilei.xing@intel.com, qiming.yang@intel.com, wenzhuo.lu@intel.com,
 mb@smartsharesystems.com, stephen@networkplumber.org,
 nicolas.chautru@intel.com, bruce.richardson@intel.com,
 konstantin.ananyev@intel.com, cristian.dumitrescu@intel.com,
 radu.nicolau@intel.com, akhil.goyal@nxp.com, declan.doherty@intel.com,
 skori@marvell.com, pbhagavatula@marvell.com, jerinj@marvell.com,
 kirankumark@marvell.com, david.hunt@intel.com, anatoly.burakov@intel.com,
 xiaoyun.li@intel.com, jingjing.wu@intel.com, john.mcnamara@intel.com,
 jasvinder.singh@intel.com, byron.marohn@intel.com, yipeng1.wang@intel.com
Date: Thu, 10 Sep 2020 22:33:35 +0300
Message-Id: <20200910193400.15326-3-i.dyukov@samsung.com>
X-Mailer: git-send-email 2.17.1
In-Reply-To: <20200910193400.15326-1-i.dyukov@samsung.com>
X-Brightmail-Tracker: H4sIAAAAAAAAA0WSe0wUVxTGe2ZmZ4YNa8aVhKMlqJsaq4kotUlPjVFpm3YSTVqN2oYEcZQJ
 YFmgO0CxSdvVVrQrpbxCK5gVJRbFB6DIIrKKtIK4rlu2GiVFRNLEQuUhsAkg2LoMTf8753e/
 7zvn3lyRNRfyC8Tk1AzVlqqkWHgjV9864VvR+HVs/KqnxYuo+rYTqLe7gqWe4oc8HR7+lqEH
 VeupyXXUQIWP7AzlFT5jyHXKwdPTnv0cDY64GOpyewQq9e8XaKTliUDf+8qAnkz1s3SkfBTI
 57xkoInrjTwNTZ3gKG/yHtAPdTeB2nKfcVR8uQEoxyHR8OlKgSq6Immk5yZHns6LAp0NdLB0
 6fY0UEFbNZA35x/DhsXyZPlJg1zR1MfIRcc7WLm1s0SQc9udjFw72MDIQ1fv8XJeXRXIt3oP
 s/KZP8f5j4yxxrUJakpylmpbuW6nManoLy+T7t+Z/Vt3K2OHpg8dECKi9Ca6fj3OOMAomqVT
 gP72EtCbMcB9+ec4vRkFvGKvNDhAnLFce5il80rAUmcxBKPMUgDwxsSXwZqXXkfPIedMbJhU
 y6Nr6j4TNM+T1uLgH58ENZy0BDv6/VywNklv43iNy6CvtBDP1DSzQXmItAYfd8Xp8ZF4/9jY
 TCRKd0X0/F4m6Pr3sKvCPeudh/1tdbM8Aj1FuZxuOAhYUH9R0Jt8wG/KBmZV67Hu7ztCcBor
 LcPqxpU6jsGDY+2cfuE5+GBgbhCzL8vC+h9ZHZvwUI5ZV1uwud0/ixGnn4fqWMZfBsZZ/anc
 gC/2lTD5sLD0/1nlAFUQrmZq1kRVi05VP4/SFKuWmZoYtTvNegFe/lTPi7aRBgj4d7WAJIIl
 1NS8JjbebFCytL3WFkCRtYSZ3vF6dphNCcreL1RbWrwtM0XVWuBVkbOEm1af6IszS4lKhvqp
 qqartv9OGTFkgR0ObNaS528RPx4tr3Vnx8W81ektmBuqdZrCX7E6hk0n3+3OiLm2ZOn8xyVR
 e+ThR+et208bHbuOPL/z/k+BvqM5dyMMX5ltkxsPeLe9tj2/1u5flMvNmV7uPhb+XU1Stit6
 KD0kkO5LWObfGhGRtiklbOMOxrfhZ2XVG0rBZ0u7P+hdF2nhtCQlejlr05R/AcJDkL6lAwAA
X-Brightmail-Tracker: H4sIAAAAAAAAAzWSa0hTcRjG+59zdjbFxWEZ/vGD1bLoOtvUfJWl1Yc6FXShopozXXpy0uZk
 Z7MMwQtSudJaRaDmskY3NbTUNLGL++ClKaVhVvOy1DArvFZU2GVD+vbwPL/n5YX3FZGSGSpQ
 lJJq4oypGp2U9qWcf1oH1j7KUsWve1e0Bqo6bAiGBuwkuC/303B2Mo+AN+Ux0FR/VQAXB7MJ
 KLw4RUD9HQsNX9y5FIxP1xPQ99gphOLuXCFMO0aFUPCiBMHo7CcSispmELyw1QngZ3MjDROz
 Nygo/NWD4HxtG4LWc1MUXH7UgOCUhYHJu7eFYO8Lgml3GwXOtzVCqPzWRUJdx28E1tYqBJ2n
 /go2LmF/ld0UsPamMYK9dL2LZFveXhGy59ptBHt/vIFgJ5700GxhbTlinw+dJdmKkR/0bl+V
 TGk0mE3cYq2BN22QxspBIZNHgkwRFimTh0bERSnCpSHRyiROl5LOGUOiE2TaSx87ibTuhBMv
 B1qIbNS0y4JEIsyE4af96RbkK5IwNxEezGsQzPkYfx4hLcjHIxfg2dcWeo6ZQbjCWiX0BjSz
 AjvP2Ahv4M+4aPzqdA7lLS9glHjcddDLUMwy3PWpm/JqMROJf1TXC+aGLsIV1c9IL+7DROH3
 fXFeW8KkYFvHd8GcDsK9174SF9D8MjSvHPlzZl6frOcVMl6j582pybJEg/4B8hz8YcvP2gZk
 Gd/rQIwISf3Ez6JU8RKBJp3P0DsQFpFSf/HmTudhiThJk3GSMxrijWYdxztQuGc/Kxm4MNHg
 eZ9UU7w8XB4BkfKI0IjQ9SANEJ9hmtUSJllj4o5xXBpn/N8jRD6B2UitOKi0uTJ3Jh7NVC68
 P3I8UWf/sLVsZVp+RmWlOSfp9lJdofZIrN+WvILSmMzP7SUv7wUHde6f5PuKrDWaTTvUKWP5
 qhhbsCtreJv6UHGd1Q3qxTpJiF8jUbO9tKfOnqBaXZ0V4L71ZtgXO5YfeNzk3remfE/g6tAr
 2kaT406vlOK1Gvkq0shr/gGP1Ph2BgMAAA==
X-CMS-MailID: 20200910193417eucas1p22204d717d003dba6eb7cb52d60e6571b
X-Msg-Generator: CA
Content-Type: text/plain; charset="utf-8"
X-RootMTR: 20200910193417eucas1p22204d717d003dba6eb7cb52d60e6571b
X-EPHeader: CA
CMS-TYPE: 201P
X-CMS-RootMailID: 20200910193417eucas1p22204d717d003dba6eb7cb52d60e6571b
References: <20200427095737.11082-1-i.dyukov@samsung.com>
 <20200910193400.15326-1-i.dyukov@samsung.com>
 <CGME20200910193417eucas1p22204d717d003dba6eb7cb52d60e6571b@eucas1p2.samsung.com>
Subject: [dpdk-dev] [PATCH v10 02/24] ethdev: format a link status text
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.15
Precedence: list
Reply-To: i.dyukov@samsung.com
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org
Sender: "dev" <dev-bounces@dpdk.org>

There is new link_speed value introduced. It's INT_MAX value which
means that speed is unknown. To simplify processing of the value
in application, new function is added which convert link_speed to
string. Also dpdk examples have many duplicated code which format
entire link status structure to text.

This commit adds two functions:
  * rte_eth_link_speed_to_str - format link_speed to string
  * rte_eth_link_to_str - convert link status structure to string

Signed-off-by: Ivan Dyukov <i.dyukov@samsung.com>
---
 MAINTAINERS                              |   1 +
 app/test/Makefile                        |   3 +
 app/test/meson.build                     |   2 +
 app/test/test_ethdev_link.c              | 169 +++++++++++++++++++++++
 lib/librte_ethdev/rte_ethdev.c           |  53 +++++++
 lib/librte_ethdev/rte_ethdev.h           |  32 +++++
 lib/librte_ethdev/rte_ethdev_version.map |   4 +
 7 files changed, 264 insertions(+)
 create mode 100644 app/test/test_ethdev_link.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 5e706cd7e..f4fb31ea2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -393,6 +393,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>
diff --git a/app/test/Makefile b/app/test/Makefile
index e5440774b..9f43b8c3c 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -251,6 +251,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 56591db4e..1e6acf701 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -39,6 +39,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',
@@ -199,6 +200,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..1c6b0c9ed
--- /dev/null
+++ b/app/test/test_ethdev_link.c
@@ -0,0 +1,169 @@
+/* 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[RTE_ETH_LINK_MAX_STR_LEN];
+
+	ret = rte_eth_link_to_str(text, sizeof(text), &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 Gbps 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_to_str(text, sizeof(text), &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 Mbps HDX Fixed",
+		text, strlen(text), "Invalid default link status "
+		"string with HDX");
+
+	link_status.link_speed = ETH_SPEED_NUM_UNKNOWN;
+	ret = rte_eth_link_to_str(text, sizeof(text), &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 HDX Fixed",
+		text, strlen(text), "Invalid default link status "
+		"string with HDX");
+
+	link_status.link_speed = ETH_SPEED_NUM_NONE;
+	ret = rte_eth_link_to_str(text, sizeof(text), &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 0 Mbps HDX Fixed",
+		text, strlen(text), "Invalid default link status "
+		"string with HDX");
+
+	/* test max str len */
+	link_status.link_speed = ETH_SPEED_NUM_200G;
+	link_status.link_duplex = ETH_LINK_HALF_DUPLEX;
+	link_status.link_autoneg = ETH_LINK_AUTONEG;
+	ret = rte_eth_link_to_str(text, sizeof(text), &link_status);
+	printf("Default link up #4:len = %d, %s\n", ret, text);
+	RTE_TEST_ASSERT(ret < RTE_ETH_LINK_MAX_STR_LEN,
+		"String length exceeds max allowed value\n");
+	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[RTE_ETH_LINK_MAX_STR_LEN];
+
+	ret = rte_eth_link_to_str(text, sizeof(text), &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_invalid(void)
+{
+	int ret = 0;
+	struct rte_eth_link link_status = {
+		.link_speed = 55555,
+		.link_status = ETH_LINK_UP,
+		.link_autoneg = ETH_LINK_AUTONEG,
+		.link_duplex = ETH_LINK_FULL_DUPLEX
+	};
+	char text[RTE_ETH_LINK_MAX_STR_LEN];
+
+	ret = rte_eth_link_to_str(text, sizeof(text), &link_status);
+	RTE_TEST_ASSERT(ret < RTE_ETH_LINK_MAX_STR_LEN,
+		"Failed to format invalid string\n");
+	printf("invalid link up #1: len=%d %s\n", ret, text);
+	TEST_ASSERT_BUFFERS_ARE_EQUAL("Link up at Invalid speed FDX Autoneg",
+		text, strlen(text), "Incorrect invalid link status string");
+
+	return TEST_SUCCESS;
+}
+
+
+static int32_t
+test_link_speed_all_values(void)
+{
+	const char *speed;
+	uint32_t i;
+	struct link_speed_t {
+		const char *value;
+		uint32_t link_speed;
+	} speed_str_map[] = {
+		{ "0 Mbps",   ETH_SPEED_NUM_NONE },
+		{ "10 Mbps",  ETH_SPEED_NUM_10M },
+		{ "100 Mbps", ETH_SPEED_NUM_100M },
+		{ "1 Gbps",   ETH_SPEED_NUM_1G },
+		{ "2.5 Gbps", ETH_SPEED_NUM_2_5G },
+		{ "5 Gbps",   ETH_SPEED_NUM_5G },
+		{ "10 Gbps",  ETH_SPEED_NUM_10G },
+		{ "20 Gbps",  ETH_SPEED_NUM_20G },
+		{ "25 Gbps",  ETH_SPEED_NUM_25G },
+		{ "40 Gbps",  ETH_SPEED_NUM_40G },
+		{ "50 Gbps",  ETH_SPEED_NUM_50G },
+		{ "56 Gbps",  ETH_SPEED_NUM_56G },
+		{ "100 Gbps", ETH_SPEED_NUM_100G },
+		{ "200 Gbps", ETH_SPEED_NUM_200G },
+		{ "Unknown",  ETH_SPEED_NUM_UNKNOWN },
+		{ "Invalid speed",   50505 }
+	};
+
+	for (i = 0; i < sizeof(speed_str_map) / sizeof(struct link_speed_t);
+			i++) {
+		speed = rte_eth_link_speed_to_str(speed_str_map[i].link_speed);
+		TEST_ASSERT_BUFFERS_ARE_EQUAL(speed_str_map[i].value,
+			speed, strlen(speed_str_map[i].value),
+			"Invalid link speed string");
+	}
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite link_status_testsuite = {
+	.suite_name = "link status formatting",
+	.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_speed_all_values),
+		TEST_CASE(test_link_status_invalid),
+		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 d06b7f9b1..cb624b7b4 100644
--- a/lib/librte_ethdev/rte_ethdev.c
+++ b/lib/librte_ethdev/rte_ethdev.c
@@ -2383,6 +2383,59 @@ rte_eth_link_get_nowait(uint16_t port_id, struct rte_eth_link *eth_link)
 	return 0;
 }
 
+const char *
+rte_eth_link_speed_to_str(uint32_t link_speed)
+{
+	switch (link_speed) {
+	case ETH_SPEED_NUM_UNKNOWN:
+		return "Unknown";
+	case ETH_SPEED_NUM_NONE:
+		return "0 Mbps";
+	case ETH_SPEED_NUM_10M:
+		return "10 Mbps";
+	case ETH_SPEED_NUM_100M:
+		return "100 Mbps";
+	case ETH_SPEED_NUM_1G:
+		return "1 Gbps";
+	case ETH_SPEED_NUM_2_5G:
+		return "2.5 Gbps";
+	case ETH_SPEED_NUM_5G:
+		return "5 Gbps";
+	case ETH_SPEED_NUM_10G:
+		return "10 Gbps";
+	case ETH_SPEED_NUM_20G:
+		return "20 Gbps";
+	case ETH_SPEED_NUM_25G:
+		return "25 Gbps";
+	case ETH_SPEED_NUM_40G:
+		return "40 Gbps";
+	case ETH_SPEED_NUM_50G:
+		return "50 Gbps";
+	case ETH_SPEED_NUM_56G:
+		return "56 Gbps";
+	case ETH_SPEED_NUM_100G:
+		return "100 Gbps";
+	case ETH_SPEED_NUM_200G:
+		return "200 Gbps";
+	default:
+		return "Invalid speed";
+	}
+}
+
+int
+rte_eth_link_to_str(char *str, size_t len, const struct rte_eth_link *eth_link)
+{
+	if (eth_link->link_status == ETH_LINK_DOWN)
+		return snprintf(str, len, "Link down");
+	else
+		return snprintf(str, len, "Link up at %s %s %s",
+			rte_eth_link_speed_to_str(eth_link->link_speed),
+			(eth_link->link_duplex == ETH_LINK_FULL_DUPLEX) ?
+			"FDX" : "HDX",
+			(eth_link->link_autoneg == ETH_LINK_AUTONEG) ?
+			"Autoneg" : "Fixed");
+}
+
 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..a86f8f916 100644
--- a/lib/librte_ethdev/rte_ethdev.h
+++ b/lib/librte_ethdev/rte_ethdev.h
@@ -323,6 +323,7 @@ struct rte_eth_link {
 #define ETH_LINK_UP          1 /**< Link is up (see link_status). */
 #define ETH_LINK_FIXED       0 /**< No autonegotiation (see link_autoneg). */
 #define ETH_LINK_AUTONEG     1 /**< Autonegotiated (see link_autoneg). */
+#define RTE_ETH_LINK_MAX_STR_LEN 40 /**< Max length of default link string. */
 
 /**
  * A structure used to configure the ring threshold registers of an RX/TX
@@ -2295,6 +2296,37 @@ 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);
 
+/**
+ * The function converts a link_speed to a string. It handles all special
+ * values like unknown or none speed.
+ * @param link_speed
+ *   link_speed of rte_eth_link struct
+ * @return
+ *   Link speed in textual format. It's pointer to immutable memory.
+ *   No free is required.
+ */
+__rte_experimental
+const char *rte_eth_link_speed_to_str(uint32_t speed_link);
+
+/**
+ * The function converts a rte_eth_link struct representing a link status to
+ * a string.
+ * @param str
+ *   A pointer to a string to be filled with textual representation of
+ *   device status. At least ETH_LINK_MAX_STR_LEN bytes should be allocated to
+ *   store default link status text.
+ * @param len
+ *   Length of available memory at 'str' string.
+ * @param eth_link
+ *   Link status returned by rte_eth_link_get function
+ * @return
+ *   - Number of bytes written to str array.
+ *
+ */
+__rte_experimental
+int rte_eth_link_to_str(char *str, size_t len,
+			const struct rte_eth_link *eth_link);
+
 /**
  * Retrieve the general I/O statistics of an Ethernet device.
  *
diff --git a/lib/librte_ethdev/rte_ethdev_version.map b/lib/librte_ethdev/rte_ethdev_version.map
index 715505604..69a763d4b 100644
--- a/lib/librte_ethdev/rte_ethdev_version.map
+++ b/lib/librte_ethdev/rte_ethdev_version.map
@@ -241,4 +241,8 @@ EXPERIMENTAL {
 	__rte_ethdev_trace_rx_burst;
 	__rte_ethdev_trace_tx_burst;
 	rte_flow_get_aged_flows;
+
+	# added in 20.11
+	rte_eth_link_to_str;
+	rte_eth_link_speed_to_str;
 };
-- 
2.17.1