// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2021 Huawei Technologies Co., Ltd */ #define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt #include #include #include #include #include #include #include #include #include #include "hinic3_crm.h" #include "hinic3_lld.h" #include "hinic3_nic_cfg.h" #include "hinic3_srv_nic.h" #include "hinic3_nic_dev.h" #include "hinic3_dcb.h" #define MAX_BW_PERCENT 100 u8 hinic3_get_dev_user_cos_num(struct hinic3_nic_dev *nic_dev) { if (nic_dev->hw_dcb_cfg.trust == 0) return nic_dev->hw_dcb_cfg.pcp_user_cos_num; if (nic_dev->hw_dcb_cfg.trust == 1) return nic_dev->hw_dcb_cfg.dscp_user_cos_num; return 0; } u8 hinic3_get_dev_valid_cos_map(struct hinic3_nic_dev *nic_dev) { if (nic_dev->hw_dcb_cfg.trust == 0) return nic_dev->hw_dcb_cfg.pcp_valid_cos_map; if (nic_dev->hw_dcb_cfg.trust == 1) return nic_dev->hw_dcb_cfg.dscp_valid_cos_map; return 0; } void hinic3_update_qp_cos_cfg(struct hinic3_nic_dev *nic_dev, u8 num_cos) { struct hinic3_dcb_config *dcb_cfg = &nic_dev->hw_dcb_cfg; u8 i, remainder, num_sq_per_cos, cur_cos_num = 0; u8 valid_cos_map = hinic3_get_dev_valid_cos_map(nic_dev); if (num_cos == 0) return; num_sq_per_cos = (u8)(nic_dev->q_params.num_qps / num_cos); if (num_sq_per_cos == 0) return; remainder = nic_dev->q_params.num_qps % num_sq_per_cos; memset(dcb_cfg->cos_qp_offset, 0, sizeof(dcb_cfg->cos_qp_offset)); memset(dcb_cfg->cos_qp_num, 0, sizeof(dcb_cfg->cos_qp_num)); for (i = 0; i < PCP_MAX_UP; i++) { if (BIT(i) & valid_cos_map) { u8 cos_qp_num = num_sq_per_cos; u8 cos_qp_offset = (u8)(cur_cos_num * num_sq_per_cos); if (cur_cos_num < remainder) { cos_qp_num++; cos_qp_offset += cur_cos_num; } else { cos_qp_offset += remainder; } cur_cos_num++; valid_cos_map -= (u8)BIT(i); dcb_cfg->cos_qp_offset[i] = cos_qp_offset; dcb_cfg->cos_qp_num[i] = cos_qp_num; hinic3_info(nic_dev, drv, "cos %u, cos_qp_offset=%u cos_qp_num=%u\n", i, cos_qp_offset, cos_qp_num); } } memcpy(nic_dev->wanted_dcb_cfg.cos_qp_offset, dcb_cfg->cos_qp_offset, sizeof(dcb_cfg->cos_qp_offset)); memcpy(nic_dev->wanted_dcb_cfg.cos_qp_num, dcb_cfg->cos_qp_num, sizeof(dcb_cfg->cos_qp_num)); } void hinic3_update_tx_db_cos(struct hinic3_nic_dev *nic_dev, u8 dcb_en) { u8 i; u16 start_qid, q_num; hinic3_set_txq_cos(nic_dev, 0, nic_dev->q_params.num_qps, nic_dev->hw_dcb_cfg.default_cos); if (!dcb_en) return; for (i = 0; i < NIC_DCB_COS_MAX; i++) { q_num = (u16)nic_dev->hw_dcb_cfg.cos_qp_num[i]; if (q_num) { start_qid = (u16)nic_dev->hw_dcb_cfg.cos_qp_offset[i]; hinic3_set_txq_cos(nic_dev, start_qid, q_num, i); hinic3_info(nic_dev, drv, "update tx db cos, start_qid %u, q_num=%u cos=%u\n", start_qid, q_num, i); } } } static int hinic3_set_tx_cos_state(struct hinic3_nic_dev *nic_dev, u8 dcb_en) { struct hinic3_dcb_config *dcb_cfg = &nic_dev->hw_dcb_cfg; struct hinic3_dcb_state dcb_state = {0}; u8 i; int err; if (HINIC3_FUNC_IS_VF(nic_dev->hwdev)) { /* VF does not support DCB, use the default cos */ dcb_cfg->default_cos = (u8)fls(nic_dev->func_dft_cos_bitmap) - 1; return 0; } dcb_state.dcb_on = dcb_en; dcb_state.default_cos = dcb_cfg->default_cos; dcb_state.trust = dcb_cfg->trust; if (dcb_en) { for (i = 0; i < NIC_DCB_COS_MAX; i++) dcb_state.pcp2cos[i] = dcb_cfg->pcp2cos[i]; for (i = 0; i < NIC_DCB_IP_PRI_MAX; i++) dcb_state.dscp2cos[i] = dcb_cfg->dscp2cos[i]; } else { memset(dcb_state.pcp2cos, dcb_cfg->default_cos, sizeof(dcb_state.pcp2cos)); memset(dcb_state.dscp2cos, dcb_cfg->default_cos, sizeof(dcb_state.dscp2cos)); } err = hinic3_set_dcb_state(nic_dev->hwdev, &dcb_state); if (err) hinic3_err(nic_dev, drv, "Failed to set dcb state\n"); return err; } static int hinic3_configure_dcb_hw(struct hinic3_nic_dev *nic_dev, u8 dcb_en) { int err; u8 user_cos_num = hinic3_get_dev_user_cos_num(nic_dev); err = hinic3_sync_dcb_state(nic_dev->hwdev, 1, dcb_en); if (err) { hinic3_err(nic_dev, drv, "Set dcb state failed\n"); return err; } hinic3_update_qp_cos_cfg(nic_dev, user_cos_num); hinic3_update_tx_db_cos(nic_dev, dcb_en); err = hinic3_set_tx_cos_state(nic_dev, dcb_en); if (err) { hinic3_err(nic_dev, drv, "Set tx cos state failed\n"); goto set_tx_cos_fail; } err = hinic3_rx_configure(nic_dev->netdev, dcb_en); if (err) { hinic3_err(nic_dev, drv, "rx configure failed\n"); goto rx_configure_fail; } if (dcb_en) set_bit(HINIC3_DCB_ENABLE, &nic_dev->flags); else clear_bit(HINIC3_DCB_ENABLE, &nic_dev->flags); return 0; rx_configure_fail: hinic3_set_tx_cos_state(nic_dev, dcb_en ? 0 : 1); set_tx_cos_fail: hinic3_update_tx_db_cos(nic_dev, dcb_en ? 0 : 1); hinic3_sync_dcb_state(nic_dev->hwdev, 1, dcb_en ? 0 : 1); return err; } int hinic3_setup_cos(struct net_device *netdev, u8 cos, u8 netif_run) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); int err; if (cos && test_bit(HINIC3_SAME_RXTX, &nic_dev->flags)) { nicif_err(nic_dev, drv, netdev, "Failed to enable DCB while Symmetric RSS is enabled\n"); return -EOPNOTSUPP; } if (cos > nic_dev->cos_config_num_max) { nicif_err(nic_dev, drv, netdev, "Invalid num_tc: %u, max cos: %u\n", cos, nic_dev->cos_config_num_max); return -EINVAL; } err = hinic3_configure_dcb_hw(nic_dev, cos ? 1 : 0); if (err) return err; return 0; } static u8 get_cos_num(u8 hw_valid_cos_bitmap) { u8 support_cos = 0; u8 i; for (i = 0; i < NIC_DCB_COS_MAX; i++) if (hw_valid_cos_bitmap & BIT(i)) support_cos++; return support_cos; } static void hinic3_sync_dcb_cfg(struct hinic3_nic_dev *nic_dev, const struct hinic3_dcb_config *dcb_cfg) { struct hinic3_dcb_config *hw_cfg = &nic_dev->hw_dcb_cfg; memcpy(hw_cfg, dcb_cfg, sizeof(struct hinic3_dcb_config)); } static int init_default_dcb_cfg(struct hinic3_nic_dev *nic_dev, struct hinic3_dcb_config *dcb_cfg) { u8 i, hw_dft_cos_map, port_cos_bitmap, dscp_ind; int err; err = hinic3_cos_valid_bitmap(nic_dev->hwdev, &hw_dft_cos_map, &port_cos_bitmap); if (err) { hinic3_err(nic_dev, drv, "None cos supported\n"); return -EFAULT; } nic_dev->func_dft_cos_bitmap = hw_dft_cos_map; nic_dev->port_dft_cos_bitmap = port_cos_bitmap; nic_dev->cos_config_num_max = get_cos_num(hw_dft_cos_map); dcb_cfg->trust = DCB_PCP; dcb_cfg->pcp_user_cos_num = nic_dev->cos_config_num_max; dcb_cfg->dscp_user_cos_num = nic_dev->cos_config_num_max; dcb_cfg->default_cos = (u8)fls(nic_dev->func_dft_cos_bitmap) - 1; dcb_cfg->pcp_valid_cos_map = hw_dft_cos_map; dcb_cfg->dscp_valid_cos_map = hw_dft_cos_map; for (i = 0; i < NIC_DCB_COS_MAX; i++) { dcb_cfg->pcp2cos[i] = hw_dft_cos_map & BIT(i) ? i : dcb_cfg->default_cos; for (dscp_ind = 0; dscp_ind < NIC_DCB_COS_MAX; dscp_ind++) dcb_cfg->dscp2cos[i * NIC_DCB_DSCP_NUM + dscp_ind] = dcb_cfg->pcp2cos[i]; } return 0; } void hinic3_dcb_reset_hw_config(struct hinic3_nic_dev *nic_dev) { struct hinic3_dcb_config dft_cfg = {0}; init_default_dcb_cfg(nic_dev, &dft_cfg); hinic3_sync_dcb_cfg(nic_dev, &dft_cfg); hinic3_info(nic_dev, drv, "Reset DCB configuration done\n"); } int hinic3_configure_dcb(struct net_device *netdev) { struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); int err; err = hinic3_sync_dcb_state(nic_dev->hwdev, 1, test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags) ? 1 : 0); if (err) { hinic3_err(nic_dev, drv, "Set dcb state failed\n"); return err; } if (test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags)) hinic3_sync_dcb_cfg(nic_dev, &nic_dev->wanted_dcb_cfg); else hinic3_dcb_reset_hw_config(nic_dev); return 0; } int hinic3_dcb_init(struct hinic3_nic_dev *nic_dev) { struct hinic3_dcb_config *dcb_cfg = &nic_dev->hw_dcb_cfg; int err; u8 dcb_en = test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags) ? 1 : 0; if (HINIC3_FUNC_IS_VF(nic_dev->hwdev)) return hinic3_set_tx_cos_state(nic_dev, dcb_en); err = init_default_dcb_cfg(nic_dev, dcb_cfg); if (err) { hinic3_err(nic_dev, drv, "Initialize dcb configuration failed\n"); return err; } memcpy(&nic_dev->wanted_dcb_cfg, &nic_dev->hw_dcb_cfg, sizeof(struct hinic3_dcb_config)); hinic3_info(nic_dev, drv, "Support num cos %u, default cos %u\n", nic_dev->cos_config_num_max, dcb_cfg->default_cos); err = hinic3_set_tx_cos_state(nic_dev, dcb_en); if (err) { hinic3_err(nic_dev, drv, "Set tx cos state failed\n"); return err; } sema_init(&nic_dev->dcb_sem, 1); return 0; } static int change_qos_cfg(struct hinic3_nic_dev *nic_dev, const struct hinic3_dcb_config *dcb_cfg) { struct net_device *netdev = nic_dev->netdev; int err = 0; u8 user_cos_num = hinic3_get_dev_user_cos_num(nic_dev); if (test_and_set_bit(HINIC3_DCB_UP_COS_SETTING, &nic_dev->dcb_flags)) { nicif_warn(nic_dev, drv, netdev, "Cos_up map setting in inprocess, please try again later\n"); return -EFAULT; } hinic3_sync_dcb_cfg(nic_dev, dcb_cfg); hinic3_update_qp_cos_cfg(nic_dev, user_cos_num); clear_bit(HINIC3_DCB_UP_COS_SETTING, &nic_dev->dcb_flags); return err; } int hinic3_dcbcfg_set_up_bitmap(struct hinic3_nic_dev *nic_dev) { int err, rollback_err; u8 netif_run = 0; struct hinic3_dcb_config old_dcb_cfg; u8 user_cos_num = hinic3_get_dev_user_cos_num(nic_dev); memcpy(&old_dcb_cfg, &nic_dev->hw_dcb_cfg, sizeof(struct hinic3_dcb_config)); if (!memcmp(&nic_dev->wanted_dcb_cfg, &old_dcb_cfg, sizeof(struct hinic3_dcb_config))) { nicif_info(nic_dev, drv, nic_dev->netdev, "Same valid up bitmap, don't need to change anything\n"); return 0; } rtnl_lock(); if (netif_running(nic_dev->netdev)) { netif_run = 1; hinic3_vport_down(nic_dev); } err = change_qos_cfg(nic_dev, &nic_dev->wanted_dcb_cfg); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Set cos_up map to hw failed\n"); goto change_qos_cfg_fail; } if (test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags)) { err = hinic3_setup_cos(nic_dev->netdev, user_cos_num, netif_run); if (err) goto set_err; } if (netif_run) { err = hinic3_vport_up(nic_dev); if (err) goto vport_up_fail; } rtnl_unlock(); return 0; vport_up_fail: if (test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags)) hinic3_setup_cos(nic_dev->netdev, user_cos_num ? 0 : user_cos_num, netif_run); set_err: rollback_err = change_qos_cfg(nic_dev, &old_dcb_cfg); if (rollback_err) nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to rollback qos configure\n"); change_qos_cfg_fail: if (netif_run) hinic3_vport_up(nic_dev); rtnl_unlock(); return err; }