diff --git a/doc/guides/rel_notes/release_24_03.rst b/doc/guides/rel_notes/release_24_03.rst
index 932688ca4d..75d93ee965 100644
--- a/doc/guides/rel_notes/release_24_03.rst
+++ b/doc/guides/rel_notes/release_24_03.rst
@@ -76,6 +76,10 @@ New Features
   * Added a fath path function ``rte_eth_tx_queue_count``
     to get the number of used descriptors of a Tx queue.
 
+* **Support setting lanes for ethdev.**
+  * Support setting lanes by extended ``RTE_ETH_LINK_SPEED_*``.
+  * Added function to convert bitmap flag to the struct of link speed info.
+
 * **Added hash calculation of an encapsulated packet as done by the HW.**
 
   Added function to calculate hash when doing tunnel encapsulation:
@@ -240,9 +244,11 @@ ABI Changes
    This section is a comment. Do not overwrite or remove it.
    Also, make sure to start the actual text at the margin.
    =======================================================
-
 * No ABI change that would break compatibility with 23.11.
 
+* ethdev: Convert a numerical speed to a bitmap flag with lanes:
+  The function ``rte_eth_speed_bitflag`` add lanes parameters.
+
 
 Known Issues
 ------------
diff --git a/drivers/net/bnxt/bnxt_ethdev.c b/drivers/net/bnxt/bnxt_ethdev.c
index ba31ae9286..e881a7f3cc 100644
--- a/drivers/net/bnxt/bnxt_ethdev.c
+++ b/drivers/net/bnxt/bnxt_ethdev.c
@@ -711,7 +711,8 @@ static int bnxt_update_phy_setting(struct bnxt *bp)
 	}
 
 	/* convert to speedbit flag */
-	curr_speed_bit = rte_eth_speed_bitflag((uint32_t)link->link_speed, 1);
+	curr_speed_bit = rte_eth_speed_bitflag((uint32_t)link->link_speed,
+					       RTE_ETH_LANES_UNKNOWN, 1);
 
 	/*
 	 * Device is not obliged link down in certain scenarios, even
diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c
index b10d1216d2..ecd3b2ef64 100644
--- a/drivers/net/hns3/hns3_ethdev.c
+++ b/drivers/net/hns3/hns3_ethdev.c
@@ -5969,6 +5969,7 @@ hns3_get_speed_fec_capa(struct rte_eth_fec_capa *speed_fec_capa,
 	for (i = 0; i < RTE_DIM(speed_fec_capa_tbl); i++) {
 		speed_bit =
 			rte_eth_speed_bitflag(speed_fec_capa_tbl[i].speed,
+					      RTE_ETH_LANES_UNKNOWN,
 					      RTE_ETH_LINK_FULL_DUPLEX);
 		if ((speed_capa & speed_bit) == 0)
 			continue;
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0dbf2dd6a2..bb7dc7acb7 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -2003,7 +2003,6 @@ __rte_internal
 int
 rte_eth_ip_reassembly_dynfield_register(int *field_offset, int *flag);
 
-
 /*
  * Legacy ethdev API used internally by drivers.
  */
diff --git a/lib/ethdev/ethdev_linux_ethtool.c b/lib/ethdev/ethdev_linux_ethtool.c
index e792204b01..b776ec6173 100644
--- a/lib/ethdev/ethdev_linux_ethtool.c
+++ b/lib/ethdev/ethdev_linux_ethtool.c
@@ -111,10 +111,107 @@ static const uint32_t link_modes[] = {
 	[101] =      11, /* ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT */
 };
 
