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

Quelle  idpf_virtchnl.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2023 Intel Corporation */

#include <linux/export.h>
#include <net/libeth/rx.h>

#include "idpf.h"
#include "idpf_virtchnl.h"
#include "idpf_ptp.h"

/**
 * struct idpf_vc_xn_manager - Manager for tracking transactions
 * @ring: backing and lookup for transactions
 * @free_xn_bm: bitmap for free transactions
 * @xn_bm_lock: make bitmap access synchronous where necessary
 * @salt: used to make cookie unique every message
 */

struct idpf_vc_xn_manager {
 struct idpf_vc_xn ring[IDPF_VC_XN_RING_LEN];
 DECLARE_BITMAP(free_xn_bm, IDPF_VC_XN_RING_LEN);
 spinlock_t xn_bm_lock;
 u8 salt;
};

/**
 * idpf_vid_to_vport - Translate vport id to vport pointer
 * @adapter: private data struct
 * @v_id: vport id to translate
 *
 * Returns vport matching v_id, NULL if not found.
 */

static
struct idpf_vport *idpf_vid_to_vport(struct idpf_adapter *adapter, u32 v_id)
{
 u16 num_max_vports = idpf_get_max_vports(adapter);
 int i;

 for (i = 0; i < num_max_vports; i++)
  if (adapter->vport_ids[i] == v_id)
   return adapter->vports[i];

 return NULL;
}

/**
 * idpf_handle_event_link - Handle link event message
 * @adapter: private data struct
 * @v2e: virtchnl event message
 */

static void idpf_handle_event_link(struct idpf_adapter *adapter,
       const struct virtchnl2_event *v2e)
{
 struct idpf_netdev_priv *np;
 struct idpf_vport *vport;

 vport = idpf_vid_to_vport(adapter, le32_to_cpu(v2e->vport_id));
 if (!vport) {
  dev_err_ratelimited(&adapter->pdev->dev, "Failed to find vport_id %d for link event\n",
        v2e->vport_id);
  return;
 }
 np = netdev_priv(vport->netdev);

 np->link_speed_mbps = le32_to_cpu(v2e->link_speed);

 if (vport->link_up == v2e->link_status)
  return;

 vport->link_up = v2e->link_status;

 if (np->state != __IDPF_VPORT_UP)
  return;

 if (vport->link_up) {
  netif_tx_start_all_queues(vport->netdev);
  netif_carrier_on(vport->netdev);
 } else {
  netif_tx_stop_all_queues(vport->netdev);
  netif_carrier_off(vport->netdev);
 }
}

/**
 * idpf_recv_event_msg - Receive virtchnl event message
 * @adapter: Driver specific private structure
 * @ctlq_msg: message to copy from
 *
 * Receive virtchnl event message
 */

static void idpf_recv_event_msg(struct idpf_adapter *adapter,
    struct idpf_ctlq_msg *ctlq_msg)
{
 int payload_size = ctlq_msg->ctx.indirect.payload->size;
 struct virtchnl2_event *v2e;
 u32 event;

 if (payload_size < sizeof(*v2e)) {
  dev_err_ratelimited(&adapter->pdev->dev, "Failed to receive valid payload for event msg (op %d len %d)\n",
        ctlq_msg->cookie.mbx.chnl_opcode,
        payload_size);
  return;
 }

 v2e = (struct virtchnl2_event *)ctlq_msg->ctx.indirect.payload->va;
 event = le32_to_cpu(v2e->event);

 switch (event) {
 case VIRTCHNL2_EVENT_LINK_CHANGE:
  idpf_handle_event_link(adapter, v2e);
  return;
 default:
  dev_err(&adapter->pdev->dev,
   "Unknown event %d from PF\n", event);
  break;
 }
}

/**
 * idpf_mb_clean - Reclaim the send mailbox queue entries
 * @adapter: Driver specific private structure
 *
 * Reclaim the send mailbox queue entries to be used to send further messages
 *
 * Returns 0 on success, negative on failure
 */

static int idpf_mb_clean(struct idpf_adapter *adapter)
{
 u16 i, num_q_msg = IDPF_DFLT_MBX_Q_LEN;
 struct idpf_ctlq_msg **q_msg;
 struct idpf_dma_mem *dma_mem;
 int err;

 q_msg = kcalloc(num_q_msg, sizeof(struct idpf_ctlq_msg *), GFP_ATOMIC);
 if (!q_msg)
  return -ENOMEM;

 err = idpf_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg);
 if (err)
  goto err_kfree;

 for (i = 0; i < num_q_msg; i++) {
  if (!q_msg[i])
   continue;
  dma_mem = q_msg[i]->ctx.indirect.payload;
  if (dma_mem)
   dma_free_coherent(&adapter->pdev->dev, dma_mem->size,
       dma_mem->va, dma_mem->pa);
  kfree(q_msg[i]);
  kfree(dma_mem);
 }

err_kfree:
 kfree(q_msg);

 return err;
}

#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
/**
 * idpf_ptp_is_mb_msg - Check if the message is PTP-related
 * @op: virtchnl opcode
 *
 * Return: true if msg is PTP-related, false otherwise.
 */

static bool idpf_ptp_is_mb_msg(u32 op)
{
 switch (op) {
 case VIRTCHNL2_OP_PTP_GET_DEV_CLK_TIME:
 case VIRTCHNL2_OP_PTP_GET_CROSS_TIME:
 case VIRTCHNL2_OP_PTP_SET_DEV_CLK_TIME:
 case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_FINE:
 case VIRTCHNL2_OP_PTP_ADJ_DEV_CLK_TIME:
 case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP_CAPS:
 case VIRTCHNL2_OP_PTP_GET_VPORT_TX_TSTAMP:
  return true;
 default:
  return false;
 }
}

/**
 * idpf_prepare_ptp_mb_msg - Prepare PTP related message
 *
 * @adapter: Driver specific private structure
 * @op: virtchnl opcode
 * @ctlq_msg: Corresponding control queue message
 */

static void idpf_prepare_ptp_mb_msg(struct idpf_adapter *adapter, u32 op,
        struct idpf_ctlq_msg *ctlq_msg)
{
 /* If the message is PTP-related and the secondary mailbox is available,
 * send the message through the secondary mailbox.
 */

 if (!idpf_ptp_is_mb_msg(op) || !adapter->ptp->secondary_mbx.valid)
  return;

 ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_peer_drv;
 ctlq_msg->func_id = adapter->ptp->secondary_mbx.peer_mbx_q_id;
 ctlq_msg->host_id = adapter->ptp->secondary_mbx.peer_id;
}
#else /* !CONFIG_PTP_1588_CLOCK */
static void idpf_prepare_ptp_mb_msg(struct idpf_adapter *adapter, u32 op,
        struct idpf_ctlq_msg *ctlq_msg)
{ }
#endif /* CONFIG_PTP_1588_CLOCK */

/**
 * idpf_send_mb_msg - Send message over mailbox
 * @adapter: Driver specific private structure
 * @op: virtchnl opcode
 * @msg_size: size of the payload
 * @msg: pointer to buffer holding the payload
 * @cookie: unique SW generated cookie per message
 *
 * Will prepare the control queue message and initiates the send api
 *
 * Returns 0 on success, negative on failure
 */

int idpf_send_mb_msg(struct idpf_adapter *adapter, u32 op,
       u16 msg_size, u8 *msg, u16 cookie)
{
 struct idpf_ctlq_msg *ctlq_msg;
 struct idpf_dma_mem *dma_mem;
 int err;

 /* If we are here and a reset is detected nothing much can be
 * done. This thread should silently abort and expected to
 * be corrected with a new run either by user or driver
 * flows after reset
 */

 if (idpf_is_reset_detected(adapter))
  return 0;

 err = idpf_mb_clean(adapter);
 if (err)
  return err;

 ctlq_msg = kzalloc(sizeof(*ctlq_msg), GFP_ATOMIC);
 if (!ctlq_msg)
  return -ENOMEM;

 dma_mem = kzalloc(sizeof(*dma_mem), GFP_ATOMIC);
 if (!dma_mem) {
  err = -ENOMEM;
  goto dma_mem_error;
 }

 ctlq_msg->opcode = idpf_mbq_opc_send_msg_to_cp;
 ctlq_msg->func_id = 0;

 idpf_prepare_ptp_mb_msg(adapter, op, ctlq_msg);

 ctlq_msg->data_len = msg_size;
 ctlq_msg->cookie.mbx.chnl_opcode = op;
 ctlq_msg->cookie.mbx.chnl_retval = 0;
 dma_mem->size = IDPF_CTLQ_MAX_BUF_LEN;
 dma_mem->va = dma_alloc_coherent(&adapter->pdev->dev, dma_mem->size,
      &dma_mem->pa, GFP_ATOMIC);
 if (!dma_mem->va) {
  err = -ENOMEM;
  goto dma_alloc_error;
 }

 /* It's possible we're just sending an opcode but no buffer */
 if (msg && msg_size)
  memcpy(dma_mem->va, msg, msg_size);
 ctlq_msg->ctx.indirect.payload = dma_mem;
 ctlq_msg->ctx.sw_cookie.data = cookie;

 err = idpf_ctlq_send(&adapter->hw, adapter->hw.asq, 1, ctlq_msg);
 if (err)
  goto send_error;

 return 0;

send_error:
 dma_free_coherent(&adapter->pdev->dev, dma_mem->size, dma_mem->va,
     dma_mem->pa);
dma_alloc_error:
 kfree(dma_mem);
dma_mem_error:
 kfree(ctlq_msg);

 return err;
}

/* API for virtchnl "transaction" support ("xn" for short).
 *
 * We are reusing the completion lock to serialize the accesses to the
 * transaction state for simplicity, but it could be its own separate synchro
 * as well. For now, this API is only used from within a workqueue context;
 * raw_spin_lock() is enough.
 */

/**
 * idpf_vc_xn_lock - Request exclusive access to vc transaction
 * @xn: struct idpf_vc_xn* to access
 */

#define idpf_vc_xn_lock(xn)   \
 raw_spin_lock(&(xn)->completed.wait.lock)

/**
 * idpf_vc_xn_unlock - Release exclusive access to vc transaction
 * @xn: struct idpf_vc_xn* to access
 */

#define idpf_vc_xn_unlock(xn)  \
 raw_spin_unlock(&(xn)->completed.wait.lock)

/**
 * idpf_vc_xn_release_bufs - Release reference to reply buffer(s) and
 * reset the transaction state.
 * @xn: struct idpf_vc_xn to update
 */

