ice: Add host SR-IOV support

Enable basic SR-IOV support for E800 adapters.

Authored-by: Eric Joyner <erj@FreeBSD.org>
Signed-off-by: Krzysztof Galazka <krzysztof.galazka@intel.com>
Reviewed by: imp
Pull Request: https://github.com/freebsd/freebsd-src/pull/1573
This commit is contained in:
Krzysztof Galazka 2025-01-16 00:21:04 +01:00 committed by Warner Losh
parent 7986051313
commit bc761988b7
11 changed files with 2549 additions and 0 deletions

View file

@ -191,6 +191,10 @@ dev/ice/irdma_di_if.m optional ice pci \
compile-with "${NORMAL_M} -I$S/dev/ice"
dev/ice/ice_ddp_common.c optional ice pci \
compile-with "${NORMAL_C} -I$S/dev/ice"
dev/ice/ice_iov.c optional ice pci pci_iov \
compile-with "${NORMAL_C} -I$S/dev/ice"
dev/ice/ice_vf_mbx.c optional ice pci pci_iov \
compile-with "${NORMAL_C} -I$S/dev/ice"
ice_ddp.c optional ice_ddp \
compile-with "${AWK} -f $S/tools/fw_stub.awk ice_ddp.fw:ice_ddp:0x01032900 -mice_ddp -c${.TARGET}" \
no-ctfconvert no-implicit-rule before-depend local \

View file

