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

Quelle  qed_sriov.c   Sprache: C

 
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* QLogic qed NIC Driver
 * Copyright (c) 2015-2017  QLogic Corporation
 * Copyright (c) 2019-2020 Marvell International Ltd.
 */


#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <linux/vmalloc.h>
#include <linux/crash_dump.h>
#include <linux/qed/qed_iov_if.h>
#include "qed_cxt.h"
#include "qed_hsi.h"
#include "qed_iro_hsi.h"
#include "qed_hw.h"
#include "qed_init_ops.h"
#include "qed_int.h"
#include "qed_mcp.h"
#include "qed_reg_addr.h"
#include "qed_sp.h"
#include "qed_sriov.h"
#include "qed_vf.h"
static int qed_iov_bulletin_set_mac(struct qed_hwfn *p_hwfn, u8 *mac, int vfid);

static u16 qed_vf_from_entity_id(__le16 entity_id)
{
 return le16_to_cpu(entity_id) - MAX_NUM_PFS;
}

static u8 qed_vf_calculate_legacy(struct qed_vf_info *p_vf)
{
 u8 legacy = 0;

 if (p_vf->acquire.vfdev_info.eth_fp_hsi_minor ==
     ETH_HSI_VER_NO_PKT_LEN_TUNN)
  legacy |= QED_QCID_LEGACY_VF_RX_PROD;

 if (!(p_vf->acquire.vfdev_info.capabilities &
       VFPF_ACQUIRE_CAP_QUEUE_QIDS))
  legacy |= QED_QCID_LEGACY_VF_CID;

 return legacy;
}

/* IOV ramrods */
static int qed_sp_vf_start(struct qed_hwfn *p_hwfn, struct qed_vf_info *p_vf)
{
 struct vf_start_ramrod_data *p_ramrod = NULL;
 struct qed_spq_entry *p_ent = NULL;
 struct qed_sp_init_data init_data;
 int rc = -EINVAL;
 u8 fp_minor;

 /* Get SPQ entry */
 memset(&init_data, 0, sizeof(init_data));
 init_data.cid = qed_spq_get_cid(p_hwfn);
 init_data.opaque_fid = p_vf->opaque_fid;
 init_data.comp_mode = QED_SPQ_MODE_EBLOCK;

 rc = qed_sp_init_request(p_hwfn, &p_ent,
     COMMON_RAMROD_VF_START,
     PROTOCOLID_COMMON, &init_data);
 if (rc)
  return rc;

 p_ramrod = &p_ent->ramrod.vf_start;

 p_ramrod->vf_id = GET_FIELD(p_vf->concrete_fid, PXP_CONCRETE_FID_VFID);
 p_ramrod->opaque_fid = cpu_to_le16(p_vf->opaque_fid);

 switch (p_hwfn->hw_info.personality) {
 case QED_PCI_ETH:
  p_ramrod->personality = PERSONALITY_ETH;
  break;
 case QED_PCI_ETH_ROCE:
 case QED_PCI_ETH_IWARP:
  p_ramrod->personality = PERSONALITY_RDMA_AND_ETH;
  break;
 default:
  DP_NOTICE(p_hwfn, "Unknown VF personality %d\n",
     p_hwfn->hw_info.personality);
  qed_sp_destroy_request(p_hwfn, p_ent);
  return -EINVAL;
 }

 fp_minor = p_vf->acquire.vfdev_info.eth_fp_hsi_minor;
 if (fp_minor > ETH_HSI_VER_MINOR &&
     fp_minor != ETH_HSI_VER_NO_PKT_LEN_TUNN) {
  DP_VERBOSE(p_hwfn,
      QED_MSG_IOV,
      "VF [%d] - Requested fp hsi %02x.%02x which is slightly newer than PF's %02x.%02x; Configuring PFs version\n",
      p_vf->abs_vf_id,
      ETH_HSI_VER_MAJOR,
      fp_minor, ETH_HSI_VER_MAJOR, ETH_HSI_VER_MINOR);
  fp_minor = ETH_HSI_VER_MINOR;
 }

 p_ramrod->hsi_fp_ver.major_ver_arr[ETH_VER_KEY] = ETH_HSI_VER_MAJOR;
 p_ramrod->hsi_fp_ver.minor_ver_arr[ETH_VER_KEY] = fp_minor;

 DP_VERBOSE(p_hwfn, QED_MSG_IOV,
     "VF[%d] - Starting using HSI %02x.%02x\n",
     p_vf->abs_vf_id, ETH_HSI_VER_MAJOR, fp_minor);

 return qed_spq_post(p_hwfn, p_ent, NULL);
}

static int qed_sp_vf_stop(struct qed_hwfn *p_hwfn,
     u32 concrete_vfid, u16 opaque_vfid)
{
 struct vf_stop_ramrod_data *p_ramrod = NULL;
 struct qed_spq_entry *p_ent = NULL;
 struct qed_sp_init_data init_data;
 int rc = -EINVAL;

 /* Get SPQ entry */
 memset(&init_data, 0, sizeof(init_data));
 init_data.cid = qed_spq_get_cid(p_hwfn);
 init_data.opaque_fid = opaque_vfid;
 init_data.comp_mode = QED_SPQ_MODE_EBLOCK;

 rc = qed_sp_init_request(p_hwfn, &p_ent,
     COMMON_RAMROD_VF_STOP,
     PROTOCOLID_COMMON, &init_data);
 if (rc)
  return rc;

 p_ramrod = &p_ent->ramrod.vf_stop;

 p_ramrod->vf_id = GET_FIELD(concrete_vfid, PXP_CONCRETE_FID_VFID);

 return qed_spq_post(p_hwfn, p_ent, NULL);
}

bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn,
      int rel_vf_id,
      bool b_enabled_only, bool b_non_malicious)
{
 if (!p_hwfn->pf_iov_info) {
  DP_NOTICE(p_hwfn->cdev, "No iov info\n");
  return false;
 }

 if ((rel_vf_id >= p_hwfn->cdev->p_iov_info->total_vfs) ||
     (rel_vf_id < 0))
  return false;

 if ((!p_hwfn->pf_iov_info->vfs_array[rel_vf_id].b_init) &&
     b_enabled_only)
  return false;

 if ((p_hwfn->pf_iov_info->vfs_array[rel_vf_id].b_malicious) &&
     b_non_malicious)
  return false;

 return true;
}

static struct qed_vf_info *qed_iov_get_vf_info(struct qed_hwfn *p_hwfn,
            u16 relative_vf_id,
            bool b_enabled_only)
{
 struct qed_vf_info *vf = NULL;

 if (!p_hwfn->pf_iov_info) {
  DP_NOTICE(p_hwfn->cdev, "No iov info\n");
  return NULL;
 }

 if (qed_iov_is_valid_vfid(p_hwfn, relative_vf_id,
      b_enabled_only, false))
  vf = &p_hwfn->pf_iov_info->vfs_array[relative_vf_id];
 else
  DP_ERR(p_hwfn, "%s: VF[%d] is not enabled\n",
         __func__, relative_vf_id);

 return vf;
}

static struct qed_queue_cid *
qed_iov_get_vf_rx_queue_cid(struct qed_vf_queue *p_queue)
{
 int i;

 for (i = 0; i < MAX_QUEUES_PER_QZONE; i++) {
  if (p_queue->cids[i].p_cid && !p_queue->cids[i].b_is_tx)
   return p_queue->cids[i].p_cid;
 }

 return NULL;
}

enum qed_iov_validate_q_mode {
 QED_IOV_VALIDATE_Q_NA,
 QED_IOV_VALIDATE_Q_ENABLE,
 QED_IOV_VALIDATE_Q_DISABLE,
};

static bool qed_iov_validate_queue_mode(struct qed_hwfn *p_hwfn,
     struct qed_vf_info *p_vf,
     u16 qid,
     enum qed_iov_validate_q_mode mode,
     bool b_is_tx)
{
 int i;

 if (mode == QED_IOV_VALIDATE_Q_NA)
  return true;

 for (i = 0; i < MAX_QUEUES_PER_QZONE; i++) {
  struct qed_vf_queue_cid *p_qcid;

  p_qcid = &p_vf->vf_queues[qid].cids[i];

  if (!p_qcid->p_cid)
   continue;

  if (p_qcid->b_is_tx != b_is_tx)
   continue;

  return mode == QED_IOV_VALIDATE_Q_ENABLE;
 }

 /* In case we haven't found any valid cid, then its disabled */
 return mode == QED_IOV_VALIDATE_Q_DISABLE;
}

static bool qed_iov_validate_rxq(struct qed_hwfn *p_hwfn,
     struct qed_vf_info *p_vf,
     u16 rx_qid,
     enum qed_iov_validate_q_mode mode)
{
 if (rx_qid >= p_vf->num_rxqs) {
  DP_VERBOSE(p_hwfn,
      QED_MSG_IOV,
      "VF[0x%02x] - can't touch Rx queue[%04x]; Only 0x%04x are allocated\n",
      p_vf->abs_vf_id, rx_qid, p_vf->num_rxqs);
  return false;
 }

 return qed_iov_validate_queue_mode(p_hwfn, p_vf, rx_qid, mode, false);
}

static bool qed_iov_validate_txq(struct qed_hwfn *p_hwfn,
     struct qed_vf_info *p_vf,
     u16 tx_qid,
     enum qed_iov_validate_q_mode mode)
{
 if (tx_qid >= p_vf->num_txqs) {
  DP_VERBOSE(p_hwfn,
      QED_MSG_IOV,
      "VF[0x%02x] - can't touch Tx queue[%04x]; Only 0x%04x are allocated\n",
      p_vf->abs_vf_id, tx_qid, p_vf->num_txqs);
  return false;
 }

 return qed_iov_validate_queue_mode(p_hwfn, p_vf, tx_qid, mode, true);
}

static bool qed_iov_validate_sb(struct qed_hwfn *p_hwfn,
    struct qed_vf_info *p_vf, u16 sb_idx)
{
 int i;

 for (i = 0; i < p_vf->num_sbs; i++)
  if (p_vf->igu_sbs[i] == sb_idx)
   return true;

 DP_VERBOSE(p_hwfn,
     QED_MSG_IOV,
     "VF[0%02x] - tried using sb_idx %04x which doesn't exist as one of its 0x%02x SBs\n",
     p_vf->abs_vf_id, sb_idx, p_vf->num_sbs);

 return false;
}

static bool qed_iov_validate_active_rxq(struct qed_hwfn *p_hwfn,
     struct qed_vf_info *p_vf)
{
 u8 i;

 for (i = 0; i < p_vf->num_rxqs; i++)
  if (qed_iov_validate_queue_mode(p_hwfn, p_vf, i,
      QED_IOV_VALIDATE_Q_ENABLE,
      false))
   return true;

 return false;
}

static bool qed_iov_validate_active_txq(struct qed_hwfn *p_hwfn,
     struct qed_vf_info *p_vf)
{
 u8 i;

 for (i = 0; i < p_vf->num_txqs; i++)
  if (qed_iov_validate_queue_mode(p_hwfn, p_vf, i,
      QED_IOV_VALIDATE_Q_ENABLE,
      true))
   return true;

 return false;
}