static void idpf_vc_xn_release_bufs(struct idpf_vc_xn *xn)
{
 xn->reply.iov_base = NULL;
 xn->reply.iov_len = 0;

 if (xn->state != IDPF_VC_XN_SHUTDOWN)
  xn->state = IDPF_VC_XN_IDLE;
}

/**
 * idpf_vc_xn_init - Initialize virtchnl transaction object
 * @vcxn_mngr: pointer to vc transaction manager struct
 */

static void idpf_vc_xn_init(struct idpf_vc_xn_manager *vcxn_mngr)
{
 int i;

 spin_lock_init(&vcxn_mngr->xn_bm_lock);

 for (i = 0; i < ARRAY_SIZE(vcxn_mngr->ring); i++) {
  struct idpf_vc_xn *xn = &vcxn_mngr->ring[i];

  xn->state = IDPF_VC_XN_IDLE;
  xn->idx = i;
  idpf_vc_xn_release_bufs(xn);
  init_completion(&xn->completed);
 }

 bitmap_fill(vcxn_mngr->free_xn_bm, IDPF_VC_XN_RING_LEN);
}

/**
 * idpf_vc_xn_shutdown - Uninitialize virtchnl transaction object
 * @vcxn_mngr: pointer to vc transaction manager struct
 *
 * All waiting threads will be woken-up and their transaction aborted. Further
 * operations on that object will fail.
 */

void idpf_vc_xn_shutdown(struct idpf_vc_xn_manager *vcxn_mngr)
{
 int i;

 spin_lock_bh(&vcxn_mngr->xn_bm_lock);
 bitmap_zero(vcxn_mngr->free_xn_bm, IDPF_VC_XN_RING_LEN);
 spin_unlock_bh(&vcxn_mngr->xn_bm_lock);

 for (i = 0; i < ARRAY_SIZE(vcxn_mngr->ring); i++) {
  struct idpf_vc_xn *xn = &vcxn_mngr->ring[i];

  idpf_vc_xn_lock(xn);
  xn->state = IDPF_VC_XN_SHUTDOWN;
  idpf_vc_xn_release_bufs(xn);
  idpf_vc_xn_unlock(xn);
  complete_all(&xn->completed);
 }
}

/**
 * idpf_vc_xn_pop_free - Pop a free transaction from free list
 * @vcxn_mngr: transaction manager to pop from
 *
 * Returns NULL if no free transactions
 */

static
struct idpf_vc_xn *idpf_vc_xn_pop_free(struct idpf_vc_xn_manager *vcxn_mngr)
{
 struct idpf_vc_xn *xn = NULL;
 unsigned long free_idx;

 spin_lock_bh(&vcxn_mngr->xn_bm_lock);
 free_idx = find_first_bit(vcxn_mngr->free_xn_bm, IDPF_VC_XN_RING_LEN);
 if (free_idx == IDPF_VC_XN_RING_LEN)
  goto do_unlock;

 clear_bit(free_idx, vcxn_mngr->free_xn_bm);
 xn = &vcxn_mngr->ring[free_idx];
 xn->salt = vcxn_mngr->salt++;

do_unlock:
 spin_unlock_bh(&vcxn_mngr->xn_bm_lock);

 return xn;
}

/**
 * idpf_vc_xn_push_free - Push a free transaction to free list
 * @vcxn_mngr: transaction manager to push to
 * @xn: transaction to push
 */

static void idpf_vc_xn_push_free(struct idpf_vc_xn_manager *vcxn_mngr,
     struct idpf_vc_xn *xn)
{
 idpf_vc_xn_release_bufs(xn);
 set_bit(xn->idx, vcxn_mngr->free_xn_bm);
}

/**
 * idpf_vc_xn_exec - Perform a send/recv virtchnl transaction
 * @adapter: driver specific private structure with vcxn_mngr
 * @params: parameters for this particular transaction including
 *   -vc_op: virtchannel operation to send
 *   -send_buf: kvec iov for send buf and len
 *   -recv_buf: kvec iov for recv buf and len (ignored if NULL)
 *   -timeout_ms: timeout waiting for a reply (milliseconds)
 *   -async: don't wait for message reply, will lose caller context
 *   -async_handler: callback to handle async replies
 *
 * @returns >= 0 for success, the size of the initial reply (may or may not be
 * >= @recv_buf.iov_len, but we never overflow @@recv_buf_iov_base). < 0 for
 * error.
 */

ssize_t idpf_vc_xn_exec(struct idpf_adapter *adapter,
   const struct idpf_vc_xn_params *params)
{
 const struct kvec *send_buf = ¶ms->send_buf;
 struct idpf_vc_xn *xn;
 ssize_t retval;
 u16 cookie;

 xn = idpf_vc_xn_pop_free(adapter->vcxn_mngr);
 /* no free transactions available */
 if (!xn)
  return -ENOSPC;

 idpf_vc_xn_lock(xn);
 if (xn->state == IDPF_VC_XN_SHUTDOWN) {
  retval = -ENXIO;
  goto only_unlock;
 } else if (xn->state != IDPF_VC_XN_IDLE) {
  /* We're just going to clobber this transaction even though
 * it's not IDLE. If we don't reuse it we could theoretically
 * eventually leak all the free transactions and not be able to
 * send any messages. At least this way we make an attempt to
 * remain functional even though something really bad is
 * happening that's corrupting what was supposed to be free
 * transactions.
 */

  WARN_ONCE(1, "There should only be idle transactions in free list (idx %d op %d)\n",
     xn->idx, xn->vc_op);
 }

 xn->reply = params->recv_buf;
 xn->reply_sz = 0;
 xn->state = params->async ? IDPF_VC_XN_ASYNC : IDPF_VC_XN_WAITING;
 xn->vc_op = params->vc_op;
 xn->async_handler = params->async_handler;
 idpf_vc_xn_unlock(xn);

 if (!params->async)
  reinit_completion(&xn->completed);
 cookie = FIELD_PREP(IDPF_VC_XN_SALT_M, xn->salt) |
   FIELD_PREP(IDPF_VC_XN_IDX_M, xn->idx);

 retval = idpf_send_mb_msg(adapter, params->vc_op,
      send_buf->iov_len, send_buf->iov_base,
      cookie);
 if (retval) {
  idpf_vc_xn_lock(xn);
  goto release_and_unlock;
 }

 if (params->async)
  return 0;

 wait_for_completion_timeout(&xn->completed,
        msecs_to_jiffies(params->timeout_ms));

 /* No need to check the return value; we check the final state of the
 * transaction below. It's possible the transaction actually gets more
 * timeout than specified if we get preempted here but after
 * wait_for_completion_timeout returns. This should be non-issue
 * however.
 */

 idpf_vc_xn_lock(xn);
 switch (xn->state) {
 case IDPF_VC_XN_SHUTDOWN:
  retval = -ENXIO;
  goto only_unlock;
 case IDPF_VC_XN_WAITING:
  dev_notice_ratelimited(&adapter->pdev->dev,
           "Transaction timed-out (op:%d cookie:%04x vc_op:%d salt:%02x timeout:%dms)\n",
           params->vc_op, cookie, xn->vc_op,
           xn->salt, params->timeout_ms);
  retval = -ETIME;
  break;
 case IDPF_VC_XN_COMPLETED_SUCCESS:
  retval = xn->reply_sz;
  break;
 case IDPF_VC_XN_COMPLETED_FAILED:
  dev_notice_ratelimited(&adapter->pdev->dev, "Transaction failed (op %d)\n",
           params->vc_op);
  retval = -EIO;
  break;
 default:
  /* Invalid state. */
  WARN_ON_ONCE(1);
  retval = -EIO;
  break;
 }

release_and_unlock:
 idpf_vc_xn_push_free(adapter->vcxn_mngr, xn);
 /* If we receive a VC reply after here, it will be dropped. */
only_unlock:
 idpf_vc_xn_unlock(xn);

 return retval;
}

/**
 * idpf_vc_xn_forward_async - Handle async reply receives
 * @adapter: private data struct
 * @xn: transaction to handle
 * @ctlq_msg: corresponding ctlq_msg
 *
 * For async sends we're going to lose the caller's context so, if an
 * async_handler was provided, it can deal with the reply, otherwise we'll just
 * check and report if there is an error.
 */

static int
idpf_vc_xn_forward_async(struct idpf_adapter *adapter, struct idpf_vc_xn *xn,
    const struct idpf_ctlq_msg *ctlq_msg)
{
 int err = 0;

 if (ctlq_msg->cookie.mbx.chnl_opcode != xn->vc_op) {
  dev_err_ratelimited(&adapter->pdev->dev, "Async message opcode does not match transaction opcode (msg: %d) (xn: %d)\n",
        ctlq_msg->cookie.mbx.chnl_opcode, xn->vc_op);
  xn->reply_sz = 0;
  err = -EINVAL;
  goto release_bufs;
 }

 if (xn->async_handler) {
  err = xn->async_handler(adapter, xn, ctlq_msg);
  goto release_bufs;
 }

 if (ctlq_msg->cookie.mbx.chnl_retval) {
  xn->reply_sz = 0;
  dev_err_ratelimited(&adapter->pdev->dev, "Async message failure (op %d)\n",
        ctlq_msg->cookie.mbx.chnl_opcode);
  err = -EINVAL;
 }

release_bufs:
 idpf_vc_xn_push_free(adapter->vcxn_mngr, xn);

 return err;
}

/**
 * idpf_vc_xn_forward_reply - copy a reply back to receiving thread
 * @adapter: driver specific private structure with vcxn_mngr
 * @ctlq_msg: controlq message to send back to receiving thread
 */

