Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/intel/ice/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 127 kB image not shown  

Quelle  ice_virtchnl.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2022, Intel Corporation. */

#include "ice_virtchnl.h"
#include "ice_vf_lib_private.h"
#include "ice.h"
#include "ice_base.h"
#include "ice_lib.h"
#include "ice_fltr.h"
#include "ice_virtchnl_allowlist.h"
#include "ice_vf_vsi_vlan_ops.h"
#include "ice_vlan.h"
#include "ice_flex_pipe.h"
#include "ice_dcb_lib.h"

#define FIELD_SELECTOR(proto_hdr_field) \
  BIT((proto_hdr_field) & PROTO_HDR_FIELD_MASK)

struct ice_vc_hdr_match_type {
 u32 vc_hdr; /* virtchnl headers (VIRTCHNL_PROTO_HDR_XXX) */
 u32 ice_hdr; /* ice headers (ICE_FLOW_SEG_HDR_XXX) */
};

static const struct ice_vc_hdr_match_type ice_vc_hdr_list[] = {
 {VIRTCHNL_PROTO_HDR_NONE, ICE_FLOW_SEG_HDR_NONE},
 {VIRTCHNL_PROTO_HDR_ETH, ICE_FLOW_SEG_HDR_ETH},
 {VIRTCHNL_PROTO_HDR_S_VLAN, ICE_FLOW_SEG_HDR_VLAN},
 {VIRTCHNL_PROTO_HDR_C_VLAN, ICE_FLOW_SEG_HDR_VLAN},
 {VIRTCHNL_PROTO_HDR_IPV4, ICE_FLOW_SEG_HDR_IPV4 |
     ICE_FLOW_SEG_HDR_IPV_OTHER},
 {VIRTCHNL_PROTO_HDR_IPV6, ICE_FLOW_SEG_HDR_IPV6 |
     ICE_FLOW_SEG_HDR_IPV_OTHER},
 {VIRTCHNL_PROTO_HDR_TCP, ICE_FLOW_SEG_HDR_TCP},
 {VIRTCHNL_PROTO_HDR_UDP, ICE_FLOW_SEG_HDR_UDP},
 {VIRTCHNL_PROTO_HDR_SCTP, ICE_FLOW_SEG_HDR_SCTP},
 {VIRTCHNL_PROTO_HDR_PPPOE, ICE_FLOW_SEG_HDR_PPPOE},
 {VIRTCHNL_PROTO_HDR_GTPU_IP, ICE_FLOW_SEG_HDR_GTPU_IP},
 {VIRTCHNL_PROTO_HDR_GTPU_EH, ICE_FLOW_SEG_HDR_GTPU_EH},
 {VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_DWN,
     ICE_FLOW_SEG_HDR_GTPU_DWN},
 {VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_UP,
     ICE_FLOW_SEG_HDR_GTPU_UP},
 {VIRTCHNL_PROTO_HDR_L2TPV3, ICE_FLOW_SEG_HDR_L2TPV3},
 {VIRTCHNL_PROTO_HDR_ESP, ICE_FLOW_SEG_HDR_ESP},
 {VIRTCHNL_PROTO_HDR_AH,  ICE_FLOW_SEG_HDR_AH},
 {VIRTCHNL_PROTO_HDR_PFCP, ICE_FLOW_SEG_HDR_PFCP_SESSION},
};

struct ice_vc_hash_field_match_type {
 u32 vc_hdr;  /* virtchnl headers
 * (VIRTCHNL_PROTO_HDR_XXX)
 */

 u32 vc_hash_field; /* virtchnl hash fields selector
 * FIELD_SELECTOR((VIRTCHNL_PROTO_HDR_ETH_XXX))
 */

 u64 ice_hash_field; /* ice hash fields
 * (BIT_ULL(ICE_FLOW_FIELD_IDX_XXX))
 */

};

static const struct
ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
 {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_SRC),
  BIT_ULL(ICE_FLOW_FIELD_IDX_ETH_SA)},
 {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_DST),
  BIT_ULL(ICE_FLOW_FIELD_IDX_ETH_DA)},
 {VIRTCHNL_PROTO_HDR_ETH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_DST),
  ICE_FLOW_HASH_ETH},
 {VIRTCHNL_PROTO_HDR_ETH,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ETH_ETHERTYPE),
  BIT_ULL(ICE_FLOW_FIELD_IDX_ETH_TYPE)},
 {VIRTCHNL_PROTO_HDR_S_VLAN,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_S_VLAN_ID),
  BIT_ULL(ICE_FLOW_FIELD_IDX_S_VLAN)},
 {VIRTCHNL_PROTO_HDR_C_VLAN,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_C_VLAN_ID),
  BIT_ULL(ICE_FLOW_FIELD_IDX_C_VLAN)},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA)},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA)},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST),
  ICE_FLOW_HASH_IPV4},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) |
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) |
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
  ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA)},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_DA)},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST),
  ICE_FLOW_HASH_IPV6},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA) |
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_DA) |
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
  ICE_FLOW_HASH_IPV6 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
 {VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
 {VIRTCHNL_PROTO_HDR_TCP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT)},
 {VIRTCHNL_PROTO_HDR_TCP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_DST_PORT)},
 {VIRTCHNL_PROTO_HDR_TCP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT),
  ICE_FLOW_HASH_TCP_PORT},
 {VIRTCHNL_PROTO_HDR_UDP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_SRC_PORT)},
 {VIRTCHNL_PROTO_HDR_UDP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_DST_PORT)},
 {VIRTCHNL_PROTO_HDR_UDP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT),
  ICE_FLOW_HASH_UDP_PORT},
 {VIRTCHNL_PROTO_HDR_SCTP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT)},
 {VIRTCHNL_PROTO_HDR_SCTP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT),
  BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_DST_PORT)},
 {VIRTCHNL_PROTO_HDR_SCTP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) |
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT),
  ICE_FLOW_HASH_SCTP_PORT},
 {VIRTCHNL_PROTO_HDR_PPPOE,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_PPPOE_SESS_ID),
  BIT_ULL(ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID)},
 {VIRTCHNL_PROTO_HDR_GTPU_IP,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_GTPU_IP_TEID),
  BIT_ULL(ICE_FLOW_FIELD_IDX_GTPU_IP_TEID)},
 {VIRTCHNL_PROTO_HDR_L2TPV3,
  FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_L2TPV3_SESS_ID),
  BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV3_SESS_ID)},
 {VIRTCHNL_PROTO_HDR_ESP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_ESP_SPI),
  BIT_ULL(ICE_FLOW_FIELD_IDX_ESP_SPI)},
 {VIRTCHNL_PROTO_HDR_AH, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_AH_SPI),
  BIT_ULL(ICE_FLOW_FIELD_IDX_AH_SPI)},
 {VIRTCHNL_PROTO_HDR_PFCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_PFCP_SEID),
  BIT_ULL(ICE_FLOW_FIELD_IDX_PFCP_SEID)},
};

/**
 * ice_vc_vf_broadcast - Broadcast a message to all VFs on PF
 * @pf: pointer to the PF structure
 * @v_opcode: operation code
 * @v_retval: return value
 * @msg: pointer to the msg buffer
 * @msglen: msg length
 */

static void
ice_vc_vf_broadcast(struct ice_pf *pf, enum virtchnl_ops v_opcode,
      enum virtchnl_status_code v_retval, u8 *msg, u16 msglen)
{
 struct ice_hw *hw = &pf->hw;
 struct ice_vf *vf;
 unsigned int bkt;

 mutex_lock(&pf->vfs.table_lock);
 ice_for_each_vf(pf, bkt, vf) {
  /* Not all vfs are enabled so skip the ones that are not */
  if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states) &&
      !test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states))
   continue;

  /* Ignore return value on purpose - a given VF may fail, but
 * we need to keep going and send to all of them
 */

  ice_aq_send_msg_to_vf(hw, vf->vf_id, v_opcode, v_retval, msg,
          msglen, NULL);
 }
 mutex_unlock(&pf->vfs.table_lock);
}

/**
 * ice_set_pfe_link - Set the link speed/status of the virtchnl_pf_event
 * @vf: pointer to the VF structure
 * @pfe: pointer to the virtchnl_pf_event to set link speed/status for
 * @ice_link_speed: link speed specified by ICE_AQ_LINK_SPEED_*
 * @link_up: whether or not to set the link up/down
 */

static void
ice_set_pfe_link(struct ice_vf *vf, struct virtchnl_pf_event *pfe,
   int ice_link_speed, bool link_up)
{
 if (vf->driver_caps & VIRTCHNL_VF_CAP_ADV_LINK_SPEED) {
  pfe->event_data.link_event_adv.link_status = link_up;
  /* Speed in Mbps */
  pfe->event_data.link_event_adv.link_speed =
   ice_conv_link_speed_to_virtchnl(true, ice_link_speed);
 } else {
  pfe->event_data.link_event.link_status = link_up;
  /* Legacy method for virtchnl link speeds */
  pfe->event_data.link_event.link_speed =
   (enum virtchnl_link_speed)
   ice_conv_link_speed_to_virtchnl(false, ice_link_speed);
 }
}

/**
 * ice_vc_notify_vf_link_state - Inform a VF of link status
 * @vf: pointer to the VF structure
 *
 * send a link status message to a single VF
 */