static int qed_iov_post_vf_bulletin(struct qed_hwfn *p_hwfn,
        int vfid, struct qed_ptt *p_ptt)
{
 struct qed_bulletin_content *p_bulletin;
 int crc_size = sizeof(p_bulletin->crc);
 struct qed_dmae_params params;
 struct qed_vf_info *p_vf;

 p_vf = qed_iov_get_vf_info(p_hwfn, (u16)vfid, true);
 if (!p_vf)
  return -EINVAL;

 if (!p_vf->vf_bulletin)
  return -EINVAL;

 p_bulletin = p_vf->bulletin.p_virt;

 /* Increment bulletin board version and compute crc */
 p_bulletin->version++;
 p_bulletin->crc = crc32(0, (u8 *)p_bulletin + crc_size,
    p_vf->bulletin.size - crc_size);

 DP_VERBOSE(p_hwfn, QED_MSG_IOV,
     "Posting Bulletin 0x%08x to VF[%d] (CRC 0x%08x)\n",
     p_bulletin->version, p_vf->relative_vf_id, p_bulletin->crc);

 /* propagate bulletin board via dmae to vm memory */
 memset(¶ms, 0, sizeof(params));
 SET_FIELD(params.flags, QED_DMAE_PARAMS_DST_VF_VALID, 0x1);
 params.dst_vfid = p_vf->abs_vf_id;
 return qed_dmae_host2host(p_hwfn, p_ptt, p_vf->bulletin.phys,
      p_vf->vf_bulletin, p_vf->bulletin.size / 4,
      ¶ms);
}

static int qed_iov_pci_cfg_info(struct qed_dev *cdev)
{
 struct qed_hw_sriov_info *iov = cdev->p_iov_info;
 int pos = iov->pos;

 DP_VERBOSE(cdev, QED_MSG_IOV, "sriov ext pos %d\n", pos);
 pci_read_config_word(cdev->pdev, pos + PCI_SRIOV_CTRL, &iov->ctrl);

 pci_read_config_word(cdev->pdev,
        pos + PCI_SRIOV_TOTAL_VF, &iov->total_vfs);
 pci_read_config_word(cdev->pdev,
        pos + PCI_SRIOV_INITIAL_VF, &iov->initial_vfs);

 pci_read_config_word(cdev->pdev, pos + PCI_SRIOV_NUM_VF, &iov->num_vfs);
 if (iov->num_vfs) {
  DP_VERBOSE(cdev,
      QED_MSG_IOV,
      "Number of VFs are already set to non-zero value. Ignoring PCI configuration value\n");
  iov->num_vfs = 0;
 }

 pci_read_config_word(cdev->pdev,
        pos + PCI_SRIOV_VF_OFFSET, &iov->offset);

 pci_read_config_word(cdev->pdev,
        pos + PCI_SRIOV_VF_STRIDE, &iov->stride);

 pci_read_config_word(cdev->pdev,
        pos + PCI_SRIOV_VF_DID, &iov->vf_device_id);

 pci_read_config_dword(cdev->pdev,
         pos + PCI_SRIOV_SUP_PGSIZE, &iov->pgsz);

 pci_read_config_dword(cdev->pdev, pos + PCI_SRIOV_CAP, &iov->cap);

 pci_read_config_byte(cdev->pdev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);

 DP_VERBOSE(cdev,
     QED_MSG_IOV,
     "IOV info: nres %d, cap 0x%x, ctrl 0x%x, total %d, initial %d, num vfs %d, offset %d, stride %d, page size 0x%x\n",
     iov->nres,
     iov->cap,
     iov->ctrl,
     iov->total_vfs,
     iov->initial_vfs,
     iov->nr_virtfn, iov->offset, iov->stride, iov->pgsz);

 /* Some sanity checks */
 if (iov->num_vfs > NUM_OF_VFS(cdev) ||
     iov->total_vfs > NUM_OF_VFS(cdev)) {
  /* This can happen only due to a bug. In this case we set
 * num_vfs to zero to avoid memory corruption in the code that
 * assumes max number of vfs
 */

  DP_NOTICE(cdev,
     "IOV: Unexpected number of vfs set: %d setting num_vf to zero\n",
     iov->num_vfs);

  iov->num_vfs = 0;
  iov->total_vfs = 0;
 }

 return 0;
}

static void qed_iov_setup_vfdb(struct qed_hwfn *p_hwfn)
{
 struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info;
 struct qed_pf_iov *p_iov_info = p_hwfn->pf_iov_info;
 struct qed_bulletin_content *p_bulletin_virt;
 dma_addr_t req_p, rply_p, bulletin_p;
 union pfvf_tlvs *p_reply_virt_addr;
 union vfpf_tlvs *p_req_virt_addr;
 u8 idx = 0;

 memset(p_iov_info->vfs_array, 0, sizeof(p_iov_info->vfs_array));

 p_req_virt_addr = p_iov_info->mbx_msg_virt_addr;
 req_p = p_iov_info->mbx_msg_phys_addr;
 p_reply_virt_addr = p_iov_info->mbx_reply_virt_addr;
 rply_p = p_iov_info->mbx_reply_phys_addr;
 p_bulletin_virt = p_iov_info->p_bulletins;
 bulletin_p = p_iov_info->bulletins_phys;
 if (!p_req_virt_addr || !p_reply_virt_addr || !p_bulletin_virt) {
  DP_ERR(p_hwfn,
         "%s called without allocating mem first\n", __func__);
  return;
 }

 for (idx = 0; idx < p_iov->total_vfs; idx++) {
  struct qed_vf_info *vf = &p_iov_info->vfs_array[idx];
  u32 concrete;

  vf->vf_mbx.req_virt = p_req_virt_addr + idx;
  vf->vf_mbx.req_phys = req_p + idx * sizeof(union vfpf_tlvs);
  vf->vf_mbx.reply_virt = p_reply_virt_addr + idx;
  vf->vf_mbx.reply_phys = rply_p + idx * sizeof(union pfvf_tlvs);

  vf->state = VF_STOPPED;
  vf->b_init = false;

  vf->bulletin.phys = idx *
        sizeof(struct qed_bulletin_content) +
        bulletin_p;
  vf->bulletin.p_virt = p_bulletin_virt + idx;
  vf->bulletin.size = sizeof(struct qed_bulletin_content);

  vf->relative_vf_id = idx;
  vf->abs_vf_id = idx + p_iov->first_vf_in_pf;
  concrete = qed_vfid_to_concrete(p_hwfn, vf->abs_vf_id);
  vf->concrete_fid = concrete;
  vf->opaque_fid = (p_hwfn->hw_info.opaque_fid & 0xff) |
     (vf->abs_vf_id << 8);
  vf->vport_id = idx + 1;

  vf->num_mac_filters = QED_ETH_VF_NUM_MAC_FILTERS;
  vf->num_vlan_filters = QED_ETH_VF_NUM_VLAN_FILTERS;
 }
}

static int qed_iov_allocate_vfdb(struct qed_hwfn *p_hwfn)
{
 struct qed_pf_iov *p_iov_info = p_hwfn->pf_iov_info;
 void **p_v_addr;
 u16 num_vfs = 0;

 num_vfs = p_hwfn->cdev->p_iov_info->total_vfs;

 DP_VERBOSE(p_hwfn, QED_MSG_IOV,
     "%s for %d VFs\n", __func__, num_vfs);

 /* Allocate PF Mailbox buffer (per-VF) */
 p_iov_info->mbx_msg_size = sizeof(union vfpf_tlvs) * num_vfs;
 p_v_addr = &p_iov_info->mbx_msg_virt_addr;
 *p_v_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
           p_iov_info->mbx_msg_size,
           &p_iov_info->mbx_msg_phys_addr,
           GFP_KERNEL);
 if (!*p_v_addr)
  return -ENOMEM;

 /* Allocate PF Mailbox Reply buffer (per-VF) */
 p_iov_info->mbx_reply_size = sizeof(union pfvf_tlvs) * num_vfs;
 p_v_addr = &p_iov_info->mbx_reply_virt_addr;
 *p_v_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
           p_iov_info->mbx_reply_size,
           &p_iov_info->mbx_reply_phys_addr,
           GFP_KERNEL);
 if (!*p_v_addr)
  return -ENOMEM;

 p_iov_info->bulletins_size = sizeof(struct qed_bulletin_content) *
         num_vfs;
 p_v_addr = &p_iov_info->p_bulletins;
 *p_v_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
           p_iov_info->bulletins_size,
           &p_iov_info->bulletins_phys,
           GFP_KERNEL);
 if (!*p_v_addr)
  return -ENOMEM;

 DP_VERBOSE(p_hwfn,
     QED_MSG_IOV,
     "PF's Requests mailbox [%p virt 0x%llx phys], Response mailbox [%p virt 0x%llx phys] Bulletins [%p virt 0x%llx phys]\n",
     p_iov_info->mbx_msg_virt_addr,
     (u64)p_iov_info->mbx_msg_phys_addr,
     p_iov_info->mbx_reply_virt_addr,
     (u64)p_iov_info->mbx_reply_phys_addr,
     p_iov_info->p_bulletins, (u64)p_iov_info->bulletins_phys);

 return 0;
}

static void qed_iov_free_vfdb(struct qed_hwfn *p_hwfn)
{
 struct qed_pf_iov *p_iov_info = p_hwfn->pf_iov_info;

 if (p_hwfn->pf_iov_info->mbx_msg_virt_addr)
  dma_free_coherent(&p_hwfn->cdev->pdev->dev,
      p_iov_info->mbx_msg_size,
      p_iov_info->mbx_msg_virt_addr,
      p_iov_info->mbx_msg_phys_addr);

 if (p_hwfn->pf_iov_info->mbx_reply_virt_addr)
  dma_free_coherent(&p_hwfn->cdev->pdev->dev,
      p_iov_info->mbx_reply_size,
      p_iov_info->mbx_reply_virt_addr,
      p_iov_info->mbx_reply_phys_addr);

 if (p_iov_info->p_bulletins)
  dma_free_coherent(&p_hwfn->cdev->pdev->dev,
      p_iov_info->bulletins_size,
      p_iov_info->p_bulletins,
      p_iov_info->bulletins_phys);
}

int qed_iov_alloc(struct qed_hwfn *p_hwfn)
{
 struct qed_pf_iov *p_sriov;

 if (!IS_PF_SRIOV(p_hwfn)) {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "No SR-IOV - no need for IOV db\n");
  return 0;
 }

 p_sriov = kzalloc(sizeof(*p_sriov), GFP_KERNEL);
 if (!p_sriov)
  return -ENOMEM;

 p_hwfn->pf_iov_info = p_sriov;

 qed_spq_register_async_cb(p_hwfn, PROTOCOLID_COMMON,
      qed_sriov_eqe_event);

 return qed_iov_allocate_vfdb(p_hwfn);
}

void qed_iov_setup(struct qed_hwfn *p_hwfn)
{
 if (!IS_PF_SRIOV(p_hwfn) || !IS_PF_SRIOV_ALLOC(p_hwfn))
  return;

 qed_iov_setup_vfdb(p_hwfn);
}

void qed_iov_free(struct qed_hwfn *p_hwfn)
{
 qed_spq_unregister_async_cb(p_hwfn, PROTOCOLID_COMMON);

 if (IS_PF_SRIOV_ALLOC(p_hwfn)) {
  qed_iov_free_vfdb(p_hwfn);
  kfree(p_hwfn->pf_iov_info);
 }
}

void qed_iov_free_hw_info(struct qed_dev *cdev)
{
 kfree(cdev->p_iov_info);
 cdev->p_iov_info = NULL;
}