+/*
+ * Link modes sorted with index as defined in ethtool.
+ * Values are lanes.
+ */
+static const uint32_t link_modes_lanes[] = {
+	  [0] =      1, /* ETHTOOL_LINK_MODE_10baseT_Half_BIT */
+	  [1] =      1, /* ETHTOOL_LINK_MODE_10baseT_Full_BIT */
+	  [2] =      1, /* ETHTOOL_LINK_MODE_100baseT_Half_BIT */
+	  [3] =      1, /* ETHTOOL_LINK_MODE_100baseT_Full_BIT */
+	  [4] =      1, /* ETHTOOL_LINK_MODE_1000baseT_Half_BIT */
+	  [5] =      1, /* ETHTOOL_LINK_MODE_1000baseT_Full_BIT */
+	 [12] =      1, /* ETHTOOL_LINK_MODE_10000baseT_Full_BIT */
+	 [15] =      1, /* ETHTOOL_LINK_MODE_2500baseX_Full_BIT */
+	 [17] =      1, /* ETHTOOL_LINK_MODE_1000baseKX_Full_BIT */
+	 [18] =      4, /* ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT */
+	 [19] =      1, /* ETHTOOL_LINK_MODE_10000baseKR_Full_BIT */
+	 [20] =      1, /* ETHTOOL_LINK_MODE_10000baseR_FEC_BIT */
+	 [21] =      2, /* ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT */
+	 [22] =      2, /* ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT */
+	 [23] =      4, /* ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT */
+	 [24] =      4, /* ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT */
+	 [25] =      4, /* ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT */
+	 [26] =      4, /* ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT */
+	 [27] =      4, /* ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT */
+	 [28] =      4, /* ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT */
+	 [29] =      4, /* ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT */
+	 [30] =      4, /* ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT */
+	 [31] =      1, /* ETHTOOL_LINK_MODE_25000baseCR_Full_BIT */
+	 [32] =      1, /* ETHTOOL_LINK_MODE_25000baseKR_Full_BIT */
+	 [33] =      1, /* ETHTOOL_LINK_MODE_25000baseSR_Full_BIT */
+	 [34] =      2, /* ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT */
+	 [35] =      2, /* ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT */
+	 [36] =      4, /* ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT */
+	 [37] =      4, /* ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT */
+	 [38] =      4, /* ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT */
+	 [39] =      4, /* ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT */
+	 [40] =      2, /* ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT */
+	 [41] =      1, /* ETHTOOL_LINK_MODE_1000baseX_Full_BIT */
+	 [42] =      1, /* ETHTOOL_LINK_MODE_10000baseCR_Full_BIT */
+	 [43] =      1, /* ETHTOOL_LINK_MODE_10000baseSR_Full_BIT */
+	 [44] =      1, /* ETHTOOL_LINK_MODE_10000baseLR_Full_BIT */
+	 [45] =      1, /* ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT */
+	 [46] =      1, /* ETHTOOL_LINK_MODE_10000baseER_Full_BIT */
+	 [47] =      1, /* ETHTOOL_LINK_MODE_2500baseT_Full_BIT */
+	 [48] =      1, /* ETHTOOL_LINK_MODE_5000baseT_Full_BIT */
+	 [52] =      1, /* ETHTOOL_LINK_MODE_50000baseKR_Full_BIT */
+	 [53] =      1, /* ETHTOOL_LINK_MODE_50000baseSR_Full_BIT */
+	 [54] =      1, /* ETHTOOL_LINK_MODE_50000baseCR_Full_BIT */
+	 [55] =      1, /* ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT */
+	 [56] =      1, /* ETHTOOL_LINK_MODE_50000baseDR_Full_BIT */
+	 [57] =      2, /* ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT */
+	 [58] =      2, /* ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT */
+	 [59] =      2, /* ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT */
+	 [60] =      2, /* ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT */
+	 [61] =      2, /* ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT */
+	 [62] =      4, /* ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT */
+	 [63] =      4, /* ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT */
+	 [64] =      4, /* ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT */
+	 [65] =      4, /* ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT */
+	 [66] =      4, /* ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT */
+	 [67] =      1, /* ETHTOOL_LINK_MODE_100baseT1_Full_BIT */
+	 [68] =      1, /* ETHTOOL_LINK_MODE_1000baseT1_Full_BIT */
+	 [69] =      8, /* ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT */
+	 [70] =      8, /* ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT */
+	 [71] =      8, /* ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT */
+	 [72] =      8, /* ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT */
+	 [73] =      8, /* ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT */
+	 [75] =      1, /* ETHTOOL_LINK_MODE_100000baseKR_Full_BIT */
+	 [76] =      1, /* ETHTOOL_LINK_MODE_100000baseSR_Full_BIT */
+	 [77] =      1, /* ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT */
+	 [78] =      1, /* ETHTOOL_LINK_MODE_100000baseCR_Full_BIT */
+	 [79] =      1, /* ETHTOOL_LINK_MODE_100000baseDR_Full_BIT */
+	 [80] =      2, /* ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT */
+	 [81] =      2, /* ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT */
+	 [82] =      2, /* ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT */
+	 [83] =      2, /* ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT */
+	 [84] =      2, /* ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT */
+	 [85] =      4, /* ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT */
+	 [86] =      4, /* ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT */
+	 [87] =      4, /* ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT */
+	 [88] =      4, /* ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT */
+	 [89] =      4, /* ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT */
+	 [90] =      1, /* ETHTOOL_LINK_MODE_100baseFX_Half_BIT */
+	 [91] =      1, /* ETHTOOL_LINK_MODE_100baseFX_Full_BIT */
+	 [92] =      1, /* ETHTOOL_LINK_MODE_10baseT1L_Full_BIT */
+	 [93] =      8, /* ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT */
+	 [94] =      8, /* ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT */
+	 [95] =      8, /* ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT */
+	 [96] =      8, /* ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT */
+	 [97] =      8, /* ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT */
+	 [98] =      8, /* ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT */
+	 [99] =      1, /* ETHTOOL_LINK_MODE_10baseT1S_Full_BIT */
+	[100] =      1, /* ETHTOOL_LINK_MODE_10baseT1S_Half_BIT */
+	[101] =      1, /* ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT */
+};
+
 uint32_t
 rte_eth_link_speed_ethtool(enum ethtool_link_mode_bit_indices bit)
 {
 	uint32_t speed;
+	uint8_t lanes;
 	int duplex;
 
 	/* get mode from array */
@@ -131,7 +228,9 @@ rte_eth_link_speed_ethtool(enum ethtool_link_mode_bit_indices bit)
 			RTE_ETH_LINK_FULL_DUPLEX;
 	speed &= RTE_GENMASK32(31, 1);
 
-	return rte_eth_speed_bitflag(speed, duplex);
+	lanes = link_modes_lanes[bit];
+
+	return rte_eth_speed_bitflag(speed, lanes, duplex);
 }
 
 uint32_t