void ice_vc_notify_vf_link_state(struct ice_vf *vf)
{
 struct virtchnl_pf_event pfe = { 0 };
 struct ice_hw *hw = &vf->pf->hw;

 pfe.event = VIRTCHNL_EVENT_LINK_CHANGE;
 pfe.severity = PF_EVENT_SEVERITY_INFO;

 if (ice_is_vf_link_up(vf))
  ice_set_pfe_link(vf, &pfe,
     hw->port_info->phy.link_info.link_speed, true);
 else
  ice_set_pfe_link(vf, &pfe, ICE_AQ_LINK_SPEED_UNKNOWN, false);

 ice_aq_send_msg_to_vf(hw, vf->vf_id, VIRTCHNL_OP_EVENT,
         VIRTCHNL_STATUS_SUCCESS, (u8 *)&pfe,
         sizeof(pfe), NULL);
}

/**
 * ice_vc_notify_link_state - Inform all VFs on a PF of link status
 * @pf: pointer to the PF structure
 */

void ice_vc_notify_link_state(struct ice_pf *pf)
{
 struct ice_vf *vf;
 unsigned int bkt;

 mutex_lock(&pf->vfs.table_lock);
 ice_for_each_vf(pf, bkt, vf)
  ice_vc_notify_vf_link_state(vf);
 mutex_unlock(&pf->vfs.table_lock);
}

/**
 * ice_vc_notify_reset - Send pending reset message to all VFs
 * @pf: pointer to the PF structure
 *
 * indicate a pending reset to all VFs on a given PF
 */

void ice_vc_notify_reset(struct ice_pf *pf)
{
 struct virtchnl_pf_event pfe;

 if (!ice_has_vfs(pf))
  return;

 pfe.event = VIRTCHNL_EVENT_RESET_IMPENDING;
 pfe.severity = PF_EVENT_SEVERITY_CERTAIN_DOOM;
 ice_vc_vf_broadcast(pf, VIRTCHNL_OP_EVENT, VIRTCHNL_STATUS_SUCCESS,
       (u8 *)&pfe, sizeof(struct virtchnl_pf_event));
}

/**
 * ice_vc_send_msg_to_vf - Send message to VF
 * @vf: pointer to the VF info
 * @v_opcode: virtual channel opcode
 * @v_retval: virtual channel return value
 * @msg: pointer to the msg buffer
 * @msglen: msg length
 *
 * send msg to VF
 */

int
ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode,
        enum virtchnl_status_code v_retval, u8 *msg, u16 msglen)
{
 struct device *dev;
 struct ice_pf *pf;
 int aq_ret;

 pf = vf->pf;
 dev = ice_pf_to_dev(pf);

 aq_ret = ice_aq_send_msg_to_vf(&pf->hw, vf->vf_id, v_opcode, v_retval,
           msg, msglen, NULL);
 if (aq_ret && pf->hw.mailboxq.sq_last_status != LIBIE_AQ_RC_ENOSYS) {
  dev_info(dev, "Unable to send the message to VF %d ret %d aq_err %s\n",
    vf->vf_id, aq_ret,
    libie_aq_str(pf->hw.mailboxq.sq_last_status));
  return -EIO;
 }

 return 0;
}

/**
 * ice_vc_get_ver_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to request the API version used by the PF
 */

static int ice_vc_get_ver_msg(struct ice_vf *vf, u8 *msg)
{
 struct virtchnl_version_info info = {
  VIRTCHNL_VERSION_MAJOR, VIRTCHNL_VERSION_MINOR
 };

 vf->vf_ver = *(struct virtchnl_version_info *)msg;
 /* VFs running the 1.0 API expect to get 1.0 back or they will cry. */
 if (VF_IS_V10(&vf->vf_ver))
  info.minor = VIRTCHNL_VERSION_MINOR_NO_VF_CAPS;

 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_VERSION,
         VIRTCHNL_STATUS_SUCCESS, (u8 *)&info,
         sizeof(struct virtchnl_version_info));
}

/**
 * ice_vc_get_max_frame_size - get max frame size allowed for VF
 * @vf: VF used to determine max frame size
 *
 * Max frame size is determined based on the current port's max frame size and
 * whether a port VLAN is configured on this VF. The VF is not aware whether
 * it's in a port VLAN so the PF needs to account for this in max frame size
 * checks and sending the max frame size to the VF.
 */

static u16 ice_vc_get_max_frame_size(struct ice_vf *vf)
{
 struct ice_port_info *pi = ice_vf_get_port_info(vf);
 u16 max_frame_size;

 max_frame_size = pi->phy.link_info.max_frame_size;

 if (ice_vf_is_port_vlan_ena(vf))
  max_frame_size -= VLAN_HLEN;

 return max_frame_size;
}

/**
 * ice_vc_get_vlan_caps
 * @hw: pointer to the hw
 * @vf: pointer to the VF info
 * @vsi: pointer to the VSI
 * @driver_caps: current driver caps
 *
 * Return 0 if there is no VLAN caps supported, or VLAN caps value
 */

static u32
ice_vc_get_vlan_caps(struct ice_hw *hw, struct ice_vf *vf, struct ice_vsi *vsi,
       u32 driver_caps)
{
 if (ice_is_eswitch_mode_switchdev(vf->pf))
  /* In switchdev setting VLAN from VF isn't supported */
  return 0;

 if (driver_caps & VIRTCHNL_VF_OFFLOAD_VLAN_V2) {
  /* VLAN offloads based on current device configuration */
  return VIRTCHNL_VF_OFFLOAD_VLAN_V2;
 } else if (driver_caps & VIRTCHNL_VF_OFFLOAD_VLAN) {
  /* allow VF to negotiate VIRTCHNL_VF_OFFLOAD explicitly for
 * these two conditions, which amounts to guest VLAN filtering
 * and offloads being based on the inner VLAN or the
 * inner/single VLAN respectively and don't allow VF to
 * negotiate VIRTCHNL_VF_OFFLOAD in any other cases
 */

  if (ice_is_dvm_ena(hw) && ice_vf_is_port_vlan_ena(vf)) {
   return VIRTCHNL_VF_OFFLOAD_VLAN;
  } else if (!ice_is_dvm_ena(hw) &&
      !ice_vf_is_port_vlan_ena(vf)) {
   /* configure backward compatible support for VFs that
 * only support VIRTCHNL_VF_OFFLOAD_VLAN, the PF is
 * configured in SVM, and no port VLAN is configured
 */

   ice_vf_vsi_cfg_svm_legacy_vlan_mode(vsi);
   return VIRTCHNL_VF_OFFLOAD_VLAN;
  } else if (ice_is_dvm_ena(hw)) {
   /* configure software offloaded VLAN support when DVM
 * is enabled, but no port VLAN is enabled
 */

   ice_vf_vsi_cfg_dvm_legacy_vlan_mode(vsi);
  }
 }

 return 0;
}

/**
 * ice_vc_get_vf_res_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to request its resources
 */

static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_vf_resource *vfres = NULL;
 struct ice_hw *hw = &vf->pf->hw;
 struct ice_vsi *vsi;
 int len = 0;
 int ret;

 if (ice_check_vf_init(vf)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 len = virtchnl_struct_size(vfres, vsi_res, 0);

 vfres = kzalloc(len, GFP_KERNEL);
 if (!vfres) {
  v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY;
  len = 0;
  goto err;
 }
 if (VF_IS_V11(&vf->vf_ver))
  vf->driver_caps = *(u32 *)msg;
 else
  vf->driver_caps = VIRTCHNL_VF_OFFLOAD_L2 |
      VIRTCHNL_VF_OFFLOAD_VLAN;

 vfres->vf_cap_flags = VIRTCHNL_VF_OFFLOAD_L2;
 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 vfres->vf_cap_flags |= ice_vc_get_vlan_caps(hw, vf, vsi,
          vf->driver_caps);

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_FDIR_PF)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_FDIR_PF;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_TC_U32 &&
     vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_FDIR_PF)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_TC_U32;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_POLLING)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_WB_ON_ITR;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_REQ_QUEUES)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_REQ_QUEUES;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_CRC)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_CRC;

 if (vf->driver_caps & VIRTCHNL_VF_CAP_ADV_LINK_SPEED)
  vfres->vf_cap_flags |= VIRTCHNL_VF_CAP_ADV_LINK_SPEED;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_USO)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_USO;

 if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_QOS)
  vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_QOS;

 if (vf->driver_caps & VIRTCHNL_VF_CAP_PTP)
  vfres->vf_cap_flags |= VIRTCHNL_VF_CAP_PTP;

 vfres->num_vsis = 1;
 /* Tx and Rx queue are equal for VF */
 vfres->num_queue_pairs = vsi->num_txq;
 vfres->max_vectors = vf->num_msix;
 vfres->rss_key_size = ICE_VSIQF_HKEY_ARRAY_SIZE;
 vfres->rss_lut_size = ICE_LUT_VSI_SIZE;
 vfres->max_mtu = ice_vc_get_max_frame_size(vf);

 vfres->vsi_res[0].vsi_id = ICE_VF_VSI_ID;
 vfres->vsi_res[0].vsi_type = VIRTCHNL_VSI_SRIOV;
 vfres->vsi_res[0].num_queue_pairs = vsi->num_txq;
 ether_addr_copy(vfres->vsi_res[0].default_mac_addr,
   vf->hw_lan_addr);

 /* match guest capabilities */
 vf->driver_caps = vfres->vf_cap_flags;

 ice_vc_set_caps_allowlist(vf);
 ice_vc_set_working_allowlist(vf);

 set_bit(ICE_VF_STATE_ACTIVE, vf->vf_states);