static int
idpf_vc_xn_forward_reply(struct idpf_adapter *adapter,
    const struct idpf_ctlq_msg *ctlq_msg)
{
 const void *payload = NULL;
 size_t payload_size = 0;
 struct idpf_vc_xn *xn;
 u16 msg_info;
 int err = 0;
 u16 xn_idx;
 u16 salt;

 msg_info = ctlq_msg->ctx.sw_cookie.data;
 xn_idx = FIELD_GET(IDPF_VC_XN_IDX_M, msg_info);
 if (xn_idx >= ARRAY_SIZE(adapter->vcxn_mngr->ring)) {
  dev_err_ratelimited(&adapter->pdev->dev, "Out of bounds cookie received: %02x\n",
        xn_idx);
  return -EINVAL;
 }
 xn = &adapter->vcxn_mngr->ring[xn_idx];
 idpf_vc_xn_lock(xn);
 salt = FIELD_GET(IDPF_VC_XN_SALT_M, msg_info);
 if (xn->salt != salt) {
  dev_err_ratelimited(&adapter->pdev->dev, "Transaction salt does not match (exp:%d@%02x(%d) != got:%d@%02x)\n",
        xn->vc_op, xn->salt, xn->state,
        ctlq_msg->cookie.mbx.chnl_opcode, salt);
  idpf_vc_xn_unlock(xn);
  return -EINVAL;
 }

 switch (xn->state) {
 case IDPF_VC_XN_WAITING:
  /* success */
  break;
 case IDPF_VC_XN_IDLE:
  dev_err_ratelimited(&adapter->pdev->dev, "Unexpected or belated VC reply (op %d)\n",
        ctlq_msg->cookie.mbx.chnl_opcode);
  err = -EINVAL;
  goto out_unlock;
 case IDPF_VC_XN_SHUTDOWN:
  /* ENXIO is a bit special here as the recv msg loop uses that
 * know if it should stop trying to clean the ring if we lost
 * the virtchnl. We need to stop playing with registers and
 * yield.
 */

  err = -ENXIO;
  goto out_unlock;
 case IDPF_VC_XN_ASYNC:
  err = idpf_vc_xn_forward_async(adapter, xn, ctlq_msg);
  idpf_vc_xn_unlock(xn);
  return err;
 default:
  dev_err_ratelimited(&adapter->pdev->dev, "Overwriting VC reply (op %d)\n",
        ctlq_msg->cookie.mbx.chnl_opcode);
  err = -EBUSY;
  goto out_unlock;
 }

 if (ctlq_msg->cookie.mbx.chnl_opcode != xn->vc_op) {
  dev_err_ratelimited(&adapter->pdev->dev, "Message opcode does not match transaction opcode (msg: %d) (xn: %d)\n",
        ctlq_msg->cookie.mbx.chnl_opcode, xn->vc_op);
  xn->reply_sz = 0;
  xn->state = IDPF_VC_XN_COMPLETED_FAILED;
  err = -EINVAL;
  goto out_unlock;
 }

 if (ctlq_msg->cookie.mbx.chnl_retval) {
  xn->reply_sz = 0;
  xn->state = IDPF_VC_XN_COMPLETED_FAILED;
  err = -EINVAL;
  goto out_unlock;
 }

 if (ctlq_msg->data_len) {
  payload = ctlq_msg->ctx.indirect.payload->va;
  payload_size = ctlq_msg->data_len;
 }

 xn->reply_sz = payload_size;
 xn->state = IDPF_VC_XN_COMPLETED_SUCCESS;

 if (xn->reply.iov_base && xn->reply.iov_len && payload_size)
  memcpy(xn->reply.iov_base, payload,
         min_t(size_t, xn->reply.iov_len, payload_size));

out_unlock:
 idpf_vc_xn_unlock(xn);
 /* we _cannot_ hold lock while calling complete */
 complete(&xn->completed);

 return err;
}

/**
 * idpf_recv_mb_msg - Receive message over mailbox
 * @adapter: Driver specific private structure
 *
 * Will receive control queue message and posts the receive buffer. Returns 0
 * on success and negative on failure.
 */

int idpf_recv_mb_msg(struct idpf_adapter *adapter)
{
 struct idpf_ctlq_msg ctlq_msg;
 struct idpf_dma_mem *dma_mem;
 int post_err, err;
 u16 num_recv;

 while (1) {
  /* This will get <= num_recv messages and output how many
 * actually received on num_recv.
 */

  num_recv = 1;
  err = idpf_ctlq_recv(adapter->hw.arq, &num_recv, &ctlq_msg);
  if (err || !num_recv)
   break;

  if (ctlq_msg.data_len) {
   dma_mem = ctlq_msg.ctx.indirect.payload;
  } else {
   dma_mem = NULL;
   num_recv = 0;
  }

  if (ctlq_msg.cookie.mbx.chnl_opcode == VIRTCHNL2_OP_EVENT)
   idpf_recv_event_msg(adapter, &ctlq_msg);
  else
   err = idpf_vc_xn_forward_reply(adapter, &ctlq_msg);

  post_err = idpf_ctlq_post_rx_buffs(&adapter->hw,
         adapter->hw.arq,
         &num_recv, &dma_mem);

  /* If post failed clear the only buffer we supplied */
  if (post_err) {
   if (dma_mem)
    dma_free_coherent(&adapter->pdev->dev,
        dma_mem->size, dma_mem->va,
        dma_mem->pa);
   break;
  }

  /* virtchnl trying to shutdown, stop cleaning */
  if (err == -ENXIO)
   break;
 }

 return err;
}

/**
 * idpf_wait_for_marker_event - wait for software marker response
 * @vport: virtual port data structure
 *
 * Returns 0 success, negative on failure.
 **/

static int idpf_wait_for_marker_event(struct idpf_vport *vport)
{
 int event;
 int i;

 for (i = 0; i < vport->num_txq; i++)
  idpf_queue_set(SW_MARKER, vport->txqs[i]);

 event = wait_event_timeout(vport->sw_marker_wq,
       test_and_clear_bit(IDPF_VPORT_SW_MARKER,
            vport->flags),
       msecs_to_jiffies(500));

 for (i = 0; i < vport->num_txq; i++)
  idpf_queue_clear(POLL_MODE, vport->txqs[i]);

 if (event)
  return 0;

 dev_warn(&vport->adapter->pdev->dev, "Failed to receive marker packets\n");

 return -ETIMEDOUT;
}

/**
 * idpf_send_ver_msg - send virtchnl version message
 * @adapter: Driver specific private structure
 *
 * Send virtchnl version message.  Returns 0 on success, negative on failure.
 */

static int idpf_send_ver_msg(struct idpf_adapter *adapter)
{
 struct idpf_vc_xn_params xn_params = {};
 struct virtchnl2_version_info vvi;
 ssize_t reply_sz;
 u32 major, minor;
 int err = 0;

 if (adapter->virt_ver_maj) {
  vvi.major = cpu_to_le32(adapter->virt_ver_maj);
  vvi.minor = cpu_to_le32(adapter->virt_ver_min);
 } else {
  vvi.major = cpu_to_le32(IDPF_VIRTCHNL_VERSION_MAJOR);
  vvi.minor = cpu_to_le32(IDPF_VIRTCHNL_VERSION_MINOR);
 }

 xn_params.vc_op = VIRTCHNL2_OP_VERSION;
 xn_params.send_buf.iov_base = &vvi;
 xn_params.send_buf.iov_len = sizeof(vvi);
 xn_params.recv_buf = xn_params.send_buf;
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;

 reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
 if (reply_sz < 0)
  return reply_sz;
 if (reply_sz < sizeof(vvi))
  return -EIO;

 major = le32_to_cpu(vvi.major);
 minor = le32_to_cpu(vvi.minor);

 if (major > IDPF_VIRTCHNL_VERSION_MAJOR) {
  dev_warn(&adapter->pdev->dev, "Virtchnl major version greater than supported\n");
  return -EINVAL;
 }

 if (major == IDPF_VIRTCHNL_VERSION_MAJOR &&
     minor > IDPF_VIRTCHNL_VERSION_MINOR)
  dev_warn(&adapter->pdev->dev, "Virtchnl minor version didn't match\n");

 /* If we have a mismatch, resend version to update receiver on what
 * version we will use.
 */

 if (!adapter->virt_ver_maj &&
     major != IDPF_VIRTCHNL_VERSION_MAJOR &&
     minor != IDPF_VIRTCHNL_VERSION_MINOR)
  err = -EAGAIN;

 adapter->virt_ver_maj = major;
 adapter->virt_ver_min = minor;

 return err;
}

/**
 * idpf_send_get_caps_msg - Send virtchnl get capabilities message
 * @adapter: Driver specific private structure
 *
 * Send virtchl get capabilities message. Returns 0 on success, negative on
 * failure.
 */

static int idpf_send_get_caps_msg(struct idpf_adapter *adapter)
{
 struct virtchnl2_get_capabilities caps = {};
 struct idpf_vc_xn_params xn_params = {};
 ssize_t reply_sz;

 caps.csum_caps =
  cpu_to_le32(VIRTCHNL2_CAP_TX_CSUM_L3_IPV4 |
       VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_TCP |
       VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_UDP |
       VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP |
       VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_TCP |
       VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_UDP |
       VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP |
       VIRTCHNL2_CAP_RX_CSUM_L3_IPV4 |
       VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP |
       VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP |
       VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP |
       VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP |
       VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP |
       VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP |
       VIRTCHNL2_CAP_TX_CSUM_L3_SINGLE_TUNNEL |
       VIRTCHNL2_CAP_RX_CSUM_L3_SINGLE_TUNNEL |
       VIRTCHNL2_CAP_TX_CSUM_L4_SINGLE_TUNNEL |
       VIRTCHNL2_CAP_RX_CSUM_L4_SINGLE_TUNNEL |
       VIRTCHNL2_CAP_RX_CSUM_GENERIC);

 caps.seg_caps =
  cpu_to_le32(VIRTCHNL2_CAP_SEG_IPV4_TCP  |
       VIRTCHNL2_CAP_SEG_IPV4_UDP  |
       VIRTCHNL2_CAP_SEG_IPV4_SCTP  |
       VIRTCHNL2_CAP_SEG_IPV6_TCP  |
       VIRTCHNL2_CAP_SEG_IPV6_UDP  |
       VIRTCHNL2_CAP_SEG_IPV6_SCTP  |
       VIRTCHNL2_CAP_SEG_TX_SINGLE_TUNNEL);

 caps.rss_caps =
  cpu_to_le64(VIRTCHNL2_FLOW_IPV4_TCP  |
       VIRTCHNL2_FLOW_IPV4_UDP  |
       VIRTCHNL2_FLOW_IPV4_SCTP  |
       VIRTCHNL2_FLOW_IPV4_OTHER  |
       VIRTCHNL2_FLOW_IPV6_TCP  |
       VIRTCHNL2_FLOW_IPV6_UDP  |
       VIRTCHNL2_FLOW_IPV6_SCTP  |
       VIRTCHNL2_FLOW_IPV6_OTHER);

 caps.hsplit_caps =
  cpu_to_le32(VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |
       VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6);

 caps.rsc_caps =
  cpu_to_le32(VIRTCHNL2_CAP_RSC_IPV4_TCP  |
       VIRTCHNL2_CAP_RSC_IPV6_TCP);

 caps.other_caps =
  cpu_to_le64(VIRTCHNL2_CAP_SRIOV   |
       VIRTCHNL2_CAP_RDMA                  |
       VIRTCHNL2_CAP_LAN_MEMORY_REGIONS |
       VIRTCHNL2_CAP_MACFILTER  |
       VIRTCHNL2_CAP_SPLITQ_QSCHED  |
       VIRTCHNL2_CAP_PROMISC  |
       VIRTCHNL2_CAP_LOOPBACK  |
       VIRTCHNL2_CAP_PTP);

 xn_params.vc_op = VIRTCHNL2_OP_GET_CAPS;
 xn_params.send_buf.iov_base = ∩︀
 xn_params.send_buf.iov_len = sizeof(caps);
 xn_params.recv_buf.iov_base = &adapter->caps;
 xn_params.recv_buf.iov_len = sizeof(adapter->caps);
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;

 reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
 if (reply_sz < 0)
  return reply_sz;
 if (reply_sz < sizeof(adapter->caps))
  return -EIO;

 return 0;
}

