freebsd-dev/sys/dev/iavf/iavf_vc_common.c
Eric Joyner ca853dee3b
iavf(4): Split source and update to 3.0.26-k
The iavf(4) driver now uses a different source base from ixl(4), since
it will be the standard VF driver for new Intel Ethernet products going
forward, including ice(4). It continues to use the iflib framework
for network drivers.

Since it now uses a different source code base, this commit adds a new
sys/dev/iavf entry, but it re-uses the existing module name so no
configuration changes are necessary.

Signed-off-by: Eric Joyner <erj@FreeBSD.org>

Reviewed by:		kbowling@
Tested by:		lukasz.szczepaniak@intel.com
Sponsored by:		Intel Corporation
Differential Revision:	https://reviews.freebsd.org/D28636
2021-11-24 11:54:08 -08:00

1331 lines
35 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright (c) 2021, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*$FreeBSD$*/
/**
* @file iavf_vc_common.c
* @brief Common virtchnl interface functions
*
* Contains functions implementing the virtchnl interface for connecting to
* the PF driver. This file contains the functions which are common between
* the legacy and iflib driver implementations.
*/
#include "iavf_vc_common.h"
/* busy wait delay in msec */
#define IAVF_BUSY_WAIT_DELAY 10
#define IAVF_BUSY_WAIT_COUNT 50
/* Static function decls */
static void iavf_handle_link_event(struct iavf_sc *sc,
struct virtchnl_pf_event *vpe);
/**
* iavf_send_pf_msg - Send virtchnl message to PF device
* @sc: device softc
* @op: the op to send
* @msg: message contents
* @len: length of the message
*
* Send a message to the PF device over the virtchnl connection. Print
* a status code if the message reports an error.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_send_pf_msg(struct iavf_sc *sc,
enum virtchnl_ops op, u8 *msg, u16 len)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
enum iavf_status status;
int val_err;
/* Validating message before sending it to the PF */
val_err = virtchnl_vc_validate_vf_msg(&sc->version, op, msg, len);
if (val_err)
device_printf(dev, "Error validating msg to PF for op %d,"
" msglen %d: error %d\n", op, len, val_err);
if (!iavf_check_asq_alive(hw)) {
if (op != VIRTCHNL_OP_GET_STATS)
device_printf(dev, "Unable to send opcode %s to PF, "
"ASQ is not alive\n", iavf_vc_opcode_str(op));
return (0);
}
if (op != VIRTCHNL_OP_GET_STATS)
iavf_dbg_vc(sc,
"Sending msg (op=%s[%d]) to PF\n",
iavf_vc_opcode_str(op), op);
status = iavf_aq_send_msg_to_pf(hw, op, IAVF_SUCCESS, msg, len, NULL);
if (status && op != VIRTCHNL_OP_GET_STATS)
device_printf(dev, "Unable to send opcode %s to PF, "
"status %s, aq error %s\n",
iavf_vc_opcode_str(op),
iavf_stat_str(hw, status),
iavf_aq_str(hw, hw->aq.asq_last_status));
return (status);
}
/**
* iavf_send_api_ver - Send the API version we support to the PF
* @sc: device softc
*
* Send API version admin queue message to the PF. The reply is not checked
* in this function.
*
* @returns 0 if the message was successfully sent, or one of the
* IAVF_ADMIN_QUEUE_ERROR_ statuses if not.
*/
int
iavf_send_api_ver(struct iavf_sc *sc)
{
struct virtchnl_version_info vvi;
vvi.major = VIRTCHNL_VERSION_MAJOR;
vvi.minor = VIRTCHNL_VERSION_MINOR;
return iavf_send_pf_msg(sc, VIRTCHNL_OP_VERSION,
(u8 *)&vvi, sizeof(vvi));
}
/**
* iavf_verify_api_ver - Verify the PF supports our API version
* @sc: device softc
*
* Compare API versions with the PF. Must be called after admin queue is
* initialized.
*
* @returns 0 if API versions match, EIO if they do not, or
* IAVF_ERR_ADMIN_QUEUE_NO_WORK if the admin queue is empty.
*/
int
iavf_verify_api_ver(struct iavf_sc *sc)
{
struct virtchnl_version_info *pf_vvi;
struct iavf_hw *hw = &sc->hw;
struct iavf_arq_event_info event;
enum iavf_status status;
device_t dev = sc->dev;
int error = 0;
int retries = 0;
event.buf_len = IAVF_AQ_BUF_SZ;
event.msg_buf = (u8 *)malloc(event.buf_len, M_IAVF, M_WAITOK);
for (;;) {
if (++retries > IAVF_AQ_MAX_ERR)
goto out_alloc;
/* Initial delay here is necessary */
iavf_msec_pause(100);
status = iavf_clean_arq_element(hw, &event, NULL);
if (status == IAVF_ERR_ADMIN_QUEUE_NO_WORK)
continue;
else if (status) {
error = EIO;
goto out_alloc;
}
if ((enum virtchnl_ops)le32toh(event.desc.cookie_high) !=
VIRTCHNL_OP_VERSION) {
iavf_dbg_vc(sc, "%s: Received unexpected op response: %d\n",
__func__, le32toh(event.desc.cookie_high));
/* Don't stop looking for expected response */
continue;
}
status = (enum iavf_status)le32toh(event.desc.cookie_low);
if (status) {
error = EIO;
goto out_alloc;
} else
break;
}
pf_vvi = (struct virtchnl_version_info *)event.msg_buf;
if ((pf_vvi->major > VIRTCHNL_VERSION_MAJOR) ||
((pf_vvi->major == VIRTCHNL_VERSION_MAJOR) &&
(pf_vvi->minor > VIRTCHNL_VERSION_MINOR))) {
device_printf(dev, "Critical PF/VF API version mismatch!\n");
error = EIO;
} else {
sc->version.major = pf_vvi->major;
sc->version.minor = pf_vvi->minor;
}
/* Log PF/VF api versions */
device_printf(dev, "PF API %d.%d / VF API %d.%d\n",
pf_vvi->major, pf_vvi->minor,
VIRTCHNL_VERSION_MAJOR, VIRTCHNL_VERSION_MINOR);
out_alloc:
free(event.msg_buf, M_IAVF);
return (error);
}
/**
* iavf_send_vf_config_msg - Send VF configuration request
* @sc: device softc
*
* Send VF configuration request admin queue message to the PF. The reply
* is not checked in this function.
*
* @returns 0 if the message was successfully sent, or one of the
* IAVF_ADMIN_QUEUE_ERROR_ statuses if not.
*/
int
iavf_send_vf_config_msg(struct iavf_sc *sc)
{
u32 caps;
/* Support the base mode functionality, as well as advanced
* speed reporting capability.
*/
caps = VF_BASE_MODE_OFFLOADS |
VIRTCHNL_VF_CAP_ADV_LINK_SPEED;
iavf_dbg_info(sc, "Sending offload flags: 0x%b\n",
caps, IAVF_PRINTF_VF_OFFLOAD_FLAGS);
if (sc->version.minor == VIRTCHNL_VERSION_MINOR_NO_VF_CAPS)
return iavf_send_pf_msg(sc, VIRTCHNL_OP_GET_VF_RESOURCES,
NULL, 0);
else
return iavf_send_pf_msg(sc, VIRTCHNL_OP_GET_VF_RESOURCES,
(u8 *)&caps, sizeof(caps));
}
/**
* iavf_get_vf_config - Get the VF configuration from the PF
* @sc: device softc
*
* Get VF configuration from PF and populate hw structure. Must be called after
* admin queue is initialized. Busy waits until response is received from PF,
* with maximum timeout. Response from PF is returned in the buffer for further
* processing by the caller.
*
* @returns zero on success, or an error code on failure
*/
int
iavf_get_vf_config(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
enum iavf_status status = IAVF_SUCCESS;
struct iavf_arq_event_info event;
u16 len;
u32 retries = 0;
int error = 0;
/* Note this assumes a single VSI */
len = sizeof(struct virtchnl_vf_resource) +
sizeof(struct virtchnl_vsi_resource);
event.buf_len = len;
event.msg_buf = (u8 *)malloc(event.buf_len, M_IAVF, M_WAITOK);
for (;;) {
status = iavf_clean_arq_element(hw, &event, NULL);
if (status == IAVF_ERR_ADMIN_QUEUE_NO_WORK) {
if (++retries <= IAVF_AQ_MAX_ERR)
iavf_msec_pause(10);
} else if ((enum virtchnl_ops)le32toh(event.desc.cookie_high) !=
VIRTCHNL_OP_GET_VF_RESOURCES) {
iavf_dbg_vc(sc, "%s: Received a response from PF,"
" opcode %d, error %d",
__func__,
le32toh(event.desc.cookie_high),
le32toh(event.desc.cookie_low));
retries++;
continue;
} else {
status = (enum iavf_status)le32toh(event.desc.cookie_low);
if (status) {
device_printf(dev, "%s: Error returned from PF,"
" opcode %d, error %d\n", __func__,
le32toh(event.desc.cookie_high),
le32toh(event.desc.cookie_low));
error = EIO;
goto out_alloc;
}
/* We retrieved the config message, with no errors */
break;
}
if (retries > IAVF_AQ_MAX_ERR) {
iavf_dbg_vc(sc,
"%s: Did not receive response after %d tries.",
__func__, retries);
error = ETIMEDOUT;
goto out_alloc;
}
}
memcpy(sc->vf_res, event.msg_buf, min(event.msg_len, len));
iavf_vf_parse_hw_config(hw, sc->vf_res);
out_alloc:
free(event.msg_buf, M_IAVF);
return (error);
}
/**
* iavf_enable_queues - Enable queues
* @sc: device softc
*
* Request that the PF enable all of our queues.
*
* @remark the reply from the PF is not checked by this function.
*
* @returns zero
*/
int
iavf_enable_queues(struct iavf_sc *sc)
{
struct virtchnl_queue_select vqs;
struct iavf_vsi *vsi = &sc->vsi;
vqs.vsi_id = sc->vsi_res->vsi_id;
vqs.tx_queues = (1 << IAVF_NTXQS(vsi)) - 1;
vqs.rx_queues = vqs.tx_queues;
iavf_send_pf_msg(sc, VIRTCHNL_OP_ENABLE_QUEUES,
(u8 *)&vqs, sizeof(vqs));
return (0);
}
/**
* iavf_disable_queues - Disable queues
* @sc: device softc
*
* Request that the PF disable all of our queues.
*
* @remark the reply from the PF is not checked by this function.
*
* @returns zero
*/
int
iavf_disable_queues(struct iavf_sc *sc)
{
struct virtchnl_queue_select vqs;
struct iavf_vsi *vsi = &sc->vsi;
vqs.vsi_id = sc->vsi_res->vsi_id;
vqs.tx_queues = (1 << IAVF_NTXQS(vsi)) - 1;
vqs.rx_queues = vqs.tx_queues;
iavf_send_pf_msg(sc, VIRTCHNL_OP_DISABLE_QUEUES,
(u8 *)&vqs, sizeof(vqs));
return (0);
}
/**
* iavf_add_vlans - Add VLAN filters
* @sc: device softc
*
* Scan the Filter List looking for vlans that need
* to be added, then create the data to hand to the AQ
* for handling.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_add_vlans(struct iavf_sc *sc)
{
struct virtchnl_vlan_filter_list *v;
struct iavf_vlan_filter *f, *ftmp;
device_t dev = sc->dev;
int i = 0, cnt = 0;
u32 len;
/* Get count of VLAN filters to add */
SLIST_FOREACH(f, sc->vlan_filters, next) {
if (f->flags & IAVF_FILTER_ADD)
cnt++;
}
if (!cnt) /* no work... */
return (ENOENT);
len = sizeof(struct virtchnl_vlan_filter_list) +
(cnt * sizeof(u16));
if (len > IAVF_AQ_BUF_SZ) {
device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
__func__);
return (EFBIG);
}
v = (struct virtchnl_vlan_filter_list *)malloc(len, M_IAVF, M_NOWAIT | M_ZERO);
if (!v) {
device_printf(dev, "%s: unable to allocate memory\n",
__func__);
return (ENOMEM);
}
v->vsi_id = sc->vsi_res->vsi_id;
v->num_elements = cnt;
/* Scan the filter array */
SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
if (f->flags & IAVF_FILTER_ADD) {
bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
f->flags = IAVF_FILTER_USED;
i++;
}
if (i == cnt)
break;
}
iavf_send_pf_msg(sc, VIRTCHNL_OP_ADD_VLAN, (u8 *)v, len);
free(v, M_IAVF);
/* add stats? */
return (0);
}
/**
* iavf_del_vlans - Delete VLAN filters
* @sc: device softc
*
* Scan the Filter Table looking for vlans that need
* to be removed, then create the data to hand to the AQ
* for handling.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_del_vlans(struct iavf_sc *sc)
{
struct virtchnl_vlan_filter_list *v;
struct iavf_vlan_filter *f, *ftmp;
device_t dev = sc->dev;
int i = 0, cnt = 0;
u32 len;
/* Get count of VLAN filters to delete */
SLIST_FOREACH(f, sc->vlan_filters, next) {
if (f->flags & IAVF_FILTER_DEL)
cnt++;
}
if (!cnt) /* no work... */
return (ENOENT);
len = sizeof(struct virtchnl_vlan_filter_list) +
(cnt * sizeof(u16));
if (len > IAVF_AQ_BUF_SZ) {
device_printf(dev, "%s: Exceeded Max AQ Buf size\n",
__func__);
return (EFBIG);
}
v = (struct virtchnl_vlan_filter_list *)
malloc(len, M_IAVF, M_NOWAIT | M_ZERO);
if (!v) {
device_printf(dev, "%s: unable to allocate memory\n",
__func__);
return (ENOMEM);
}
v->vsi_id = sc->vsi_res->vsi_id;
v->num_elements = cnt;
/* Scan the filter array */
SLIST_FOREACH_SAFE(f, sc->vlan_filters, next, ftmp) {
if (f->flags & IAVF_FILTER_DEL) {
bcopy(&f->vlan, &v->vlan_id[i], sizeof(u16));
i++;
SLIST_REMOVE(sc->vlan_filters, f, iavf_vlan_filter, next);
free(f, M_IAVF);
}
if (i == cnt)
break;
}
iavf_send_pf_msg(sc, VIRTCHNL_OP_DEL_VLAN, (u8 *)v, len);
free(v, M_IAVF);
/* add stats? */
return (0);
}
/**
* iavf_add_ether_filters - Add MAC filters
* @sc: device softc
*
* This routine takes additions to the vsi filter
* table and creates an Admin Queue call to create
* the filters in the hardware.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_add_ether_filters(struct iavf_sc *sc)
{
struct virtchnl_ether_addr_list *a;
struct iavf_mac_filter *f;
device_t dev = sc->dev;
int len, j = 0, cnt = 0;
int error;
/* Get count of MAC addresses to add */
SLIST_FOREACH(f, sc->mac_filters, next) {
if (f->flags & IAVF_FILTER_ADD)
cnt++;
}
if (cnt == 0) { /* Should not happen... */
iavf_dbg_vc(sc, "%s: cnt == 0, exiting...\n", __func__);
return (ENOENT);
}
len = sizeof(struct virtchnl_ether_addr_list) +
(cnt * sizeof(struct virtchnl_ether_addr));
a = (struct virtchnl_ether_addr_list *)
malloc(len, M_IAVF, M_NOWAIT | M_ZERO);
if (a == NULL) {
device_printf(dev, "%s: Failed to get memory for "
"virtchnl_ether_addr_list\n", __func__);
return (ENOMEM);
}
a->vsi_id = sc->vsi.id;
a->num_elements = cnt;
/* Scan the filter array */
SLIST_FOREACH(f, sc->mac_filters, next) {
if (f->flags & IAVF_FILTER_ADD) {
bcopy(f->macaddr, a->list[j].addr, ETHER_ADDR_LEN);
f->flags &= ~IAVF_FILTER_ADD;
j++;
iavf_dbg_vc(sc, "%s: ADD: " MAC_FORMAT "\n",
__func__, MAC_FORMAT_ARGS(f->macaddr));
}
if (j == cnt)
break;
}
iavf_dbg_vc(sc, "%s: len %d, j %d, cnt %d\n", __func__,
len, j, cnt);
error = iavf_send_pf_msg(sc,
VIRTCHNL_OP_ADD_ETH_ADDR, (u8 *)a, len);
/* add stats? */
free(a, M_IAVF);
return (error);
}
/**
* iavf_del_ether_filters - Delete MAC filters
* @sc: device softc
*
* This routine takes filters flagged for deletion in the
* sc MAC filter list and creates an Admin Queue call
* to delete those filters in the hardware.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_del_ether_filters(struct iavf_sc *sc)
{
struct virtchnl_ether_addr_list *d;
struct iavf_mac_filter *f, *f_temp;
device_t dev = sc->dev;
int len, j = 0, cnt = 0;
/* Get count of MAC addresses to delete */
SLIST_FOREACH(f, sc->mac_filters, next) {
if (f->flags & IAVF_FILTER_DEL)
cnt++;
}
if (cnt == 0) {
iavf_dbg_vc(sc, "%s: cnt == 0, exiting...\n", __func__);
return (ENOENT);
}
len = sizeof(struct virtchnl_ether_addr_list) +
(cnt * sizeof(struct virtchnl_ether_addr));
d = (struct virtchnl_ether_addr_list *)
malloc(len, M_IAVF, M_NOWAIT | M_ZERO);
if (d == NULL) {
device_printf(dev, "%s: Failed to get memory for "
"virtchnl_ether_addr_list\n", __func__);
return (ENOMEM);
}
d->vsi_id = sc->vsi.id;
d->num_elements = cnt;
/* Scan the filter array */
SLIST_FOREACH_SAFE(f, sc->mac_filters, next, f_temp) {
if (f->flags & IAVF_FILTER_DEL) {
bcopy(f->macaddr, d->list[j].addr, ETHER_ADDR_LEN);
iavf_dbg_vc(sc, "DEL: " MAC_FORMAT "\n",
MAC_FORMAT_ARGS(f->macaddr));
j++;
SLIST_REMOVE(sc->mac_filters, f, iavf_mac_filter, next);
free(f, M_IAVF);
}
if (j == cnt)
break;
}
iavf_send_pf_msg(sc,
VIRTCHNL_OP_DEL_ETH_ADDR, (u8 *)d, len);
/* add stats? */
free(d, M_IAVF);
return (0);
}
/**
* iavf_request_reset - Request a device reset
* @sc: device softc
*
* Request that the PF reset this VF. No response is expected.
*
* @returns zero
*/
int
iavf_request_reset(struct iavf_sc *sc)
{
/*
** Set the reset status to "in progress" before
** the request, this avoids any possibility of
** a mistaken early detection of completion.
*/
wr32(&sc->hw, IAVF_VFGEN_RSTAT, VIRTCHNL_VFR_INPROGRESS);
iavf_send_pf_msg(sc, VIRTCHNL_OP_RESET_VF, NULL, 0);
return (0);
}
/**
* iavf_request_stats - Request VF stats
* @sc: device softc
*
* Request the statistics for this VF's VSI from PF.
*
* @remark prints an error message on failure to obtain stats, but does not
* return with an error code.
*
* @returns zero
*/
int
iavf_request_stats(struct iavf_sc *sc)
{
struct virtchnl_queue_select vqs;
int error = 0;
vqs.vsi_id = sc->vsi_res->vsi_id;
/* Low priority, we don't need to error check */
error = iavf_send_pf_msg(sc, VIRTCHNL_OP_GET_STATS,
(u8 *)&vqs, sizeof(vqs));
if (error)
device_printf(sc->dev, "Error sending stats request to PF: %d\n", error);
return (0);
}
/**
* iavf_update_stats_counters - Update driver statistics
* @sc: device softc
* @es: ethernet stats storage
*
* Updates driver's stats counters with VSI stats returned from PF.
*/
void
iavf_update_stats_counters(struct iavf_sc *sc, struct iavf_eth_stats *es)
{
struct iavf_vsi *vsi = &sc->vsi;
uint64_t tx_discards;
tx_discards = es->tx_discards;
/* Update ifnet stats */
IAVF_SET_IPACKETS(vsi, es->rx_unicast +
es->rx_multicast +
es->rx_broadcast);
IAVF_SET_OPACKETS(vsi, es->tx_unicast +
es->tx_multicast +
es->tx_broadcast);
IAVF_SET_IBYTES(vsi, es->rx_bytes);
IAVF_SET_OBYTES(vsi, es->tx_bytes);
IAVF_SET_IMCASTS(vsi, es->rx_multicast);
IAVF_SET_OMCASTS(vsi, es->tx_multicast);
IAVF_SET_OERRORS(vsi, es->tx_errors);
IAVF_SET_IQDROPS(vsi, es->rx_discards);
IAVF_SET_OQDROPS(vsi, tx_discards);
IAVF_SET_NOPROTO(vsi, es->rx_unknown_protocol);
IAVF_SET_COLLISIONS(vsi, 0);
vsi->eth_stats = *es;
}
/**
* iavf_config_rss_key - Configure RSS key over virtchnl
* @sc: device softc
*
* Send a message to the PF to configure the RSS key using the virtchnl
* interface.
*
* @remark this does not check the reply from the PF.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_config_rss_key(struct iavf_sc *sc)
{
struct virtchnl_rss_key *rss_key_msg;
int msg_len, key_length;
u8 rss_seed[IAVF_RSS_KEY_SIZE];
#ifdef RSS
/* Fetch the configured RSS key */
rss_getkey((uint8_t *) &rss_seed);
#else
iavf_get_default_rss_key((u32 *)rss_seed);
#endif
/* Send the fetched key */
key_length = IAVF_RSS_KEY_SIZE;
msg_len = sizeof(struct virtchnl_rss_key) + (sizeof(u8) * key_length) - 1;
rss_key_msg = (struct virtchnl_rss_key *)
malloc(msg_len, M_IAVF, M_NOWAIT | M_ZERO);
if (rss_key_msg == NULL) {
device_printf(sc->dev, "Unable to allocate msg memory for RSS key msg.\n");
return (ENOMEM);
}
rss_key_msg->vsi_id = sc->vsi_res->vsi_id;
rss_key_msg->key_len = key_length;
bcopy(rss_seed, &rss_key_msg->key[0], key_length);
iavf_dbg_vc(sc, "%s: vsi_id %d, key_len %d\n", __func__,
rss_key_msg->vsi_id, rss_key_msg->key_len);
iavf_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_RSS_KEY,
(u8 *)rss_key_msg, msg_len);
free(rss_key_msg, M_IAVF);
return (0);
}
/**
* iavf_set_rss_hena - Configure the RSS HENA
* @sc: device softc
*
* Configure the RSS HENA values by sending a virtchnl message to the PF
*
* @remark the reply from the PF is not checked by this function.
*
* @returns zero
*/
int
iavf_set_rss_hena(struct iavf_sc *sc)
{
struct virtchnl_rss_hena hena;
struct iavf_hw *hw = &sc->hw;
if (hw->mac.type == IAVF_MAC_VF)
hena.hena = IAVF_DEFAULT_RSS_HENA_AVF;
else if (hw->mac.type == IAVF_MAC_X722_VF)
hena.hena = IAVF_DEFAULT_RSS_HENA_X722;
else
hena.hena = IAVF_DEFAULT_RSS_HENA_BASE;
iavf_send_pf_msg(sc, VIRTCHNL_OP_SET_RSS_HENA,
(u8 *)&hena, sizeof(hena));
return (0);
}
/**
* iavf_config_rss_lut - Configure RSS lookup table
* @sc: device softc
*
* Configure the RSS lookup table by sending a virtchnl message to the PF.
*
* @remark the reply from the PF is not checked in this function.
*
* @returns zero on success, or an error code on failure.
*/
int
iavf_config_rss_lut(struct iavf_sc *sc)
{
struct virtchnl_rss_lut *rss_lut_msg;
int msg_len;
u16 lut_length;
u32 lut;
int i, que_id;
lut_length = IAVF_RSS_VSI_LUT_SIZE;
msg_len = sizeof(struct virtchnl_rss_lut) + (lut_length * sizeof(u8)) - 1;
rss_lut_msg = (struct virtchnl_rss_lut *)
malloc(msg_len, M_IAVF, M_NOWAIT | M_ZERO);
if (rss_lut_msg == NULL) {
device_printf(sc->dev, "Unable to allocate msg memory for RSS lut msg.\n");
return (ENOMEM);
}
rss_lut_msg->vsi_id = sc->vsi_res->vsi_id;
/* Each LUT entry is a max of 1 byte, so this is easy */
rss_lut_msg->lut_entries = lut_length;
/* Populate the LUT with max no. of queues in round robin fashion */
for (i = 0; i < lut_length; i++) {
#ifdef RSS
/*
* Fetch the RSS bucket id for the given indirection entry.
* Cap it at the number of configured buckets (which is
* num_queues.)
*/
que_id = rss_get_indirection_to_bucket(i);
que_id = que_id % sc->vsi.num_rx_queues;
#else
que_id = i % sc->vsi.num_rx_queues;
#endif
lut = que_id & IAVF_RSS_VSI_LUT_ENTRY_MASK;
rss_lut_msg->lut[i] = lut;
}
iavf_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_RSS_LUT,
(u8 *)rss_lut_msg, msg_len);
free(rss_lut_msg, M_IAVF);
return (0);
}
/**
* iavf_config_promisc_mode - Configure promiscuous mode
* @sc: device softc
*
* Configure the device into promiscuous mode by sending a virtchnl message to
* the PF.
*
* @remark the reply from the PF is not checked in this function.
*
* @returns zero
*/
int
iavf_config_promisc_mode(struct iavf_sc *sc)
{
struct virtchnl_promisc_info pinfo;
pinfo.vsi_id = sc->vsi_res->vsi_id;
pinfo.flags = sc->promisc_flags;
iavf_send_pf_msg(sc, VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
(u8 *)&pinfo, sizeof(pinfo));
return (0);
}
/**
* iavf_vc_send_cmd - Convert request into virtchnl calls
* @sc: device softc
* @request: the requested command to run
*
* Send the proper virtchnl call based on the request value.
*
* @returns zero on success, or an error code on failure. Note that unknown
* requests will return zero.
*/
int
iavf_vc_send_cmd(struct iavf_sc *sc, uint32_t request)
{
switch (request) {
case IAVF_FLAG_AQ_MAP_VECTORS:
return iavf_map_queues(sc);
case IAVF_FLAG_AQ_ADD_MAC_FILTER:
return iavf_add_ether_filters(sc);
case IAVF_FLAG_AQ_ADD_VLAN_FILTER:
return iavf_add_vlans(sc);
case IAVF_FLAG_AQ_DEL_MAC_FILTER:
return iavf_del_ether_filters(sc);
case IAVF_FLAG_AQ_DEL_VLAN_FILTER:
return iavf_del_vlans(sc);
case IAVF_FLAG_AQ_CONFIGURE_QUEUES:
return iavf_configure_queues(sc);
case IAVF_FLAG_AQ_DISABLE_QUEUES:
return iavf_disable_queues(sc);
case IAVF_FLAG_AQ_ENABLE_QUEUES:
return iavf_enable_queues(sc);
case IAVF_FLAG_AQ_CONFIG_RSS_KEY:
return iavf_config_rss_key(sc);
case IAVF_FLAG_AQ_SET_RSS_HENA:
return iavf_set_rss_hena(sc);
case IAVF_FLAG_AQ_CONFIG_RSS_LUT:
return iavf_config_rss_lut(sc);
case IAVF_FLAG_AQ_CONFIGURE_PROMISC:
return iavf_config_promisc_mode(sc);
}
return (0);
}
/**
* iavf_vc_get_op_chan - Get op channel for a request
* @sc: device softc
* @request: the request type
*
* @returns the op channel for the given request, or NULL if no channel is
* used.
*/
void *
iavf_vc_get_op_chan(struct iavf_sc *sc, uint32_t request)
{
switch (request) {
case IAVF_FLAG_AQ_ENABLE_QUEUES:
return (&sc->enable_queues_chan);
case IAVF_FLAG_AQ_DISABLE_QUEUES:
return (&sc->disable_queues_chan);
default:
return (NULL);
}
}
/**
* iavf_vc_stat_str - convert virtchnl status err code to a string
* @hw: pointer to the HW structure
* @stat_err: the status error code to convert
*
* @returns the human readable string representing the specified error code.
**/
const char *
iavf_vc_stat_str(struct iavf_hw *hw, enum virtchnl_status_code stat_err)
{
switch (stat_err) {
case VIRTCHNL_STATUS_SUCCESS:
return "OK";
case VIRTCHNL_ERR_PARAM:
return "VIRTCHNL_ERR_PARAM";
case VIRTCHNL_STATUS_ERR_NO_MEMORY:
return "VIRTCHNL_STATUS_ERR_NO_MEMORY";
case VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH:
return "VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH";
case VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR:
return "VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR";
case VIRTCHNL_STATUS_ERR_INVALID_VF_ID:
return "VIRTCHNL_STATUS_ERR_INVALID_VF_ID";
case VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR:
return "VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR";
case VIRTCHNL_STATUS_NOT_SUPPORTED:
return "VIRTCHNL_STATUS_NOT_SUPPORTED";
}
snprintf(hw->err_str, sizeof(hw->err_str), "%d", stat_err);
return hw->err_str;
}
/**
* iavf_adv_speed_to_ext_speed - Convert numeric speed to iavf speed enum
* @adv_link_speed: link speed in Mb/s
*
* Converts the link speed from the "advanced" link speed virtchnl op into the
* closest approximation of the internal iavf link speed, rounded down.
*
* @returns the link speed as an iavf_ext_link_speed enum value
*/
enum iavf_ext_link_speed
iavf_adv_speed_to_ext_speed(u32 adv_link_speed)
{
if (adv_link_speed >= 100000)
return IAVF_EXT_LINK_SPEED_100GB;
if (adv_link_speed >= 50000)
return IAVF_EXT_LINK_SPEED_50GB;
if (adv_link_speed >= 40000)
return IAVF_EXT_LINK_SPEED_40GB;
if (adv_link_speed >= 25000)
return IAVF_EXT_LINK_SPEED_25GB;
if (adv_link_speed >= 20000)
return IAVF_EXT_LINK_SPEED_20GB;
if (adv_link_speed >= 10000)
return IAVF_EXT_LINK_SPEED_10GB;
if (adv_link_speed >= 5000)
return IAVF_EXT_LINK_SPEED_5GB;
if (adv_link_speed >= 2500)
return IAVF_EXT_LINK_SPEED_2500MB;
if (adv_link_speed >= 1000)
return IAVF_EXT_LINK_SPEED_1000MB;
if (adv_link_speed >= 100)
return IAVF_EXT_LINK_SPEED_100MB;
if (adv_link_speed >= 10)
return IAVF_EXT_LINK_SPEED_10MB;
return IAVF_EXT_LINK_SPEED_UNKNOWN;
}
/**
* iavf_ext_speed_to_ifmedia - Convert internal iavf speed to ifmedia value
* @link_speed: the link speed
*
* @remark this is sort of a hack, because we don't actually know what media
* type the VF is running on. In an ideal world we might just report the media
* type as "virtual" and have another mechanism for reporting the link
* speed.
*
* @returns a suitable ifmedia type for the given link speed.
*/
u32
iavf_ext_speed_to_ifmedia(enum iavf_ext_link_speed link_speed)
{
switch (link_speed) {
case IAVF_EXT_LINK_SPEED_100GB:
return IFM_100G_SR4;
case IAVF_EXT_LINK_SPEED_50GB:
return IFM_50G_SR2;
case IAVF_EXT_LINK_SPEED_40GB:
return IFM_40G_SR4;
case IAVF_EXT_LINK_SPEED_25GB:
return IFM_25G_SR;
case IAVF_EXT_LINK_SPEED_20GB:
return IFM_20G_KR2;
case IAVF_EXT_LINK_SPEED_10GB:
return IFM_10G_SR;
case IAVF_EXT_LINK_SPEED_5GB:
return IFM_5000_T;
case IAVF_EXT_LINK_SPEED_2500MB:
return IFM_2500_T;
case IAVF_EXT_LINK_SPEED_1000MB:
return IFM_1000_T;
case IAVF_EXT_LINK_SPEED_100MB:
return IFM_100_TX;
case IAVF_EXT_LINK_SPEED_10MB:
return IFM_10_T;
case IAVF_EXT_LINK_SPEED_UNKNOWN:
default:
return IFM_UNKNOWN;
}
}
/**
* iavf_vc_speed_to_ext_speed - Convert virtchnl speed enum to native iavf
* driver speed representation.
* @link_speed: link speed enum value
*
* @returns the link speed in the native iavf format.
*/
enum iavf_ext_link_speed
iavf_vc_speed_to_ext_speed(enum virtchnl_link_speed link_speed)
{
switch (link_speed) {
case VIRTCHNL_LINK_SPEED_40GB:
return IAVF_EXT_LINK_SPEED_40GB;
case VIRTCHNL_LINK_SPEED_25GB:
return IAVF_EXT_LINK_SPEED_25GB;
case VIRTCHNL_LINK_SPEED_20GB:
return IAVF_EXT_LINK_SPEED_20GB;
case VIRTCHNL_LINK_SPEED_10GB:
return IAVF_EXT_LINK_SPEED_10GB;
case VIRTCHNL_LINK_SPEED_1GB:
return IAVF_EXT_LINK_SPEED_1000MB;
case VIRTCHNL_LINK_SPEED_100MB:
return IAVF_EXT_LINK_SPEED_100MB;
case VIRTCHNL_LINK_SPEED_UNKNOWN:
default:
return IAVF_EXT_LINK_SPEED_UNKNOWN;
}
}
/**
* iavf_vc_speed_to_string - Convert virtchnl speed to a string
* @link_speed: the speed to convert
*
* @returns string representing the link speed as reported by the virtchnl
* interface.
*/
const char *
iavf_vc_speed_to_string(enum virtchnl_link_speed link_speed)
{
return iavf_ext_speed_to_str(iavf_vc_speed_to_ext_speed(link_speed));
}
/**
* iavf_ext_speed_to_str - Convert iavf speed enum to string representation
* @link_speed: link speed enum value
*
* XXX: This is an iavf-modified copy of ice_aq_speed_to_str()
*
* @returns the string representation of the given link speed.
*/
const char *
iavf_ext_speed_to_str(enum iavf_ext_link_speed link_speed)
{
switch (link_speed) {
case IAVF_EXT_LINK_SPEED_100GB:
return "100 Gbps";
case IAVF_EXT_LINK_SPEED_50GB:
return "50 Gbps";
case IAVF_EXT_LINK_SPEED_40GB:
return "40 Gbps";
case IAVF_EXT_LINK_SPEED_25GB:
return "25 Gbps";
case IAVF_EXT_LINK_SPEED_20GB:
return "20 Gbps";
case IAVF_EXT_LINK_SPEED_10GB:
return "10 Gbps";
case IAVF_EXT_LINK_SPEED_5GB:
return "5 Gbps";
case IAVF_EXT_LINK_SPEED_2500MB:
return "2.5 Gbps";
case IAVF_EXT_LINK_SPEED_1000MB:
return "1 Gbps";
case IAVF_EXT_LINK_SPEED_100MB:
return "100 Mbps";
case IAVF_EXT_LINK_SPEED_10MB:
return "10 Mbps";
case IAVF_EXT_LINK_SPEED_UNKNOWN:
default:
return "Unknown";
}
}
/**
* iavf_vc_opcode_str - Convert virtchnl opcode to string
* @op: the virtchnl op code
*
* @returns the string representation of the given virtchnl op code
*/
const char *
iavf_vc_opcode_str(uint16_t op)
{
switch (op) {
case VIRTCHNL_OP_VERSION:
return ("VERSION");
case VIRTCHNL_OP_RESET_VF:
return ("RESET_VF");
case VIRTCHNL_OP_GET_VF_RESOURCES:
return ("GET_VF_RESOURCES");
case VIRTCHNL_OP_CONFIG_TX_QUEUE:
return ("CONFIG_TX_QUEUE");
case VIRTCHNL_OP_CONFIG_RX_QUEUE:
return ("CONFIG_RX_QUEUE");
case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
return ("CONFIG_VSI_QUEUES");
case VIRTCHNL_OP_CONFIG_IRQ_MAP:
return ("CONFIG_IRQ_MAP");
case VIRTCHNL_OP_ENABLE_QUEUES:
return ("ENABLE_QUEUES");
case VIRTCHNL_OP_DISABLE_QUEUES:
return ("DISABLE_QUEUES");
case VIRTCHNL_OP_ADD_ETH_ADDR:
return ("ADD_ETH_ADDR");
case VIRTCHNL_OP_DEL_ETH_ADDR:
return ("DEL_ETH_ADDR");
case VIRTCHNL_OP_ADD_VLAN:
return ("ADD_VLAN");
case VIRTCHNL_OP_DEL_VLAN:
return ("DEL_VLAN");
case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
return ("CONFIG_PROMISCUOUS_MODE");
case VIRTCHNL_OP_GET_STATS:
return ("GET_STATS");
case VIRTCHNL_OP_RSVD:
return ("RSVD");
case VIRTCHNL_OP_EVENT:
return ("EVENT");
case VIRTCHNL_OP_CONFIG_RSS_KEY:
return ("CONFIG_RSS_KEY");
case VIRTCHNL_OP_CONFIG_RSS_LUT:
return ("CONFIG_RSS_LUT");
case VIRTCHNL_OP_GET_RSS_HENA_CAPS:
return ("GET_RSS_HENA_CAPS");
case VIRTCHNL_OP_SET_RSS_HENA:
return ("SET_RSS_HENA");
default:
return ("UNKNOWN");
}
}
/**
* iavf_vc_completion - Handle PF reply messages
* @sc: device softc
* @v_opcode: virtchnl op code
* @v_retval: virtchnl return value
* @msg: the message to send
* @msglen: length of the msg buffer
*
* Asynchronous completion function for admin queue messages. Rather than busy
* wait, we fire off our requests and assume that no errors will be returned.
* This function handles the reply messages.
*/
void
iavf_vc_completion(struct iavf_sc *sc,
enum virtchnl_ops v_opcode,
enum virtchnl_status_code v_retval, u8 *msg, u16 msglen __unused)
{
device_t dev = sc->dev;
if (v_opcode != VIRTCHNL_OP_GET_STATS)
iavf_dbg_vc(sc, "%s: opcode %s\n", __func__,
iavf_vc_opcode_str(v_opcode));
if (v_opcode == VIRTCHNL_OP_EVENT) {
struct virtchnl_pf_event *vpe =
(struct virtchnl_pf_event *)msg;
switch (vpe->event) {
case VIRTCHNL_EVENT_LINK_CHANGE:
iavf_handle_link_event(sc, vpe);
break;
case VIRTCHNL_EVENT_RESET_IMPENDING:
device_printf(dev, "PF initiated reset!\n");
iavf_set_state(&sc->state, IAVF_STATE_RESET_PENDING);
break;
default:
iavf_dbg_vc(sc, "Unknown event %d from AQ\n",
vpe->event);
break;
}
return;
}
/* Catch-all error response */
if (v_retval) {
bool print_error = true;
switch (v_opcode) {
case VIRTCHNL_OP_ADD_ETH_ADDR:
device_printf(dev, "WARNING: Error adding VF mac filter!\n");
device_printf(dev, "WARNING: Device may not receive traffic!\n");
break;
case VIRTCHNL_OP_ENABLE_QUEUES:
sc->enable_queues_chan = 1;
wakeup_one(&sc->enable_queues_chan);
break;
case VIRTCHNL_OP_DISABLE_QUEUES:
sc->disable_queues_chan = 1;
wakeup_one(&sc->disable_queues_chan);
/* This may fail, but it does not necessarily mean that
* something is critically wrong.
*/
if (!(sc->dbg_mask & IAVF_DBG_VC))
print_error = false;
break;
default:
break;
}
if (print_error)
device_printf(dev,
"%s: AQ returned error %s to our request %s!\n",
__func__, iavf_vc_stat_str(&sc->hw, v_retval),
iavf_vc_opcode_str(v_opcode));
return;
}
switch (v_opcode) {
case VIRTCHNL_OP_GET_STATS:
iavf_update_stats_counters(sc, (struct iavf_eth_stats *)msg);
break;
case VIRTCHNL_OP_ADD_ETH_ADDR:
break;
case VIRTCHNL_OP_DEL_ETH_ADDR:
break;
case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
break;
case VIRTCHNL_OP_ADD_VLAN:
break;
case VIRTCHNL_OP_DEL_VLAN:
break;
case VIRTCHNL_OP_ENABLE_QUEUES:
atomic_store_rel_32(&sc->queues_enabled, 1);
sc->enable_queues_chan = 1;
wakeup_one(&sc->enable_queues_chan);
break;
case VIRTCHNL_OP_DISABLE_QUEUES:
atomic_store_rel_32(&sc->queues_enabled, 0);
sc->disable_queues_chan = 1;
wakeup_one(&sc->disable_queues_chan);
break;
case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
break;
case VIRTCHNL_OP_CONFIG_IRQ_MAP:
break;
case VIRTCHNL_OP_CONFIG_RSS_KEY:
break;
case VIRTCHNL_OP_SET_RSS_HENA:
break;
case VIRTCHNL_OP_CONFIG_RSS_LUT:
break;
default:
iavf_dbg_vc(sc,
"Received unexpected message %s from PF.\n",
iavf_vc_opcode_str(v_opcode));
break;
}
}
/**
* iavf_handle_link_event - Handle Link event virtchml message
* @sc: device softc
* @vpe: virtchnl PF link event structure
*
* Process a virtchnl PF link event and update the driver and stack status of
* the link event.
*/
static void
iavf_handle_link_event(struct iavf_sc *sc, struct virtchnl_pf_event *vpe)
{
MPASS(vpe->event == VIRTCHNL_EVENT_LINK_CHANGE);
if (sc->vf_res->vf_cap_flags & VIRTCHNL_VF_CAP_ADV_LINK_SPEED)
{
iavf_dbg_vc(sc, "Link change (adv): status %d, speed %u\n",
vpe->event_data.link_event_adv.link_status,
vpe->event_data.link_event_adv.link_speed);
sc->link_up =
vpe->event_data.link_event_adv.link_status;
sc->link_speed_adv =
vpe->event_data.link_event_adv.link_speed;
} else {
iavf_dbg_vc(sc, "Link change: status %d, speed %x\n",
vpe->event_data.link_event.link_status,
vpe->event_data.link_event.link_speed);
sc->link_up =
vpe->event_data.link_event.link_status;
sc->link_speed =
vpe->event_data.link_event.link_speed;
}
iavf_update_link_status(sc);
}