err:
 /* send the response back to the VF */
 ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_VF_RESOURCES, v_ret,
        (u8 *)vfres, len);

 kfree(vfres);
 return ret;
}

/**
 * ice_vc_reset_vf_msg
 * @vf: pointer to the VF info
 *
 * called from the VF to reset itself,
 * unlike other virtchnl messages, PF driver
 * doesn't send the response back to the VF
 */

static void ice_vc_reset_vf_msg(struct ice_vf *vf)
{
 if (test_bit(ICE_VF_STATE_INIT, vf->vf_states))
  ice_reset_vf(vf, 0);
}

/**
 * ice_vc_isvalid_vsi_id
 * @vf: pointer to the VF info
 * @vsi_id: VF relative VSI ID
 *
 * check for the valid VSI ID
 */

bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id)
{
 return vsi_id == ICE_VF_VSI_ID;
}

/**
 * ice_vc_isvalid_q_id
 * @vsi: VSI to check queue ID against
 * @qid: VSI relative queue ID
 *
 * check for the valid queue ID
 */

static bool ice_vc_isvalid_q_id(struct ice_vsi *vsi, u16 qid)
{
 /* allocated Tx and Rx queues should be always equal for VF VSI */
 return qid < vsi->alloc_txq;
}

/**
 * ice_vc_isvalid_ring_len
 * @ring_len: length of ring
 *
 * check for the valid ring count, should be multiple of ICE_REQ_DESC_MULTIPLE
 * or zero
 */

static bool ice_vc_isvalid_ring_len(u16 ring_len)
{
 return ring_len == 0 ||
        (ring_len >= ICE_MIN_NUM_DESC &&
  ring_len <= ICE_MAX_NUM_DESC &&
  !(ring_len % ICE_REQ_DESC_MULTIPLE));
}

/**
 * ice_vc_validate_pattern
 * @vf: pointer to the VF info
 * @proto: virtchnl protocol headers
 *
 * validate the pattern is supported or not.
 *
 * Return: true on success, false on error.
 */

bool
ice_vc_validate_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto)
{
 bool is_ipv4 = false;
 bool is_ipv6 = false;
 bool is_udp = false;
 u16 ptype = -1;
 int i = 0;

 while (i < proto->count &&
        proto->proto_hdr[i].type != VIRTCHNL_PROTO_HDR_NONE) {
  switch (proto->proto_hdr[i].type) {
  case VIRTCHNL_PROTO_HDR_ETH:
   ptype = ICE_PTYPE_MAC_PAY;
   break;
  case VIRTCHNL_PROTO_HDR_IPV4:
   ptype = ICE_PTYPE_IPV4_PAY;
   is_ipv4 = true;
   break;
  case VIRTCHNL_PROTO_HDR_IPV6:
   ptype = ICE_PTYPE_IPV6_PAY;
   is_ipv6 = true;
   break;
  case VIRTCHNL_PROTO_HDR_UDP:
   if (is_ipv4)
    ptype = ICE_PTYPE_IPV4_UDP_PAY;
   else if (is_ipv6)
    ptype = ICE_PTYPE_IPV6_UDP_PAY;
   is_udp = true;
   break;
  case VIRTCHNL_PROTO_HDR_TCP:
   if (is_ipv4)
    ptype = ICE_PTYPE_IPV4_TCP_PAY;
   else if (is_ipv6)
    ptype = ICE_PTYPE_IPV6_TCP_PAY;
   break;
  case VIRTCHNL_PROTO_HDR_SCTP:
   if (is_ipv4)
    ptype = ICE_PTYPE_IPV4_SCTP_PAY;
   else if (is_ipv6)
    ptype = ICE_PTYPE_IPV6_SCTP_PAY;
   break;
  case VIRTCHNL_PROTO_HDR_GTPU_IP:
  case VIRTCHNL_PROTO_HDR_GTPU_EH:
   if (is_ipv4)
    ptype = ICE_MAC_IPV4_GTPU;
   else if (is_ipv6)
    ptype = ICE_MAC_IPV6_GTPU;
   goto out;
  case VIRTCHNL_PROTO_HDR_L2TPV3:
   if (is_ipv4)
    ptype = ICE_MAC_IPV4_L2TPV3;
   else if (is_ipv6)
    ptype = ICE_MAC_IPV6_L2TPV3;
   goto out;
  case VIRTCHNL_PROTO_HDR_ESP:
   if (is_ipv4)
    ptype = is_udp ? ICE_MAC_IPV4_NAT_T_ESP :
      ICE_MAC_IPV4_ESP;
   else if (is_ipv6)
    ptype = is_udp ? ICE_MAC_IPV6_NAT_T_ESP :
      ICE_MAC_IPV6_ESP;
   goto out;
  case VIRTCHNL_PROTO_HDR_AH:
   if (is_ipv4)
    ptype = ICE_MAC_IPV4_AH;
   else if (is_ipv6)
    ptype = ICE_MAC_IPV6_AH;
   goto out;
  case VIRTCHNL_PROTO_HDR_PFCP:
   if (is_ipv4)
    ptype = ICE_MAC_IPV4_PFCP_SESSION;
   else if (is_ipv6)
    ptype = ICE_MAC_IPV6_PFCP_SESSION;
   goto out;
  default:
   break;
  }
  i++;
 }

out:
 return ice_hw_ptype_ena(&vf->pf->hw, ptype);
}

/**
 * ice_vc_parse_rss_cfg - parses hash fields and headers from
 * a specific virtchnl RSS cfg
 * @hw: pointer to the hardware
 * @rss_cfg: pointer to the virtchnl RSS cfg
 * @hash_cfg: pointer to the HW hash configuration
 *
 * Return true if all the protocol header and hash fields in the RSS cfg could
 * be parsed, else return false
 *
 * This function parses the virtchnl RSS cfg to be the intended
 * hash fields and the intended header for RSS configuration
 */

static bool ice_vc_parse_rss_cfg(struct ice_hw *hw,
     struct virtchnl_rss_cfg *rss_cfg,
     struct ice_rss_hash_cfg *hash_cfg)
{
 const struct ice_vc_hash_field_match_type *hf_list;
 const struct ice_vc_hdr_match_type *hdr_list;
 int i, hf_list_len, hdr_list_len;
 u32 *addl_hdrs = &hash_cfg->addl_hdrs;
 u64 *hash_flds = &hash_cfg->hash_flds;

 /* set outer layer RSS as default */
 hash_cfg->hdr_type = ICE_RSS_OUTER_HEADERS;

 if (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC)
  hash_cfg->symm = true;
 else
  hash_cfg->symm = false;

 hf_list = ice_vc_hash_field_list;
 hf_list_len = ARRAY_SIZE(ice_vc_hash_field_list);
 hdr_list = ice_vc_hdr_list;
 hdr_list_len = ARRAY_SIZE(ice_vc_hdr_list);

 for (i = 0; i < rss_cfg->proto_hdrs.count; i++) {
  struct virtchnl_proto_hdr *proto_hdr =
     &rss_cfg->proto_hdrs.proto_hdr[i];
  bool hdr_found = false;
  int j;

  /* Find matched ice headers according to virtchnl headers. */
  for (j = 0; j < hdr_list_len; j++) {
   struct ice_vc_hdr_match_type hdr_map = hdr_list[j];

   if (proto_hdr->type == hdr_map.vc_hdr) {
    *addl_hdrs |= hdr_map.ice_hdr;
    hdr_found = true;
   }
  }

  if (!hdr_found)
   return false;

  /* Find matched ice hash fields according to
 * virtchnl hash fields.
 */

  for (j = 0; j < hf_list_len; j++) {
   struct ice_vc_hash_field_match_type hf_map = hf_list[j];

   if (proto_hdr->type == hf_map.vc_hdr &&
       proto_hdr->field_selector == hf_map.vc_hash_field) {
    *hash_flds |= hf_map.ice_hash_field;
    break;
   }
  }
 }

 return true;
}

/**
 * ice_vf_adv_rss_offload_ena - determine if capabilities support advanced
 * RSS offloads
 * @caps: VF driver negotiated capabilities
 *
 * Return true if VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF capability is set,
 * else return false
 */

static bool ice_vf_adv_rss_offload_ena(u32 caps)
{
 return !!(caps & VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF);
}

/**
 * ice_vc_handle_rss_cfg
 * @vf: pointer to the VF info
 * @msg: pointer to the message buffer
 * @add: add a RSS config if true, otherwise delete a RSS config
 *
 * This function adds/deletes a RSS config
 */

