freebsd-src/sys/dev/bnxt/bnxt_en/bnxt_dcb.c
Chandrakanth patil 35b53f8c98 bnxt_en: Add PFC, ETS & App TLVs protocols support
Created new directory "bnxt_en" in /dev/bnxt and /modules/bnxt
and moved source files and Makefile into respective directory.

ETS support:

   - Added new files bnxt_dcb.c & bnxt_dcb.h
   - Added sysctl node 'dcb' and created handlers 'ets' and
     'dcbx_cap'
   - Add logic to validate user input and configure ETS in
     the firmware
   - Updated makefile to include bnxt_dcb.c & bnxt_dcb.h

PFC support:

   - Created sysctl handlers 'pfc' under node 'dcb'
   - Added logic to validate user input and configure PFC in
     the firmware.

App TLV support:

   - Created 3 new sysctl handlers under node 'dcb'
       - set_apptlv (write only): Sets a specified TLV
       - del_apptlv (write only): Deletes a specified TLV
       - list_apptlv (read only): Lists all APP TLVs configured
   - Added logic to validate user input and configure APP TLVs
     in the firmware.

Added Below DCB ops for management interface:

   - Set PFC, Get PFC, Set ETS, Get ETS, Add App_TLV, Del App_TLV
     Lst App_TLV

Reviewed by:            imp
Approved by:            imp
Differential revision:  https://reviews.freebsd.org/D45005
2024-05-28 10:15:29 +00:00

862 lines
22 KiB
C

