2026-01-29 22:25:33 +08:00

1796 lines
44 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Huawei HiNIC PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*
* 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.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include "ossl_knl.h"
#include "hinic_hw.h"
#include "hinic_hw_mgmt.h"
#include "hinic_lld.h"
#include "hinic_nic_cfg.h"
#include "hinic_nic_dev.h"
#include "hinic_dcb.h"
#define DCB_HW_CFG_CHG 0
#define DCB_HW_CFG_NO_CHG 1
#define DCB_HW_CFG_ERR 2
#define DCB_CFG_CHG_PG_TX 0x1
#define DCB_CFG_CHG_PG_RX 0x2
#define DCB_CFG_CHG_PFC 0x4
#define DCB_CFG_CHG_UP_COS 0x8
u8 hinic_dcb_get_tc(struct hinic_dcb_config *dcb_cfg, int dir, u8 up)
{
struct hinic_tc_cfg *tc_cfg = &dcb_cfg->tc_cfg[0];
u8 tc = dcb_cfg->pg_tcs;
if (!tc)
return 0;
for (tc--; tc; tc--) {
if (BIT(up) & tc_cfg[tc].path[dir].up_map)
break;
}
return tc;
}
#define UP_MAPPING(prio) ((u8)(1U << ((HINIC_DCB_UP_MAX - 1) - (prio))))
void hinic_dcb_config_init(struct hinic_nic_dev *nic_dev,
struct hinic_dcb_config *dcb_cfg)
{
struct hinic_tc_cfg *tc;
int i;
memset(dcb_cfg->tc_cfg, 0, sizeof(dcb_cfg->tc_cfg));
tc = &dcb_cfg->tc_cfg[0];
/* All TC mapping to PG0 */
for (i = 0; i < dcb_cfg->pg_tcs; i++) {
tc = &dcb_cfg->tc_cfg[i];
tc->path[HINIC_DCB_CFG_TX].pg_id = 0;
tc->path[HINIC_DCB_CFG_TX].bw_pct = 100;
tc->path[HINIC_DCB_CFG_TX].up_map = UP_MAPPING(i);
tc->path[HINIC_DCB_CFG_RX].pg_id = 0;
tc->path[HINIC_DCB_CFG_RX].bw_pct = 100;
tc->path[HINIC_DCB_CFG_RX].up_map = UP_MAPPING(i);
tc->pfc_en = false;
}
for (; i < HINIC_DCB_UP_MAX; i++) {
tc->path[HINIC_DCB_CFG_TX].up_map |= UP_MAPPING(i);
tc->path[HINIC_DCB_CFG_RX].up_map |= UP_MAPPING(i);
}
memset(dcb_cfg->bw_pct, 0, sizeof(dcb_cfg->bw_pct));
/* Use PG0 in default, PG0's bw is 100% */
dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][0] = 100;
dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][0] = 100;
dcb_cfg->pfc_state = false;
}
void hinic_init_ieee_settings(struct hinic_nic_dev *nic_dev)
{
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
struct ieee_ets *ets = &nic_dev->hinic_ieee_ets_default;
struct ieee_pfc *pfc = &nic_dev->hinic_ieee_pfc;
struct hinic_tc_attr *tc_attr;
u8 i;
memset(ets, 0x0, sizeof(struct ieee_ets));
memset(&nic_dev->hinic_ieee_ets, 0x0, sizeof(struct ieee_ets));
ets->ets_cap = dcb_cfg->pg_tcs;
for (i = 0; i < HINIC_DCB_TC_MAX; i++) {
tc_attr = &dcb_cfg->tc_cfg[i].path[HINIC_DCB_CFG_TX];
ets->tc_tsa[i] = tc_attr->prio_type ?
IEEE8021Q_TSA_STRICT : IEEE8021Q_TSA_ETS;
ets->tc_tx_bw[i] = nic_dev->dcb_cfg.bw_pct[HINIC_DCB_CFG_TX][i];
ets->tc_rx_bw[i] = nic_dev->dcb_cfg.bw_pct[HINIC_DCB_CFG_RX][i];
ets->prio_tc[i] = hinic_dcb_get_tc(dcb_cfg,
HINIC_DCB_CFG_TX, i);
}
memcpy(&nic_dev->hinic_ieee_ets, ets, sizeof(struct ieee_ets));
memset(pfc, 0x0, sizeof(struct ieee_pfc));
pfc->pfc_cap = dcb_cfg->pfc_tcs;
for (i = 0; i < dcb_cfg->pfc_tcs; i++) {
if (dcb_cfg->tc_cfg[i].pfc_en)
pfc->pfc_en |= (u8)BIT(i);
}
}
static int hinic_set_up_cos_map(struct hinic_nic_dev *nic_dev,
u8 num_cos, u8 *cos_up)
{
u8 up_valid_bitmap, up_cos[HINIC_DCB_UP_MAX] = {0};
u8 i;
up_valid_bitmap = 0;
for (i = 0; i < num_cos; i++) {
if (cos_up[i] >= HINIC_DCB_UP_MAX) {
hinic_info(nic_dev, drv, "Invalid up %d mapping to cos %d\n",
cos_up[i], i);
return -EFAULT;
}
if (i > 0 && cos_up[i] >= cos_up[i - 1]) {
hinic_info(nic_dev, drv,
"Invalid priority order, should be descending cos[%d]=%d, cos[%d]=%d\n",
i, cos_up[i], i - 1, cos_up[i - 1]);
return -EINVAL;
}
up_valid_bitmap |= (u8)BIT(cos_up[i]);
if (i == (num_cos - 1))
up_cos[cos_up[i]] = nic_dev->default_cos_id;
else
up_cos[cos_up[i]] = i; /* reverse up and cos */
}
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
if (up_valid_bitmap & (u8)BIT(i))
continue;
up_cos[i] = nic_dev->default_cos_id;
}
nic_dev->up_valid_bitmap = up_valid_bitmap;
memcpy(nic_dev->up_cos, up_cos, sizeof(up_cos));
return hinic_sq_cos_mapping(nic_dev->netdev);
}
static int hinic_init_up_cos_map(struct hinic_nic_dev *nic_dev, u8 num_cos)
{
u8 default_map[HINIC_DCB_COS_MAX] = {0};
bool setted = false;
u8 max_cos, cos_id, up;
int err;
max_cos = hinic_max_num_cos(nic_dev->hwdev);
if (!max_cos || ((max_cos - 1) < nic_dev->default_cos_id)) {
hinic_err(nic_dev, drv, "Max_cos is %d, default cos id %d\n",
max_cos, nic_dev->default_cos_id);
return -EFAULT;
}
err = hinic_get_chip_cos_up_map(nic_dev->pdev, &setted, default_map);
if (err) {
hinic_err(nic_dev, drv, "Get chip cos_up map failed\n");
return -EFAULT;
}
if (!setted) {
/* Use (max_cos-1)~0 as default user priority and mapping
* to cos0~(max_cos-1)
*/
up = nic_dev->max_cos - 1;
for (cos_id = 0; cos_id < nic_dev->max_cos; cos_id++, up--)
default_map[cos_id] = up;
}
return hinic_set_up_cos_map(nic_dev, num_cos, default_map);
}
int hinic_dcb_init(struct hinic_nic_dev *nic_dev)
{
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
u8 num_cos, support_cos = 0, default_cos = 0;
u8 i, cos_valid_bitmap;
int err;
if (HINIC_FUNC_IS_VF(nic_dev->hwdev))
return 0;
cos_valid_bitmap = hinic_cos_valid_bitmap(nic_dev->hwdev);
if (!cos_valid_bitmap) {
hinic_err(nic_dev, drv, "None cos supported\n");
return -EFAULT;
}
for (i = 0; i < HINIC_DCB_COS_MAX; i++) {
if (cos_valid_bitmap & BIT(i)) {
support_cos++;
default_cos = i; /* Find max cos id as default cos */
}
}
hinic_info(nic_dev, drv, "Support num cos %d, default cos %d\n",
support_cos, default_cos);
num_cos = (u8)(1U << ilog2(support_cos));
if (num_cos != support_cos)
hinic_info(nic_dev, drv, "Adjust num_cos from %d to %d\n",
support_cos, num_cos);
nic_dev->dcbx_cap = 0;
nic_dev->max_cos = num_cos;
nic_dev->default_cos_id = default_cos;
dcb_cfg->pfc_tcs = nic_dev->max_cos;
dcb_cfg->pg_tcs = nic_dev->max_cos;
err = hinic_init_up_cos_map(nic_dev, num_cos);
if (err) {
hinic_info(nic_dev, drv, "Initialize up_cos mapping failed\n");
return -EFAULT;
}
hinic_dcb_config_init(nic_dev, dcb_cfg);
nic_dev->dcb_changes = DCB_CFG_CHG_PFC | DCB_CFG_CHG_PG_TX |
DCB_CFG_CHG_PG_RX | DCB_CFG_CHG_UP_COS;
nic_dev->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE;
memcpy(&nic_dev->tmp_dcb_cfg, &nic_dev->dcb_cfg,
sizeof(nic_dev->tmp_dcb_cfg));
memcpy(&nic_dev->save_dcb_cfg, &nic_dev->dcb_cfg,
sizeof(nic_dev->save_dcb_cfg));
hinic_init_ieee_settings(nic_dev);
sema_init(&nic_dev->dcb_sem, 1);
return 0;
}
void hinic_set_prio_tc_map(struct hinic_nic_dev *nic_dev)
{
struct net_device *netdev = nic_dev->netdev;
u8 prio, tc;
for (prio = 0; prio < HINIC_DCB_UP_MAX; prio++) {
tc = nic_dev->up_cos[prio];
if (tc == nic_dev->default_cos_id)
tc = nic_dev->max_cos - 1;
netdev_set_prio_tc_map(netdev, prio, tc);
}
}
int hinic_setup_tc(struct net_device *netdev, u8 tc)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int err;
if (!FUNC_SUPPORT_DCB(nic_dev->hwdev)) {
nicif_err(nic_dev, drv, netdev,
"Current function don't support DCB\n");
return -EOPNOTSUPP;
}
if (tc > nic_dev->dcb_cfg.pg_tcs) {
nicif_err(nic_dev, drv, netdev, "Invalid num_tc: %d, max tc: %d\n",
tc, nic_dev->dcb_cfg.pg_tcs);
return -EINVAL;
}
if (netif_running(netdev)) {
err = hinic_close(netdev);
if (err) {
nicif_err(nic_dev, drv, netdev, "Failed to close device\n");
return -EFAULT;
}
}
if (tc) {
if (tc & (tc - 1)) {
nicif_err(nic_dev, drv, netdev,
"Invalid num_tc: %d, must be power of 2\n",
tc);
return -EINVAL;
}
netdev_set_num_tc(netdev, tc);
hinic_set_prio_tc_map(nic_dev);
set_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
} else {
netdev_reset_tc(netdev);
clear_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
}
hinic_sq_cos_mapping(netdev);
if (netif_running(netdev)) {
err = hinic_open(netdev);
if (err) {
nicif_err(nic_dev, drv, netdev, "Failed to open device\n");
return -EFAULT;
}
} else {
hinic_update_num_qps(netdev);
}
hinic_configure_dcb(netdev);
return 0;
}
u8 hinic_setup_dcb_tool(struct net_device *netdev, u8 *dcb_en, bool wr_flag)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int err = 0;
if (wr_flag) {
if (nic_dev->max_qps < nic_dev->dcb_cfg.pg_tcs && *dcb_en) {
nicif_err(nic_dev, drv, netdev,
"max_qps: %d is less than %d\n",
nic_dev->max_qps, nic_dev->dcb_cfg.pg_tcs);
return 1;
}
if (*dcb_en)
set_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
else
clear_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
/*hinic_setup_tc need get the nic_mutex lock again */
mutex_unlock(&nic_dev->nic_mutex);
/* kill the rtnl assert warning */
rtnl_lock();
err = hinic_setup_tc(netdev,
*dcb_en ? nic_dev->dcb_cfg.pg_tcs : 0);
rtnl_unlock();
mutex_lock(&nic_dev->nic_mutex);
if (!err)
nicif_info(nic_dev, drv, netdev, "%s DCB\n",
*dcb_en ? "Enable" : "Disable");
} else {
*dcb_en = (u8)test_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
}
return !!err;
}
static u8 hinic_dcbnl_get_state(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
return !!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
}
static u8 hinic_dcbnl_set_state(struct net_device *netdev, u8 state)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u8 curr_state = !!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags);
int err = 0;
if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return 1;
if (state == curr_state)
return 0;
if (nic_dev->max_qps < nic_dev->dcb_cfg.pg_tcs && state) {
nicif_err(nic_dev, drv, netdev,
"max_qps: %d is less than %d\n",
nic_dev->max_qps, nic_dev->dcb_cfg.pg_tcs);
return 1;
}
err = hinic_setup_tc(netdev, state ? nic_dev->dcb_cfg.pg_tcs : 0);
if (!err)
nicif_info(nic_dev, drv, netdev, "%s DCB\n",
state ? "Enable" : "Disable");
return !!err;
}
static void hinic_dcbnl_get_perm_hw_addr(struct net_device *netdev,
u8 *perm_addr)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int err;
memset(perm_addr, 0xff, MAX_ADDR_LEN);
err = hinic_get_default_mac(nic_dev->hwdev, perm_addr);
if (err)
nicif_err(nic_dev, drv, netdev, "Failed to get default mac\n");
}
void hinic_dcbnl_set_ets_tc_tool(struct net_device *netdev, u8 tc[], bool flag)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_tc_cfg *cfg = nic_dev->tmp_dcb_cfg.tc_cfg;
struct hinic_tc_cfg *tc_conf = nic_dev->dcb_cfg.tc_cfg;
u8 i, tc_tmp, j;
if (flag) {
/*need to clear first */
for (i = 0; i < HINIC_DCB_TC_MAX; i++) {
cfg[i].path[HINIC_DCB_CFG_TX].up_map = 0;
cfg[i].path[HINIC_DCB_CFG_RX].up_map = 0;
}
for (i = 0; i < HINIC_DCB_TC_MAX; i++) {
tc_tmp = tc[i];
cfg[tc_tmp].path[HINIC_DCB_CFG_TX].up_map |= (u8)BIT(i);
cfg[tc_tmp].path[HINIC_DCB_CFG_RX].up_map |= (u8)BIT(i);
cfg[tc_tmp].path[HINIC_DCB_CFG_TX].pg_id = (u8)tc_tmp;
cfg[tc_tmp].path[HINIC_DCB_CFG_RX].pg_id = (u8)tc_tmp;
}
} else {
for (i = 0; i < HINIC_DCB_TC_MAX; i++) {
for (j = 0; j < HINIC_DCB_TC_MAX; j++) {
if (tc_conf[i].path[HINIC_DCB_CFG_TX].up_map &
(u8)BIT(j)) {
tc[j] = i;
}
}
}
}
}
void hinic_dcbnl_set_ets_pecent_tool(struct net_device *netdev,
u8 percent[], bool flag)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int i;
if (flag) {
for (i = 0; i < HINIC_DCB_COS_MAX; i++) {
nic_dev->tmp_dcb_cfg.bw_pct[HINIC_DCB_CFG_TX][i] =
percent[i];
nic_dev->tmp_dcb_cfg.bw_pct[HINIC_DCB_CFG_RX][i] =
percent[i];
}
} else {
for (i = 0; i < HINIC_DCB_COS_MAX; i++)
percent[i] =
nic_dev->dcb_cfg.bw_pct[HINIC_DCB_CFG_TX][i];
}
}
static void hinic_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
u8 prio, u8 pg_id, u8 bw_pct,
u8 up_map)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (tc > HINIC_DCB_TC_MAX - 1)
return;
if (prio != DCB_ATTR_VALUE_UNDEFINED)
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].prio_type = prio;
if (pg_id != DCB_ATTR_VALUE_UNDEFINED)
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].pg_id = pg_id;
if (bw_pct != DCB_ATTR_VALUE_UNDEFINED)
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].bw_pct = bw_pct;
/* if all priority mapping to the same tc,
* up_map is 0xFF, and it's a valid value
*/
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[0].up_map = up_map;
}
static void hinic_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
u8 bw_pct)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (bwg_id > HINIC_DCB_PG_MAX - 1)
return;
nic_dev->tmp_dcb_cfg.bw_pct[0][bwg_id] = bw_pct;
}
static void hinic_dcbnl_set_pg_tc_cfg_rx(struct net_device *netdev, int tc,
u8 prio, u8 pg_id, u8 bw_pct,
u8 up_map)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (tc > HINIC_DCB_TC_MAX - 1)
return;
if (prio != DCB_ATTR_VALUE_UNDEFINED)
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].prio_type = prio;
if (pg_id != DCB_ATTR_VALUE_UNDEFINED)
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].pg_id = pg_id;
if (bw_pct != DCB_ATTR_VALUE_UNDEFINED)
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].bw_pct = bw_pct;
nic_dev->tmp_dcb_cfg.tc_cfg[tc].path[1].up_map = up_map;
}
static void hinic_dcbnl_set_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
u8 bw_pct)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (bwg_id > HINIC_DCB_PG_MAX - 1)
return;
nic_dev->tmp_dcb_cfg.bw_pct[1][bwg_id] = bw_pct;
}
static void hinic_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int tc,
u8 *prio, u8 *pg_id, u8 *bw_pct,
u8 *up_map)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (tc > HINIC_DCB_TC_MAX - 1)
return;
*prio = nic_dev->dcb_cfg.tc_cfg[tc].path[0].prio_type;
*pg_id = nic_dev->dcb_cfg.tc_cfg[tc].path[0].pg_id;
*bw_pct = nic_dev->dcb_cfg.tc_cfg[tc].path[0].bw_pct;
*up_map = nic_dev->dcb_cfg.tc_cfg[tc].path[0].up_map;
}
static void hinic_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int bwg_id,
u8 *bw_pct)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (bwg_id > HINIC_DCB_PG_MAX - 1)
return;
*bw_pct = nic_dev->dcb_cfg.bw_pct[0][bwg_id];
}
static void hinic_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int tc,
u8 *prio, u8 *pg_id, u8 *bw_pct,
u8 *up_map)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (tc > HINIC_DCB_TC_MAX - 1)
return;
*prio = nic_dev->dcb_cfg.tc_cfg[tc].path[1].prio_type;
*pg_id = nic_dev->dcb_cfg.tc_cfg[tc].path[1].pg_id;
*bw_pct = nic_dev->dcb_cfg.tc_cfg[tc].path[1].bw_pct;
*up_map = nic_dev->dcb_cfg.tc_cfg[tc].path[1].up_map;
}
static void hinic_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int bwg_id,
u8 *bw_pct)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (bwg_id > HINIC_DCB_PG_MAX - 1)
return;
*bw_pct = nic_dev->dcb_cfg.bw_pct[1][bwg_id];
}
void hinic_dcbnl_set_pfc_cfg_tool(struct net_device *netdev, u8 setting)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u8 i;
for (i = 0; i < HINIC_DCB_TC_MAX; i++) {
nic_dev->tmp_dcb_cfg.tc_cfg[i].pfc_en = !!(setting & BIT(i));
if (nic_dev->tmp_dcb_cfg.tc_cfg[i].pfc_en !=
nic_dev->dcb_cfg.tc_cfg[i].pfc_en) {
nic_dev->tmp_dcb_cfg.pfc_state = true;
}
}
}
void hinic_dcbnl_set_ets_strict_tool(struct net_device *netdev,
u8 *setting, bool flag)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_tc_cfg *cfg = nic_dev->tmp_dcb_cfg.tc_cfg;
struct hinic_tc_cfg *conf = nic_dev->dcb_cfg.tc_cfg;
u8 i;
if (flag) {
for (i = 0; i < HINIC_DCB_COS_MAX; i++) {
cfg[i].path[HINIC_DCB_CFG_TX].prio_type =
!!(*setting & BIT(i)) ? 2 : 0;
cfg[i].path[HINIC_DCB_CFG_RX].prio_type =
!!(*setting & BIT(i)) ? 2 : 0;
}
} else {
for (i = 0; i < HINIC_DCB_COS_MAX; i++) {
*setting = *setting |
(u8)((u32)(!!(conf[i].path[0].prio_type)) << i);
}
}
}
void hinic_dcbnl_set_pfc_en_tool(struct net_device *netdev,
u8 *value, bool flag)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (flag)
nic_dev->tmp_dcb_cfg.pfc_state = !!(*value);
else
*value = nic_dev->tmp_dcb_cfg.pfc_state;
}
void hinic_dcbnl_set_ets_en_tool(struct net_device *netdev,
u8 *value, bool flag)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (flag) {
if (*value)
set_bit(HINIC_ETS_ENABLE, &nic_dev->flags);
else
clear_bit(HINIC_ETS_ENABLE, &nic_dev->flags);
} else {
*value = (u8)test_bit(HINIC_ETS_ENABLE, &nic_dev->flags);
}
}
static void hinic_dcbnl_set_pfc_cfg(struct net_device *netdev, int prio,
u8 setting)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
nic_dev->tmp_dcb_cfg.tc_cfg[prio].pfc_en = !!setting;
if (nic_dev->tmp_dcb_cfg.tc_cfg[prio].pfc_en !=
nic_dev->dcb_cfg.tc_cfg[prio].pfc_en)
nic_dev->tmp_dcb_cfg.pfc_state = true;
}
void hinic_dcbnl_get_pfc_cfg_tool(struct net_device *netdev, u8 *setting)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u8 i;
for (i = 0; i < HINIC_DCB_TC_MAX; i++) {
*setting = *setting |
(u8)((u32)(nic_dev->dcb_cfg.tc_cfg[i].pfc_en) << i);
}
}
void hinic_dcbnl_get_tc_num_tool(struct net_device *netdev, u8 *tc_num)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
*tc_num = nic_dev->max_cos;
}
static void hinic_dcbnl_get_pfc_cfg(struct net_device *netdev, int prio,
u8 *setting)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (prio > HINIC_DCB_TC_MAX - 1)
return;
*setting = nic_dev->dcb_cfg.tc_cfg[prio].pfc_en;
}
static u8 hinic_dcbnl_getcap(struct net_device *netdev, int cap_id,
u8 *dcb_cap)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
switch (cap_id) {
case DCB_CAP_ATTR_PG:
*dcb_cap = true;
break;
case DCB_CAP_ATTR_PFC:
*dcb_cap = true;
break;
case DCB_CAP_ATTR_UP2TC:
*dcb_cap = false;
break;
case DCB_CAP_ATTR_PG_TCS:
*dcb_cap = 0x80;
break;
case DCB_CAP_ATTR_PFC_TCS:
*dcb_cap = 0x80;
break;
case DCB_CAP_ATTR_GSP:
*dcb_cap = true;
break;
case DCB_CAP_ATTR_BCN:
*dcb_cap = false;
break;
case DCB_CAP_ATTR_DCBX:
*dcb_cap = nic_dev->dcbx_cap;
break;
default:
*dcb_cap = false;
break;
}
return 0;
}
static u8 hinic_sync_tc_cfg(struct hinic_tc_cfg *tc_dst,
struct hinic_tc_cfg *tc_src, int dir)
{
u8 tc_dir_change = (dir == HINIC_DCB_CFG_TX) ?
DCB_CFG_CHG_PG_TX : DCB_CFG_CHG_PG_RX;
u8 changes = 0;
if (tc_dst->path[dir].prio_type != tc_src->path[dir].prio_type) {
tc_dst->path[dir].prio_type = tc_src->path[dir].prio_type;
changes |= tc_dir_change;
}
if (tc_dst->path[dir].pg_id != tc_src->path[dir].pg_id) {
tc_dst->path[dir].pg_id = tc_src->path[dir].pg_id;
changes |= tc_dir_change;
}
if (tc_dst->path[dir].bw_pct != tc_src->path[dir].bw_pct) {
tc_dst->path[dir].bw_pct = tc_src->path[dir].bw_pct;
changes |= tc_dir_change;
}
if (tc_dst->path[dir].up_map != tc_src->path[dir].up_map) {
tc_dst->path[dir].up_map = tc_src->path[dir].up_map;
changes |= (tc_dir_change | DCB_CFG_CHG_PFC);
}
return changes;
}
static u8 hinic_sync_dcb_cfg(struct hinic_nic_dev *nic_dev)
{
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
struct hinic_dcb_config *tmp_dcb_cfg = &nic_dev->tmp_dcb_cfg;
struct hinic_tc_cfg *tc_dst, *tc_src;
u8 changes = 0;
int i;
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
tc_src = &tmp_dcb_cfg->tc_cfg[i];
tc_dst = &dcb_cfg->tc_cfg[i];
changes |= hinic_sync_tc_cfg(tc_dst, tc_src, HINIC_DCB_CFG_TX);
changes |= hinic_sync_tc_cfg(tc_dst, tc_src, HINIC_DCB_CFG_RX);
}
for (i = 0; i < HINIC_DCB_PG_MAX; i++) {
if (dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i] !=
tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i]) {
dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i] =
tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i];
changes |= DCB_CFG_CHG_PG_TX;
}
if (dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i] !=
tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i]) {
dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i] =
tmp_dcb_cfg->bw_pct[HINIC_DCB_CFG_RX][i];
changes |= DCB_CFG_CHG_PG_RX;
}
}
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
if (dcb_cfg->tc_cfg[i].pfc_en !=
tmp_dcb_cfg->tc_cfg[i].pfc_en) {
dcb_cfg->tc_cfg[i].pfc_en =
tmp_dcb_cfg->tc_cfg[i].pfc_en;
changes |= DCB_CFG_CHG_PFC;
}
}
if (dcb_cfg->pfc_state != tmp_dcb_cfg->pfc_state) {
dcb_cfg->pfc_state = tmp_dcb_cfg->pfc_state;
changes |= DCB_CFG_CHG_PFC;
}
return changes;
}
static void hinic_dcb_get_pfc_map(struct hinic_nic_dev *nic_dev,
struct hinic_dcb_config *dcb_cfg, u8 *pfc_map)
{
u8 i, up;
u8 pfc_en = 0, outof_range_pfc = 0;
for (i = 0; i < dcb_cfg->pfc_tcs; i++) {
up = (HINIC_DCB_UP_MAX - 1) - i;
if (dcb_cfg->tc_cfg[up].pfc_en)
*pfc_map |= (u8)BIT(up);
}
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
up = (HINIC_DCB_UP_MAX - 1) - i;
if (dcb_cfg->tc_cfg[up].pfc_en)
pfc_en |= (u8)BIT(up);
}
*pfc_map = pfc_en & nic_dev->up_valid_bitmap;
outof_range_pfc = pfc_en & (~nic_dev->up_valid_bitmap);
if (outof_range_pfc)
hinic_info(nic_dev, drv,
"PFC setting out of range, 0x%x will be ignored\n",
outof_range_pfc);
}
static bool is_cos_in_use(u8 cos, u8 up_valid_bitmap, u8 *up_cos)
{
u32 i;
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
if (!(up_valid_bitmap & BIT(i)))
continue;
if (cos == up_cos[i])
return true;
}
return false;
}
static void hinic_dcb_adjust_up_bw(struct hinic_nic_dev *nic_dev, u8 *up_pgid,
u8 *up_bw)
{
u8 tmp_cos, pg_id;
u16 bw_all;
u8 bw_remain, cos_cnt;
for (pg_id = 0; pg_id < HINIC_DCB_PG_MAX; pg_id++) {
bw_all = 0;
cos_cnt = 0;
/* Find all up mapping to the same pg */
for (tmp_cos = 0; tmp_cos < HINIC_DCB_UP_MAX; tmp_cos++) {
if (!is_cos_in_use(tmp_cos, nic_dev->up_valid_bitmap,
nic_dev->up_cos))
continue;
if (up_pgid[tmp_cos] == pg_id) {
bw_all += up_bw[tmp_cos];
cos_cnt++;
}
}
if (bw_all <= 100 || !cos_cnt)
continue;
/* Calculate up percent of bandwidth group, The sum of
* percentages for priorities in the same priority group
* must be 100
*/
bw_remain = 100 % cos_cnt;
for (tmp_cos = 0; tmp_cos < HINIC_DCB_UP_MAX; tmp_cos++) {
if (!is_cos_in_use(tmp_cos, nic_dev->up_valid_bitmap,
nic_dev->up_cos))
continue;
if (up_pgid[tmp_cos] == pg_id) {
up_bw[tmp_cos] =
(u8)(100 * up_bw[tmp_cos] / bw_all +
(u8)!!bw_remain);
if (bw_remain)
bw_remain--;
}
}
}
}
static void hinic_dcb_dump_configuration(struct hinic_nic_dev *nic_dev,
u8 *up_tc, u8 *up_pgid, u8 *up_bw,
u8 *pg_bw, u8 *up_strict, u8 *bw_pct)
{
u8 i;
u8 cos;
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
if (!(nic_dev->up_valid_bitmap & BIT(i)))
continue;
cos = nic_dev->up_cos[i];
hinic_info(nic_dev, drv,
"up: %d, cos: %d, tc: %d, pgid: %d, bw: %d, tsa: %d\n",
i, cos, up_tc[cos], up_pgid[cos], up_bw[cos],
up_strict[cos]);
}
for (i = 0; i < HINIC_DCB_PG_MAX; i++)
hinic_info(nic_dev, drv, "pgid: %d, bw: %d\n", i, pg_bw[i]);
}
/* Ucode thread timeout is 210ms, must be lagger then 210ms */
#define HINIC_WAIT_PORT_IO_STOP 250
static int hinic_stop_port_traffic_flow(struct hinic_nic_dev *nic_dev)
{
int err = 0;
down(&nic_dev->dcb_sem);
if (nic_dev->disable_port_cnt++ != 0)
goto out;
err = hinic_force_port_disable(nic_dev);
if (err) {
hinic_err(nic_dev, drv, "Failed to disable port\n");
goto set_port_err;
}
err = hinic_set_port_funcs_state(nic_dev->hwdev, false);
if (err) {
hinic_err(nic_dev, drv,
"Failed to disable all functions in port\n");
goto set_port_funcs_err;
}
hinic_info(nic_dev, drv, "Stop port traffic flow\n");
goto out;
set_port_funcs_err:
hinic_force_set_port_state(nic_dev, !!netif_running(nic_dev->netdev));
set_port_err:
out:
if (err)
nic_dev->disable_port_cnt--;
up(&nic_dev->dcb_sem);
return err;
}
static int hinic_start_port_traffic_flow(struct hinic_nic_dev *nic_dev)
{
int err;
down(&nic_dev->dcb_sem);
nic_dev->disable_port_cnt--;
if (nic_dev->disable_port_cnt > 0) {
up(&nic_dev->dcb_sem);
return 0;
}
nic_dev->disable_port_cnt = 0;
up(&nic_dev->dcb_sem);
err = hinic_force_set_port_state(nic_dev,
!!netif_running(nic_dev->netdev));
if (err)
hinic_err(nic_dev, drv, "Failed to disable port\n");
err = hinic_set_port_funcs_state(nic_dev->hwdev, true);
if (err)
hinic_err(nic_dev, drv,
"Failed to disable all functions in port\n");
hinic_info(nic_dev, drv, "Start port traffic flow\n");
return err;
}
static int __set_hw_cos_up_map(struct hinic_nic_dev *nic_dev)
{
u8 cos, cos_valid_bitmap, cos_up_map[HINIC_DCB_COS_MAX] = {0};
u8 i;
int err;
cos_valid_bitmap = 0;
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
if (!(nic_dev->up_valid_bitmap & BIT(i)))
continue;
cos = nic_dev->up_cos[i];
cos_up_map[cos] = i;
cos_valid_bitmap |= (u8)BIT(cos);
}
err = hinic_dcb_set_cos_up_map(nic_dev->hwdev, cos_valid_bitmap,
cos_up_map);
if (err) {
hinic_info(nic_dev, drv, "Set cos_up map failed\n");
return err;
}
return 0;
}
static int __set_hw_ets(struct hinic_nic_dev *nic_dev)
{
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets;
struct hinic_tc_attr *tc_attr;
u8 up_tc[HINIC_DCB_UP_MAX] = {0};
u8 up_pgid[HINIC_DCB_UP_MAX] = {0};
u8 up_bw[HINIC_DCB_UP_MAX] = {0};
u8 pg_bw[HINIC_DCB_UP_MAX] = {0};
u8 up_strict[HINIC_DCB_UP_MAX] = {0};
u8 i, tc, cos;
int err;
for (i = 0; i < HINIC_DCB_UP_MAX; i++) {
if (!(nic_dev->up_valid_bitmap & BIT(i)))
continue;
cos = nic_dev->up_cos[i];
if ((nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) {
up_tc[cos] = my_ets->prio_tc[i];
up_pgid[cos] = my_ets->prio_tc[i];
up_bw[cos] = 100;
up_strict[i] =
(my_ets->tc_tsa[cos] == IEEE8021Q_TSA_STRICT) ?
HINIC_DCB_TSA_TC_SP : HINIC_DCB_TSA_TC_DWRR;
} else {
tc = hinic_dcb_get_tc(dcb_cfg, HINIC_DCB_CFG_TX, i);
tc_attr = &dcb_cfg->tc_cfg[tc].path[HINIC_DCB_CFG_TX];
up_tc[cos] = tc;
up_pgid[cos] = tc_attr->pg_id;
up_bw[cos] = tc_attr->bw_pct;
up_strict[cos] = tc_attr->prio_type ?
HINIC_DCB_TSA_TC_SP : HINIC_DCB_TSA_TC_DWRR;
}
}
hinic_dcb_adjust_up_bw(nic_dev, up_pgid, up_bw);
if (nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) {
for (i = 0; i < HINIC_DCB_PG_MAX; i++)
pg_bw[i] = my_ets->tc_tx_bw[i];
} else {
for (i = 0; i < HINIC_DCB_PG_MAX; i++)
pg_bw[i] = dcb_cfg->bw_pct[HINIC_DCB_CFG_TX][i];
}
if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags))
hinic_dcb_dump_configuration(nic_dev, up_tc, up_pgid,
up_bw, pg_bw, up_strict,
pg_bw);
err = hinic_dcb_set_ets(nic_dev->hwdev, up_tc, pg_bw, up_pgid,
up_bw, up_strict);
if (err) {
hinic_err(nic_dev, drv, "Failed to set ets with mode: %d\n",
nic_dev->dcbx_cap);
return err;
}
hinic_info(nic_dev, drv, "Set ets to hw done with mode: %d\n",
nic_dev->dcbx_cap);
return 0;
}
u8 hinic_dcbnl_set_ets_tool(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u8 state = DCB_HW_CFG_CHG;
int err;
nic_dev->dcb_changes |= hinic_sync_dcb_cfg(nic_dev);
if (!nic_dev->dcb_changes)
return DCB_HW_CFG_CHG;
err = hinic_stop_port_traffic_flow(nic_dev);
if (err)
return DCB_HW_CFG_ERR;
/* wait all traffic flow stopped */
if (netdev->reg_state == NETREG_REGISTERED)
msleep(HINIC_WAIT_PORT_IO_STOP);
if (nic_dev->dcb_changes & DCB_CFG_CHG_UP_COS) {
err = __set_hw_cos_up_map(nic_dev);
if (err) {
hinic_info(nic_dev, drv,
"Set cos_up map to hardware failed\n");
state = DCB_HW_CFG_ERR;
goto out;
}
nic_dev->dcb_changes &= (~DCB_CFG_CHG_UP_COS);
}
if (nic_dev->dcb_changes & (DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX)) {
err = __set_hw_ets(nic_dev);
if (err) {
state = DCB_HW_CFG_ERR;
goto out;
}
nic_dev->dcb_changes &=
(~(DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX));
}
out:
hinic_start_port_traffic_flow(nic_dev);
return state;
}
static int hinic_dcbnl_set_df_ieee_cfg(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct ieee_ets *ets_default = &nic_dev->hinic_ieee_ets_default;
struct ieee_pfc *my_pfc = &nic_dev->hinic_ieee_pfc;
struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets;
struct ieee_pfc pfc = {0};
int err1 = 0;
int err2 = 0;
u8 flag = 0;
if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return 0;
if (memcmp(my_ets, ets_default, sizeof(struct ieee_ets)))
flag |= (u8)BIT(0);
if (my_pfc->pfc_en)
flag |= (u8)BIT(1);
if (!flag)
return 0;
err1 = hinic_stop_port_traffic_flow(nic_dev);
if (err1)
return err1;
if (netdev->reg_state == NETREG_REGISTERED)
msleep(HINIC_WAIT_PORT_IO_STOP);
if (flag & BIT(0)) {
memcpy(my_ets, ets_default, sizeof(struct ieee_ets));
err1 = __set_hw_ets(nic_dev);
}
if (flag & BIT(1)) {
my_pfc->pfc_en = 0;
err2 = hinic_dcb_set_pfc(nic_dev->hwdev, false, pfc.pfc_en);
if (err2)
nicif_err(nic_dev, drv, netdev, "Failed to set pfc\n");
}
hinic_start_port_traffic_flow(nic_dev);
return (err1 || err2) ? -EINVAL : 0;
}
u8 hinic_dcbnl_set_pfc_tool(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
u8 state = DCB_HW_CFG_CHG;
int err;
nic_dev->dcb_changes |= hinic_sync_dcb_cfg(nic_dev);
if (!nic_dev->dcb_changes)
return DCB_HW_CFG_CHG;
if (nic_dev->dcb_changes & DCB_CFG_CHG_PFC) {
u8 pfc_map = 0;
hinic_dcb_get_pfc_map(nic_dev, dcb_cfg, &pfc_map);
err = hinic_dcb_set_pfc(nic_dev->hwdev, dcb_cfg->pfc_state,
pfc_map);
if (err) {
hinic_info(nic_dev, drv, "Failed to %s PFC\n",
dcb_cfg->pfc_state ? "enable" : "disable");
state = DCB_HW_CFG_ERR;
goto out;
}
if (dcb_cfg->pfc_state)
hinic_info(nic_dev, drv, "Set PFC: 0x%x to hw done\n",
pfc_map);
else
hinic_info(nic_dev, drv, "Disable PFC, enable tx/rx pause\n");
nic_dev->dcb_changes &= (~DCB_CFG_CHG_PFC);
}
out:
return state;
}
u8 hinic_dcbnl_set_all(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
u8 state = DCB_HW_CFG_CHG;
int err;
if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return DCB_HW_CFG_ERR;
nic_dev->dcb_changes |= hinic_sync_dcb_cfg(nic_dev);
if (!nic_dev->dcb_changes)
return DCB_HW_CFG_NO_CHG;
err = hinic_stop_port_traffic_flow(nic_dev);
if (err)
return DCB_HW_CFG_ERR;
/* wait all traffic flow stopped */
if (netdev->reg_state == NETREG_REGISTERED)
msleep(HINIC_WAIT_PORT_IO_STOP);
if (nic_dev->dcb_changes & DCB_CFG_CHG_UP_COS) {
err = __set_hw_cos_up_map(nic_dev);
if (err) {
hinic_info(nic_dev, drv,
"Set cos_up map to hardware failed\n");
state = DCB_HW_CFG_ERR;
goto out;
}
nic_dev->dcb_changes &= (~DCB_CFG_CHG_UP_COS);
}
if (nic_dev->dcb_changes & (DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX)) {
err = __set_hw_ets(nic_dev);
if (err) {
state = DCB_HW_CFG_ERR;
goto out;
}
nic_dev->dcb_changes &=
(~(DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX));
}
if (nic_dev->dcb_changes & DCB_CFG_CHG_PFC) {
u8 pfc_map = 0;
hinic_dcb_get_pfc_map(nic_dev, dcb_cfg, &pfc_map);
err = hinic_dcb_set_pfc(nic_dev->hwdev, dcb_cfg->pfc_state,
pfc_map);
if (err) {
hinic_info(nic_dev, drv, "Failed to %s PFC\n",
dcb_cfg->pfc_state ? "enable" : "disable");
state = DCB_HW_CFG_ERR;
goto out;
}
if (dcb_cfg->pfc_state)
hinic_info(nic_dev, drv, "Set PFC: 0x%x to hw done\n",
pfc_map);
else
hinic_info(nic_dev, drv, "Disable PFC, enable tx/rx pause\n");
nic_dev->dcb_changes &= (~DCB_CFG_CHG_PFC);
}
out:
hinic_start_port_traffic_flow(nic_dev);
return state;
}
static int hinic_dcbnl_ieee_get_ets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets;
ets->ets_cap = my_ets->ets_cap;
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->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc));
memcpy(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa));
return 0;
}
static int hinic_dcbnl_ieee_set_ets(struct net_device *netdev,
struct ieee_ets *ets)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets;
struct ieee_ets back_ets;
int err, i;
u8 max_tc = 0;
u16 total_bw = 0;
if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
if (!memcmp(ets->tc_tx_bw, my_ets->tc_tx_bw, sizeof(ets->tc_tx_bw)) &&
!memcmp(ets->tc_rx_bw, my_ets->tc_rx_bw, sizeof(ets->tc_rx_bw)) &&
!memcmp(ets->prio_tc, my_ets->prio_tc, sizeof(ets->prio_tc)) &&
!memcmp(ets->tc_tsa, my_ets->tc_tsa, sizeof(ets->tc_tsa)))
return 0;
for (i = 0; i < HINIC_DCB_TC_MAX; i++)
total_bw += ets->tc_tx_bw[i];
if (!total_bw)
return -EINVAL;
for (i = 0; i < dcb_cfg->pg_tcs; i++) {
if (ets->prio_tc[i] > max_tc)
max_tc = ets->prio_tc[i];
}
if (max_tc)
max_tc++;
if (max_tc > dcb_cfg->pg_tcs)
return -EINVAL;
max_tc = max_tc ? dcb_cfg->pg_tcs : 0;
memcpy(&back_ets, my_ets, sizeof(struct ieee_ets));
memcpy(my_ets->tc_tx_bw, ets->tc_tx_bw, sizeof(ets->tc_tx_bw));
memcpy(my_ets->tc_rx_bw, ets->tc_rx_bw, sizeof(ets->tc_rx_bw));
memcpy(my_ets->prio_tc, ets->prio_tc, sizeof(ets->prio_tc));
memcpy(my_ets->tc_tsa, ets->tc_tsa, sizeof(ets->tc_tsa));
if (max_tc != netdev_get_num_tc(netdev)) {
err = hinic_setup_tc(netdev, max_tc);
if (err) {
nicif_err(nic_dev, drv, netdev,
"Failed to setup tc with max_tc: %d, err: %d\n",
max_tc, err);
memcpy(my_ets, &back_ets, sizeof(struct ieee_ets));
return err;
}
}
err = hinic_stop_port_traffic_flow(nic_dev);
if (err)
return err;
if (netdev->reg_state == NETREG_REGISTERED)
msleep(HINIC_WAIT_PORT_IO_STOP);
err = __set_hw_ets(nic_dev);
hinic_start_port_traffic_flow(nic_dev);
return err;
}
static int hinic_dcbnl_ieee_get_pfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct ieee_pfc *my_pfc = &nic_dev->hinic_ieee_pfc;
pfc->pfc_en = my_pfc->pfc_en;
pfc->pfc_cap = my_pfc->pfc_cap;
return 0;
}
static int hinic_dcbnl_ieee_set_pfc(struct net_device *netdev,
struct ieee_pfc *pfc)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
struct ieee_pfc *my_pfc = &nic_dev->hinic_ieee_pfc;
struct ieee_ets *my_ets = &nic_dev->hinic_ieee_ets;
int err, i;
u8 pfc_map, max_tc;
u8 outof_range_pfc = 0;
bool pfc_en;
if (!(nic_dev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
if (my_pfc->pfc_en == pfc->pfc_en)
return 0;
pfc_map = pfc->pfc_en & nic_dev->up_valid_bitmap;
outof_range_pfc = pfc->pfc_en & (~nic_dev->up_valid_bitmap);
if (outof_range_pfc)
nicif_info(nic_dev, drv, netdev,
"pfc setting out of range, 0x%x will be ignored\n",
outof_range_pfc);
err = hinic_stop_port_traffic_flow(nic_dev);
if (err)
return err;
if (netdev->reg_state == NETREG_REGISTERED)
msleep(HINIC_WAIT_PORT_IO_STOP);
pfc_en = pfc_map ? true : false;
max_tc = 0;
for (i = 0; i < dcb_cfg->pg_tcs; i++) {
if (my_ets->prio_tc[i] > max_tc)
max_tc = my_ets->prio_tc[i];
}
pfc_en = max_tc ? pfc_en : false;
err = hinic_dcb_set_pfc(nic_dev->hwdev, pfc_en, pfc_map);
if (err) {
hinic_info(nic_dev, drv,
"Failed to set pfc to hw with pfc_map: 0x%x err: %d\n",
pfc_map, err);
hinic_start_port_traffic_flow(nic_dev);
return err;
}
hinic_start_port_traffic_flow(nic_dev);
my_pfc->pfc_en = pfc->pfc_en;
hinic_info(nic_dev, drv,
"Set pfc successfully with pfc_map: 0x%x, pfc_en: %d\n",
pfc_map, pfc_en);
return 0;
}
static int hinic_dcbnl_getnumtcs(struct net_device *netdev, int tcid, u8 *num)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_dcb_config *dcb_cfg = &nic_dev->dcb_cfg;
if (!test_bit(HINIC_DCB_ENABLE, &nic_dev->flags))
return -EINVAL;
switch (tcid) {
case DCB_NUMTCS_ATTR_PG:
*num = dcb_cfg->pg_tcs;
break;
case DCB_NUMTCS_ATTR_PFC:
*num = dcb_cfg->pfc_tcs;
break;
default:
return -EINVAL;
}
return 0;
}
static int hinic_dcbnl_setnumtcs(struct net_device *netdev, int tcid, u8 num)
{
return -EINVAL;
}
static u8 hinic_dcbnl_getpfcstate(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
return (u8)nic_dev->dcb_cfg.pfc_state;
}
static void hinic_dcbnl_setpfcstate(struct net_device *netdev, u8 state)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
nic_dev->tmp_dcb_cfg.pfc_state = !!state;
}
static u8 hinic_dcbnl_getdcbx(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
return nic_dev->dcbx_cap;
}
static u8 hinic_dcbnl_setdcbx(struct net_device *netdev, u8 mode)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int err;
if (((mode & DCB_CAP_DCBX_VER_IEEE) && (mode & DCB_CAP_DCBX_VER_CEE)) ||
((mode & DCB_CAP_DCBX_LLD_MANAGED) &&
(!(mode & DCB_CAP_DCBX_HOST)))) {
nicif_info(nic_dev, drv, netdev,
"Set dcbx failed with invalid mode: %d\n", mode);
return 1;
}
if (nic_dev->dcbx_cap == mode)
return 0;
nic_dev->dcbx_cap = mode;
if (mode & DCB_CAP_DCBX_VER_CEE) {
u8 mask = DCB_CFG_CHG_PFC | DCB_CFG_CHG_PG_TX |
DCB_CFG_CHG_PG_RX;
nic_dev->dcb_changes |= mask;
hinic_dcbnl_set_all(netdev);
} else if (mode & DCB_CAP_DCBX_VER_IEEE) {
if (netdev_get_num_tc(netdev)) {
err = hinic_setup_tc(netdev, 0);
if (err) {
nicif_err(nic_dev, drv, netdev,
"Failed to setup tc with mode: %d\n",
mode);
return 1;
}
}
hinic_dcbnl_set_df_ieee_cfg(netdev);
hinic_force_port_relink(nic_dev->hwdev);
} else {
err = hinic_setup_tc(netdev, 0);
if (err) {
nicif_err(nic_dev, drv, netdev,
"Failed to setup tc with mode: %d\n", mode);
return 1;
}
}
nicif_info(nic_dev, drv, netdev, "Change dcbx mode to 0x%x\n", mode);
return 0;
}
const struct dcbnl_rtnl_ops hinic_dcbnl_ops = {
/* IEEE 802.1Qaz std */
.ieee_getets = hinic_dcbnl_ieee_get_ets,
.ieee_setets = hinic_dcbnl_ieee_set_ets,
.ieee_getpfc = hinic_dcbnl_ieee_get_pfc,
.ieee_setpfc = hinic_dcbnl_ieee_set_pfc,
/* CEE std */
.getstate = hinic_dcbnl_get_state,
.setstate = hinic_dcbnl_set_state,
.getpermhwaddr = hinic_dcbnl_get_perm_hw_addr,
.setpgtccfgtx = hinic_dcbnl_set_pg_tc_cfg_tx,
.setpgbwgcfgtx = hinic_dcbnl_set_pg_bwg_cfg_tx,
.setpgtccfgrx = hinic_dcbnl_set_pg_tc_cfg_rx,
.setpgbwgcfgrx = hinic_dcbnl_set_pg_bwg_cfg_rx,
.getpgtccfgtx = hinic_dcbnl_get_pg_tc_cfg_tx,
.getpgbwgcfgtx = hinic_dcbnl_get_pg_bwg_cfg_tx,
.getpgtccfgrx = hinic_dcbnl_get_pg_tc_cfg_rx,
.getpgbwgcfgrx = hinic_dcbnl_get_pg_bwg_cfg_rx,
.setpfccfg = hinic_dcbnl_set_pfc_cfg,
.getpfccfg = hinic_dcbnl_get_pfc_cfg,
.setall = hinic_dcbnl_set_all,
.getcap = hinic_dcbnl_getcap,
.getnumtcs = hinic_dcbnl_getnumtcs,
.setnumtcs = hinic_dcbnl_setnumtcs,
.getpfcstate = hinic_dcbnl_getpfcstate,
.setpfcstate = hinic_dcbnl_setpfcstate,
/* DCBX configuration */
.getdcbx = hinic_dcbnl_getdcbx,
.setdcbx = hinic_dcbnl_setdcbx,
};
int hinic_dcb_reset_hw_config(struct hinic_nic_dev *nic_dev)
{
struct net_device *netdev = nic_dev->netdev;
u8 state;
hinic_dcb_config_init(nic_dev, &nic_dev->tmp_dcb_cfg);
state = hinic_dcbnl_set_all(netdev);
if (state == DCB_HW_CFG_ERR)
return -EFAULT;
if (state == DCB_HW_CFG_CHG)
hinic_info(nic_dev, drv,
"Reset hardware DCB configuration done\n");
return 0;
}
void hinic_configure_dcb(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int err;
if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) {
memcpy(&nic_dev->tmp_dcb_cfg, &nic_dev->save_dcb_cfg,
sizeof(nic_dev->tmp_dcb_cfg));
hinic_dcbnl_set_all(netdev);
} else {
memcpy(&nic_dev->save_dcb_cfg, &nic_dev->tmp_dcb_cfg,
sizeof(nic_dev->save_dcb_cfg));
err = hinic_dcb_reset_hw_config(nic_dev);
if (err)
nicif_warn(nic_dev, drv, netdev,
"Failed to reset hw dcb configuration\n");
}
}
static bool __is_cos_up_map_change(struct hinic_nic_dev *nic_dev, u8 *cos_up)
{
u8 cos, up;
for (cos = 0; cos < nic_dev->max_cos; cos++) {
up = cos_up[cos];
if (BIT(up) != (nic_dev->up_valid_bitmap & BIT(up)))
return true;
}
return false;
}
int __set_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *cos_up)
{
struct net_device *netdev;
u8 state;
int err = 0;
if (!nic_dev || !cos_up)
return -EINVAL;
netdev = nic_dev->netdev;
if (test_and_set_bit(HINIC_DCB_UP_COS_SETTING, &nic_dev->dcb_flags)) {
nicif_err(nic_dev, drv, netdev,
"Cos_up map setting in inprocess, please try again later\n");
return -EFAULT;
}
nicif_info(nic_dev, drv, netdev, "Set cos2up: %d%d%d%d%d%d%d%d\n",
cos_up[0], cos_up[1], cos_up[2], cos_up[3],
cos_up[4], cos_up[5], cos_up[6], cos_up[7]);
if (!__is_cos_up_map_change(nic_dev, cos_up)) {
nicif_err(nic_dev, drv, netdev,
"Same mapping, don't need to change anything\n");
err = 0;
goto out;
}
err = hinic_set_up_cos_map(nic_dev, nic_dev->max_cos, cos_up);
if (err) {
err = -EFAULT;
goto out;
}
nic_dev->dcb_changes = DCB_CFG_CHG_PG_TX | DCB_CFG_CHG_PG_RX |
DCB_CFG_CHG_PFC | DCB_CFG_CHG_UP_COS;
if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) {
/* Change map in kernel */
hinic_set_prio_tc_map(nic_dev);
state = hinic_dcbnl_set_all(netdev);
if (state == DCB_HW_CFG_ERR) {
nicif_err(nic_dev, drv, netdev,
"Reconfig dcb to hw failed\n");
err = -EFAULT;
}
}
out:
clear_bit(HINIC_DCB_UP_COS_SETTING, &nic_dev->dcb_flags);
return err;
}
int hinic_get_num_cos(struct hinic_nic_dev *nic_dev, u8 *num_cos)
{
if (!nic_dev || !num_cos)
return -EINVAL;
*num_cos = nic_dev->max_cos;
return 0;
}
int hinic_get_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *num_cos,
u8 *cos_up)
{
u8 up, cos;
if (!nic_dev || !cos_up)
return -EINVAL;
for (cos = 0; cos < HINIC_DCB_COS_MAX; cos++) {
for (up = 0; up < HINIC_DCB_UP_MAX; up++) {
if (!(nic_dev->up_valid_bitmap & BIT(up)))
continue;
if (nic_dev->up_cos[up] == cos ||
nic_dev->up_cos[up] == nic_dev->default_cos_id)
cos_up[cos] = up;
}
}
*num_cos = nic_dev->max_cos;
return 0;
}
static int __stop_port_flow(void *uld_array[], u32 num_dev)
{
struct hinic_nic_dev *tmp_dev;
u32 i, idx;
int err;
for (idx = 0; idx < num_dev; idx++) {
tmp_dev = (struct hinic_nic_dev *)uld_array[idx];
err = hinic_stop_port_traffic_flow(tmp_dev);
if (err) {
nicif_err(tmp_dev, drv, tmp_dev->netdev,
"Stop port traffic flow failed\n");
goto stop_port_err;
}
}
/* wait all traffic flow stopped */
msleep(HINIC_WAIT_PORT_IO_STOP);
return 0;
stop_port_err:
for (i = 0; i < idx; i++) {
tmp_dev = (struct hinic_nic_dev *)uld_array[i];
hinic_start_port_traffic_flow(tmp_dev);
}
return err;
}
static void __start_port_flow(void *uld_array[], u32 num_dev)
{
struct hinic_nic_dev *tmp_dev;
u32 idx;
for (idx = 0; idx < num_dev; idx++) {
tmp_dev = (struct hinic_nic_dev *)uld_array[idx];
hinic_start_port_traffic_flow(tmp_dev);
}
}
/* for hinicadm tool, need to chang all port of the chip */
int hinic_set_cos_up_map(struct hinic_nic_dev *nic_dev, u8 *cos_up)
{
void *uld_array[HINIC_MAX_PF_NUM];
struct hinic_nic_dev *tmp_dev;
u8 num_cos, old_cos_up[HINIC_DCB_COS_MAX] = {0};
u32 i, idx, num_dev = 0;
int err, rollback_err;
/* Save old map, in case of set failed */
err = hinic_get_cos_up_map(nic_dev, &num_cos, old_cos_up);
if (err || !num_cos) {
nicif_err(nic_dev, drv, nic_dev->netdev,
"Get old cos_up map failed\n");
return -EFAULT;
}
if (!memcmp(cos_up, old_cos_up, sizeof(u8) * num_cos)) {
nicif_info(nic_dev, drv, nic_dev->netdev,
"Same cos2up map, don't need to change anything\n");
return 0;
}
/* Get all pf of this chip */
err = hinic_get_pf_uld_array(nic_dev->pdev, &num_dev, uld_array);
if (err) {
nicif_err(nic_dev, drv, nic_dev->netdev,
"Get all pf private handle failed\n");
return -EFAULT;
}
err = __stop_port_flow(uld_array, num_dev);
if (err)
return -EFAULT;
for (idx = 0; idx < num_dev; idx++) {
tmp_dev = (struct hinic_nic_dev *)uld_array[idx];
err = __set_cos_up_map(tmp_dev, cos_up);
if (err) {
nicif_err(tmp_dev, drv, tmp_dev->netdev,
"Set cos_up map to hw failed\n");
goto set_err;
}
}
__start_port_flow(uld_array, num_dev);
hinic_set_chip_cos_up_map(nic_dev->pdev, cos_up);
return 0;
set_err:
/* undo all settings */
for (i = 0; i < idx; i++) {
tmp_dev = (struct hinic_nic_dev *)uld_array[i];
rollback_err = __set_cos_up_map(tmp_dev, old_cos_up);
if (rollback_err)
nicif_err(tmp_dev, drv, tmp_dev->netdev,
"Undo cos_up map to hw failed\n");
}
__start_port_flow(uld_array, num_dev);
return err;
}