int qed_iov_hw_info(struct qed_hwfn *p_hwfn)
{
 struct qed_dev *cdev = p_hwfn->cdev;
 int pos;
 int rc;

 if (is_kdump_kernel())
  return 0;

 if (IS_VF(p_hwfn->cdev))
  return 0;

 /* Learn the PCI configuration */
 pos = pci_find_ext_capability(p_hwfn->cdev->pdev,
          PCI_EXT_CAP_ID_SRIOV);
 if (!pos) {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV, "No PCIe IOV support\n");
  return 0;
 }

 /* Allocate a new struct for IOV information */
 cdev->p_iov_info = kzalloc(sizeof(*cdev->p_iov_info), GFP_KERNEL);
 if (!cdev->p_iov_info)
  return -ENOMEM;

 cdev->p_iov_info->pos = pos;

 rc = qed_iov_pci_cfg_info(cdev);
 if (rc)
  return rc;

 /* We want PF IOV to be synonemous with the existence of p_iov_info;
 * In case the capability is published but there are no VFs, simply
 * de-allocate the struct.
 */

 if (!cdev->p_iov_info->total_vfs) {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "IOV capabilities, but no VFs are published\n");
  kfree(cdev->p_iov_info);
  cdev->p_iov_info = NULL;
  return 0;
 }

 /* First VF index based on offset is tricky:
 *  - If ARI is supported [likely], offset - (16 - pf_id) would
 *    provide the number for eng0. 2nd engine Vfs would begin
 *    after the first engine's VFs.
 *  - If !ARI, VFs would start on next device.
 *    so offset - (256 - pf_id) would provide the number.
 * Utilize the fact that (256 - pf_id) is achieved only by later
 * to differentiate between the two.
 */


 if (p_hwfn->cdev->p_iov_info->offset < (256 - p_hwfn->abs_pf_id)) {
  u32 first = p_hwfn->cdev->p_iov_info->offset +
       p_hwfn->abs_pf_id - 16;

  cdev->p_iov_info->first_vf_in_pf = first;

  if (QED_PATH_ID(p_hwfn))
   cdev->p_iov_info->first_vf_in_pf -= MAX_NUM_VFS_BB;
 } else {
  u32 first = p_hwfn->cdev->p_iov_info->offset +
       p_hwfn->abs_pf_id - 256;

  cdev->p_iov_info->first_vf_in_pf = first;
 }

 DP_VERBOSE(p_hwfn, QED_MSG_IOV,
     "First VF in hwfn 0x%08x\n",
     cdev->p_iov_info->first_vf_in_pf);

 return 0;
}

static bool _qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn,
         int vfid, bool b_fail_malicious)
{
 /* Check PF supports sriov */
 if (IS_VF(p_hwfn->cdev) || !IS_QED_SRIOV(p_hwfn->cdev) ||
     !IS_PF_SRIOV_ALLOC(p_hwfn))
  return false;

 /* Check VF validity */
 if (!qed_iov_is_valid_vfid(p_hwfn, vfid, true, b_fail_malicious))
  return false;

 return true;
}

static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid)
{
 return _qed_iov_pf_sanity_check(p_hwfn, vfid, true);
}

static void qed_iov_set_vf_to_disable(struct qed_dev *cdev,
          u16 rel_vf_id, u8 to_disable)
{
 struct qed_vf_info *vf;
 int i;

 for_each_hwfn(cdev, i) {
  struct qed_hwfn *p_hwfn = &cdev->hwfns[i];

  vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, false);
  if (!vf)
   continue;

  vf->to_disable = to_disable;
 }
}

static void qed_iov_set_vfs_to_disable(struct qed_dev *cdev, u8 to_disable)
{
 u16 i;

 if (!IS_QED_SRIOV(cdev))
  return;

 for (i = 0; i < cdev->p_iov_info->total_vfs; i++)
  qed_iov_set_vf_to_disable(cdev, i, to_disable);
}

static void qed_iov_vf_pglue_clear_err(struct qed_hwfn *p_hwfn,
           struct qed_ptt *p_ptt, u8 abs_vfid)
{
 qed_wr(p_hwfn, p_ptt,
        PGLUE_B_REG_WAS_ERROR_VF_31_0_CLR + (abs_vfid >> 5) * 4,
        1 << (abs_vfid & 0x1f));
}

static void qed_iov_vf_igu_reset(struct qed_hwfn *p_hwfn,
     struct qed_ptt *p_ptt, struct qed_vf_info *vf)
{
 int i;

 /* Set VF masks and configuration - pretend */
 qed_fid_pretend(p_hwfn, p_ptt, (u16)vf->concrete_fid);

 qed_wr(p_hwfn, p_ptt, IGU_REG_STATISTIC_NUM_VF_MSG_SENT, 0);

 /* unpretend */
 qed_fid_pretend(p_hwfn, p_ptt, (u16)p_hwfn->hw_info.concrete_fid);

 /* iterate over all queues, clear sb consumer */
 for (i = 0; i < vf->num_sbs; i++)
  qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt,
      vf->igu_sbs[i],
      vf->opaque_fid, true);
}

static void qed_iov_vf_igu_set_int(struct qed_hwfn *p_hwfn,
       struct qed_ptt *p_ptt,
       struct qed_vf_info *vf, bool enable)
{
 u32 igu_vf_conf;

 qed_fid_pretend(p_hwfn, p_ptt, (u16)vf->concrete_fid);

 igu_vf_conf = qed_rd(p_hwfn, p_ptt, IGU_REG_VF_CONFIGURATION);

 if (enable)
  igu_vf_conf |= IGU_VF_CONF_MSI_MSIX_EN;
 else
  igu_vf_conf &= ~IGU_VF_CONF_MSI_MSIX_EN;

 qed_wr(p_hwfn, p_ptt, IGU_REG_VF_CONFIGURATION, igu_vf_conf);

 /* unpretend */
 qed_fid_pretend(p_hwfn, p_ptt, (u16)p_hwfn->hw_info.concrete_fid);
}

static int
qed_iov_enable_vf_access_msix(struct qed_hwfn *p_hwfn,
         struct qed_ptt *p_ptt, u8 abs_vf_id, u8 num_sbs)
{
 u8 current_max = 0;
 int i;

 /* For AH onward, configuration is per-PF. Find maximum of all
 * the currently enabled child VFs, and set the number to be that.
 */

 if (!QED_IS_BB(p_hwfn->cdev)) {
  qed_for_each_vf(p_hwfn, i) {
   struct qed_vf_info *p_vf;

   p_vf = qed_iov_get_vf_info(p_hwfn, (u16)i, true);
   if (!p_vf)
    continue;

   current_max = max_t(u8, current_max, p_vf->num_sbs);
  }
 }

 if (num_sbs > current_max)
  return qed_mcp_config_vf_msix(p_hwfn, p_ptt,
           abs_vf_id, num_sbs);

 return 0;
}

static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn,
        struct qed_ptt *p_ptt,
        struct qed_vf_info *vf)
{
 u32 igu_vf_conf = IGU_VF_CONF_FUNC_EN;
 int rc;

 /* It's possible VF was previously considered malicious -
 * clear the indication even if we're only going to disable VF.
 */

 vf->b_malicious = false;

 if (vf->to_disable)
  return 0;

 DP_VERBOSE(p_hwfn,
     QED_MSG_IOV,
     "Enable internal access for vf %x [abs %x]\n",
     vf->abs_vf_id, QED_VF_ABS_ID(p_hwfn, vf));

 qed_iov_vf_pglue_clear_err(p_hwfn, p_ptt, QED_VF_ABS_ID(p_hwfn, vf));

 qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf);

 rc = qed_iov_enable_vf_access_msix(p_hwfn, p_ptt,
        vf->abs_vf_id, vf->num_sbs);
 if (rc)
  return rc;

 qed_fid_pretend(p_hwfn, p_ptt, (u16)vf->concrete_fid);

 SET_FIELD(igu_vf_conf, IGU_VF_CONF_PARENT, p_hwfn->rel_pf_id);
 STORE_RT_REG(p_hwfn, IGU_REG_VF_CONFIGURATION_RT_OFFSET, igu_vf_conf);

 qed_init_run(p_hwfn, p_ptt, PHASE_VF, vf->abs_vf_id,
       p_hwfn->hw_info.hw_mode);

 /* unpretend */
 qed_fid_pretend(p_hwfn, p_ptt, (u16)p_hwfn->hw_info.concrete_fid);

 vf->state = VF_FREE;

 return rc;
}

/**
 * qed_iov_config_perm_table() - Configure the permission zone table.
 *
 * @p_hwfn: HW device data.
 * @p_ptt: PTT window for writing the registers.
 * @vf: VF info data.
 * @enable: The actual permission for this VF.
 *
 * In E4, queue zone permission table size is 320x9. There
 * are 320 VF queues for single engine device (256 for dual
 * engine device), and each entry has the following format:
 * {Valid, VF[7:0]}
 */

static void qed_iov_config_perm_table(struct qed_hwfn *p_hwfn,
          struct qed_ptt *p_ptt,
          struct qed_vf_info *vf, u8 enable)
{
 u32 reg_addr, val;
 u16 qzone_id = 0;
 int qid;

 for (qid = 0; qid < vf->num_rxqs; qid++) {
  qed_fw_l2_queue(p_hwfn, vf->vf_queues[qid].fw_rx_qid,
    &qzone_id);

  reg_addr = PSWHST_REG_ZONE_PERMISSION_TABLE + qzone_id * 4;
  val = enable ? (vf->abs_vf_id | BIT(8)) : 0;
  qed_wr(p_hwfn, p_ptt, reg_addr, val);
 }
}

static void qed_iov_enable_vf_traffic(struct qed_hwfn *p_hwfn,
          struct qed_ptt *p_ptt,
          struct qed_vf_info *vf)
{
 /* Reset vf in IGU - interrupts are still disabled */
 qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf);

 qed_iov_vf_igu_set_int(p_hwfn, p_ptt, vf, 1);

 /* Permission Table */
 qed_iov_config_perm_table(p_hwfn, p_ptt, vf, true);
}

static u8 qed_iov_alloc_vf_igu_sbs(struct qed_hwfn *p_hwfn,
       struct qed_ptt *p_ptt,
       struct qed_vf_info *vf, u16 num_rx_queues)
{
 struct qed_igu_block *p_block;
 struct cau_sb_entry sb_entry;
 int qid = 0;
 u32 val = 0;

 if (num_rx_queues > p_hwfn->hw_info.p_igu_info->usage.free_cnt_iov)
  num_rx_queues = p_hwfn->hw_info.p_igu_info->usage.free_cnt_iov;
 p_hwfn->hw_info.p_igu_info->usage.free_cnt_iov -= num_rx_queues;

 SET_FIELD(val, IGU_MAPPING_LINE_FUNCTION_NUMBER, vf->abs_vf_id);
 SET_FIELD(val, IGU_MAPPING_LINE_VALID, 1);
 SET_FIELD(val, IGU_MAPPING_LINE_PF_VALID, 0);

 for (qid = 0; qid < num_rx_queues; qid++) {
  p_block = qed_get_igu_free_sb(p_hwfn, false);
  vf->igu_sbs[qid] = p_block->igu_sb_id;
  p_block->status &= ~QED_IGU_STATUS_FREE;
  SET_FIELD(val, IGU_MAPPING_LINE_VECTOR_NUMBER, qid);

  qed_wr(p_hwfn, p_ptt,
         IGU_REG_MAPPING_MEMORY +
         sizeof(u32) * p_block->igu_sb_id, val);

  /* Configure igu sb in CAU which were marked valid */
  qed_init_cau_sb_entry(p_hwfn, &sb_entry,
          p_hwfn->rel_pf_id, vf->abs_vf_id, 1);

  qed_dmae_host2grc(p_hwfn, p_ptt,
      (u64)(uintptr_t)&sb_entry,
      CAU_REG_SB_VAR_MEMORY +
      p_block->igu_sb_id * sizeof(u64), 2, NULL);
 }

