Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i40e: add ability to reset VF for Tx and Rx MDD events

Implement "mdd-auto-reset-vf" priv-flag to handle Tx and Rx MDD events for VFs.
This flag is also used in other network adapters like ICE.

Usage:
- "on" - The problematic VF will be automatically reset
if a malformed descriptor is detected.
- "off" - The problematic VF will be disabled.

In cases where a VF sends malformed packets classified as malicious, it can
cause the Tx queue to freeze, rendering it unusable for several minutes. When
an MDD event occurs, this new implementation allows for a graceful VF reset to
quickly restore operational state.

Currently, VF queues are disabled if an MDD event occurs. This patch adds the
ability to reset the VF if a Tx or Rx MDD event occurs. It also includes MDD
event logging throttling to avoid dmesg pollution and unifies the format of
Tx and Rx MDD messages.

Note: Standard message rate limiting functions like dev_info_ratelimited()
do not meet our requirements. Custom rate limiting is implemented,
please see the code for details.

Co-developed-by: Jan Sokolowski <jan.sokolowski@intel.com>
Signed-off-by: Jan Sokolowski <jan.sokolowski@intel.com>
Co-developed-by: Padraig J Connolly <padraig.j.connolly@intel.com>
Signed-off-by: Padraig J Connolly <padraig.j.connolly@intel.com>
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Michal Schmidt <mschmidt@redhat.com>
Tested-by: Rafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Link: https://patch.msgid.link/20250106221929.956999-13-anthony.l.nguyen@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Aleksandr Loktionov and committed by
Jakub Kicinski
07af482e 35f715cb