/**
 * idpf_send_get_lan_memory_regions - Send virtchnl get LAN memory regions msg
 * @adapter: Driver specific private struct
 *
 * Return: 0 on success or error code on failure.
 */

static int idpf_send_get_lan_memory_regions(struct idpf_adapter *adapter)
{
 struct virtchnl2_get_lan_memory_regions *rcvd_regions __free(kfree);
 struct idpf_vc_xn_params xn_params = {
  .vc_op = VIRTCHNL2_OP_GET_LAN_MEMORY_REGIONS,
  .recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN,
  .timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC,
 };
 int num_regions, size;
 struct idpf_hw *hw;
 ssize_t reply_sz;
 int err = 0;

 rcvd_regions = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
 if (!rcvd_regions)
  return -ENOMEM;

 xn_params.recv_buf.iov_base = rcvd_regions;
 reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
 if (reply_sz < 0)
  return reply_sz;

 num_regions = le16_to_cpu(rcvd_regions->num_memory_regions);
 size = struct_size(rcvd_regions, mem_reg, num_regions);
 if (reply_sz < size)
  return -EIO;

 if (size > IDPF_CTLQ_MAX_BUF_LEN)
  return -EINVAL;

 hw = &adapter->hw;
 hw->lan_regs = kcalloc(num_regions, sizeof(*hw->lan_regs), GFP_KERNEL);
 if (!hw->lan_regs)
  return -ENOMEM;

 for (int i = 0; i < num_regions; i++) {
  hw->lan_regs[i].addr_len =
   le64_to_cpu(rcvd_regions->mem_reg[i].size);
  hw->lan_regs[i].addr_start =
   le64_to_cpu(rcvd_regions->mem_reg[i].start_offset);
 }
 hw->num_lan_regs = num_regions;

 return err;
}

/**
 * idpf_calc_remaining_mmio_regs - calculate MMIO regions outside mbx and rstat
 * @adapter: Driver specific private structure
 *
 * Called when idpf_send_get_lan_memory_regions is not supported. This will
 * calculate the offsets and sizes for the regions before, in between, and
 * after the mailbox and rstat MMIO mappings.
 *
 * Return: 0 on success or error code on failure.
 */

static int idpf_calc_remaining_mmio_regs(struct idpf_adapter *adapter)
{
 struct resource *rstat_reg = &adapter->dev_ops.static_reg_info[1];
 struct resource *mbx_reg = &adapter->dev_ops.static_reg_info[0];
 struct idpf_hw *hw = &adapter->hw;

 hw->num_lan_regs = IDPF_MMIO_MAP_FALLBACK_MAX_REMAINING;
 hw->lan_regs = kcalloc(hw->num_lan_regs, sizeof(*hw->lan_regs),
          GFP_KERNEL);
 if (!hw->lan_regs)
  return -ENOMEM;

 /* Region preceding mailbox */
 hw->lan_regs[0].addr_start = 0;
 hw->lan_regs[0].addr_len = mbx_reg->start;
 /* Region between mailbox and rstat */
 hw->lan_regs[1].addr_start = mbx_reg->end + 1;
 hw->lan_regs[1].addr_len = rstat_reg->start -
     hw->lan_regs[1].addr_start;
 /* Region after rstat */
 hw->lan_regs[2].addr_start = rstat_reg->end + 1;
 hw->lan_regs[2].addr_len = pci_resource_len(adapter->pdev, 0) -
     hw->lan_regs[2].addr_start;

 return 0;
}

/**
 * idpf_map_lan_mmio_regs - map remaining LAN BAR regions
 * @adapter: Driver specific private structure
 *
 * Return: 0 on success or error code on failure.
 */

static int idpf_map_lan_mmio_regs(struct idpf_adapter *adapter)
{
 struct pci_dev *pdev = adapter->pdev;
 struct idpf_hw *hw = &adapter->hw;
 resource_size_t res_start;

 res_start = pci_resource_start(pdev, 0);

 for (int i = 0; i < hw->num_lan_regs; i++) {
  resource_size_t start;
  long len;

  len = hw->lan_regs[i].addr_len;
  if (!len)
   continue;
  start = hw->lan_regs[i].addr_start + res_start;

  hw->lan_regs[i].vaddr = devm_ioremap(&pdev->dev, start, len);
  if (!hw->lan_regs[i].vaddr) {
   pci_err(pdev, "failed to allocate BAR0 region\n");
   return -ENOMEM;
  }
 }

 return 0;
}

/**
 * idpf_add_del_fsteer_filters - Send virtchnl add/del Flow Steering message
 * @adapter: adapter info struct
 * @rule: Flow steering rule to add/delete
 * @opcode: VIRTCHNL2_OP_ADD_FLOW_RULE to add filter, or
 *          VIRTCHNL2_OP_DEL_FLOW_RULE to delete. All other values are invalid.
 *
 * Send ADD/DELETE flow steering virtchnl message and receive the result.
 *
 * Return: 0 on success, negative on failure.
 */

int idpf_add_del_fsteer_filters(struct idpf_adapter *adapter,
    struct virtchnl2_flow_rule_add_del *rule,
    enum virtchnl2_op opcode)
{
 int rule_count = le32_to_cpu(rule->count);
 struct idpf_vc_xn_params xn_params = {};
 ssize_t reply_sz;

 if (opcode != VIRTCHNL2_OP_ADD_FLOW_RULE &&
     opcode != VIRTCHNL2_OP_DEL_FLOW_RULE)
  return -EINVAL;

 xn_params.vc_op = opcode;
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
 xn_params.async = false;
 xn_params.send_buf.iov_base = rule;
 xn_params.send_buf.iov_len = struct_size(rule, rule_info, rule_count);
 xn_params.recv_buf.iov_base = rule;
 xn_params.recv_buf.iov_len = struct_size(rule, rule_info, rule_count);

 reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
 return reply_sz < 0 ? reply_sz : 0;
}

/**
 * idpf_vport_alloc_max_qs - Allocate max queues for a vport
 * @adapter: Driver specific private structure
 * @max_q: vport max queue structure
 */

int idpf_vport_alloc_max_qs(struct idpf_adapter *adapter,
       struct idpf_vport_max_q *max_q)
{
 struct idpf_avail_queue_info *avail_queues = &adapter->avail_queues;
 struct virtchnl2_get_capabilities *caps = &adapter->caps;
 u16 default_vports = idpf_get_default_vports(adapter);
 int max_rx_q, max_tx_q;

 mutex_lock(&adapter->queue_lock);

 max_rx_q = le16_to_cpu(caps->max_rx_q) / default_vports;
 max_tx_q = le16_to_cpu(caps->max_tx_q) / default_vports;
 if (adapter->num_alloc_vports < default_vports) {
  max_q->max_rxq = min_t(u16, max_rx_q, IDPF_MAX_Q);
  max_q->max_txq = min_t(u16, max_tx_q, IDPF_MAX_Q);
 } else {
  max_q->max_rxq = IDPF_MIN_Q;
  max_q->max_txq = IDPF_MIN_Q;
 }
 max_q->max_bufq = max_q->max_rxq * IDPF_MAX_BUFQS_PER_RXQ_GRP;
 max_q->max_complq = max_q->max_txq;

 if (avail_queues->avail_rxq < max_q->max_rxq ||
     avail_queues->avail_txq < max_q->max_txq ||
     avail_queues->avail_bufq < max_q->max_bufq ||
     avail_queues->avail_complq < max_q->max_complq) {
  mutex_unlock(&adapter->queue_lock);

  return -EINVAL;
 }

 avail_queues->avail_rxq -= max_q->max_rxq;
 avail_queues->avail_txq -= max_q->max_txq;
 avail_queues->avail_bufq -= max_q->max_bufq;
 avail_queues->avail_complq -= max_q->max_complq;

 mutex_unlock(&adapter->queue_lock);

 return 0;
}

/**
 * idpf_vport_dealloc_max_qs - Deallocate max queues of a vport
 * @adapter: Driver specific private structure
 * @max_q: vport max queue structure
 */

void idpf_vport_dealloc_max_qs(struct idpf_adapter *adapter,
          struct idpf_vport_max_q *max_q)
{
 struct idpf_avail_queue_info *avail_queues;

 mutex_lock(&adapter->queue_lock);
 avail_queues = &adapter->avail_queues;

 avail_queues->avail_rxq += max_q->max_rxq;
 avail_queues->avail_txq += max_q->max_txq;
 avail_queues->avail_bufq += max_q->max_bufq;
 avail_queues->avail_complq += max_q->max_complq;

 mutex_unlock(&adapter->queue_lock);
}

/**
 * idpf_init_avail_queues - Initialize available queues on the device
 * @adapter: Driver specific private structure
 */

static void idpf_init_avail_queues(struct idpf_adapter *adapter)
{
 struct idpf_avail_queue_info *avail_queues = &adapter->avail_queues;
 struct virtchnl2_get_capabilities *caps = &adapter->caps;

 avail_queues->avail_rxq = le16_to_cpu(caps->max_rx_q);
 avail_queues->avail_txq = le16_to_cpu(caps->max_tx_q);
 avail_queues->avail_bufq = le16_to_cpu(caps->max_rx_bufq);
 avail_queues->avail_complq = le16_to_cpu(caps->max_tx_complq);
}

/**
 * idpf_get_reg_intr_vecs - Get vector queue register offset
 * @vport: virtual port structure
 * @reg_vals: Register offsets to store in
 *
 * Returns number of registers that got populated
 */

int idpf_get_reg_intr_vecs(struct idpf_vport *vport,
      struct idpf_vec_regs *reg_vals)
{
 struct virtchnl2_vector_chunks *chunks;
 struct idpf_vec_regs reg_val;
 u16 num_vchunks, num_vec;
 int num_regs = 0, i, j;

 chunks = &vport->adapter->req_vec_chunks->vchunks;
 num_vchunks = le16_to_cpu(chunks->num_vchunks);