 vf->num_sbs = (u8)num_rx_queues;

 return vf->num_sbs;
}

static void qed_iov_free_vf_igu_sbs(struct qed_hwfn *p_hwfn,
        struct qed_ptt *p_ptt,
        struct qed_vf_info *vf)
{
 struct qed_igu_info *p_info = p_hwfn->hw_info.p_igu_info;
 int idx, igu_id;
 u32 addr, val;

 /* Invalidate igu CAM lines and mark them as free */
 for (idx = 0; idx < vf->num_sbs; idx++) {
  igu_id = vf->igu_sbs[idx];
  addr = IGU_REG_MAPPING_MEMORY + sizeof(u32) * igu_id;

  val = qed_rd(p_hwfn, p_ptt, addr);
  SET_FIELD(val, IGU_MAPPING_LINE_VALID, 0);
  qed_wr(p_hwfn, p_ptt, addr, val);

  p_info->entry[igu_id].status |= QED_IGU_STATUS_FREE;
  p_hwfn->hw_info.p_igu_info->usage.free_cnt_iov++;
 }

 vf->num_sbs = 0;
}

static void qed_iov_set_link(struct qed_hwfn *p_hwfn,
        u16 vfid,
        struct qed_mcp_link_params *params,
        struct qed_mcp_link_state *link,
        struct qed_mcp_link_capabilities *p_caps)
{
 struct qed_vf_info *p_vf = qed_iov_get_vf_info(p_hwfn,
             vfid,
             false);
 struct qed_bulletin_content *p_bulletin;

 if (!p_vf)
  return;

 p_bulletin = p_vf->bulletin.p_virt;
 p_bulletin->req_autoneg = params->speed.autoneg;
 p_bulletin->req_adv_speed = params->speed.advertised_speeds;
 p_bulletin->req_forced_speed = params->speed.forced_speed;
 p_bulletin->req_autoneg_pause = params->pause.autoneg;
 p_bulletin->req_forced_rx = params->pause.forced_rx;
 p_bulletin->req_forced_tx = params->pause.forced_tx;
 p_bulletin->req_loopback = params->loopback_mode;

 p_bulletin->link_up = link->link_up;
 p_bulletin->speed = link->speed;
 p_bulletin->full_duplex = link->full_duplex;
 p_bulletin->autoneg = link->an;
 p_bulletin->autoneg_complete = link->an_complete;
 p_bulletin->parallel_detection = link->parallel_detection;
 p_bulletin->pfc_enabled = link->pfc_enabled;
 p_bulletin->partner_adv_speed = link->partner_adv_speed;
 p_bulletin->partner_tx_flow_ctrl_en = link->partner_tx_flow_ctrl_en;
 p_bulletin->partner_rx_flow_ctrl_en = link->partner_rx_flow_ctrl_en;
 p_bulletin->partner_adv_pause = link->partner_adv_pause;
 p_bulletin->sfp_tx_fault = link->sfp_tx_fault;

 p_bulletin->capability_speed = p_caps->speed_capabilities;
}

static int qed_iov_init_hw_for_vf(struct qed_hwfn *p_hwfn,
      struct qed_ptt *p_ptt,
      struct qed_iov_vf_init_params *p_params)
{
 struct qed_mcp_link_capabilities link_caps;
 struct qed_mcp_link_params link_params;
 struct qed_mcp_link_state link_state;
 u8 num_of_vf_avaiable_chains = 0;
 struct qed_vf_info *vf = NULL;
 u16 qid, num_irqs;
 int rc = 0;
 u32 cids;
 u8 i;

 vf = qed_iov_get_vf_info(p_hwfn, p_params->rel_vf_id, false);
 if (!vf) {
  DP_ERR(p_hwfn, "%s : vf is NULL\n", __func__);
  return -EINVAL;
 }

 if (vf->b_init) {
  DP_NOTICE(p_hwfn, "VF[%d] is already active.\n",
     p_params->rel_vf_id);
  return -EINVAL;
 }

 /* Perform sanity checking on the requested queue_id */
 for (i = 0; i < p_params->num_queues; i++) {
  u16 min_vf_qzone = FEAT_NUM(p_hwfn, QED_PF_L2_QUE);
  u16 max_vf_qzone = min_vf_qzone +
      FEAT_NUM(p_hwfn, QED_VF_L2_QUE) - 1;

  qid = p_params->req_rx_queue[i];
  if (qid < min_vf_qzone || qid > max_vf_qzone) {
   DP_NOTICE(p_hwfn,
      "Can't enable Rx qid [%04x] for VF[%d]: qids [0x%04x,...,0x%04x] available\n",
      qid,
      p_params->rel_vf_id,
      min_vf_qzone, max_vf_qzone);
   return -EINVAL;
  }

  qid = p_params->req_tx_queue[i];
  if (qid > max_vf_qzone) {
   DP_NOTICE(p_hwfn,
      "Can't enable Tx qid [%04x] for VF[%d]: max qid 0x%04x\n",
      qid, p_params->rel_vf_id, max_vf_qzone);
   return -EINVAL;
  }

  /* If client *really* wants, Tx qid can be shared with PF */
  if (qid < min_vf_qzone)
   DP_VERBOSE(p_hwfn,
       QED_MSG_IOV,
       "VF[%d] is using PF qid [0x%04x] for Txq[0x%02x]\n",
       p_params->rel_vf_id, qid, i);
 }

 /* Limit number of queues according to number of CIDs */
 qed_cxt_get_proto_cid_count(p_hwfn, PROTOCOLID_ETH, &cids);
 DP_VERBOSE(p_hwfn,
     QED_MSG_IOV,
     "VF[%d] - requesting to initialize for 0x%04x queues [0x%04x CIDs available]\n",
     vf->relative_vf_id, p_params->num_queues, (u16)cids);
 num_irqs = min_t(u16, p_params->num_queues, ((u16)cids));

 num_of_vf_avaiable_chains = qed_iov_alloc_vf_igu_sbs(p_hwfn,
            p_ptt,
            vf, num_irqs);
 if (!num_of_vf_avaiable_chains) {
  DP_ERR(p_hwfn, "no available igu sbs\n");
  return -ENOMEM;
 }

 /* Choose queue number and index ranges */
 vf->num_rxqs = num_of_vf_avaiable_chains;
 vf->num_txqs = num_of_vf_avaiable_chains;

 for (i = 0; i < vf->num_rxqs; i++) {
  struct qed_vf_queue *p_queue = &vf->vf_queues[i];

  p_queue->fw_rx_qid = p_params->req_rx_queue[i];
  p_queue->fw_tx_qid = p_params->req_tx_queue[i];

  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "VF[%d] - Q[%d] SB %04x, qid [Rx %04x Tx %04x]\n",
      vf->relative_vf_id, i, vf->igu_sbs[i],
      p_queue->fw_rx_qid, p_queue->fw_tx_qid);
 }

 /* Update the link configuration in bulletin */
 memcpy(&link_params, qed_mcp_get_link_params(p_hwfn),
        sizeof(link_params));
 memcpy(&link_state, qed_mcp_get_link_state(p_hwfn), sizeof(link_state));
 memcpy(&link_caps, qed_mcp_get_link_capabilities(p_hwfn),
        sizeof(link_caps));
 qed_iov_set_link(p_hwfn, p_params->rel_vf_id,
    &link_params, &link_state, &link_caps);

 rc = qed_iov_enable_vf_access(p_hwfn, p_ptt, vf);
 if (!rc) {
  vf->b_init = true;

  if (IS_LEAD_HWFN(p_hwfn))
   p_hwfn->cdev->p_iov_info->num_vfs++;
 }

 return rc;
}

static int qed_iov_release_hw_for_vf(struct qed_hwfn *p_hwfn,
         struct qed_ptt *p_ptt, u16 rel_vf_id)
{
 struct qed_mcp_link_capabilities caps;
 struct qed_mcp_link_params params;
 struct qed_mcp_link_state link;
 struct qed_vf_info *vf = NULL;

 vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, true);
 if (!vf) {
  DP_ERR(p_hwfn, "%s : vf is NULL\n", __func__);
  return -EINVAL;
 }

 if (vf->bulletin.p_virt)
  memset(vf->bulletin.p_virt, 0, sizeof(*vf->bulletin.p_virt));

 memset(&vf->p_vf_info, 0, sizeof(vf->p_vf_info));

 /* Get the link configuration back in bulletin so
 * that when VFs are re-enabled they get the actual
 * link configuration.
 */

 memcpy(¶ms, qed_mcp_get_link_params(p_hwfn), sizeof(params));
 memcpy(&link, qed_mcp_get_link_state(p_hwfn), sizeof(link));
 memcpy(&caps, qed_mcp_get_link_capabilities(p_hwfn), sizeof(caps));
 qed_iov_set_link(p_hwfn, rel_vf_id, ¶ms, &link, &caps);

 /* Forget the VF's acquisition message */
 memset(&vf->acquire, 0, sizeof(vf->acquire));

 /* disablng interrupts and resetting permission table was done during
 * vf-close, however, we could get here without going through vf_close
 */

 /* Disable Interrupts for VF */
 qed_iov_vf_igu_set_int(p_hwfn, p_ptt, vf, 0);

 /* Reset Permission table */
 qed_iov_config_perm_table(p_hwfn, p_ptt, vf, 0);

 vf->num_rxqs = 0;
 vf->num_txqs = 0;
 qed_iov_free_vf_igu_sbs(p_hwfn, p_ptt, vf);

 if (vf->b_init) {
  vf->b_init = false;

  if (IS_LEAD_HWFN(p_hwfn))
   p_hwfn->cdev->p_iov_info->num_vfs--;
 }

 return 0;
}

static bool qed_iov_tlv_supported(u16 tlvtype)
{
 return CHANNEL_TLV_NONE < tlvtype && tlvtype < CHANNEL_TLV_MAX;
}

/* place a given tlv on the tlv buffer, continuing current tlv list */
void *qed_add_tlv(struct qed_hwfn *p_hwfn, u8 **offset, u16 type, u16 length)
{
 struct channel_tlv *tl = (struct channel_tlv *)*offset;

 tl->type = type;
 tl->length = length;

 /* Offset should keep pointing to next TLV (the end of the last) */
 *offset += length;

 /* Return a pointer to the start of the added tlv */
 return *offset - length;
}

/* list the types and lengths of the tlvs on the buffer */
void qed_dp_tlv_list(struct qed_hwfn *p_hwfn, void *tlvs_list)
{
 u16 i = 1, total_length = 0;
 struct channel_tlv *tlv;

 do {
  tlv = (struct channel_tlv *)((u8 *)tlvs_list + total_length);

  /* output tlv */
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "TLV number %d: type %d, length %d\n",
      i, tlv->type, tlv->length);

  if (tlv->type == CHANNEL_TLV_LIST_END)
   return;

  /* Validate entry - protect against malicious VFs */
  if (!tlv->length) {
   DP_NOTICE(p_hwfn, "TLV of length 0 found\n");
   return;
  }

  total_length += tlv->length;

  if (total_length >= sizeof(struct tlv_buffer_size)) {
   DP_NOTICE(p_hwfn, "TLV ==> Buffer overflow\n");
   return;
  }

  i++;
 } while (1);
}