static int ice_vc_handle_rss_cfg(struct ice_vf *vf, u8 *msg, bool add)
{
 u32 v_opcode = add ? VIRTCHNL_OP_ADD_RSS_CFG : VIRTCHNL_OP_DEL_RSS_CFG;
 struct virtchnl_rss_cfg *rss_cfg = (struct virtchnl_rss_cfg *)msg;
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct device *dev = ice_pf_to_dev(vf->pf);
 struct ice_hw *hw = &vf->pf->hw;
 struct ice_vsi *vsi;

 if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
  dev_dbg(dev, "VF %d attempting to configure RSS, but RSS is not supported by the PF\n",
   vf->vf_id);
  v_ret = VIRTCHNL_STATUS_ERR_NOT_SUPPORTED;
  goto error_param;
 }

 if (!ice_vf_adv_rss_offload_ena(vf->driver_caps)) {
  dev_dbg(dev, "VF %d attempting to configure RSS, but Advanced RSS offload is not supported\n",
   vf->vf_id);
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (rss_cfg->proto_hdrs.count > VIRTCHNL_MAX_NUM_PROTO_HDRS ||
     rss_cfg->rss_algorithm < VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC ||
     rss_cfg->rss_algorithm > VIRTCHNL_RSS_ALG_XOR_SYMMETRIC) {
  dev_dbg(dev, "VF %d attempting to configure RSS, but RSS configuration is not valid\n",
   vf->vf_id);
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_validate_pattern(vf, &rss_cfg->proto_hdrs)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_R_ASYMMETRIC) {
  struct ice_vsi_ctx *ctx;
  u8 lut_type, hash_type;
  int status;

  lut_type = ICE_AQ_VSI_Q_OPT_RSS_LUT_VSI;
  hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_XOR :
    ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ;

  ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
  if (!ctx) {
   v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY;
   goto error_param;
  }

  ctx->info.q_opt_rss =
   FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_LUT_M, lut_type) |
   FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hash_type);

  /* Preserve existing queueing option setting */
  ctx->info.q_opt_rss |= (vsi->info.q_opt_rss &
       ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_M);
  ctx->info.q_opt_tc = vsi->info.q_opt_tc;
  ctx->info.q_opt_flags = vsi->info.q_opt_rss;

  ctx->info.valid_sections =
    cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID);

  status = ice_update_vsi(hw, vsi->idx, ctx, NULL);
  if (status) {
   dev_err(dev, "update VSI for RSS failed, err %d aq_err %s\n",
    status, libie_aq_str(hw->adminq.sq_last_status));
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  } else {
   vsi->info.q_opt_rss = ctx->info.q_opt_rss;
  }

  kfree(ctx);
 } else {
  struct ice_rss_hash_cfg cfg;

  /* Only check for none raw pattern case */
  if (!ice_vc_validate_pattern(vf, &rss_cfg->proto_hdrs)) {
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }
  cfg.addl_hdrs = ICE_FLOW_SEG_HDR_NONE;
  cfg.hash_flds = ICE_HASH_INVALID;
  cfg.hdr_type = ICE_RSS_ANY_HEADERS;

  if (!ice_vc_parse_rss_cfg(hw, rss_cfg, &cfg)) {
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  if (add) {
   if (ice_add_rss_cfg(hw, vsi, &cfg)) {
    v_ret = VIRTCHNL_STATUS_ERR_PARAM;
    dev_err(dev, "ice_add_rss_cfg failed for vsi = %d, v_ret = %d\n",
     vsi->vsi_num, v_ret);
   }
  } else {
   int status;

   status = ice_rem_rss_cfg(hw, vsi->idx, &cfg);
   /* We just ignore -ENOENT, because if two configurations
 * share the same profile remove one of them actually
 * removes both, since the profile is deleted.
 */

   if (status && status != -ENOENT) {
    v_ret = VIRTCHNL_STATUS_ERR_PARAM;
    dev_err(dev, "ice_rem_rss_cfg failed for VF ID:%d, error:%d\n",
     vf->vf_id, status);
   }
  }
 }

error_param:
 return ice_vc_send_msg_to_vf(vf, v_opcode, v_ret, NULL, 0);
}

/**
 * ice_vc_config_rss_key
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * Configure the VF's RSS key
 */

static int ice_vc_config_rss_key(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_rss_key *vrk =
  (struct virtchnl_rss_key *)msg;
 struct ice_vsi *vsi;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, vrk->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (vrk->key_len != ICE_VSIQF_HKEY_ARRAY_SIZE) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (ice_set_rss_key(vsi, vrk->key))
  v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR;
error_param:
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_KEY, v_ret,
         NULL, 0);
}

/**
 * ice_vc_config_rss_lut
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * Configure the VF's RSS LUT
 */

static int ice_vc_config_rss_lut(struct ice_vf *vf, u8 *msg)
{
 struct virtchnl_rss_lut *vrl = (struct virtchnl_rss_lut *)msg;
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct ice_vsi *vsi;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, vrl->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (vrl->lut_entries != ICE_LUT_VSI_SIZE) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (ice_set_rss_lut(vsi, vrl->lut, ICE_LUT_VSI_SIZE))
  v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR;
error_param:
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_LUT, v_ret,
         NULL, 0);
}

/**
 * ice_vc_config_rss_hfunc
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * Configure the VF's RSS Hash function
 */

static int ice_vc_config_rss_hfunc(struct ice_vf *vf, u8 *msg)
{
 struct virtchnl_rss_hfunc *vrh = (struct virtchnl_rss_hfunc *)msg;
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 u8 hfunc = ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ;
 struct ice_vsi *vsi;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, vrh->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (vrh->rss_algorithm == VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC)
  hfunc = ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ;

 if (ice_set_rss_hfunc(vsi, hfunc))
  v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR;
error_param:
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_HFUNC, v_ret,
         NULL, 0);
}

/**
 * ice_vc_get_qos_caps - Get current QoS caps from PF
 * @vf: pointer to the VF info
 *
 * Get VF's QoS capabilities, such as TC number, arbiter and
 * bandwidth from PF.
 *
 * Return: 0 on success or negative error value.
 */

static int ice_vc_get_qos_caps(struct ice_vf *vf)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_qos_cap_list *cap_list = NULL;
 u8 tc_prio[ICE_MAX_TRAFFIC_CLASS] = { 0 };
 struct virtchnl_qos_cap_elem *cfg = NULL;
 struct ice_vsi_ctx *vsi_ctx;
 struct ice_pf *pf = vf->pf;
 struct ice_port_info *pi;
 struct ice_vsi *vsi;
 u8 numtc, tc;
 u16 len = 0;
 int ret, i;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 pi = pf->hw.port_info;
 numtc = vsi->tc_cfg.numtc;

 vsi_ctx = ice_get_vsi_ctx(pi->hw, vf->lan_vsi_idx);
 if (!vsi_ctx) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 len = struct_size(cap_list, cap, numtc);
 cap_list = kzalloc(len, GFP_KERNEL);
 if (!cap_list) {
  v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY;
  len = 0;
  goto err;
 }

 cap_list->vsi_id = vsi->vsi_num;
 cap_list->num_elem = numtc;

 /* Store the UP2TC configuration from DCB to a user priority bitmap
 * of each TC. Each element of prio_of_tc represents one TC. Each
 * bitmap indicates the user priorities belong to this TC.
 */

 for (i = 0; i < ICE_MAX_USER_PRIORITY; i++) {
  tc = pi->qos_cfg.local_dcbx_cfg.etscfg.prio_table[i];
  tc_prio[tc] |= BIT(i);
 }

 for (i = 0; i < numtc; i++) {
  cfg = &cap_list->cap[i];
  cfg->tc_num = i;
  cfg->tc_prio = tc_prio[i];
  cfg->arbiter = pi->qos_cfg.local_dcbx_cfg.etscfg.tsatable[i];
  cfg->weight = VIRTCHNL_STRICT_WEIGHT;
  cfg->type = VIRTCHNL_BW_SHAPER;
  cfg->shaper.committed = vsi_ctx->sched.bw_t_info[i].cir_bw.bw;
  cfg->shaper.peak = vsi_ctx->sched.bw_t_info[i].eir_bw.bw;
 }

err:
 ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_QOS_CAPS, v_ret,
        (u8 *)cap_list, len);
 kfree(cap_list);
 return ret;
}

/**
 * ice_vf_cfg_qs_bw - Configure per queue bandwidth
 * @vf: pointer to the VF info
 * @num_queues: number of queues to be configured
 *
 * Configure per queue bandwidth.
 *
 * Return: 0 on success or negative error value.
 */

static int ice_vf_cfg_qs_bw(struct ice_vf *vf, u16 num_queues)
{
 struct ice_hw *hw = &vf->pf->hw;
 struct ice_vsi *vsi;
 int ret;
 u16 i;

 vsi = ice_get_vf_vsi(vf);
 if (!vsi)
  return -EINVAL;

 for (i = 0; i < num_queues; i++) {
  u32 p_rate, min_rate;
  u8 tc;

  p_rate = vf->qs_bw[i].peak;
  min_rate = vf->qs_bw[i].committed;
  tc = vf->qs_bw[i].tc;
  if (p_rate)
   ret = ice_cfg_q_bw_lmt(hw->port_info, vsi->idx, tc,
            vf->qs_bw[i].queue_id,
            ICE_MAX_BW, p_rate);
  else
   ret = ice_cfg_q_bw_dflt_lmt(hw->port_info, vsi->idx, tc,
          vf->qs_bw[i].queue_id,
          ICE_MAX_BW);
  if (ret)
   return ret;

  if (min_rate)
   ret = ice_cfg_q_bw_lmt(hw->port_info, vsi->idx, tc,
            vf->qs_bw[i].queue_id,
            ICE_MIN_BW, min_rate);
  else
   ret = ice_cfg_q_bw_dflt_lmt(hw->port_info, vsi->idx, tc,
          vf->qs_bw[i].queue_id,
          ICE_MIN_BW);

  if (ret)
   return ret;
 }

 return 0;
}