+123 -17
+12
Documentation/networking/device_drivers/ethernet/intel/i40e.rst
··· 299 299 ethtool --show-priv-flags ethX 300 300 ethtool --set-priv-flags ethX link-down-on-close [on|off] 301 301 302 + Setting the mdd-auto-reset-vf Private Flag 303 + ------------------------------------------ 304 + 305 + When the mdd-auto-reset-vf private flag is set to "on", the problematic VF will 306 + be automatically reset if a malformed descriptor is detected. If the flag is 307 + set to "off", the problematic VF will be disabled. 308 + 309 + Use ethtool to view and set mdd-auto-reset-vf, as follows:: 310 + 311 + ethtool --show-priv-flags ethX 312 + ethtool --set-priv-flags ethX mdd-auto-reset-vf [on|off] 313 + 302 314 Viewing Link Messages 303 315 --------------------- 304 316 Link messages will not be displayed to the console if the distribution is
+3 -1
drivers/net/ethernet/intel/i40e/i40e.h
··· 88 88 __I40E_SERVICE_SCHED, 89 89 __I40E_ADMINQ_EVENT_PENDING, 90 90 __I40E_MDD_EVENT_PENDING, 91 + __I40E_MDD_VF_PRINT_PENDING, 91 92 __I40E_VFLR_EVENT_PENDING, 92 93 __I40E_RESET_RECOVERY_PENDING, 93 94 __I40E_TIMEOUT_RECOVERY_PENDING, ··· 192 191 */ 193 192 I40E_FLAG_TOTAL_PORT_SHUTDOWN_ENA, 194 193 I40E_FLAG_VF_VLAN_PRUNING_ENA, 194 + I40E_FLAG_MDD_AUTO_RESET_VF, 195 195 I40E_PF_FLAGS_NBITS, /* must be last */ 196 196 }; 197 197 ··· 574 572 int num_alloc_vfs; /* actual number of VFs allocated */ 575 573 u32 vf_aq_requests; 576 574 u32 arq_overflows; /* Not fatal, possibly indicative of problems */ 577 - 575 + struct ratelimit_state mdd_message_rate_limit; 578 576 /* DCBx/DCBNL capability for PF that indicates 579 577 * whether DCBx is managed by firmware or host 580 578 * based agent (LLDPAD). Also, indicates what
+1 -1
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
··· 722 722 dev_info(&pf->pdev->dev, "vf %2d: VSI id=%d, seid=%d, qps=%d\n", 723 723 vf_id, vf->lan_vsi_id, vsi->seid, vf->num_queue_pairs); 724 724 dev_info(&pf->pdev->dev, " num MDD=%lld\n", 725 - vf->num_mdd_events); 725 + vf->mdd_tx_events.count + vf->mdd_rx_events.count); 726 726 } else { 727 727 dev_info(&pf->pdev->dev, "invalid VF id %d\n", vf_id); 728 728 }
+2
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
··· 459 459 I40E_PRIV_FLAG("base-r-fec", I40E_FLAG_BASE_R_FEC, 0), 460 460 I40E_PRIV_FLAG("vf-vlan-pruning", 461 461 I40E_FLAG_VF_VLAN_PRUNING_ENA, 0), 462 + I40E_PRIV_FLAG("mdd-auto-reset-vf", 463 + I40E_FLAG_MDD_AUTO_RESET_VF, 0), 462 464 }; 463 465 464 466 #define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gstrings_priv_flags)
+94 -13
drivers/net/ethernet/intel/i40e/i40e_main.c
··· 11180 11180 } 11181 11181 11182 11182 /** 11183 + * i40e_print_vf_mdd_event - print VF Tx/Rx malicious driver detect event 11184 + * @pf: board private structure 11185 + * @vf: pointer to the VF structure 11186 + * @is_tx: true - for Tx event, false - for Rx 11187 + */ 11188 + static void i40e_print_vf_mdd_event(struct i40e_pf *pf, struct i40e_vf *vf, 11189 + bool is_tx) 11190 + { 11191 + dev_err(&pf->pdev->dev, is_tx ? 11192 + "%lld Tx Malicious Driver Detection events detected on PF %d VF %d MAC %pm. mdd-auto-reset-vfs=%s\n" : 11193 + "%lld Rx Malicious Driver Detection events detected on PF %d VF %d MAC %pm. mdd-auto-reset-vfs=%s\n", 11194 + is_tx ? vf->mdd_tx_events.count : vf->mdd_rx_events.count, 11195 + pf->hw.pf_id, 11196 + vf->vf_id, 11197 + vf->default_lan_addr.addr, 11198 + str_on_off(test_bit(I40E_FLAG_MDD_AUTO_RESET_VF, pf->flags))); 11199 + } 11200 + 11201 + /** 11202 + * i40e_print_vfs_mdd_events - print VFs malicious driver detect event 11203 + * @pf: pointer to the PF structure 11204 + * 11205 + * Called from i40e_handle_mdd_event to rate limit and print VFs MDD events. 11206 + */ 11207 + static void i40e_print_vfs_mdd_events(struct i40e_pf *pf) 11208 + { 11209 + unsigned int i; 11210 + 11211 + /* check that there are pending MDD events to print */ 11212 + if (!test_and_clear_bit(__I40E_MDD_VF_PRINT_PENDING, pf->state)) 11213 + return; 11214 + 11215 + if (!__ratelimit(&pf->mdd_message_rate_limit)) 11216 + return; 11217 + 11218 + for (i = 0; i < pf->num_alloc_vfs; i++) { 11219 + struct i40e_vf *vf = &pf->vf[i]; 11220 + bool is_printed = false; 11221 + 11222 + /* only print Rx MDD event message if there are new events */ 11223 + if (vf->mdd_rx_events.count != vf->mdd_rx_events.last_printed) { 11224 + vf->mdd_rx_events.last_printed = vf->mdd_rx_events.count; 11225 + i40e_print_vf_mdd_event(pf, vf, false); 11226 + is_printed = true; 11227 + } 11228 + 11229 + /* only print Tx MDD event message if there are new events */ 11230 + if (vf->mdd_tx_events.count != vf->mdd_tx_events.last_printed) { 11231 + vf->mdd_tx_events.last_printed = vf->mdd_tx_events.count; 11232 + i40e_print_vf_mdd_event(pf, vf, true); 11233 + is_printed = true; 11234 + } 11235 + 11236 + if (is_printed && !test_bit(I40E_FLAG_MDD_AUTO_RESET_VF, pf->flags)) 11237 + dev_info(&pf->pdev->dev, 11238 + "Use PF Control I/F to re-enable the VF #%d\n", 11239 + i); 11240 + } 11241 + } 11242 + 11243 + /** 11183 11244 * i40e_handle_mdd_event 11184 11245 * @pf: pointer to the PF structure 11185 11246 * ··· 11254 11193 u32 reg; 11255 11194 int i; 11256 11195 11257 - if (!test_bit(__I40E_MDD_EVENT_PENDING, pf->state)) 11196 + if (!test_and_clear_bit(__I40E_MDD_EVENT_PENDING, pf->state)) { 11197 + /* Since the VF MDD event logging is rate limited, check if 11198 + * there are pending MDD events. 11199 + */ 11200 + i40e_print_vfs_mdd_events(pf); 11258 11201 return; 11202 + } 11259 11203 11260 11204 /* find what triggered the MDD event */ 11261 11205 reg = rd32(hw, I40E_GL_MDET_TX); ··· 11304 11238 11305 11239 /* see if one of the VFs needs its hand slapped */ 11306 11240 for (i = 0; i < pf->num_alloc_vfs && mdd_detected; i++) { 11241 + bool is_mdd_on_tx = false; 11242 + bool is_mdd_on_rx = false; 11243 + 11307 11244 vf = &(pf->vf[i]); 11308 11245 reg = rd32(hw, I40E_VP_MDET_TX(i)); 11309 11246 if (reg & I40E_VP_MDET_TX_VALID_MASK) { 11247 + set_bit(__I40E_MDD_VF_PRINT_PENDING, pf->state); 11310 11248 wr32(hw, I40E_VP_MDET_TX(i), 0xFFFF); 11311 - vf->num_mdd_events++; 11312 - dev_info(&pf->pdev->dev, "TX driver issue detected on VF %d\n", 11313 - i); 11314 - dev_info(&pf->pdev->dev, 11315 - "Use PF Control I/F to re-enable the VF\n"); 11249 + vf->mdd_tx_events.count++; 11316 11250 set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); 11251 + is_mdd_on_tx = true; 11317 11252 } 11318 11253 11319 11254 reg = rd32(hw, I40E_VP_MDET_RX(i)); 11320 11255 if (reg & I40E_VP_MDET_RX_VALID_MASK) { 11256 + set_bit(__I40E_MDD_VF_PRINT_PENDING, pf->state); 11321 11257 wr32(hw, I40E_VP_MDET_RX(i), 0xFFFF); 11322 - vf->num_mdd_events++; 11323 - dev_info(&pf->pdev->dev, "RX driver issue detected on VF %d\n", 11324 - i); 11325 - dev_info(&pf->pdev->dev, 11326 - "Use PF Control I/F to re-enable the VF\n"); 11258 + vf->mdd_rx_events.count++; 11327 11259 set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); 11260 + is_mdd_on_rx = true; 11261 + } 11262 + 11263 + if ((is_mdd_on_tx || is_mdd_on_rx) && 11264 + test_bit(I40E_FLAG_MDD_AUTO_RESET_VF, pf->flags)) { 11265 + /* VF MDD event counters will be cleared by 11266 + * reset, so print the event prior to reset. 11267 + */ 11268 + if (is_mdd_on_rx) 11269 + i40e_print_vf_mdd_event(pf, vf, false); 11270 + if (is_mdd_on_tx) 11271 + i40e_print_vf_mdd_event(pf, vf, true); 11272 + 11273 + i40e_vc_reset_vf(vf, true); 11328 11274 } 11329 11275 } 11330 11276 11331 - /* re-enable mdd interrupt cause */ 11332 - clear_bit(__I40E_MDD_EVENT_PENDING, pf->state); 11333 11277 reg = rd32(hw, I40E_PFINT_ICR0_ENA); 11334 11278 reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; 11335 11279 wr32(hw, I40E_PFINT_ICR0_ENA, reg); 11336 11280 i40e_flush(hw); 11281 + 11282 + i40e_print_vfs_mdd_events(pf); 11337 11283 } 11338 11284 11339 11285 /** ··· 15955 15877 dev_info(&pf->pdev->dev, "set phy mask fail, err %pe aq_err %s\n", 15956 15878 ERR_PTR(err), 15957 15879 i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); 15880 + 15881 + /* VF MDD event logs are rate limited to one second intervals */ 15882 + ratelimit_state_init(&pf->mdd_message_rate_limit, 1 * HZ, 1); 15958 15883 15959 15884 /* Reconfigure hardware for allowing smaller MSS in the case 15960 15885 * of TSO, so that we avoid the MDD being fired and causing
+1 -1
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
··· 216 216 * @notify_vf: notify vf about reset or not 217 217 * Reset VF handler. 218 218 **/ 219 - static void i40e_vc_reset_vf(struct i40e_vf *vf, bool notify_vf) 219 + void i40e_vc_reset_vf(struct i40e_vf *vf, bool notify_vf) 220 220 { 221 221 struct i40e_pf *pf = vf->pf; 222 222 int i;
+10 -1
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
··· 64 64 u64 max_tx_rate; /* bandwidth rate allocation for VSIs */ 65 65 }; 66 66 67 + struct i40e_mdd_vf_events { 68 + u64 count; /* total count of Rx|Tx events */ 69 + /* count number of the last printed event */ 70 + u64 last_printed; 71 + }; 72 + 67 73 /* VF information structure */ 68 74 struct i40e_vf { 69 75 struct i40e_pf *pf; ··· 98 92 99 93 u8 num_queue_pairs; /* num of qps assigned to VF vsis */ 100 94 u8 num_req_queues; /* num of requested qps */ 101 - u64 num_mdd_events; /* num of mdd events detected */ 95 + /* num of mdd tx and rx events detected */ 96 + struct i40e_mdd_vf_events mdd_rx_events; 97 + struct i40e_mdd_vf_events mdd_tx_events; 102 98 103 99 unsigned long vf_caps; /* vf's adv. capabilities */ 104 100 unsigned long vf_states; /* vf's runtime states */ ··· 128 120 int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, 129 121 u32 v_retval, u8 *msg, u16 msglen); 130 122 int i40e_vc_process_vflr_event(struct i40e_pf *pf); 123 + void i40e_vc_reset_vf(struct i40e_vf *vf, bool notify_vf); 131 124 bool i40e_reset_vf(struct i40e_vf *vf, bool flr); 132 125 bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr); 133 126 void i40e_vc_notify_vf_reset(struct i40e_vf *vf);