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

986 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2021 Huawei Technologies Co., Ltd */
#define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/semaphore.h>
#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);
}