/**
 * ice_vf_cfg_q_quanta_profile - Configure quanta profile
 * @vf: pointer to the VF info
 * @quanta_prof_idx: pointer to the quanta profile index
 * @quanta_size: quanta size to be set
 *
 * This function chooses available quanta profile and configures the register.
 * The quanta profile is evenly divided by the number of device ports, and then
 * available to the specific PF and VFs. The first profile for each PF is a
 * reserved default profile. Only quanta size of the rest unused profile can be
 * modified.
 *
 * Return: 0 on success or negative error value.
 */

static int ice_vf_cfg_q_quanta_profile(struct ice_vf *vf, u16 quanta_size,
           u16 *quanta_prof_idx)
{
 const u16 n_desc = calc_quanta_desc(quanta_size);
 struct ice_hw *hw = &vf->pf->hw;
 const u16 n_cmd = 2 * n_desc;
 struct ice_pf *pf = vf->pf;
 u16 per_pf, begin_id;
 u8 n_used;
 u32 reg;

 begin_id = (GLCOMM_QUANTA_PROF_MAX_INDEX + 1) / hw->dev_caps.num_funcs *
     hw->logical_pf_id;

 if (quanta_size == ICE_DFLT_QUANTA) {
  *quanta_prof_idx = begin_id;
 } else {
  per_pf = (GLCOMM_QUANTA_PROF_MAX_INDEX + 1) /
    hw->dev_caps.num_funcs;
  n_used = pf->num_quanta_prof_used;
  if (n_used < per_pf) {
   *quanta_prof_idx = begin_id + 1 + n_used;
   pf->num_quanta_prof_used++;
  } else {
   return -EINVAL;
  }
 }

 reg = FIELD_PREP(GLCOMM_QUANTA_PROF_QUANTA_SIZE_M, quanta_size) |
       FIELD_PREP(GLCOMM_QUANTA_PROF_MAX_CMD_M, n_cmd) |
       FIELD_PREP(GLCOMM_QUANTA_PROF_MAX_DESC_M, n_desc);
 wr32(hw, GLCOMM_QUANTA_PROF(*quanta_prof_idx), reg);

 return 0;
}

/**
 * ice_vc_cfg_promiscuous_mode_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to configure VF VSIs promiscuous mode
 */

static int ice_vc_cfg_promiscuous_mode_msg(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 bool rm_promisc, alluni = false, allmulti = false;
 struct virtchnl_promisc_info *info =
     (struct virtchnl_promisc_info *)msg;
 struct ice_vsi_vlan_ops *vlan_ops;
 int mcast_err = 0, ucast_err = 0;
 struct ice_pf *pf = vf->pf;
 struct ice_vsi *vsi;
 u8 mcast_m, ucast_m;
 struct device *dev;
 int ret = 0;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, info->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 dev = ice_pf_to_dev(pf);
 if (!ice_is_vf_trusted(vf)) {
  dev_err(dev, "Unprivileged VF %d is attempting to configure promiscuous mode\n",
   vf->vf_id);
  /* Leave v_ret alone, lie to the VF on purpose. */
  goto error_param;
 }

 if (info->flags & FLAG_VF_UNICAST_PROMISC)
  alluni = true;

 if (info->flags & FLAG_VF_MULTICAST_PROMISC)
  allmulti = true;

 rm_promisc = !allmulti && !alluni;

 vlan_ops = ice_get_compat_vsi_vlan_ops(vsi);
 if (rm_promisc)
  ret = vlan_ops->ena_rx_filtering(vsi);
 else
  ret = vlan_ops->dis_rx_filtering(vsi);
 if (ret) {
  dev_err(dev, "Failed to configure VLAN pruning in promiscuous mode\n");
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 ice_vf_get_promisc_masks(vf, vsi, &ucast_m, &mcast_m);

 if (!test_bit(ICE_FLAG_VF_TRUE_PROMISC_ENA, pf->flags)) {
  if (alluni) {
   /* in this case we're turning on promiscuous mode */
   ret = ice_set_dflt_vsi(vsi);
  } else {
   /* in this case we're turning off promiscuous mode */
   if (ice_is_dflt_vsi_in_use(vsi->port_info))
    ret = ice_clear_dflt_vsi(vsi);
  }

  /* in this case we're turning on/off only
 * allmulticast
 */

  if (allmulti)
   mcast_err = ice_vf_set_vsi_promisc(vf, vsi, mcast_m);
  else
   mcast_err = ice_vf_clear_vsi_promisc(vf, vsi, mcast_m);

  if (ret) {
   dev_err(dev, "Turning on/off promiscuous mode for VF %d failed, error: %d\n",
    vf->vf_id, ret);
   v_ret = VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR;
   goto error_param;
  }
 } else {
  if (alluni)
   ucast_err = ice_vf_set_vsi_promisc(vf, vsi, ucast_m);
  else
   ucast_err = ice_vf_clear_vsi_promisc(vf, vsi, ucast_m);

  if (allmulti)
   mcast_err = ice_vf_set_vsi_promisc(vf, vsi, mcast_m);
  else
   mcast_err = ice_vf_clear_vsi_promisc(vf, vsi, mcast_m);

  if (ucast_err || mcast_err)
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
 }

 if (!mcast_err) {
  if (allmulti &&
      !test_and_set_bit(ICE_VF_STATE_MC_PROMISC, vf->vf_states))
   dev_info(dev, "VF %u successfully set multicast promiscuous mode\n",
     vf->vf_id);
  else if (!allmulti &&
    test_and_clear_bit(ICE_VF_STATE_MC_PROMISC,
         vf->vf_states))
   dev_info(dev, "VF %u successfully unset multicast promiscuous mode\n",
     vf->vf_id);
 } else {
  dev_err(dev, "Error while modifying multicast promiscuous mode for VF %u, error: %d\n",
   vf->vf_id, mcast_err);
 }

 if (!ucast_err) {
  if (alluni &&
      !test_and_set_bit(ICE_VF_STATE_UC_PROMISC, vf->vf_states))
   dev_info(dev, "VF %u successfully set unicast promiscuous mode\n",
     vf->vf_id);
  else if (!alluni &&
    test_and_clear_bit(ICE_VF_STATE_UC_PROMISC,
         vf->vf_states))
   dev_info(dev, "VF %u successfully unset unicast promiscuous mode\n",
     vf->vf_id);
 } else {
  dev_err(dev, "Error while modifying unicast promiscuous mode for VF %u, error: %d\n",
   vf->vf_id, ucast_err);
 }

error_param:
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
         v_ret, NULL, 0);
}

/**
 * ice_vc_get_stats_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to get VSI stats
 */

static int ice_vc_get_stats_msg(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_queue_select *vqs =
  (struct virtchnl_queue_select *)msg;
 struct ice_eth_stats stats = { 0 };
 struct ice_vsi *vsi;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, vqs->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 ice_update_eth_stats(vsi);

 stats = vsi->eth_stats;

error_param:
 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS, v_ret,
         (u8 *)&stats, sizeof(stats));
}

/**
 * ice_vc_validate_vqs_bitmaps - validate Rx/Tx queue bitmaps from VIRTCHNL
 * @vqs: virtchnl_queue_select structure containing bitmaps to validate
 *
 * Return true on successful validation, else false
 */

static bool ice_vc_validate_vqs_bitmaps(struct virtchnl_queue_select *vqs)
{
 if ((!vqs->rx_queues && !vqs->tx_queues) ||
     vqs->rx_queues >= BIT(ICE_MAX_RSS_QS_PER_VF) ||
     vqs->tx_queues >= BIT(ICE_MAX_RSS_QS_PER_VF))
  return false;

 return true;
}

/**
 * ice_vf_ena_txq_interrupt - enable Tx queue interrupt via QINT_TQCTL
 * @vsi: VSI of the VF to configure
 * @q_idx: VF queue index used to determine the queue in the PF's space
 */

void ice_vf_ena_txq_interrupt(struct ice_vsi *vsi, u32 q_idx)
{
 struct ice_hw *hw = &vsi->back->hw;
 u32 pfq = vsi->txq_map[q_idx];
 u32 reg;

 reg = rd32(hw, QINT_TQCTL(pfq));

 /* MSI-X index 0 in the VF's space is always for the OICR, which means
 * this is most likely a poll mode VF driver, so don't enable an
 * interrupt that was never configured via VIRTCHNL_OP_CONFIG_IRQ_MAP
 */

 if (!(reg & QINT_TQCTL_MSIX_INDX_M))
  return;

 wr32(hw, QINT_TQCTL(pfq), reg | QINT_TQCTL_CAUSE_ENA_M);
}

/**
 * ice_vf_ena_rxq_interrupt - enable Tx queue interrupt via QINT_RQCTL
 * @vsi: VSI of the VF to configure
 * @q_idx: VF queue index used to determine the queue in the PF's space
 */