@ -91,7 +91,9 @@ enum feat_list {
static inline void
ice_disable_unsupported_features(ice_bitmap_t __unused *bitmap)
{
#ifndef PCI_IOV
ice_clear_bit(ICE_FEATURE_SRIOV, bitmap);
#endif
#ifndef DEV_NETMAP
ice_clear_bit(ICE_FEATURE_NETMAP, bitmap);
#endif

View file

@ -139,6 +139,9 @@ struct ice_irq_vector {
* @tc: traffic class queue belongs to
* @q_handle: qidx in tc; used in TXQ enable functions
*
* ice_iov.c requires the following parameters (when PCI_IOV is defined):
* @itr_idx: ITR index to use for this queue
*
* Other parameters may be iflib driver specific
*/
struct ice_tx_queue {
@ -153,6 +156,9 @@ struct ice_tx_queue {
u32 me;
u16 q_handle;
u8 tc;
#ifdef PCI_IOV
u8 itr_idx;
#endif
/* descriptor writeback status */
qidx_t *tx_rsq;
@ -175,6 +181,9 @@ struct ice_tx_queue {
* @stats: queue statistics
* @tc: traffic class queue belongs to
*
* ice_iov.c requires the following parameters (when PCI_IOV is defined):
* @itr_idx: ITR index to use for this queue
*
* Other parameters may be iflib driver specific
*/
struct ice_rx_queue {
@ -187,6 +196,9 @@ struct ice_rx_queue {
struct ice_irq_vector *irqv;
u32 me;
u8 tc;
#ifdef PCI_IOV
u8 itr_idx;
#endif
struct if_irq que_irq;
};
@ -332,6 +344,10 @@ struct ice_softc {
ice_declare_bitmap(feat_cap, ICE_FEATURE_COUNT);
ice_declare_bitmap(feat_en, ICE_FEATURE_COUNT);
#ifdef PCI_IOV
struct ice_vf *vfs;
u16 num_vfs;
#endif
struct ice_resmgr os_imgr;
/* For mirror interface */
struct ice_mirr_if *mirr_if;

1732
sys/dev/ice/ice_iov.c Normal file

File diff suppressed because it is too large Load diff

115
sys/dev/ice/ice_iov.h Normal file
View file

@ -0,0 +1,115 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2025, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file ice_iov.h
* @brief header for IOV functionality
*
* This header includes definitions used to implement device Virtual Functions
* for the ice driver.
*/
#ifndef _ICE_IOV_H_
#define _ICE_IOV_H_
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/nv.h>
#include <sys/iov_schema.h>
#include <sys/param.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pci_iov.h>
#include "ice_iflib.h"
#include "ice_vf_mbx.h"
/**
* @enum ice_vf_flags
* @brief VF state flags
*
* Used to indicate the status of a PF's VF, as well as indicating what each VF
* is capabile of. Intended to be modified only using atomic operations, so
* they can be read and modified in places that aren't locked.
*
* Used in struct ice_vf's vf_flags field.
*/
enum ice_vf_flags {
VF_FLAG_ENABLED = BIT(0),
VF_FLAG_SET_MAC_CAP = BIT(1),
VF_FLAG_VLAN_CAP = BIT(2),
VF_FLAG_PROMISC_CAP = BIT(3),
VF_FLAG_MAC_ANTI_SPOOF = BIT(4),
};
/**
* @struct ice_vf
* @brief PF's VF software context
*
* Represents the state and options for a VF spawned from a PF.
*/
struct ice_vf {
struct ice_vsi *vsi;
u32 vf_flags;
u8 mac[ETHER_ADDR_LEN];
u16 vf_num;
struct virtchnl_version_info version;
u16 num_irq_vectors;
u16 *vf_imap;
struct ice_irq_vector *tx_irqvs;
struct ice_irq_vector *rx_irqvs;
};
#define ICE_PCIE_DEV_STATUS 0xAA
#define ICE_PCI_CIAD_WAIT_COUNT 100
#define ICE_PCI_CIAD_WAIT_DELAY_US 1
#define ICE_VPGEN_VFRSTAT_WAIT_COUNT 100
#define ICE_VPGEN_VFRSTAT_WAIT_DELAY_US 20
#define ICE_VIRTCHNL_VALID_PROMISC_FLAGS (FLAG_VF_UNICAST_PROMISC | \
FLAG_VF_MULTICAST_PROMISC)
int ice_iov_attach(struct ice_softc *sc);
int ice_iov_detach(struct ice_softc *sc);
int ice_iov_init(struct ice_softc *sc, uint16_t num_vfs, const nvlist_t *params);
int ice_iov_add_vf(struct ice_softc *sc, uint16_t vfnum, const nvlist_t *params);
void ice_iov_uninit(struct ice_softc *sc);
void ice_vc_handle_vf_msg(struct ice_softc *sc, struct ice_rq_event_info *event);
void ice_vc_notify_all_vfs_link_state(struct ice_softc *sc);
#endif /* _ICE_IOV_H_ */

View file

@ -42,6 +42,9 @@
#include "ice_lib.h"
#include "ice_iflib.h"
#ifdef PCI_IOV
#include "ice_iov.h"
#endif
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <machine/resource.h>
@ -741,6 +744,12 @@ ice_initialize_vsi(struct ice_vsi *vsi)
case ICE_VSI_VMDQ2:
ctx.flags = ICE_AQ_VSI_TYPE_VMDQ2;
break;
#ifdef PCI_IOV
case ICE_VSI_VF:
ctx.flags = ICE_AQ_VSI_TYPE_VF;
ctx.vf_num = vsi->vf_num;
break;
#endif
default:
return (ENODEV);
}
@ -1607,6 +1616,12 @@ ice_setup_tx_ctx(struct ice_tx_queue *txq, struct ice_tlan_ctx *tlan_ctx, u16 pf
case ICE_VSI_VMDQ2:
tlan_ctx->vmvf_type = ICE_TLAN_CTX_VMVF_TYPE_VMQ;
break;
#ifdef PCI_IOV
case ICE_VSI_VF:
tlan_ctx->vmvf_type = ICE_TLAN_CTX_VMVF_TYPE_VF;
tlan_ctx->vmvf_num = hw->func_caps.vf_base_id + vsi->vf_num;
break;
#endif
default:
return (ENODEV);
}
@ -2257,6 +2272,11 @@ ice_process_ctrlq_event(struct ice_softc *sc, const char *qname,
case ice_aqc_opc_get_link_status:
ice_process_link_event(sc, event);
break;
#ifdef PCI_IOV
case ice_mbx_opc_send_msg_to_pf:
ice_vc_handle_vf_msg(sc, event);
break;
#endif
case ice_aqc_opc_fw_logs_event:
ice_handle_fw_log_event(sc, &event->desc, event->msg_buf);
break;

View file

@ -611,6 +611,10 @@ struct ice_vsi {
u16 mirror_src_vsi;
u16 rule_mir_ingress;
u16 rule_mir_egress;
#ifdef PCI_IOV
u8 vf_num; /* Index of owning VF, if applicable */
#endif
};
/**

471
sys/dev/ice/ice_vf_mbx.c Normal file
View file

@ -0,0 +1,471 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2025, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "ice_common.h"
#include "ice_hw_autogen.h"
#include "ice_vf_mbx.h"
/**
* ice_aq_send_msg_to_vf
* @hw: pointer to the hardware structure
* @vfid: VF ID to send msg
* @v_opcode: opcodes for VF-PF communication
* @v_retval: return error code
* @msg: pointer to the msg buffer
* @msglen: msg length
* @cd: pointer to command details
*
* Send message to VF driver (0x0802) using mailbox
* queue and asynchronously sending message via
* ice_sq_send_cmd() function
*/
int
ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval,
u8 *msg, u16 msglen, struct ice_sq_cd *cd)
{
struct ice_aqc_pf_vf_msg *cmd;
struct ice_aq_desc desc;
ice_fill_dflt_direct_cmd_desc(&desc, ice_mbx_opc_send_msg_to_vf);
cmd = &desc.params.virt;
cmd->id = CPU_TO_LE32(vfid);
desc.cookie_high = CPU_TO_LE32(v_opcode);
desc.cookie_low = CPU_TO_LE32(v_retval);
if (msglen)
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
return ice_sq_send_cmd(hw, &hw->mailboxq, &desc, msg, msglen, cd);
}
/**
* ice_aq_send_msg_to_pf
* @hw: pointer to the hardware structure
* @v_opcode: opcodes for VF-PF communication
* @v_retval: return error code
* @msg: pointer to the msg buffer
* @msglen: msg length
* @cd: pointer to command details
*
* Send message to PF driver using mailbox queue. By default, this
* message is sent asynchronously, i.e. ice_sq_send_cmd()
* does not wait for completion before returning.
*/
int
ice_aq_send_msg_to_pf(struct ice_hw *hw, enum virtchnl_ops v_opcode,
int v_retval, u8 *msg, u16 msglen,
struct ice_sq_cd *cd)
{
struct ice_aq_desc desc;
ice_fill_dflt_direct_cmd_desc(&desc, ice_mbx_opc_send_msg_to_pf);
desc.cookie_high = CPU_TO_LE32(v_opcode);
desc.cookie_low = CPU_TO_LE32(v_retval);
if (msglen)
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
return ice_sq_send_cmd(hw, &hw->mailboxq, &desc, msg, msglen, cd);
}
static const u32 ice_legacy_aq_to_vc_speed[] = {
VIRTCHNL_LINK_SPEED_100MB, /* BIT(0) */
VIRTCHNL_LINK_SPEED_100MB,
VIRTCHNL_LINK_SPEED_1GB,
VIRTCHNL_LINK_SPEED_1GB,
VIRTCHNL_LINK_SPEED_1GB,
VIRTCHNL_LINK_SPEED_10GB,
VIRTCHNL_LINK_SPEED_20GB,
VIRTCHNL_LINK_SPEED_25GB,
VIRTCHNL_LINK_SPEED_40GB,
VIRTCHNL_LINK_SPEED_40GB,
VIRTCHNL_LINK_SPEED_40GB,
};
/**
* ice_conv_link_speed_to_virtchnl
* @adv_link_support: determines the format of the returned link speed
* @link_speed: variable containing the link_speed to be converted
*
* Convert link speed supported by HW to link speed supported by virtchnl.
* If adv_link_support is true, then return link speed in Mbps. Else return
* link speed as a VIRTCHNL_LINK_SPEED_* casted to a u32. Note that the caller
* needs to cast back to an enum virtchnl_link_speed in the case where
* adv_link_support is false, but when adv_link_support is true the caller can
* expect the speed in Mbps.
*/
u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed)
{
/* convert a BIT() value into an array index */
u16 index = (u16)(ice_fls(link_speed) - 1);
if (adv_link_support)
return ice_get_link_speed(index);
else if (index < ARRAY_SIZE(ice_legacy_aq_to_vc_speed))
/* Virtchnl speeds are not defined for every speed supported in
* the hardware. To maintain compatibility with older AVF
* drivers, while reporting the speed the new speed values are
* resolved to the closest known virtchnl speeds
*/
return ice_legacy_aq_to_vc_speed[index];
return VIRTCHNL_LINK_SPEED_UNKNOWN;
}
/* The mailbox overflow detection algorithm helps to check if there
* is a possibility of a malicious VF transmitting too many MBX messages to the
* PF.
* 1. The mailbox snapshot structure, ice_mbx_snapshot, is initialized during
* driver initialization in ice_init_hw() using ice_mbx_init_snapshot().
* The struct ice_mbx_snapshot helps to track and traverse a static window of
* messages within the mailbox queue while looking for a malicious VF.
*
* 2. When the caller starts processing its mailbox queue in response to an
* interrupt, the structure ice_mbx_snapshot is expected to be cleared before
* the algorithm can be run for the first time for that interrupt. This
* requires calling ice_mbx_reset_snapshot() as well as calling
* ice_mbx_reset_vf_info() for each VF tracking structure.
*
* 3. For every message read by the caller from the MBX Queue, the caller must
* call the detection algorithm's entry function ice_mbx_vf_state_handler().
* Before every call to ice_mbx_vf_state_handler() the struct ice_mbx_data is
* filled as it is required to be passed to the algorithm.
*
* 4. Every time a message is read from the MBX queue, a tracking structure
* for the VF must be passed to the state handler. The boolean output
* report_malvf from ice_mbx_vf_state_handler() serves as an indicator to the
* caller whether it must report this VF as malicious or not.
*
* 5. When a VF is identified to be malicious, the caller can send a message
* to the system administrator.
*
* 6. The PF is responsible for maintaining the struct ice_mbx_vf_info
* structure for each VF. The PF should clear the VF tracking structure if the
* VF is reset. When a VF is shut down and brought back up, we will then
* assume that the new VF is not malicious and may report it again if we
* detect it again.
*
* 7. The function ice_mbx_reset_snapshot() is called to reset the information
* in ice_mbx_snapshot for every new mailbox interrupt handled.
*/
#define ICE_RQ_DATA_MASK(rq_data) ((rq_data) & PF_MBX_ARQH_ARQH_M)
/* Using the highest value for an unsigned 16-bit value 0xFFFF to indicate that
* the max messages check must be ignored in the algorithm
*/
#define ICE_IGNORE_MAX_MSG_CNT 0xFFFF
/**
* ice_mbx_reset_snapshot - Initialize mailbox snapshot structure
* @snap: pointer to the mailbox snapshot
*/
static void ice_mbx_reset_snapshot(struct ice_mbx_snapshot *snap)
{
struct ice_mbx_vf_info *vf_info;
/* Clear mbx_buf in the mailbox snaphot structure and setting the
* mailbox snapshot state to a new capture.
*/
ice_memset(&snap->mbx_buf, 0, sizeof(snap->mbx_buf), ICE_NONDMA_MEM);
snap->mbx_buf.state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT;
/* Reset message counts for all VFs to zero */
LIST_FOR_EACH_ENTRY(vf_info, &snap->mbx_vf, ice_mbx_vf_info, list_entry)
vf_info->msg_count = 0;
}
/**
* ice_mbx_traverse - Pass through mailbox snapshot
* @hw: pointer to the HW struct
* @new_state: new algorithm state
*
* Traversing the mailbox static snapshot without checking
* for malicious VFs.
*/
static void
ice_mbx_traverse(struct ice_hw *hw,
enum ice_mbx_snapshot_state *new_state)
{
struct ice_mbx_snap_buffer_data *snap_buf;
u32 num_iterations;
snap_buf = &hw->mbx_snapshot.mbx_buf;
/* As mailbox buffer is circular, applying a mask
* on the incremented iteration count.
*/
num_iterations = ICE_RQ_DATA_MASK(++snap_buf->num_iterations);
/* Checking either of the below conditions to exit snapshot traversal:
* Condition-1: If the number of iterations in the mailbox is equal to
* the mailbox head which would indicate that we have reached the end
* of the static snapshot.
* Condition-2: If the maximum messages serviced in the mailbox for a
* given interrupt is the highest possible value then there is no need
* to check if the number of messages processed is equal to it. If not
* check if the number of messages processed is greater than or equal
* to the maximum number of mailbox entries serviced in current work item.
*/
if (num_iterations == snap_buf->head ||
(snap_buf->max_num_msgs_mbx < ICE_IGNORE_MAX_MSG_CNT &&
++snap_buf->num_msg_proc >= snap_buf->max_num_msgs_mbx))
*new_state = ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT;
}
/**
* ice_mbx_detect_malvf - Detect malicious VF in snapshot
* @hw: pointer to the HW struct
* @vf_info: mailbox tracking structure for a VF
* @new_state: new algorithm state
* @is_malvf: boolean output to indicate if VF is malicious
*
* This function tracks the number of asynchronous messages
* sent per VF and marks the VF as malicious if it exceeds
* the permissible number of messages to send.
*/
static int
ice_mbx_detect_malvf(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info,
enum ice_mbx_snapshot_state *new_state,
bool *is_malvf)
{
/* increment the message count for this VF */
vf_info->msg_count++;
if (vf_info->msg_count >= ICE_ASYNC_VF_MSG_THRESHOLD)
*is_malvf = true;
/* continue to iterate through the mailbox snapshot */
ice_mbx_traverse(hw, new_state);
return 0;
}
/**
* ice_e830_mbx_vf_dec_trig - Decrements the VF mailbox queue counter
* @hw: pointer to the HW struct
* @event: pointer to the control queue receive event
*
* This function triggers to decrement the counter
* MBX_VF_IN_FLIGHT_MSGS_AT_PF_CNT when the driver replenishes
* the buffers at the PF mailbox queue.
*/
void ice_e830_mbx_vf_dec_trig(struct ice_hw *hw,
struct ice_rq_event_info *event)
{
u16 vfid = LE16_TO_CPU(event->desc.retval);
wr32(hw, E830_MBX_VF_DEC_TRIG(vfid), 1);
}
/**
* ice_mbx_vf_clear_cnt_e830 - Clear the VF mailbox queue count
* @hw: pointer to the HW struct
* @vf_id: VF ID in the PF space
*
* This function clears the counter MBX_VF_IN_FLIGHT_MSGS_AT_PF_CNT, and should
* be called when a VF is created and on VF reset.
*/
void ice_mbx_vf_clear_cnt_e830(struct ice_hw *hw, u16 vf_id)
{
u32 reg = rd32(hw, E830_MBX_VF_IN_FLIGHT_MSGS_AT_PF_CNT(vf_id));
wr32(hw, E830_MBX_VF_DEC_TRIG(vf_id), reg);
}
/**
* ice_mbx_vf_state_handler - Handle states of the overflow algorithm
* @hw: pointer to the HW struct
* @mbx_data: pointer to structure containing mailbox data
* @vf_info: mailbox tracking structure for the VF in question
* @report_malvf: boolean output to indicate whether VF should be reported
*
* The function serves as an entry point for the malicious VF
* detection algorithm by handling the different states and state
* transitions of the algorithm:
* New snapshot: This state is entered when creating a new static
* snapshot. The data from any previous mailbox snapshot is
* cleared and a new capture of the mailbox head and tail is
* logged. This will be the new static snapshot to detect
* asynchronous messages sent by VFs. On capturing the snapshot
* and depending on whether the number of pending messages in that
* snapshot exceed the watermark value, the state machine enters
* traverse or detect states.
* Traverse: If pending message count is below watermark then iterate
* through the snapshot without any action on VF.
* Detect: If pending message count exceeds watermark traverse
* the static snapshot and look for a malicious VF.
*/
int
ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data,
struct ice_mbx_vf_info *vf_info, bool *report_malvf)
{
struct ice_mbx_snapshot *snap = &hw->mbx_snapshot;
struct ice_mbx_snap_buffer_data *snap_buf;
struct ice_ctl_q_info *cq = &hw->mailboxq;
enum ice_mbx_snapshot_state new_state;
int status = 0;
bool is_malvf = false;
if (!report_malvf || !mbx_data || !vf_info)
return ICE_ERR_BAD_PTR;
*report_malvf = false;
/* When entering the mailbox state machine assume that the VF
* is not malicious until detected.
*/
/* Checking if max messages allowed to be processed while servicing current
* interrupt is not less than the defined AVF message threshold.
*/
if (mbx_data->max_num_msgs_mbx <= ICE_ASYNC_VF_MSG_THRESHOLD)
return ICE_ERR_INVAL_SIZE;
/* The watermark value should not be lesser than the threshold limit
* set for the number of asynchronous messages a VF can send to mailbox
* nor should it be greater than the maximum number of messages in the
* mailbox serviced in current interrupt.
*/
if (mbx_data->async_watermark_val < ICE_ASYNC_VF_MSG_THRESHOLD ||
mbx_data->async_watermark_val > mbx_data->max_num_msgs_mbx)
return ICE_ERR_PARAM;
new_state = ICE_MAL_VF_DETECT_STATE_INVALID;
snap_buf = &snap->mbx_buf;
switch (snap_buf->state) {
case ICE_MAL_VF_DETECT_STATE_NEW_SNAPSHOT:
/* Clear any previously held data in mailbox snapshot structure. */
ice_mbx_reset_snapshot(snap);
/* Collect the pending ARQ count, number of messages processed and
* the maximum number of messages allowed to be processed from the
* Mailbox for current interrupt.
*/
snap_buf->num_pending_arq = mbx_data->num_pending_arq;
snap_buf->num_msg_proc = mbx_data->num_msg_proc;
snap_buf->max_num_msgs_mbx = mbx_data->max_num_msgs_mbx;
/* Capture a new static snapshot of the mailbox by logging the
* head and tail of snapshot and set num_iterations to the tail
* value to mark the start of the iteration through the snapshot.
*/
snap_buf->head = ICE_RQ_DATA_MASK(cq->rq.next_to_clean +
mbx_data->num_pending_arq);
snap_buf->tail = ICE_RQ_DATA_MASK(cq->rq.next_to_clean - 1);
snap_buf->num_iterations = snap_buf->tail;
/* Pending ARQ messages returned by ice_clean_rq_elem
* is the difference between the head and tail of the
* mailbox queue. Comparing this value against the watermark
* helps to check if we potentially have malicious VFs.
*/
if (snap_buf->num_pending_arq >=
mbx_data->async_watermark_val) {
new_state = ICE_MAL_VF_DETECT_STATE_DETECT;
status = ice_mbx_detect_malvf(hw, vf_info, &new_state, &is_malvf);
} else {
new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE;
ice_mbx_traverse(hw, &new_state);
}
break;
case ICE_MAL_VF_DETECT_STATE_TRAVERSE:
new_state = ICE_MAL_VF_DETECT_STATE_TRAVERSE;
ice_mbx_traverse(hw, &new_state);
break;
case ICE_MAL_VF_DETECT_STATE_DETECT:
new_state = ICE_MAL_VF_DETECT_STATE_DETECT;
status = ice_mbx_detect_malvf(hw, vf_info, &new_state, &is_malvf);
break;
default:
new_state = ICE_MAL_VF_DETECT_STATE_INVALID;
status = ICE_ERR_CFG;
}
snap_buf->state = new_state;
/* Only report VFs as malicious the first time we detect it */
if (is_malvf && !vf_info->malicious) {
vf_info->malicious = 1;
*report_malvf = true;
}
return status;
}
/**
* ice_mbx_clear_malvf - Clear VF mailbox info
* @vf_info: the mailbox tracking structure for a VF
*
* In case of a VF reset, this function shall be called to clear the VF's
* current mailbox tracking state.
*/
void ice_mbx_clear_malvf(struct ice_mbx_vf_info *vf_info)
{
vf_info->malicious = 0;
vf_info->msg_count = 0;
}
/**
* ice_mbx_init_vf_info - Initialize a new VF mailbox tracking info
* @hw: pointer to the hardware structure
* @vf_info: the mailbox tracking info structure for a VF
*
* Initialize a VF mailbox tracking info structure and insert it into the
* snapshot list.
*
* If you remove the VF, you must also delete the associated VF info structure
* from the linked list.
*/
void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info)
{
struct ice_mbx_snapshot *snap = &hw->mbx_snapshot;
ice_mbx_clear_malvf(vf_info);
LIST_ADD(&vf_info->list_entry, &snap->mbx_vf);
}
/**
* ice_mbx_init_snapshot - Initialize mailbox snapshot data
* @hw: pointer to the hardware structure
*
* Clear the mailbox snapshot structure and initialize the VF mailbox list.
*/
void ice_mbx_init_snapshot(struct ice_hw *hw)
{
struct ice_mbx_snapshot *snap = &hw->mbx_snapshot;
INIT_LIST_HEAD(&snap->mbx_vf);
ice_mbx_reset_snapshot(snap);
}

67
sys/dev/ice/ice_vf_mbx.h Normal file
View file

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2025, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ICE_VF_MBX_H_
#define _ICE_VF_MBX_H_
#include "ice_type.h"
#include "ice_controlq.h"
/* Defining the mailbox message threshold as 63 asynchronous
* pending messages. Normal VF functionality does not require
* sending more than 63 asynchronous pending message.
*/
/* Threshold value should be used to initialize
* MBX_VF_IN_FLIGHT_MSGS_AT_PF_CNT register.
*/
#define ICE_ASYNC_VF_MSG_THRESHOLD 63
int
ice_aq_send_msg_to_pf(struct ice_hw *hw, enum virtchnl_ops v_opcode,
int v_retval, u8 *msg, u16 msglen,
struct ice_sq_cd *cd);
int
ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval,
u8 *msg, u16 msglen, struct ice_sq_cd *cd);
u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed);
void ice_e830_mbx_vf_dec_trig(struct ice_hw *hw,
struct ice_rq_event_info *event);
void ice_mbx_vf_clear_cnt_e830(struct ice_hw *hw, u16 vf_id);
int
ice_mbx_vf_state_handler(struct ice_hw *hw, struct ice_mbx_data *mbx_data,
struct ice_mbx_vf_info *vf_info, bool *report_malvf);
void ice_mbx_clear_malvf(struct ice_mbx_vf_info *vf_info);
void ice_mbx_init_vf_info(struct ice_hw *hw, struct ice_mbx_vf_info *vf_info);
void ice_mbx_init_snapshot(struct ice_hw *hw);
#endif /* _ICE_VF_MBX_H_ */

View file

@ -42,6 +42,9 @@
#include "ice_drv_info.h"
#include "ice_switch.h"
#include "ice_sched.h"
#ifdef PCI_IOV
#include "ice_iov.h"
#endif
#include <sys/module.h>
#include <sys/sockio.h>
@ -85,6 +88,12 @@ static int ice_if_suspend(if_ctx_t ctx);
static int ice_if_resume(if_ctx_t ctx);
static bool ice_if_needs_restart(if_ctx_t ctx, enum iflib_restart_event event);
static void ice_init_link(struct ice_softc *sc);
#ifdef PCI_IOV
static int ice_if_iov_init(if_ctx_t ctx, uint16_t num_vfs, const nvlist_t *params);
static void ice_if_iov_uninit(if_ctx_t ctx);
static int ice_if_iov_vf_add(if_ctx_t ctx, uint16_t vfnum, const nvlist_t *params);
static void ice_if_vflr_handle(if_ctx_t ctx);
#endif
static int ice_setup_mirror_vsi(struct ice_mirr_if *mif);
static int ice_wire_mirror_intrs(struct ice_mirr_if *mif);
static void ice_free_irqvs_subif(struct ice_mirr_if *mif);
@ -158,6 +167,11 @@ static device_method_t ice_methods[] = {
DEVMETHOD(device_shutdown, iflib_device_shutdown),
DEVMETHOD(device_suspend, iflib_device_suspend),
DEVMETHOD(device_resume, iflib_device_resume),
#ifdef PCI_IOV
DEVMETHOD(pci_iov_init, iflib_device_iov_init),
DEVMETHOD(pci_iov_uninit, iflib_device_iov_uninit),
DEVMETHOD(pci_iov_add_vf, iflib_device_iov_add_vf),
#endif
DEVMETHOD_END
};
@ -198,6 +212,12 @@ static device_method_t ice_iflib_methods[] = {
DEVMETHOD(ifdi_suspend, ice_if_suspend),
DEVMETHOD(ifdi_resume, ice_if_resume),
DEVMETHOD(ifdi_needs_restart, ice_if_needs_restart),
#ifdef PCI_IOV
DEVMETHOD(ifdi_iov_vf_add, ice_if_iov_vf_add),
DEVMETHOD(ifdi_iov_init, ice_if_iov_init),
DEVMETHOD(ifdi_iov_uninit, ice_if_iov_uninit),
DEVMETHOD(ifdi_vflr_handle, ice_if_vflr_handle),
#endif
DEVMETHOD_END
};
@ -733,6 +753,9 @@ ice_update_link_status(struct ice_softc *sc, bool update_media)
iflib_link_state_change(sc->ctx, LINK_STATE_DOWN, 0);
ice_rdma_link_change(sc, LINK_STATE_DOWN, 0);
}
#ifdef PCI_IOV
ice_vc_notify_all_vfs_link_state(sc);
#endif
update_media = true;
}
@ -831,6 +854,14 @@ ice_if_attach_post(if_ctx_t ctx)
ice_add_device_sysctls(sc);
#ifdef PCI_IOV
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_SRIOV)) {
err = ice_iov_attach(sc);
if (err == ENOMEM)
return (err);
}
#endif /* PCI_IOV */
/* Get DCBX/LLDP state and start DCBX agent */
ice_init_dcb_setup(sc);
@ -953,6 +984,11 @@ ice_if_detach(if_ctx_t ctx)
ice_destroy_mirror_interface(sc);
ice_rdma_pf_detach(sc);
#ifdef PCI_IOV
if (ice_is_bit_set(sc->feat_cap, ICE_FEATURE_SRIOV))
ice_iov_detach(sc);
#endif /* PCI_IOV */
/* Free allocated media types */
ifmedia_removeall(sc->media);
@ -2277,7 +2313,12 @@ ice_transition_recovery_mode(struct ice_softc *sc)
ice_rdma_pf_detach(sc);
ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap);
#ifdef PCI_IOV
if (ice_test_and_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en))
ice_iov_detach(sc);
#else
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en);
#endif /* PCI_IOV */
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_cap);
ice_vsi_del_txqs_ctx(vsi);
@ -2325,7 +2366,12 @@ ice_transition_safe_mode(struct ice_softc *sc)
ice_rdma_pf_detach(sc);
ice_clear_bit(ICE_FEATURE_RDMA, sc->feat_cap);
#ifdef PCI_IOV
if (ice_test_and_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en))
ice_iov_detach(sc);
#else
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_en);
#endif /* PCI_IOV */
ice_clear_bit(ICE_FEATURE_SRIOV, sc->feat_cap);
ice_clear_bit(ICE_FEATURE_RSS, sc->feat_cap);
@ -3349,6 +3395,77 @@ ice_init_link(struct ice_softc *sc)
}
#ifdef PCI_IOV
/**
* ice_if_iov_init - iov init handler for iflib
* @ctx: iflib context pointer
* @num_vfs: number of VFs to create
* @params: configuration parameters for the PF
*
* Configure the driver for SR-IOV mode. Used to setup things like memory
* before any VFs are created.
*
* @remark This is a wrapper for ice_iov_init
*/
static int
ice_if_iov_init(if_ctx_t ctx, uint16_t num_vfs, const nvlist_t *params)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
return ice_iov_init(sc, num_vfs, params);
}
/**
* ice_if_iov_uninit - iov uninit handler for iflib
* @ctx: iflib context pointer
*
* Destroys VFs and frees their memory and resources.
*
* @remark This is a wrapper for ice_iov_uninit
*/
static void
ice_if_iov_uninit(if_ctx_t ctx)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
ice_iov_uninit(sc);
}
/**
* ice_if_iov_vf_add - iov add vf handler for iflib
* @ctx: iflib context pointer
* @vfnum: index of VF to configure
* @params: configuration parameters for the VF
*
* Sets up the VF given by the vfnum index. This is called by the OS
* for each VF created by the PF driver after it is spawned.
*
* @remark This is a wrapper for ice_iov_vf_add
*/
static int
ice_if_iov_vf_add(if_ctx_t ctx, uint16_t vfnum, const nvlist_t *params)
{
struct ice_softc *sc = (struct ice_softc *)iflib_get_softc(ctx);
return ice_iov_add_vf(sc, vfnum, params);
}
/**
* ice_if_vflr_handle - iov VFLR handler
* @ctx: iflib context pointer
*
* Performs the necessar teardown or setup required for a VF after
* a VFLR is initiated.
*
* @remark This is unimplemented
*/
static void
ice_if_vflr_handle(if_ctx_t ctx __unused)
{
return;
}
#endif /* PCI_IOV */
extern struct if_txrx ice_subif_txrx;
/**

View file

@ -13,6 +13,7 @@ SRCS += opt_inet.h opt_inet6.h opt_rss.h opt_iflib.h
SRCS += ice_lib.c ice_osdep.c ice_resmgr.c ice_strings.c
SRCS += ice_iflib_recovery_txrx.c ice_iflib_txrx.c if_ice_iflib.c
SRCS += ice_fw_logging.c ice_ddp_common.c
SRCS.PCI_IOV += pci_iov_if.h ice_iov.c ice_vf_mbx.c
# RDMA Client interface
# TODO: Is this the right way to compile this?