static void qed_iov_send_response(struct qed_hwfn *p_hwfn,
      struct qed_ptt *p_ptt,
      struct qed_vf_info *p_vf,
      u16 length, u8 status)
{
 struct qed_iov_vf_mbx *mbx = &p_vf->vf_mbx;
 struct qed_dmae_params params;
 u8 eng_vf_id;

 mbx->reply_virt->default_resp.hdr.status = status;

 qed_dp_tlv_list(p_hwfn, mbx->reply_virt);

 eng_vf_id = p_vf->abs_vf_id;

 memset(¶ms, 0, sizeof(params));
 SET_FIELD(params.flags, QED_DMAE_PARAMS_DST_VF_VALID, 0x1);
 params.dst_vfid = eng_vf_id;

 qed_dmae_host2host(p_hwfn, p_ptt, mbx->reply_phys + sizeof(u64),
      mbx->req_virt->first_tlv.reply_address +
      sizeof(u64),
      (sizeof(union pfvf_tlvs) - sizeof(u64)) / 4,
      ¶ms);

 /* Once PF copies the rc to the VF, the latter can continue
 * and send an additional message. So we have to make sure the
 * channel would be re-set to ready prior to that.
 */

 REG_WR(p_hwfn,
        GET_GTT_REG_ADDR(GTT_BAR0_MAP_REG_USDM_RAM,
    USTORM_VF_PF_CHANNEL_READY, eng_vf_id), 1);

 qed_dmae_host2host(p_hwfn, p_ptt, mbx->reply_phys,
      mbx->req_virt->first_tlv.reply_address,
      sizeof(u64) / 4, ¶ms);
}

static u16 qed_iov_vport_to_tlv(struct qed_hwfn *p_hwfn,
    enum qed_iov_vport_update_flag flag)
{
 switch (flag) {
 case QED_IOV_VP_UPDATE_ACTIVATE:
  return CHANNEL_TLV_VPORT_UPDATE_ACTIVATE;
 case QED_IOV_VP_UPDATE_VLAN_STRIP:
  return CHANNEL_TLV_VPORT_UPDATE_VLAN_STRIP;
 case QED_IOV_VP_UPDATE_TX_SWITCH:
  return CHANNEL_TLV_VPORT_UPDATE_TX_SWITCH;
 case QED_IOV_VP_UPDATE_MCAST:
  return CHANNEL_TLV_VPORT_UPDATE_MCAST;
 case QED_IOV_VP_UPDATE_ACCEPT_PARAM:
  return CHANNEL_TLV_VPORT_UPDATE_ACCEPT_PARAM;
 case QED_IOV_VP_UPDATE_RSS:
  return CHANNEL_TLV_VPORT_UPDATE_RSS;
 case QED_IOV_VP_UPDATE_ACCEPT_ANY_VLAN:
  return CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN;
 case QED_IOV_VP_UPDATE_SGE_TPA:
  return CHANNEL_TLV_VPORT_UPDATE_SGE_TPA;
 default:
  return 0;
 }
}

static u16 qed_iov_prep_vp_update_resp_tlvs(struct qed_hwfn *p_hwfn,
         struct qed_vf_info *p_vf,
         struct qed_iov_vf_mbx *p_mbx,
         u8 status,
         u16 tlvs_mask, u16 tlvs_accepted)
{
 struct pfvf_def_resp_tlv *resp;
 u16 size, total_len, i;

 memset(p_mbx->reply_virt, 0, sizeof(union pfvf_tlvs));
 p_mbx->offset = (u8 *)p_mbx->reply_virt;
 size = sizeof(struct pfvf_def_resp_tlv);
 total_len = size;

 qed_add_tlv(p_hwfn, &p_mbx->offset, CHANNEL_TLV_VPORT_UPDATE, size);

 /* Prepare response for all extended tlvs if they are found by PF */
 for (i = 0; i < QED_IOV_VP_UPDATE_MAX; i++) {
  if (!(tlvs_mask & BIT(i)))
   continue;

  resp = qed_add_tlv(p_hwfn, &p_mbx->offset,
       qed_iov_vport_to_tlv(p_hwfn, i), size);

  if (tlvs_accepted & BIT(i))
   resp->hdr.status = status;
  else
   resp->hdr.status = PFVF_STATUS_NOT_SUPPORTED;

  DP_VERBOSE(p_hwfn,
      QED_MSG_IOV,
      "VF[%d] - vport_update response: TLV %d, status %02x\n",
      p_vf->relative_vf_id,
      qed_iov_vport_to_tlv(p_hwfn, i), resp->hdr.status);

  total_len += size;
 }

 qed_add_tlv(p_hwfn, &p_mbx->offset, CHANNEL_TLV_LIST_END,
      sizeof(struct channel_list_end_tlv));

 return total_len;
}

static void qed_iov_prepare_resp(struct qed_hwfn *p_hwfn,
     struct qed_ptt *p_ptt,
     struct qed_vf_info *vf_info,
     u16 type, u16 length, u8 status)
{
 struct qed_iov_vf_mbx *mbx = &vf_info->vf_mbx;

 mbx->offset = (u8 *)mbx->reply_virt;

 qed_add_tlv(p_hwfn, &mbx->offset, type, length);
 qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END,
      sizeof(struct channel_list_end_tlv));

 qed_iov_send_response(p_hwfn, p_ptt, vf_info, length, status);
}

static struct
qed_public_vf_info *qed_iov_get_public_vf_info(struct qed_hwfn *p_hwfn,
            u16 relative_vf_id,
            bool b_enabled_only)
{
 struct qed_vf_info *vf = NULL;

 vf = qed_iov_get_vf_info(p_hwfn, relative_vf_id, b_enabled_only);
 if (!vf)
  return NULL;

 return &vf->p_vf_info;
}

static void qed_iov_clean_vf(struct qed_hwfn *p_hwfn, u8 vfid)
{
 struct qed_public_vf_info *vf_info;

 vf_info = qed_iov_get_public_vf_info(p_hwfn, vfid, false);

 if (!vf_info)
  return;

 /* Clear the VF mac */
 eth_zero_addr(vf_info->mac);

 vf_info->rx_accept_mode = 0;
 vf_info->tx_accept_mode = 0;
}

static void qed_iov_vf_cleanup(struct qed_hwfn *p_hwfn,
          struct qed_vf_info *p_vf)
{
 u32 i, j;

 p_vf->vf_bulletin = 0;
 p_vf->vport_instance = 0;
 p_vf->configured_features = 0;

 /* If VF previously requested less resources, go back to default */
 p_vf->num_rxqs = p_vf->num_sbs;
 p_vf->num_txqs = p_vf->num_sbs;

 p_vf->num_active_rxqs = 0;

 for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) {
  struct qed_vf_queue *p_queue = &p_vf->vf_queues[i];

  for (j = 0; j < MAX_QUEUES_PER_QZONE; j++) {
   if (!p_queue->cids[j].p_cid)
    continue;

   qed_eth_queue_cid_release(p_hwfn,
        p_queue->cids[j].p_cid);
   p_queue->cids[j].p_cid = NULL;
  }
 }

 memset(&p_vf->shadow_config, 0, sizeof(p_vf->shadow_config));
 memset(&p_vf->acquire, 0, sizeof(p_vf->acquire));
 qed_iov_clean_vf(p_hwfn, p_vf->relative_vf_id);
}

/* Returns either 0, or log(size) */
static u32 qed_iov_vf_db_bar_size(struct qed_hwfn *p_hwfn,
      struct qed_ptt *p_ptt)
{
 u32 val = qed_rd(p_hwfn, p_ptt, PGLUE_B_REG_VF_BAR1_SIZE);

 if (val)
  return val + 11;
 return 0;
}

static void
qed_iov_vf_mbx_acquire_resc_cids(struct qed_hwfn *p_hwfn,
     struct qed_ptt *p_ptt,
     struct qed_vf_info *p_vf,
     struct vf_pf_resc_request *p_req,
     struct pf_vf_resc *p_resp)
{
 u8 num_vf_cons = p_hwfn->pf_params.eth_pf_params.num_vf_cons;
 u8 db_size = qed_db_addr_vf(1, DQ_DEMS_LEGACY) -
       qed_db_addr_vf(0, DQ_DEMS_LEGACY);
 u32 bar_size;

 p_resp->num_cids = min_t(u8, p_req->num_cids, num_vf_cons);

 /* If VF didn't bother asking for QIDs than don't bother limiting
 * number of CIDs. The VF doesn't care about the number, and this
 * has the likely result of causing an additional acquisition.
 */

 if (!(p_vf->acquire.vfdev_info.capabilities &
       VFPF_ACQUIRE_CAP_QUEUE_QIDS))
  return;

 /* If doorbell bar was mapped by VF, limit the VF CIDs to an amount
 * that would make sure doorbells for all CIDs fall within the bar.
 * If it doesn't, make sure regview window is sufficient.
 */

 if (p_vf->acquire.vfdev_info.capabilities &
     VFPF_ACQUIRE_CAP_PHYSICAL_BAR) {
  bar_size = qed_iov_vf_db_bar_size(p_hwfn, p_ptt);
  if (bar_size)
   bar_size = 1 << bar_size;

  if (p_hwfn->cdev->num_hwfns > 1)
   bar_size /= 2;
 } else {
  bar_size = PXP_VF_BAR0_DQ_LENGTH;
 }

 if (bar_size / db_size < 256)
  p_resp->num_cids = min_t(u8, p_resp->num_cids,
      (u8)(bar_size / db_size));
}

static u8 qed_iov_vf_mbx_acquire_resc(struct qed_hwfn *p_hwfn,
          struct qed_ptt *p_ptt,
          struct qed_vf_info *p_vf,
          struct vf_pf_resc_request *p_req,
          struct pf_vf_resc *p_resp)
{
 u8 i;

 /* Queue related information */
 p_resp->num_rxqs = p_vf->num_rxqs;
 p_resp->num_txqs = p_vf->num_txqs;
 p_resp->num_sbs = p_vf->num_sbs;

 for (i = 0; i < p_resp->num_sbs; i++) {
  p_resp->hw_sbs[i].hw_sb_id = p_vf->igu_sbs[i];
  p_resp->hw_sbs[i].sb_qid = 0;
 }

 /* These fields are filled for backward compatibility.
 * Unused by modern vfs.
 */

 for (i = 0; i < p_resp->num_rxqs; i++) {
  qed_fw_l2_queue(p_hwfn, p_vf->vf_queues[i].fw_rx_qid,
    (u16 *)&p_resp->hw_qid[i]);
  p_resp->cid[i] = i;
 }

 /* Filter related information */
 p_resp->num_mac_filters = min_t(u8, p_vf->num_mac_filters,
     p_req->num_mac_filters);
 p_resp->num_vlan_filters = min_t(u8, p_vf->num_vlan_filters,
      p_req->num_vlan_filters);

 qed_iov_vf_mbx_acquire_resc_cids(p_hwfn, p_ptt, p_vf, p_req, p_resp);

 /* This isn't really needed/enforced, but some legacy VFs might depend
 * on the correct filling of this field.
 */

 p_resp->num_mc_filters = QED_MAX_MC_ADDRS;