void ice_vf_ena_rxq_interrupt(struct ice_vsi *vsi, u32 q_idx)
{
 struct ice_hw *hw = &vsi->back->hw;
 u32 pfq = vsi->rxq_map[q_idx];
 u32 reg;

 reg = rd32(hw, QINT_RQCTL(pfq));

 /* MSI-X index 0 in the VF's space is always for the OICR, which means
 * this is most likely a poll mode VF driver, so don't enable an
 * interrupt that was never configured via VIRTCHNL_OP_CONFIG_IRQ_MAP
 */

 if (!(reg & QINT_RQCTL_MSIX_INDX_M))
  return;

 wr32(hw, QINT_RQCTL(pfq), reg | QINT_RQCTL_CAUSE_ENA_M);
}

/**
 * ice_vc_ena_qs_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to enable all or specific queue(s)
 */

static int ice_vc_ena_qs_msg(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_queue_select *vqs =
     (struct virtchnl_queue_select *)msg;
 struct ice_vsi *vsi;
 unsigned long q_map;
 u16 vf_q_id;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, vqs->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_validate_vqs_bitmaps(vqs)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 /* Enable only Rx rings, Tx rings were enabled by the FW when the
 * Tx queue group list was configured and the context bits were
 * programmed using ice_vsi_cfg_txqs
 */

 q_map = vqs->rx_queues;
 for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
  if (!ice_vc_isvalid_q_id(vsi, vf_q_id)) {
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  /* Skip queue if enabled */
  if (test_bit(vf_q_id, vf->rxq_ena))
   continue;

  if (ice_vsi_ctrl_one_rx_ring(vsi, true, vf_q_id, true)) {
   dev_err(ice_pf_to_dev(vsi->back), "Failed to enable Rx ring %d on VSI %d\n",
    vf_q_id, vsi->vsi_num);
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  ice_vf_ena_rxq_interrupt(vsi, vf_q_id);
  set_bit(vf_q_id, vf->rxq_ena);
 }

 q_map = vqs->tx_queues;
 for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
  if (!ice_vc_isvalid_q_id(vsi, vf_q_id)) {
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  /* Skip queue if enabled */
  if (test_bit(vf_q_id, vf->txq_ena))
   continue;

  ice_vf_ena_txq_interrupt(vsi, vf_q_id);
  set_bit(vf_q_id, vf->txq_ena);
 }

 /* Set flag to indicate that queues are enabled */
 if (v_ret == VIRTCHNL_STATUS_SUCCESS)
  set_bit(ICE_VF_STATE_QS_ENA, vf->vf_states);

error_param:
 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ENABLE_QUEUES, v_ret,
         NULL, 0);
}

/**
 * ice_vf_vsi_dis_single_txq - disable a single Tx queue
 * @vf: VF to disable queue for
 * @vsi: VSI for the VF
 * @q_id: VF relative (0-based) queue ID
 *
 * Attempt to disable the Tx queue passed in. If the Tx queue was successfully
 * disabled then clear q_id bit in the enabled queues bitmap and return
 * success. Otherwise return error.
 */

int ice_vf_vsi_dis_single_txq(struct ice_vf *vf, struct ice_vsi *vsi, u16 q_id)
{
 struct ice_txq_meta txq_meta = { 0 };
 struct ice_tx_ring *ring;
 int err;

 if (!test_bit(q_id, vf->txq_ena))
  dev_dbg(ice_pf_to_dev(vsi->back), "Queue %u on VSI %u is not enabled, but stopping it anyway\n",
   q_id, vsi->vsi_num);

 ring = vsi->tx_rings[q_id];
 if (!ring)
  return -EINVAL;

 ice_fill_txq_meta(vsi, ring, &txq_meta);

 err = ice_vsi_stop_tx_ring(vsi, ICE_NO_RESET, vf->vf_id, ring, &txq_meta);
 if (err) {
  dev_err(ice_pf_to_dev(vsi->back), "Failed to stop Tx ring %d on VSI %d\n",
   q_id, vsi->vsi_num);
  return err;
 }

 /* Clear enabled queues flag */
 clear_bit(q_id, vf->txq_ena);

 return 0;
}

/**
 * ice_vc_dis_qs_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to disable all or specific queue(s)
 */

static int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_queue_select *vqs =
     (struct virtchnl_queue_select *)msg;
 struct ice_vsi *vsi;
 unsigned long q_map;
 u16 vf_q_id;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states) &&
     !test_bit(ICE_VF_STATE_QS_ENA, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_isvalid_vsi_id(vf, vqs->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (!ice_vc_validate_vqs_bitmaps(vqs)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 if (vqs->tx_queues) {
  q_map = vqs->tx_queues;

  for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
   if (!ice_vc_isvalid_q_id(vsi, vf_q_id)) {
    v_ret = VIRTCHNL_STATUS_ERR_PARAM;
    goto error_param;
   }

   if (ice_vf_vsi_dis_single_txq(vf, vsi, vf_q_id)) {
    v_ret = VIRTCHNL_STATUS_ERR_PARAM;
    goto error_param;
   }
  }
 }

 q_map = vqs->rx_queues;
 /* speed up Rx queue disable by batching them if possible */
 if (q_map &&
     bitmap_equal(&q_map, vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF)) {
  if (ice_vsi_stop_all_rx_rings(vsi)) {
   dev_err(ice_pf_to_dev(vsi->back), "Failed to stop all Rx rings on VSI %d\n",
    vsi->vsi_num);
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  bitmap_zero(vf->rxq_ena, ICE_MAX_RSS_QS_PER_VF);
 } else if (q_map) {
  for_each_set_bit(vf_q_id, &q_map, ICE_MAX_RSS_QS_PER_VF) {
   if (!ice_vc_isvalid_q_id(vsi, vf_q_id)) {
    v_ret = VIRTCHNL_STATUS_ERR_PARAM;
    goto error_param;
   }

   /* Skip queue if not enabled */
   if (!test_bit(vf_q_id, vf->rxq_ena))
    continue;

   if (ice_vsi_ctrl_one_rx_ring(vsi, false, vf_q_id,
           true)) {
    dev_err(ice_pf_to_dev(vsi->back), "Failed to stop Rx ring %d on VSI %d\n",
     vf_q_id, vsi->vsi_num);
    v_ret = VIRTCHNL_STATUS_ERR_PARAM;
    goto error_param;
   }

   /* Clear enabled queues flag */
   clear_bit(vf_q_id, vf->rxq_ena);
  }
 }

 /* Clear enabled queues flag */
 if (v_ret == VIRTCHNL_STATUS_SUCCESS && ice_vf_has_no_qs_ena(vf))
  clear_bit(ICE_VF_STATE_QS_ENA, vf->vf_states);

error_param:
 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DISABLE_QUEUES, v_ret,
         NULL, 0);
}

/**
 * ice_cfg_interrupt
 * @vf: pointer to the VF info
 * @vsi: the VSI being configured
 * @map: vector map for mapping vectors to queues
 * @q_vector: structure for interrupt vector
 * configure the IRQ to queue map
 */

static enum virtchnl_status_code
ice_cfg_interrupt(struct ice_vf *vf, struct ice_vsi *vsi,
    struct virtchnl_vector_map *map,
    struct ice_q_vector *q_vector)
{
 u16 vsi_q_id, vsi_q_id_idx;
 unsigned long qmap;

 q_vector->num_ring_rx = 0;
 q_vector->num_ring_tx = 0;

 qmap = map->rxq_map;
 for_each_set_bit(vsi_q_id_idx, &qmap, ICE_MAX_RSS_QS_PER_VF) {
  vsi_q_id = vsi_q_id_idx;

  if (!ice_vc_isvalid_q_id(vsi, vsi_q_id))
   return VIRTCHNL_STATUS_ERR_PARAM;

  q_vector->num_ring_rx++;
  q_vector->rx.itr_idx = map->rxitr_idx;
  vsi->rx_rings[vsi_q_id]->q_vector = q_vector;
  ice_cfg_rxq_interrupt(vsi, vsi_q_id,
          q_vector->vf_reg_idx,
          q_vector->rx.itr_idx);
 }

 qmap = map->txq_map;
 for_each_set_bit(vsi_q_id_idx, &qmap, ICE_MAX_RSS_QS_PER_VF) {
  vsi_q_id = vsi_q_id_idx;

  if (!ice_vc_isvalid_q_id(vsi, vsi_q_id))
   return VIRTCHNL_STATUS_ERR_PARAM;

  q_vector->num_ring_tx++;
  q_vector->tx.itr_idx = map->txitr_idx;
  vsi->tx_rings[vsi_q_id]->q_vector = q_vector;
  ice_cfg_txq_interrupt(vsi, vsi_q_id,
          q_vector->vf_reg_idx,
          q_vector->tx.itr_idx);
 }

 return VIRTCHNL_STATUS_SUCCESS;
}

/**
 * ice_vc_cfg_irq_map_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to configure the IRQ to queue map
 */

static int ice_vc_cfg_irq_map_msg(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 u16 num_q_vectors_mapped, vsi_id, vector_id;
 struct virtchnl_irq_map_info *irqmap_info;
 struct virtchnl_vector_map *map;
 struct ice_vsi *vsi;
 int i;

 irqmap_info = (struct virtchnl_irq_map_info *)msg;
 num_q_vectors_mapped = irqmap_info->num_vectors;

 /* Check to make sure number of VF vectors mapped is not greater than
 * number of VF vectors originally allocated, and check that
 * there is actually at least a single VF queue vector mapped
 */

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states) ||
     vf->num_msix < num_q_vectors_mapped ||
     !num_q_vectors_mapped) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto error_param;
 }

 for (i = 0; i < num_q_vectors_mapped; i++) {
  struct ice_q_vector *q_vector;

  map = &irqmap_info->vecmap[i];

  vector_id = map->vector_id;
  vsi_id = map->vsi_id;
  /* vector_id is always 0-based for each VF, and can never be
 * larger than or equal to the max allowed interrupts per VF
 */

  if (!(vector_id < vf->num_msix) ||
      !ice_vc_isvalid_vsi_id(vf, vsi_id) ||
      (!vector_id && (map->rxq_map || map->txq_map))) {
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  /* No need to map VF miscellaneous or rogue vector */
  if (!vector_id)
   continue;

  /* Subtract non queue vector from vector_id passed by VF
 * to get actual number of VSI queue vector array index
 */

  q_vector = vsi->q_vectors[vector_id - ICE_NONQ_VECS_VF];
  if (!q_vector) {
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto error_param;
  }

  /* lookout for the invalid queue index */
  v_ret = ice_cfg_interrupt(vf, vsi, map, q_vector);
  if (v_ret)
   goto error_param;
 }

error_param:
 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_IRQ_MAP, v_ret,
         NULL, 0);
}

