From: Pim van Pelt Date: Thu, 14 Aug 2025 16:56:57 +0000 (+0200) Subject: vnet: add eeprom_read_function X-Git-Tag: v26.02-rc0~88 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F50%2F43550%2F6;p=vpp.git vnet: add eeprom_read_function Introduce a device-class function to read EEPROM from interfaces. Implement it for the DPDK plugin. Type: improvement Change-Id: I8723937a5618fada2d87d7bded33bf9fc85836f3 Signed-off-by: pim@ipng.nl --- diff --git a/src/plugins/dpdk/device/common.c b/src/plugins/dpdk/device/common.c index f496226c891..9de39b07f6e 100644 --- a/src/plugins/dpdk/device/common.c +++ b/src/plugins/dpdk/device/common.c @@ -515,6 +515,70 @@ dpdk_get_vmbus_device (const struct rte_eth_dev_info *info) } #endif /* __linux__ */ +clib_error_t * +dpdk_read_eeprom (vnet_main_t *vnm, vnet_hw_interface_t *hi, + vnet_interface_eeprom_t **eeprom) +{ + dpdk_main_t *dm = &dpdk_main; + vnet_interface_main_t *im = &vnm->interface_main; + dpdk_device_t *xd; + vnet_device_class_t *dc; + struct rte_eth_dev_module_info mi = { 0 }; + struct rte_dev_eeprom_info ei = { 0 }; + + dc = vec_elt_at_index (im->device_classes, hi->dev_class_index); + *eeprom = NULL; + + if (dc->index != dpdk_device_class.index) + { + return clib_error_return (0, "Interface %v is not a DPDK interface", + hi->name); + } + + if (hi->dev_instance >= vec_len (dm->devices)) + { + return clib_error_return (0, "Invalid device instance %u", + hi->dev_instance); + } + + xd = vec_elt_at_index (dm->devices, hi->dev_instance); + + /* Get module info */ + if (rte_eth_dev_get_module_info (xd->port_id, &mi) != 0) + { + return clib_error_return ( + 0, "Module info not available for interface %v", hi->name); + } + if (mi.eeprom_len > 1024) + { + return clib_error_return (0, "EEPROM invalid length: %u bytes", + mi.eeprom_len); + } + + /* Allocate EEPROM structure */ + *eeprom = clib_mem_alloc (sizeof (vnet_interface_eeprom_t)); + if (!*eeprom) + { + return clib_error_return (0, "Memory allocation failed"); + } + + /* Get EEPROM data */ + ei.length = mi.eeprom_len; + ei.data = (*eeprom)->eeprom_raw; + + if (rte_eth_dev_get_module_eeprom (xd->port_id, &ei) != 0) + { + clib_mem_free (*eeprom); + *eeprom = NULL; + return clib_error_return (0, "EEPROM read error for interface %v", + hi->name); + } + + (*eeprom)->eeprom_len = mi.eeprom_len; + (*eeprom)->eeprom_type = mi.type; + return 0; +} + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/dpdk/device/device.c b/src/plugins/dpdk/device/device.c index ffa1d7a7bff..6b3edb01553 100644 --- a/src/plugins/dpdk/device/device.c +++ b/src/plugins/dpdk/device/device.c @@ -778,6 +778,7 @@ VNET_DEVICE_CLASS (dpdk_device_class) = { .flow_ops_function = dpdk_flow_ops_fn, .set_rss_queues_function = dpdk_interface_set_rss_queues, .rx_mode_change_function = dpdk_interface_rx_mode_change, + .eeprom_read_function = dpdk_read_eeprom, }; #define UP_DOWN_FLAG_EVENT 1 diff --git a/src/plugins/dpdk/device/dpdk.h b/src/plugins/dpdk/device/dpdk.h index 70d9cc715dc..347418ca00b 100644 --- a/src/plugins/dpdk/device/dpdk.h +++ b/src/plugins/dpdk/device/dpdk.h @@ -461,6 +461,9 @@ vnet_flow_dev_ops_function_t dpdk_flow_ops_fn; clib_error_t *unformat_rss_fn (unformat_input_t * input, uword * rss_fn); +clib_error_t *dpdk_read_eeprom (vnet_main_t *vnm, vnet_hw_interface_t *hi, + vnet_interface_eeprom_t **eeprom); + struct rte_pci_device *dpdk_get_pci_device (const struct rte_eth_dev_info *info); struct rte_vmbus_device * diff --git a/src/vnet/interface.h b/src/vnet/interface.h index c6d01859058..c9778f9bac4 100644 --- a/src/vnet/interface.h +++ b/src/vnet/interface.h @@ -98,6 +98,20 @@ typedef clib_error_t *(vnet_interface_rss_queues_set_t) (struct vnet_main_t * vnm, struct vnet_hw_interface_t * hi, clib_bitmap_t * bitmap); +/* EEPROM structure for physical network devices */ +typedef struct +{ + u32 eeprom_type; /* from linux/ethtool.h */ + u32 eeprom_len; + u8 eeprom_raw[1024]; +} vnet_interface_eeprom_t; + +/* Interface EEPROM read function */ +typedef clib_error_t *( + vnet_interface_eeprom_read_t) (struct vnet_main_t *vnm, + struct vnet_hw_interface_t *hi, + vnet_interface_eeprom_t **eeprom); + typedef enum { VNET_FLOW_DEV_OP_ADD_FLOW, @@ -306,6 +320,9 @@ typedef struct _vnet_device_class /* Interface to set rss queues of the interface */ vnet_interface_rss_queues_set_t *set_rss_queues_function; + /* Function to read EEPROM data from physical network device */ + vnet_interface_eeprom_read_t *eeprom_read_function; + } vnet_device_class_t; u32 vnet_register_device_class (vlib_main_t *, vnet_device_class_t *); diff --git a/src/vnet/interface_cli.c b/src/vnet/interface_cli.c index 4d3c98f6aa5..d0b66304858 100644 --- a/src/vnet/interface_cli.c +++ b/src/vnet/interface_cli.c @@ -2658,6 +2658,160 @@ VLIB_CLI_COMMAND (cmd_show_tx_hash, static) = { .function = show_tx_hash, }; +static void +show_interface_transceiver_output (vlib_main_t *vm, vnet_hw_interface_t *hi, + u8 show_module, u8 show_diag, + u8 show_eeprom, u8 is_terse) +{ + clib_error_t *error = 0; + vnet_main_t *vnm = vnet_get_main (); + vnet_interface_eeprom_t *eeprom = 0; + vnet_interface_main_t *im = &vnm->interface_main; + vnet_device_class_t *dc = + vec_elt_at_index (im->device_classes, hi->dev_class_index); + + if (!dc->eeprom_read_function) + { + error = clib_error_return ( + 0, "interface %v does not support EEPROM reading", hi->name); + goto done; + } + + error = dc->eeprom_read_function (vnm, hi, &eeprom); + if (error) + goto done; + + if (!eeprom) + { + error = clib_error_return ( + 0, "no EEPROM data available for interface %v", hi->name); + goto done; + } + + vlib_cli_output (vm, "Interface: %v", hi->name); + + /* Default to module if none are set */ + if (!show_module && !show_diag && !show_eeprom) + show_module = 1; + + if (show_eeprom) + { + vlib_cli_output (vm, " EEPROM Type: 0x%x", eeprom->eeprom_type); + vlib_cli_output (vm, " EEPROM Length: %u bytes", eeprom->eeprom_len); + vlib_cli_output (vm, " EEPROM Data:"); + + /* Print hexdump */ + for (u32 offset = 0; offset < eeprom->eeprom_len; offset += 16) + { + u8 *line = format (0, " %04x: ", offset); + + /* Print hex bytes */ + for (u32 j = 0; j < 16 && (offset + j) < eeprom->eeprom_len; j++) + { + line = format (line, "%02x ", eeprom->eeprom_raw[offset + j]); + } + + /* Pad to align ASCII section */ + for (u32 j = (offset + 16 > eeprom->eeprom_len) ? + eeprom->eeprom_len - offset : + 16; + j < 16; j++) + { + line = format (line, " "); + } + + line = format (line, " |"); + + /* Print ASCII representation */ + for (u32 j = 0; j < 16 && (offset + j) < eeprom->eeprom_len; j++) + { + u8 c = eeprom->eeprom_raw[offset + j]; + line = format (line, "%c", (c >= 32 && c <= 126) ? c : '.'); + } + + line = format (line, "|"); + vlib_cli_output (vm, "%v", line); + vec_free (line); + } + + vlib_cli_output (vm, ""); + } + + if (show_module) + { + vlib_cli_output (vm, " module: not implemented yet"); + } + + if (show_diag) + { + vlib_cli_output (vm, " diag: not implemented yet"); + } + +done: + if (eeprom) + clib_mem_free (eeprom); +} + +static clib_error_t * +show_interface_transceiver (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + vnet_main_t *vnm = vnet_get_main (); + vnet_interface_main_t *im = &vnm->interface_main; + u32 hw_if_index = (u32) ~0; + vnet_hw_interface_t *hi; + u8 is_terse = 1; + u8 show_diag = 0; + u8 show_module = 0; + u8 show_eeprom = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "diag")) + show_diag = 1; + else if (unformat (input, "module")) + show_module = 1; + else if (unformat (input, "eeprom")) + show_eeprom = 1; + else if (unformat (input, "verbose")) + is_terse = 0; + else if (unformat (input, "%U", unformat_vnet_hw_interface, vnm, + &hw_if_index)) + ; + else + { + return clib_error_return (0, "parse error: '%U'", + format_unformat_error, input); + } + } + + pool_foreach (hi, im->hw_interfaces) + { + if (hw_if_index == ~0 || hw_if_index == hi->hw_if_index) + { + show_interface_transceiver_output (vm, hi, show_module, show_diag, + show_eeprom, is_terse); + } + } + return 0; +} + +/*? + * This command displays the transceiver EEPROM data for a given interface. + * The EEPROM data is read from the physical transceiver module (SFP, QSFP, + * etc.) and displayed as a hexadecimal dump. + * + * @cliexpar + * Example of how to display transceiver EEPROM data: + * @cliexcmd{show interface transceiver GigabitEthernet0/8/0 module diag} + ?*/ +VLIB_CLI_COMMAND (cmd_show_interface_transceiver, static) = { + .path = "show interface transceiver", + .short_help = "show interface transceiver [] [module] [diag] " + "[eeprom] [verbose]", + .function = show_interface_transceiver, +}; + /* * fd.io coding-style-patch-verification: ON *