bnxt: add support for Flow control setting using sysctl

Created sysctl node dev.bnxt.0.fc with following options.

A. dev.bnxt.0.fc.autoneg
B. dev.bnxt.0.fc.rx
C. dev.bnxt.0.fc.tx

Description:-
dev.bnxt.0.fc: flow ctrl
dev.bnxt.0.fc.autoneg: Enable or Disable Autoneg Flow Ctrl: 0 / 1
dev.bnxt.0.fc.rx: Enable or Disable Rx Flow Ctrl: 0 / 1
dev.bnxt.0.fc.tx: Enable or Disable Tx Flow Ctrl: 0 / 1

Submitted by:	Bhargava Chenna Marreddy <bhargava.marreddy@broadcom.com>
Reviewed by:	shurd, sbruno
Approved by:	sbruno (mentor)
Sponsored by:	Broadcom Limited
Differential Revision:	https://reviews.freebsd.org/D12599
This commit is contained in:
Stephen Hurd 2017-10-24 21:18:50 +00:00
parent ffbde5ea28
commit 7b79d6d61a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=324964
6 changed files with 187 additions and 90 deletions

View file

@ -211,6 +211,10 @@ __FBSDID("$FreeBSD$");
#define BNXT_HWRM_LOCK_DESTROY(_softc) mtx_destroy(&(_softc)->hwrm_lock)
#define BNXT_HWRM_LOCK_ASSERT(_softc) mtx_assert(&(_softc)->hwrm_lock, \
MA_OWNED)
#define BNXT_IS_FLOW_CTRL_CHANGED(link_info) \
((link_info->last_flow_ctrl.tx != link_info->flow_ctrl.tx) || \
(link_info->last_flow_ctrl.rx != link_info->flow_ctrl.rx) || \
(link_info->last_flow_ctrl.autoneg != link_info->flow_ctrl.autoneg))
/* Chip info */
#define BNXT_TSO_SIZE UINT16_MAX
@ -288,6 +292,12 @@ struct bnxt_bar_info {
int rid;
};
struct bnxt_flow_ctrl {
bool rx;
bool tx;
bool autoneg;
};
struct bnxt_link_info {
uint8_t media_type;
uint8_t transceiver;
@ -299,10 +309,8 @@ struct bnxt_link_info {
uint8_t last_link_up;
uint8_t duplex;
uint8_t last_duplex;
uint8_t pause;
uint8_t last_pause;
uint8_t auto_pause;
uint8_t force_pause;
struct bnxt_flow_ctrl flow_ctrl;
struct bnxt_flow_ctrl last_flow_ctrl;
uint8_t duplex_setting;
uint8_t auto_mode;
#define PHY_VER_LEN 3
@ -320,7 +328,6 @@ struct bnxt_link_info {
#define BNXT_AUTONEG_SPEED 1
#define BNXT_AUTONEG_FLOW_CTRL 2
uint8_t req_duplex;
uint8_t req_flow_ctrl;
uint16_t req_link_speed;
};
@ -596,6 +603,8 @@ struct bnxt_softc {
struct sysctl_oid *hw_stats_oid;
struct sysctl_ctx_list hw_lro_ctx;
struct sysctl_oid *hw_lro_oid;
struct sysctl_ctx_list flow_ctrl_ctx;
struct sysctl_oid *flow_ctrl_oid;
struct bnxt_ver_info *ver_info;
struct bnxt_nvram_info *nvm_info;

View file

@ -503,33 +503,28 @@ static void
bnxt_hwrm_set_pause_common(struct bnxt_softc *softc,
struct hwrm_port_phy_cfg_input *req)
{
if (softc->link_info.autoneg & BNXT_AUTONEG_FLOW_CTRL) {
struct bnxt_link_info *link_info = &softc->link_info;
if (link_info->flow_ctrl.autoneg) {
req->auto_pause =
HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_AUTONEG_PAUSE;
if (softc->link_info.req_flow_ctrl &
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)
if (link_info->flow_ctrl.rx)
req->auto_pause |=
HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_RX;
if (softc->link_info.req_flow_ctrl &
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX)
if (link_info->flow_ctrl.tx)
req->auto_pause |=
HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_RX;
HWRM_PORT_PHY_CFG_INPUT_AUTO_PAUSE_TX;
req->enables |=
htole32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_PAUSE);
} else {
if (softc->link_info.req_flow_ctrl &
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)
if (link_info->flow_ctrl.rx)
req->force_pause |=
HWRM_PORT_PHY_CFG_INPUT_FORCE_PAUSE_RX;
if (softc->link_info.req_flow_ctrl &
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX)
if (link_info->flow_ctrl.tx)
req->force_pause |=
HWRM_PORT_PHY_CFG_INPUT_FORCE_PAUSE_TX;
req->enables |=
htole32(HWRM_PORT_PHY_CFG_INPUT_ENABLES_FORCE_PAUSE);
req->auto_pause = req->force_pause;
req->enables |= htole32(
HWRM_PORT_PHY_CFG_INPUT_ENABLES_AUTO_PAUSE);
}
}
@ -563,26 +558,7 @@ bnxt_hwrm_set_eee(struct bnxt_softc *softc, struct hwrm_port_phy_cfg_input *req)
int
bnxt_hwrm_set_link_setting(struct bnxt_softc *softc, bool set_pause,
bool set_eee)
{
struct hwrm_port_phy_cfg_input req = {0};
if (softc->flags & BNXT_FLAG_NPAR)
return ENOTSUP;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_PHY_CFG);
if (set_pause)
bnxt_hwrm_set_pause_common(softc, &req);
bnxt_hwrm_set_link_common(softc, &req);
if (set_eee)
bnxt_hwrm_set_eee(softc, &req);
return hwrm_send_message(softc, &req, sizeof(req));
}
int
bnxt_hwrm_set_pause(struct bnxt_softc *softc)
bool set_eee, bool set_link)
{
struct hwrm_port_phy_cfg_input req = {0};
int rc;
@ -591,21 +567,32 @@ bnxt_hwrm_set_pause(struct bnxt_softc *softc)
return ENOTSUP;
bnxt_hwrm_cmd_hdr_init(softc, &req, HWRM_PORT_PHY_CFG);
bnxt_hwrm_set_pause_common(softc, &req);
if (set_pause) {
bnxt_hwrm_set_pause_common(softc, &req);
if (softc->link_info.autoneg & BNXT_AUTONEG_FLOW_CTRL)
if (softc->link_info.flow_ctrl.autoneg)
set_link = true;
}
if (set_link)
bnxt_hwrm_set_link_common(softc, &req);
if (set_eee)
bnxt_hwrm_set_eee(softc, &req);
BNXT_HWRM_LOCK(softc);
rc = _hwrm_send_message(softc, &req, sizeof(req));
if (!rc && !(softc->link_info.autoneg & BNXT_AUTONEG_FLOW_CTRL)) {
/* since changing of pause setting doesn't trigger any link
* change event, the driver needs to update the current pause
* result upon successfully return of the phy_cfg command */
softc->link_info.pause =
softc->link_info.force_pause = softc->link_info.req_flow_ctrl;
softc->link_info.auto_pause = 0;
bnxt_report_link(softc);
if (!rc) {
if (set_pause) {
/* since changing of 'force pause' setting doesn't
* trigger any link change event, the driver needs to
* update the current pause result upon successfully i
* return of the phy_cfg command */
if (!softc->link_info.flow_ctrl.autoneg)
bnxt_report_link(softc);
}
}
BNXT_HWRM_UNLOCK(softc);
return rc;
@ -1536,10 +1523,43 @@ bnxt_hwrm_port_phy_qcfg(struct bnxt_softc *softc)
link_info->phy_link_status = resp->link;
link_info->duplex = resp->duplex_cfg;
link_info->pause = resp->pause;
link_info->auto_mode = resp->auto_mode;
link_info->auto_pause = resp->auto_pause;
link_info->force_pause = resp->force_pause;
/*
* When AUTO_PAUSE_AUTONEG_PAUSE bit is set to 1,
* the advertisement of pause is enabled.
* 1. When the auto_mode is not set to none and this flag is set to 1,
* then the auto_pause bits on this port are being advertised and
* autoneg pause results are being interpreted.
* 2. When the auto_mode is not set to none and this flag is set to 0,
* the pause is forced as indicated in force_pause, and also
* advertised as auto_pause bits, but the autoneg results are not
* interpreted since the pause configuration is being forced.
* 3. When the auto_mode is set to none and this flag is set to 1,
* auto_pause bits should be ignored and should be set to 0.
*/
link_info->flow_ctrl.autoneg = false;
link_info->flow_ctrl.tx = false;
link_info->flow_ctrl.rx = false;
if ((resp->auto_mode) &&
(resp->auto_pause & BNXT_AUTO_PAUSE_AUTONEG_PAUSE)) {
link_info->flow_ctrl.autoneg = true;
}
if (link_info->flow_ctrl.autoneg) {
if (resp->auto_pause & BNXT_PAUSE_TX)
link_info->flow_ctrl.tx = true;
if (resp->auto_pause & BNXT_PAUSE_RX)
link_info->flow_ctrl.rx = true;
} else {
if (resp->force_pause & BNXT_PAUSE_TX)
link_info->flow_ctrl.tx = true;
if (resp->force_pause & BNXT_PAUSE_RX)
link_info->flow_ctrl.rx = true;
}
link_info->duplex_setting = resp->duplex_cfg;
if (link_info->phy_link_status == HWRM_PORT_PHY_QCFG_OUTPUT_LINK_LINK)
link_info->link_speed = le16toh(resp->link_speed);

View file

@ -32,6 +32,11 @@ __FBSDID("$FreeBSD$");
#ifndef _BNXT_HWRM_H
#define _BNXT_HWRM_H
#define BNXT_PAUSE_TX (HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX)
#define BNXT_PAUSE_RX (HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)
#define BNXT_AUTO_PAUSE_AUTONEG_PAUSE \
(HWRM_PORT_PHY_QCFG_OUTPUT_AUTO_PAUSE_AUTONEG_PAUSE)
/* HWRM Function Prototypes */
int bnxt_alloc_hwrm_dma_mem(struct bnxt_softc *softc);
void bnxt_free_hwrm_dma_mem(struct bnxt_softc *softc);
@ -45,8 +50,8 @@ int bnxt_hwrm_func_drv_unrgtr(struct bnxt_softc *softc, bool shutdown);
int bnxt_hwrm_func_qcaps(struct bnxt_softc *softc);
int bnxt_hwrm_func_qcfg(struct bnxt_softc *softc);
int bnxt_hwrm_func_reset(struct bnxt_softc *softc);
int bnxt_hwrm_set_link_setting(struct bnxt_softc *, bool set_pause,
bool set_eee);
int bnxt_hwrm_set_link_setting(struct bnxt_softc *softc, bool set_pause,
bool set_eee, bool set_link);
int bnxt_hwrm_set_pause(struct bnxt_softc *softc);
int bnxt_hwrm_vnic_ctx_alloc(struct bnxt_softc *softc, uint16_t *ctx_id);
int bnxt_hwrm_vnic_cfg(struct bnxt_softc *softc, struct bnxt_vnic_info *vnic);

View file

@ -94,6 +94,16 @@ bnxt_init_sysctl_ctx(struct bnxt_softc *softc)
return ENOMEM;
}
sysctl_ctx_init(&softc->flow_ctrl_ctx);
ctx = device_get_sysctl_ctx(softc->dev);
softc->flow_ctrl_oid = SYSCTL_ADD_NODE(ctx,
SYSCTL_CHILDREN(device_get_sysctl_tree(softc->dev)), OID_AUTO,
"fc", CTLFLAG_RD, 0, "flow ctrl");
if (!softc->flow_ctrl_oid) {
sysctl_ctx_free(&softc->flow_ctrl_ctx);
return ENOMEM;
}
return 0;
}
@ -132,6 +142,14 @@ bnxt_free_sysctl_ctx(struct bnxt_softc *softc)
softc->hw_lro_oid = NULL;
}
if (softc->flow_ctrl_oid != NULL) {
orc = sysctl_ctx_free(&softc->flow_ctrl_ctx);
if (orc)
rc = orc;
else
softc->flow_ctrl_oid = NULL;
}
return rc;
}
@ -1258,6 +1276,62 @@ BNXT_HW_LRO_FN(bnxt_hw_lro_set_max_agg_segs, max_agg_segs)
BNXT_HW_LRO_FN(bnxt_hw_lro_set_max_aggs, max_aggs)
BNXT_HW_LRO_FN(bnxt_hw_lro_set_min_agg_len, min_agg_len)
#define BNXT_FLOW_CTRL_FN(fn_name, arg) \
static int \
fn_name(SYSCTL_HANDLER_ARGS) { \
struct bnxt_softc *softc = arg1; \
int rc; \
int val; \
\
if (softc == NULL) \
return EBUSY; \
\
val = softc->link_info.flow_ctrl.arg; \
rc = sysctl_handle_int(oidp, &val, 0, req); \
if (rc || !req->newptr) \
return rc; \
\
if (val) \
val = 1; \
\
if (softc->link_info.flow_ctrl.arg != val) { \
softc->link_info.flow_ctrl.arg = val; \
rc = bnxt_hwrm_set_link_setting(softc, true, false, false);\
rc = bnxt_hwrm_port_phy_qcfg(softc); \
} \
\
return rc; \
}
BNXT_FLOW_CTRL_FN(bnxt_flow_ctrl_tx, tx)
BNXT_FLOW_CTRL_FN(bnxt_flow_ctrl_rx, rx)
BNXT_FLOW_CTRL_FN(bnxt_flow_ctrl_autoneg, autoneg)
int
bnxt_create_pause_fc_sysctls(struct bnxt_softc *softc)
{
struct sysctl_oid *oid = softc->flow_ctrl_oid;
if (!oid)
return ENOMEM;
SYSCTL_ADD_PROC(&softc->flow_ctrl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
"tx", CTLTYPE_INT|CTLFLAG_RWTUN, softc, 0,
bnxt_flow_ctrl_tx, "A",
"Enable or Disable Tx Flow Ctrl: 0 / 1");
SYSCTL_ADD_PROC(&softc->flow_ctrl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
"rx", CTLTYPE_INT|CTLFLAG_RWTUN, softc, 0,
bnxt_flow_ctrl_rx, "A",
"Enable or Disable Tx Flow Ctrl: 0 / 1");
SYSCTL_ADD_PROC(&softc->flow_ctrl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
"autoneg", CTLTYPE_INT|CTLFLAG_RWTUN, softc, 0,
bnxt_flow_ctrl_autoneg, "A",
"Enable or Disable Autoneg Flow Ctrl: 0 / 1");
return 0;
}
int
bnxt_create_hw_lro_sysctls(struct bnxt_softc *softc)
{

View file

@ -41,3 +41,4 @@ int bnxt_create_nvram_sysctls(struct bnxt_nvram_info *ni);
int bnxt_create_config_sysctls_pre(struct bnxt_softc *softc);
int bnxt_create_config_sysctls_post(struct bnxt_softc *softc);
int bnxt_create_hw_lro_sysctls(struct bnxt_softc *softc);
int bnxt_create_pause_fc_sysctls(struct bnxt_softc *softc);

View file

@ -878,6 +878,10 @@ bnxt_attach_pre(if_ctx_t ctx)
if (rc)
goto failed;
rc = bnxt_create_pause_fc_sysctls(softc);
if (rc)
goto failed;
/* Initialize the vlan list */
SLIST_INIT(&softc->vnic_info.vlan_tags);
softc->vnic_info.vlan_tag_list.idi_vaddr = NULL;
@ -1377,13 +1381,10 @@ bnxt_media_status(if_ctx_t ctx, struct ifmediareq * ifmr)
return;
}
if (link_info->pause == (HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX |
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX))
ifmr->ifm_active |= (IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE);
else if (link_info->pause == HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX)
ifmr->ifm_active |= IFM_ETH_TXPAUSE;
else if (link_info->pause == HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)
if (link_info->flow_ctrl.rx)
ifmr->ifm_active |= IFM_ETH_RXPAUSE;
if (link_info->flow_ctrl.tx)
ifmr->ifm_active |= IFM_ETH_TXPAUSE;
bnxt_report_link(softc);
return;
@ -1471,7 +1472,7 @@ bnxt_media_change(if_ctx_t ctx)
softc->link_info.autoneg |= BNXT_AUTONEG_SPEED;
break;
}
rc = bnxt_hwrm_set_link_setting(softc, true, true);
rc = bnxt_hwrm_set_link_setting(softc, true, true, true);
bnxt_media_status(softc->ctx, &ifmr);
return rc;
}
@ -2096,18 +2097,6 @@ bnxt_probe_phy(struct bnxt_softc *softc)
if (link_info->auto_mode != HWRM_PORT_PHY_QCFG_OUTPUT_AUTO_MODE_NONE)
link_info->autoneg |= BNXT_AUTONEG_SPEED;
if (link_info->auto_pause & (HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX |
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)) {
if (link_info->auto_pause == (
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX |
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX))
link_info->autoneg |= BNXT_AUTONEG_FLOW_CTRL;
link_info->req_flow_ctrl = link_info->auto_pause;
} else if (link_info->force_pause & (
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX |
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)) {
link_info->req_flow_ctrl = link_info->force_pause;
}
link_info->req_duplex = link_info->duplex_setting;
if (link_info->autoneg & BNXT_AUTONEG_SPEED)
link_info->req_link_speed = link_info->auto_link_speed;
@ -2352,47 +2341,46 @@ bnxt_update_link(struct bnxt_softc *softc, bool chng_link_state)
void
bnxt_report_link(struct bnxt_softc *softc)
{
struct bnxt_link_info *link_info = &softc->link_info;
const char *duplex = NULL, *flow_ctrl = NULL;
if (softc->link_info.link_up == softc->link_info.last_link_up) {
if (!softc->link_info.link_up)
if (link_info->link_up == link_info->last_link_up) {
if (!link_info->link_up)
return;
if (softc->link_info.pause == softc->link_info.last_pause &&
softc->link_info.duplex == softc->link_info.last_duplex)
if ((link_info->duplex == link_info->last_duplex) &&
(!(BNXT_IS_FLOW_CTRL_CHANGED(link_info))))
return;
}
if (softc->link_info.link_up) {
if (softc->link_info.duplex ==
if (link_info->link_up) {
if (link_info->duplex ==
HWRM_PORT_PHY_QCFG_OUTPUT_DUPLEX_CFG_FULL)
duplex = "full duplex";
else
duplex = "half duplex";
if (softc->link_info.pause == (
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX |
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX))
if (link_info->flow_ctrl.tx & link_info->flow_ctrl.rx)
flow_ctrl = "FC - receive & transmit";
else if (softc->link_info.pause ==
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_TX)
else if (link_info->flow_ctrl.tx)
flow_ctrl = "FC - transmit";
else if (softc->link_info.pause ==
HWRM_PORT_PHY_QCFG_OUTPUT_PAUSE_RX)
else if (link_info->flow_ctrl.rx)
flow_ctrl = "FC - receive";
else
flow_ctrl = "FC - none";
iflib_link_state_change(softc->ctx, LINK_STATE_UP,
IF_Gbps(100));
device_printf(softc->dev, "Link is UP %s, %s - %d Mbps \n", duplex,
flow_ctrl, (softc->link_info.link_speed * 100));
flow_ctrl, (link_info->link_speed * 100));
} else {
iflib_link_state_change(softc->ctx, LINK_STATE_DOWN,
bnxt_get_baudrate(&softc->link_info));
device_printf(softc->dev, "Link is Down\n");
}
softc->link_info.last_link_up = softc->link_info.link_up;
softc->link_info.last_pause = softc->link_info.pause;
softc->link_info.last_duplex = softc->link_info.duplex;
link_info->last_link_up = link_info->link_up;
link_info->last_duplex = link_info->duplex;
link_info->last_flow_ctrl.tx = link_info->flow_ctrl.tx;
link_info->last_flow_ctrl.rx = link_info->flow_ctrl.rx;
link_info->last_flow_ctrl.autoneg = link_info->flow_ctrl.autoneg;
}
static int