 for (j = 0; j < num_vchunks; j++) {
  struct virtchnl2_vector_chunk *chunk;
  u32 dynctl_reg_spacing;
  u32 itrn_reg_spacing;

  chunk = &chunks->vchunks[j];
  num_vec = le16_to_cpu(chunk->num_vectors);
  reg_val.dyn_ctl_reg = le32_to_cpu(chunk->dynctl_reg_start);
  reg_val.itrn_reg = le32_to_cpu(chunk->itrn_reg_start);
  reg_val.itrn_index_spacing = le32_to_cpu(chunk->itrn_index_spacing);

  dynctl_reg_spacing = le32_to_cpu(chunk->dynctl_reg_spacing);
  itrn_reg_spacing = le32_to_cpu(chunk->itrn_reg_spacing);

  for (i = 0; i < num_vec; i++) {
   reg_vals[num_regs].dyn_ctl_reg = reg_val.dyn_ctl_reg;
   reg_vals[num_regs].itrn_reg = reg_val.itrn_reg;
   reg_vals[num_regs].itrn_index_spacing =
      reg_val.itrn_index_spacing;

   reg_val.dyn_ctl_reg += dynctl_reg_spacing;
   reg_val.itrn_reg += itrn_reg_spacing;
   num_regs++;
  }
 }

 return num_regs;
}

/**
 * idpf_vport_get_q_reg - Get the queue registers for the vport
 * @reg_vals: register values needing to be set
 * @num_regs: amount we expect to fill
 * @q_type: queue model
 * @chunks: queue regs received over mailbox
 *
 * This function parses the queue register offsets from the queue register
 * chunk information, with a specific queue type and stores it into the array
 * passed as an argument. It returns the actual number of queue registers that
 * are filled.
 */

static int idpf_vport_get_q_reg(u32 *reg_vals, int num_regs, u32 q_type,
    struct virtchnl2_queue_reg_chunks *chunks)
{
 u16 num_chunks = le16_to_cpu(chunks->num_chunks);
 int reg_filled = 0, i;
 u32 reg_val;

 while (num_chunks--) {
  struct virtchnl2_queue_reg_chunk *chunk;
  u16 num_q;

  chunk = &chunks->chunks[num_chunks];
  if (le32_to_cpu(chunk->type) != q_type)
   continue;

  num_q = le32_to_cpu(chunk->num_queues);
  reg_val = le64_to_cpu(chunk->qtail_reg_start);
  for (i = 0; i < num_q && reg_filled < num_regs ; i++) {
   reg_vals[reg_filled++] = reg_val;
   reg_val += le32_to_cpu(chunk->qtail_reg_spacing);
  }
 }

 return reg_filled;
}

/**
 * __idpf_queue_reg_init - initialize queue registers
 * @vport: virtual port structure
 * @reg_vals: registers we are initializing
 * @num_regs: how many registers there are in total
 * @q_type: queue model
 *
 * Return number of queues that are initialized
 */

static int __idpf_queue_reg_init(struct idpf_vport *vport, u32 *reg_vals,
     int num_regs, u32 q_type)
{
 struct idpf_adapter *adapter = vport->adapter;
 int i, j, k = 0;

 switch (q_type) {
 case VIRTCHNL2_QUEUE_TYPE_TX:
  for (i = 0; i < vport->num_txq_grp; i++) {
   struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];

   for (j = 0; j < tx_qgrp->num_txq && k < num_regs; j++, k++)
    tx_qgrp->txqs[j]->tail =
     idpf_get_reg_addr(adapter, reg_vals[k]);
  }
  break;
 case VIRTCHNL2_QUEUE_TYPE_RX:
  for (i = 0; i < vport->num_rxq_grp; i++) {
   struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
   u16 num_rxq = rx_qgrp->singleq.num_rxq;

   for (j = 0; j < num_rxq && k < num_regs; j++, k++) {
    struct idpf_rx_queue *q;

    q = rx_qgrp->singleq.rxqs[j];
    q->tail = idpf_get_reg_addr(adapter,
           reg_vals[k]);
   }
  }
  break;
 case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
  for (i = 0; i < vport->num_rxq_grp; i++) {
   struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
   u8 num_bufqs = vport->num_bufqs_per_qgrp;

   for (j = 0; j < num_bufqs && k < num_regs; j++, k++) {
    struct idpf_buf_queue *q;

    q = &rx_qgrp->splitq.bufq_sets[j].bufq;
    q->tail = idpf_get_reg_addr(adapter,
           reg_vals[k]);
   }
  }
  break;
 default:
  break;
 }

 return k;
}

/**
 * idpf_queue_reg_init - initialize queue registers
 * @vport: virtual port structure
 *
 * Return 0 on success, negative on failure
 */

int idpf_queue_reg_init(struct idpf_vport *vport)
{
 struct virtchnl2_create_vport *vport_params;
 struct virtchnl2_queue_reg_chunks *chunks;
 struct idpf_vport_config *vport_config;
 u16 vport_idx = vport->idx;
 int num_regs, ret = 0;
 u32 *reg_vals;

 /* We may never deal with more than 256 same type of queues */
 reg_vals = kzalloc(sizeof(void *) * IDPF_LARGE_MAX_Q, GFP_KERNEL);
 if (!reg_vals)
  return -ENOMEM;

 vport_config = vport->adapter->vport_config[vport_idx];
 if (vport_config->req_qs_chunks) {
  struct virtchnl2_add_queues *vc_aq =
    (struct virtchnl2_add_queues *)vport_config->req_qs_chunks;
  chunks = &vc_aq->chunks;
 } else {
  vport_params = vport->adapter->vport_params_recvd[vport_idx];
  chunks = &vport_params->chunks;
 }

 /* Initialize Tx queue tail register address */
 num_regs = idpf_vport_get_q_reg(reg_vals, IDPF_LARGE_MAX_Q,
     VIRTCHNL2_QUEUE_TYPE_TX,
     chunks);
 if (num_regs < vport->num_txq) {
  ret = -EINVAL;
  goto free_reg_vals;
 }

 num_regs = __idpf_queue_reg_init(vport, reg_vals, num_regs,
      VIRTCHNL2_QUEUE_TYPE_TX);
 if (num_regs < vport->num_txq) {
  ret = -EINVAL;
  goto free_reg_vals;
 }

 /* Initialize Rx/buffer queue tail register address based on Rx queue
 * model
 */

 if (idpf_is_queue_model_split(vport->rxq_model)) {
  num_regs = idpf_vport_get_q_reg(reg_vals, IDPF_LARGE_MAX_Q,
      VIRTCHNL2_QUEUE_TYPE_RX_BUFFER,
      chunks);
  if (num_regs < vport->num_bufq) {
   ret = -EINVAL;
   goto free_reg_vals;
  }

  num_regs = __idpf_queue_reg_init(vport, reg_vals, num_regs,
       VIRTCHNL2_QUEUE_TYPE_RX_BUFFER);
  if (num_regs < vport->num_bufq) {
   ret = -EINVAL;
   goto free_reg_vals;
  }
 } else {
  num_regs = idpf_vport_get_q_reg(reg_vals, IDPF_LARGE_MAX_Q,
      VIRTCHNL2_QUEUE_TYPE_RX,
      chunks);
  if (num_regs < vport->num_rxq) {
   ret = -EINVAL;
   goto free_reg_vals;
  }

  num_regs = __idpf_queue_reg_init(vport, reg_vals, num_regs,
       VIRTCHNL2_QUEUE_TYPE_RX);
  if (num_regs < vport->num_rxq) {
   ret = -EINVAL;
   goto free_reg_vals;
  }
 }

free_reg_vals:
 kfree(reg_vals);

 return ret;
}

/**
 * idpf_send_create_vport_msg - Send virtchnl create vport message
 * @adapter: Driver specific private structure
 * @max_q: vport max queue info
 *
 * send virtchnl creae vport message
 *
 * Returns 0 on success, negative on failure
 */

int idpf_send_create_vport_msg(struct idpf_adapter *adapter,
          struct idpf_vport_max_q *max_q)
{
 struct virtchnl2_create_vport *vport_msg;
 struct idpf_vc_xn_params xn_params = {};
 u16 idx = adapter->next_vport;
 int err, buf_size;
 ssize_t reply_sz;

 buf_size = sizeof(struct virtchnl2_create_vport);
 if (!adapter->vport_params_reqd[idx]) {
  adapter->vport_params_reqd[idx] = kzalloc(buf_size,
         GFP_KERNEL);
  if (!adapter->vport_params_reqd[idx])
   return -ENOMEM;
 }

 vport_msg = adapter->vport_params_reqd[idx];
 vport_msg->vport_type = cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
 vport_msg->vport_index = cpu_to_le16(idx);

 if (adapter->req_tx_splitq || !IS_ENABLED(CONFIG_IDPF_SINGLEQ))
  vport_msg->txq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
 else
  vport_msg->txq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);

 if (adapter->req_rx_splitq || !IS_ENABLED(CONFIG_IDPF_SINGLEQ))
  vport_msg->rxq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
 else
  vport_msg->rxq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);

 err = idpf_vport_calc_total_qs(adapter, idx, vport_msg, max_q);
 if (err) {
  dev_err(&adapter->pdev->dev, "Enough queues are not available");

  return err;
 }

 if (!adapter->vport_params_recvd[idx]) {
  adapter->vport_params_recvd[idx] = kzalloc(IDPF_CTLQ_MAX_BUF_LEN,
          GFP_KERNEL);
  if (!adapter->vport_params_recvd[idx]) {
   err = -ENOMEM;
   goto free_vport_params;
  }
 }

 xn_params.vc_op = VIRTCHNL2_OP_CREATE_VPORT;
 xn_params.send_buf.iov_base = vport_msg;
 xn_params.send_buf.iov_len = buf_size;
 xn_params.recv_buf.iov_base = adapter->vport_params_recvd[idx];
 xn_params.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN;
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
 reply_sz = idpf_vc_xn_exec(adapter, &xn_params);
 if (reply_sz < 0) {
  err = reply_sz;
  goto free_vport_params;
 }

 return 0;

free_vport_params:
 kfree(adapter->vport_params_recvd[idx]);
 adapter->vport_params_recvd[idx] = NULL;
 kfree(adapter->vport_params_reqd[idx]);
 adapter->vport_params_reqd[idx] = NULL;

 return err;
}

/**
 * idpf_check_supported_desc_ids - Verify we have required descriptor support
 * @vport: virtual port structure
 *
 * Return 0 on success, error on failure
 */