 /* Validate sufficient resources for VF */
 if (p_resp->num_rxqs < p_req->num_rxqs ||
     p_resp->num_txqs < p_req->num_txqs ||
     p_resp->num_sbs < p_req->num_sbs ||
     p_resp->num_mac_filters < p_req->num_mac_filters ||
     p_resp->num_vlan_filters < p_req->num_vlan_filters ||
     p_resp->num_mc_filters < p_req->num_mc_filters ||
     p_resp->num_cids < p_req->num_cids) {
  DP_VERBOSE(p_hwfn,
      QED_MSG_IOV,
      "VF[%d] - Insufficient resources: rxq [%02x/%02x] txq [%02x/%02x] sbs [%02x/%02x] mac [%02x/%02x] vlan [%02x/%02x] mc [%02x/%02x] cids [%02x/%02x]\n",
      p_vf->abs_vf_id,
      p_req->num_rxqs,
      p_resp->num_rxqs,
      p_req->num_rxqs,
      p_resp->num_txqs,
      p_req->num_sbs,
      p_resp->num_sbs,
      p_req->num_mac_filters,
      p_resp->num_mac_filters,
      p_req->num_vlan_filters,
      p_resp->num_vlan_filters,
      p_req->num_mc_filters,
      p_resp->num_mc_filters,
      p_req->num_cids, p_resp->num_cids);

  /* Some legacy OSes are incapable of correctly handling this
 * failure.
 */

  if ((p_vf->acquire.vfdev_info.eth_fp_hsi_minor ==
       ETH_HSI_VER_NO_PKT_LEN_TUNN) &&
      (p_vf->acquire.vfdev_info.os_type ==
       VFPF_ACQUIRE_OS_WINDOWS))
   return PFVF_STATUS_SUCCESS;

  return PFVF_STATUS_NO_RESOURCE;
 }

 return PFVF_STATUS_SUCCESS;
}

static void qed_iov_vf_mbx_acquire_stats(struct qed_hwfn *p_hwfn,
      struct pfvf_stats_info *p_stats)
{
 p_stats->mstats.address = PXP_VF_BAR0_START_MSDM_ZONE_B +
      offsetof(struct mstorm_vf_zone,
        non_trigger.eth_queue_stat);
 p_stats->mstats.len = sizeof(struct eth_mstorm_per_queue_stat);
 p_stats->ustats.address = PXP_VF_BAR0_START_USDM_ZONE_B +
      offsetof(struct ustorm_vf_zone,
        non_trigger.eth_queue_stat);
 p_stats->ustats.len = sizeof(struct eth_ustorm_per_queue_stat);
 p_stats->pstats.address = PXP_VF_BAR0_START_PSDM_ZONE_B +
      offsetof(struct pstorm_vf_zone,
        non_trigger.eth_queue_stat);
 p_stats->pstats.len = sizeof(struct eth_pstorm_per_queue_stat);
 p_stats->tstats.address = 0;
 p_stats->tstats.len = 0;
}

static void qed_iov_vf_mbx_acquire(struct qed_hwfn *p_hwfn,
       struct qed_ptt *p_ptt,
       struct qed_vf_info *vf)
{
 struct qed_iov_vf_mbx *mbx = &vf->vf_mbx;
 struct pfvf_acquire_resp_tlv *resp = &mbx->reply_virt->acquire_resp;
 struct pf_vf_pfdev_info *pfdev_info = &resp->pfdev_info;
 struct vfpf_acquire_tlv *req = &mbx->req_virt->acquire;
 u8 vfpf_status = PFVF_STATUS_NOT_SUPPORTED;
 struct pf_vf_resc *resc = &resp->resc;
 int rc;

 memset(resp, 0, sizeof(*resp));

 /* Write the PF version so that VF would know which version
 * is supported - might be later overridden. This guarantees that
 * VF could recognize legacy PF based on lack of versions in reply.
 */

 pfdev_info->major_fp_hsi = ETH_HSI_VER_MAJOR;
 pfdev_info->minor_fp_hsi = ETH_HSI_VER_MINOR;

 if (vf->state != VF_FREE && vf->state != VF_STOPPED) {
  DP_VERBOSE(p_hwfn,
      QED_MSG_IOV,
      "VF[%d] sent ACQUIRE but is already in state %d - fail request\n",
      vf->abs_vf_id, vf->state);
  goto out;
 }

 /* Validate FW compatibility */
 if (req->vfdev_info.eth_fp_hsi_major != ETH_HSI_VER_MAJOR) {
  if (req->vfdev_info.capabilities &
      VFPF_ACQUIRE_CAP_PRE_FP_HSI) {
   struct vf_pf_vfdev_info *p_vfdev = &req->vfdev_info;

   DP_VERBOSE(p_hwfn, QED_MSG_IOV,
       "VF[%d] is pre-fastpath HSI\n",
       vf->abs_vf_id);
   p_vfdev->eth_fp_hsi_major = ETH_HSI_VER_MAJOR;
   p_vfdev->eth_fp_hsi_minor = ETH_HSI_VER_NO_PKT_LEN_TUNN;
  } else {
   DP_INFO(p_hwfn,
    "VF[%d] needs fastpath HSI %02x.%02x, which is incompatible with loaded FW's fastpath HSI %02x.%02x\n",
    vf->abs_vf_id,
    req->vfdev_info.eth_fp_hsi_major,
    req->vfdev_info.eth_fp_hsi_minor,
    ETH_HSI_VER_MAJOR, ETH_HSI_VER_MINOR);

   goto out;
  }
 }

 /* On 100g PFs, prevent old VFs from loading */
 if ((p_hwfn->cdev->num_hwfns > 1) &&
     !(req->vfdev_info.capabilities & VFPF_ACQUIRE_CAP_100G)) {
  DP_INFO(p_hwfn,
   "VF[%d] is running an old driver that doesn't support 100g\n",
   vf->abs_vf_id);
  goto out;
 }

 /* Store the acquire message */
 memcpy(&vf->acquire, req, sizeof(vf->acquire));

 vf->opaque_fid = req->vfdev_info.opaque_fid;

 vf->vf_bulletin = req->bulletin_addr;
 vf->bulletin.size = (vf->bulletin.size < req->bulletin_size) ?
       vf->bulletin.size : req->bulletin_size;

 /* fill in pfdev info */
 pfdev_info->chip_num = p_hwfn->cdev->chip_num;
 pfdev_info->db_size = 0;
 pfdev_info->indices_per_sb = PIS_PER_SB;

 pfdev_info->capabilities = PFVF_ACQUIRE_CAP_DEFAULT_UNTAGGED |
       PFVF_ACQUIRE_CAP_POST_FW_OVERRIDE;
 if (p_hwfn->cdev->num_hwfns > 1)
  pfdev_info->capabilities |= PFVF_ACQUIRE_CAP_100G;

 /* Share our ability to use multiple queue-ids only with VFs
 * that request it.
 */

 if (req->vfdev_info.capabilities & VFPF_ACQUIRE_CAP_QUEUE_QIDS)
  pfdev_info->capabilities |= PFVF_ACQUIRE_CAP_QUEUE_QIDS;

 /* Share the sizes of the bars with VF */
 resp->pfdev_info.bar_size = qed_iov_vf_db_bar_size(p_hwfn, p_ptt);

 qed_iov_vf_mbx_acquire_stats(p_hwfn, &pfdev_info->stats_info);

 memcpy(pfdev_info->port_mac, p_hwfn->hw_info.hw_mac_addr, ETH_ALEN);

 pfdev_info->fw_major = FW_MAJOR_VERSION;
 pfdev_info->fw_minor = FW_MINOR_VERSION;
 pfdev_info->fw_rev = FW_REVISION_VERSION;
 pfdev_info->fw_eng = FW_ENGINEERING_VERSION;

 /* Incorrect when legacy, but doesn't matter as legacy isn't reading
 * this field.
 */

 pfdev_info->minor_fp_hsi = min_t(u8, ETH_HSI_VER_MINOR,
      req->vfdev_info.eth_fp_hsi_minor);
 pfdev_info->os_type = VFPF_ACQUIRE_OS_LINUX;
 qed_mcp_get_mfw_ver(p_hwfn, p_ptt, &pfdev_info->mfw_ver, NULL);

 pfdev_info->dev_type = p_hwfn->cdev->type;
 pfdev_info->chip_rev = p_hwfn->cdev->chip_rev;

 /* Fill resources available to VF; Make sure there are enough to
 * satisfy the VF's request.
 */

 vfpf_status = qed_iov_vf_mbx_acquire_resc(p_hwfn, p_ptt, vf,
        &req->resc_request, resc);
 if (vfpf_status != PFVF_STATUS_SUCCESS)
  goto out;

 /* Start the VF in FW */
 rc = qed_sp_vf_start(p_hwfn, vf);
 if (rc) {
  DP_NOTICE(p_hwfn, "Failed to start VF[%02x]\n", vf->abs_vf_id);
  vfpf_status = PFVF_STATUS_FAILURE;
  goto out;
 }

 /* Fill agreed size of bulletin board in response */
 resp->bulletin_size = vf->bulletin.size;
 qed_iov_post_vf_bulletin(p_hwfn, vf->relative_vf_id, p_ptt);

 DP_VERBOSE(p_hwfn,
     QED_MSG_IOV,
     "VF[%d] ACQUIRE_RESPONSE: pfdev_info- chip_num=0x%x, db_size=%d, idx_per_sb=%d, pf_cap=0x%llx\n"
     "resources- n_rxq-%d, n_txq-%d, n_sbs-%d, n_macs-%d, n_vlans-%d\n",
     vf->abs_vf_id,
     resp->pfdev_info.chip_num,
     resp->pfdev_info.db_size,
     resp->pfdev_info.indices_per_sb,
     resp->pfdev_info.capabilities,
     resc->num_rxqs,
     resc->num_txqs,
     resc->num_sbs,
     resc->num_mac_filters,
     resc->num_vlan_filters);
 vf->state = VF_ACQUIRED;

 /* Prepare Response */
out:
 qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_ACQUIRE,
        sizeof(struct pfvf_acquire_resp_tlv), vfpf_status);
}

static int __qed_iov_spoofchk_set(struct qed_hwfn *p_hwfn,
      struct qed_vf_info *p_vf, bool val)
{
 struct qed_sp_vport_update_params params;
 int rc;

 if (val == p_vf->spoof_chk) {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "Spoofchk value[%d] is already configured\n", val);
  return 0;
 }

 memset(¶ms, 0, sizeof(struct qed_sp_vport_update_params));
 params.opaque_fid = p_vf->opaque_fid;
 params.vport_id = p_vf->vport_id;
 params.update_anti_spoofing_en_flg = 1;
 params.anti_spoofing_en = val;

 rc = qed_sp_vport_update(p_hwfn, ¶ms, QED_SPQ_MODE_EBLOCK, NULL);
 if (!rc) {
  p_vf->spoof_chk = val;
  p_vf->req_spoofchk_val = p_vf->spoof_chk;
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "Spoofchk val[%d] configured\n", val);
 } else {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "Spoofchk configuration[val:%d] failed for VF[%d]\n",
      val, p_vf->relative_vf_id);
 }

 return rc;
}

static int qed_iov_reconfigure_unicast_vlan(struct qed_hwfn *p_hwfn,
         struct qed_vf_info *p_vf)
{
 struct qed_filter_ucast filter;
 int rc = 0;
 int i;

 memset(&filter, 0, sizeof(filter));
 filter.is_rx_filter = 1;
 filter.is_tx_filter = 1;
 filter.vport_to_add_to = p_vf->vport_id;
 filter.opcode = QED_FILTER_ADD;

