This starts refactoring how virtchnl messages are handled by adding a transaction manager (idpf_vc_xn_manager). There are two primary motivations here which are to enable handling of multiple messages at once and to make it more robust in general. As it is right now, the driver may only have one pending message at a time and there's no guarantee that the response we receive was actually intended for the message we sent prior. This works by utilizing a "cookie" field of the message descriptor. It is arbitrary what data we put in the cookie and the response is required to have the same cookie the original message was sent with. Then using a "transaction" abstraction that uses the completion API to pair responses to the message it belongs to. The cookie works such that the first half is the index to the transaction in our array, and the second half is a "salt" that gets incremented every message. This enables quick lookups into the array and also ensuring we have the correct message. The salt is necessary because after, for example, a message times out and we deem the response was lost for some reason, we could theoretically reuse the same index but using a different salt ensures that when we do actually get a response it's not the old message that timed out previously finally coming in. Since the number of transactions allocated is U8_MAX and the salt is 8 bits, we can never have a conflict because we can't roll over the salt without using more transactions than we have available. This starts by only converting the VIRTCHNL2_OP_VERSION message to use this new transaction API. Follow up patches will convert all virtchnl messages to use the API. Tested-by: Alexander Lobakin <aleksander.lobakin@intel.com> Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com> Reviewed-by: Igor Bagnucki <igor.bagnucki@intel.com> Co-developed-by: Joshua Hay <joshua.a.hay@intel.com> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com> Signed-off-by: Alan Brady <alan.brady@intel.com> Tested-by: Krishneil Singh <krishneil.k.singh@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
164 lines
4.8 KiB
C
164 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (C) 2023 Intel Corporation */
|
|
|
|
#include "idpf.h"
|
|
#include "idpf_lan_vf_regs.h"
|
|
#include "idpf_virtchnl.h"
|
|
|
|
#define IDPF_VF_ITR_IDX_SPACING 0x40
|
|
|
|
/**
|
|
* idpf_vf_ctlq_reg_init - initialize default mailbox registers
|
|
* @cq: pointer to the array of create control queues
|
|
*/
|
|
static void idpf_vf_ctlq_reg_init(struct idpf_ctlq_create_info *cq)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IDPF_NUM_DFLT_MBX_Q; i++) {
|
|
struct idpf_ctlq_create_info *ccq = cq + i;
|
|
|
|
switch (ccq->type) {
|
|
case IDPF_CTLQ_TYPE_MAILBOX_TX:
|
|
/* set head and tail registers in our local struct */
|
|
ccq->reg.head = VF_ATQH;
|
|
ccq->reg.tail = VF_ATQT;
|
|
ccq->reg.len = VF_ATQLEN;
|
|
ccq->reg.bah = VF_ATQBAH;
|
|
ccq->reg.bal = VF_ATQBAL;
|
|
ccq->reg.len_mask = VF_ATQLEN_ATQLEN_M;
|
|
ccq->reg.len_ena_mask = VF_ATQLEN_ATQENABLE_M;
|
|
ccq->reg.head_mask = VF_ATQH_ATQH_M;
|
|
break;
|
|
case IDPF_CTLQ_TYPE_MAILBOX_RX:
|
|
/* set head and tail registers in our local struct */
|
|
ccq->reg.head = VF_ARQH;
|
|
ccq->reg.tail = VF_ARQT;
|
|
ccq->reg.len = VF_ARQLEN;
|
|
ccq->reg.bah = VF_ARQBAH;
|
|
ccq->reg.bal = VF_ARQBAL;
|
|
ccq->reg.len_mask = VF_ARQLEN_ARQLEN_M;
|
|
ccq->reg.len_ena_mask = VF_ARQLEN_ARQENABLE_M;
|
|
ccq->reg.head_mask = VF_ARQH_ARQH_M;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* idpf_vf_mb_intr_reg_init - Initialize the mailbox register
|
|
* @adapter: adapter structure
|
|
*/
|
|
static void idpf_vf_mb_intr_reg_init(struct idpf_adapter *adapter)
|
|
{
|
|
struct idpf_intr_reg *intr = &adapter->mb_vector.intr_reg;
|
|
u32 dyn_ctl = le32_to_cpu(adapter->caps.mailbox_dyn_ctl);
|
|
|
|
intr->dyn_ctl = idpf_get_reg_addr(adapter, dyn_ctl);
|
|
intr->dyn_ctl_intena_m = VF_INT_DYN_CTL0_INTENA_M;
|
|
intr->dyn_ctl_itridx_m = VF_INT_DYN_CTL0_ITR_INDX_M;
|
|
intr->icr_ena = idpf_get_reg_addr(adapter, VF_INT_ICR0_ENA1);
|
|
intr->icr_ena_ctlq_m = VF_INT_ICR0_ENA1_ADMINQ_M;
|
|
}
|
|
|
|
/**
|
|
* idpf_vf_intr_reg_init - Initialize interrupt registers
|
|
* @vport: virtual port structure
|
|
*/
|
|
static int idpf_vf_intr_reg_init(struct idpf_vport *vport)
|
|
{
|
|
struct idpf_adapter *adapter = vport->adapter;
|
|
int num_vecs = vport->num_q_vectors;
|
|
struct idpf_vec_regs *reg_vals;
|
|
int num_regs, i, err = 0;
|
|
u32 rx_itr, tx_itr;
|
|
u16 total_vecs;
|
|
|
|
total_vecs = idpf_get_reserved_vecs(vport->adapter);
|
|
reg_vals = kcalloc(total_vecs, sizeof(struct idpf_vec_regs),
|
|
GFP_KERNEL);
|
|
if (!reg_vals)
|
|
return -ENOMEM;
|
|
|
|
num_regs = idpf_get_reg_intr_vecs(vport, reg_vals);
|
|
if (num_regs < num_vecs) {
|
|
err = -EINVAL;
|
|
goto free_reg_vals;
|
|
}
|
|
|
|
for (i = 0; i < num_vecs; i++) {
|
|
struct idpf_q_vector *q_vector = &vport->q_vectors[i];
|
|
u16 vec_id = vport->q_vector_idxs[i] - IDPF_MBX_Q_VEC;
|
|
struct idpf_intr_reg *intr = &q_vector->intr_reg;
|
|
u32 spacing;
|
|
|
|
intr->dyn_ctl = idpf_get_reg_addr(adapter,
|
|
reg_vals[vec_id].dyn_ctl_reg);
|
|
intr->dyn_ctl_intena_m = VF_INT_DYN_CTLN_INTENA_M;
|
|
intr->dyn_ctl_itridx_s = VF_INT_DYN_CTLN_ITR_INDX_S;
|
|
|
|
spacing = IDPF_ITR_IDX_SPACING(reg_vals[vec_id].itrn_index_spacing,
|
|
IDPF_VF_ITR_IDX_SPACING);
|
|
rx_itr = VF_INT_ITRN_ADDR(VIRTCHNL2_ITR_IDX_0,
|
|
reg_vals[vec_id].itrn_reg,
|
|
spacing);
|
|
tx_itr = VF_INT_ITRN_ADDR(VIRTCHNL2_ITR_IDX_1,
|
|
reg_vals[vec_id].itrn_reg,
|
|
spacing);
|
|
intr->rx_itr = idpf_get_reg_addr(adapter, rx_itr);
|
|
intr->tx_itr = idpf_get_reg_addr(adapter, tx_itr);
|
|
}
|
|
|
|
free_reg_vals:
|
|
kfree(reg_vals);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* idpf_vf_reset_reg_init - Initialize reset registers
|
|
* @adapter: Driver specific private structure
|
|
*/
|
|
static void idpf_vf_reset_reg_init(struct idpf_adapter *adapter)
|
|
{
|
|
adapter->reset_reg.rstat = idpf_get_reg_addr(adapter, VFGEN_RSTAT);
|
|
adapter->reset_reg.rstat_m = VFGEN_RSTAT_VFR_STATE_M;
|
|
}
|
|
|
|
/**
|
|
* idpf_vf_trigger_reset - trigger reset
|
|
* @adapter: Driver specific private structure
|
|
* @trig_cause: Reason to trigger a reset
|
|
*/
|
|
static void idpf_vf_trigger_reset(struct idpf_adapter *adapter,
|
|
enum idpf_flags trig_cause)
|
|
{
|
|
/* Do not send VIRTCHNL2_OP_RESET_VF message on driver unload */
|
|
if (trig_cause == IDPF_HR_FUNC_RESET &&
|
|
!test_bit(IDPF_REMOVE_IN_PROG, adapter->flags))
|
|
idpf_send_mb_msg(adapter, VIRTCHNL2_OP_RESET_VF, 0, NULL, 0);
|
|
}
|
|
|
|
/**
|
|
* idpf_vf_reg_ops_init - Initialize register API function pointers
|
|
* @adapter: Driver specific private structure
|
|
*/
|
|
static void idpf_vf_reg_ops_init(struct idpf_adapter *adapter)
|
|
{
|
|
adapter->dev_ops.reg_ops.ctlq_reg_init = idpf_vf_ctlq_reg_init;
|
|
adapter->dev_ops.reg_ops.intr_reg_init = idpf_vf_intr_reg_init;
|
|
adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_vf_mb_intr_reg_init;
|
|
adapter->dev_ops.reg_ops.reset_reg_init = idpf_vf_reset_reg_init;
|
|
adapter->dev_ops.reg_ops.trigger_reset = idpf_vf_trigger_reset;
|
|
}
|
|
|
|
/**
|
|
* idpf_vf_dev_ops_init - Initialize device API function pointers
|
|
* @adapter: Driver specific private structure
|
|
*/
|
|
void idpf_vf_dev_ops_init(struct idpf_adapter *adapter)
|
|
{
|
|
idpf_vf_reg_ops_init(adapter);
|
|
}
|