int idpf_check_supported_desc_ids(struct idpf_vport *vport)
{
 struct idpf_adapter *adapter = vport->adapter;
 struct virtchnl2_create_vport *vport_msg;
 u64 rx_desc_ids, tx_desc_ids;

 vport_msg = adapter->vport_params_recvd[vport->idx];

 if (!IS_ENABLED(CONFIG_IDPF_SINGLEQ) &&
     (vport_msg->rxq_model == VIRTCHNL2_QUEUE_MODEL_SINGLE ||
      vport_msg->txq_model == VIRTCHNL2_QUEUE_MODEL_SINGLE)) {
  pci_err(adapter->pdev, "singleq mode requested, but not compiled-in\n");
  return -EOPNOTSUPP;
 }

 rx_desc_ids = le64_to_cpu(vport_msg->rx_desc_ids);
 tx_desc_ids = le64_to_cpu(vport_msg->tx_desc_ids);

 if (idpf_is_queue_model_split(vport->rxq_model)) {
  if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SPLITQ_M)) {
   dev_info(&adapter->pdev->dev, "Minimum RX descriptor support not provided, using the default\n");
   vport_msg->rx_desc_ids = cpu_to_le64(VIRTCHNL2_RXDID_2_FLEX_SPLITQ_M);
  }
 } else {
  if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M))
   vport->base_rxd = true;
 }

 if (!idpf_is_queue_model_split(vport->txq_model))
  return 0;

 if ((tx_desc_ids & MIN_SUPPORT_TXDID) != MIN_SUPPORT_TXDID) {
  dev_info(&adapter->pdev->dev, "Minimum TX descriptor support not provided, using the default\n");
  vport_msg->tx_desc_ids = cpu_to_le64(MIN_SUPPORT_TXDID);
 }

 return 0;
}

/**
 * idpf_send_destroy_vport_msg - Send virtchnl destroy vport message
 * @vport: virtual port data structure
 *
 * Send virtchnl destroy vport message.  Returns 0 on success, negative on
 * failure.
 */

int idpf_send_destroy_vport_msg(struct idpf_vport *vport)
{
 struct idpf_vc_xn_params xn_params = {};
 struct virtchnl2_vport v_id;
 ssize_t reply_sz;

 v_id.vport_id = cpu_to_le32(vport->vport_id);

 xn_params.vc_op = VIRTCHNL2_OP_DESTROY_VPORT;
 xn_params.send_buf.iov_base = &v_id;
 xn_params.send_buf.iov_len = sizeof(v_id);
 xn_params.timeout_ms = IDPF_VC_XN_MIN_TIMEOUT_MSEC;
 reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);

 return reply_sz < 0 ? reply_sz : 0;
}

/**
 * idpf_send_enable_vport_msg - Send virtchnl enable vport message
 * @vport: virtual port data structure
 *
 * Send enable vport virtchnl message.  Returns 0 on success, negative on
 * failure.
 */

int idpf_send_enable_vport_msg(struct idpf_vport *vport)
{
 struct idpf_vc_xn_params xn_params = {};
 struct virtchnl2_vport v_id;
 ssize_t reply_sz;

 v_id.vport_id = cpu_to_le32(vport->vport_id);

 xn_params.vc_op = VIRTCHNL2_OP_ENABLE_VPORT;
 xn_params.send_buf.iov_base = &v_id;
 xn_params.send_buf.iov_len = sizeof(v_id);
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
 reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);

 return reply_sz < 0 ? reply_sz : 0;
}

/**
 * idpf_send_disable_vport_msg - Send virtchnl disable vport message
 * @vport: virtual port data structure
 *
 * Send disable vport virtchnl message.  Returns 0 on success, negative on
 * failure.
 */

int idpf_send_disable_vport_msg(struct idpf_vport *vport)
{
 struct idpf_vc_xn_params xn_params = {};
 struct virtchnl2_vport v_id;
 ssize_t reply_sz;

 v_id.vport_id = cpu_to_le32(vport->vport_id);

 xn_params.vc_op = VIRTCHNL2_OP_DISABLE_VPORT;
 xn_params.send_buf.iov_base = &v_id;
 xn_params.send_buf.iov_len = sizeof(v_id);
 xn_params.timeout_ms = IDPF_VC_XN_MIN_TIMEOUT_MSEC;
 reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);

 return reply_sz < 0 ? reply_sz : 0;
}

/**
 * idpf_send_config_tx_queues_msg - Send virtchnl config tx queues message
 * @vport: virtual port data structure
 *
 * Send config tx queues virtchnl message. Returns 0 on success, negative on
 * failure.
 */

static int idpf_send_config_tx_queues_msg(struct idpf_vport *vport)
{
 struct virtchnl2_config_tx_queues *ctq __free(kfree) = NULL;
 struct virtchnl2_txq_info *qi __free(kfree) = NULL;
 struct idpf_vc_xn_params xn_params = {};
 u32 config_sz, chunk_sz, buf_sz;
 int totqs, num_msgs, num_chunks;
 ssize_t reply_sz;
 int i, k = 0;

 totqs = vport->num_txq + vport->num_complq;
 qi = kcalloc(totqs, sizeof(struct virtchnl2_txq_info), GFP_KERNEL);
 if (!qi)
  return -ENOMEM;

 /* Populate the queue info buffer with all queue context info */
 for (i = 0; i < vport->num_txq_grp; i++) {
  struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];
  int j, sched_mode;

  for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
   qi[k].queue_id =
    cpu_to_le32(tx_qgrp->txqs[j]->q_id);
   qi[k].model =
    cpu_to_le16(vport->txq_model);
   qi[k].type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_TX);
   qi[k].ring_len =
    cpu_to_le16(tx_qgrp->txqs[j]->desc_count);
   qi[k].dma_ring_addr =
    cpu_to_le64(tx_qgrp->txqs[j]->dma);
   if (idpf_is_queue_model_split(vport->txq_model)) {
    struct idpf_tx_queue *q = tx_qgrp->txqs[j];

    qi[k].tx_compl_queue_id =
     cpu_to_le16(tx_qgrp->complq->q_id);
    qi[k].relative_queue_id = cpu_to_le16(j);

    if (idpf_queue_has(FLOW_SCH_EN, q))
     qi[k].sched_mode =
     cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_FLOW);
    else
     qi[k].sched_mode =
     cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
   } else {
    qi[k].sched_mode =
     cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
   }
  }

  if (!idpf_is_queue_model_split(vport->txq_model))
   continue;

  qi[k].queue_id = cpu_to_le32(tx_qgrp->complq->q_id);
  qi[k].model = cpu_to_le16(vport->txq_model);
  qi[k].type = cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION);
  qi[k].ring_len = cpu_to_le16(tx_qgrp->complq->desc_count);
  qi[k].dma_ring_addr = cpu_to_le64(tx_qgrp->complq->dma);

  if (idpf_queue_has(FLOW_SCH_EN, tx_qgrp->complq))
   sched_mode = VIRTCHNL2_TXQ_SCHED_MODE_FLOW;
  else
   sched_mode = VIRTCHNL2_TXQ_SCHED_MODE_QUEUE;
  qi[k].sched_mode = cpu_to_le16(sched_mode);

  k++;
 }

 /* Make sure accounting agrees */
 if (k != totqs)
  return -EINVAL;

 /* Chunk up the queue contexts into multiple messages to avoid
 * sending a control queue message buffer that is too large
 */

 config_sz = sizeof(struct virtchnl2_config_tx_queues);
 chunk_sz = sizeof(struct virtchnl2_txq_info);

 num_chunks = min_t(u32, IDPF_NUM_CHUNKS_PER_MSG(config_sz, chunk_sz),
      totqs);
 num_msgs = DIV_ROUND_UP(totqs, num_chunks);

 buf_sz = struct_size(ctq, qinfo, num_chunks);
 ctq = kzalloc(buf_sz, GFP_KERNEL);
 if (!ctq)
  return -ENOMEM;

 xn_params.vc_op = VIRTCHNL2_OP_CONFIG_TX_QUEUES;
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;

 for (i = 0, k = 0; i < num_msgs; i++) {
  memset(ctq, 0, buf_sz);
  ctq->vport_id = cpu_to_le32(vport->vport_id);
  ctq->num_qinfo = cpu_to_le16(num_chunks);
  memcpy(ctq->qinfo, &qi[k], chunk_sz * num_chunks);

  xn_params.send_buf.iov_base = ctq;
  xn_params.send_buf.iov_len = buf_sz;
  reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
  if (reply_sz < 0)
   return reply_sz;

  k += num_chunks;
  totqs -= num_chunks;
  num_chunks = min(num_chunks, totqs);
  /* Recalculate buffer size */
  buf_sz = struct_size(ctq, qinfo, num_chunks);
 }

 return 0;
}

/**
 * idpf_send_config_rx_queues_msg - Send virtchnl config rx queues message
 * @vport: virtual port data structure
 *
 * Send config rx queues virtchnl message.  Returns 0 on success, negative on
 * failure.
 */

static int idpf_send_config_rx_queues_msg(struct idpf_vport *vport)
{
 struct virtchnl2_config_rx_queues *crq __free(kfree) = NULL;
 struct virtchnl2_rxq_info *qi __free(kfree) = NULL;
 struct idpf_vc_xn_params xn_params = {};
 u32 config_sz, chunk_sz, buf_sz;
 int totqs, num_msgs, num_chunks;
 ssize_t reply_sz;
 int i, k = 0;

 totqs = vport->num_rxq + vport->num_bufq;
 qi = kcalloc(totqs, sizeof(struct virtchnl2_rxq_info), GFP_KERNEL);
 if (!qi)
  return -ENOMEM;

 /* Populate the queue info buffer with all queue context info */
 for (i = 0; i < vport->num_rxq_grp; i++) {
  struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
  u16 num_rxq;
  int j;

  if (!idpf_is_queue_model_split(vport->rxq_model))
   goto setup_rxqs;

  for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
   struct idpf_buf_queue *bufq =
    &rx_qgrp->splitq.bufq_sets[j].bufq;

   qi[k].queue_id = cpu_to_le32(bufq->q_id);
   qi[k].model = cpu_to_le16(vport->rxq_model);
   qi[k].type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_RX_BUFFER);
   qi[k].desc_ids = cpu_to_le64(VIRTCHNL2_RXDID_2_FLEX_SPLITQ_M);
   qi[k].ring_len = cpu_to_le16(bufq->desc_count);
   qi[k].dma_ring_addr = cpu_to_le64(bufq->dma);
   qi[k].data_buffer_size = cpu_to_le32(bufq->rx_buf_size);
   qi[k].buffer_notif_stride = IDPF_RX_BUF_STRIDE;
   qi[k].rx_buffer_low_watermark =
    cpu_to_le16(bufq->rx_buffer_low_watermark);
   if (idpf_is_feature_ena(vport, NETIF_F_GRO_HW))
    qi[k].qflags |= cpu_to_le16(VIRTCHNL2_RXQ_RSC);
  }