 /* Reconfigure vlans */
 for (i = 0; i < QED_ETH_VF_NUM_VLAN_FILTERS + 1; i++) {
  if (!p_vf->shadow_config.vlans[i].used)
   continue;

  filter.type = QED_FILTER_VLAN;
  filter.vlan = p_vf->shadow_config.vlans[i].vid;
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "Reconfiguring VLAN [0x%04x] for VF [%04x]\n",
      filter.vlan, p_vf->relative_vf_id);
  rc = qed_sp_eth_filter_ucast(p_hwfn, p_vf->opaque_fid,
          &filter, QED_SPQ_MODE_CB, NULL);
  if (rc) {
   DP_NOTICE(p_hwfn,
      "Failed to configure VLAN [%04x] to VF [%04x]\n",
      filter.vlan, p_vf->relative_vf_id);
   break;
  }
 }

 return rc;
}

static int
qed_iov_reconfigure_unicast_shadow(struct qed_hwfn *p_hwfn,
       struct qed_vf_info *p_vf, u64 events)
{
 int rc = 0;

 if ((events & BIT(VLAN_ADDR_FORCED)) &&
     !(p_vf->configured_features & (1 << VLAN_ADDR_FORCED)))
  rc = qed_iov_reconfigure_unicast_vlan(p_hwfn, p_vf);

 return rc;
}

static int qed_iov_configure_vport_forced(struct qed_hwfn *p_hwfn,
       struct qed_vf_info *p_vf, u64 events)
{
 int rc = 0;
 struct qed_filter_ucast filter;

 if (!p_vf->vport_instance)
  return -EINVAL;

 if ((events & BIT(MAC_ADDR_FORCED)) ||
     p_vf->p_vf_info.is_trusted_configured) {
  /* Since there's no way [currently] of removing the MAC,
 * we can always assume this means we need to force it.
 */

  memset(&filter, 0, sizeof(filter));
  filter.type = QED_FILTER_MAC;
  filter.opcode = QED_FILTER_REPLACE;
  filter.is_rx_filter = 1;
  filter.is_tx_filter = 1;
  filter.vport_to_add_to = p_vf->vport_id;
  ether_addr_copy(filter.mac, p_vf->bulletin.p_virt->mac);

  rc = qed_sp_eth_filter_ucast(p_hwfn, p_vf->opaque_fid,
          &filter, QED_SPQ_MODE_CB, NULL);
  if (rc) {
   DP_NOTICE(p_hwfn,
      "PF failed to configure MAC for VF\n");
   return rc;
  }
  if (p_vf->p_vf_info.is_trusted_configured)
   p_vf->configured_features |=
    BIT(VFPF_BULLETIN_MAC_ADDR);
  else
   p_vf->configured_features |=
    BIT(MAC_ADDR_FORCED);
 }

 if (events & BIT(VLAN_ADDR_FORCED)) {
  struct qed_sp_vport_update_params vport_update;
  u8 removal;
  int i;

  memset(&filter, 0, sizeof(filter));
  filter.type = QED_FILTER_VLAN;
  filter.is_rx_filter = 1;
  filter.is_tx_filter = 1;
  filter.vport_to_add_to = p_vf->vport_id;
  filter.vlan = p_vf->bulletin.p_virt->pvid;
  filter.opcode = filter.vlan ? QED_FILTER_REPLACE :
           QED_FILTER_FLUSH;

  /* Send the ramrod */
  rc = qed_sp_eth_filter_ucast(p_hwfn, p_vf->opaque_fid,
          &filter, QED_SPQ_MODE_CB, NULL);
  if (rc) {
   DP_NOTICE(p_hwfn,
      "PF failed to configure VLAN for VF\n");
   return rc;
  }

  /* Update the default-vlan & silent vlan stripping */
  memset(&vport_update, 0, sizeof(vport_update));
  vport_update.opaque_fid = p_vf->opaque_fid;
  vport_update.vport_id = p_vf->vport_id;
  vport_update.update_default_vlan_enable_flg = 1;
  vport_update.default_vlan_enable_flg = filter.vlan ? 1 : 0;
  vport_update.update_default_vlan_flg = 1;
  vport_update.default_vlan = filter.vlan;

  vport_update.update_inner_vlan_removal_flg = 1;
  removal = filter.vlan ? 1
          : p_vf->shadow_config.inner_vlan_removal;
  vport_update.inner_vlan_removal_flg = removal;
  vport_update.silent_vlan_removal_flg = filter.vlan ? 1 : 0;
  rc = qed_sp_vport_update(p_hwfn,
      &vport_update,
      QED_SPQ_MODE_EBLOCK, NULL);
  if (rc) {
   DP_NOTICE(p_hwfn,
      "PF failed to configure VF vport for vlan\n");
   return rc;
  }

  /* Update all the Rx queues */
  for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) {
   struct qed_vf_queue *p_queue = &p_vf->vf_queues[i];
   struct qed_queue_cid *p_cid = NULL;

   /* There can be at most 1 Rx queue on qzone. Find it */
   p_cid = qed_iov_get_vf_rx_queue_cid(p_queue);
   if (!p_cid)
    continue;

   rc = qed_sp_eth_rx_queues_update(p_hwfn,
        (void **)&p_cid,
        1, 0, 1,
        QED_SPQ_MODE_EBLOCK,
        NULL);
   if (rc) {
    DP_NOTICE(p_hwfn,
       "Failed to send Rx update fo queue[0x%04x]\n",
       p_cid->rel.queue_id);
    return rc;
   }
  }

  if (filter.vlan)
   p_vf->configured_features |= 1 << VLAN_ADDR_FORCED;
  else
   p_vf->configured_features &= ~BIT(VLAN_ADDR_FORCED);
 }

 /* If forced features are terminated, we need to configure the shadow
 * configuration back again.
 */

 if (events)
  qed_iov_reconfigure_unicast_shadow(p_hwfn, p_vf, events);

 return rc;
}

static void qed_iov_vf_mbx_start_vport(struct qed_hwfn *p_hwfn,
           struct qed_ptt *p_ptt,
           struct qed_vf_info *vf)
{
 struct qed_sp_vport_start_params params = { 0 };
 struct qed_iov_vf_mbx *mbx = &vf->vf_mbx;
 struct vfpf_vport_start_tlv *start;
 u8 status = PFVF_STATUS_SUCCESS;
 struct qed_vf_info *vf_info;
 u64 *p_bitmap;
 int sb_id;
 int rc;

 vf_info = qed_iov_get_vf_info(p_hwfn, (u16)vf->relative_vf_id, true);
 if (!vf_info) {
  DP_NOTICE(p_hwfn->cdev,
     "Failed to get VF info, invalid vfid [%d]\n",
     vf->relative_vf_id);
  return;
 }

 vf->state = VF_ENABLED;
 start = &mbx->req_virt->start_vport;

 qed_iov_enable_vf_traffic(p_hwfn, p_ptt, vf);

 /* Initialize Status block in CAU */
 for (sb_id = 0; sb_id < vf->num_sbs; sb_id++) {
  if (!start->sb_addr[sb_id]) {
   DP_VERBOSE(p_hwfn, QED_MSG_IOV,
       "VF[%d] did not fill the address of SB %d\n",
       vf->relative_vf_id, sb_id);
   break;
  }

  qed_int_cau_conf_sb(p_hwfn, p_ptt,
        start->sb_addr[sb_id],
        vf->igu_sbs[sb_id], vf->abs_vf_id, 1);
 }

 vf->mtu = start->mtu;
 vf->shadow_config.inner_vlan_removal = start->inner_vlan_removal;

 /* Take into consideration configuration forced by hypervisor;
 * If none is configured, use the supplied VF values [for old
 * vfs that would still be fine, since they passed '0' as padding].
 */

 p_bitmap = &vf_info->bulletin.p_virt->valid_bitmap;
 if (!(*p_bitmap & BIT(VFPF_BULLETIN_UNTAGGED_DEFAULT_FORCED))) {
  u8 vf_req = start->only_untagged;

  vf_info->bulletin.p_virt->default_only_untagged = vf_req;
  *p_bitmap |= 1 << VFPF_BULLETIN_UNTAGGED_DEFAULT;
 }

 params.tpa_mode = start->tpa_mode;
 params.remove_inner_vlan = start->inner_vlan_removal;
 params.tx_switching = true;

 params.only_untagged = vf_info->bulletin.p_virt->default_only_untagged;
 params.drop_ttl0 = false;
 params.concrete_fid = vf->concrete_fid;
 params.opaque_fid = vf->opaque_fid;
 params.vport_id = vf->vport_id;
 params.max_buffers_per_cqe = start->max_buffers_per_cqe;
 params.mtu = vf->mtu;

 /* Non trusted VFs should enable control frame filtering */
 params.check_mac = !vf->p_vf_info.is_trusted_configured;

 rc = qed_sp_eth_vport_start(p_hwfn, ¶ms);
 if (rc) {
  DP_ERR(p_hwfn,
         "%s returned error %d\n", __func__, rc);
  status = PFVF_STATUS_FAILURE;
 } else {
  vf->vport_instance++;

  /* Force configuration if needed on the newly opened vport */
  qed_iov_configure_vport_forced(p_hwfn, vf, *p_bitmap);

  __qed_iov_spoofchk_set(p_hwfn, vf, vf->req_spoofchk_val);
 }
 qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_VPORT_START,
        sizeof(struct pfvf_def_resp_tlv), status);
}

static void qed_iov_vf_mbx_stop_vport(struct qed_hwfn *p_hwfn,
          struct qed_ptt *p_ptt,
          struct qed_vf_info *vf)
{
 u8 status = PFVF_STATUS_SUCCESS;
 int rc;

 vf->vport_instance--;
 vf->spoof_chk = false;

 if ((qed_iov_validate_active_rxq(p_hwfn, vf)) ||
     (qed_iov_validate_active_txq(p_hwfn, vf))) {
  vf->b_malicious = true;
  DP_NOTICE(p_hwfn,
     "VF [%02x] - considered malicious; Unable to stop RX/TX queues\n",
     vf->abs_vf_id);
  status = PFVF_STATUS_MALICIOUS;
  goto out;
 }

 rc = qed_sp_vport_stop(p_hwfn, vf->opaque_fid, vf->vport_id);
 if (rc) {
  DP_ERR(p_hwfn, "%s returned error %d\n",
         __func__, rc);
  status = PFVF_STATUS_FAILURE;
 }

 /* Forget the configuration on the vport */
 vf->configured_features = 0;
 memset(&vf->shadow_config, 0, sizeof(vf->shadow_config));

out:
 qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_VPORT_TEARDOWN,
        sizeof(struct pfvf_def_resp_tlv), status);
}

static void qed_iov_vf_mbx_start_rxq_resp(struct qed_hwfn *p_hwfn,
       struct qed_ptt *p_ptt,
       struct qed_vf_info *vf,
       u8 status, bool b_legacy)
{
 struct qed_iov_vf_mbx *mbx = &vf->vf_mbx;
 struct pfvf_start_queue_resp_tlv *p_tlv;
 struct vfpf_start_rxq_tlv *req;
 u16 length;

 mbx->offset = (u8 *)mbx->reply_virt;

 /* Taking a bigger struct instead of adding a TLV to list was a
 * mistake, but one which we're now stuck with, as some older
 * clients assume the size of the previous response.
 */

 if (!b_legacy)
  length = sizeof(*p_tlv);
 else
  length = sizeof(struct pfvf_def_resp_tlv);

 p_tlv = qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_START_RXQ,
       length);
 qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END,
      sizeof(struct channel_list_end_tlv));

 /* Update the TLV with the response */
 if ((status == PFVF_STATUS_SUCCESS) && !b_legacy) {
  req = &mbx->req_virt->start_rxq;
  p_tlv->offset = PXP_VF_BAR0_START_MSDM_ZONE_B +
    offsetof(struct mstorm_vf_zone,
      non_trigger.eth_rx_queue_producers) +
    sizeof(struct eth_rx_prod_data) * req->rx_qid;
 }

 qed_iov_send_response(p_hwfn, p_ptt, vf, length, status);
}

