Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iavf/iavf_vc_common.c
- This file was added.
/* 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); | |||||
} |