setup_rxqs:
  if (idpf_is_queue_model_split(vport->rxq_model))
   num_rxq = rx_qgrp->splitq.num_rxq_sets;
  else
   num_rxq = rx_qgrp->singleq.num_rxq;

  for (j = 0; j < num_rxq; j++, k++) {
   const struct idpf_bufq_set *sets;
   struct idpf_rx_queue *rxq;

   if (!idpf_is_queue_model_split(vport->rxq_model)) {
    rxq = rx_qgrp->singleq.rxqs[j];
    goto common_qi_fields;
   }

   rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
   sets = rxq->bufq_sets;

   /* In splitq mode, RXQ buffer size should be
 * set to that of the first buffer queue
 * associated with this RXQ.
 */

   rxq->rx_buf_size = sets[0].bufq.rx_buf_size;

   qi[k].rx_bufq1_id = cpu_to_le16(sets[0].bufq.q_id);
   if (vport->num_bufqs_per_qgrp > IDPF_SINGLE_BUFQ_PER_RXQ_GRP) {
    qi[k].bufq2_ena = IDPF_BUFQ2_ENA;
    qi[k].rx_bufq2_id =
     cpu_to_le16(sets[1].bufq.q_id);
   }
   qi[k].rx_buffer_low_watermark =
    cpu_to_le16(rxq->rx_buffer_low_watermark);
   if (idpf_is_feature_ena(vport, NETIF_F_GRO_HW))
    qi[k].qflags |= cpu_to_le16(VIRTCHNL2_RXQ_RSC);

   rxq->rx_hbuf_size = sets[0].bufq.rx_hbuf_size;

   if (idpf_queue_has(HSPLIT_EN, rxq)) {
    qi[k].qflags |=
     cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
    qi[k].hdr_buffer_size =
     cpu_to_le16(rxq->rx_hbuf_size);
   }

common_qi_fields:
   qi[k].queue_id = cpu_to_le32(rxq->q_id);
   qi[k].model = cpu_to_le16(vport->rxq_model);
   qi[k].type = cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_RX);
   qi[k].ring_len = cpu_to_le16(rxq->desc_count);
   qi[k].dma_ring_addr = cpu_to_le64(rxq->dma);
   qi[k].max_pkt_size = cpu_to_le32(rxq->rx_max_pkt_size);
   qi[k].data_buffer_size = cpu_to_le32(rxq->rx_buf_size);
   qi[k].qflags |=
    cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
   qi[k].desc_ids = cpu_to_le64(rxq->rxdids);
  }
 }

 /* Make sure accounting agrees */
 if (k != totqs)
  return -EINVAL;

 /* Chunk up the queue contexts into multiple messages to avoid
 * sending a control queue message buffer that is too large
 */

 config_sz = sizeof(struct virtchnl2_config_rx_queues);
 chunk_sz = sizeof(struct virtchnl2_rxq_info);

 num_chunks = min_t(u32, IDPF_NUM_CHUNKS_PER_MSG(config_sz, chunk_sz),
      totqs);
 num_msgs = DIV_ROUND_UP(totqs, num_chunks);

 buf_sz = struct_size(crq, qinfo, num_chunks);
 crq = kzalloc(buf_sz, GFP_KERNEL);
 if (!crq)
  return -ENOMEM;

 xn_params.vc_op = VIRTCHNL2_OP_CONFIG_RX_QUEUES;
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;

 for (i = 0, k = 0; i < num_msgs; i++) {
  memset(crq, 0, buf_sz);
  crq->vport_id = cpu_to_le32(vport->vport_id);
  crq->num_qinfo = cpu_to_le16(num_chunks);
  memcpy(crq->qinfo, &qi[k], chunk_sz * num_chunks);

  xn_params.send_buf.iov_base = crq;
  xn_params.send_buf.iov_len = buf_sz;
  reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
  if (reply_sz < 0)
   return reply_sz;

  k += num_chunks;
  totqs -= num_chunks;
  num_chunks = min(num_chunks, totqs);
  /* Recalculate buffer size */
  buf_sz = struct_size(crq, qinfo, num_chunks);
 }

 return 0;
}

/**
 * idpf_send_ena_dis_queues_msg - Send virtchnl enable or disable
 * queues message
 * @vport: virtual port data structure
 * @ena: if true enable, false disable
 *
 * Send enable or disable queues virtchnl message. Returns 0 on success,
 * negative on failure.
 */

static int idpf_send_ena_dis_queues_msg(struct idpf_vport *vport, bool ena)
{
 struct virtchnl2_del_ena_dis_queues *eq __free(kfree) = NULL;
 struct virtchnl2_queue_chunk *qc __free(kfree) = NULL;
 u32 num_msgs, num_chunks, num_txq, num_rxq, num_q;
 struct idpf_vc_xn_params xn_params = {};
 struct virtchnl2_queue_chunks *qcs;
 u32 config_sz, chunk_sz, buf_sz;
 ssize_t reply_sz;
 int i, j, k = 0;

 num_txq = vport->num_txq + vport->num_complq;
 num_rxq = vport->num_rxq + vport->num_bufq;
 num_q = num_txq + num_rxq;
 buf_sz = sizeof(struct virtchnl2_queue_chunk) * num_q;
 qc = kzalloc(buf_sz, GFP_KERNEL);
 if (!qc)
  return -ENOMEM;

 for (i = 0; i < vport->num_txq_grp; i++) {
  struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];

  for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
   qc[k].type = cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_TX);
   qc[k].start_queue_id = cpu_to_le32(tx_qgrp->txqs[j]->q_id);
   qc[k].num_queues = cpu_to_le32(IDPF_NUMQ_PER_CHUNK);
  }
 }
 if (vport->num_txq != k)
  return -EINVAL;

 if (!idpf_is_queue_model_split(vport->txq_model))
  goto setup_rx;

 for (i = 0; i < vport->num_txq_grp; i++, k++) {
  struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];

  qc[k].type = cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION);
  qc[k].start_queue_id = cpu_to_le32(tx_qgrp->complq->q_id);
  qc[k].num_queues = cpu_to_le32(IDPF_NUMQ_PER_CHUNK);
 }
 if (vport->num_complq != (k - vport->num_txq))
  return -EINVAL;

setup_rx:
 for (i = 0; i < vport->num_rxq_grp; i++) {
  struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];

  if (idpf_is_queue_model_split(vport->rxq_model))
   num_rxq = rx_qgrp->splitq.num_rxq_sets;
  else
   num_rxq = rx_qgrp->singleq.num_rxq;

  for (j = 0; j < num_rxq; j++, k++) {
   if (idpf_is_queue_model_split(vport->rxq_model)) {
    qc[k].start_queue_id =
    cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]->rxq.q_id);
    qc[k].type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_RX);
   } else {
    qc[k].start_queue_id =
    cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_id);
    qc[k].type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_RX);
   }
   qc[k].num_queues = cpu_to_le32(IDPF_NUMQ_PER_CHUNK);
  }
 }
 if (vport->num_rxq != k - (vport->num_txq + vport->num_complq))
  return -EINVAL;

 if (!idpf_is_queue_model_split(vport->rxq_model))
  goto send_msg;

 for (i = 0; i < vport->num_rxq_grp; i++) {
  struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];

  for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
   const struct idpf_buf_queue *q;

   q = &rx_qgrp->splitq.bufq_sets[j].bufq;
   qc[k].type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_RX_BUFFER);
   qc[k].start_queue_id = cpu_to_le32(q->q_id);
   qc[k].num_queues = cpu_to_le32(IDPF_NUMQ_PER_CHUNK);
  }
 }
 if (vport->num_bufq != k - (vport->num_txq +
        vport->num_complq +
        vport->num_rxq))
  return -EINVAL;

send_msg:
 /* Chunk up the queue info into multiple messages */
 config_sz = sizeof(struct virtchnl2_del_ena_dis_queues);
 chunk_sz = sizeof(struct virtchnl2_queue_chunk);

 num_chunks = min_t(u32, IDPF_NUM_CHUNKS_PER_MSG(config_sz, chunk_sz),
      num_q);
 num_msgs = DIV_ROUND_UP(num_q, num_chunks);

 buf_sz = struct_size(eq, chunks.chunks, num_chunks);
 eq = kzalloc(buf_sz, GFP_KERNEL);
 if (!eq)
  return -ENOMEM;

 if (ena) {
  xn_params.vc_op = VIRTCHNL2_OP_ENABLE_QUEUES;
  xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
 } else {
  xn_params.vc_op = VIRTCHNL2_OP_DISABLE_QUEUES;
  xn_params.timeout_ms = IDPF_VC_XN_MIN_TIMEOUT_MSEC;
 }

 for (i = 0, k = 0; i < num_msgs; i++) {
  memset(eq, 0, buf_sz);
  eq->vport_id = cpu_to_le32(vport->vport_id);
  eq->chunks.num_chunks = cpu_to_le16(num_chunks);
  qcs = &eq->chunks;
  memcpy(qcs->chunks, &qc[k], chunk_sz * num_chunks);

  xn_params.send_buf.iov_base = eq;
  xn_params.send_buf.iov_len = buf_sz;
  reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
  if (reply_sz < 0)
   return reply_sz;

  k += num_chunks;
  num_q -= num_chunks;
  num_chunks = min(num_chunks, num_q);
  /* Recalculate buffer size */
  buf_sz = struct_size(eq, chunks.chunks, num_chunks);
 }

 return 0;
}

/**
 * idpf_send_map_unmap_queue_vector_msg - Send virtchnl map or unmap queue
 * vector message
 * @vport: virtual port data structure
 * @map: true for map and false for unmap
 *
 * Send map or unmap queue vector virtchnl message.  Returns 0 on success,
 * negative on failure.
 */