static u8 qed_iov_vf_mbx_qid(struct qed_hwfn *p_hwfn,
        struct qed_vf_info *p_vf, bool b_is_tx)
{
 struct qed_iov_vf_mbx *p_mbx = &p_vf->vf_mbx;
 struct vfpf_qid_tlv *p_qid_tlv;

 /* Search for the qid if the VF published its going to provide it */
 if (!(p_vf->acquire.vfdev_info.capabilities &
       VFPF_ACQUIRE_CAP_QUEUE_QIDS)) {
  if (b_is_tx)
   return QED_IOV_LEGACY_QID_TX;
  else
   return QED_IOV_LEGACY_QID_RX;
 }

 p_qid_tlv = (struct vfpf_qid_tlv *)
      qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt,
          CHANNEL_TLV_QID);
 if (!p_qid_tlv) {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "VF[%2x]: Failed to provide qid\n",
      p_vf->relative_vf_id);

  return QED_IOV_QID_INVALID;
 }

 if (p_qid_tlv->qid >= MAX_QUEUES_PER_QZONE) {
  DP_VERBOSE(p_hwfn, QED_MSG_IOV,
      "VF[%02x]: Provided qid out-of-bounds %02x\n",
      p_vf->relative_vf_id, p_qid_tlv->qid);
  return QED_IOV_QID_INVALID;
 }

 return p_qid_tlv->qid;
}

static void qed_iov_vf_mbx_start_rxq(struct qed_hwfn *p_hwfn,
         struct qed_ptt *p_ptt,
         struct qed_vf_info *vf)
{
 struct qed_queue_start_common_params params;
 struct qed_queue_cid_vf_params vf_params;
 struct qed_iov_vf_mbx *mbx = &vf->vf_mbx;
 u8 status = PFVF_STATUS_NO_RESOURCE;
 u8 qid_usage_idx, vf_legacy = 0;
 struct vfpf_start_rxq_tlv *req;
 struct qed_vf_queue *p_queue;
 struct qed_queue_cid *p_cid;
 struct qed_sb_info sb_dummy;
 int rc;

 req = &mbx->req_virt->start_rxq;

 if (!qed_iov_validate_rxq(p_hwfn, vf, req->rx_qid,
      QED_IOV_VALIDATE_Q_DISABLE) ||
     !qed_iov_validate_sb(p_hwfn, vf, req->hw_sb))
  goto out;

 qid_usage_idx = qed_iov_vf_mbx_qid(p_hwfn, vf, false);
 if (qid_usage_idx == QED_IOV_QID_INVALID)
  goto out;

 p_queue = &vf->vf_queues[req->rx_qid];
 if (p_queue->cids[qid_usage_idx].p_cid)
  goto out;

 vf_legacy = qed_vf_calculate_legacy(vf);

 /* Acquire a new queue-cid */
 memset(¶ms, 0, sizeof(params));
 params.queue_id = p_queue->fw_rx_qid;
 params.vport_id = vf->vport_id;
 params.stats_id = vf->abs_vf_id + 0x10;
 /* Since IGU index is passed via sb_info, construct a dummy one */
 memset(&sb_dummy, 0, sizeof(sb_dummy));
 sb_dummy.igu_sb_id = req->hw_sb;
 params.p_sb = &sb_dummy;
 params.sb_idx = req->sb_index;

 memset(&vf_params, 0, sizeof(vf_params));
 vf_params.vfid = vf->relative_vf_id;
 vf_params.vf_qid = (u8)req->rx_qid;
 vf_params.vf_legacy = vf_legacy;
 vf_params.qid_usage_idx = qid_usage_idx;
 p_cid = qed_eth_queue_to_cid(p_hwfn, vf->opaque_fid,
         ¶ms, true, &vf_params);
 if (!p_cid)
  goto out;

 /* Legacy VFs have their Producers in a different location, which they
 * calculate on their own and clean the producer prior to this.
 */

 if (!(vf_legacy & QED_QCID_LEGACY_VF_RX_PROD))
  qed_wr(p_hwfn, p_ptt, MSEM_REG_FAST_MEMORY +
         SEM_FAST_REG_INT_RAM +
         MSTORM_ETH_VF_PRODS_OFFSET(vf->abs_vf_id,
        req->rx_qid), 0);

 rc = qed_eth_rxq_start_ramrod(p_hwfn, p_cid,
          req->bd_max_bytes,
          req->rxq_addr,
          req->cqe_pbl_addr, req->cqe_pbl_size);
 if (rc) {
  status = PFVF_STATUS_FAILURE;
  qed_eth_queue_cid_release(p_hwfn, p_cid);
 } else {
  p_queue->cids[qid_usage_idx].p_cid = p_cid;
  p_queue->cids[qid_usage_idx].b_is_tx = false;
  status = PFVF_STATUS_SUCCESS;
  vf->num_active_rxqs++;
 }

out:
 qed_iov_vf_mbx_start_rxq_resp(p_hwfn, p_ptt, vf, status,
          !!(vf_legacy &
      QED_QCID_LEGACY_VF_RX_PROD));
}

static void
qed_iov_pf_update_tun_response(struct pfvf_update_tunn_param_tlv *p_resp,
          struct qed_tunnel_info *p_tun,
          u16 tunn_feature_mask)
{
 p_resp->tunn_feature_mask = tunn_feature_mask;
 p_resp->vxlan_mode = p_tun->vxlan.b_mode_enabled;
 p_resp->l2geneve_mode = p_tun->l2_geneve.b_mode_enabled;
 p_resp->ipgeneve_mode = p_tun->ip_geneve.b_mode_enabled;
 p_resp->l2gre_mode = p_tun->l2_gre.b_mode_enabled;
 p_resp->ipgre_mode = p_tun->l2_gre.b_mode_enabled;
 p_resp->vxlan_clss = p_tun->vxlan.tun_cls;
 p_resp->l2gre_clss = p_tun->l2_gre.tun_cls;
 p_resp->ipgre_clss = p_tun->ip_gre.tun_cls;
 p_resp->l2geneve_clss = p_tun->l2_geneve.tun_cls;
 p_resp->ipgeneve_clss = p_tun->ip_geneve.tun_cls;
 p_resp->geneve_udp_port = p_tun->geneve_port.port;
 p_resp->vxlan_udp_port = p_tun->vxlan_port.port;
}

static void
__qed_iov_pf_update_tun_param(struct vfpf_update_tunn_param_tlv *p_req,
         struct qed_tunn_update_type *p_tun,
         enum qed_tunn_mode mask, u8 tun_cls)
{
 if (p_req->tun_mode_update_mask & BIT(mask)) {
  p_tun->b_update_mode = true;

  if (p_req->tunn_mode & BIT(mask))
   p_tun->b_mode_enabled = true;
 }

 p_tun->tun_cls = tun_cls;
}

static void
qed_iov_pf_update_tun_param(struct vfpf_update_tunn_param_tlv *p_req,
       struct qed_tunn_update_type *p_tun,
       struct qed_tunn_update_udp_port *p_port,
       enum qed_tunn_mode mask,
       u8 tun_cls, u8 update_port, u16 port)
{
 if (update_port) {
  p_port->b_update_port = true;
  p_port->port = port;
 }

 __qed_iov_pf_update_tun_param(p_req, p_tun, mask, tun_cls);
}

static bool
qed_iov_pf_validate_tunn_param(struct vfpf_update_tunn_param_tlv *p_req)
{
 bool b_update_requested = false;

 if (p_req->tun_mode_update_mask || p_req->update_tun_cls ||
     p_req->update_geneve_port || p_req->update_vxlan_port)
  b_update_requested = true;

 return b_update_requested;
}

static void qed_pf_validate_tunn_mode(struct qed_tunn_update_type *tun, int *rc)
{
 if (tun->b_update_mode && !tun->b_mode_enabled) {
  tun->b_update_mode = false;
  *rc = -EINVAL;
 }
}

static int
qed_pf_validate_modify_tunn_config(struct qed_hwfn *p_hwfn,
       u16 *tun_features, bool *update,
       struct qed_tunnel_info *tun_src)
{
 struct qed_eth_cb_ops *ops = p_hwfn->cdev->protocol_ops.eth;
 struct qed_tunnel_info *tun = &p_hwfn->cdev->tunnel;
 u16 bultn_vxlan_port, bultn_geneve_port;
 void *cookie = p_hwfn->cdev->ops_cookie;
 int i, rc = 0;

 *tun_features = p_hwfn->cdev->tunn_feature_mask;
 bultn_vxlan_port = tun->vxlan_port.port;
 bultn_geneve_port = tun->geneve_port.port;
 qed_pf_validate_tunn_mode(&tun_src->vxlan, &rc);
 qed_pf_validate_tunn_mode(&tun_src->l2_geneve, &rc);
 qed_pf_validate_tunn_mode(&tun_src->ip_geneve, &rc);
 qed_pf_validate_tunn_mode(&tun_src->l2_gre, &rc);
 qed_pf_validate_tunn_mode(&tun_src->ip_gre, &rc);

 if ((tun_src->b_update_rx_cls || tun_src->b_update_tx_cls) &&
     (tun_src->vxlan.tun_cls != QED_TUNN_CLSS_MAC_VLAN ||
      tun_src->l2_geneve.tun_cls != QED_TUNN_CLSS_MAC_VLAN ||
      tun_src->ip_geneve.tun_cls != QED_TUNN_CLSS_MAC_VLAN ||
      tun_src->l2_gre.tun_cls != QED_TUNN_CLSS_MAC_VLAN ||
      tun_src->ip_gre.tun_cls != QED_TUNN_CLSS_MAC_VLAN)) {
  tun_src->b_update_rx_cls = false;
  tun_src->b_update_tx_cls = false;
  rc = -EINVAL;
 }

 if (tun_src->vxlan_port.b_update_port) {
  if (tun_src->vxlan_port.port == tun->vxlan_port.port) {
   tun_src->vxlan_port.b_update_port = false;
  } else {
   *update = true;
   bultn_vxlan_port = tun_src->vxlan_port.port;
  }
 }

 if (tun_src->geneve_port.b_update_port) {
  if (tun_src->geneve_port.port == tun->geneve_port.port) {
   tun_src->geneve_port.b_update_port = false;
  } else {
   *update = true;
   bultn_geneve_port = tun_src->geneve_port.port;
  }
 }

 qed_for_each_vf(p_hwfn, i) {
  qed_iov_bulletin_set_udp_ports(p_hwfn, i, bultn_vxlan_port,
            bultn_geneve_port);
 }

 qed_schedule_iov(p_hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG);
 ops->ports_update(cookie, bultn_vxlan_port, bultn_geneve_port);

 return rc;
}

static void qed_iov_vf_mbx_update_tunn_param(struct qed_hwfn *p_hwfn,
          struct qed_ptt *p_ptt,
          struct qed_vf_info *p_vf)
{
 struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel;
 struct qed_iov_vf_mbx *mbx = &p_vf->vf_mbx;
 struct pfvf_update_tunn_param_tlv *p_resp;
 struct vfpf_update_tunn_param_tlv *p_req;
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=92 G=93

¤ Dauer der Verarbeitung: 0.13 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.