// 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 "hinic3_crm.h" #include "hinic3_hw.h" #include "hinic3_mt.h" #include "hinic3_nic_dev.h" #include "hinic3_nic_dbg.h" #include "hinic3_nic_qp.h" #include "hinic3_rx.h" #include "hinic3_tx.h" #include "hinic3_dcb.h" #include "hinic3_nic.h" #include "hinic3_mgmt_interface.h" #include "mag_mpu_cmd.h" #include "mag_cmd.h" typedef int (*nic_driv_module)(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size); struct nic_drv_module_handle { enum driver_cmd_type driv_cmd_name; nic_driv_module driv_func; }; static int get_nic_drv_version(void *buf_out, const u32 *out_size) { struct drv_version_info *ver_info = buf_out; int err; if (!buf_out) { pr_err("Buf_out is NULL.\n"); return -EINVAL; } if (*out_size != sizeof(*ver_info)) { pr_err("Unexpect out buf size from user :%u, expect: %lu\n", *out_size, sizeof(*ver_info)); return -EINVAL; } err = snprintf(ver_info->ver, sizeof(ver_info->ver), "%s %s", HINIC3_NIC_DRV_VERSION, "2023-05-17_19:56:38"); if (err < 0) return -EINVAL; return 0; } static int get_tx_info(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 q_id; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get tx info\n"); return -EFAULT; } if (!buf_in || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Buf_in or buf_out is NULL.\n"); return -EINVAL; } if (!out_size || in_size != sizeof(u32)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect in buf size from user :%u, expect: %lu\n", in_size, sizeof(u32)); return -EINVAL; } q_id = (u16)(*((u32 *)buf_in)); return hinic3_dbg_get_sq_info(nic_dev->hwdev, q_id, buf_out, *out_size); } static int get_q_num(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get queue number\n"); return -EFAULT; } if (!buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Get queue number para buf_out is NULL.\n"); return -EINVAL; } if (!out_size || *out_size != sizeof(u16)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %u, expect: %lu\n", *out_size, sizeof(u16)); return -EINVAL; } *((u16 *)buf_out) = nic_dev->q_params.num_qps; return 0; } static int get_tx_wqe_info(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct wqe_info *info = buf_in; u16 wqebb_cnt = 1; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get tx wqe info\n"); return -EFAULT; } if (!buf_in || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Buf_in or buf_out is NULL.\n"); return -EINVAL; } if (!out_size || in_size != sizeof(struct wqe_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, expect: %lu\n", in_size, sizeof(struct wqe_info)); return -EINVAL; } return hinic3_dbg_get_wqe_info(nic_dev->hwdev, (u16)info->q_id, (u16)info->wqe_id, wqebb_cnt, buf_out, (u16 *)out_size, HINIC3_SQ); } static int get_rx_info(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct nic_rq_info *rq_info = buf_out; u16 q_id; int err; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rx info\n"); return -EFAULT; } if (!buf_in || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Buf_in or buf_out is NULL.\n"); return -EINVAL; } if (!out_size || in_size != sizeof(u32)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, expect: %lu\n", in_size, sizeof(u32)); return -EINVAL; } q_id = (u16)(*((u32 *)buf_in)); err = hinic3_dbg_get_rq_info(nic_dev->hwdev, q_id, buf_out, *out_size); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Get rq info failed, ret is %d.\n", err); return err; } rq_info->delta = (u16)nic_dev->rxqs[q_id].delta; rq_info->ci = (u16)(nic_dev->rxqs[q_id].cons_idx & nic_dev->rxqs[q_id].q_mask); rq_info->sw_pi = nic_dev->rxqs[q_id].next_to_update; rq_info->msix_vector = nic_dev->rxqs[q_id].irq_id; rq_info->coalesc_timer_cfg = nic_dev->rxqs[q_id].last_coalesc_timer_cfg; rq_info->pending_limt = nic_dev->rxqs[q_id].last_pending_limt; return 0; } static int get_rx_wqe_info(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct wqe_info *info = buf_in; u16 wqebb_cnt = 1; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rx wqe info\n"); return -EFAULT; } if (!buf_in || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Buf_in or buf_out is NULL.\n"); return -EINVAL; } if (!out_size || in_size != sizeof(struct wqe_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, expect: %lu\n", in_size, sizeof(struct wqe_info)); return -EINVAL; } return hinic3_dbg_get_wqe_info(nic_dev->hwdev, (u16)info->q_id, (u16)info->wqe_id, wqebb_cnt, buf_out, (u16 *)out_size, HINIC3_RQ); } static int get_rx_cqe_info(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct wqe_info *info = buf_in; u16 q_id = 0; u16 idx = 0; if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't get rx cqe info\n"); return -EFAULT; } if (!buf_in || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Buf_in or buf_out is NULL.\n"); return -EINVAL; } if (in_size != sizeof(struct wqe_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, expect: %lu\n", in_size, sizeof(struct wqe_info)); return -EINVAL; } if (!out_size || *out_size != sizeof(struct hinic3_rq_cqe)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user :%u, expect: %lu\n", *out_size, sizeof(struct hinic3_rq_cqe)); return -EINVAL; } q_id = (u16)info->q_id; idx = (u16)info->wqe_id; if (q_id >= nic_dev->q_params.num_qps || idx >= nic_dev->rxqs[q_id].q_depth) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid q_id[%u] >= %u, or wqe idx[%u] >= %u.\n", q_id, nic_dev->q_params.num_qps, idx, nic_dev->rxqs[q_id].q_depth); return -EFAULT; } memcpy(buf_out, nic_dev->rxqs[q_id].rx_info[idx].cqe, sizeof(struct hinic3_rq_cqe)); return 0; } static void clean_nicdev_stats(struct hinic3_nic_dev *nic_dev) { u64_stats_update_begin(&nic_dev->stats.syncp); nic_dev->stats.netdev_tx_timeout = 0; nic_dev->stats.tx_carrier_off_drop = 0; nic_dev->stats.tx_invalid_qid = 0; nic_dev->stats.rsvd1 = 0; nic_dev->stats.rsvd2 = 0; u64_stats_update_end(&nic_dev->stats.syncp); } static int clear_func_static(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int i; *out_size = 0; #ifndef HAVE_NETDEV_STATS_IN_NETDEV memset(&nic_dev->net_stats, 0, sizeof(nic_dev->net_stats)); #endif clean_nicdev_stats(nic_dev); for (i = 0; i < nic_dev->max_qps; i++) { hinic3_rxq_clean_stats(&nic_dev->rxqs[i].rxq_stats); hinic3_txq_clean_stats(&nic_dev->txqs[i].txq_stats); } return 0; } static int get_loopback_mode(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic3_nic_loop_mode *mode = buf_out; if (!out_size || !mode) return -EINVAL; if (*out_size != sizeof(*mode)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %u, expect: %lu\n", *out_size, sizeof(*mode)); return -EINVAL; } return hinic3_get_loopback_mode(nic_dev->hwdev, (u8 *)&mode->loop_mode, (u8 *)&mode->loop_ctrl); } static int set_loopback_mode(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct hinic3_nic_loop_mode *mode = buf_in; int err; if (!test_bit(HINIC3_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't set loopback mode\n"); return -EFAULT; } if (!mode || !out_size || in_size != sizeof(*mode)) return -EINVAL; if (*out_size != sizeof(*mode)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %u, expect: %lu\n", *out_size, sizeof(*mode)); return -EINVAL; } err = hinic3_set_loopback_mode(nic_dev->hwdev, (u8)mode->loop_mode, (u8)mode->loop_ctrl); if (err == 0) nicif_info(nic_dev, drv, nic_dev->netdev, "Set loopback mode %u en %u succeed\n", mode->loop_mode, mode->loop_ctrl); return err; } enum hinic3_nic_link_mode { HINIC3_LINK_MODE_AUTO = 0, HINIC3_LINK_MODE_UP, HINIC3_LINK_MODE_DOWN, HINIC3_LINK_MODE_MAX, }; static int set_link_mode_param_valid(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, const u32 *out_size) { if (!test_bit(HINIC3_INTF_UP, &nic_dev->flags)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Netdev is down, can't set link mode\n"); return -EFAULT; } if (!buf_in || !out_size || in_size != sizeof(enum hinic3_nic_link_mode)) return -EINVAL; if (*out_size != sizeof(enum hinic3_nic_link_mode)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %u, expect: %lu\n", *out_size, sizeof(enum hinic3_nic_link_mode)); return -EINVAL; } return 0; } static int set_link_mode(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const enum hinic3_nic_link_mode *link = buf_in; u8 link_status; if (set_link_mode_param_valid(nic_dev, buf_in, in_size, out_size)) return -EFAULT; switch (*link) { case HINIC3_LINK_MODE_AUTO: if (hinic3_get_link_state(nic_dev->hwdev, &link_status)) link_status = false; hinic3_link_status_change(nic_dev, (bool)link_status); nicif_info(nic_dev, drv, nic_dev->netdev, "Set link mode: auto succeed, now is link %s\n", (link_status ? "up" : "down")); break; case HINIC3_LINK_MODE_UP: hinic3_link_status_change(nic_dev, true); nicif_info(nic_dev, drv, nic_dev->netdev, "Set link mode: up succeed\n"); break; case HINIC3_LINK_MODE_DOWN: hinic3_link_status_change(nic_dev, false); nicif_info(nic_dev, drv, nic_dev->netdev, "Set link mode: down succeed\n"); break; default: nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid link mode %d to set\n", *link); return -EINVAL; } return 0; } static int set_pf_bw_limit(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u32 pf_bw_limit; int err; if (HINIC3_FUNC_IS_VF(nic_dev->hwdev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "To set VF bandwidth rate, please use ip link cmd\n"); return -EINVAL; } if (!buf_in || !buf_out || in_size != sizeof(u32) || !out_size || *out_size != sizeof(u8)) return -EINVAL; pf_bw_limit = *((u32 *)buf_in); err = hinic3_set_pf_bw_limit(nic_dev->hwdev, pf_bw_limit); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Failed to set pf bandwidth limit to %d%%\n", pf_bw_limit); if (err < 0) return err; } *((u8 *)buf_out) = (u8)err; return 0; } static int get_pf_bw_limit(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic3_nic_io *nic_io = NULL; if (HINIC3_FUNC_IS_VF(nic_dev->hwdev)) { nicif_err(nic_dev, drv, nic_dev->netdev, "To get VF bandwidth rate, please use ip link cmd\n"); return -EINVAL; } if (!buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(u32)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user: %d, expect: %lu\n", *out_size, sizeof(u32)); return -EFAULT; } nic_io = hinic3_get_service_adapter(nic_dev->hwdev, SERVICE_T_NIC); if (!nic_io) return -EINVAL; *((u32 *)buf_out) = nic_io->nic_cfg.pf_bw_limit; return 0; } static int get_sset_count(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u32 count; if (!buf_in || in_size != sizeof(u32) || !out_size || *out_size != sizeof(u32) || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid parameters, in_size: %u\n", in_size); return -EINVAL; } switch (*((u32 *)buf_in)) { case HINIC3_SHOW_SSET_IO_STATS: count = hinic3_get_io_stats_size(nic_dev); break; default: count = 0; break; } *((u32 *)buf_out) = count; return 0; } static int get_sset_stats(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct hinic3_show_item *items = buf_out; u32 sset, count, size; int err; if (!buf_in || in_size != sizeof(u32) || !out_size || !buf_out) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid parameters, in_size: %u\n", in_size); return -EINVAL; } size = sizeof(u32); err = get_sset_count(nic_dev, buf_in, in_size, &count, &size); if (err) { nicif_err(nic_dev, drv, nic_dev->netdev, "Get sset count failed, ret=%d\n", err); return -EINVAL; } if (count * sizeof(*items) != *out_size) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user :%u, expect: %lu\n", *out_size, count * sizeof(*items)); return -EINVAL; } sset = *((u32 *)buf_in); switch (sset) { case HINIC3_SHOW_SSET_IO_STATS: hinic3_get_io_stats(nic_dev, items); break; default: nicif_err(nic_dev, drv, nic_dev->netdev, "Unknown %u to get stats\n", sset); err = -EINVAL; break; } return err; } static int update_pcp_dscp_cfg(struct hinic3_nic_dev *nic_dev, struct hinic3_dcb_config *wanted_dcb_cfg, const struct hinic3_mt_qos_dev_cfg *qos_in) { int i; u8 cos_num = 0, valid_cos_bitmap = 0; if (qos_in->cfg_bitmap & CMD_QOS_DEV_PCP2COS) { for (i = 0; i < NIC_DCB_UP_MAX; i++) { if (!(nic_dev->func_dft_cos_bitmap & BIT(qos_in->pcp2cos[i]))) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid cos=%u, func cos valid map is %u", qos_in->pcp2cos[i], nic_dev->func_dft_cos_bitmap); return -EINVAL; } if ((BIT(qos_in->pcp2cos[i]) & valid_cos_bitmap) == 0) { valid_cos_bitmap |= (u8)BIT(qos_in->pcp2cos[i]); cos_num++; } } memcpy(wanted_dcb_cfg->pcp2cos, qos_in->pcp2cos, sizeof(qos_in->pcp2cos)); wanted_dcb_cfg->pcp_user_cos_num = cos_num; wanted_dcb_cfg->pcp_valid_cos_map = valid_cos_bitmap; } if (qos_in->cfg_bitmap & CMD_QOS_DEV_DSCP2COS) { cos_num = 0; valid_cos_bitmap = 0; for (i = 0; i < NIC_DCB_IP_PRI_MAX; i++) { u8 cos = qos_in->dscp2cos[i] == DBG_DFLT_DSCP_VAL ? nic_dev->wanted_dcb_cfg.dscp2cos[i] : qos_in->dscp2cos[i]; if (cos >= NIC_DCB_UP_MAX || !(nic_dev->func_dft_cos_bitmap & BIT(cos))) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid cos=%u, func cos valid map is %u", cos, nic_dev->func_dft_cos_bitmap); return -EINVAL; } if ((BIT(cos) & valid_cos_bitmap) == 0) { valid_cos_bitmap |= (u8)BIT(cos); cos_num++; } } for (i = 0; i < NIC_DCB_IP_PRI_MAX; i++) wanted_dcb_cfg->dscp2cos[i] = qos_in->dscp2cos[i] == DBG_DFLT_DSCP_VAL ? nic_dev->hw_dcb_cfg.dscp2cos[i] : qos_in->dscp2cos[i]; wanted_dcb_cfg->dscp_user_cos_num = cos_num; wanted_dcb_cfg->dscp_valid_cos_map = valid_cos_bitmap; } return 0; } static int update_wanted_qos_cfg(struct hinic3_nic_dev *nic_dev, struct hinic3_dcb_config *wanted_dcb_cfg, const struct hinic3_mt_qos_dev_cfg *qos_in) { int ret; u8 cos_num, valid_cos_bitmap; if (qos_in->cfg_bitmap & CMD_QOS_DEV_TRUST) { if (qos_in->trust > DCB_DSCP) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid trust=%u\n", qos_in->trust); return -EINVAL; } wanted_dcb_cfg->trust = qos_in->trust; } if (qos_in->cfg_bitmap & CMD_QOS_DEV_DFT_COS) { if (!(BIT(qos_in->dft_cos) & nic_dev->func_dft_cos_bitmap)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Invalid dft_cos=%u\n", qos_in->dft_cos); return -EINVAL; } wanted_dcb_cfg->default_cos = qos_in->dft_cos; } ret = update_pcp_dscp_cfg(nic_dev, wanted_dcb_cfg, qos_in); if (ret) return ret; if (wanted_dcb_cfg->trust == DCB_PCP) { cos_num = wanted_dcb_cfg->pcp_user_cos_num; valid_cos_bitmap = wanted_dcb_cfg->pcp_valid_cos_map; } else { cos_num = wanted_dcb_cfg->dscp_user_cos_num; valid_cos_bitmap = wanted_dcb_cfg->dscp_valid_cos_map; } if (test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags)) { if (cos_num > nic_dev->q_params.num_qps) { nicif_err(nic_dev, drv, nic_dev->netdev, "DCB is on, cos num should not more than channel num:%u\n", nic_dev->q_params.num_qps); return -EOPNOTSUPP; } } if (!(BIT(wanted_dcb_cfg->default_cos) & valid_cos_bitmap)) { nicif_info(nic_dev, drv, nic_dev->netdev, "Current default_cos=%u, change to %u\n", wanted_dcb_cfg->default_cos, (u8)fls(valid_cos_bitmap) - 1); wanted_dcb_cfg->default_cos = (u8)fls(valid_cos_bitmap) - 1; } return 0; } static int dcb_mt_qos_map(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct hinic3_mt_qos_dev_cfg *qos_in = buf_in; struct hinic3_mt_qos_dev_cfg *qos_out = buf_out; u8 i; int err; if (!buf_out || !out_size || !buf_in) return -EINVAL; if (*out_size != sizeof(*qos_out) || in_size != sizeof(*qos_in)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, out_size: %u, expect: %lu\n", in_size, *out_size, sizeof(*qos_in)); return -EINVAL; } memcpy(qos_out, qos_in, sizeof(*qos_in)); qos_out->head.status = 0; if (qos_in->op_code & MT_DCB_OPCODE_WR) { memcpy(&nic_dev->wanted_dcb_cfg, &nic_dev->hw_dcb_cfg, sizeof(struct hinic3_dcb_config)); err = update_wanted_qos_cfg(nic_dev, &nic_dev->wanted_dcb_cfg, qos_in); if (err) { qos_out->head.status = MT_EINVAL; return 0; } err = hinic3_dcbcfg_set_up_bitmap(nic_dev); if (err) qos_out->head.status = MT_EIO; } else { qos_out->dft_cos = nic_dev->hw_dcb_cfg.default_cos; qos_out->trust = nic_dev->hw_dcb_cfg.trust; for (i = 0; i < NIC_DCB_UP_MAX; i++) qos_out->pcp2cos[i] = nic_dev->hw_dcb_cfg.pcp2cos[i]; for (i = 0; i < NIC_DCB_IP_PRI_MAX; i++) qos_out->dscp2cos[i] = nic_dev->hw_dcb_cfg.dscp2cos[i]; } return 0; } static int dcb_mt_dcb_state(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct hinic3_mt_dcb_state *dcb_in = buf_in; struct hinic3_mt_dcb_state *dcb_out = buf_out; int err; u8 user_cos_num; u8 netif_run = 0; if (!buf_in || !buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*dcb_out) || in_size != sizeof(*dcb_in)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, out_size: %u, expect: %lu\n", in_size, *out_size, sizeof(*dcb_in)); return -EINVAL; } user_cos_num = hinic3_get_dev_user_cos_num(nic_dev); memcpy(dcb_out, dcb_in, sizeof(*dcb_in)); dcb_out->head.status = 0; if (dcb_in->op_code & MT_DCB_OPCODE_WR) { if (test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags) == dcb_in->state) return 0; if (dcb_in->state) { if (user_cos_num > nic_dev->q_params.num_qps) { nicif_err(nic_dev, drv, nic_dev->netdev, "cos num %u should not more than channel num %u\n", user_cos_num, nic_dev->q_params.num_qps); return -EOPNOTSUPP; } } rtnl_lock(); if (netif_running(nic_dev->netdev)) { netif_run = 1; hinic3_vport_down(nic_dev); } err = hinic3_setup_cos(nic_dev->netdev, dcb_in->state ? user_cos_num : 0, netif_run); if (err) goto setup_cos_fail; if (netif_run) { err = hinic3_vport_up(nic_dev); if (err) goto vport_up_fail; } rtnl_unlock(); } else { dcb_out->state = !!test_bit(HINIC3_DCB_ENABLE, &nic_dev->flags); } return 0; vport_up_fail: hinic3_setup_cos(nic_dev->netdev, dcb_in->state ? 0 : user_cos_num, netif_run); setup_cos_fail: if (netif_run) hinic3_vport_up(nic_dev); rtnl_unlock(); return err; } static int dcb_mt_hw_qos_get(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { const struct hinic3_mt_qos_cos_cfg *cos_cfg_in = buf_in; struct hinic3_mt_qos_cos_cfg *cos_cfg_out = buf_out; if (!buf_in || !buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*cos_cfg_out) || in_size != sizeof(*cos_cfg_in)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, out_size: %u, expect: %lu\n", in_size, *out_size, sizeof(*cos_cfg_in)); return -EINVAL; } memcpy(cos_cfg_out, cos_cfg_in, sizeof(*cos_cfg_in)); cos_cfg_out->head.status = 0; cos_cfg_out->port_id = hinic3_physical_port_id(nic_dev->hwdev); cos_cfg_out->func_cos_bitmap = (u8)nic_dev->func_dft_cos_bitmap; cos_cfg_out->port_cos_bitmap = (u8)nic_dev->port_dft_cos_bitmap; cos_cfg_out->func_max_cos_num = nic_dev->cos_config_num_max; return 0; } static int get_inter_num(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { u16 intr_num; intr_num = hinic3_intr_num(nic_dev->hwdev); if (!buf_out || !out_size || *out_size != sizeof(u16)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user :%u, expect: %lu\n", *out_size, sizeof(u16)); return -EFAULT; } *(u16 *)buf_out = intr_num; return 0; } static int get_netdev_name(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { if (!buf_out || !out_size || *out_size != IFNAMSIZ) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect out buf size from user :%u, expect: %u\n", *out_size, IFNAMSIZ); return -EFAULT; } strscpy(buf_out, nic_dev->netdev->name, IFNAMSIZ); return 0; } static int get_netdev_tx_timeout(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct net_device *net_dev = nic_dev->netdev; int *tx_timeout = buf_out; if (!buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(int)) { nicif_err(nic_dev, drv, net_dev, "Unexpect buf size from user, out_size: %u, expect: %lu\n", *out_size, sizeof(int)); return -EINVAL; } *tx_timeout = net_dev->watchdog_timeo; return 0; } static int set_netdev_tx_timeout(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct net_device *net_dev = nic_dev->netdev; const int *tx_timeout = buf_in; if (!buf_in) return -EINVAL; if (in_size != sizeof(int)) { nicif_err(nic_dev, drv, net_dev, "Unexpect buf size from user, in_size: %u, expect: %lu\n", in_size, sizeof(int)); return -EINVAL; } net_dev->watchdog_timeo = *tx_timeout * HZ; nicif_info(nic_dev, drv, net_dev, "Set tx timeout check period to %ds\n", *tx_timeout); return 0; } static int get_xsfp_present(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct mag_cmd_get_xsfp_present *sfp_abs = buf_out; if (!buf_in || !buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*sfp_abs) || in_size != sizeof(*sfp_abs)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, out_size: %u, expect: %lu\n", in_size, *out_size, sizeof(*sfp_abs)); return -EINVAL; } sfp_abs->head.status = 0; sfp_abs->abs_status = hinic3_if_sfp_absent(nic_dev->hwdev); return 0; } static int get_xsfp_info(struct hinic3_nic_dev *nic_dev, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { struct mag_cmd_get_xsfp_info *sfp_info = buf_out; int err; if (!buf_in || !buf_out || !out_size) return -EINVAL; if (*out_size != sizeof(*sfp_info) || in_size != sizeof(*sfp_info)) { nicif_err(nic_dev, drv, nic_dev->netdev, "Unexpect buf size from user, in_size: %u, out_size: %u, expect: %lu\n", in_size, *out_size, sizeof(*sfp_info)); return -EINVAL; } err = hinic3_get_sfp_info(nic_dev->hwdev, sfp_info); if (err) { sfp_info->head.status = MT_EIO; return 0; } return 0; } static const struct nic_drv_module_handle nic_driv_module_cmd_handle[] = { {TX_INFO, get_tx_info}, {Q_NUM, get_q_num}, {TX_WQE_INFO, get_tx_wqe_info}, {RX_INFO, get_rx_info}, {RX_WQE_INFO, get_rx_wqe_info}, {RX_CQE_INFO, get_rx_cqe_info}, {GET_INTER_NUM, get_inter_num}, {CLEAR_FUNC_STASTIC, clear_func_static}, {GET_LOOPBACK_MODE, get_loopback_mode}, {SET_LOOPBACK_MODE, set_loopback_mode}, {SET_LINK_MODE, set_link_mode}, {SET_PF_BW_LIMIT, set_pf_bw_limit}, {GET_PF_BW_LIMIT, get_pf_bw_limit}, {GET_SSET_COUNT, get_sset_count}, {GET_SSET_ITEMS, get_sset_stats}, {DCB_STATE, dcb_mt_dcb_state}, {QOS_DEV, dcb_mt_qos_map}, {GET_QOS_COS, dcb_mt_hw_qos_get}, {GET_ULD_DEV_NAME, get_netdev_name}, {GET_TX_TIMEOUT, get_netdev_tx_timeout}, {SET_TX_TIMEOUT, set_netdev_tx_timeout}, {GET_XSFP_PRESENT, get_xsfp_present}, {GET_XSFP_INFO, get_xsfp_info}, }; static int send_to_nic_driver(struct hinic3_nic_dev *nic_dev, u32 cmd, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { int index, num_cmds = sizeof(nic_driv_module_cmd_handle) / sizeof(nic_driv_module_cmd_handle[0]); enum driver_cmd_type cmd_type = (enum driver_cmd_type)cmd; int err = 0; mutex_lock(&nic_dev->nic_mutex); for (index = 0; index < num_cmds; index++) { if (cmd_type == nic_driv_module_cmd_handle[index].driv_cmd_name) { err = nic_driv_module_cmd_handle[index].driv_func (nic_dev, buf_in, in_size, buf_out, out_size); break; } } mutex_unlock(&nic_dev->nic_mutex); if (index == num_cmds) { pr_err("Can't find callback for %d\n", cmd_type); return -EINVAL; } return err; } int nic_ioctl(void *uld_dev, u32 cmd, const void *buf_in, u32 in_size, void *buf_out, u32 *out_size) { if (cmd == GET_DRV_VERSION) return get_nic_drv_version(buf_out, out_size); else if (!uld_dev) return -EINVAL; return send_to_nic_driver(uld_dev, cmd, buf_in, in_size, buf_out, out_size); }