From: Wei Fang When a VF is driven by DPDK, the VF relies on the PF to provide accurate link speed information so that the VF-side user space application can make correct forwarding and configuration decisions. Therefore, add link speed message support for DPDK-owned VF. The PF will reply the current link speed when it receives the get link speed message from VF. With this enhancement, VFs controlled by DPDK can obtain real-time link speed information from the PF, improving overall link state visibility, synchronization between PF/VF, and enabling more accurate performance adjustments in VF-side applications. The PSI-to-VSI message is 16 bits and the high 8 bits are the message class ID. For link speed message, the low bits are the speed code, so the message supports up to 255 speed values (ENETC_MSG_SPEED_MAX = 0xff). Instead of enumerating every speed value greater than 5Gbps explicitly, introduce a formula-based approach: speed_code = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G This removes the explicit enum entries for 10G, 25G, 40G,50G, 100G and so on. Making it easy to support any future high speed without modifying the enum or the switch statement. Note that currently the link speed change notification is not supported. Signed-off-by: Wei Fang --- .../ethernet/freescale/enetc/enetc_mailbox.h | 36 ++++++++ .../net/ethernet/freescale/enetc/enetc_msg.c | 91 +++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h index 846998f07989..bd669543e96c 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_mailbox.h @@ -91,6 +91,7 @@ * The class code for the following messages is 8-bit. * 1. Get IP revision messages * 2. Link status messages + * 3. Link speed messages */ #define ENETC_PF_MSG_CLASS_CODE_U8 GENMASK(7, 0) #define ENETC_PF_MSG_CLASS_ID GENMASK(15, 8) @@ -112,6 +113,7 @@ enum enetc_msg_class_id { /* Common Class ID for PSI-to-VSI and VSI-to-PSI messages */ ENETC_MSG_CLASS_ID_MAC_FILTER = 0x20, ENETC_MSG_CLASS_ID_LINK_STATUS = 0x80, + ENETC_MSG_CLASS_ID_LINK_SPEED = 0x81, ENETC_MSG_CLASS_ID_IP_REVISION = 0xf0, }; @@ -129,6 +131,13 @@ enum enetc_msg_link_status_cmd_id { ENETC_MSG_UNREGISTER_LINK_CHANGE_NOTIFIER, }; +enum enetc_msg_link_speed_cmd_id { + ENETC_MSG_GET_CURRENT_LINK_SPEED, + /* The following command IDs are not currently supported */ + ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER, + ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER, +}; + /* Class-specific error return codes of MAC filter */ enum enetc_mac_filter_class_code { ENETC_MF_CLASS_CODE_INVALID_MAC, @@ -138,6 +147,28 @@ enum enetc_mac_filter_class_code { #define ENETC_CLASS_CODE_LINK_DOWN BIT(0) #define ENETC_CLASS_CODE_TX_PAUSE_EN BIT(1) +/* Class-specific notifications/codes of link speed */ +enum enetc_link_speed_class_code { + ENETC_MSG_SPEED_UNKNOWN, + ENETC_MSG_SPEED_10M_HD, + ENETC_MSG_SPEED_10M_FD, + ENETC_MSG_SPEED_100M_HD, + ENETC_MSG_SPEED_100M_FD, + ENETC_MSG_SPEED_1000M, + ENETC_MSG_SPEED_2500M, + ENETC_MSG_SPEED_5G, + /* Do not add enumeration values for any speed greater than + * 5Gbps. For any speed greater than 5Gbps, its speed class + * code should follow the formula below. + * + * SPEED = (link_speed - 5000) / 1000 + ENETC_MSG_SPEED_5G + * + * The unit of link_speed should be Mbps, the max SPEED + * should <= ENETC_MSG_SPEED_MAX. + */ + ENETC_MSG_SPEED_MAX = 0xff, +}; + struct enetc_msg_swbd { void *vaddr; dma_addr_t dma; @@ -181,6 +212,11 @@ struct enetc_msg_mac_exact_filter { * cmd_id 0x0: get the current link status * cmd_id 0x1: register link status change notification * cmd_id 0x2: unregister link status change notification + * + * Link speed message, class_id 0x81. + * cmd_id 0x0: get the current link speed. + * cmd_id 0x1: register link speed change notification, not supported yet + * cmd_id 0x2: unregister link speed change notification, not supported yet */ struct enetc_msg_generic { struct enetc_msg_header hdr; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c index e21414acdc0d..c3ae4c024f34 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c @@ -280,6 +280,93 @@ static u16 enetc_msg_handle_link_status(struct enetc_pf *pf, int vf_id, return 0; } +static u16 enetc_build_link_speed_msg(int speed, int duplex) +{ + u32 speed_code = ENETC_MSG_SPEED_UNKNOWN; + + switch (speed) { + case SPEED_10: + if (duplex == DUPLEX_HALF) + speed_code = ENETC_MSG_SPEED_10M_HD; + else if (duplex == DUPLEX_FULL) + speed_code = ENETC_MSG_SPEED_10M_FD; + break; + case SPEED_100: + if (duplex == DUPLEX_HALF) + speed_code = ENETC_MSG_SPEED_100M_HD; + else if (duplex == DUPLEX_FULL) + speed_code = ENETC_MSG_SPEED_100M_FD; + break; + case SPEED_1000: + speed_code = ENETC_MSG_SPEED_1000M; + break; + case SPEED_2500: + speed_code = ENETC_MSG_SPEED_2500M; + break; + case SPEED_5000: + speed_code = ENETC_MSG_SPEED_5G; + break; + default: + if (speed < SPEED_5000) + break; + + speed_code = (speed - SPEED_5000) / SPEED_1000 + + ENETC_MSG_SPEED_5G; + if (speed_code > ENETC_MSG_SPEED_MAX) + speed_code = ENETC_MSG_SPEED_UNKNOWN; + } + + return FIELD_PREP(ENETC_PF_MSG_CLASS_ID, + ENETC_MSG_CLASS_ID_LINK_SPEED) | + FIELD_PREP(ENETC_PF_MSG_CLASS_CODE_U8, speed_code); +} + +static u16 enetc_msg_get_link_speed(struct enetc_pf *pf, int vf_id) +{ + struct enetc_ndev_priv *priv = netdev_priv(pf->si->ndev); + struct enetc_vf_state *vf_state = &pf->vf_state[vf_id]; + struct ethtool_link_ksettings link_info = {}; + + /* A malicious or malfunctioning VM could potentially spam these + * messages in a tight loop causing global rtnl_lock contention, + * which may severely starve other processes on the host that + * require rtnl_lock for routine network configuration, resulting + * in a system-wide control-plane denial of service. Therefore, + * we expect the VF query for link speed to be trusted. There's no + * need to consider the transition from trusted to untrusted here, + * as this won't cause rtnl_lock() to be called frequently. + */ + mutex_lock(&vf_state->lock); + if (!(vf_state->flags & ENETC_VF_FLAG_TRUSTED)) { + mutex_unlock(&vf_state->lock); + + return ENETC_PF_MSG_PERM_DENY; + } + mutex_unlock(&vf_state->lock); + + rtnl_lock(); + phylink_ethtool_ksettings_get(priv->phylink, &link_info); + rtnl_unlock(); + + return enetc_build_link_speed_msg(link_info.base.speed, + link_info.base.duplex); +} + +static u16 enetc_msg_handle_link_speed(struct enetc_pf *pf, int vf_id, + void *vf_msg) +{ + struct enetc_msg_header *msg_hdr = vf_msg; + + switch (msg_hdr->cmd_id) { + case ENETC_MSG_GET_CURRENT_LINK_SPEED: + return enetc_msg_get_link_speed(pf, vf_id); + case ENETC_MSG_REGISTER_SPEED_CHANGE_NOTIFIER: + case ENETC_MSG_UNREGISTER_SPEED_CHANGE_NOTIFIER: + default: + return ENETC_PF_MSG_NOTSUPP; + } +} + /* If *pf_msg is set to 0, it means that PF has responded to VF in * enetc_msg_handle_rxmsg() through enetc_pf_reply_msg(), which also * clears the corresponding VF MR bit in PSIIDR. @@ -362,6 +449,9 @@ static void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, case ENETC_MSG_CLASS_ID_LINK_STATUS: *pf_msg = enetc_msg_handle_link_status(pf, vf_id, msg); break; + case ENETC_MSG_CLASS_ID_LINK_SPEED: + *pf_msg = enetc_msg_handle_link_speed(pf, vf_id, msg); + break; default: dev_err_ratelimited(dev, "Unsupported message class ID: 0x%x\n", @@ -546,6 +636,7 @@ int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs) dev_err(&pdev->dev, "pci_enable_sriov err %d\n", err); goto err_en_sriov; } + } return num_vfs; -- 2.34.1