diff --git a/lib/ethdev/ethdev_private.h b/lib/ethdev/ethdev_private.h
index 0d36b9c30f..9092ab3a9e 100644
--- a/lib/ethdev/ethdev_private.h
+++ b/lib/ethdev/ethdev_private.h
@@ -79,4 +79,8 @@ void eth_dev_txq_release(struct rte_eth_dev *dev, uint16_t qid);
 int eth_dev_rx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues);
 int eth_dev_tx_queue_config(struct rte_eth_dev *dev, uint16_t nb_queues);
 
+/* versioned functions */
+uint32_t rte_eth_speed_bitflag_v24(uint32_t speed, int duplex);
+uint32_t rte_eth_speed_bitflag_v25(uint32_t speed, uint8_t lanes, int duplex);
+
 #endif /* _ETH_PRIVATE_H_ */
diff --git a/lib/ethdev/ethdev_trace.h b/lib/ethdev/ethdev_trace.h
index 3bec87bfdb..5547b49cab 100644
--- a/lib/ethdev/ethdev_trace.h
+++ b/lib/ethdev/ethdev_trace.h
@@ -183,8 +183,10 @@ RTE_TRACE_POINT(
 
 RTE_TRACE_POINT(
 	rte_eth_trace_speed_bitflag,
-	RTE_TRACE_POINT_ARGS(uint32_t speed, int duplex, uint32_t ret),
+	RTE_TRACE_POINT_ARGS(uint32_t speed, uint8_t lanes, int duplex,
+			     uint32_t ret),
 	rte_trace_point_emit_u32(speed);
+	rte_trace_point_emit_u8(lanes);
 	rte_trace_point_emit_int(duplex);
 	rte_trace_point_emit_u32(ret);
 )
diff --git a/lib/ethdev/meson.build b/lib/ethdev/meson.build
index f1d2586591..2c9588d0b3 100644
--- a/lib/ethdev/meson.build
+++ b/lib/ethdev/meson.build
@@ -62,3 +62,5 @@ endif
 if get_option('buildtype').contains('debug')
     cflags += ['-DRTE_FLOW_DEBUG']
 endif
+
+use_function_versioning = true
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..522f8796b1 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -26,6 +26,7 @@
 #include <rte_class.h>
 #include <rte_ether.h>
 #include <rte_telemetry.h>
+#include <rte_function_versioning.h>
 
 #include "rte_ethdev.h"
 #include "rte_ethdev_trace_fp.h"
@@ -991,63 +992,111 @@ rte_eth_dev_tx_queue_stop(uint16_t port_id, uint16_t tx_queue_id)
 	return ret;
 }
 