/**
 * ice_vc_cfg_q_bw - Configure per queue bandwidth
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer which holds the command descriptor
 *
 * Configure VF queues bandwidth.
 *
 * Return: 0 on success or negative error value.
 */

static int ice_vc_cfg_q_bw(struct ice_vf *vf, u8 *msg)
{
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_queues_bw_cfg *qbw =
  (struct virtchnl_queues_bw_cfg *)msg;
 struct ice_vsi *vsi;
 u16 i;

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states) ||
     !ice_vc_isvalid_vsi_id(vf, qbw->vsi_id)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 if (qbw->num_queues > ICE_MAX_RSS_QS_PER_VF ||
     qbw->num_queues > min_t(u16, vsi->alloc_txq, vsi->alloc_rxq)) {
  dev_err(ice_pf_to_dev(vf->pf), "VF-%d trying to configure more than allocated number of queues: %d\n",
   vf->vf_id, min_t(u16, vsi->alloc_txq, vsi->alloc_rxq));
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 for (i = 0; i < qbw->num_queues; i++) {
  if (qbw->cfg[i].shaper.peak != 0 && vf->max_tx_rate != 0 &&
      qbw->cfg[i].shaper.peak > vf->max_tx_rate) {
   dev_warn(ice_pf_to_dev(vf->pf), "The maximum queue %d rate limit configuration may not take effect because the maximum TX rate for VF-%d is %d\n",
     qbw->cfg[i].queue_id, vf->vf_id,
     vf->max_tx_rate);
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto err;
  }
  if (qbw->cfg[i].shaper.committed != 0 && vf->min_tx_rate != 0 &&
      qbw->cfg[i].shaper.committed < vf->min_tx_rate) {
   dev_warn(ice_pf_to_dev(vf->pf), "The minimum queue %d rate limit configuration may not take effect because the minimum TX rate for VF-%d is %d\n",
     qbw->cfg[i].queue_id, vf->vf_id,
     vf->min_tx_rate);
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto err;
  }
  if (qbw->cfg[i].queue_id > vf->num_vf_qs) {
   dev_warn(ice_pf_to_dev(vf->pf), "VF-%d trying to configure invalid queue_id\n",
     vf->vf_id);
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto err;
  }
  if (qbw->cfg[i].tc >= ICE_MAX_TRAFFIC_CLASS) {
   dev_warn(ice_pf_to_dev(vf->pf), "VF-%d trying to configure a traffic class higher than allowed\n",
     vf->vf_id);
   v_ret = VIRTCHNL_STATUS_ERR_PARAM;
   goto err;
  }
 }

 for (i = 0; i < qbw->num_queues; i++) {
  vf->qs_bw[i].queue_id = qbw->cfg[i].queue_id;
  vf->qs_bw[i].peak = qbw->cfg[i].shaper.peak;
  vf->qs_bw[i].committed = qbw->cfg[i].shaper.committed;
  vf->qs_bw[i].tc = qbw->cfg[i].tc;
 }

 if (ice_vf_cfg_qs_bw(vf, qbw->num_queues))
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;

err:
 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_QUEUE_BW,
        v_ret, NULL, 0);
}

/**
 * ice_vc_cfg_q_quanta - Configure per queue quanta
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer which holds the command descriptor
 *
 * Configure VF queues quanta.
 *
 * Return: 0 on success or negative error value.
 */

static int ice_vc_cfg_q_quanta(struct ice_vf *vf, u8 *msg)
{
 u16 quanta_prof_id, quanta_size, start_qid, num_queues, end_qid, i;
 enum virtchnl_status_code v_ret = VIRTCHNL_STATUS_SUCCESS;
 struct virtchnl_quanta_cfg *qquanta =
  (struct virtchnl_quanta_cfg *)msg;
 struct ice_vsi *vsi;
 int ret;

 start_qid = qquanta->queue_select.start_queue_id;
 num_queues = qquanta->queue_select.num_queues;

 if (check_add_overflow(start_qid, num_queues, &end_qid)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 vsi = ice_get_vf_vsi(vf);
 if (!vsi) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 if (end_qid > ICE_MAX_RSS_QS_PER_VF ||
     end_qid > min_t(u16, vsi->alloc_txq, vsi->alloc_rxq)) {
  dev_err(ice_pf_to_dev(vf->pf), "VF-%d trying to configure more than allocated number of queues: %d\n",
   vf->vf_id, min_t(u16, vsi->alloc_txq, vsi->alloc_rxq));
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 quanta_size = qquanta->quanta_size;
 if (quanta_size > ICE_MAX_QUANTA_SIZE ||
     quanta_size < ICE_MIN_QUANTA_SIZE) {
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 if (quanta_size % 64) {
  dev_err(ice_pf_to_dev(vf->pf), "quanta size should be the product of 64\n");
  v_ret = VIRTCHNL_STATUS_ERR_PARAM;
  goto err;
 }

 ret = ice_vf_cfg_q_quanta_profile(vf, quanta_size,
       &quanta_prof_id);
 if (ret) {
  v_ret = VIRTCHNL_STATUS_ERR_NOT_SUPPORTED;
  goto err;
 }

 for (i = start_qid; i < end_qid; i++)
  vsi->tx_rings[i]->quanta_prof_id = quanta_prof_id;

err:
 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_QUANTA,
         v_ret, NULL, 0);
}

/**
 * ice_vc_cfg_qs_msg
 * @vf: pointer to the VF info
 * @msg: pointer to the msg buffer
 *
 * called from the VF to configure the Rx/Tx queues
 */

static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
{
 struct virtchnl_vsi_queue_config_info *qci =
     (struct virtchnl_vsi_queue_config_info *)msg;
 struct virtchnl_queue_pair_info *qpi;
 struct ice_pf *pf = vf->pf;
 struct ice_vsi *vsi;
 int i = -1, q_idx;
 bool ena_ts;
 u8 act_prt;

 mutex_lock(&pf->lag_mutex);
 act_prt = ice_lag_prepare_vf_reset(pf->lag);

 if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states))
  goto error_param;

 if (!ice_vc_isvalid_vsi_id(vf, qci->vsi_id))
  goto error_param;

 vsi = ice_get_vf_vsi(vf);
 if (!vsi)
  goto error_param;

 if (qci->num_queue_pairs > ICE_MAX_RSS_QS_PER_VF ||
     qci->num_queue_pairs > min_t(u16, vsi->alloc_txq, vsi->alloc_rxq)) {
  dev_err(ice_pf_to_dev(pf), "VF-%d requesting more than supported number of queues: %d\n",
   vf->vf_id, min_t(u16, vsi->alloc_txq, vsi->alloc_rxq));
  goto error_param;
 }

 for (i = 0; i < qci->num_queue_pairs; i++) {
  if (!qci->qpair[i].rxq.crc_disable)
   continue;

  if (!(vf->driver_caps & VIRTCHNL_VF_OFFLOAD_CRC) ||
      vf->vlan_strip_ena)
   goto error_param;
 }

 for (i = 0; i < qci->num_queue_pairs; i++) {
  qpi = &qci->qpair[i];
  if (qpi->txq.vsi_id != qci->vsi_id ||
      qpi->rxq.vsi_id != qci->vsi_id ||
      qpi->rxq.queue_id != qpi->txq.queue_id ||
      qpi->txq.headwb_enabled ||
      !ice_vc_isvalid_ring_len(qpi->txq.ring_len) ||
      !ice_vc_isvalid_ring_len(qpi->rxq.ring_len) ||
      !ice_vc_isvalid_q_id(vsi, qpi->txq.queue_id)) {
   goto error_param;
  }

  q_idx = qpi->rxq.queue_id;

  /* make sure selected "q_idx" is in valid range of queues
 * for selected "vsi"
 */

  if (q_idx >= vsi->alloc_txq || q_idx >= vsi->alloc_rxq) {
   goto error_param;
  }

  /* copy Tx queue info from VF into VSI */
  if (qpi->txq.ring_len > 0) {
   vsi->tx_rings[q_idx]->dma = qpi->txq.dma_ring_addr;
   vsi->tx_rings[q_idx]->count = qpi->txq.ring_len;

   /* Disable any existing queue first */
   if (ice_vf_vsi_dis_single_txq(vf, vsi, q_idx))
    goto error_param;

   /* Configure a queue with the requested settings */
   if (ice_vsi_cfg_single_txq(vsi, vsi->tx_rings, q_idx)) {
    dev_warn(ice_pf_to_dev(pf), "VF-%d failed to configure TX queue %d\n",
      vf->vf_id, q_idx);
    goto error_param;
   }
  }

  /* copy Rx queue info from VF into VSI */
  if (qpi->rxq.ring_len > 0) {
   u16 max_frame_size = ice_vc_get_max_frame_size(vf);
   struct ice_rx_ring *ring = vsi->rx_rings[q_idx];
   u32 rxdid;

   ring->dma = qpi->rxq.dma_ring_addr;
   ring->count = qpi->rxq.ring_len;

   if (qpi->rxq.crc_disable)
    ring->flags |= ICE_RX_FLAGS_CRC_STRIP_DIS;
   else
    ring->flags &= ~ICE_RX_FLAGS_CRC_STRIP_DIS;

   if (qpi->rxq.databuffer_size != 0 &&
       (qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
        qpi->rxq.databuffer_size < 1024))
    goto error_param;
   ring->rx_buf_len = qpi->rxq.databuffer_size;
   if (qpi->rxq.max_pkt_size > max_frame_size ||
       qpi->rxq.max_pkt_size < 64)
    goto error_param;

   ring->max_frame = qpi->rxq.max_pkt_size;
   /* add space for the port VLAN since the VF driver is
 * not expected to account for it in the MTU
 * calculation
 */

   if (ice_vf_is_port_vlan_ena(vf))
    ring->max_frame += VLAN_HLEN;

   if (ice_vsi_cfg_single_rxq(vsi, q_idx)) {
    dev_warn(ice_pf_to_dev(pf), "VF-%d failed to configure RX queue %d\n",
      vf->vf_id, q_idx);
    goto error_param;
   }

   /* If Rx flex desc is supported, select RXDID for Rx
 * queues. Otherwise, use legacy 32byte descriptor
 * format. Legacy 16byte descriptor is not supported.
 * If this RXDID is selected, return error.
 */

   if (vf->driver_caps &
       VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC) {
    rxdid = qpi->rxq.rxdid;
    if (!(BIT(rxdid) & pf->supported_rxdids))
     goto error_param;
   } else {
    rxdid = ICE_RXDID_LEGACY_1;
   }

   ena_ts = ((vf->driver_caps &
      VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC) &&
      (vf->driver_caps & VIRTCHNL_VF_CAP_PTP) &&
      (qpi->rxq.flags & VIRTCHNL_PTP_RX_TSTAMP));

   ice_write_qrxflxp_cntxt(&vsi->back->hw,
      vsi->rxq_map[q_idx], rxdid,
      ICE_RXDID_PRIO, ena_ts);
  }
 }

 ice_lag_complete_vf_reset(pf->lag, act_prt);
 mutex_unlock(&pf->lag_mutex);

 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_VSI_QUEUES,
         VIRTCHNL_STATUS_SUCCESS, NULL, 0);