int idpf_send_map_unmap_queue_vector_msg(struct idpf_vport *vport, bool map)
{
 struct virtchnl2_queue_vector_maps *vqvm __free(kfree) = NULL;
 struct virtchnl2_queue_vector *vqv __free(kfree) = NULL;
 struct idpf_vc_xn_params xn_params = {};
 u32 config_sz, chunk_sz, buf_sz;
 u32 num_msgs, num_chunks, num_q;
 ssize_t reply_sz;
 int i, j, k = 0;

 num_q = vport->num_txq + vport->num_rxq;

 buf_sz = sizeof(struct virtchnl2_queue_vector) * num_q;
 vqv = kzalloc(buf_sz, GFP_KERNEL);
 if (!vqv)
  return -ENOMEM;

 for (i = 0; i < vport->num_txq_grp; i++) {
  struct idpf_txq_group *tx_qgrp = &vport->txq_grps[i];

  for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
   vqv[k].queue_type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_TX);
   vqv[k].queue_id = cpu_to_le32(tx_qgrp->txqs[j]->q_id);

   if (idpf_is_queue_model_split(vport->txq_model)) {
    vqv[k].vector_id =
    cpu_to_le16(tx_qgrp->complq->q_vector->v_idx);
    vqv[k].itr_idx =
    cpu_to_le32(tx_qgrp->complq->q_vector->tx_itr_idx);
   } else {
    vqv[k].vector_id =
    cpu_to_le16(tx_qgrp->txqs[j]->q_vector->v_idx);
    vqv[k].itr_idx =
    cpu_to_le32(tx_qgrp->txqs[j]->q_vector->tx_itr_idx);
   }
  }
 }

 if (vport->num_txq != k)
  return -EINVAL;

 for (i = 0; i < vport->num_rxq_grp; i++) {
  struct idpf_rxq_group *rx_qgrp = &vport->rxq_grps[i];
  u16 num_rxq;

  if (idpf_is_queue_model_split(vport->rxq_model))
   num_rxq = rx_qgrp->splitq.num_rxq_sets;
  else
   num_rxq = rx_qgrp->singleq.num_rxq;

  for (j = 0; j < num_rxq; j++, k++) {
   struct idpf_rx_queue *rxq;

   if (idpf_is_queue_model_split(vport->rxq_model))
    rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
   else
    rxq = rx_qgrp->singleq.rxqs[j];

   vqv[k].queue_type =
    cpu_to_le32(VIRTCHNL2_QUEUE_TYPE_RX);
   vqv[k].queue_id = cpu_to_le32(rxq->q_id);
   vqv[k].vector_id = cpu_to_le16(rxq->q_vector->v_idx);
   vqv[k].itr_idx = cpu_to_le32(rxq->q_vector->rx_itr_idx);
  }
 }

 if (idpf_is_queue_model_split(vport->txq_model)) {
  if (vport->num_rxq != k - vport->num_complq)
   return -EINVAL;
 } else {
  if (vport->num_rxq != k - vport->num_txq)
   return -EINVAL;
 }

 /* Chunk up the vector info into multiple messages */
 config_sz = sizeof(struct virtchnl2_queue_vector_maps);
 chunk_sz = sizeof(struct virtchnl2_queue_vector);

 num_chunks = min_t(u32, IDPF_NUM_CHUNKS_PER_MSG(config_sz, chunk_sz),
      num_q);
 num_msgs = DIV_ROUND_UP(num_q, num_chunks);

 buf_sz = struct_size(vqvm, qv_maps, num_chunks);
 vqvm = kzalloc(buf_sz, GFP_KERNEL);
 if (!vqvm)
  return -ENOMEM;

 if (map) {
  xn_params.vc_op = VIRTCHNL2_OP_MAP_QUEUE_VECTOR;
  xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
 } else {
  xn_params.vc_op = VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR;
  xn_params.timeout_ms = IDPF_VC_XN_MIN_TIMEOUT_MSEC;
 }

 for (i = 0, k = 0; i < num_msgs; i++) {
  memset(vqvm, 0, buf_sz);
  xn_params.send_buf.iov_base = vqvm;
  xn_params.send_buf.iov_len = buf_sz;
  vqvm->vport_id = cpu_to_le32(vport->vport_id);
  vqvm->num_qv_maps = cpu_to_le16(num_chunks);
  memcpy(vqvm->qv_maps, &vqv[k], chunk_sz * num_chunks);

  reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);
  if (reply_sz < 0)
   return reply_sz;

  k += num_chunks;
  num_q -= num_chunks;
  num_chunks = min(num_chunks, num_q);
  /* Recalculate buffer size */
  buf_sz = struct_size(vqvm, qv_maps, num_chunks);
 }

 return 0;
}

/**
 * idpf_send_enable_queues_msg - send enable queues virtchnl message
 * @vport: Virtual port private data structure
 *
 * Will send enable queues virtchnl message.  Returns 0 on success, negative on
 * failure.
 */

int idpf_send_enable_queues_msg(struct idpf_vport *vport)
{
 return idpf_send_ena_dis_queues_msg(vport, true);
}

/**
 * idpf_send_disable_queues_msg - send disable queues virtchnl message
 * @vport: Virtual port private data structure
 *
 * Will send disable queues virtchnl message.  Returns 0 on success, negative
 * on failure.
 */

int idpf_send_disable_queues_msg(struct idpf_vport *vport)
{
 int err, i;

 err = idpf_send_ena_dis_queues_msg(vport, false);
 if (err)
  return err;

 /* switch to poll mode as interrupts will be disabled after disable
 * queues virtchnl message is sent
 */

 for (i = 0; i < vport->num_txq; i++)
  idpf_queue_set(POLL_MODE, vport->txqs[i]);

 /* schedule the napi to receive all the marker packets */
 local_bh_disable();
 for (i = 0; i < vport->num_q_vectors; i++)
  napi_schedule(&vport->q_vectors[i].napi);
 local_bh_enable();

 return idpf_wait_for_marker_event(vport);
}

/**
 * idpf_convert_reg_to_queue_chunks - Copy queue chunk information to the right
 * structure
 * @dchunks: Destination chunks to store data to
 * @schunks: Source chunks to copy data from
 * @num_chunks: number of chunks to copy
 */

static void idpf_convert_reg_to_queue_chunks(struct virtchnl2_queue_chunk *dchunks,
          struct virtchnl2_queue_reg_chunk *schunks,
          u16 num_chunks)
{
 u16 i;

 for (i = 0; i < num_chunks; i++) {
  dchunks[i].type = schunks[i].type;
  dchunks[i].start_queue_id = schunks[i].start_queue_id;
  dchunks[i].num_queues = schunks[i].num_queues;
 }
}

/**
 * idpf_send_delete_queues_msg - send delete queues virtchnl message
 * @vport: Virtual port private data structure
 *
 * Will send delete queues virtchnl message. Return 0 on success, negative on
 * failure.
 */

int idpf_send_delete_queues_msg(struct idpf_vport *vport)
{
 struct virtchnl2_del_ena_dis_queues *eq __free(kfree) = NULL;
 struct virtchnl2_create_vport *vport_params;
 struct virtchnl2_queue_reg_chunks *chunks;
 struct idpf_vc_xn_params xn_params = {};
 struct idpf_vport_config *vport_config;
 u16 vport_idx = vport->idx;
 ssize_t reply_sz;
 u16 num_chunks;
 int buf_size;

 vport_config = vport->adapter->vport_config[vport_idx];
 if (vport_config->req_qs_chunks) {
  chunks = &vport_config->req_qs_chunks->chunks;
 } else {
  vport_params = vport->adapter->vport_params_recvd[vport_idx];
  chunks = &vport_params->chunks;
 }

 num_chunks = le16_to_cpu(chunks->num_chunks);
 buf_size = struct_size(eq, chunks.chunks, num_chunks);

 eq = kzalloc(buf_size, GFP_KERNEL);
 if (!eq)
  return -ENOMEM;

 eq->vport_id = cpu_to_le32(vport->vport_id);
 eq->chunks.num_chunks = cpu_to_le16(num_chunks);

 idpf_convert_reg_to_queue_chunks(eq->chunks.chunks, chunks->chunks,
      num_chunks);

 xn_params.vc_op = VIRTCHNL2_OP_DEL_QUEUES;
 xn_params.timeout_ms = IDPF_VC_XN_MIN_TIMEOUT_MSEC;
 xn_params.send_buf.iov_base = eq;
 xn_params.send_buf.iov_len = buf_size;
 reply_sz = idpf_vc_xn_exec(vport->adapter, &xn_params);

 return reply_sz < 0 ? reply_sz : 0;
}

/**
 * idpf_send_config_queues_msg - Send config queues virtchnl message
 * @vport: Virtual port private data structure
 *
 * Will send config queues virtchnl message. Returns 0 on success, negative on
 * failure.
 */

int idpf_send_config_queues_msg(struct idpf_vport *vport)
{
 int err;

 err = idpf_send_config_tx_queues_msg(vport);
 if (err)
  return err;

 return idpf_send_config_rx_queues_msg(vport);
}

/**
 * idpf_send_add_queues_msg - Send virtchnl add queues message
 * @vport: Virtual port private data structure
 * @num_tx_q: number of transmit queues
 * @num_complq: number of transmit completion queues
 * @num_rx_q: number of receive queues
 * @num_rx_bufq: number of receive buffer queues
 *
 * Returns 0 on success, negative on failure. vport _MUST_ be const here as
 * we should not change any fields within vport itself in this function.
 */

int idpf_send_add_queues_msg(const struct idpf_vport *vport, u16 num_tx_q,
        u16 num_complq, u16 num_rx_q, u16 num_rx_bufq)
{
 struct virtchnl2_add_queues *vc_msg __free(kfree) = NULL;
 struct idpf_vc_xn_params xn_params = {};
 struct idpf_vport_config *vport_config;
 struct virtchnl2_add_queues aq = {};
 u16 vport_idx = vport->idx;
 ssize_t reply_sz;
 int size;

 vc_msg = kzalloc(IDPF_CTLQ_MAX_BUF_LEN, GFP_KERNEL);
 if (!vc_msg)
  return -ENOMEM;

 vport_config = vport->adapter->vport_config[vport_idx];
 kfree(vport_config->req_qs_chunks);
 vport_config->req_qs_chunks = NULL;

 aq.vport_id = cpu_to_le32(vport->vport_id);
 aq.num_tx_q = cpu_to_le16(num_tx_q);
 aq.num_tx_complq = cpu_to_le16(num_complq);
 aq.num_rx_q = cpu_to_le16(num_rx_q);
 aq.num_rx_bufq = cpu_to_le16(num_rx_bufq);

 xn_params.vc_op = VIRTCHNL2_OP_ADD_QUEUES;
 xn_params.timeout_ms = IDPF_VC_XN_DEFAULT_TIMEOUT_MSEC;
 xn_params.send_buf.iov_base = &aq;
 xn_params.send_buf.iov_len = sizeof(aq);
 xn_params.recv_buf.iov_base = vc_msg;
 xn_params.recv_buf.iov_len = IDPF_CTLQ_MAX_BUF_LEN;
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.23 Sekunden  ¤

*© 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.