-uint32_t
-rte_eth_speed_bitflag(uint32_t speed, int duplex)
+uint32_t __vsym
+rte_eth_speed_bitflag_v25(uint32_t speed, uint8_t lanes, int duplex)
 {
-	uint32_t ret;
+	uint32_t ret = 0;
 
 	switch (speed) {
 	case RTE_ETH_SPEED_NUM_10M:
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_1)
+			break;
 		ret = duplex ? RTE_ETH_LINK_SPEED_10M : RTE_ETH_LINK_SPEED_10M_HD;
 		break;
 	case RTE_ETH_SPEED_NUM_100M:
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_1)
+			break;
 		ret = duplex ? RTE_ETH_LINK_SPEED_100M : RTE_ETH_LINK_SPEED_100M_HD;
 		break;
 	case RTE_ETH_SPEED_NUM_1G:
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_1)
+			break;
 		ret = RTE_ETH_LINK_SPEED_1G;
 		break;
 	case RTE_ETH_SPEED_NUM_2_5G:
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_1)
+			break;
 		ret = RTE_ETH_LINK_SPEED_2_5G;
 		break;
 	case RTE_ETH_SPEED_NUM_5G:
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_1)
+			break;
 		ret = RTE_ETH_LINK_SPEED_5G;
 		break;
 	case RTE_ETH_SPEED_NUM_10G:
-		ret = RTE_ETH_LINK_SPEED_10G;
+		if (lanes == RTE_ETH_LANES_1)
+			ret = RTE_ETH_LINK_SPEED_10G;
+		if (lanes == RTE_ETH_LANES_4)
+			ret = RTE_ETH_LINK_SPEED_10G_4LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_20G:
-		ret = RTE_ETH_LINK_SPEED_20G;
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_2)
+			break;
+		ret = RTE_ETH_LINK_SPEED_20G_2LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_25G:
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_1)
+			break;
 		ret = RTE_ETH_LINK_SPEED_25G;
 		break;
 	case RTE_ETH_SPEED_NUM_40G:
-		ret = RTE_ETH_LINK_SPEED_40G;
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_4)
+			break;
+		ret = RTE_ETH_LINK_SPEED_40G_4LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_50G:
-		ret = RTE_ETH_LINK_SPEED_50G;
+		if (lanes == RTE_ETH_LANES_1)
+			ret = RTE_ETH_LINK_SPEED_50G;
+		if (lanes == RTE_ETH_LANES_2)
+			ret = RTE_ETH_LINK_SPEED_50G_2LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_56G:
-		ret = RTE_ETH_LINK_SPEED_56G;
+		if (lanes != RTE_ETH_LANES_UNKNOWN && lanes != RTE_ETH_LANES_4)
+			break;
+		ret = RTE_ETH_LINK_SPEED_56G_4LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_100G:
-		ret = RTE_ETH_LINK_SPEED_100G;
+		if (lanes == RTE_ETH_LANES_1)
+			ret = RTE_ETH_LINK_SPEED_100G;
+		if (lanes == RTE_ETH_LANES_2)
+			ret = RTE_ETH_LINK_SPEED_100G_2LANES;
+		if (lanes == RTE_ETH_LANES_4)
+			ret = RTE_ETH_LINK_SPEED_100G_4LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_200G:
-		ret = RTE_ETH_LINK_SPEED_200G;
+		if (lanes == RTE_ETH_LANES_2)
+			ret = RTE_ETH_LINK_SPEED_200G_2LANES;
+		if (lanes == RTE_ETH_LANES_4)
+			ret = RTE_ETH_LINK_SPEED_200G_4LANES;
 		break;
 	case RTE_ETH_SPEED_NUM_400G:
+		if (lanes == RTE_ETH_LANES_4)
+			ret = RTE_ETH_LINK_SPEED_400G_4LANES;
+		if (lanes == RTE_ETH_LANES_8)
+			ret = RTE_ETH_LINK_SPEED_400G_8LANES;
 		ret = RTE_ETH_LINK_SPEED_400G;
 		break;
 	default:
 		ret = 0;
 	}
 
-	rte_eth_trace_speed_bitflag(speed, duplex, ret);
+	rte_eth_trace_speed_bitflag(speed, lanes, duplex, ret);
 
 	return ret;
 }
 
+uint32_t __vsym
+rte_eth_speed_bitflag_v24(uint32_t speed, int duplex)
+{
+	return rte_eth_speed_bitflag_v25(speed, RTE_ETH_LANES_UNKNOWN, duplex);
+}
+
+/* mark the v24 function as the older version, and v25 as the default version */
+VERSION_SYMBOL(rte_eth_speed_bitflag, _v24, 24);
+BIND_DEFAULT_SYMBOL(rte_eth_speed_bitflag, _v25, 25);
+MAP_STATIC_SYMBOL(uint32_t rte_eth_speed_bitflag(uint32_t speed, uint8_t lanes, int duplex),
+		  rte_eth_speed_bitflag_v25);
+
 const char *
 rte_eth_dev_rx_offload_name(uint64_t offload)
 {
@@ -1066,6 +1115,204 @@ rte_eth_dev_rx_offload_name(uint64_t offload)
 	return name;
 }
 
