linux_dsm_epyc7002/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
Anish Bhatt 10b0046685 cxgb4: IEEE fixes for DCBx state machine
* Changes required due to 16eecd9be4 ("dcbnl : Fix misleading
  dcb_app->priority explanation")
* Driver was previously not aware of what DCBx version was negotiated by
  firmware, this could lead to DCB app table  in kernel or in firmware being
  populated wrong  since IEEE/CEE used different formats made clear by above
  mentioned commit
* Driver was missing a couple of state transitions that could be caused
  by other drivers that use chelsio hardware, resulting in incorrect behaviour
  (the change that addresses this also flips the state machine to switch on
   state instead of transition, hope this is okay in current window)
* Prio queue info & tsa is no longer thrown away

v2: Print DCBx state transition messages only when debug is enabled

Signed-off-by: Anish Bhatt <anish@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2014-08-07 16:18:55 -07:00

1092 lines
28 KiB
C

/*
* Copyright (C) 2013-2014 Chelsio Communications. All rights reserved.
*
* Written by Anish Bhatt (anish@chelsio.com)
* Casey Leedom (leedom@chelsio.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
*/
#include "cxgb4.h"
/* DCBx version control
*/
char *dcb_ver_array[] = {
"Unknown",
"DCBx-CIN",
"DCBx-CEE 1.01",
"DCBx-IEEE",
"", "", "",
"Auto Negotiated"
};
/* Initialize a port's Data Center Bridging state. Typically used after a
* Link Down event.
*/
void cxgb4_dcb_state_init(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
struct port_dcb_info *dcb = &pi->dcb;
int version_temp = dcb->dcb_version;
memset(dcb, 0, sizeof(struct port_dcb_info));
dcb->state = CXGB4_DCB_STATE_START;
if (version_temp)
dcb->dcb_version = version_temp;
netdev_dbg(dev, "%s: Initializing DCB state for port[%d]\n",
__func__, pi->port_id);
}
void cxgb4_dcb_version_init(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
struct port_dcb_info *dcb = &pi->dcb;
/* Any writes here are only done on kernels that exlicitly need
* a specific version, say < 2.6.38 which only support CEE
*/
dcb->dcb_version = FW_PORT_DCB_VER_AUTO;
}
/* Finite State machine for Data Center Bridging.
*/
void cxgb4_dcb_state_fsm(struct net_device *dev,
enum cxgb4_dcb_state_input transition_to)
{
struct port_info *pi = netdev2pinfo(dev);
struct port_dcb_info *dcb = &pi->dcb;
struct adapter *adap = pi->adapter;
enum cxgb4_dcb_state current_state = dcb->state;
netdev_dbg(dev, "%s: State change from %d to %d for %s\n",
__func__, dcb->state, transition_to, dev->name);
switch (current_state) {
case CXGB4_DCB_STATE_START: {
switch (transition_to) {
case CXGB4_DCB_INPUT_FW_DISABLED: {
/* we're going to use Host DCB */
dcb->state = CXGB4_DCB_STATE_HOST;
dcb->supported = CXGB4_DCBX_HOST_SUPPORT;
dcb->enabled = 1;
break;
}
case CXGB4_DCB_INPUT_FW_ENABLED: {
/* we're going to use Firmware DCB */
dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE;
dcb->supported = CXGB4_DCBX_FW_SUPPORT;
break;
}
case CXGB4_DCB_INPUT_FW_INCOMPLETE: {
/* expected transition */
break;
}
case CXGB4_DCB_INPUT_FW_ALLSYNCED: {
dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED;
break;
}
default:
goto bad_state_input;
}
break;
}
case CXGB4_DCB_STATE_FW_INCOMPLETE: {
switch (transition_to) {
case CXGB4_DCB_INPUT_FW_ENABLED: {
/* we're alreaady in firmware DCB mode */
break;
}
case CXGB4_DCB_INPUT_FW_INCOMPLETE: {
/* we're already incomplete */
break;
}
case CXGB4_DCB_INPUT_FW_ALLSYNCED: {
dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED;
dcb->enabled = 1;
linkwatch_fire_event(dev);
break;
}
default:
goto bad_state_input;
}
break;
}
case CXGB4_DCB_STATE_FW_ALLSYNCED: {
switch (transition_to) {
case CXGB4_DCB_INPUT_FW_ENABLED: {
/* we're alreaady in firmware DCB mode */
break;
}
case CXGB4_DCB_INPUT_FW_INCOMPLETE: {
/* We were successfully running with firmware DCB but
* now it's telling us that it's in an "incomplete
* state. We need to reset back to a ground state
* of incomplete.
*/
cxgb4_dcb_state_init(dev);
dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE;
dcb->supported = CXGB4_DCBX_FW_SUPPORT;
linkwatch_fire_event(dev);
break;
}
case CXGB4_DCB_INPUT_FW_ALLSYNCED: {
/* we're already all sync'ed
* this is only applicable for IEEE or
* when another VI already completed negotiaton
*/
dcb->enabled = 1;
linkwatch_fire_event(dev);
break;
}
default:
goto bad_state_input;
}
break;
}
case CXGB4_DCB_STATE_HOST: {
switch (transition_to) {
case CXGB4_DCB_INPUT_FW_DISABLED: {
/* we're alreaady in Host DCB mode */
break;
}
default:
goto bad_state_input;
}
break;
}
default:
goto bad_state_transition;
}
return;
bad_state_input:
dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: illegal input symbol %d\n",
transition_to);
return;
bad_state_transition:
dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: bad state transition, state = %d, input = %d\n",
current_state, transition_to);
}
/* Handle a DCB/DCBX update message from the firmware.
*/
void cxgb4_dcb_handle_fw_update(struct adapter *adap,
const struct fw_port_cmd *pcmd)
{
const union fw_port_dcb *fwdcb = &pcmd->u.dcb;
int port = FW_PORT_CMD_PORTID_GET(be32_to_cpu(pcmd->op_to_portid));
struct net_device *dev = adap->port[port];
struct port_info *pi = netdev_priv(dev);
struct port_dcb_info *dcb = &pi->dcb;
int dcb_type = pcmd->u.dcb.pgid.type;
int dcb_running_version;
/* Handle Firmware DCB Control messages separately since they drive
* our state machine.
*/
if (dcb_type == FW_PORT_DCB_TYPE_CONTROL) {
enum cxgb4_dcb_state_input input =
((pcmd->u.dcb.control.all_syncd_pkd &
FW_PORT_CMD_ALL_SYNCD)
? CXGB4_DCB_STATE_FW_ALLSYNCED
: CXGB4_DCB_STATE_FW_INCOMPLETE);
if (dcb->dcb_version != FW_PORT_DCB_VER_UNKNOWN) {
dcb_running_version = FW_PORT_CMD_DCB_VERSION_GET(
be16_to_cpu(
pcmd->u.dcb.control.dcb_version_to_app_state));
if (dcb_running_version == FW_PORT_DCB_VER_CEE1D01 ||
dcb_running_version == FW_PORT_DCB_VER_IEEE) {
dcb->dcb_version = dcb_running_version;
dev_warn(adap->pdev_dev, "Interface %s is running %s\n",
dev->name,
dcb_ver_array[dcb->dcb_version]);
} else {
dev_warn(adap->pdev_dev,
"Something screwed up, requested firmware for %s, but firmware returned %s instead\n",
dcb_ver_array[dcb->dcb_version],
dcb_ver_array[dcb_running_version]);
dcb->dcb_version = FW_PORT_DCB_VER_UNKNOWN;
}
}
cxgb4_dcb_state_fsm(dev, input);
return;
}
/* It's weird, and almost certainly an error, to get Firmware DCB
* messages when we either haven't been told whether we're going to be
* doing Host or Firmware DCB; and even worse when we've been told
* that we're doing Host DCB!
*/
if (dcb->state == CXGB4_DCB_STATE_START ||
dcb->state == CXGB4_DCB_STATE_HOST) {
dev_err(adap->pdev_dev, "Receiving Firmware DCB messages in State %d\n",
dcb->state);
return;
}
/* Now handle the general Firmware DCB update messages ...
*/
switch (dcb_type) {
case FW_PORT_DCB_TYPE_PGID:
dcb->pgid = be32_to_cpu(fwdcb->pgid.pgid);
dcb->msgs |= CXGB4_DCB_FW_PGID;
break;
case FW_PORT_DCB_TYPE_PGRATE:
dcb->pg_num_tcs_supported = fwdcb->pgrate.num_tcs_supported;
memcpy(dcb->pgrate, &fwdcb->pgrate.pgrate,
sizeof(dcb->pgrate));
memcpy(dcb->tsa, &fwdcb->pgrate.tsa,
sizeof(dcb->tsa));
dcb->msgs |= CXGB4_DCB_FW_PGRATE;
if (dcb->msgs & CXGB4_DCB_FW_PGID)
IEEE_FAUX_SYNC(dev, dcb);
break;
case FW_PORT_DCB_TYPE_PRIORATE:
memcpy(dcb->priorate, &fwdcb->priorate.strict_priorate,
sizeof(dcb->priorate));
dcb->msgs |= CXGB4_DCB_FW_PRIORATE;
break;
case FW_PORT_DCB_TYPE_PFC:
dcb->pfcen = fwdcb->pfc.pfcen;
dcb->pfc_num_tcs_supported = fwdcb->pfc.max_pfc_tcs;
dcb->msgs |= CXGB4_DCB_FW_PFC;
IEEE_FAUX_SYNC(dev, dcb);
break;
case FW_PORT_DCB_TYPE_APP_ID: {
const struct fw_port_app_priority *fwap = &fwdcb->app_priority;
int idx = fwap->idx;
struct app_priority *ap = &dcb->app_priority[idx];
struct dcb_app app = {
.protocol = be16_to_cpu(fwap->protocolid),
};
int err;
/* Convert from firmware format to relevant format
* when using app selector
*/
if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) {
app.selector = (fwap->sel_field + 1);
app.priority = ffs(fwap->user_prio_map) - 1;
err = dcb_ieee_setapp(dev, &app);
IEEE_FAUX_SYNC(dev, dcb);
} else {
/* Default is CEE */
app.selector = !!(fwap->sel_field);
app.priority = fwap->user_prio_map;
err = dcb_setapp(dev, &app);
}
if (err)
dev_err(adap->pdev_dev,
"Failed DCB Set Application Priority: sel=%d, prot=%d, prio=%d, err=%d\n",
app.selector, app.protocol, app.priority, -err);
ap->user_prio_map = fwap->user_prio_map;
ap->sel_field = fwap->sel_field;
ap->protocolid = be16_to_cpu(fwap->protocolid);
dcb->msgs |= CXGB4_DCB_FW_APP_ID;
break;
}
default:
dev_err(adap->pdev_dev, "Unknown DCB update type received %x\n",
dcb_type);
break;
}
}
/* Data Center Bridging netlink operations.
*/
/* Get current DCB enabled/disabled state.
*/
static u8 cxgb4_getstate(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
return pi->dcb.enabled;
}
/* Set DCB enabled/disabled.
*/
static u8 cxgb4_setstate(struct net_device *dev, u8 enabled)
{
struct port_info *pi = netdev2pinfo(dev);
/* Firmware doesn't provide any mechanism to control the DCB state.
*/
if (enabled != (pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED))
return 1;
return 0;
}
static void cxgb4_getpgtccfg(struct net_device *dev, int tc,
u8 *prio_type, u8 *pgid, u8 *bw_per,
u8 *up_tc_map, int local)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int err;
*prio_type = *pgid = *bw_per = *up_tc_map = 0;
if (local)
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
else
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err);
return;
}
*pgid = (be32_to_cpu(pcmd.u.dcb.pgid.pgid) >> (tc * 4)) & 0xf;
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
-err);
return;
}
*bw_per = pcmd.u.dcb.pgrate.pgrate[*pgid];
*up_tc_map = (1 << tc);
/* prio_type is link strict */
*prio_type = 0x2;
}
static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc,
u8 *prio_type, u8 *pgid, u8 *bw_per,
u8 *up_tc_map)
{
return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 1);
}
static void cxgb4_getpgtccfg_rx(struct net_device *dev, int tc,
u8 *prio_type, u8 *pgid, u8 *bw_per,
u8 *up_tc_map)
{
return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 0);
}
static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc,
u8 prio_type, u8 pgid, u8 bw_per,
u8 up_tc_map)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
u32 _pgid;
int err;
if (pgid == DCB_ATTR_VALUE_UNDEFINED)
return;
if (bw_per == DCB_ATTR_VALUE_UNDEFINED)
return;
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err);
return;
}
_pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
_pgid &= ~(0xF << (tc * 4));
_pgid |= pgid << (tc * 4);
pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid);
INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB write PGID failed with %d\n",
-err);
return;
}
memset(&pcmd, 0, sizeof(struct fw_port_cmd));
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
-err);
return;
}
pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per;
INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS)
dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n",
-err);
}
static void cxgb4_getpgbwgcfg(struct net_device *dev, int pgid, u8 *bw_per,
int local)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int err;
if (local)
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
else
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
-err);
return;
}
*bw_per = pcmd.u.dcb.pgrate.pgrate[pgid];
}
static void cxgb4_getpgbwgcfg_tx(struct net_device *dev, int pgid, u8 *bw_per)
{
return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 1);
}
static void cxgb4_getpgbwgcfg_rx(struct net_device *dev, int pgid, u8 *bw_per)
{
return cxgb4_getpgbwgcfg(dev, pgid, bw_per, 0);
}
static void cxgb4_setpgbwgcfg_tx(struct net_device *dev, int pgid,
u8 bw_per)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int err;
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
-err);
return;
}
pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per;
INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS)
dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n",
-err);
}
/* Return whether the specified Traffic Class Priority has Priority Pause
* Frames enabled.
*/
static void cxgb4_getpfccfg(struct net_device *dev, int priority, u8 *pfccfg)
{
struct port_info *pi = netdev2pinfo(dev);
struct port_dcb_info *dcb = &pi->dcb;
if (dcb->state != CXGB4_DCB_STATE_FW_ALLSYNCED ||
priority >= CXGB4_MAX_PRIORITY)
*pfccfg = 0;
else
*pfccfg = (pi->dcb.pfcen >> priority) & 1;
}
/* Enable/disable Priority Pause Frames for the specified Traffic Class
* Priority.
*/
static void cxgb4_setpfccfg(struct net_device *dev, int priority, u8 pfccfg)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int err;
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED ||
priority >= CXGB4_MAX_PRIORITY)
return;
INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
pcmd.u.dcb.pfc.type = FW_PORT_DCB_TYPE_PFC;
pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen;
if (pfccfg)
pcmd.u.dcb.pfc.pfcen |= (1 << priority);
else
pcmd.u.dcb.pfc.pfcen &= (~(1 << priority));
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB PFC write failed with %d\n", -err);
return;
}
pi->dcb.pfcen = pcmd.u.dcb.pfc.pfcen;
}
static u8 cxgb4_setall(struct net_device *dev)
{
return 0;
}
/* Return DCB capabilities.
*/
static u8 cxgb4_getcap(struct net_device *dev, int cap_id, u8 *caps)
{
struct port_info *pi = netdev2pinfo(dev);
switch (cap_id) {
case DCB_CAP_ATTR_PG:
case DCB_CAP_ATTR_PFC:
*caps = true;
break;
case DCB_CAP_ATTR_PG_TCS:
/* 8 priorities for PG represented by bitmap */
*caps = 0x80;
break;
case DCB_CAP_ATTR_PFC_TCS:
/* 8 priorities for PFC represented by bitmap */
*caps = 0x80;
break;
case DCB_CAP_ATTR_GSP:
*caps = true;
break;
case DCB_CAP_ATTR_UP2TC:
case DCB_CAP_ATTR_BCN:
*caps = false;
break;
case DCB_CAP_ATTR_DCBX:
*caps = pi->dcb.supported;
break;
default:
*caps = false;
}
return 0;
}
/* Return the number of Traffic Classes for the indicated Traffic Class ID.
*/
static int cxgb4_getnumtcs(struct net_device *dev, int tcs_id, u8 *num)
{
struct port_info *pi = netdev2pinfo(dev);
switch (tcs_id) {
case DCB_NUMTCS_ATTR_PG:
if (pi->dcb.msgs & CXGB4_DCB_FW_PGRATE)
*num = pi->dcb.pg_num_tcs_supported;
else
*num = 0x8;
break;
case DCB_NUMTCS_ATTR_PFC:
*num = 0x8;
break;
default:
return -EINVAL;
}
return 0;
}
/* Set the number of Traffic Classes supported for the indicated Traffic Class
* ID.
*/
static int cxgb4_setnumtcs(struct net_device *dev, int tcs_id, u8 num)
{
/* Setting the number of Traffic Classes isn't supported.
*/
return -ENOSYS;
}
/* Return whether Priority Flow Control is enabled. */
static u8 cxgb4_getpfcstate(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
return false;
return pi->dcb.pfcen != 0;
}
/* Enable/disable Priority Flow Control. */
static void cxgb4_setpfcstate(struct net_device *dev, u8 state)
{
/* We can't enable/disable Priority Flow Control but we also can't
* return an error ...
*/
}
/* Return the Application User Priority Map associated with the specified
* Application ID.
*/
static int __cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id,
int peer)
{
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int i;
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
return 0;
for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
struct fw_port_cmd pcmd;
int err;
if (peer)
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
else
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
pcmd.u.dcb.app_priority.idx = i;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB APP read failed with %d\n",
-err);
return err;
}
if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id)
if (pcmd.u.dcb.app_priority.sel_field == app_idtype)
return pcmd.u.dcb.app_priority.user_prio_map;
/* exhausted app list */
if (!pcmd.u.dcb.app_priority.protocolid)
break;
}
return -EEXIST;
}
/* Return the Application User Priority Map associated with the specified
* Application ID.
*/
static int cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id)
{
return __cxgb4_getapp(dev, app_idtype, app_id, 0);
}
/* Write a new Application User Priority Map for the specified Application ID
*/
static int __cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id,
u8 app_prio)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int i, err;
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
return -EINVAL;
/* DCB info gets thrown away on link up */
if (!netif_carrier_ok(dev))
return -ENOLINK;
for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id);
pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
pcmd.u.dcb.app_priority.idx = i;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB app table read failed with %d\n",
-err);
return err;
}
if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) {
/* overwrite existing app table */
pcmd.u.dcb.app_priority.protocolid = 0;
break;
}
/* find first empty slot */
if (!pcmd.u.dcb.app_priority.protocolid)
break;
}
if (i == CXGB4_MAX_DCBX_APP_SUPPORTED) {
/* no empty slots available */
dev_err(adap->pdev_dev, "DCB app table full\n");
return -EBUSY;
}
/* write out new app table entry */
INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
if (pi->dcb.state == CXGB4_DCB_STATE_HOST)
pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY);
pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
pcmd.u.dcb.app_priority.protocolid = cpu_to_be16(app_id);
pcmd.u.dcb.app_priority.sel_field = app_idtype;
pcmd.u.dcb.app_priority.user_prio_map = app_prio;
pcmd.u.dcb.app_priority.idx = i;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB app table write failed with %d\n",
-err);
return err;
}
return 0;
}
/* Priority for CEE inside dcb_app is bitmask, with 0 being an invalid value */
static int cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id,
u8 app_prio)
{
int ret;
struct dcb_app app = {
.selector = app_idtype,
.protocol = app_id,
.priority = app_prio,
};
if (app_idtype != DCB_APP_IDTYPE_ETHTYPE &&
app_idtype != DCB_APP_IDTYPE_PORTNUM)
return -EINVAL;
/* Convert app_idtype to a format that firmware understands */
ret = __cxgb4_setapp(dev, app_idtype == DCB_APP_IDTYPE_ETHTYPE ?
app_idtype : 3, app_id, app_prio);
if (ret)
return ret;
return dcb_setapp(dev, &app);
}
/* Return whether IEEE Data Center Bridging has been negotiated.
*/
static inline int cxgb4_ieee_negotiation_complete(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
struct port_dcb_info *dcb = &pi->dcb;
return (dcb->state == CXGB4_DCB_STATE_FW_ALLSYNCED &&
(dcb->supported & DCB_CAP_DCBX_VER_IEEE));
}
/* Fill in the Application User Priority Map associated with the
* specified Application.
* Priority for IEEE dcb_app is an integer, with 0 being a valid value
*/
static int cxgb4_ieee_getapp(struct net_device *dev, struct dcb_app *app)
{
int prio;
if (!cxgb4_ieee_negotiation_complete(dev))
return -EINVAL;
if (!(app->selector && app->protocol))
return -EINVAL;
/* Try querying firmware first, use firmware format */
prio = __cxgb4_getapp(dev, app->selector - 1, app->protocol, 0);
if (prio < 0)
prio = dcb_ieee_getapp_mask(dev, app);
app->priority = ffs(prio) - 1;
return 0;
}
/* Write a new Application User Priority Map for the specified Application ID.
* Priority for IEEE dcb_app is an integer, with 0 being a valid value
*/
static int cxgb4_ieee_setapp(struct net_device *dev, struct dcb_app *app)
{
int ret;
if (!cxgb4_ieee_negotiation_complete(dev))
return -EINVAL;
if (!(app->selector && app->protocol))
return -EINVAL;
if (!(app->selector > IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
app->selector < IEEE_8021QAZ_APP_SEL_ANY))
return -EINVAL;
/* change selector to a format that firmware understands */
ret = __cxgb4_setapp(dev, app->selector - 1, app->protocol,
(1 << app->priority));
if (ret)
return ret;
return dcb_ieee_setapp(dev, app);
}
/* Return our DCBX parameters.
*/
static u8 cxgb4_getdcbx(struct net_device *dev)
{
struct port_info *pi = netdev2pinfo(dev);
/* This is already set by cxgb4_set_dcb_caps, so just return it */
return pi->dcb.supported;
}
/* Set our DCBX parameters.
*/
static u8 cxgb4_setdcbx(struct net_device *dev, u8 dcb_request)
{
struct port_info *pi = netdev2pinfo(dev);
/* Filter out requests which exceed our capabilities.
*/
if ((dcb_request & (CXGB4_DCBX_FW_SUPPORT | CXGB4_DCBX_HOST_SUPPORT))
!= dcb_request)
return 1;
/* Can't enable DCB if we haven't successfully negotiated it.
*/
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
return 1;
/* There's currently no mechanism to allow for the firmware DCBX
* negotiation to be changed from the Host Driver. If the caller
* requests exactly the same parameters that we already have then
* we'll allow them to be successfully "set" ...
*/
if (dcb_request != pi->dcb.supported)
return 1;
pi->dcb.supported = dcb_request;
return 0;
}
static int cxgb4_getpeer_app(struct net_device *dev,
struct dcb_peer_app_info *info, u16 *app_count)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int i, err = 0;
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
return 1;
info->willing = 0;
info->error = 0;
*app_count = 0;
for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
pcmd.u.dcb.app_priority.idx = *app_count;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB app table read failed with %d\n",
-err);
return err;
}
/* find first empty slot */
if (!pcmd.u.dcb.app_priority.protocolid)
break;
}
*app_count = i;
return err;
}
static int cxgb4_getpeerapp_tbl(struct net_device *dev, struct dcb_app *table)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
int i, err = 0;
if (pi->dcb.state != CXGB4_DCB_STATE_FW_ALLSYNCED)
return 1;
for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) {
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID;
pcmd.u.dcb.app_priority.idx = i;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB app table read failed with %d\n",
-err);
return err;
}
/* find first empty slot */
if (!pcmd.u.dcb.app_priority.protocolid)
break;
table[i].selector = pcmd.u.dcb.app_priority.sel_field;
table[i].protocol =
be16_to_cpu(pcmd.u.dcb.app_priority.protocolid);
table[i].priority =
ffs(pcmd.u.dcb.app_priority.user_prio_map) - 1;
}
return err;
}
/* Return Priority Group information.
*/
static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg)
{
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
u32 pgid;
int i, err;
/* We're always "willing" -- the Switch Fabric always dictates the
* DCBX parameters to us.
*/
pg->willing = true;
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err);
return err;
}
pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
for (i = 0; i < CXGB4_MAX_PRIORITY; i++)
pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF;
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n",
-err);
return err;
}
for (i = 0; i < CXGB4_MAX_PRIORITY; i++)
pg->pg_bw[i] = pcmd.u.dcb.pgrate.pgrate[i];
return 0;
}
/* Return Priority Flow Control information.
*/
static int cxgb4_cee_peer_getpfc(struct net_device *dev, struct cee_pfc *pfc)
{
struct port_info *pi = netdev2pinfo(dev);
cxgb4_getnumtcs(dev, DCB_NUMTCS_ATTR_PFC, &(pfc->tcs_supported));
pfc->pfc_en = pi->dcb.pfcen;
return 0;
}
const struct dcbnl_rtnl_ops cxgb4_dcb_ops = {
.ieee_getapp = cxgb4_ieee_getapp,
.ieee_setapp = cxgb4_ieee_setapp,
/* CEE std */
.getstate = cxgb4_getstate,
.setstate = cxgb4_setstate,
.getpgtccfgtx = cxgb4_getpgtccfg_tx,
.getpgbwgcfgtx = cxgb4_getpgbwgcfg_tx,
.getpgtccfgrx = cxgb4_getpgtccfg_rx,
.getpgbwgcfgrx = cxgb4_getpgbwgcfg_rx,
.setpgtccfgtx = cxgb4_setpgtccfg_tx,
.setpgbwgcfgtx = cxgb4_setpgbwgcfg_tx,
.setpfccfg = cxgb4_setpfccfg,
.getpfccfg = cxgb4_getpfccfg,
.setall = cxgb4_setall,
.getcap = cxgb4_getcap,
.getnumtcs = cxgb4_getnumtcs,
.setnumtcs = cxgb4_setnumtcs,
.getpfcstate = cxgb4_getpfcstate,
.setpfcstate = cxgb4_setpfcstate,
.getapp = cxgb4_getapp,
.setapp = cxgb4_setapp,
/* DCBX configuration */
.getdcbx = cxgb4_getdcbx,
.setdcbx = cxgb4_setdcbx,
/* peer apps */
.peer_getappinfo = cxgb4_getpeer_app,
.peer_getapptable = cxgb4_getpeerapp_tbl,
/* CEE peer */
.cee_peer_getpg = cxgb4_cee_peer_getpg,
.cee_peer_getpfc = cxgb4_cee_peer_getpfc,
};