error_param:
 /* disable whatever we can */
 for (; i >= 0; i--) {
  if (ice_vsi_ctrl_one_rx_ring(vsi, false, i, true))
   dev_err(ice_pf_to_dev(pf), "VF-%d could not disable RX queue %d\n",
    vf->vf_id, i);
  if (ice_vf_vsi_dis_single_txq(vf, vsi, i))
   dev_err(ice_pf_to_dev(pf), "VF-%d could not disable TX queue %d\n",
    vf->vf_id, i);
 }

 ice_lag_complete_vf_reset(pf->lag, act_prt);
 mutex_unlock(&pf->lag_mutex);

 ice_lag_move_new_vf_nodes(vf);

 /* send the response to the VF */
 return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_VSI_QUEUES,
         VIRTCHNL_STATUS_ERR_PARAM, NULL, 0);
}

/**
 * ice_can_vf_change_mac
 * @vf: pointer to the VF info
 *
 * Return true if the VF is allowed to change its MAC filters, false otherwise
 */

static bool ice_can_vf_change_mac(struct ice_vf *vf)
{
 /* If the VF MAC address has been set administratively (via the
 * ndo_set_vf_mac command), then deny permission to the VF to
 * add/delete unicast MAC addresses, unless the VF is trusted
 */

 if (vf->pf_set_mac && !ice_is_vf_trusted(vf))
  return false;

 return true;
}

/**
 * ice_vc_ether_addr_type - get type of virtchnl_ether_addr
 * @vc_ether_addr: used to extract the type
 */

static u8
ice_vc_ether_addr_type(struct virtchnl_ether_addr *vc_ether_addr)
{
 return (vc_ether_addr->type & VIRTCHNL_ETHER_ADDR_TYPE_MASK);
}

/**
 * ice_is_vc_addr_legacy - check if the MAC address is from an older VF
 * @vc_ether_addr: VIRTCHNL structure that contains MAC and type
 */

static bool
ice_is_vc_addr_legacy(struct virtchnl_ether_addr *vc_ether_addr)
{
 u8 type = ice_vc_ether_addr_type(vc_ether_addr);

 return (type == VIRTCHNL_ETHER_ADDR_LEGACY);
}

/**
 * ice_is_vc_addr_primary - check if the MAC address is the VF's primary MAC
 * @vc_ether_addr: VIRTCHNL structure that contains MAC and type
 *
 * This function should only be called when the MAC address in
 * virtchnl_ether_addr is a valid unicast MAC
 */

static bool
ice_is_vc_addr_primary(struct virtchnl_ether_addr __maybe_unused *vc_ether_addr)
{
 u8 type = ice_vc_ether_addr_type(vc_ether_addr);

 return (type == VIRTCHNL_ETHER_ADDR_PRIMARY);
}

/**
 * ice_vfhw_mac_add - update the VF's cached hardware MAC if allowed
 * @vf: VF to update
 * @vc_ether_addr: structure from VIRTCHNL with MAC to add
 */

static void
ice_vfhw_mac_add(struct ice_vf *vf, struct virtchnl_ether_addr *vc_ether_addr)
{
 u8 *mac_addr = vc_ether_addr->addr;

 if (!is_valid_ether_addr(mac_addr))
  return;

 /* only allow legacy VF drivers to set the device and hardware MAC if it
 * is zero and allow new VF drivers to set the hardware MAC if the type
 * was correctly specified over VIRTCHNL
 */

 if ((ice_is_vc_addr_legacy(vc_ether_addr) &&
      is_zero_ether_addr(vf->hw_lan_addr)) ||
     ice_is_vc_addr_primary(vc_ether_addr)) {
  ether_addr_copy(vf->dev_lan_addr, mac_addr);
  ether_addr_copy(vf->hw_lan_addr, mac_addr);
 }

 /* hardware and device MACs are already set, but its possible that the
 * VF driver sent the VIRTCHNL_OP_ADD_ETH_ADDR message before the
 * VIRTCHNL_OP_DEL_ETH_ADDR when trying to update its MAC, so save it
 * away for the legacy VF driver case as it will be updated in the
 * delete flow for this case
 */

 if (ice_is_vc_addr_legacy(vc_ether_addr)) {
  ether_addr_copy(vf->legacy_last_added_umac.addr,
    mac_addr);
  vf->legacy_last_added_umac.time_modified = jiffies;
 }
}

/**
 * ice_is_mc_lldp_eth_addr - check if the given MAC is a multicast LLDP address
 * @mac: address to check
 *
 * Return: true if the address is one of the three possible LLDP multicast
 *    addresses, false otherwise.
 */

static bool ice_is_mc_lldp_eth_addr(const u8 *mac)
{
 const u8 lldp_mac_base[] = {0x01, 0x80, 0xc2, 0x00, 0x00};

 if (memcmp(mac, lldp_mac_base, sizeof(lldp_mac_base)))
  return false;

 return (mac[5] == 0x0e || mac[5] == 0x03 || mac[5] == 0x00);
}

/**
 * ice_vc_can_add_mac - check if the VF is allowed to add a given MAC
 * @vf: a VF to add the address to
 * @mac: address to check
 *
 * Return: true if the VF is allowed to add such MAC address, false otherwise.
 */

static bool ice_vc_can_add_mac(const struct ice_vf *vf, const u8 *mac)
{
 struct device *dev = ice_pf_to_dev(vf->pf);

 if (is_unicast_ether_addr(mac) &&
     !ice_can_vf_change_mac((struct ice_vf *)vf)) {
  dev_err(dev,
   "VF attempting to override administratively set MAC address, bring down and up the VF interface to resume normal operation\n");
  return false;
 }

 if (!vf->trusted && ice_is_mc_lldp_eth_addr(mac)) {
  dev_warn(dev,
    "An untrusted VF %u is attempting to configure an LLDP multicast address\n",
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=72 G=84

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.