+int
+rte_eth_speed_capa_to_info(uint32_t link_speed,
+			   struct rte_eth_speed_capa_info *capa_info)
+{
+	const struct {
+		uint32_t speed_capa;
+		struct rte_eth_speed_capa_info capa_info;
+	} speed_capa_info_map[] = {
+		{
+			RTE_ETH_LINK_SPEED_10M_HD,
+				{
+					RTE_ETH_SPEED_NUM_10M,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_HALF_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_10M,
+				{
+					RTE_ETH_SPEED_NUM_10M,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_100M_HD,
+				{
+					RTE_ETH_SPEED_NUM_100M,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_HALF_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_100M,
+				{
+					RTE_ETH_SPEED_NUM_100M,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_1G,
+				{
+					RTE_ETH_SPEED_NUM_1G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_2_5G,
+				{
+					RTE_ETH_SPEED_NUM_2_5G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_5G,
+				{
+					RTE_ETH_SPEED_NUM_5G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_10G,
+				{
+					RTE_ETH_SPEED_NUM_10G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_20G_2LANES,
+				{
+					RTE_ETH_SPEED_NUM_20G,
+					RTE_ETH_LANES_2,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_25G,
+				{
+					RTE_ETH_SPEED_NUM_25G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_40G_4LANES,
+				{
+					RTE_ETH_SPEED_NUM_40G,
+					RTE_ETH_LANES_4,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_50G,
+				{
+					RTE_ETH_SPEED_NUM_50G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_56G_4LANES,
+				{
+					RTE_ETH_SPEED_NUM_56G,
+					RTE_ETH_LANES_4,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_100G,
+				{
+					RTE_ETH_SPEED_NUM_100G,
+					RTE_ETH_LANES_1,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_200G_4LANES,
+				{
+					RTE_ETH_SPEED_NUM_200G,
+					RTE_ETH_LANES_4,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_400G_4LANES,
+				{
+					RTE_ETH_SPEED_NUM_400G,
+					RTE_ETH_LANES_4,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_10G_4LANES,
+				{
+					RTE_ETH_SPEED_NUM_10G,
+					RTE_ETH_LANES_4,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_50G_2LANES,
+				{
+					RTE_ETH_SPEED_NUM_50G,
+					RTE_ETH_LANES_2,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_100G_2LANES,
+				{
+					RTE_ETH_SPEED_NUM_100G,
+					RTE_ETH_LANES_2,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_100G_4LANES,
+				{
+					RTE_ETH_SPEED_NUM_100G,
+					RTE_ETH_LANES_4,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_200G_2LANES,
+				{
+					RTE_ETH_SPEED_NUM_200G,
+					RTE_ETH_LANES_2,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		},
+		{
+			RTE_ETH_LINK_SPEED_400G_8LANES,
+				{
+					RTE_ETH_SPEED_NUM_400G,
+					RTE_ETH_LANES_8,
+					RTE_ETH_LINK_FULL_DUPLEX
+				}
+		}
+	};
+	uint32_t i;
+
+	for (i = 0; i < RTE_DIM(speed_capa_info_map); i++)
+		if (link_speed == speed_capa_info_map[i].speed_capa) {
+			capa_info->speed = speed_capa_info_map[i].capa_info.speed;
+			capa_info->lanes = speed_capa_info_map[i].capa_info.lanes;
+			capa_info->duplex = speed_capa_info_map[i].capa_info.duplex;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
 const char *
 rte_eth_dev_tx_offload_name(uint64_t offload)
 {
@@ -3111,8 +3358,9 @@ rte_eth_link_to_str(char *str, size_t len, const struct rte_eth_link *eth_link)
 	if (eth_link->link_status == RTE_ETH_LINK_DOWN)
 		ret = snprintf(str, len, "Link down");
 	else
-		ret = snprintf(str, len, "Link up at %s %s %s",
+		ret = snprintf(str, len, "Link up at %s %ulanes %s %s",
 			rte_eth_link_speed_to_str(eth_link->link_speed),
+			eth_link->link_lanes,
 			(eth_link->link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ?
 			"FDX" : "HDX",
 			(eth_link->link_autoneg == RTE_ETH_LINK_AUTONEG) ?
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 147257d6a2..3383ad8495 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -288,24 +288,40 @@ struct rte_eth_stats {
 /**@{@name Link speed capabilities
  * Device supported speeds bitmap flags
  */
-#define RTE_ETH_LINK_SPEED_AUTONEG 0             /**< Autonegotiate (all speeds) */
-#define RTE_ETH_LINK_SPEED_FIXED   RTE_BIT32(0)  /**< Disable autoneg (fixed speed) */
-#define RTE_ETH_LINK_SPEED_10M_HD  RTE_BIT32(1)  /**<  10 Mbps half-duplex */
-#define RTE_ETH_LINK_SPEED_10M     RTE_BIT32(2)  /**<  10 Mbps full-duplex */
-#define RTE_ETH_LINK_SPEED_100M_HD RTE_BIT32(3)  /**< 100 Mbps half-duplex */
-#define RTE_ETH_LINK_SPEED_100M    RTE_BIT32(4)  /**< 100 Mbps full-duplex */
-#define RTE_ETH_LINK_SPEED_1G      RTE_BIT32(5)  /**<   1 Gbps */
-#define RTE_ETH_LINK_SPEED_2_5G    RTE_BIT32(6)  /**< 2.5 Gbps */
-#define RTE_ETH_LINK_SPEED_5G      RTE_BIT32(7)  /**<   5 Gbps */
-#define RTE_ETH_LINK_SPEED_10G     RTE_BIT32(8)  /**<  10 Gbps */
-#define RTE_ETH_LINK_SPEED_20G     RTE_BIT32(9)  /**<  20 Gbps */
-#define RTE_ETH_LINK_SPEED_25G     RTE_BIT32(10) /**<  25 Gbps */
-#define RTE_ETH_LINK_SPEED_40G     RTE_BIT32(11) /**<  40 Gbps */
-#define RTE_ETH_LINK_SPEED_50G     RTE_BIT32(12) /**<  50 Gbps */
-#define RTE_ETH_LINK_SPEED_56G     RTE_BIT32(13) /**<  56 Gbps */
-#define RTE_ETH_LINK_SPEED_100G    RTE_BIT32(14) /**< 100 Gbps */
-#define RTE_ETH_LINK_SPEED_200G    RTE_BIT32(15) /**< 200 Gbps */
-#define RTE_ETH_LINK_SPEED_400G    RTE_BIT32(16) /**< 400 Gbps */
+#define RTE_ETH_LINK_SPEED_AUTONEG        0             /**< Autonegotiate (all speeds) */
+#define RTE_ETH_LINK_SPEED_FIXED          RTE_BIT32(0)  /**< Disable autoneg (fixed speed) */
+#define RTE_ETH_LINK_SPEED_10M_HD         RTE_BIT32(1)  /**<  10 Mbps half-duplex */
+#define RTE_ETH_LINK_SPEED_10M            RTE_BIT32(2)  /**<  10 Mbps full-duplex */
+#define RTE_ETH_LINK_SPEED_100M_HD        RTE_BIT32(3)  /**< 100 Mbps half-duplex */
+#define RTE_ETH_LINK_SPEED_100M           RTE_BIT32(4)  /**< 100 Mbps full-duplex */
+#define RTE_ETH_LINK_SPEED_1G             RTE_BIT32(5)  /**<   1 Gbps */
+#define RTE_ETH_LINK_SPEED_2_5G           RTE_BIT32(6)  /**< 2.5 Gbps */
+#define RTE_ETH_LINK_SPEED_5G             RTE_BIT32(7)  /**<   5 Gbps */
+#define RTE_ETH_LINK_SPEED_10G            RTE_BIT32(8)  /**<  10 Gbps */
+#define RTE_ETH_LINK_SPEED_20G_2LANES     RTE_BIT32(9)  /**<  20 Gbps 2lanes */
+#define RTE_ETH_LINK_SPEED_25G            RTE_BIT32(10) /**<  25 Gbps */
+#define RTE_ETH_LINK_SPEED_40G_4LANES     RTE_BIT32(11) /**<  40 Gbps 4lanes */
+#define RTE_ETH_LINK_SPEED_50G            RTE_BIT32(12) /**<  50 Gbps */
+#define RTE_ETH_LINK_SPEED_56G_4LANES     RTE_BIT32(13) /**<  56 Gbps  4lanes */
+#define RTE_ETH_LINK_SPEED_100G           RTE_BIT32(14) /**< 100 Gbps */
+#define RTE_ETH_LINK_SPEED_200G_4LANES    RTE_BIT32(15) /**< 200 Gbps 4lanes */
+#define RTE_ETH_LINK_SPEED_400G_4LANES    RTE_BIT32(16) /**< 400 Gbps 4lanes */
+#define RTE_ETH_LINK_SPEED_10G_4LANES     RTE_BIT32(17)  /**<  10 Gbps 4lanes */
+#define RTE_ETH_LINK_SPEED_50G_2LANES     RTE_BIT32(18) /**<  50 Gbps 2 lanes */
+#define RTE_ETH_LINK_SPEED_100G_2LANES    RTE_BIT32(19) /**< 100 Gbps 2 lanes */
+#define RTE_ETH_LINK_SPEED_100G_4LANES    RTE_BIT32(20) /**< 100 Gbps 4lanes */
+#define RTE_ETH_LINK_SPEED_200G_2LANES    RTE_BIT32(21) /**< 200 Gbps 2lanes */
+#define RTE_ETH_LINK_SPEED_400G_8LANES    RTE_BIT32(22) /**< 400 Gbps 8lanes */
+/**@}*/
+
+/**@{@name Link speed capabilities
+ * Default lanes, use to compatible with earlier versions
+ */
+#define RTE_ETH_LINK_SPEED_20G	RTE_ETH_LINK_SPEED_20G_2LANES
+#define RTE_ETH_LINK_SPEED_40G	RTE_ETH_LINK_SPEED_40G_4LANES
+#define RTE_ETH_LINK_SPEED_56G	RTE_ETH_LINK_SPEED_56G_4LANES
+#define RTE_ETH_LINK_SPEED_200G	RTE_ETH_LINK_SPEED_200G_4LANES
+#define RTE_ETH_LINK_SPEED_400G	RTE_ETH_LINK_SPEED_400G_4LANES
 /**@}*/
 
 /**@{@name Link speed
@@ -329,6 +345,25 @@ struct rte_eth_stats {
 #define RTE_ETH_SPEED_NUM_UNKNOWN UINT32_MAX /**< Unknown */
 /**@}*/
 
+/**@{@name Link lane number
+ * Ethernet lane number
+ */
+#define RTE_ETH_LANES_UNKNOWN    0 /**< Unknown */
+#define RTE_ETH_LANES_1          1 /**< 1 lanes */
+#define RTE_ETH_LANES_2          2 /**< 2 lanes */
+#define RTE_ETH_LANES_4          4 /**< 4 lanes */
+#define RTE_ETH_LANES_8          8 /**< 8 lanes */
+/**@}*/
+
+/**
+ * A structure used to store information of link speed capability.
+ */
+struct rte_eth_speed_capa_info {
+	uint32_t speed;        /**< RTE_ETH_SPEED_NUM_ */
+	uint8_t  lanes;        /**< RTE_ETH_LANES_ */
+	uint8_t  duplex;       /**< RTE_ETH_LANES_ */
+};
+
 /**
  * A structure used to retrieve link-level information of an Ethernet port.
  */
@@ -338,6 +373,7 @@ struct __rte_aligned(8) rte_eth_link { /**< aligned for atomic64 read/write */
 	uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
 	uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
 	uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
+	uint16_t link_lanes   : 8;  /**< RTE_ETH_LANES_ */
 };
 
 /**@{@name Link negotiation
@@ -1641,6 +1677,13 @@ struct rte_eth_conf {
 #define RTE_ETH_DEV_CAPA_FLOW_RULE_KEEP         RTE_BIT64(3)
 /** Device supports keeping shared flow objects across restart. */
 #define RTE_ETH_DEV_CAPA_FLOW_SHARED_OBJECT_KEEP RTE_BIT64(4)
+/**
+ * Device supports setting lanes. When the driver does not support setting lane,
+ * the lane in the speed capability reported by the driver may be incorrect,
+ * for example, if the driver reports that the 200G speed capability
+ * (@see RTE_ETH_LINK_SPEED_200G), the number of used lanes may be 2 or 4.
+ */
+#define RTE_ETH_DEV_CAPA_SETTING_LANES RTE_BIT64(4)
 /**@}*/
 
 /*
@@ -2301,12 +2344,30 @@ uint16_t rte_eth_dev_count_total(void);
  *
  * @param speed
  *   Numerical speed value in Mbps
+ * @param lanes
+ *   RTE_ETH_LANES_x
  * @param duplex
  *   RTE_ETH_LINK_[HALF/FULL]_DUPLEX (only for 10/100M speeds)
  * @return
  *   0 if the speed cannot be mapped
  */
-uint32_t rte_eth_speed_bitflag(uint32_t speed, int duplex);
+uint32_t rte_eth_speed_bitflag(uint32_t speed, uint8_t lanes, int duplex);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Convert bitmap flag to the struct of link speed capability info
+ *
+ * @param link_speed
+ *   speed bitmap (RTE_ETH_LINK_SPEED_)
+ * @return
+ *   - (0) if successful.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_speed_capa_to_info(uint32_t link_speed,
+				struct rte_eth_speed_capa_info *capa_info);
 
 /**
  * Get RTE_ETH_RX_OFFLOAD_* flag name.
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 79f6f5293b..0e9c560920 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -169,6 +169,12 @@ DPDK_24 {
 	local: *;
 };
 
+DPDK_25 {
+	global:
+
+	rte_eth_speed_bitflag;
+} DPDK_24;
+
 EXPERIMENTAL {
 	global:
 
@@ -320,6 +326,7 @@ EXPERIMENTAL {
 	# added in 24.03
 	__rte_eth_trace_tx_queue_count;
 	rte_eth_find_rss_algo;
+	rte_eth_speed_capa_to_info;
 	rte_flow_async_update_resized;
 	rte_flow_calc_encap_hash;
 	rte_flow_template_table_resizable;