/*-
* Broadcom NetXtreme-C/E network driver.
*
* Copyright (c) 2024 Broadcom, All Rights Reserved.
* The term Broadcom refers to Broadcom Limited and/or its subsidiaries
*
* 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.
*
* 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.
*/
#include <sys/endian.h>
#include <linux/errno.h>
#include <linux/bitops.h>
#include "bnxt.h"
#include "bnxt_hwrm.h"
#include "bnxt_dcb.h"
#include "hsi_struct_def.h"
static int
bnxt_tx_queue_to_tc(struct bnxt_softc *softc, uint8_t queue_id)
{
int i, j;
for (i = 0; i < softc->max_tc; i++) {
if (softc->tx_q_info[i].queue_id == queue_id) {
for (j = 0; j < softc->max_tc; j++) {
if (softc->tc_to_qidx[j] == i)
return j;
}
}
}
return -EINVAL;
}
static int
bnxt_hwrm_queue_pri2cos_cfg(struct bnxt_softc *softc,
struct bnxt_ieee_ets *ets,
uint32_t path_dir)
{
struct hwrm_queue_pri2cos_cfg_input req = {0};
struct bnxt_queue_info *q_info;
uint8_t *pri2cos;
int i;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PRI2COS_CFG);
req.flags = htole32(path_dir | HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_IVLAN);
if (path_dir == HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_BIDIR ||
path_dir == HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_TX)
q_info = softc->tx_q_info;
else
q_info = softc->rx_q_info;
pri2cos = &req.pri0_cos_queue_id;
for (i = 0; i < BNXT_IEEE_8021QAZ_MAX_TCS; i++) {
uint8_t qidx;
req.enables |= htole32(HWRM_QUEUE_PRI2COS_CFG_INPUT_ENABLES_PRI0_COS_QUEUE_ID << i);
qidx = softc->tc_to_qidx[ets->prio_tc[i]];
pri2cos[i] = q_info[qidx].queue_id;
}
return _hwrm_send_message(softc, &req, sizeof(req));
}
static int
bnxt_hwrm_queue_pri2cos_qcfg(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
{
struct hwrm_queue_pri2cos_qcfg_output *resp =
(void *)softc->hwrm_cmd_resp.idi_vaddr;
struct hwrm_queue_pri2cos_qcfg_input req = {0};
int rc;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PRI2COS_QCFG);
req.flags = htole32(HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_IVLAN);
rc = _hwrm_send_message(softc, &req, sizeof(req));
if (!rc) {
uint8_t *pri2cos = &resp->pri0_cos_queue_id;
int i;
for (i = 0; i < BNXT_IEEE_8021QAZ_MAX_TCS; i++) {
uint8_t queue_id = pri2cos[i];
int tc;
tc = bnxt_tx_queue_to_tc(softc, queue_id);
if (tc >= 0)
ets->prio_tc[i] = tc;
}
}
return rc;
}
static int
bnxt_hwrm_queue_cos2bw_cfg(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets,
uint8_t max_tc)
{
struct hwrm_queue_cos2bw_cfg_input req = {0};
struct bnxt_cos2bw_cfg cos2bw;
void *data;
int i;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_COS2BW_CFG);
for (i = 0; i < max_tc; i++) {
uint8_t qidx = softc->tc_to_qidx[i];
req.enables |=
htole32(HWRM_QUEUE_COS2BW_CFG_INPUT_ENABLES_COS_QUEUE_ID0_VALID << qidx);
memset(&cos2bw, 0, sizeof(cos2bw));
cos2bw.queue_id = softc->tx_q_info[qidx].queue_id;
if (ets->tc_tsa[i] == BNXT_IEEE_8021QAZ_TSA_STRICT) {
cos2bw.tsa =
HWRM_QUEUE_COS2BW_QCFG_OUTPUT_QUEUE_ID0_TSA_ASSIGN_SP;
cos2bw.pri_lvl = i;
} else {
cos2bw.tsa =
HWRM_QUEUE_COS2BW_QCFG_OUTPUT_QUEUE_ID0_TSA_ASSIGN_ETS;
cos2bw.bw_weight = ets->tc_tx_bw[i];
/* older firmware requires min_bw to be set to the
* same weight value in percent.
*/
if (BNXT_FW_MAJ(softc) < 218) {
cos2bw.min_bw =
htole32((ets->tc_tx_bw[i] * 100) |
BW_VALUE_UNIT_PERCENT1_100);
}
}
data = &req.unused_0 + qidx * (sizeof(cos2bw) - 4);
memcpy(data, &cos2bw.queue_id, sizeof(cos2bw) - 4);
if (qidx == 0) {
req.queue_id0 = cos2bw.queue_id;
req.unused_0 = 0;
}
}
return _hwrm_send_message(softc, &req, sizeof(req));
}
static int
bnxt_hwrm_queue_cos2bw_qcfg(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
{
struct hwrm_queue_cos2bw_qcfg_output *resp =
(void *)softc->hwrm_cmd_resp.idi_vaddr;
struct hwrm_queue_cos2bw_qcfg_input req = {0};
struct bnxt_cos2bw_cfg cos2bw;
uint8_t *data;
int rc, i;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_COS2BW_QCFG);
rc = _hwrm_send_message(softc, &req, sizeof(req));
if (rc) {
return rc;
}
data = &resp->queue_id0 + offsetof(struct bnxt_cos2bw_cfg, queue_id);
for (i = 0; i < softc->max_tc; i++, data += sizeof(cos2bw.cfg)) {
int tc;
memcpy(&cos2bw.cfg, data, sizeof(cos2bw.cfg));
if (i == 0)
cos2bw.queue_id = resp->queue_id0;
tc = bnxt_tx_queue_to_tc(softc, cos2bw.queue_id);
if (tc < 0)
continue;
if (cos2bw.tsa == HWRM_QUEUE_COS2BW_QCFG_OUTPUT_QUEUE_ID0_TSA_ASSIGN_SP) {
ets->tc_tsa[tc] = BNXT_IEEE_8021QAZ_TSA_STRICT;
} else {
ets->tc_tsa[tc] = BNXT_IEEE_8021QAZ_TSA_ETS;
ets->tc_tx_bw[tc] = cos2bw.bw_weight;
}
}
return 0;
}
static int
bnxt_queue_remap(struct bnxt_softc *softc, unsigned int lltc_mask)
{
unsigned long qmap = 0;
int max = softc->max_tc;
int i, j, rc;
/* Assign lossless TCs first */
for (i = 0, j = 0; i < max; ) {
if (lltc_mask & (1 << i)) {
if (BNXT_LLQ(softc->rx_q_info[j].queue_profile)) {
softc->tc_to_qidx[i] = j;
__set_bit(j, &qmap);
i++;
}
j++;
continue;
}
i++;
}
for (i = 0, j = 0; i < max; i++) {
if (lltc_mask & (1 << i))
continue;
j = find_next_zero_bit(&qmap, max, j);
softc->tc_to_qidx[i] = j;
__set_bit(j, &qmap);
j++;
}
if (softc->ieee_ets) {
rc = bnxt_hwrm_queue_cos2bw_cfg(softc, softc->ieee_ets, softc->max_tc);
if (rc) {
device_printf(softc->dev, "failed to config BW, rc = %d\n", rc);
return rc;
}
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, softc->ieee_ets,
HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_BIDIR);
if (rc) {
device_printf(softc->dev, "failed to config prio, rc = %d\n", rc);
return rc;
}
}
return 0;
}
static int
bnxt_hwrm_queue_pfc_cfg(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
{
struct hwrm_queue_pfcenable_cfg_input req = {0};
struct bnxt_ieee_ets *my_ets = softc->ieee_ets;
unsigned int tc_mask = 0, pri_mask = 0;
uint8_t i, pri, lltc_count = 0;
bool need_q_remap = false;
if (!my_ets)
return -EINVAL;
for (i = 0; i < softc->max_tc; i++) {
for (pri = 0; pri < BNXT_IEEE_8021QAZ_MAX_TCS; pri++) {
if ((pfc->pfc_en & (1 << pri)) &&
(my_ets->prio_tc[pri] == i)) {
pri_mask |= 1 << pri;
tc_mask |= 1 << i;
}
}
if (tc_mask & (1 << i))
lltc_count++;
}
if (lltc_count > softc->max_lltc) {
device_printf(softc->dev,
"Hardware doesn't support %d lossless queues "
"to configure PFC (cap %d)\n", lltc_count, softc->max_lltc);
return -EINVAL;
}
for (i = 0; i < softc->max_tc; i++) {
if (tc_mask & (1 << i)) {
uint8_t qidx = softc->tc_to_qidx[i];
if (!BNXT_LLQ(softc->rx_q_info[qidx].queue_profile)) {
need_q_remap = true;
break;
}
}
}
if (need_q_remap)
bnxt_queue_remap(softc, tc_mask);
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PFCENABLE_CFG);
req.flags = htole32(pri_mask);
return _hwrm_send_message(softc, &req, sizeof(req));
}
static int
bnxt_hwrm_queue_pfc_qcfg(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
{
struct hwrm_queue_pfcenable_qcfg_output *resp =
(void *)softc->hwrm_cmd_resp.idi_vaddr;
struct hwrm_queue_pfcenable_qcfg_input req = {0};
uint8_t pri_mask;
int rc;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_PFCENABLE_QCFG);
rc = _hwrm_send_message(softc, &req, sizeof(req));
if (rc) {
return rc;
}
pri_mask = le32toh(resp->flags);
pfc->pfc_en = pri_mask;
return 0;
}
static int
bnxt_hwrm_get_dcbx_app(struct bnxt_softc *softc, struct bnxt_dcb_app *app, int *num_inputs)
{
struct hwrm_fw_get_structured_data_input get = {0};
struct hwrm_struct_data_dcbx_app *fw_app;
struct hwrm_struct_hdr *data;
struct iflib_dma_info dma_data;
size_t data_len;
int rc, n, i;
if (softc->hwrm_spec_code < 0x10601)
return 0;
bnxt_hwrm_cmd_hdr_init(softc, &get, HWRM_FW_GET_STRUCTURED_DATA);
n = BNXT_IEEE_8021QAZ_MAX_TCS;
data_len = sizeof(*data) + sizeof(*fw_app) * n;
rc = iflib_dma_alloc(softc->ctx, data_len, &dma_data,
BUS_DMA_NOWAIT);
if (rc)
return ENOMEM;
get.dest_data_addr = htole64(dma_data.idi_paddr);
get.structure_id = htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP);
get.subtype = htole16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
get.count = 0;
rc = _hwrm_send_message(softc, &get, sizeof(get));
if (rc)
goto set_app_exit;
data = (void *)dma_data.idi_vaddr;
fw_app = (struct hwrm_struct_data_dcbx_app *)(data + 1);
if (data->struct_id != htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP)) {
rc = -ENODEV;
goto set_app_exit;
}
n = data->count;
for (i = 0; i < n; i++, fw_app++) {
app[*num_inputs].priority = fw_app->priority;
app[*num_inputs].protocol = htobe16(fw_app->protocol_id);
app[*num_inputs].selector = fw_app->protocol_selector;
(*num_inputs)++;
}
set_app_exit:
iflib_dma_free(&dma_data);
return rc;
}
static int
bnxt_hwrm_set_dcbx_app(struct bnxt_softc *softc, struct bnxt_dcb_app *app,
bool add)
{
struct hwrm_fw_set_structured_data_input set = {0};
struct hwrm_fw_get_structured_data_input get = {0};
struct hwrm_struct_data_dcbx_app *fw_app;
struct hwrm_struct_hdr *data;
struct iflib_dma_info dma_data;
size_t data_len;
int rc, n, i;
if (softc->hwrm_spec_code < 0x10601)
return 0;
bnxt_hwrm_cmd_hdr_init(softc, &get, HWRM_FW_GET_STRUCTURED_DATA);
n = BNXT_IEEE_8021QAZ_MAX_TCS;
data_len = sizeof(*data) + sizeof(*fw_app) * n;
rc = iflib_dma_alloc(softc->ctx, data_len, &dma_data,
BUS_DMA_NOWAIT);
if (rc)
return ENOMEM;
get.dest_data_addr = htole64(dma_data.idi_paddr);
get.structure_id = htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP);
get.subtype = htole16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
get.count = 0;
rc = _hwrm_send_message(softc, &get, sizeof(get));
if (rc)
goto set_app_exit;
data = (void *)dma_data.idi_vaddr;
fw_app = (struct hwrm_struct_data_dcbx_app *)(data + 1);
if (data->struct_id != htole16(HWRM_STRUCT_HDR_STRUCT_ID_DCBX_APP)) {
rc = -ENODEV;
goto set_app_exit;
}
n = data->count;
for (i = 0; i < n; i++, fw_app++) {
if (fw_app->protocol_id == htobe16(app->protocol) &&
fw_app->protocol_selector == app->selector &&
fw_app->priority == app->priority) {
if (add)
goto set_app_exit;
else
break;
}
}
if (add) {
/* append */
n++;
fw_app->protocol_id = htobe16(app->protocol);
fw_app->protocol_selector = app->selector;
fw_app->priority = app->priority;
fw_app->valid = 1;
} else {
size_t len = 0;
/* not found, nothing to delete */
if (n == i)
goto set_app_exit;
len = (n - 1 - i) * sizeof(*fw_app);
if (len)
memmove(fw_app, fw_app + 1, len);
n--;
memset(fw_app + n, 0, sizeof(*fw_app));
}
data->count = n;
data->len = htole16(sizeof(*fw_app) * n);
data->subtype = htole16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL);
bnxt_hwrm_cmd_hdr_init(softc, &set, HWRM_FW_SET_STRUCTURED_DATA);
set.src_data_addr = htole64(dma_data.idi_paddr);
set.data_len = htole16(sizeof(*data) + sizeof(*fw_app) * n);
set.hdr_cnt = 1;
rc = _hwrm_send_message(softc, &set, sizeof(set));
set_app_exit:
iflib_dma_free(&dma_data);
return rc;
}
static int
bnxt_hwrm_queue_dscp_qcaps(struct bnxt_softc *softc)
{
struct hwrm_queue_dscp_qcaps_output *resp =
(void *)softc->hwrm_cmd_resp.idi_vaddr;
struct hwrm_queue_dscp_qcaps_input req = {0};
int rc;
softc->max_dscp_value = 0;
if (softc->hwrm_spec_code < 0x10800 || BNXT_VF(softc))
return 0;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_DSCP_QCAPS);
rc = _hwrm_send_message(softc, &req, sizeof(req));
if (!rc) {
softc->max_dscp_value = (1 << resp->num_dscp_bits) - 1;
if (softc->max_dscp_value < 0x3f)
softc->max_dscp_value = 0;
}
return rc;
}
static int
bnxt_hwrm_queue_dscp2pri_qcfg(struct bnxt_softc *softc, struct bnxt_dcb_app *app, int *num_inputs)
{
struct hwrm_queue_dscp2pri_qcfg_input req = {0};
struct hwrm_queue_dscp2pri_qcfg_output *resp =
(void *)softc->hwrm_cmd_resp.idi_vaddr;
struct bnxt_dscp2pri_entry *dscp2pri;
struct iflib_dma_info dma_data;
int rc, entry_cnt;
int i;
if (softc->hwrm_spec_code < 0x10800)
return 0;
rc = iflib_dma_alloc(softc->ctx, sizeof(*dscp2pri) * 128, &dma_data,
BUS_DMA_NOWAIT);
if (rc)
return ENOMEM;
dscp2pri = (void *)dma_data.idi_vaddr;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_DSCP2PRI_QCFG);
req.dest_data_addr = htole64(dma_data.idi_paddr);
req.dest_data_buffer_size = htole16(sizeof(*dscp2pri) * 64);
req.port_id = htole16(softc->pf.port_id);
rc = _hwrm_send_message(softc, &req, sizeof(req));
if (rc)
goto end;
entry_cnt = le16toh(resp->entry_cnt);
for (i = 0; i < entry_cnt; i++) {
app[*num_inputs].priority = dscp2pri[i].pri;
app[*num_inputs].protocol = dscp2pri[i].dscp;
app[*num_inputs].selector = BNXT_IEEE_8021QAZ_APP_SEL_DSCP;
(*num_inputs)++;
}
end:
iflib_dma_free(&dma_data);
return rc;
}
static int
bnxt_hwrm_queue_dscp2pri_cfg(struct bnxt_softc *softc, struct bnxt_dcb_app *app,
bool add)
{
struct hwrm_queue_dscp2pri_cfg_input req = {0};
struct bnxt_dscp2pri_entry *dscp2pri;
struct iflib_dma_info dma_data;
int rc;
if (softc->hwrm_spec_code < 0x10800)
return 0;
rc = iflib_dma_alloc(softc->ctx, sizeof(*dscp2pri), &dma_data,
BUS_DMA_NOWAIT);
if (rc)
return ENOMEM;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_QUEUE_DSCP2PRI_CFG);
req.src_data_addr = htole64(dma_data.idi_paddr);
dscp2pri = (void *)dma_data.idi_vaddr;
dscp2pri->dscp = app->protocol;
if (add)
dscp2pri->mask = 0x3f;
else
dscp2pri->mask = 0;
dscp2pri->pri = app->priority;
req.entry_cnt = htole16(1);
req.port_id = htole16(softc->pf.port_id);
rc = _hwrm_send_message(softc, &req, sizeof(req));
iflib_dma_free(&dma_data);
return rc;
}
static int
bnxt_ets_validate(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets, uint8_t *tc)
{
int total_ets_bw = 0;
bool zero = false;
uint8_t max_tc = 0;
int i;
for (i = 0; i < BNXT_IEEE_8021QAZ_MAX_TCS; i++) {
if (ets->prio_tc[i] > softc->max_tc) {
device_printf(softc->dev, "priority to TC mapping exceeds TC count %d\n",
ets->prio_tc[i]);
return -EINVAL;
}
if (ets->prio_tc[i] > max_tc)
max_tc = ets->prio_tc[i];
if ((ets->tc_tx_bw[i] || ets->tc_tsa[i]) && i > softc->max_tc)
return -EINVAL;
switch (ets->tc_tsa[i]) {
case BNXT_IEEE_8021QAZ_TSA_STRICT:
break;
case BNXT_IEEE_8021QAZ_TSA_ETS:
total_ets_bw += ets->tc_tx_bw[i];
zero = zero || !ets->tc_tx_bw[i];
break;
default:
return -ENOTSUPP;
}
}
if (total_ets_bw > 100) {
device_printf(softc->dev, "rejecting ETS config exceeding available bandwidth\n");
return -EINVAL;
}
if (zero && total_ets_bw == 100) {
device_printf(softc->dev, "rejecting ETS config starving a TC\n");
return -EINVAL;
}
if (max_tc >= softc->max_tc)
*tc = softc->max_tc;
else
*tc = max_tc + 1;
return 0;
}
int
bnxt_dcb_ieee_getets(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
{
struct bnxt_ieee_ets *my_ets = softc->ieee_ets;
int rc;
if (!my_ets)
return 0;
rc = bnxt_hwrm_queue_cos2bw_qcfg(softc, my_ets);
if (rc)
goto error;
rc = bnxt_hwrm_queue_pri2cos_qcfg(softc, my_ets);
if (rc)
goto error;
if (ets) {
ets->cbs = my_ets->cbs;
ets->ets_cap = softc->max_tc;
memcpy(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
memcpy(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw));
memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa));
memcpy(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc));
}
return 0;
error:
return rc;
}
int
bnxt_dcb_ieee_setets(struct bnxt_softc *softc, struct bnxt_ieee_ets *ets)
{
uint8_t max_tc = 0;
int rc;
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST))
return -EINVAL;
rc = bnxt_ets_validate(softc, ets, &max_tc);
if (rc)
return rc;
rc = bnxt_hwrm_queue_cos2bw_cfg(softc, ets, max_tc);
if (rc)
goto error;
if (!softc->is_asym_q) {
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, ets,
HWRM_QUEUE_PRI2COS_CFG_INPUT_FLAGS_PATH_BIDIR);
if (rc)
goto error;
} else {
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, ets,
HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_PATH_TX);
if (rc)
goto error;
rc = bnxt_hwrm_queue_pri2cos_cfg(softc, ets,
HWRM_QUEUE_PRI2COS_QCFG_INPUT_FLAGS_PATH_RX);
if (rc)
goto error;
}
memcpy(softc->ieee_ets, ets, sizeof(*ets));
return 0;
error:
return rc;
}
int
bnxt_dcb_ieee_getpfc(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
{
struct bnxt_ieee_pfc *my_pfc = softc->ieee_pfc;
int rc;
if (!my_pfc)
return -1;
pfc->pfc_cap = softc->max_lltc;
rc = bnxt_hwrm_queue_pfc_qcfg(softc, my_pfc);
if (rc)
return 0;
pfc->pfc_en = my_pfc->pfc_en;
pfc->mbc = my_pfc->mbc;
pfc->delay = my_pfc->delay;
return 0;
}
int
bnxt_dcb_ieee_setpfc(struct bnxt_softc *softc, struct bnxt_ieee_pfc *pfc)
{
struct bnxt_ieee_pfc *my_pfc = softc->ieee_pfc;
int rc;
if (!my_pfc)
return -1;
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST) ||
(softc->phy_flags & BNXT_PHY_FL_NO_PAUSE))
return -EINVAL;
rc = bnxt_hwrm_queue_pfc_cfg(softc, pfc);
if (!rc)
memcpy(my_pfc, pfc, sizeof(*my_pfc));
return rc;
}
static int
bnxt_dcb_ieee_dscp_app_prep(struct bnxt_softc *softc, struct bnxt_dcb_app *app)
{
if (app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DSCP) {
if (!softc->max_dscp_value)
return -ENOTSUPP;
if (app->protocol > softc->max_dscp_value)
return -EINVAL;
}
return 0;
}
int
bnxt_dcb_ieee_setapp(struct bnxt_softc *softc, struct bnxt_dcb_app *app)
{
int rc;
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST))
return -EINVAL;
rc = bnxt_dcb_ieee_dscp_app_prep(softc, app);
if (rc)
return rc;
if ((app->selector == BNXT_IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
app->protocol == ETH_P_ROCE) ||
(app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DGRAM &&
app->protocol == ROCE_V2_UDP_DPORT))
rc = bnxt_hwrm_set_dcbx_app(softc, app, true);
if (app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DSCP)
rc = bnxt_hwrm_queue_dscp2pri_cfg(softc, app, true);
return rc;
}
int
bnxt_dcb_ieee_delapp(struct bnxt_softc *softc, struct bnxt_dcb_app *app)
{
int rc;
if (!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_VER_IEEE) ||
!(softc->dcbx_cap & BNXT_DCB_CAP_DCBX_HOST))
return -EINVAL;
rc = bnxt_dcb_ieee_dscp_app_prep(softc, app);
if (rc)
return rc;
if ((app->selector == BNXT_IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
app->protocol == ETH_P_ROCE) ||
(app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DGRAM &&
app->protocol == ROCE_V2_UDP_DPORT))
rc = bnxt_hwrm_set_dcbx_app(softc, app, false);
if (app->selector == BNXT_IEEE_8021QAZ_APP_SEL_DSCP)
rc = bnxt_hwrm_queue_dscp2pri_cfg(softc, app, false);
return rc;
}
int
bnxt_dcb_ieee_listapp(struct bnxt_softc *softc, struct bnxt_dcb_app *app, int *num_inputs)
{
bnxt_hwrm_get_dcbx_app(softc, app, num_inputs);
bnxt_hwrm_queue_dscp2pri_qcfg(softc, app, num_inputs);
return 0;
}
uint8_t
bnxt_dcb_getdcbx(struct bnxt_softc *softc)
{
return softc->dcbx_cap;
}
uint8_t
bnxt_dcb_setdcbx(struct bnxt_softc *softc, uint8_t mode)
{
/* All firmware DCBX settings are set in NVRAM */
if (softc->dcbx_cap & BNXT_DCB_CAP_DCBX_LLD_MANAGED)
return 1;
/*
* Do't allow editing CAP_DCBX_LLD_MANAGED since it is driven
* based on FUNC_QCFG_OUTPUT_FLAGS_FW_DCBX_AGENT_ENABLED
*/
if ((softc->dcbx_cap & BNXT_DCB_CAP_DCBX_LLD_MANAGED) !=
(mode & BNXT_DCB_CAP_DCBX_LLD_MANAGED))
return 1;
if (mode & BNXT_DCB_CAP_DCBX_HOST) {
if (BNXT_VF(softc) || (softc->fw_cap & BNXT_FW_CAP_LLDP_AGENT))
return 1;
/* only support BNXT_IEEE */
if ((mode & BNXT_DCB_CAP_DCBX_VER_CEE) ||
!(mode & BNXT_DCB_CAP_DCBX_VER_IEEE))
return 1;
}
if (mode == softc->dcbx_cap)
return 0;
softc->dcbx_cap = mode;
return 0;
}
void
bnxt_dcb_init(struct bnxt_softc *softc)
{
struct bnxt_ieee_ets ets = {0};
struct bnxt_ieee_pfc pfc = {0};
softc->dcbx_cap = 0;
if (softc->hwrm_spec_code < 0x10501)
return;
softc->ieee_ets = malloc(sizeof(struct bnxt_ieee_ets), M_DEVBUF, M_NOWAIT | M_ZERO);
if (!softc->ieee_ets)
return;
softc->ieee_pfc = malloc(sizeof(struct bnxt_ieee_pfc), M_DEVBUF, M_NOWAIT | M_ZERO);
if (!softc->ieee_pfc)
return;
bnxt_hwrm_queue_dscp_qcaps(softc);
softc->dcbx_cap = BNXT_DCB_CAP_DCBX_VER_IEEE;
if (BNXT_PF(softc) && !(softc->fw_cap & BNXT_FW_CAP_LLDP_AGENT))
softc->dcbx_cap |= BNXT_DCB_CAP_DCBX_HOST;
else if (softc->fw_cap & BNXT_FW_CAP_DCBX_AGENT)
softc->dcbx_cap |= BNXT_DCB_CAP_DCBX_LLD_MANAGED;
bnxt_dcb_ieee_setets(softc, &ets);
bnxt_dcb_ieee_setpfc(softc, &pfc);
}
void
bnxt_dcb_free(struct bnxt_softc *softc)
{
free(softc->ieee_ets, M_DEVBUF);
softc->ieee_ets = NULL;
free(softc->ieee_pfc, M_DEVBUF);
softc->ieee_pfc = NULL;
}