| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/20 17:19 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"HINIC3"
],
"FocusSymbols": [
"hinic3_set_ringparam",
"hinic3_set_coalesce",
"hinic3_set_rxnfc",
"hinic3_set_channels",
"hinic3_set_rxfh",
"hinic3_change_channel_settings",
"hinic3_get_ethtool_stats",
"hinic3_get_strings",
"hinic3_get_eth_phy_stats",
"hinic3_get_eth_mac_stats",
"hinic3_get_eth_ctrl_stats",
"hinic3_get_rmon_stats",
"hinic3_get_pause_stats",
"hinic3_get_coalesce",
"hinic3_get_rxnfc",
"hinic3_get_rxfh"
],
"Reasoning": "The patch adds multiple ethtool operations (ringparam, coalesce, rxnfc, channels, rxfh, stats) to the hinic3 network driver. These operations can be triggered via netlink/ioctl from userspace and modify the driver's internal state, so they should be fuzzed.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/20 17:19 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit a109dd523ca18e984944f181e6a820aa25c9a2eb\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Jul 20 17:19:46 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c\nindex 90fc16288de951..c65f6a4536265f 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c\n@@ -9,14 +9,21 @@\n #include \u003clinux/errno.h\u003e\n #include \u003clinux/etherdevice.h\u003e\n #include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/netlink.h\u003e\n #include \u003clinux/ethtool.h\u003e\n \n #include \"hinic3_lld.h\"\n #include \"hinic3_hw_comm.h\"\n #include \"hinic3_nic_dev.h\"\n #include \"hinic3_nic_cfg.h\"\n+#include \"hinic3_rss.h\"\n \n #define HINIC3_MGMT_VERSION_MAX_LEN 32\n+/* Coalesce time properties in microseconds */\n+#define COALESCE_PENDING_LIMIT_UNIT 8\n+#define COALESCE_TIMER_CFG_UNIT 5\n+#define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT)\n+#define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT)\n \n static void hinic3_get_drvinfo(struct net_device *netdev,\n \t\t\t struct ethtool_drvinfo *info)\n@@ -409,14 +416,873 @@ hinic3_get_link_ksettings(struct net_device *netdev,\n \treturn 0;\n }\n \n+static void hinic3_get_ringparam(struct net_device *netdev,\n+\t\t\t\t struct ethtool_ringparam *ring,\n+\t\t\t\t struct kernel_ethtool_ringparam *kernel_ring,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\n+\tring-\u003erx_max_pending = HINIC3_MAX_RX_QUEUE_DEPTH;\n+\tring-\u003etx_max_pending = HINIC3_MAX_TX_QUEUE_DEPTH;\n+\tring-\u003erx_pending = nic_dev-\u003eq_params.rq_depth;\n+\tring-\u003etx_pending = nic_dev-\u003eq_params.sq_depth;\n+}\n+\n+static void hinic3_update_qp_depth(struct net_device *netdev,\n+\t\t\t\t u32 sq_depth, u32 rq_depth)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tu16 i;\n+\n+\tnic_dev-\u003eq_params.sq_depth = sq_depth;\n+\tnic_dev-\u003eq_params.rq_depth = rq_depth;\n+\tfor (i = 0; i \u003c nic_dev-\u003emax_qps; i++) {\n+\t\tnic_dev-\u003etxqs[i].q_depth = sq_depth;\n+\t\tnic_dev-\u003etxqs[i].q_mask = sq_depth - 1;\n+\t\tnic_dev-\u003erxqs[i].q_depth = rq_depth;\n+\t\tnic_dev-\u003erxqs[i].q_mask = rq_depth - 1;\n+\t}\n+}\n+\n+static int hinic3_check_ringparam_valid(struct net_device *netdev,\n+\t\t\t\t\tconst struct ethtool_ringparam *ring,\n+\t\t\t\t\tstruct netlink_ext_ack *extack)\n+{\n+\tif (ring-\u003etx_pending \u003c HINIC3_MIN_QUEUE_DEPTH ||\n+\t ring-\u003erx_pending \u003c HINIC3_MIN_QUEUE_DEPTH) {\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t \"Queue depth underflow[%d]: tx[%d], rx[%d]\",\n+\t\t\t\t HINIC3_MIN_QUEUE_DEPTH,\n+\t\t\t\t ring-\u003etx_pending, ring-\u003erx_pending);\n+\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_set_ringparam(struct net_device *netdev,\n+\t\t\t\tstruct ethtool_ringparam *ring,\n+\t\t\t\tstruct kernel_ethtool_ringparam *kernel_ring,\n+\t\t\t\tstruct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_dyna_txrxq_params q_params = {};\n+\tu32 new_sq_depth, new_rq_depth;\n+\tint err;\n+\n+\terr = hinic3_check_ringparam_valid(netdev, ring, extack);\n+\tif (err)\n+\t\treturn err;\n+\n+\tnew_sq_depth = rounddown_pow_of_two(ring-\u003etx_pending);\n+\tnew_rq_depth = rounddown_pow_of_two(ring-\u003erx_pending);\n+\tif (new_sq_depth == nic_dev-\u003eq_params.sq_depth \u0026\u0026\n+\t new_rq_depth == nic_dev-\u003eq_params.rq_depth)\n+\t\treturn 0;\n+\n+\tif (new_sq_depth != ring-\u003etx_pending ||\n+\t new_rq_depth != ring-\u003erx_pending)\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t \"Requested Tx/Rx ring depth %u/%u trimmed to %u/%u\",\n+\t\t\t\t ring-\u003etx_pending, ring-\u003erx_pending,\n+\t\t\t\t new_sq_depth, new_rq_depth);\n+\n+\tif (!netif_running(netdev)) {\n+\t\thinic3_update_qp_depth(netdev, new_sq_depth, new_rq_depth);\n+\t} else {\n+\t\tq_params = nic_dev-\u003eq_params;\n+\t\tq_params.sq_depth = new_sq_depth;\n+\t\tq_params.rq_depth = new_rq_depth;\n+\n+\t\terr = hinic3_change_channel_settings(netdev, \u0026q_params);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+struct hinic3_stats {\n+\tchar name[ETH_GSTRING_LEN];\n+\tu32 size;\n+\tint offset;\n+};\n+\n+#define HINIC3_RXQ_STAT(_stat_item) { \\\n+\t.name = \"rxq%d_\"#_stat_item, \\\n+\t.size = sizeof_field(struct hinic3_rxq_stats, _stat_item), \\\n+\t.offset = offsetof(struct hinic3_rxq_stats, _stat_item) \\\n+}\n+\n+#define HINIC3_TXQ_STAT(_stat_item) { \\\n+\t.name = \"txq%d_\"#_stat_item, \\\n+\t.size = sizeof_field(struct hinic3_txq_stats, _stat_item), \\\n+\t.offset = offsetof(struct hinic3_txq_stats, _stat_item) \\\n+}\n+\n+static struct hinic3_stats hinic3_rx_queue_stats[] = {\n+\tHINIC3_RXQ_STAT(csum_errors),\n+\tHINIC3_RXQ_STAT(other_errors),\n+\tHINIC3_RXQ_STAT(rx_buf_empty),\n+\tHINIC3_RXQ_STAT(alloc_skb_err),\n+\tHINIC3_RXQ_STAT(alloc_rx_buf_err),\n+};\n+\n+static struct hinic3_stats hinic3_tx_queue_stats[] = {\n+\tHINIC3_TXQ_STAT(busy),\n+\tHINIC3_TXQ_STAT(skb_pad_err),\n+\tHINIC3_TXQ_STAT(frag_len_overflow),\n+\tHINIC3_TXQ_STAT(offload_cow_skb_err),\n+\tHINIC3_TXQ_STAT(map_frag_err),\n+\tHINIC3_TXQ_STAT(unknown_tunnel_pkt),\n+\tHINIC3_TXQ_STAT(frag_size_err),\n+};\n+\n+#define HINIC3_FUNC_STAT(_stat_item) {\t\\\n+\t.name = #_stat_item, \\\n+\t.size = sizeof_field(struct l2nic_vport_stats, _stat_item), \\\n+\t.offset = offsetof(struct l2nic_vport_stats, _stat_item) \\\n+}\n+\n+static struct hinic3_stats hinic3_function_stats[] = {\n+\tHINIC3_FUNC_STAT(tx_unicast_pkts_vport),\n+\tHINIC3_FUNC_STAT(tx_unicast_bytes_vport),\n+\tHINIC3_FUNC_STAT(tx_multicast_pkts_vport),\n+\tHINIC3_FUNC_STAT(tx_multicast_bytes_vport),\n+\tHINIC3_FUNC_STAT(tx_broadcast_pkts_vport),\n+\tHINIC3_FUNC_STAT(tx_broadcast_bytes_vport),\n+\n+\tHINIC3_FUNC_STAT(rx_unicast_pkts_vport),\n+\tHINIC3_FUNC_STAT(rx_unicast_bytes_vport),\n+\tHINIC3_FUNC_STAT(rx_multicast_pkts_vport),\n+\tHINIC3_FUNC_STAT(rx_multicast_bytes_vport),\n+\tHINIC3_FUNC_STAT(rx_broadcast_pkts_vport),\n+\tHINIC3_FUNC_STAT(rx_broadcast_bytes_vport),\n+\n+\tHINIC3_FUNC_STAT(tx_discard_vport),\n+\tHINIC3_FUNC_STAT(rx_discard_vport),\n+\tHINIC3_FUNC_STAT(tx_err_vport),\n+\tHINIC3_FUNC_STAT(rx_err_vport),\n+};\n+\n+#define HINIC3_PORT_STAT(_stat_item) { \\\n+\t.name = #_stat_item, \\\n+\t.size = sizeof_field(struct mag_cmd_port_stats, _stat_item), \\\n+\t.offset = offsetof(struct mag_cmd_port_stats, _stat_item) \\\n+}\n+\n+static struct hinic3_stats hinic3_port_stats[] = {\n+\tHINIC3_PORT_STAT(mac_tx_fragment_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_undersize_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_undermin_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_1519_max_bad_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_1519_max_good_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_oversize_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_jabber_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_bad_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_bad_oct_num),\n+\tHINIC3_PORT_STAT(mac_tx_good_oct_num),\n+\tHINIC3_PORT_STAT(mac_tx_total_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_uni_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri0_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri1_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri2_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri3_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri4_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri5_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri6_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_pfc_pri7_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_err_all_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_from_app_good_pkt_num),\n+\tHINIC3_PORT_STAT(mac_tx_from_app_bad_pkt_num),\n+\n+\tHINIC3_PORT_STAT(mac_rx_undermin_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_1519_max_bad_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_1519_max_good_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_bad_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_bad_oct_num),\n+\tHINIC3_PORT_STAT(mac_rx_good_oct_num),\n+\tHINIC3_PORT_STAT(mac_rx_total_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_uni_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri0_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri1_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri2_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri3_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri4_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri5_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri6_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_pfc_pri7_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_send_app_good_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_send_app_bad_pkt_num),\n+\tHINIC3_PORT_STAT(mac_rx_unfilter_pkt_num),\n+};\n+\n+static int hinic3_get_sset_count(struct net_device *netdev, int sset)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tint count, q_num;\n+\n+\tswitch (sset) {\n+\tcase ETH_SS_STATS:\n+\t\tq_num = nic_dev-\u003eq_params.num_qps;\n+\t\tcount = ARRAY_SIZE(hinic3_function_stats) +\n+\t\t\t(ARRAY_SIZE(hinic3_tx_queue_stats) +\n+\t\t\t ARRAY_SIZE(hinic3_rx_queue_stats)) *\n+\t\t\tq_num;\n+\n+\t\tif (!HINIC3_IS_VF(nic_dev-\u003ehwdev))\n+\t\t\tcount += ARRAY_SIZE(hinic3_port_stats);\n+\n+\t\treturn count;\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+}\n+\n+static u64 get_val_of_ptr(u32 size, const void *ptr)\n+{\n+\tu64 ret = size == sizeof(u64) ? *(u64 *)ptr :\n+\t\t size == sizeof(u32) ? *(u32 *)ptr :\n+\t\t size == sizeof(u16) ? *(u16 *)ptr :\n+\t\t *(u8 *)ptr;\n+\n+\treturn ret;\n+}\n+\n+static void hinic3_get_drv_queue_stats(struct net_device *netdev, u64 *data)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_txq_stats txq_stats = {};\n+\tstruct hinic3_rxq_stats rxq_stats = {};\n+\tu16 i = 0, j, qid;\n+\tchar *p;\n+\n+\tfor (qid = 0; qid \u003c nic_dev-\u003eq_params.num_qps; qid++) {\n+\t\tif (!nic_dev-\u003etxqs)\n+\t\t\tbreak;\n+\n+\t\thinic3_txq_get_stats(\u0026nic_dev-\u003etxqs[qid], \u0026txq_stats);\n+\t\tfor (j = 0; j \u003c ARRAY_SIZE(hinic3_tx_queue_stats); j++, i++) {\n+\t\t\tp = (char *)\u0026txq_stats +\n+\t\t\t hinic3_tx_queue_stats[j].offset;\n+\t\t\tdata[i] = get_val_of_ptr(hinic3_tx_queue_stats[j].size,\n+\t\t\t\t\t\t p);\n+\t\t}\n+\t}\n+\n+\ti = nic_dev-\u003eq_params.num_qps * ARRAY_SIZE(hinic3_tx_queue_stats);\n+\tfor (qid = 0; qid \u003c nic_dev-\u003eq_params.num_qps; qid++) {\n+\t\tif (!nic_dev-\u003erxqs)\n+\t\t\tbreak;\n+\n+\t\thinic3_rxq_get_stats(\u0026nic_dev-\u003erxqs[qid], \u0026rxq_stats);\n+\t\tfor (j = 0; j \u003c ARRAY_SIZE(hinic3_rx_queue_stats); j++, i++) {\n+\t\t\tp = (char *)\u0026rxq_stats +\n+\t\t\t hinic3_rx_queue_stats[j].offset;\n+\t\t\tdata[i] = get_val_of_ptr(hinic3_rx_queue_stats[j].size,\n+\t\t\t\t\t\t p);\n+\t\t}\n+\t}\n+}\n+\n+static u16 hinic3_get_ethtool_port_stats(struct net_device *netdev, u64 *data)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct mag_cmd_port_stats *ps;\n+\tu16 i = 0, j;\n+\tchar *p;\n+\tint err;\n+\n+\tps = kmalloc_obj(*ps);\n+\tif (!ps)\n+\t\tgoto err_zero_stats;\n+\n+\terr = hinic3_get_phy_port_stats(nic_dev-\u003ehwdev, ps);\n+\tif (err) {\n+\t\tkfree(ps);\n+\t\tnetdev_err(netdev, \"Failed to get port stats from fw\\n\");\n+\t\tgoto err_zero_stats;\n+\t}\n+\n+\tfor (j = 0; j \u003c ARRAY_SIZE(hinic3_port_stats); j++, i++) {\n+\t\tp = (char *)ps + hinic3_port_stats[j].offset;\n+\t\tdata[i] = get_val_of_ptr(hinic3_port_stats[j].size, p);\n+\t}\n+\n+\tkfree(ps);\n+\n+\treturn i;\n+\n+err_zero_stats:\n+\tmemset(\u0026data[i], 0, ARRAY_SIZE(hinic3_port_stats) * sizeof(*data));\n+\n+\treturn i + ARRAY_SIZE(hinic3_port_stats);\n+}\n+\n+static void hinic3_get_ethtool_stats(struct net_device *netdev,\n+\t\t\t\t struct ethtool_stats *stats, u64 *data)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct l2nic_vport_stats vport_stats = {};\n+\tu16 i = 0, j;\n+\tchar *p;\n+\tint err;\n+\n+\terr = hinic3_get_vport_stats(nic_dev-\u003ehwdev,\n+\t\t\t\t hinic3_global_func_id(nic_dev-\u003ehwdev),\n+\t\t\t\t \u0026vport_stats);\n+\tif (err)\n+\t\tnetdev_err(netdev, \"Failed to get function stats from fw\\n\");\n+\n+\tfor (j = 0; j \u003c ARRAY_SIZE(hinic3_function_stats); j++, i++) {\n+\t\tp = (char *)\u0026vport_stats + hinic3_function_stats[j].offset;\n+\t\tdata[i] = get_val_of_ptr(hinic3_function_stats[j].size, p);\n+\t}\n+\n+\tif (!HINIC3_IS_VF(nic_dev-\u003ehwdev))\n+\t\ti += hinic3_get_ethtool_port_stats(netdev, data + i);\n+\n+\thinic3_get_drv_queue_stats(netdev, data + i);\n+}\n+\n+static u16 hinic3_get_hw_stats_strings(struct net_device *netdev, char *p)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tu16 i, cnt = 0;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(hinic3_function_stats); i++) {\n+\t\tmemcpy(p, hinic3_function_stats[i].name, ETH_GSTRING_LEN);\n+\t\tp += ETH_GSTRING_LEN;\n+\t\tcnt++;\n+\t}\n+\n+\tif (!HINIC3_IS_VF(nic_dev-\u003ehwdev)) {\n+\t\tfor (i = 0; i \u003c ARRAY_SIZE(hinic3_port_stats); i++) {\n+\t\t\tmemcpy(p, hinic3_port_stats[i].name, ETH_GSTRING_LEN);\n+\t\t\tp += ETH_GSTRING_LEN;\n+\t\t\tcnt++;\n+\t\t}\n+\t}\n+\n+\treturn cnt;\n+}\n+\n+static void hinic3_get_qp_stats_strings(struct net_device *netdev, char *p)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tu8 *data = p;\n+\tu16 i, j;\n+\n+\tfor (i = 0; i \u003c nic_dev-\u003eq_params.num_qps; i++) {\n+\t\tfor (j = 0; j \u003c ARRAY_SIZE(hinic3_tx_queue_stats); j++)\n+\t\t\tethtool_sprintf(\u0026data,\n+\t\t\t\t\thinic3_tx_queue_stats[j].name, i);\n+\t}\n+\n+\tfor (i = 0; i \u003c nic_dev-\u003eq_params.num_qps; i++) {\n+\t\tfor (j = 0; j \u003c ARRAY_SIZE(hinic3_rx_queue_stats); j++)\n+\t\t\tethtool_sprintf(\u0026data,\n+\t\t\t\t\thinic3_rx_queue_stats[j].name, i);\n+\t}\n+}\n+\n+static void hinic3_get_strings(struct net_device *netdev,\n+\t\t\t u32 stringset, u8 *data)\n+{\n+\tchar *p = (char *)data;\n+\tu16 offset;\n+\n+\tswitch (stringset) {\n+\tcase ETH_SS_STATS:\n+\t\toffset = hinic3_get_hw_stats_strings(netdev, p);\n+\t\thinic3_get_qp_stats_strings(netdev,\n+\t\t\t\t\t p + offset * ETH_GSTRING_LEN);\n+\n+\t\treturn;\n+\tdefault:\n+\t\tnetdev_err(netdev, \"Invalid string set %u.\\n\", stringset);\n+\t\treturn;\n+\t}\n+}\n+\n+static void hinic3_get_eth_phy_stats(struct net_device *netdev,\n+\t\t\t\t struct ethtool_eth_phy_stats *phy_stats)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct mag_cmd_port_stats *ps;\n+\tint err;\n+\n+\tps = kmalloc_obj(*ps);\n+\tif (!ps)\n+\t\treturn;\n+\n+\terr = hinic3_get_phy_port_stats(nic_dev-\u003ehwdev, ps);\n+\tif (err) {\n+\t\tkfree(ps);\n+\t\tnetdev_err(netdev, \"Failed to get eth phy stats from fw\\n\");\n+\t\treturn;\n+\t}\n+\n+\tphy_stats-\u003eSymbolErrorDuringCarrier = ps-\u003emac_rx_sym_err_pkt_num;\n+\n+\tkfree(ps);\n+}\n+\n+static void hinic3_get_eth_mac_stats(struct net_device *netdev,\n+\t\t\t\t struct ethtool_eth_mac_stats *mac_stats)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct mag_cmd_port_stats *ps;\n+\tint err;\n+\n+\tps = kmalloc_obj(*ps);\n+\tif (!ps)\n+\t\treturn;\n+\n+\terr = hinic3_get_phy_port_stats(nic_dev-\u003ehwdev, ps);\n+\tif (err) {\n+\t\tkfree(ps);\n+\t\tnetdev_err(netdev, \"Failed to get eth mac stats from fw\\n\");\n+\t\treturn;\n+\t}\n+\n+\tmac_stats-\u003eFramesTransmittedOK = ps-\u003emac_tx_good_pkt_num;\n+\tmac_stats-\u003eFramesReceivedOK = ps-\u003emac_rx_good_pkt_num;\n+\tmac_stats-\u003eFrameCheckSequenceErrors = ps-\u003emac_rx_fcs_err_pkt_num;\n+\tmac_stats-\u003eOctetsTransmittedOK = ps-\u003emac_tx_total_oct_num;\n+\tmac_stats-\u003eOctetsReceivedOK = ps-\u003emac_rx_total_oct_num;\n+\tmac_stats-\u003eMulticastFramesXmittedOK = ps-\u003emac_tx_multi_pkt_num;\n+\tmac_stats-\u003eBroadcastFramesXmittedOK = ps-\u003emac_tx_broad_pkt_num;\n+\tmac_stats-\u003eMulticastFramesReceivedOK = ps-\u003emac_rx_multi_pkt_num;\n+\tmac_stats-\u003eBroadcastFramesReceivedOK = ps-\u003emac_rx_broad_pkt_num;\n+\n+\tkfree(ps);\n+}\n+\n+static void hinic3_get_eth_ctrl_stats(struct net_device *netdev,\n+\t\t\t\t struct ethtool_eth_ctrl_stats *ctrl_stats)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct mag_cmd_port_stats *ps;\n+\tint err;\n+\n+\tps = kmalloc_obj(*ps);\n+\tif (!ps)\n+\t\treturn;\n+\n+\terr = hinic3_get_phy_port_stats(nic_dev-\u003ehwdev, ps);\n+\tif (err) {\n+\t\tkfree(ps);\n+\t\tnetdev_err(netdev, \"Failed to get eth ctrl stats from fw\\n\");\n+\t\treturn;\n+\t}\n+\n+\tctrl_stats-\u003eMACControlFramesTransmitted = ps-\u003emac_tx_control_pkt_num;\n+\tctrl_stats-\u003eMACControlFramesReceived = ps-\u003emac_rx_control_pkt_num;\n+\n+\tkfree(ps);\n+}\n+\n+static const struct ethtool_rmon_hist_range hinic3_rmon_ranges[] = {\n+\t{ 0, 64 },\n+\t{ 65, 127 },\n+\t{ 128, 255 },\n+\t{ 256, 511 },\n+\t{ 512, 1023 },\n+\t{ 1024, 1518 },\n+\t{ 1519, 2047 },\n+\t{ 2048, 4095 },\n+\t{ 4096, 8191 },\n+\t{ 8192, 9216 },\n+\t{ 9217, 12287 },\n+\t{}\n+};\n+\n+static void hinic3_get_rmon_stats(struct net_device *netdev,\n+\t\t\t\t struct ethtool_rmon_stats *rmon_stats,\n+\t\t\t\t const struct ethtool_rmon_hist_range **ranges)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct mag_cmd_port_stats *ps;\n+\tint err;\n+\n+\tps = kmalloc_obj(*ps);\n+\tif (!ps)\n+\t\treturn;\n+\n+\terr = hinic3_get_phy_port_stats(nic_dev-\u003ehwdev, ps);\n+\tif (err) {\n+\t\tkfree(ps);\n+\t\tnetdev_err(netdev, \"Failed to get eth rmon stats from fw\\n\");\n+\t\treturn;\n+\t}\n+\n+\trmon_stats-\u003eundersize_pkts\t= ps-\u003emac_rx_undersize_pkt_num;\n+\trmon_stats-\u003eoversize_pkts\t= ps-\u003emac_rx_oversize_pkt_num;\n+\trmon_stats-\u003efragments\t\t= ps-\u003emac_rx_fragment_pkt_num;\n+\trmon_stats-\u003ejabbers\t\t= ps-\u003emac_rx_jabber_pkt_num;\n+\n+\trmon_stats-\u003ehist[0]\t\t= ps-\u003emac_rx_64_oct_pkt_num;\n+\trmon_stats-\u003ehist[1]\t\t= ps-\u003emac_rx_65_127_oct_pkt_num;\n+\trmon_stats-\u003ehist[2]\t\t= ps-\u003emac_rx_128_255_oct_pkt_num;\n+\trmon_stats-\u003ehist[3]\t\t= ps-\u003emac_rx_256_511_oct_pkt_num;\n+\trmon_stats-\u003ehist[4]\t\t= ps-\u003emac_rx_512_1023_oct_pkt_num;\n+\trmon_stats-\u003ehist[5]\t\t= ps-\u003emac_rx_1024_1518_oct_pkt_num;\n+\trmon_stats-\u003ehist[6]\t\t= ps-\u003emac_rx_1519_2047_oct_pkt_num;\n+\trmon_stats-\u003ehist[7]\t\t= ps-\u003emac_rx_2048_4095_oct_pkt_num;\n+\trmon_stats-\u003ehist[8]\t\t= ps-\u003emac_rx_4096_8191_oct_pkt_num;\n+\trmon_stats-\u003ehist[9]\t\t= ps-\u003emac_rx_8192_9216_oct_pkt_num;\n+\trmon_stats-\u003ehist[10]\t\t= ps-\u003emac_rx_9217_12287_oct_pkt_num;\n+\n+\trmon_stats-\u003ehist_tx[0]\t\t= ps-\u003emac_tx_64_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[1]\t\t= ps-\u003emac_tx_65_127_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[2]\t\t= ps-\u003emac_tx_128_255_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[3]\t\t= ps-\u003emac_tx_256_511_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[4]\t\t= ps-\u003emac_tx_512_1023_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[5]\t\t= ps-\u003emac_tx_1024_1518_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[6]\t\t= ps-\u003emac_tx_1519_2047_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[7]\t\t= ps-\u003emac_tx_2048_4095_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[8]\t\t= ps-\u003emac_tx_4096_8191_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[9]\t\t= ps-\u003emac_tx_8192_9216_oct_pkt_num;\n+\trmon_stats-\u003ehist_tx[10]\t\t= ps-\u003emac_tx_9217_12287_oct_pkt_num;\n+\n+\t*ranges = hinic3_rmon_ranges;\n+\n+\tkfree(ps);\n+}\n+\n+static void hinic3_get_pause_stats(struct net_device *netdev,\n+\t\t\t\t struct ethtool_pause_stats *pause_stats)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct mag_cmd_port_stats *ps;\n+\tint err;\n+\n+\tps = kmalloc_obj(*ps);\n+\tif (!ps)\n+\t\treturn;\n+\n+\terr = hinic3_get_phy_port_stats(nic_dev-\u003ehwdev, ps);\n+\tif (err) {\n+\t\tkfree(ps);\n+\t\tnetdev_err(netdev, \"Failed to get eth pause stats from fw\\n\");\n+\t\treturn;\n+\t}\n+\n+\tpause_stats-\u003etx_pause_frames = ps-\u003emac_tx_pause_num;\n+\tpause_stats-\u003erx_pause_frames = ps-\u003emac_rx_pause_num;\n+\n+\tkfree(ps);\n+}\n+\n+static int hinic3_set_queue_coalesce(struct net_device *netdev, u16 q_id,\n+\t\t\t\t struct hinic3_intr_coal_info *coal,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_intr_coal_info *intr_coal;\n+\tstruct hinic3_interrupt_info info = {};\n+\tbool is_active;\n+\tint err;\n+\n+\tmutex_lock(\u0026nic_dev-\u003estate_lock);\n+\tis_active = test_bit(HINIC3_INTF_UP, \u0026nic_dev-\u003eflags) \u0026\u0026\n+\t\t (q_id \u003c nic_dev-\u003eq_params.num_qps);\n+\n+\tif (is_active) {\n+\t\tinfo.msix_index =\n+\t\t\tnic_dev-\u003eq_params.irq_cfg[q_id].msix_entry_idx;\n+\t\tinfo.resend_timer_cfg =\n+\t\t\tnic_dev-\u003eintr_coalesce[q_id].resend_timer_cfg;\n+\t}\n+\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n+\n+\tif (is_active) {\n+\t\tinfo.interrupt_coalesc_set = 1;\n+\t\tinfo.coalesc_timer_cfg = coal-\u003ecoalesce_timer_cfg;\n+\t\tinfo.pending_limit = coal-\u003epending_limit;\n+\n+\t\terr = hinic3_set_interrupt_cfg(nic_dev-\u003ehwdev, info);\n+\t\tif (err) {\n+\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t\t \"Failed to set queue%u coalesce\",\n+\t\t\t\t\t q_id);\n+\t\t\treturn err;\n+\t\t}\n+\t}\n+\n+\tmutex_lock(\u0026nic_dev-\u003estate_lock);\n+\tintr_coal = \u0026nic_dev-\u003eintr_coalesce[q_id];\n+\tintr_coal-\u003ecoalesce_timer_cfg = coal-\u003ecoalesce_timer_cfg;\n+\tintr_coal-\u003epending_limit = coal-\u003epending_limit;\n+\tintr_coal-\u003erx_pending_limit_low = coal-\u003erx_pending_limit_low;\n+\tintr_coal-\u003erx_pending_limit_high = coal-\u003erx_pending_limit_high;\n+\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n+\n+\treturn 0;\n+}\n+\n+static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tconst struct {\n+\t\tconst char *name;\n+\t\tu32 value;\n+\t\tu32 limit;\n+\t} coalesce_limits[] = {\n+\t\t{\"rx_coalesce_usecs\",\n+\t\t coal-\u003erx_coalesce_usecs,\n+\t\t COALESCE_MAX_TIMER_CFG},\n+\t\t{\"rx_max_coalesced_frames\",\n+\t\t coal-\u003erx_max_coalesced_frames,\n+\t\t COALESCE_MAX_PENDING_LIMIT},\n+\t\t{\"rx_max_coalesced_frames_low\",\n+\t\t coal-\u003erx_max_coalesced_frames_low,\n+\t\t COALESCE_MAX_PENDING_LIMIT},\n+\t\t{\"rx_max_coalesced_frames_high\",\n+\t\t coal-\u003erx_max_coalesced_frames_high,\n+\t\t COALESCE_MAX_PENDING_LIMIT},\n+\t};\n+\n+\tfor (int i = 0; i \u003c ARRAY_SIZE(coalesce_limits); i++) {\n+\t\tif (coalesce_limits[i].value \u003e coalesce_limits[i].limit) {\n+\t\t\tNL_SET_ERR_MSG_FMT_MOD(extack, \"%s out of range %d-%d\",\n+\t\t\t\t\t coalesce_limits[i].name, 0,\n+\t\t\t\t\t coalesce_limits[i].limit);\n+\t\t\treturn -ERANGE;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+static int hinic3_validate_coalesce(const struct ethtool_coalesce *coal,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tint err;\n+\n+\terr = is_coalesce_exceed_limit(coal, extack);\n+\tif (err)\n+\t\treturn err;\n+\n+\tif (coal-\u003erx_max_coalesced_frames_low \u003e\n+\t coal-\u003erx_max_coalesced_frames_high) {\n+\t\tNL_SET_ERR_MSG_FMT_MOD(extack,\n+\t\t\t\t \"invalid coalesce frame high %u, low %u\",\n+\t\t\t\t coal-\u003erx_max_coalesced_frames_high,\n+\t\t\t\t coal-\u003erx_max_coalesced_frames_low);\n+\t\treturn -ERANGE;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void check_coalesce_align(struct net_device *netdev,\n+\t\t\t\t u32 item, u32 unit, const char *str)\n+{\n+\tif (item % unit)\n+\t\tnetdev_warn(netdev, \"%s in %d units, change to %u\\n\",\n+\t\t\t str, unit, item - item % unit);\n+}\n+\n+#define CHECK_COALESCE_ALIGN(member, unit) \\\n+\tcheck_coalesce_align(netdev, member, unit, #member)\n+\n+static void check_coalesce_changed(struct net_device *netdev,\n+\t\t\t\t u32 item, u32 unit, u32 ori_val,\n+\t\t\t\t const char *obj_str, const char *str)\n+{\n+\tif ((item / unit) != ori_val)\n+\t\tnetdev_dbg(netdev, \"Change %s from %d to %u %s\\n\",\n+\t\t\t str, ori_val * unit, item - item % unit, obj_str);\n+}\n+\n+#define CHECK_COALESCE_CHANGED(member, unit, ori_val, obj_str) \\\n+\tcheck_coalesce_changed(netdev, member, unit, ori_val, obj_str, #member)\n+\n+static int hinic3_set_hw_coal_param(struct net_device *netdev,\n+\t\t\t\t struct hinic3_intr_coal_info *intr_coal,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tint err;\n+\tu16 i;\n+\n+\tfor (i = 0; i \u003c nic_dev-\u003emax_qps; i++) {\n+\t\terr = hinic3_set_queue_coalesce(netdev, i, intr_coal, extack);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_get_coalesce(struct net_device *netdev,\n+\t\t\t struct ethtool_coalesce *coal,\n+\t\t\t struct kernel_ethtool_coalesce *kernel_coal,\n+\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_intr_coal_info *interrupt_info;\n+\n+\tinterrupt_info = \u0026nic_dev-\u003eintr_coalesce[0];\n+\n+\tcoal-\u003euse_adaptive_rx_coalesce = READ_ONCE(nic_dev-\u003eadaptive_rx_coal);\n+\n+\tmutex_lock(\u0026nic_dev-\u003estate_lock);\n+\n+\tcoal-\u003erx_max_coalesced_frames_low =\n+\t\tinterrupt_info-\u003erx_pending_limit_low *\n+\t\tCOALESCE_PENDING_LIMIT_UNIT;\n+\n+\tcoal-\u003erx_max_coalesced_frames_high =\n+\t\tinterrupt_info-\u003erx_pending_limit_high *\n+\t\tCOALESCE_PENDING_LIMIT_UNIT;\n+\n+\t/* TX/RX uses the same interrupt.\n+\t * So we only declare RX ethtool_coalesce parameters.\n+\t */\n+\tcoal-\u003erx_coalesce_usecs = interrupt_info-\u003ecoalesce_timer_cfg *\n+\t\t\t\t COALESCE_TIMER_CFG_UNIT;\n+\n+\tcoal-\u003erx_max_coalesced_frames = interrupt_info-\u003epending_limit *\n+\t\t\t\t\tCOALESCE_PENDING_LIMIT_UNIT;\n+\n+\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_set_coalesce(struct net_device *netdev,\n+\t\t\t struct ethtool_coalesce *coal,\n+\t\t\t struct kernel_ethtool_coalesce *kernel_coal,\n+\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_intr_coal_info *ori_intr_coal;\n+\tstruct hinic3_intr_coal_info intr_coal = {};\n+\tconst char *obj_str = \"for netdev\";\n+\tbool old_adaptive, new_adaptive;\n+\tstruct hinic3_rxq *rxq = NULL;\n+\tbool dim_stopped = false;\n+\tint err;\n+\tint i;\n+\n+\terr = hinic3_validate_coalesce(coal, extack);\n+\tif (err)\n+\t\treturn err;\n+\n+\tCHECK_COALESCE_ALIGN(coal-\u003erx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT);\n+\tCHECK_COALESCE_ALIGN(coal-\u003erx_max_coalesced_frames,\n+\t\t\t COALESCE_PENDING_LIMIT_UNIT);\n+\tCHECK_COALESCE_ALIGN(coal-\u003erx_max_coalesced_frames_high,\n+\t\t\t COALESCE_PENDING_LIMIT_UNIT);\n+\tCHECK_COALESCE_ALIGN(coal-\u003erx_max_coalesced_frames_low,\n+\t\t\t COALESCE_PENDING_LIMIT_UNIT);\n+\n+\tori_intr_coal = \u0026nic_dev-\u003eintr_coalesce[0];\n+\n+\tCHECK_COALESCE_CHANGED(coal-\u003erx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT,\n+\t\t\t ori_intr_coal-\u003ecoalesce_timer_cfg, obj_str);\n+\tCHECK_COALESCE_CHANGED(coal-\u003erx_max_coalesced_frames,\n+\t\t\t COALESCE_PENDING_LIMIT_UNIT,\n+\t\t\t ori_intr_coal-\u003epending_limit, obj_str);\n+\tCHECK_COALESCE_CHANGED(coal-\u003erx_max_coalesced_frames_high,\n+\t\t\t COALESCE_PENDING_LIMIT_UNIT,\n+\t\t\t ori_intr_coal-\u003erx_pending_limit_high, obj_str);\n+\tCHECK_COALESCE_CHANGED(coal-\u003erx_max_coalesced_frames_low,\n+\t\t\t COALESCE_PENDING_LIMIT_UNIT,\n+\t\t\t ori_intr_coal-\u003erx_pending_limit_low, obj_str);\n+\n+\told_adaptive = READ_ONCE(nic_dev-\u003eadaptive_rx_coal);\n+\tnew_adaptive = !!coal-\u003euse_adaptive_rx_coalesce;\n+\n+\tif (old_adaptive) {\n+\t\tWRITE_ONCE(nic_dev-\u003eadaptive_rx_coal, false);\n+\n+\t\tsynchronize_net();\n+\n+\t\tfor (i = 0; i \u003c nic_dev-\u003eq_params.num_qps; i++) {\n+\t\t\trxq = nic_dev-\u003eq_params.irq_cfg[i].rxq;\n+\t\t\tif (!rxq)\n+\t\t\t\tcontinue;\n+\n+\t\t\tcancel_work_sync(\u0026rxq-\u003edim.work);\n+\n+\t\t\trxq-\u003edim.state = DIM_START_MEASURE;\n+\t\t}\n+\n+\t\tdim_stopped = true;\n+\t}\n+\n+\tintr_coal.coalesce_timer_cfg =\n+\t\t(u8)(coal-\u003erx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);\n+\n+\tintr_coal.pending_limit =\n+\t\t(u8)(coal-\u003erx_max_coalesced_frames /\n+\t\t COALESCE_PENDING_LIMIT_UNIT);\n+\n+\tintr_coal.rx_pending_limit_high =\n+\t\t(u8)(coal-\u003erx_max_coalesced_frames_high /\n+\t\t COALESCE_PENDING_LIMIT_UNIT);\n+\n+\tintr_coal.rx_pending_limit_low =\n+\t\t(u8)(coal-\u003erx_max_coalesced_frames_low /\n+\t\t COALESCE_PENDING_LIMIT_UNIT);\n+\n+\t/* coalesce timer or pending set to zero will disable coalesce */\n+\tif (!new_adaptive \u0026\u0026\n+\t (!intr_coal.coalesce_timer_cfg || !intr_coal.pending_limit))\n+\t\tnetdev_info(netdev, \"Coalesce will be disabled\\n\");\n+\n+\terr = hinic3_set_hw_coal_param(netdev, \u0026intr_coal, extack);\n+\tif (err) {\n+\t\tif (dim_stopped)\n+\t\t\tWRITE_ONCE(nic_dev-\u003eadaptive_rx_coal, old_adaptive);\n+\n+\t\treturn err;\n+\t}\n+\n+\tWRITE_ONCE(nic_dev-\u003eadaptive_rx_coal, new_adaptive);\n+\n+\treturn 0;\n+}\n+\n static const struct ethtool_ops hinic3_ethtool_ops = {\n-\t.supported_coalesce_params = ETHTOOL_COALESCE_USECS |\n-\t\t\t\t\t ETHTOOL_COALESCE_PKT_RATE_RX_USECS,\n+\t.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |\n+\t\t\t\t\t ETHTOOL_COALESCE_RX_MAX_FRAMES |\n+\t\t\t\t\t ETHTOOL_COALESCE_USE_ADAPTIVE_RX |\n+\t\t\t\t\t ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW |\n+\t\t\t\t\t ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH,\n \t.get_link_ksettings = hinic3_get_link_ksettings,\n \t.get_drvinfo = hinic3_get_drvinfo,\n \t.get_msglevel = hinic3_get_msglevel,\n \t.set_msglevel = hinic3_set_msglevel,\n \t.get_link = ethtool_op_get_link,\n+\t.get_ringparam = hinic3_get_ringparam,\n+\t.set_ringparam = hinic3_set_ringparam,\n+\t.get_sset_count = hinic3_get_sset_count,\n+\t.get_ethtool_stats = hinic3_get_ethtool_stats,\n+\t.get_strings = hinic3_get_strings,\n+\t.get_eth_phy_stats = hinic3_get_eth_phy_stats,\n+\t.get_eth_mac_stats = hinic3_get_eth_mac_stats,\n+\t.get_eth_ctrl_stats = hinic3_get_eth_ctrl_stats,\n+\t.get_rmon_stats = hinic3_get_rmon_stats,\n+\t.get_pause_stats = hinic3_get_pause_stats,\n+\t.get_coalesce = hinic3_get_coalesce,\n+\t.set_coalesce = hinic3_set_coalesce,\n+\t.get_rxnfc = hinic3_get_rxnfc,\n+\t.set_rxnfc = hinic3_set_rxnfc,\n+\t.get_rx_ring_count = hinic3_get_rx_ring_count,\n+\t.get_channels = hinic3_get_channels,\n+\t.set_channels = hinic3_set_channels,\n+\t.get_rxfh_indir_size = hinic3_get_rxfh_indir_size,\n+\t.get_rxfh_key_size = hinic3_get_rxfh_key_size,\n+\t.get_rxfh = hinic3_get_rxfh,\n+\t.set_rxfh = hinic3_set_rxfh,\n };\n \n void hinic3_set_ethtool_ops(struct net_device *netdev)\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h\nindex cfc9daa3034feb..6b0f486ba590be 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h\n@@ -53,6 +53,17 @@ static inline void mgmt_msg_params_init_default(struct mgmt_msg_params *msg_para\n \tmsg_params-\u003etimeout_ms = 0;\n }\n \n+static inline void\n+mgmt_msg_params_init_in_out(struct mgmt_msg_params *msg_params, void *in_buf,\n+\t\t\t void *out_buf, u32 in_buf_size, u32 out_buf_size)\n+{\n+\tmsg_params-\u003ebuf_in = in_buf;\n+\tmsg_params-\u003ebuf_out = out_buf;\n+\tmsg_params-\u003ein_size = in_buf_size;\n+\tmsg_params-\u003eexpected_out_size = out_buf_size;\n+\tmsg_params-\u003etimeout_ms = 0;\n+}\n+\n enum cfg_cmd {\n \tCFG_CMD_GET_DEV_CAP = 0,\n };\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c\nindex e7d6c2033b451e..a5013dd8ade246 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c\n@@ -20,7 +20,7 @@ static void hinic3_net_dim(struct hinic3_nic_dev *nic_dev,\n \tstruct dim_sample sample = {};\n \n \tif (!test_bit(HINIC3_INTF_UP, \u0026nic_dev-\u003eflags) ||\n-\t !nic_dev-\u003eadaptive_rx_coal)\n+\t !READ_ONCE(nic_dev-\u003eadaptive_rx_coal))\n \t\treturn;\n \n \tdim_update_sample(irq_cfg-\u003etotal_events, rxq-\u003erxq_stats.packets,\n@@ -105,6 +105,7 @@ static int hinic3_request_irq(struct hinic3_irq_cfg *irq_cfg, u16 q_id)\n \tinfo.coalesc_timer_cfg =\n \t\tnic_dev-\u003eintr_coalesce[q_id].coalesce_timer_cfg;\n \tinfo.resend_timer_cfg = nic_dev-\u003eintr_coalesce[q_id].resend_timer_cfg;\n+\n \terr = hinic3_set_interrupt_cfg(nic_dev-\u003ehwdev, info);\n \tif (err) {\n \t\tnetdev_err(netdev, \"Failed to set RX interrupt coalescing attribute.\\n\");\n@@ -148,13 +149,9 @@ static int hinic3_set_interrupt_moder(struct net_device *netdev, u16 q_id,\n \t\tnic_dev-\u003eintr_coalesce[q_id].resend_timer_cfg;\n \n \terr = hinic3_set_interrupt_cfg(nic_dev-\u003ehwdev, info);\n-\tif (err) {\n+\tif (err)\n \t\tnetdev_err(netdev,\n \t\t\t \"Failed to modify moderation for Queue: %u\\n\", q_id);\n-\t} else {\n-\t\tnic_dev-\u003erxqs[q_id].last_coalesc_timer_cfg = coalesc_timer_cfg;\n-\t\tnic_dev-\u003erxqs[q_id].last_pending_limit = pending_limit;\n-\t}\n \n \treturn err;\n }\n@@ -162,17 +159,20 @@ static int hinic3_set_interrupt_moder(struct net_device *netdev, u16 q_id,\n static void hinic3_update_queue_coal(struct net_device *netdev, u16 q_id,\n \t\t\t\t u16 coal_timer, u16 coal_pkts)\n {\n+\tu8 coalesc_timer_cfg, pending_limit, limit_low, limit_high;\n \tstruct hinic3_intr_coal_info *q_coal;\n-\tu8 coalesc_timer_cfg, pending_limit;\n \tstruct hinic3_nic_dev *nic_dev;\n \n \tnic_dev = netdev_priv(netdev);\n \n \tq_coal = \u0026nic_dev-\u003eintr_coalesce[q_id];\n \tcoalesc_timer_cfg = (u8)coal_timer;\n+\n+\tlimit_low = q_coal-\u003erx_pending_limit_low;\n+\tlimit_high = q_coal-\u003erx_pending_limit_high;\n+\n \tpending_limit = clamp_t(u8, coal_pkts \u003e\u003e HINIC3_COAL_PKT_SHIFT,\n-\t\t\t\tq_coal-\u003erx_pending_limit_low,\n-\t\t\t\tq_coal-\u003erx_pending_limit_high);\n+\t\t\t\tlimit_low, limit_high);\n \n \thinic3_set_interrupt_moder(nic_dev-\u003enetdev, q_id,\n \t\t\t\t coalesc_timer_cfg, pending_limit);\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c\nindex 0a888fe4c975b2..a0618e6dc21971 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c\n@@ -51,7 +51,7 @@ static void init_intr_coal_param(struct net_device *netdev)\n \t\tinfo-\u003erx_pending_limit_low = HINIC3_RX_PENDING_LIMIT_LOW;\n \t}\n \n-\tnic_dev-\u003eadaptive_rx_coal = 1;\n+\tWRITE_ONCE(nic_dev-\u003eadaptive_rx_coal, 1);\n }\n \n static int hinic3_init_intr_coalesce(struct net_device *netdev)\n@@ -178,7 +178,7 @@ static int hinic3_sw_init(struct net_device *netdev)\n \tu8 mac_addr[ETH_ALEN];\n \tint err;\n \n-\tmutex_init(\u0026nic_dev-\u003eport_state_mutex);\n+\tmutex_init(\u0026nic_dev-\u003estate_lock);\n \n \tnic_dev-\u003eq_params.sq_depth = HINIC3_SQ_DEPTH;\n \tnic_dev-\u003eq_params.rq_depth = HINIC3_RQ_DEPTH;\n@@ -330,6 +330,8 @@ static void hinic3_link_status_change(struct net_device *netdev,\n \t\tnetif_carrier_off(netdev);\n \t\tnetdev_dbg(netdev, \"Link is down\\n\");\n \t}\n+\n+\treturn;\n }\n \n static void hinic3_port_module_event_handler(struct net_device *netdev,\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h\nindex c5bca3c4af961e..3c1263ff99ffbf 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h\n@@ -143,6 +143,41 @@ struct l2nic_cmd_set_dcb_state {\n \tu8 rsvd[7];\n };\n \n+struct l2nic_port_stats_info {\n+\tstruct mgmt_msg_head msg_head;\n+\tu16 func_id;\n+\tu16 rsvd1;\n+};\n+\n+struct l2nic_vport_stats {\n+\tu64 tx_unicast_pkts_vport;\n+\tu64 tx_unicast_bytes_vport;\n+\tu64 tx_multicast_pkts_vport;\n+\tu64 tx_multicast_bytes_vport;\n+\tu64 tx_broadcast_pkts_vport;\n+\tu64 tx_broadcast_bytes_vport;\n+\n+\tu64 rx_unicast_pkts_vport;\n+\tu64 rx_unicast_bytes_vport;\n+\tu64 rx_multicast_pkts_vport;\n+\tu64 rx_multicast_bytes_vport;\n+\tu64 rx_broadcast_pkts_vport;\n+\tu64 rx_broadcast_bytes_vport;\n+\n+\tu64 tx_discard_vport;\n+\tu64 rx_discard_vport;\n+\tu64 tx_err_vport;\n+\tu64 rx_err_vport;\n+};\n+\n+struct l2nic_cmd_vport_stats {\n+\tstruct mgmt_msg_head msg_head;\n+\tu32 stats_size;\n+\tu32 rsvd1;\n+\tstruct l2nic_vport_stats stats;\n+\tu64 rsvd2[6];\n+};\n+\n struct l2nic_cmd_lro_config {\n \tstruct mgmt_msg_head msg_head;\n \tu16 func_id;\n@@ -234,6 +269,7 @@ enum l2nic_cmd {\n \tL2NIC_CMD_SET_VPORT_ENABLE = 6,\n \tL2NIC_CMD_SET_RX_MODE = 7,\n \tL2NIC_CMD_SET_SQ_CI_ATTR = 8,\n+\tL2NIC_CMD_GET_VPORT_STAT = 9,\n \tL2NIC_CMD_CLEAR_QP_RESOURCE = 11,\n \tL2NIC_CMD_CFG_RX_LRO = 13,\n \tL2NIC_CMD_CFG_LRO_TIMER = 14,\n@@ -246,6 +282,7 @@ enum l2nic_cmd {\n \tL2NIC_CMD_SET_VLAN_FILTER_EN = 26,\n \tL2NIC_CMD_SET_RX_VLAN_OFFLOAD = 27,\n \tL2NIC_CMD_CFG_RSS = 60,\n+\tL2NIC_CMD_GET_RSS_CTX_TBL = 62,\n \tL2NIC_CMD_CFG_RSS_HASH_KEY = 63,\n \tL2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,\n \tL2NIC_CMD_SET_RSS_CTX_TBL = 65,\n@@ -265,6 +302,7 @@ enum l2nic_ucode_cmd {\n \tL2NIC_UCODE_CMD_MODIFY_QUEUE_CTX = 0,\n \tL2NIC_UCODE_CMD_CLEAN_QUEUE_CTX = 1,\n \tL2NIC_UCODE_CMD_SET_RSS_INDIR_TBL = 4,\n+\tL2NIC_UCODE_CMD_GET_RSS_INDIR_TBL = 6,\n };\n \n /* hilink mac group command */\n@@ -272,6 +310,7 @@ enum mag_cmd {\n \tMAG_CMD_SET_PORT_ENABLE = 6,\n \tMAG_CMD_GET_LINK_STATUS = 7,\n \n+\tMAG_CMD_GET_PORT_STAT = 151,\n \tMAG_CMD_GET_PORT_INFO = 153,\n };\n \ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c\nindex da73811641a9de..7f304d7ac47ba3 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c\n@@ -288,7 +288,8 @@ static void hinic3_free_channel_resources(struct net_device *netdev,\n \thinic3_free_qps(nic_dev, qp_params);\n }\n \n-static int hinic3_open_channel(struct net_device *netdev)\n+static int hinic3_prepare_channel(struct net_device *netdev,\n+\t\t\t\t struct hinic3_dyna_txrxq_params *qp_params)\n {\n \tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n \tint err;\n@@ -299,12 +300,25 @@ static int hinic3_open_channel(struct net_device *netdev)\n \t\treturn err;\n \t}\n \n-\terr = hinic3_configure_txrxqs(netdev, \u0026nic_dev-\u003eq_params);\n+\terr = hinic3_configure_txrxqs(netdev, qp_params);\n \tif (err) {\n \t\tnetdev_err(netdev, \"Failed to configure txrxqs\\n\");\n \t\tgoto err_free_qp_ctxts;\n \t}\n \n+\treturn 0;\n+\n+err_free_qp_ctxts:\n+\thinic3_free_qp_ctxts(nic_dev);\n+\n+\treturn err;\n+}\n+\n+static int hinic3_open_channel(struct net_device *netdev)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tint err;\n+\n \terr = hinic3_qps_irq_init(netdev);\n \tif (err) {\n \t\tnetdev_err(netdev, \"Failed to init txrxq irq\\n\");\n@@ -321,6 +335,7 @@ static int hinic3_open_channel(struct net_device *netdev)\n \n err_uninit_qps_irq:\n \thinic3_qps_irq_uninit(netdev);\n+\n err_free_qp_ctxts:\n \thinic3_free_qp_ctxts(nic_dev);\n \n@@ -341,9 +356,7 @@ static int hinic3_maybe_set_port_state(struct net_device *netdev, bool enable)\n \tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n \tint err;\n \n-\tmutex_lock(\u0026nic_dev-\u003eport_state_mutex);\n \terr = hinic3_set_port_enable(nic_dev-\u003ehwdev, enable);\n-\tmutex_unlock(\u0026nic_dev-\u003eport_state_mutex);\n \n \treturn err;\n }\n@@ -428,6 +441,76 @@ static void hinic3_vport_down(struct net_device *netdev)\n \t}\n }\n \n+int\n+hinic3_change_channel_settings(struct net_device *netdev,\n+\t\t\t struct hinic3_dyna_txrxq_params *trxq_params)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_dyna_txrxq_params cur_trxq_params = {};\n+\tstruct hinic3_dyna_qp_params new_qp_params = {};\n+\tstruct hinic3_dyna_qp_params cur_qp_params = {};\n+\tint err;\n+\n+\tmutex_lock(\u0026nic_dev-\u003estate_lock);\n+\tcur_trxq_params = nic_dev-\u003eq_params;\n+\n+\thinic3_config_num_qps(netdev, trxq_params);\n+\n+\terr = hinic3_alloc_channel_resources(netdev, \u0026new_qp_params,\n+\t\t\t\t\t trxq_params);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to alloc channel resources\\n\");\n+\t\thinic3_config_num_qps(netdev, \u0026cur_trxq_params);\n+\t\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n+\t\treturn err;\n+\t}\n+\n+\thinic3_vport_down(netdev);\n+\thinic3_close_channel(netdev);\n+\thinic3_get_cur_qps(nic_dev, \u0026cur_qp_params);\n+\n+\thinic3_init_qps(nic_dev, \u0026new_qp_params);\n+\n+\terr = hinic3_prepare_channel(netdev, trxq_params);\n+\tif (err)\n+\t\tgoto err_uninit_qps;\n+\n+\tif (nic_dev-\u003enum_qp_irq \u003e trxq_params-\u003enum_qps)\n+\t\thinic3_qp_irq_change(netdev, trxq_params-\u003enum_qps);\n+\n+\tnic_dev-\u003eq_params = *trxq_params;\n+\n+\terr = hinic3_open_channel(netdev);\n+\tif (err)\n+\t\tgoto err_qp_irq_reset;\n+\n+\terr = hinic3_vport_up(netdev);\n+\tif (err)\n+\t\tgoto err_close_channel;\n+\n+\thinic3_free_channel_resources(netdev, \u0026cur_qp_params, \u0026cur_trxq_params);\n+\n+\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n+\n+\treturn 0;\n+\n+err_close_channel:\n+\thinic3_close_channel(netdev);\n+err_qp_irq_reset:\n+\tnic_dev-\u003eq_params = cur_trxq_params;\n+\n+\tif (trxq_params-\u003enum_qps \u003e cur_trxq_params.num_qps)\n+\t\thinic3_qp_irq_change(netdev, cur_trxq_params.num_qps);\n+err_uninit_qps:\n+\thinic3_get_cur_qps(nic_dev, \u0026new_qp_params);\n+\thinic3_config_num_qps(netdev, \u0026cur_trxq_params);\n+\thinic3_init_qps(nic_dev, \u0026cur_qp_params);\n+\thinic3_free_channel_resources(netdev, \u0026new_qp_params, trxq_params);\n+\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n+\n+\treturn err;\n+}\n+\n static int hinic3_open(struct net_device *netdev)\n {\n \tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n@@ -458,6 +541,10 @@ static int hinic3_open(struct net_device *netdev)\n \n \thinic3_init_qps(nic_dev, \u0026qp_params);\n \n+\terr = hinic3_prepare_channel(netdev, \u0026nic_dev-\u003eq_params);\n+\tif (err)\n+\t\tgoto err_uninit_qps;\n+\n \terr = hinic3_open_channel(netdev);\n \tif (err)\n \t\tgoto err_uninit_qps;\n@@ -473,7 +560,7 @@ static int hinic3_open(struct net_device *netdev)\n err_close_channel:\n \thinic3_close_channel(netdev);\n err_uninit_qps:\n-\thinic3_uninit_qps(nic_dev, \u0026qp_params);\n+\thinic3_get_cur_qps(nic_dev, \u0026qp_params);\n \thinic3_free_channel_resources(netdev, \u0026qp_params, \u0026nic_dev-\u003eq_params);\n err_destroy_num_qps:\n \thinic3_destroy_num_qps(netdev);\n@@ -493,10 +580,15 @@ static int hinic3_close(struct net_device *netdev)\n \t\treturn 0;\n \t}\n \n+\tmutex_lock(\u0026nic_dev-\u003estate_lock);\n \thinic3_vport_down(netdev);\n \thinic3_close_channel(netdev);\n-\thinic3_uninit_qps(nic_dev, \u0026qp_params);\n-\thinic3_free_channel_resources(netdev, \u0026qp_params, \u0026nic_dev-\u003eq_params);\n+\thinic3_get_cur_qps(nic_dev, \u0026qp_params);\n+\thinic3_free_channel_resources(netdev, \u0026qp_params,\n+\t\t\t\t \u0026nic_dev-\u003eq_params);\n+\thinic3_free_nicio_res(nic_dev);\n+\thinic3_destroy_num_qps(netdev);\n+\tmutex_unlock(\u0026nic_dev-\u003estate_lock);\n \n \treturn 0;\n }\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c\nindex de5a7984d2cb95..1b14dc824ce1e9 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c\n@@ -639,6 +639,42 @@ int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up)\n \treturn 0;\n }\n \n+int hinic3_get_phy_port_stats(struct hinic3_hwdev *hwdev,\n+\t\t\t struct mag_cmd_port_stats *stats)\n+{\n+\tstruct mag_cmd_port_stats_info stats_info = {};\n+\tstruct mag_cmd_get_port_stat *ps;\n+\tstruct mgmt_msg_params msg_params = {};\n+\tint err;\n+\n+\tps = kzalloc_obj(*ps);\n+\tif (!ps)\n+\t\treturn -ENOMEM;\n+\n+\tstats_info.port_id = hinic3_physical_port_id(hwdev);\n+\n+\tmgmt_msg_params_init_in_out(\u0026msg_params, \u0026stats_info, ps,\n+\t\t\t\t sizeof(stats_info), sizeof(*ps));\n+\n+\terr = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_HILINK,\n+\t\t\t\t MAG_CMD_GET_PORT_STAT, \u0026msg_params);\n+\n+\tif (err || ps-\u003ehead.status) {\n+\t\tdev_err(hwdev-\u003edev,\n+\t\t\t\"Failed to get port statistics, err: %d, status: 0x%x\\n\",\n+\t\t\terr, ps-\u003ehead.status);\n+\t\terr = -EFAULT;\n+\t\tgoto out;\n+\t}\n+\n+\tmemcpy(stats, \u0026ps-\u003ecounter, sizeof(*stats));\n+\n+out:\n+\tkfree(ps);\n+\n+\treturn err;\n+}\n+\n int hinic3_get_port_info(struct hinic3_hwdev *hwdev,\n \t\t\t struct hinic3_nic_port_info *port_info)\n {\n@@ -738,3 +774,31 @@ int hinic3_get_pause_info(struct hinic3_nic_dev *nic_dev,\n \treturn hinic3_cfg_hw_pause(nic_dev-\u003ehwdev, MGMT_MSG_CMD_OP_GET,\n \t\t\t\t nic_pause);\n }\n+\n+int hinic3_get_vport_stats(struct hinic3_hwdev *hwdev, u16 func_id,\n+\t\t\t struct l2nic_vport_stats *stats)\n+{\n+\tstruct l2nic_cmd_vport_stats vport_stats = {};\n+\tstruct l2nic_port_stats_info stats_info = {};\n+\tstruct mgmt_msg_params msg_params = {};\n+\tint err;\n+\n+\tstats_info.func_id = func_id;\n+\n+\tmgmt_msg_params_init_in_out(\u0026msg_params, \u0026stats_info, \u0026vport_stats,\n+\t\t\t\t sizeof(stats_info), sizeof(vport_stats));\n+\n+\terr = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,\n+\t\t\t\t L2NIC_CMD_GET_VPORT_STAT, \u0026msg_params);\n+\n+\tif (err || vport_stats.msg_head.status) {\n+\t\tdev_err(hwdev-\u003edev,\n+\t\t\t\"Failed to get function statistics, err: %d, status: 0x%x\\n\",\n+\t\t\terr, vport_stats.msg_head.status);\n+\t\treturn -EFAULT;\n+\t}\n+\n+\tmemcpy(stats, \u0026vport_stats.stats, sizeof(*stats));\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h\nindex 5d52202a8d4eb1..80573c12153973 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h\n@@ -129,6 +129,110 @@ struct mag_cmd_get_xsfp_present {\n \tu8 rsvd[2];\n };\n \n+struct mag_cmd_port_stats {\n+\tu64 mac_tx_fragment_pkt_num;\n+\tu64 mac_tx_undersize_pkt_num;\n+\tu64 mac_tx_undermin_pkt_num;\n+\tu64 mac_tx_64_oct_pkt_num;\n+\tu64 mac_tx_65_127_oct_pkt_num;\n+\tu64 mac_tx_128_255_oct_pkt_num;\n+\tu64 mac_tx_256_511_oct_pkt_num;\n+\tu64 mac_tx_512_1023_oct_pkt_num;\n+\tu64 mac_tx_1024_1518_oct_pkt_num;\n+\tu64 mac_tx_1519_2047_oct_pkt_num;\n+\tu64 mac_tx_2048_4095_oct_pkt_num;\n+\tu64 mac_tx_4096_8191_oct_pkt_num;\n+\tu64 mac_tx_8192_9216_oct_pkt_num;\n+\tu64 mac_tx_9217_12287_oct_pkt_num;\n+\tu64 mac_tx_12288_16383_oct_pkt_num;\n+\tu64 mac_tx_1519_max_bad_pkt_num;\n+\tu64 mac_tx_1519_max_good_pkt_num;\n+\tu64 mac_tx_oversize_pkt_num;\n+\tu64 mac_tx_jabber_pkt_num;\n+\tu64 mac_tx_bad_pkt_num;\n+\tu64 mac_tx_bad_oct_num;\n+\tu64 mac_tx_good_pkt_num;\n+\tu64 mac_tx_good_oct_num;\n+\tu64 mac_tx_total_pkt_num;\n+\tu64 mac_tx_total_oct_num;\n+\tu64 mac_tx_uni_pkt_num;\n+\tu64 mac_tx_multi_pkt_num;\n+\tu64 mac_tx_broad_pkt_num;\n+\tu64 mac_tx_pause_num;\n+\tu64 mac_tx_pfc_pkt_num;\n+\tu64 mac_tx_pfc_pri0_pkt_num;\n+\tu64 mac_tx_pfc_pri1_pkt_num;\n+\tu64 mac_tx_pfc_pri2_pkt_num;\n+\tu64 mac_tx_pfc_pri3_pkt_num;\n+\tu64 mac_tx_pfc_pri4_pkt_num;\n+\tu64 mac_tx_pfc_pri5_pkt_num;\n+\tu64 mac_tx_pfc_pri6_pkt_num;\n+\tu64 mac_tx_pfc_pri7_pkt_num;\n+\tu64 mac_tx_control_pkt_num;\n+\tu64 mac_tx_err_all_pkt_num;\n+\tu64 mac_tx_from_app_good_pkt_num;\n+\tu64 mac_tx_from_app_bad_pkt_num;\n+\n+\tu64 mac_rx_fragment_pkt_num;\n+\tu64 mac_rx_undersize_pkt_num;\n+\tu64 mac_rx_undermin_pkt_num;\n+\tu64 mac_rx_64_oct_pkt_num;\n+\tu64 mac_rx_65_127_oct_pkt_num;\n+\tu64 mac_rx_128_255_oct_pkt_num;\n+\tu64 mac_rx_256_511_oct_pkt_num;\n+\tu64 mac_rx_512_1023_oct_pkt_num;\n+\tu64 mac_rx_1024_1518_oct_pkt_num;\n+\tu64 mac_rx_1519_2047_oct_pkt_num;\n+\tu64 mac_rx_2048_4095_oct_pkt_num;\n+\tu64 mac_rx_4096_8191_oct_pkt_num;\n+\tu64 mac_rx_8192_9216_oct_pkt_num;\n+\tu64 mac_rx_9217_12287_oct_pkt_num;\n+\tu64 mac_rx_12288_16383_oct_pkt_num;\n+\tu64 mac_rx_1519_max_bad_pkt_num;\n+\tu64 mac_rx_1519_max_good_pkt_num;\n+\tu64 mac_rx_oversize_pkt_num;\n+\tu64 mac_rx_jabber_pkt_num;\n+\tu64 mac_rx_bad_pkt_num;\n+\tu64 mac_rx_bad_oct_num;\n+\tu64 mac_rx_good_pkt_num;\n+\tu64 mac_rx_good_oct_num;\n+\tu64 mac_rx_total_pkt_num;\n+\tu64 mac_rx_total_oct_num;\n+\tu64 mac_rx_uni_pkt_num;\n+\tu64 mac_rx_multi_pkt_num;\n+\tu64 mac_rx_broad_pkt_num;\n+\tu64 mac_rx_pause_num;\n+\tu64 mac_rx_pfc_pkt_num;\n+\tu64 mac_rx_pfc_pri0_pkt_num;\n+\tu64 mac_rx_pfc_pri1_pkt_num;\n+\tu64 mac_rx_pfc_pri2_pkt_num;\n+\tu64 mac_rx_pfc_pri3_pkt_num;\n+\tu64 mac_rx_pfc_pri4_pkt_num;\n+\tu64 mac_rx_pfc_pri5_pkt_num;\n+\tu64 mac_rx_pfc_pri6_pkt_num;\n+\tu64 mac_rx_pfc_pri7_pkt_num;\n+\tu64 mac_rx_control_pkt_num;\n+\tu64 mac_rx_sym_err_pkt_num;\n+\tu64 mac_rx_fcs_err_pkt_num;\n+\tu64 mac_rx_send_app_good_pkt_num;\n+\tu64 mac_rx_send_app_bad_pkt_num;\n+\tu64 mac_rx_unfilter_pkt_num;\n+};\n+\n+struct mag_cmd_port_stats_info {\n+\tstruct mgmt_msg_head head;\n+\n+\tu8 port_id;\n+\tu8 rsvd0[3];\n+};\n+\n+struct mag_cmd_get_port_stat {\n+\tstruct mgmt_msg_head head;\n+\n+\tstruct mag_cmd_port_stats counter;\n+\tu64 rsvd1[15];\n+};\n+\n enum link_err_type {\n \tLINK_ERR_MODULE_UNRECOGENIZED,\n \tLINK_ERR_NUM,\n@@ -209,6 +313,11 @@ int hinic3_get_port_info(struct hinic3_hwdev *hwdev,\n \t\t\t struct hinic3_nic_port_info *port_info);\n int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,\n \t\t\t bool enable);\n+int hinic3_get_phy_port_stats(struct hinic3_hwdev *hwdev,\n+\t\t\t struct mag_cmd_port_stats *stats);\n+int hinic3_get_vport_stats(struct hinic3_hwdev *hwdev, u16 func_id,\n+\t\t\t struct l2nic_vport_stats *stats);\n+\n int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);\n int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);\n \ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h\nindex 9502293ff710c6..ccef82d57fddc2 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h\n@@ -10,6 +10,9 @@\n #include \"hinic3_hw_cfg.h\"\n #include \"hinic3_hwdev.h\"\n #include \"hinic3_mgmt_interface.h\"\n+#include \"hinic3_nic_io.h\"\n+#include \"hinic3_tx.h\"\n+#include \"hinic3_rx.h\"\n \n #define HINIC3_VLAN_BITMAP_BYTE_SIZE(nic_dev) (sizeof(*(nic_dev)-\u003evlan_bitmap))\n #define HINIC3_VLAN_BITMAP_SIZE(nic_dev) \\\n@@ -127,8 +130,8 @@ struct hinic3_nic_dev {\n \tstruct workqueue_struct *workq;\n \tstruct delayed_work periodic_work;\n \tstruct work_struct rx_mode_work;\n-\t/* lock for enable/disable port */\n-\tstruct mutex port_state_mutex;\n+\t/* Protects Interface state */\n+\tstruct mutex state_lock;\n \n \tstruct list_head uc_filter_list;\n \tstruct list_head mc_filter_list;\n@@ -143,6 +146,10 @@ struct hinic3_nic_dev {\n \n void hinic3_set_netdev_ops(struct net_device *netdev);\n int hinic3_set_hw_features(struct net_device *netdev);\n+int\n+hinic3_change_channel_settings(struct net_device *netdev,\n+\t\t\t struct hinic3_dyna_txrxq_params *trxq_params);\n+\n int hinic3_qps_irq_init(struct net_device *netdev);\n void hinic3_qps_irq_uninit(struct net_device *netdev);\n \ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c\nindex 87e736adba025c..0e7a0ccfba982c 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c\n@@ -484,8 +484,8 @@ void hinic3_init_qps(struct hinic3_nic_dev *nic_dev,\n \t}\n }\n \n-void hinic3_uninit_qps(struct hinic3_nic_dev *nic_dev,\n-\t\t struct hinic3_dyna_qp_params *qp_params)\n+void hinic3_get_cur_qps(struct hinic3_nic_dev *nic_dev,\n+\t\t\tstruct hinic3_dyna_qp_params *qp_params)\n {\n \tstruct hinic3_nic_io *nic_io = nic_dev-\u003enic_io;\n \ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h\nindex 12eefabcf1dbbc..571b34d6395059 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h\n@@ -14,6 +14,10 @@ struct hinic3_nic_dev;\n #define HINIC3_RQ_WQEBB_SHIFT 3\n #define HINIC3_SQ_WQEBB_SIZE BIT(HINIC3_SQ_WQEBB_SHIFT)\n \n+#define HINIC3_MAX_TX_QUEUE_DEPTH 65536\n+#define HINIC3_MAX_RX_QUEUE_DEPTH 16384\n+#define HINIC3_MIN_QUEUE_DEPTH 128\n+\n /* ******************** RQ_CTRL ******************** */\n enum hinic3_rq_wqe_type {\n \tHINIC3_NORMAL_RQ_WQE = 1,\n@@ -136,8 +140,8 @@ void hinic3_free_qps(struct hinic3_nic_dev *nic_dev,\n \t\t struct hinic3_dyna_qp_params *qp_params);\n void hinic3_init_qps(struct hinic3_nic_dev *nic_dev,\n \t\t struct hinic3_dyna_qp_params *qp_params);\n-void hinic3_uninit_qps(struct hinic3_nic_dev *nic_dev,\n-\t\t struct hinic3_dyna_qp_params *qp_params);\n+void hinic3_get_cur_qps(struct hinic3_nic_dev *nic_dev,\n+\t\t\tstruct hinic3_dyna_qp_params *qp_params);\n \n int hinic3_init_qp_ctxts(struct hinic3_nic_dev *nic_dev);\n void hinic3_free_qp_ctxts(struct hinic3_nic_dev *nic_dev);\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c\nindex 25db74d8c7ddb7..62cf5f59a0cf52 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c\n@@ -155,7 +155,7 @@ static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,\n \t\t\t\t L2NIC_CMD_SET_RSS_CTX_TBL, \u0026msg_params);\n \n \tif (ctx_tbl.msg_head.status == MGMT_STATUS_CMD_UNSUPPORTED) {\n-\t\treturn MGMT_STATUS_CMD_UNSUPPORTED;\n+\t\treturn -EOPNOTSUPP;\n \t} else if (err || ctx_tbl.msg_head.status) {\n \t\tdev_err(hwdev-\u003edev, \"mgmt Failed to set rss context offload, err: %d, status: 0x%x\\n\",\n \t\t\terr, ctx_tbl.msg_head.status);\n@@ -165,6 +165,41 @@ static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,\n \treturn 0;\n }\n \n+static int hinic3_get_rss_type(struct hinic3_hwdev *hwdev,\n+\t\t\t struct hinic3_rss_type *rss_type)\n+{\n+\tstruct l2nic_cmd_rss_ctx_tbl ctx_tbl = {};\n+\tstruct mgmt_msg_params msg_params = {};\n+\tint err;\n+\n+\tctx_tbl.func_id = hinic3_global_func_id(hwdev);\n+\n+\tmgmt_msg_params_init_default(\u0026msg_params, \u0026ctx_tbl, sizeof(ctx_tbl));\n+\n+\terr = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,\n+\t\t\t\t L2NIC_CMD_GET_RSS_CTX_TBL,\n+\t\t\t\t \u0026msg_params);\n+\tif (ctx_tbl.msg_head.status == MGMT_STATUS_CMD_UNSUPPORTED) {\n+\t\treturn -EOPNOTSUPP;\n+\t} else if (err || ctx_tbl.msg_head.status) {\n+\t\tdev_err(hwdev-\u003edev, \"Failed to get hash type, err: %d, status: 0x%x\\n\",\n+\t\t\terr, ctx_tbl.msg_head.status);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\trss_type-\u003eipv4 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, IPV4);\n+\trss_type-\u003eipv6 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, IPV6);\n+\trss_type-\u003eipv6_ext = L2NIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT);\n+\trss_type-\u003etcp_ipv4 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4);\n+\trss_type-\u003etcp_ipv6 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6);\n+\trss_type-\u003etcp_ipv6_ext = L2NIC_RSS_TYPE_GET(ctx_tbl.context,\n+\t\t\t\t\t\t TCP_IPV6_EXT);\n+\trss_type-\u003eudp_ipv4 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4);\n+\trss_type-\u003eudp_ipv6 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6);\n+\n+\treturn 0;\n+}\n+\n static int hinic3_rss_cfg_hash_type(struct hinic3_hwdev *hwdev, u8 opcode,\n \t\t\t\t enum hinic3_rss_hash_type *type)\n {\n@@ -264,7 +299,8 @@ static int hinic3_set_hw_rss_parameters(struct net_device *netdev, u8 rss_en)\n \tif (err)\n \t\treturn err;\n \n-\thinic3_fillout_indir_tbl(netdev, nic_dev-\u003erss_indir);\n+\tif (!netif_is_rxfh_configured(netdev))\n+\t\thinic3_fillout_indir_tbl(netdev, nic_dev-\u003erss_indir);\n \n \terr = hinic3_config_rss_hw_resource(netdev, nic_dev-\u003erss_indir);\n \tif (err)\n@@ -334,3 +370,524 @@ void hinic3_try_to_enable_rss(struct net_device *netdev)\n \tclear_bit(HINIC3_RSS_ENABLE, \u0026nic_dev-\u003eflags);\n \tnic_dev-\u003eq_params.num_qps = nic_dev-\u003emax_qps;\n }\n+\n+static int hinic3_set_l4_rss_hash_ops(const struct ethtool_rxnfc *cmd,\n+\t\t\t\t struct hinic3_rss_type *rss_type)\n+{\n+\tu8 rss_l4_en;\n+\n+\tswitch (cmd-\u003edata \u0026 (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {\n+\tcase 0:\n+\t\trss_l4_en = 0;\n+\t\tbreak;\n+\tcase (RXH_L4_B_0_1 | RXH_L4_B_2_3):\n+\t\trss_l4_en = 1;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tswitch (cmd-\u003eflow_type) {\n+\tcase TCP_V4_FLOW:\n+\t\trss_type-\u003etcp_ipv4 = rss_l4_en;\n+\t\tbreak;\n+\tcase TCP_V6_FLOW:\n+\t\trss_type-\u003etcp_ipv6 = rss_l4_en;\n+\t\tbreak;\n+\tcase UDP_V4_FLOW:\n+\t\trss_type-\u003eudp_ipv4 = rss_l4_en;\n+\t\tbreak;\n+\tcase UDP_V6_FLOW:\n+\t\trss_type-\u003eudp_ipv6 = rss_l4_en;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_update_rss_hash_opts(struct net_device *netdev,\n+\t\t\t\t struct ethtool_rxnfc *cmd,\n+\t\t\t\t struct hinic3_rss_type *rss_type)\n+{\n+\tint err;\n+\n+\tswitch (cmd-\u003eflow_type) {\n+\tcase TCP_V4_FLOW:\n+\tcase TCP_V6_FLOW:\n+\tcase UDP_V4_FLOW:\n+\tcase UDP_V6_FLOW:\n+\t\terr = hinic3_set_l4_rss_hash_ops(cmd, rss_type);\n+\t\tif (err)\n+\t\t\treturn err;\n+\n+\t\tbreak;\n+\tcase IPV4_FLOW:\n+\t\trss_type-\u003eipv4 = (cmd-\u003edata != 0) ? 1 : 0;\n+\t\tbreak;\n+\tcase IPV6_FLOW:\n+\t\trss_type-\u003eipv6 = (cmd-\u003edata != 0) ? 1 : 0;\n+\t\tbreak;\n+\tdefault:\n+\t\tnetdev_err(netdev, \"Unsupported flow type\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_set_rss_hash_opts(struct net_device *netdev,\n+\t\t\t\t struct ethtool_rxnfc *cmd)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_rss_type rss_type;\n+\tint err;\n+\n+\tif (!test_bit(HINIC3_RSS_ENABLE, \u0026nic_dev-\u003eflags)) {\n+\t\tcmd-\u003edata = 0;\n+\t\tnetdev_err(netdev, \"RSS is disable, not support to set flow-hash\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\t/* RSS only supports hashing of IP addresses and L4 ports */\n+\tif (cmd-\u003edata \u0026 ~(RXH_IP_SRC | RXH_IP_DST |\n+\t\t\t RXH_L4_B_0_1 | RXH_L4_B_2_3))\n+\t\treturn -EINVAL;\n+\n+\t/* Both IP addresses must be part of the hash tuple */\n+\tif (cmd-\u003edata \u0026\u0026\n+\t (!(cmd-\u003edata \u0026 RXH_IP_SRC) || !(cmd-\u003edata \u0026 RXH_IP_DST)))\n+\t\treturn -EINVAL;\n+\n+\t/* L4 hash bits are not valid for pure L3 flow types */\n+\tif ((cmd-\u003eflow_type == IPV4_FLOW || cmd-\u003eflow_type == IPV6_FLOW) \u0026\u0026\n+\t (cmd-\u003edata \u0026 (RXH_L4_B_0_1 | RXH_L4_B_2_3)))\n+\t\treturn -EINVAL;\n+\n+\terr = hinic3_get_rss_type(nic_dev-\u003ehwdev, \u0026rss_type);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to get rss type\\n\");\n+\t\treturn err;\n+\t}\n+\n+\terr = hinic3_update_rss_hash_opts(netdev, cmd, \u0026rss_type);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = hinic3_set_rss_type(nic_dev-\u003ehwdev, rss_type);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to set rss type\\n\");\n+\t\treturn err;\n+\t}\n+\n+\tnic_dev-\u003erss_type = rss_type;\n+\n+\treturn 0;\n+}\n+\n+static void convert_rss_l3_type(u8 rss_opt, struct ethtool_rxnfc *cmd)\n+{\n+\tif (!rss_opt)\n+\t\tcmd-\u003edata \u0026= ~(RXH_IP_SRC | RXH_IP_DST);\n+}\n+\n+static void convert_rss_l4_type(u8 rss_opt, struct ethtool_rxnfc *cmd)\n+{\n+\tif (rss_opt)\n+\t\tcmd-\u003edata |= RXH_L4_B_0_1 | RXH_L4_B_2_3;\n+}\n+\n+static int hinic3_convert_rss_type(struct net_device *netdev,\n+\t\t\t\t struct hinic3_rss_type *rss_type,\n+\t\t\t\t struct ethtool_rxnfc *cmd)\n+{\n+\tcmd-\u003edata = RXH_IP_SRC | RXH_IP_DST;\n+\tswitch (cmd-\u003eflow_type) {\n+\tcase TCP_V4_FLOW:\n+\t\tconvert_rss_l4_type(rss_type-\u003etcp_ipv4, cmd);\n+\t\tbreak;\n+\tcase TCP_V6_FLOW:\n+\t\tconvert_rss_l4_type(rss_type-\u003etcp_ipv6, cmd);\n+\t\tbreak;\n+\tcase UDP_V4_FLOW:\n+\t\tconvert_rss_l4_type(rss_type-\u003eudp_ipv4, cmd);\n+\t\tbreak;\n+\tcase UDP_V6_FLOW:\n+\t\tconvert_rss_l4_type(rss_type-\u003eudp_ipv6, cmd);\n+\t\tbreak;\n+\tcase IPV4_FLOW:\n+\t\tconvert_rss_l3_type(rss_type-\u003eipv4, cmd);\n+\t\tbreak;\n+\tcase IPV6_FLOW:\n+\t\tconvert_rss_l3_type(rss_type-\u003eipv6, cmd);\n+\t\tbreak;\n+\tdefault:\n+\t\tnetdev_err(netdev, \"Unsupported flow type\\n\");\n+\t\tcmd-\u003edata = 0;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_get_rss_hash_opts(struct net_device *netdev,\n+\t\t\t\t struct ethtool_rxnfc *cmd)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tstruct hinic3_rss_type rss_type;\n+\tint err;\n+\n+\tcmd-\u003edata = 0;\n+\n+\tif (!test_bit(HINIC3_RSS_ENABLE, \u0026nic_dev-\u003eflags))\n+\t\treturn 0;\n+\n+\terr = hinic3_get_rss_type(nic_dev-\u003ehwdev, \u0026rss_type);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to get rss type\\n\");\n+\t\treturn err;\n+\t}\n+\n+\treturn hinic3_convert_rss_type(netdev, \u0026rss_type, cmd);\n+}\n+\n+u32 hinic3_get_rx_ring_count(struct net_device *netdev)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\n+\treturn nic_dev-\u003eq_params.num_qps;\n+}\n+\n+int hinic3_get_rxnfc(struct net_device *netdev,\n+\t\t struct ethtool_rxnfc *cmd, u32 *rule_locs)\n+{\n+\tint err;\n+\n+\tswitch (cmd-\u003ecmd) {\n+\tcase ETHTOOL_GRXFH:\n+\t\terr = hinic3_get_rss_hash_opts(netdev, cmd);\n+\t\tbreak;\n+\tdefault:\n+\t\terr = -EOPNOTSUPP;\n+\t\tbreak;\n+\t}\n+\n+\treturn err;\n+}\n+\n+int hinic3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)\n+{\n+\tint err;\n+\n+\tswitch (cmd-\u003ecmd) {\n+\tcase ETHTOOL_SRXFH:\n+\t\terr = hinic3_set_rss_hash_opts(netdev, cmd);\n+\t\tbreak;\n+\tdefault:\n+\t\terr = -EOPNOTSUPP;\n+\t\tbreak;\n+\t}\n+\n+\treturn err;\n+}\n+\n+static u16 hinic3_max_channels(struct net_device *netdev)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tu8 tcs = netdev_get_num_tc(netdev);\n+\n+\treturn tcs ? nic_dev-\u003emax_qps / tcs : nic_dev-\u003emax_qps;\n+}\n+\n+static u16 hinic3_curr_channels(struct net_device *netdev)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\n+\tif (netif_running(netdev))\n+\t\treturn nic_dev-\u003eq_params.num_qps ?\n+\t\t\t\tnic_dev-\u003eq_params.num_qps : 1;\n+\telse\n+\t\treturn min_t(u16, hinic3_max_channels(netdev),\n+\t\t\t nic_dev-\u003eq_params.num_qps);\n+}\n+\n+void hinic3_get_channels(struct net_device *netdev,\n+\t\t\t struct ethtool_channels *channels)\n+{\n+\tchannels-\u003emax_rx = 0;\n+\tchannels-\u003emax_tx = 0;\n+\tchannels-\u003emax_other = 0;\n+\t/* report maximum channels */\n+\tchannels-\u003emax_combined = hinic3_max_channels(netdev);\n+\tchannels-\u003erx_count = 0;\n+\tchannels-\u003etx_count = 0;\n+\tchannels-\u003eother_count = 0;\n+\t/* report flow director queues as maximum channels */\n+\tchannels-\u003ecombined_count = hinic3_curr_channels(netdev);\n+}\n+\n+static int\n+hinic3_validate_channel_parameter(struct net_device *netdev,\n+\t\t\t\t const struct ethtool_channels *channels)\n+{\n+\tu16 max_channel = hinic3_max_channels(netdev);\n+\tunsigned int count = channels-\u003ecombined_count;\n+\n+\tif (!count) {\n+\t\tnetdev_err(netdev, \"Unsupported combined_count=0\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (channels-\u003etx_count || channels-\u003erx_count || channels-\u003eother_count) {\n+\t\tnetdev_err(netdev, \"Setting rx/tx/other count not supported\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (count \u003e max_channel) {\n+\t\tnetdev_err(netdev, \"Combined count %u exceed limit %u\\n\", count,\n+\t\t\t max_channel);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int hinic3_rss_update_num_qps_and_reprogram(struct net_device *netdev)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tint err;\n+\n+\tif (!netif_is_rxfh_configured(netdev)) {\n+\t\thinic3_fillout_indir_tbl(netdev, nic_dev-\u003erss_indir);\n+\t\thinic3_rss_set_indir_tbl(nic_dev-\u003ehwdev, nic_dev-\u003erss_indir);\n+\t}\n+\n+\tif (!netif_running(netdev))\n+\t\treturn 0;\n+\n+\terr = hinic3_set_hw_rss_parameters(netdev, 1);\n+\tif (err)\n+\t\tnetdev_err(netdev,\n+\t\t\t \"Failed to update RSS parameters after changing channels\\n\");\n+\n+\treturn err;\n+}\n+\n+int hinic3_set_channels(struct net_device *netdev,\n+\t\t\tstruct ethtool_channels *channels)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tunsigned int count = channels-\u003ecombined_count;\n+\tstruct hinic3_dyna_txrxq_params q_params;\n+\tu16 old_num_qps;\n+\tint err;\n+\n+\terr = hinic3_validate_channel_parameter(netdev, channels);\n+\tif (err)\n+\t\treturn err;\n+\n+\tif (!test_bit(HINIC3_RSS_ENABLE, \u0026nic_dev-\u003eflags)) {\n+\t\tnetdev_err(netdev, \"This function doesn't support RSS, only support 1 queue pair\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\told_num_qps = nic_dev-\u003eq_params.num_qps;\n+\n+\tnetdev_dbg(netdev, \"Set max combined queue number from %u to %u\\n\",\n+\t\t old_num_qps, count);\n+\n+\tif (netif_running(netdev)) {\n+\t\tq_params = nic_dev-\u003eq_params;\n+\t\tq_params.num_qps = (u16)count;\n+\t\tq_params.txqs_res = NULL;\n+\t\tq_params.rxqs_res = NULL;\n+\t\tq_params.irq_cfg = NULL;\n+\n+\t\terr = hinic3_change_channel_settings(netdev, \u0026q_params);\n+\t\tif (err) {\n+\t\t\tnetdev_err(netdev, \"Failed to change channel settings\\n\");\n+\t\t\treturn err;\n+\t\t}\n+\t} else {\n+\t\tnic_dev-\u003eq_params.num_qps = (u16)count;\n+\t}\n+\n+\terr = hinic3_rss_update_num_qps_and_reprogram(netdev);\n+\tif (err) {\n+\t\tif (netif_running(netdev)) {\n+\t\t\tq_params.num_qps = old_num_qps;\n+\t\t\tif (hinic3_change_channel_settings(netdev, \u0026q_params))\n+\t\t\t\tnetdev_err(netdev, \"Failed to rollback channel settings after RSS error\\n\");\n+\t\t} else {\n+\t\t\tnic_dev-\u003eq_params.num_qps = old_num_qps;\n+\t\t}\n+\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+u32 hinic3_get_rxfh_indir_size(struct net_device *netdev)\n+{\n+\treturn L2NIC_RSS_INDIR_SIZE;\n+}\n+\n+static int hinic3_set_rss_rxfh(struct net_device *netdev,\n+\t\t\t const u32 *indir, u8 *key)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tu16 temp_indir[L2NIC_RSS_INDIR_SIZE];\n+\tint err;\n+\tu32 i;\n+\n+\tif (indir) {\n+\t\tfor (i = 0; i \u003c L2NIC_RSS_INDIR_SIZE; i++)\n+\t\t\ttemp_indir[i] = (u16)indir[i];\n+\n+\t\terr = hinic3_rss_set_indir_tbl(nic_dev-\u003ehwdev, temp_indir);\n+\t\tif (err) {\n+\t\t\tnetdev_err(netdev, \"Failed to set rss indir table\\n\");\n+\t\t\treturn err;\n+\t\t}\n+\n+\t\tmemcpy(nic_dev-\u003erss_indir, temp_indir, sizeof(temp_indir));\n+\t}\n+\n+\tif (key) {\n+\t\terr = hinic3_rss_set_hash_key(nic_dev-\u003ehwdev, key);\n+\t\tif (err) {\n+\t\t\tnetdev_err(netdev, \"Failed to set rss key\\n\");\n+\t\t\treturn err;\n+\t\t}\n+\n+\t\tmemcpy(nic_dev-\u003erss_hkey, key, L2NIC_RSS_KEY_SIZE);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+u32 hinic3_get_rxfh_key_size(struct net_device *netdev)\n+{\n+\treturn L2NIC_RSS_KEY_SIZE;\n+}\n+\n+static int hinic3_rss_get_indir_tbl(struct hinic3_hwdev *hwdev,\n+\t\t\t\t u32 *indir_table)\n+{\n+\tstruct hinic3_cmd_buf_pair pair;\n+\t__le16 *indir_tbl = NULL;\n+\tint err, i;\n+\n+\terr = hinic3_cmd_buf_pair_init(hwdev, \u0026pair);\n+\tif (err) {\n+\t\tdev_err(hwdev-\u003edev, \"Failed to allocate cmd_buf.\\n\");\n+\t\treturn err;\n+\t}\n+\n+\tmemset(pair.in-\u003ebuf, 0, le16_to_cpu(pair.in-\u003esize));\n+\n+\terr = hinic3_cmdq_detail_resp(hwdev, MGMT_MOD_L2NIC,\n+\t\t\t\t L2NIC_UCODE_CMD_GET_RSS_INDIR_TBL,\n+\t\t\t\t pair.in, pair.out, NULL);\n+\tif (err) {\n+\t\tdev_err(hwdev-\u003edev, \"Failed to get rss indir table\\n\");\n+\t\tgoto err_get_indir_tbl;\n+\t}\n+\n+\tindir_tbl = (__force __le16 *)pair.out-\u003ebuf;\n+\tfor (i = 0; i \u003c L2NIC_RSS_INDIR_SIZE; i++)\n+\t\tindir_table[i] = le16_to_cpu(*(indir_tbl + i));\n+\n+err_get_indir_tbl:\n+\thinic3_cmd_buf_pair_uninit(hwdev, \u0026pair);\n+\n+\treturn err;\n+}\n+\n+int hinic3_get_rxfh(struct net_device *netdev,\n+\t\t struct ethtool_rxfh_param *rxfh)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tint err = 0;\n+\n+\tif (!test_bit(HINIC3_RSS_ENABLE, \u0026nic_dev-\u003eflags)) {\n+\t\tnetdev_err(netdev, \"Rss is disabled\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\trxfh-\u003ehfunc =\n+\t\tnic_dev-\u003erss_hash_type == HINIC3_RSS_HASH_ENGINE_TYPE_XOR ?\n+\t\tETH_RSS_HASH_XOR : ETH_RSS_HASH_TOP;\n+\n+\tif (rxfh-\u003eindir) {\n+\t\terr = hinic3_rss_get_indir_tbl(nic_dev-\u003ehwdev, rxfh-\u003eindir);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\tif (rxfh-\u003ekey)\n+\t\tmemcpy(rxfh-\u003ekey, nic_dev-\u003erss_hkey, L2NIC_RSS_KEY_SIZE);\n+\n+\treturn err;\n+}\n+\n+static int hinic3_update_hash_func_type(struct net_device *netdev, u8 hfunc)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tenum hinic3_rss_hash_type new_rss_hash_type;\n+\tint err;\n+\n+\tswitch (hfunc) {\n+\tcase ETH_RSS_HASH_NO_CHANGE:\n+\t\treturn 0;\n+\tcase ETH_RSS_HASH_XOR:\n+\t\tnew_rss_hash_type = HINIC3_RSS_HASH_ENGINE_TYPE_XOR;\n+\t\tbreak;\n+\tcase ETH_RSS_HASH_TOP:\n+\t\tnew_rss_hash_type = HINIC3_RSS_HASH_ENGINE_TYPE_TOEP;\n+\t\tbreak;\n+\tdefault:\n+\t\tnetdev_err(netdev, \"Unsupported hash func %u\\n\", hfunc);\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (new_rss_hash_type == nic_dev-\u003erss_hash_type)\n+\t\treturn 0;\n+\n+\terr = hinic3_rss_set_hash_type(nic_dev-\u003ehwdev, new_rss_hash_type);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"Failed to set RSS hash type to HW\\n\");\n+\t\treturn err;\n+\t}\n+\n+\tnic_dev-\u003erss_hash_type = new_rss_hash_type;\n+\n+\treturn 0;\n+}\n+\n+int hinic3_set_rxfh(struct net_device *netdev,\n+\t\t struct ethtool_rxfh_param *rxfh,\n+\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct hinic3_nic_dev *nic_dev = netdev_priv(netdev);\n+\tu8 old_hfunc;\n+\tint err;\n+\n+\tif (!test_bit(HINIC3_RSS_ENABLE, \u0026nic_dev-\u003eflags)) {\n+\t\tnetdev_err(netdev, \"Not support to set rss parameters when rss is disable\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\told_hfunc = nic_dev-\u003erss_hash_type;\n+\n+\terr = hinic3_update_hash_func_type(netdev, rxfh-\u003ehfunc);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = hinic3_set_rss_rxfh(netdev, rxfh-\u003eindir, rxfh-\u003ekey);\n+\tif (err)\n+\t\thinic3_update_hash_func_type(netdev, old_hfunc);\n+\n+\treturn err;\n+}\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h\nindex 78d82c2aca0653..cf48a644940490 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h\n@@ -5,10 +5,30 @@\n #define _HINIC3_RSS_H_\n \n #include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/ethtool.h\u003e\n \n int hinic3_rss_init(struct net_device *netdev);\n void hinic3_rss_uninit(struct net_device *netdev);\n void hinic3_try_to_enable_rss(struct net_device *netdev);\n void hinic3_clear_rss_config(struct net_device *netdev);\n \n+int hinic3_get_rxnfc(struct net_device *netdev,\n+\t\t struct ethtool_rxnfc *cmd, u32 *rule_locs);\n+int hinic3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd);\n+\n+void hinic3_get_channels(struct net_device *netdev,\n+\t\t\t struct ethtool_channels *channels);\n+int hinic3_set_channels(struct net_device *netdev,\n+\t\t\tstruct ethtool_channels *channels);\n+\n+u32 hinic3_get_rxfh_indir_size(struct net_device *netdev);\n+u32 hinic3_get_rxfh_key_size(struct net_device *netdev);\n+\n+u32 hinic3_get_rx_ring_count(struct net_device *netdev);\n+int hinic3_get_rxfh(struct net_device *netdev,\n+\t\t struct ethtool_rxfh_param *rxfh);\n+int hinic3_set_rxfh(struct net_device *netdev,\n+\t\t struct ethtool_rxfh_param *rxfh,\n+\t\t struct netlink_ext_ack *extack);\n+\n #endif\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c\nindex 309ab590137979..a83a40202e356d 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c\n@@ -28,10 +28,6 @@\n \n #define HINIC3_LRO_PKT_HDR_LEN_IPV4 66\n #define HINIC3_LRO_PKT_HDR_LEN_IPV6 86\n-#define HINIC3_LRO_PKT_HDR_LEN(cqe) \\\n-\t(RQ_CQE_OFFOLAD_TYPE_GET((cqe)-\u003eoffload_type, IP_TYPE) == \\\n-\t HINIC3_RX_IPV6_PKT ? HINIC3_LRO_PKT_HDR_LEN_IPV6 : \\\n-\t HINIC3_LRO_PKT_HDR_LEN_IPV4)\n \n static void hinic3_rxq_clean_stats(struct hinic3_rxq_stats *rxq_stats)\n {\n@@ -46,7 +42,6 @@ static void hinic3_rxq_clean_stats(struct hinic3_rxq_stats *rxq_stats)\n \n \trxq_stats-\u003ealloc_skb_err = 0;\n \trxq_stats-\u003ealloc_rx_buf_err = 0;\n-\trxq_stats-\u003erestore_drop_sge = 0;\n \tu64_stats_update_end(\u0026rxq_stats-\u003esyncp);\n }\n \n@@ -155,8 +150,12 @@ static u32 hinic3_rx_fill_buffers(struct hinic3_rxq *rxq)\n \n \t\terr = rx_alloc_mapped_page(rxq-\u003epage_pool, rx_info,\n \t\t\t\t\t rxq-\u003ebuf_len);\n-\t\tif (unlikely(err))\n+\t\tif (unlikely(err)) {\n+\t\t\tu64_stats_update_begin(\u0026rxq-\u003erxq_stats.syncp);\n+\t\t\trxq-\u003erxq_stats.alloc_rx_buf_err++;\n+\t\t\tu64_stats_update_end(\u0026rxq-\u003erxq_stats.syncp);\n \t\t\tbreak;\n+\t\t}\n \n \t\tdma_addr = page_pool_get_dma_addr(rx_info-\u003epage) +\n \t\t\trx_info-\u003epage_offset;\n@@ -170,6 +169,10 @@ static u32 hinic3_rx_fill_buffers(struct hinic3_rxq *rxq)\n \t\t\t\trxq-\u003enext_to_update \u003c\u003c HINIC3_NORMAL_RQ_WQE);\n \t\trxq-\u003edelta -= i;\n \t\trxq-\u003enext_to_alloc = rxq-\u003enext_to_update;\n+\t} else if (free_wqebbs == rxq-\u003eq_depth - 1) {\n+\t\tu64_stats_update_begin(\u0026rxq-\u003erxq_stats.syncp);\n+\t\trxq-\u003erxq_stats.rx_buf_empty++;\n+\t\tu64_stats_update_end(\u0026rxq-\u003erxq_stats.syncp);\n \t}\n \n \treturn i;\n@@ -330,11 +333,24 @@ static void hinic3_rx_csum(struct hinic3_rxq *rxq, u32 offload_type,\n \tstruct net_device *netdev = rxq-\u003enetdev;\n \tbool l2_tunnel;\n \n+\t/* HW ensures that only 1 bit is set in csum_err. */\n+\tif (unlikely(csum_err == HINIC3_RX_CSUM_IPSU_OTHER_ERR)) {\n+\t\tu64_stats_update_begin(\u0026rxq-\u003erxq_stats.syncp);\n+\t\trxq-\u003erxq_stats.other_errors++;\n+\t\tu64_stats_update_end(\u0026rxq-\u003erxq_stats.syncp);\n+\t}\n+\n \tif (!(netdev-\u003efeatures \u0026 NETIF_F_RXCSUM))\n \t\treturn;\n \n \tif (unlikely(csum_err)) {\n \t\t/* pkt type is recognized by HW, and csum is wrong */\n+\t\tif (csum_err != HINIC3_RX_CSUM_HW_CHECK_NONE \u0026\u0026\n+\t\t csum_err != HINIC3_RX_CSUM_IPSU_OTHER_ERR) {\n+\t\t\tu64_stats_update_begin(\u0026rxq-\u003erxq_stats.syncp);\n+\t\t\trxq-\u003erxq_stats.csum_errors++;\n+\t\t\tu64_stats_update_end(\u0026rxq-\u003erxq_stats.syncp);\n+\t\t}\n \t\tskb-\u003eip_summed = CHECKSUM_NONE;\n \t\treturn;\n \t}\n@@ -387,8 +403,12 @@ static int recv_one_pkt(struct hinic3_rxq *rxq, struct hinic3_rq_cqe *rx_cqe,\n \tu16 num_lro;\n \n \tskb = hinic3_fetch_rx_buffer(rxq, pkt_len);\n-\tif (unlikely(!skb))\n+\tif (unlikely(!skb)) {\n+\t\tu64_stats_update_begin(\u0026rxq-\u003erxq_stats.syncp);\n+\t\trxq-\u003erxq_stats.alloc_skb_err++;\n+\t\tu64_stats_update_end(\u0026rxq-\u003erxq_stats.syncp);\n \t\treturn -ENOMEM;\n+\t}\n \n \t/* place header in linear portion of buffer */\n \tif (skb_is_nonlinear(skb))\n@@ -541,7 +561,7 @@ int hinic3_configure_rxqs(struct net_device *netdev, u16 num_rq,\n \t\trq_associate_cqes(rxq);\n \n \t\tpkts = hinic3_rx_fill_buffers(rxq);\n-\t\tif (!pkts) {\n+\t\tif (pkts \u003c rxq-\u003eq_mask) {\n \t\t\tnetdev_err(netdev, \"Failed to fill Rx buffer\\n\");\n \t\t\treturn -ENOMEM;\n \t\t}\n@@ -550,11 +570,29 @@ int hinic3_configure_rxqs(struct net_device *netdev, u16 num_rq,\n \treturn 0;\n }\n \n+void hinic3_rxq_get_stats(struct hinic3_rxq *rxq,\n+\t\t\t struct hinic3_rxq_stats *stats)\n+{\n+\tstruct hinic3_rxq_stats *rxq_stats = \u0026rxq-\u003erxq_stats;\n+\tunsigned int start;\n+\n+\tdo {\n+\t\tstart = u64_stats_fetch_begin(\u0026rxq_stats-\u003esyncp);\n+\t\tstats-\u003ecsum_errors = rxq_stats-\u003ecsum_errors;\n+\t\tstats-\u003eother_errors = rxq_stats-\u003eother_errors;\n+\t\tstats-\u003erx_buf_empty = rxq_stats-\u003erx_buf_empty;\n+\t\tstats-\u003ealloc_skb_err = rxq_stats-\u003ealloc_skb_err;\n+\t\tstats-\u003ealloc_rx_buf_err = rxq_stats-\u003ealloc_rx_buf_err;\n+\t} while (u64_stats_fetch_retry(\u0026rxq_stats-\u003esyncp, start));\n+}\n+\n int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget)\n {\n \tstruct hinic3_nic_dev *nic_dev = netdev_priv(rxq-\u003enetdev);\n+\tu32 ip_type, offload_type, pkt_hdr_len;\n \tu32 sw_ci, status, pkt_len, vlan_len;\n \tstruct hinic3_rq_cqe *rx_cqe;\n+\tu64 rx_bytes = 0;\n \tu32 num_wqe = 0;\n \tint nr_pkts = 0;\n \tu16 num_lro;\n@@ -574,10 +612,20 @@ int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget)\n \t\tif (recv_one_pkt(rxq, rx_cqe, pkt_len, vlan_len, status))\n \t\t\tbreak;\n \n+\t\trx_bytes += pkt_len;\n \t\tnr_pkts++;\n \t\tnum_lro = RQ_CQE_STATUS_GET(status, NUM_LRO);\n-\t\tif (num_lro)\n+\t\tif (num_lro) {\n+\t\t\toffload_type = le32_to_cpu(rx_cqe-\u003eoffload_type);\n+\t\t\tip_type = RQ_CQE_OFFOLAD_TYPE_GET(offload_type,\n+\t\t\t\t\t\t\t IP_TYPE);\n+\t\t\tpkt_hdr_len = ip_type == HINIC3_RX_IPV6_PKT ?\n+\t\t\t\t HINIC3_LRO_PKT_HDR_LEN_IPV6 :\n+\t\t\t\t HINIC3_LRO_PKT_HDR_LEN_IPV4;\n+\n+\t\t\trx_bytes += (num_lro - 1) * pkt_hdr_len;\n \t\t\tnum_wqe += hinic3_get_sge_num(rxq, pkt_len);\n+\t\t}\n \n \t\trx_cqe-\u003estatus = 0;\n \n@@ -588,5 +636,10 @@ int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget)\n \tif (rxq-\u003edelta \u003e= HINIC3_RX_BUFFER_WRITE)\n \t\thinic3_rx_fill_buffers(rxq);\n \n+\tu64_stats_update_begin(\u0026rxq-\u003erxq_stats.syncp);\n+\trxq-\u003erxq_stats.packets += (u64)nr_pkts;\n+\trxq-\u003erxq_stats.bytes += rx_bytes;\n+\tu64_stats_update_end(\u0026rxq-\u003erxq_stats.syncp);\n+\n \treturn nr_pkts;\n }\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h\nindex 06d1b3299e7cf4..2ab691ed11a93b 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h\n@@ -8,6 +8,17 @@\n #include \u003clinux/dim.h\u003e\n #include \u003clinux/netdevice.h\u003e\n \n+/* rx cqe checksum err */\n+#define HINIC3_RX_CSUM_IP_CSUM_ERR BIT(0)\n+#define HINIC3_RX_CSUM_TCP_CSUM_ERR BIT(1)\n+#define HINIC3_RX_CSUM_UDP_CSUM_ERR BIT(2)\n+#define HINIC3_RX_CSUM_IGMP_CSUM_ERR BIT(3)\n+#define HINIC3_RX_CSUM_ICMPV4_CSUM_ERR BIT(4)\n+#define HINIC3_RX_CSUM_ICMPV6_CSUM_ERR BIT(5)\n+#define HINIC3_RX_CSUM_SCTP_CRC_ERR BIT(6)\n+#define HINIC3_RX_CSUM_HW_CHECK_NONE BIT(7)\n+#define HINIC3_RX_CSUM_IPSU_OTHER_ERR BIT(8)\n+\n #define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK GENMASK(4, 0)\n #define RQ_CQE_OFFOLAD_TYPE_IP_TYPE_MASK GENMASK(6, 5)\n #define RQ_CQE_OFFOLAD_TYPE_TUNNEL_PKT_FORMAT_MASK GENMASK(11, 8)\n@@ -39,7 +50,6 @@ struct hinic3_rxq_stats {\n \tu64 rx_buf_empty;\n \tu64 alloc_skb_err;\n \tu64 alloc_rx_buf_err;\n-\tu64 restore_drop_sge;\n \tstruct u64_stats_sync syncp;\n };\n \n@@ -101,9 +111,6 @@ struct hinic3_rxq {\n \tdma_addr_t cqe_start_paddr;\n \n \tstruct dim dim;\n-\n-\tu8 last_coalesc_timer_cfg;\n-\tu8 last_pending_limit;\n } ____cacheline_aligned;\n \n struct hinic3_dyna_rxq_res {\n@@ -123,6 +130,9 @@ void hinic3_free_rxqs_res(struct net_device *netdev, u16 num_rq,\n \t\t\t u32 rq_depth, struct hinic3_dyna_rxq_res *rxqs_res);\n int hinic3_configure_rxqs(struct net_device *netdev, u16 num_rq,\n \t\t\t u32 rq_depth, struct hinic3_dyna_rxq_res *rxqs_res);\n+\n+void hinic3_rxq_get_stats(struct hinic3_rxq *rxq,\n+\t\t\t struct hinic3_rxq_stats *stats);\n int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget);\n \n #endif\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c\nindex 9306bf0020cafe..5b7075e9fe5dca 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c\n@@ -97,8 +97,12 @@ static int hinic3_tx_map_skb(struct net_device *netdev, struct sk_buff *skb,\n \n \tdma_info[0].dma = dma_map_single(\u0026pdev-\u003edev, skb-\u003edata,\n \t\t\t\t\t skb_headlen(skb), DMA_TO_DEVICE);\n-\tif (dma_mapping_error(\u0026pdev-\u003edev, dma_info[0].dma))\n+\tif (dma_mapping_error(\u0026pdev-\u003edev, dma_info[0].dma)) {\n+\t\tu64_stats_update_begin(\u0026txq-\u003etxq_stats.syncp);\n+\t\ttxq-\u003etxq_stats.map_frag_err++;\n+\t\tu64_stats_update_end(\u0026txq-\u003etxq_stats.syncp);\n \t\treturn -EFAULT;\n+\t}\n \n \tdma_info[0].len = skb_headlen(skb);\n \n@@ -117,6 +121,9 @@ static int hinic3_tx_map_skb(struct net_device *netdev, struct sk_buff *skb,\n \t\t\t\t\t\t skb_frag_size(frag),\n \t\t\t\t\t\t DMA_TO_DEVICE);\n \t\tif (dma_mapping_error(\u0026pdev-\u003edev, dma_info[idx].dma)) {\n+\t\t\tu64_stats_update_begin(\u0026txq-\u003etxq_stats.syncp);\n+\t\t\ttxq-\u003etxq_stats.map_frag_err++;\n+\t\t\tu64_stats_update_end(\u0026txq-\u003etxq_stats.syncp);\n \t\t\terr = -EFAULT;\n \t\t\tgoto err_unmap_page;\n \t\t}\n@@ -260,9 +267,11 @@ static int hinic3_tx_csum(struct hinic3_txq *txq, struct hinic3_sq_task *task,\n \t\tif (l4_proto != IPPROTO_UDP ||\n \t\t ((struct udphdr *)skb_transport_header(skb))-\u003edest !=\n \t\t VXLAN_OFFLOAD_PORT_LE) {\n+\t\t\tu64_stats_update_begin(\u0026txq-\u003etxq_stats.syncp);\n+\t\t\ttxq-\u003etxq_stats.unknown_tunnel_pkt++;\n+\t\t\tu64_stats_update_end(\u0026txq-\u003etxq_stats.syncp);\n \t\t\t/* Unsupported tunnel packet, disable csum offload */\n-\t\t\tskb_checksum_help(skb);\n-\t\t\treturn 0;\n+\t\t\treturn skb_checksum_help(skb);\n \t\t}\n \t}\n \n@@ -412,6 +421,10 @@ static u32 hinic3_tx_offload(struct sk_buff *skb, struct hinic3_sq_task *task,\n \t\toffload |= HINIC3_TX_OFFLOAD_TSO;\n \t} else {\n \t\ttso_cs_en = hinic3_tx_csum(txq, task, skb);\n+\t\tif (tso_cs_en \u003c 0) {\n+\t\t\toffload = HINIC3_TX_OFFLOAD_INVALID;\n+\t\t\treturn offload;\n+\t\t}\n \t\tif (tso_cs_en)\n \t\t\toffload |= HINIC3_TX_OFFLOAD_CSUM;\n \t}\n@@ -433,6 +446,27 @@ static u32 hinic3_tx_offload(struct sk_buff *skb, struct hinic3_sq_task *task,\n \treturn offload;\n }\n \n+static void hinic3_get_pkt_stats(struct hinic3_txq *txq, struct sk_buff *skb)\n+{\n+\tu32 hdr_len, tx_bytes;\n+\tunsigned short pkts;\n+\n+\tif (skb_is_gso(skb)) {\n+\t\thdr_len = (skb_shinfo(skb)-\u003egso_segs - 1) *\n+\t\t\t skb_tcp_all_headers(skb);\n+\t\ttx_bytes = skb-\u003elen + hdr_len;\n+\t\tpkts = skb_shinfo(skb)-\u003egso_segs;\n+\t} else {\n+\t\ttx_bytes = skb-\u003elen \u003e ETH_ZLEN ? skb-\u003elen : ETH_ZLEN;\n+\t\tpkts = 1;\n+\t}\n+\n+\tu64_stats_update_begin(\u0026txq-\u003etxq_stats.syncp);\n+\ttxq-\u003etxq_stats.bytes += tx_bytes;\n+\ttxq-\u003etxq_stats.packets += pkts;\n+\tu64_stats_update_end(\u0026txq-\u003etxq_stats.syncp);\n+}\n+\n static u16 hinic3_get_and_update_sq_owner(struct hinic3_io_queue *sq,\n \t\t\t\t\t u16 curr_pi, u16 wqebb_cnt)\n {\n@@ -539,8 +573,12 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb,\n \tint err;\n \n \tif (unlikely(skb-\u003elen \u003c MIN_SKB_LEN)) {\n-\t\tif (skb_pad(skb, MIN_SKB_LEN - skb-\u003elen))\n+\t\tif (skb_pad(skb, MIN_SKB_LEN - skb-\u003elen)) {\n+\t\t\tu64_stats_update_begin(\u0026txq-\u003etxq_stats.syncp);\n+\t\t\ttxq-\u003etxq_stats.skb_pad_err++;\n+\t\t\tu64_stats_update_end(\u0026txq-\u003etxq_stats.syncp);\n \t\t\tgoto err_out;\n+\t\t}\n \n \t\tskb-\u003elen = MIN_SKB_LEN;\n \t}\n@@ -595,6 +633,7 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb,\n \t\t\t\t txq-\u003etx_stop_thrs,\n \t\t\t\t txq-\u003etx_start_thrs);\n \n+\thinic3_get_pkt_stats(txq, skb);\n \thinic3_prepare_sq_ctrl(\u0026wqe_combo, queue_info, num_sge, owner);\n \thinic3_write_db(txq-\u003esq, 0, DB_CFLAG_DP_SQ,\n \t\t\thinic3_get_sq_local_pi(txq-\u003esq));\n@@ -604,6 +643,10 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb,\n err_drop_pkt:\n \tdev_kfree_skb_any(skb);\n err_out:\n+\tu64_stats_update_begin(\u0026txq-\u003etxq_stats.syncp);\n+\ttxq-\u003etxq_stats.dropped++;\n+\tu64_stats_update_end(\u0026txq-\u003etxq_stats.syncp);\n+\n \treturn NETDEV_TX_OK;\n }\n \n@@ -754,6 +797,24 @@ int hinic3_configure_txqs(struct net_device *netdev, u16 num_sq,\n \treturn 0;\n }\n \n+void hinic3_txq_get_stats(struct hinic3_txq *txq,\n+\t\t\t struct hinic3_txq_stats *stats)\n+{\n+\tstruct hinic3_txq_stats *txq_stats = \u0026txq-\u003etxq_stats;\n+\tunsigned int start;\n+\n+\tdo {\n+\t\tstart = u64_stats_fetch_begin(\u0026txq_stats-\u003esyncp);\n+\t\tstats-\u003ebusy = txq_stats-\u003ebusy;\n+\t\tstats-\u003eskb_pad_err = txq_stats-\u003eskb_pad_err;\n+\t\tstats-\u003efrag_len_overflow = txq_stats-\u003efrag_len_overflow;\n+\t\tstats-\u003eoffload_cow_skb_err = txq_stats-\u003eoffload_cow_skb_err;\n+\t\tstats-\u003emap_frag_err = txq_stats-\u003emap_frag_err;\n+\t\tstats-\u003eunknown_tunnel_pkt = txq_stats-\u003eunknown_tunnel_pkt;\n+\t\tstats-\u003efrag_size_err = txq_stats-\u003efrag_size_err;\n+\t} while (u64_stats_fetch_retry(\u0026txq_stats-\u003esyncp, start));\n+}\n+\n bool hinic3_tx_poll(struct hinic3_txq *txq, int budget)\n {\n \tstruct net_device *netdev = txq-\u003enetdev;\ndiff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h\nindex 00194f2a1bcc1a..0a21c423618fe8 100644\n--- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h\n+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h\n@@ -157,6 +157,8 @@ int hinic3_configure_txqs(struct net_device *netdev, u16 num_sq,\n \t\t\t u32 sq_depth, struct hinic3_dyna_txq_res *txqs_res);\n \n netdev_tx_t hinic3_xmit_frame(struct sk_buff *skb, struct net_device *netdev);\n+void hinic3_txq_get_stats(struct hinic3_txq *txq,\n+\t\t\t struct hinic3_txq_stats *stats);\n bool hinic3_tx_poll(struct hinic3_txq *txq, int budget);\n void hinic3_flush_txqs(struct net_device *netdev);\n \n"
} |
| 2/1 | 2026/07/20 17:19 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"HINIC3"
],
"FocusSymbols": [
"hinic3_set_ringparam",
"hinic3_set_coalesce",
"hinic3_set_rxnfc",
"hinic3_set_channels",
"hinic3_set_rxfh",
"hinic3_change_channel_settings",
"hinic3_get_ethtool_stats",
"hinic3_get_strings",
"hinic3_get_eth_phy_stats",
"hinic3_get_eth_mac_stats",
"hinic3_get_eth_ctrl_stats",
"hinic3_get_rmon_stats",
"hinic3_get_pause_stats",
"hinic3_get_coalesce",
"hinic3_get_rxnfc",
"hinic3_get_rxfh"
],
"Reasoning": "The patch adds multiple ethtool operations (ringparam, coalesce, rxnfc, channels, rxfh, stats) to the hinic3 network driver. These operations can be triggered via netlink/ioctl from userspace and modify the driver's internal state, so they should be fuzzed.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit a109dd523ca18e984944f181e6a820aa25c9a2eb
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Jul 20 17:19:46 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c
index 90fc16288de951..c65f6a4536265f 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c
@@ -9,14 +9,21 @@
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
+#include <linux/netlink.h>
#include <linux/ethtool.h>
#include "hinic3_lld.h"
#include "hinic3_hw_comm.h"
#include "hinic3_nic_dev.h"
#include "hinic3_nic_cfg.h"
+#include "hinic3_rss.h"
#define HINIC3_MGMT_VERSION_MAX_LEN 32
+/* Coalesce time properties in microseconds */
+#define COALESCE_PENDING_LIMIT_UNIT 8
+#define COALESCE_TIMER_CFG_UNIT 5
+#define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT)
+#define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT)
static void hinic3_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
@@ -409,14 +416,873 @@ hinic3_get_link_ksettings(struct net_device *netdev,
return 0;
}
+static void hinic3_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring,
+ struct kernel_ethtool_ringparam *kernel_ring,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+ ring->rx_max_pending = HINIC3_MAX_RX_QUEUE_DEPTH;
+ ring->tx_max_pending = HINIC3_MAX_TX_QUEUE_DEPTH;
+ ring->rx_pending = nic_dev->q_params.rq_depth;
+ ring->tx_pending = nic_dev->q_params.sq_depth;
+}
+
+static void hinic3_update_qp_depth(struct net_device *netdev,
+ u32 sq_depth, u32 rq_depth)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u16 i;
+
+ nic_dev->q_params.sq_depth = sq_depth;
+ nic_dev->q_params.rq_depth = rq_depth;
+ for (i = 0; i < nic_dev->max_qps; i++) {
+ nic_dev->txqs[i].q_depth = sq_depth;
+ nic_dev->txqs[i].q_mask = sq_depth - 1;
+ nic_dev->rxqs[i].q_depth = rq_depth;
+ nic_dev->rxqs[i].q_mask = rq_depth - 1;
+ }
+}
+
+static int hinic3_check_ringparam_valid(struct net_device *netdev,
+ const struct ethtool_ringparam *ring,
+ struct netlink_ext_ack *extack)
+{
+ if (ring->tx_pending < HINIC3_MIN_QUEUE_DEPTH ||
+ ring->rx_pending < HINIC3_MIN_QUEUE_DEPTH) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Queue depth underflow[%d]: tx[%d], rx[%d]",
+ HINIC3_MIN_QUEUE_DEPTH,
+ ring->tx_pending, ring->rx_pending);
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hinic3_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring,
+ struct kernel_ethtool_ringparam *kernel_ring,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_dyna_txrxq_params q_params = {};
+ u32 new_sq_depth, new_rq_depth;
+ int err;
+
+ err = hinic3_check_ringparam_valid(netdev, ring, extack);
+ if (err)
+ return err;
+
+ new_sq_depth = rounddown_pow_of_two(ring->tx_pending);
+ new_rq_depth = rounddown_pow_of_two(ring->rx_pending);
+ if (new_sq_depth == nic_dev->q_params.sq_depth &&
+ new_rq_depth == nic_dev->q_params.rq_depth)
+ return 0;
+
+ if (new_sq_depth != ring->tx_pending ||
+ new_rq_depth != ring->rx_pending)
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Requested Tx/Rx ring depth %u/%u trimmed to %u/%u",
+ ring->tx_pending, ring->rx_pending,
+ new_sq_depth, new_rq_depth);
+
+ if (!netif_running(netdev)) {
+ hinic3_update_qp_depth(netdev, new_sq_depth, new_rq_depth);
+ } else {
+ q_params = nic_dev->q_params;
+ q_params.sq_depth = new_sq_depth;
+ q_params.rq_depth = new_rq_depth;
+
+ err = hinic3_change_channel_settings(netdev, &q_params);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+struct hinic3_stats {
+ char name[ETH_GSTRING_LEN];
+ u32 size;
+ int offset;
+};
+
+#define HINIC3_RXQ_STAT(_stat_item) { \
+ .name = "rxq%d_"#_stat_item, \
+ .size = sizeof_field(struct hinic3_rxq_stats, _stat_item), \
+ .offset = offsetof(struct hinic3_rxq_stats, _stat_item) \
+}
+
+#define HINIC3_TXQ_STAT(_stat_item) { \
+ .name = "txq%d_"#_stat_item, \
+ .size = sizeof_field(struct hinic3_txq_stats, _stat_item), \
+ .offset = offsetof(struct hinic3_txq_stats, _stat_item) \
+}
+
+static struct hinic3_stats hinic3_rx_queue_stats[] = {
+ HINIC3_RXQ_STAT(csum_errors),
+ HINIC3_RXQ_STAT(other_errors),
+ HINIC3_RXQ_STAT(rx_buf_empty),
+ HINIC3_RXQ_STAT(alloc_skb_err),
+ HINIC3_RXQ_STAT(alloc_rx_buf_err),
+};
+
+static struct hinic3_stats hinic3_tx_queue_stats[] = {
+ HINIC3_TXQ_STAT(busy),
+ HINIC3_TXQ_STAT(skb_pad_err),
+ HINIC3_TXQ_STAT(frag_len_overflow),
+ HINIC3_TXQ_STAT(offload_cow_skb_err),
+ HINIC3_TXQ_STAT(map_frag_err),
+ HINIC3_TXQ_STAT(unknown_tunnel_pkt),
+ HINIC3_TXQ_STAT(frag_size_err),
+};
+
+#define HINIC3_FUNC_STAT(_stat_item) { \
+ .name = #_stat_item, \
+ .size = sizeof_field(struct l2nic_vport_stats, _stat_item), \
+ .offset = offsetof(struct l2nic_vport_stats, _stat_item) \
+}
+
+static struct hinic3_stats hinic3_function_stats[] = {
+ HINIC3_FUNC_STAT(tx_unicast_pkts_vport),
+ HINIC3_FUNC_STAT(tx_unicast_bytes_vport),
+ HINIC3_FUNC_STAT(tx_multicast_pkts_vport),
+ HINIC3_FUNC_STAT(tx_multicast_bytes_vport),
+ HINIC3_FUNC_STAT(tx_broadcast_pkts_vport),
+ HINIC3_FUNC_STAT(tx_broadcast_bytes_vport),
+
+ HINIC3_FUNC_STAT(rx_unicast_pkts_vport),
+ HINIC3_FUNC_STAT(rx_unicast_bytes_vport),
+ HINIC3_FUNC_STAT(rx_multicast_pkts_vport),
+ HINIC3_FUNC_STAT(rx_multicast_bytes_vport),
+ HINIC3_FUNC_STAT(rx_broadcast_pkts_vport),
+ HINIC3_FUNC_STAT(rx_broadcast_bytes_vport),
+
+ HINIC3_FUNC_STAT(tx_discard_vport),
+ HINIC3_FUNC_STAT(rx_discard_vport),
+ HINIC3_FUNC_STAT(tx_err_vport),
+ HINIC3_FUNC_STAT(rx_err_vport),
+};
+
+#define HINIC3_PORT_STAT(_stat_item) { \
+ .name = #_stat_item, \
+ .size = sizeof_field(struct mag_cmd_port_stats, _stat_item), \
+ .offset = offsetof(struct mag_cmd_port_stats, _stat_item) \
+}
+
+static struct hinic3_stats hinic3_port_stats[] = {
+ HINIC3_PORT_STAT(mac_tx_fragment_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_undersize_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_undermin_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_1519_max_bad_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_1519_max_good_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_oversize_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_jabber_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_bad_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_bad_oct_num),
+ HINIC3_PORT_STAT(mac_tx_good_oct_num),
+ HINIC3_PORT_STAT(mac_tx_total_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_uni_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri0_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri1_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri2_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri3_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri4_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri5_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri6_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_pfc_pri7_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_err_all_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_from_app_good_pkt_num),
+ HINIC3_PORT_STAT(mac_tx_from_app_bad_pkt_num),
+
+ HINIC3_PORT_STAT(mac_rx_undermin_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_1519_max_bad_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_1519_max_good_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_bad_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_bad_oct_num),
+ HINIC3_PORT_STAT(mac_rx_good_oct_num),
+ HINIC3_PORT_STAT(mac_rx_total_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_uni_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri0_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri1_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri2_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri3_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri4_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri5_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri6_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_pfc_pri7_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_send_app_good_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_send_app_bad_pkt_num),
+ HINIC3_PORT_STAT(mac_rx_unfilter_pkt_num),
+};
+
+static int hinic3_get_sset_count(struct net_device *netdev, int sset)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ int count, q_num;
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ q_num = nic_dev->q_params.num_qps;
+ count = ARRAY_SIZE(hinic3_function_stats) +
+ (ARRAY_SIZE(hinic3_tx_queue_stats) +
+ ARRAY_SIZE(hinic3_rx_queue_stats)) *
+ q_num;
+
+ if (!HINIC3_IS_VF(nic_dev->hwdev))
+ count += ARRAY_SIZE(hinic3_port_stats);
+
+ return count;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static u64 get_val_of_ptr(u32 size, const void *ptr)
+{
+ u64 ret = size == sizeof(u64) ? *(u64 *)ptr :
+ size == sizeof(u32) ? *(u32 *)ptr :
+ size == sizeof(u16) ? *(u16 *)ptr :
+ *(u8 *)ptr;
+
+ return ret;
+}
+
+static void hinic3_get_drv_queue_stats(struct net_device *netdev, u64 *data)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_txq_stats txq_stats = {};
+ struct hinic3_rxq_stats rxq_stats = {};
+ u16 i = 0, j, qid;
+ char *p;
+
+ for (qid = 0; qid < nic_dev->q_params.num_qps; qid++) {
+ if (!nic_dev->txqs)
+ break;
+
+ hinic3_txq_get_stats(&nic_dev->txqs[qid], &txq_stats);
+ for (j = 0; j < ARRAY_SIZE(hinic3_tx_queue_stats); j++, i++) {
+ p = (char *)&txq_stats +
+ hinic3_tx_queue_stats[j].offset;
+ data[i] = get_val_of_ptr(hinic3_tx_queue_stats[j].size,
+ p);
+ }
+ }
+
+ i = nic_dev->q_params.num_qps * ARRAY_SIZE(hinic3_tx_queue_stats);
+ for (qid = 0; qid < nic_dev->q_params.num_qps; qid++) {
+ if (!nic_dev->rxqs)
+ break;
+
+ hinic3_rxq_get_stats(&nic_dev->rxqs[qid], &rxq_stats);
+ for (j = 0; j < ARRAY_SIZE(hinic3_rx_queue_stats); j++, i++) {
+ p = (char *)&rxq_stats +
+ hinic3_rx_queue_stats[j].offset;
+ data[i] = get_val_of_ptr(hinic3_rx_queue_stats[j].size,
+ p);
+ }
+ }
+}
+
+static u16 hinic3_get_ethtool_port_stats(struct net_device *netdev, u64 *data)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct mag_cmd_port_stats *ps;
+ u16 i = 0, j;
+ char *p;
+ int err;
+
+ ps = kmalloc_obj(*ps);
+ if (!ps)
+ goto err_zero_stats;
+
+ err = hinic3_get_phy_port_stats(nic_dev->hwdev, ps);
+ if (err) {
+ kfree(ps);
+ netdev_err(netdev, "Failed to get port stats from fw\n");
+ goto err_zero_stats;
+ }
+
+ for (j = 0; j < ARRAY_SIZE(hinic3_port_stats); j++, i++) {
+ p = (char *)ps + hinic3_port_stats[j].offset;
+ data[i] = get_val_of_ptr(hinic3_port_stats[j].size, p);
+ }
+
+ kfree(ps);
+
+ return i;
+
+err_zero_stats:
+ memset(&data[i], 0, ARRAY_SIZE(hinic3_port_stats) * sizeof(*data));
+
+ return i + ARRAY_SIZE(hinic3_port_stats);
+}
+
+static void hinic3_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct l2nic_vport_stats vport_stats = {};
+ u16 i = 0, j;
+ char *p;
+ int err;
+
+ err = hinic3_get_vport_stats(nic_dev->hwdev,
+ hinic3_global_func_id(nic_dev->hwdev),
+ &vport_stats);
+ if (err)
+ netdev_err(netdev, "Failed to get function stats from fw\n");
+
+ for (j = 0; j < ARRAY_SIZE(hinic3_function_stats); j++, i++) {
+ p = (char *)&vport_stats + hinic3_function_stats[j].offset;
+ data[i] = get_val_of_ptr(hinic3_function_stats[j].size, p);
+ }
+
+ if (!HINIC3_IS_VF(nic_dev->hwdev))
+ i += hinic3_get_ethtool_port_stats(netdev, data + i);
+
+ hinic3_get_drv_queue_stats(netdev, data + i);
+}
+
+static u16 hinic3_get_hw_stats_strings(struct net_device *netdev, char *p)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u16 i, cnt = 0;
+
+ for (i = 0; i < ARRAY_SIZE(hinic3_function_stats); i++) {
+ memcpy(p, hinic3_function_stats[i].name, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ cnt++;
+ }
+
+ if (!HINIC3_IS_VF(nic_dev->hwdev)) {
+ for (i = 0; i < ARRAY_SIZE(hinic3_port_stats); i++) {
+ memcpy(p, hinic3_port_stats[i].name, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+static void hinic3_get_qp_stats_strings(struct net_device *netdev, char *p)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u8 *data = p;
+ u16 i, j;
+
+ for (i = 0; i < nic_dev->q_params.num_qps; i++) {
+ for (j = 0; j < ARRAY_SIZE(hinic3_tx_queue_stats); j++)
+ ethtool_sprintf(&data,
+ hinic3_tx_queue_stats[j].name, i);
+ }
+
+ for (i = 0; i < nic_dev->q_params.num_qps; i++) {
+ for (j = 0; j < ARRAY_SIZE(hinic3_rx_queue_stats); j++)
+ ethtool_sprintf(&data,
+ hinic3_rx_queue_stats[j].name, i);
+ }
+}
+
+static void hinic3_get_strings(struct net_device *netdev,
+ u32 stringset, u8 *data)
+{
+ char *p = (char *)data;
+ u16 offset;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ offset = hinic3_get_hw_stats_strings(netdev, p);
+ hinic3_get_qp_stats_strings(netdev,
+ p + offset * ETH_GSTRING_LEN);
+
+ return;
+ default:
+ netdev_err(netdev, "Invalid string set %u.\n", stringset);
+ return;
+ }
+}
+
+static void hinic3_get_eth_phy_stats(struct net_device *netdev,
+ struct ethtool_eth_phy_stats *phy_stats)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct mag_cmd_port_stats *ps;
+ int err;
+
+ ps = kmalloc_obj(*ps);
+ if (!ps)
+ return;
+
+ err = hinic3_get_phy_port_stats(nic_dev->hwdev, ps);
+ if (err) {
+ kfree(ps);
+ netdev_err(netdev, "Failed to get eth phy stats from fw\n");
+ return;
+ }
+
+ phy_stats->SymbolErrorDuringCarrier = ps->mac_rx_sym_err_pkt_num;
+
+ kfree(ps);
+}
+
+static void hinic3_get_eth_mac_stats(struct net_device *netdev,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct mag_cmd_port_stats *ps;
+ int err;
+
+ ps = kmalloc_obj(*ps);
+ if (!ps)
+ return;
+
+ err = hinic3_get_phy_port_stats(nic_dev->hwdev, ps);
+ if (err) {
+ kfree(ps);
+ netdev_err(netdev, "Failed to get eth mac stats from fw\n");
+ return;
+ }
+
+ mac_stats->FramesTransmittedOK = ps->mac_tx_good_pkt_num;
+ mac_stats->FramesReceivedOK = ps->mac_rx_good_pkt_num;
+ mac_stats->FrameCheckSequenceErrors = ps->mac_rx_fcs_err_pkt_num;
+ mac_stats->OctetsTransmittedOK = ps->mac_tx_total_oct_num;
+ mac_stats->OctetsReceivedOK = ps->mac_rx_total_oct_num;
+ mac_stats->MulticastFramesXmittedOK = ps->mac_tx_multi_pkt_num;
+ mac_stats->BroadcastFramesXmittedOK = ps->mac_tx_broad_pkt_num;
+ mac_stats->MulticastFramesReceivedOK = ps->mac_rx_multi_pkt_num;
+ mac_stats->BroadcastFramesReceivedOK = ps->mac_rx_broad_pkt_num;
+
+ kfree(ps);
+}
+
+static void hinic3_get_eth_ctrl_stats(struct net_device *netdev,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct mag_cmd_port_stats *ps;
+ int err;
+
+ ps = kmalloc_obj(*ps);
+ if (!ps)
+ return;
+
+ err = hinic3_get_phy_port_stats(nic_dev->hwdev, ps);
+ if (err) {
+ kfree(ps);
+ netdev_err(netdev, "Failed to get eth ctrl stats from fw\n");
+ return;
+ }
+
+ ctrl_stats->MACControlFramesTransmitted = ps->mac_tx_control_pkt_num;
+ ctrl_stats->MACControlFramesReceived = ps->mac_rx_control_pkt_num;
+
+ kfree(ps);
+}
+
+static const struct ethtool_rmon_hist_range hinic3_rmon_ranges[] = {
+ { 0, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1518 },
+ { 1519, 2047 },
+ { 2048, 4095 },
+ { 4096, 8191 },
+ { 8192, 9216 },
+ { 9217, 12287 },
+ {}
+};
+
+static void hinic3_get_rmon_stats(struct net_device *netdev,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct mag_cmd_port_stats *ps;
+ int err;
+
+ ps = kmalloc_obj(*ps);
+ if (!ps)
+ return;
+
+ err = hinic3_get_phy_port_stats(nic_dev->hwdev, ps);
+ if (err) {
+ kfree(ps);
+ netdev_err(netdev, "Failed to get eth rmon stats from fw\n");
+ return;
+ }
+
+ rmon_stats->undersize_pkts = ps->mac_rx_undersize_pkt_num;
+ rmon_stats->oversize_pkts = ps->mac_rx_oversize_pkt_num;
+ rmon_stats->fragments = ps->mac_rx_fragment_pkt_num;
+ rmon_stats->jabbers = ps->mac_rx_jabber_pkt_num;
+
+ rmon_stats->hist[0] = ps->mac_rx_64_oct_pkt_num;
+ rmon_stats->hist[1] = ps->mac_rx_65_127_oct_pkt_num;
+ rmon_stats->hist[2] = ps->mac_rx_128_255_oct_pkt_num;
+ rmon_stats->hist[3] = ps->mac_rx_256_511_oct_pkt_num;
+ rmon_stats->hist[4] = ps->mac_rx_512_1023_oct_pkt_num;
+ rmon_stats->hist[5] = ps->mac_rx_1024_1518_oct_pkt_num;
+ rmon_stats->hist[6] = ps->mac_rx_1519_2047_oct_pkt_num;
+ rmon_stats->hist[7] = ps->mac_rx_2048_4095_oct_pkt_num;
+ rmon_stats->hist[8] = ps->mac_rx_4096_8191_oct_pkt_num;
+ rmon_stats->hist[9] = ps->mac_rx_8192_9216_oct_pkt_num;
+ rmon_stats->hist[10] = ps->mac_rx_9217_12287_oct_pkt_num;
+
+ rmon_stats->hist_tx[0] = ps->mac_tx_64_oct_pkt_num;
+ rmon_stats->hist_tx[1] = ps->mac_tx_65_127_oct_pkt_num;
+ rmon_stats->hist_tx[2] = ps->mac_tx_128_255_oct_pkt_num;
+ rmon_stats->hist_tx[3] = ps->mac_tx_256_511_oct_pkt_num;
+ rmon_stats->hist_tx[4] = ps->mac_tx_512_1023_oct_pkt_num;
+ rmon_stats->hist_tx[5] = ps->mac_tx_1024_1518_oct_pkt_num;
+ rmon_stats->hist_tx[6] = ps->mac_tx_1519_2047_oct_pkt_num;
+ rmon_stats->hist_tx[7] = ps->mac_tx_2048_4095_oct_pkt_num;
+ rmon_stats->hist_tx[8] = ps->mac_tx_4096_8191_oct_pkt_num;
+ rmon_stats->hist_tx[9] = ps->mac_tx_8192_9216_oct_pkt_num;
+ rmon_stats->hist_tx[10] = ps->mac_tx_9217_12287_oct_pkt_num;
+
+ *ranges = hinic3_rmon_ranges;
+
+ kfree(ps);
+}
+
+static void hinic3_get_pause_stats(struct net_device *netdev,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct mag_cmd_port_stats *ps;
+ int err;
+
+ ps = kmalloc_obj(*ps);
+ if (!ps)
+ return;
+
+ err = hinic3_get_phy_port_stats(nic_dev->hwdev, ps);
+ if (err) {
+ kfree(ps);
+ netdev_err(netdev, "Failed to get eth pause stats from fw\n");
+ return;
+ }
+
+ pause_stats->tx_pause_frames = ps->mac_tx_pause_num;
+ pause_stats->rx_pause_frames = ps->mac_rx_pause_num;
+
+ kfree(ps);
+}
+
+static int hinic3_set_queue_coalesce(struct net_device *netdev, u16 q_id,
+ struct hinic3_intr_coal_info *coal,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_intr_coal_info *intr_coal;
+ struct hinic3_interrupt_info info = {};
+ bool is_active;
+ int err;
+
+ mutex_lock(&nic_dev->state_lock);
+ is_active = test_bit(HINIC3_INTF_UP, &nic_dev->flags) &&
+ (q_id < nic_dev->q_params.num_qps);
+
+ if (is_active) {
+ info.msix_index =
+ nic_dev->q_params.irq_cfg[q_id].msix_entry_idx;
+ info.resend_timer_cfg =
+ nic_dev->intr_coalesce[q_id].resend_timer_cfg;
+ }
+ mutex_unlock(&nic_dev->state_lock);
+
+ if (is_active) {
+ info.interrupt_coalesc_set = 1;
+ info.coalesc_timer_cfg = coal->coalesce_timer_cfg;
+ info.pending_limit = coal->pending_limit;
+
+ err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
+ if (err) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Failed to set queue%u coalesce",
+ q_id);
+ return err;
+ }
+ }
+
+ mutex_lock(&nic_dev->state_lock);
+ intr_coal = &nic_dev->intr_coalesce[q_id];
+ intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg;
+ intr_coal->pending_limit = coal->pending_limit;
+ intr_coal->rx_pending_limit_low = coal->rx_pending_limit_low;
+ intr_coal->rx_pending_limit_high = coal->rx_pending_limit_high;
+ mutex_unlock(&nic_dev->state_lock);
+
+ return 0;
+}
+
+static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal,
+ struct netlink_ext_ack *extack)
+{
+ const struct {
+ const char *name;
+ u32 value;
+ u32 limit;
+ } coalesce_limits[] = {
+ {"rx_coalesce_usecs",
+ coal->rx_coalesce_usecs,
+ COALESCE_MAX_TIMER_CFG},
+ {"rx_max_coalesced_frames",
+ coal->rx_max_coalesced_frames,
+ COALESCE_MAX_PENDING_LIMIT},
+ {"rx_max_coalesced_frames_low",
+ coal->rx_max_coalesced_frames_low,
+ COALESCE_MAX_PENDING_LIMIT},
+ {"rx_max_coalesced_frames_high",
+ coal->rx_max_coalesced_frames_high,
+ COALESCE_MAX_PENDING_LIMIT},
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(coalesce_limits); i++) {
+ if (coalesce_limits[i].value > coalesce_limits[i].limit) {
+ NL_SET_ERR_MSG_FMT_MOD(extack, "%s out of range %d-%d",
+ coalesce_limits[i].name, 0,
+ coalesce_limits[i].limit);
+ return -ERANGE;
+ }
+ }
+ return 0;
+}
+
+static int hinic3_validate_coalesce(const struct ethtool_coalesce *coal,
+ struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = is_coalesce_exceed_limit(coal, extack);
+ if (err)
+ return err;
+
+ if (coal->rx_max_coalesced_frames_low >
+ coal->rx_max_coalesced_frames_high) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "invalid coalesce frame high %u, low %u",
+ coal->rx_max_coalesced_frames_high,
+ coal->rx_max_coalesced_frames_low);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static void check_coalesce_align(struct net_device *netdev,
+ u32 item, u32 unit, const char *str)
+{
+ if (item % unit)
+ netdev_warn(netdev, "%s in %d units, change to %u\n",
+ str, unit, item - item % unit);
+}
+
+#define CHECK_COALESCE_ALIGN(member, unit) \
+ check_coalesce_align(netdev, member, unit, #member)
+
+static void check_coalesce_changed(struct net_device *netdev,
+ u32 item, u32 unit, u32 ori_val,
+ const char *obj_str, const char *str)
+{
+ if ((item / unit) != ori_val)
+ netdev_dbg(netdev, "Change %s from %d to %u %s\n",
+ str, ori_val * unit, item - item % unit, obj_str);
+}
+
+#define CHECK_COALESCE_CHANGED(member, unit, ori_val, obj_str) \
+ check_coalesce_changed(netdev, member, unit, ori_val, obj_str, #member)
+
+static int hinic3_set_hw_coal_param(struct net_device *netdev,
+ struct hinic3_intr_coal_info *intr_coal,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ int err;
+ u16 i;
+
+ for (i = 0; i < nic_dev->max_qps; i++) {
+ err = hinic3_set_queue_coalesce(netdev, i, intr_coal, extack);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int hinic3_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *coal,
+ struct kernel_ethtool_coalesce *kernel_coal,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_intr_coal_info *interrupt_info;
+
+ interrupt_info = &nic_dev->intr_coalesce[0];
+
+ coal->use_adaptive_rx_coalesce = READ_ONCE(nic_dev->adaptive_rx_coal);
+
+ mutex_lock(&nic_dev->state_lock);
+
+ coal->rx_max_coalesced_frames_low =
+ interrupt_info->rx_pending_limit_low *
+ COALESCE_PENDING_LIMIT_UNIT;
+
+ coal->rx_max_coalesced_frames_high =
+ interrupt_info->rx_pending_limit_high *
+ COALESCE_PENDING_LIMIT_UNIT;
+
+ /* TX/RX uses the same interrupt.
+ * So we only declare RX ethtool_coalesce parameters.
+ */
+ coal->rx_coalesce_usecs = interrupt_info->coalesce_timer_cfg *
+ COALESCE_TIMER_CFG_UNIT;
+
+ coal->rx_max_coalesced_frames = interrupt_info->pending_limit *
+ COALESCE_PENDING_LIMIT_UNIT;
+
+ mutex_unlock(&nic_dev->state_lock);
+
+ return 0;
+}
+
+static int hinic3_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *coal,
+ struct kernel_ethtool_coalesce *kernel_coal,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_intr_coal_info *ori_intr_coal;
+ struct hinic3_intr_coal_info intr_coal = {};
+ const char *obj_str = "for netdev";
+ bool old_adaptive, new_adaptive;
+ struct hinic3_rxq *rxq = NULL;
+ bool dim_stopped = false;
+ int err;
+ int i;
+
+ err = hinic3_validate_coalesce(coal, extack);
+ if (err)
+ return err;
+
+ CHECK_COALESCE_ALIGN(coal->rx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT);
+ CHECK_COALESCE_ALIGN(coal->rx_max_coalesced_frames,
+ COALESCE_PENDING_LIMIT_UNIT);
+ CHECK_COALESCE_ALIGN(coal->rx_max_coalesced_frames_high,
+ COALESCE_PENDING_LIMIT_UNIT);
+ CHECK_COALESCE_ALIGN(coal->rx_max_coalesced_frames_low,
+ COALESCE_PENDING_LIMIT_UNIT);
+
+ ori_intr_coal = &nic_dev->intr_coalesce[0];
+
+ CHECK_COALESCE_CHANGED(coal->rx_coalesce_usecs, COALESCE_TIMER_CFG_UNIT,
+ ori_intr_coal->coalesce_timer_cfg, obj_str);
+ CHECK_COALESCE_CHANGED(coal->rx_max_coalesced_frames,
+ COALESCE_PENDING_LIMIT_UNIT,
+ ori_intr_coal->pending_limit, obj_str);
+ CHECK_COALESCE_CHANGED(coal->rx_max_coalesced_frames_high,
+ COALESCE_PENDING_LIMIT_UNIT,
+ ori_intr_coal->rx_pending_limit_high, obj_str);
+ CHECK_COALESCE_CHANGED(coal->rx_max_coalesced_frames_low,
+ COALESCE_PENDING_LIMIT_UNIT,
+ ori_intr_coal->rx_pending_limit_low, obj_str);
+
+ old_adaptive = READ_ONCE(nic_dev->adaptive_rx_coal);
+ new_adaptive = !!coal->use_adaptive_rx_coalesce;
+
+ if (old_adaptive) {
+ WRITE_ONCE(nic_dev->adaptive_rx_coal, false);
+
+ synchronize_net();
+
+ for (i = 0; i < nic_dev->q_params.num_qps; i++) {
+ rxq = nic_dev->q_params.irq_cfg[i].rxq;
+ if (!rxq)
+ continue;
+
+ cancel_work_sync(&rxq->dim.work);
+
+ rxq->dim.state = DIM_START_MEASURE;
+ }
+
+ dim_stopped = true;
+ }
+
+ intr_coal.coalesce_timer_cfg =
+ (u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
+
+ intr_coal.pending_limit =
+ (u8)(coal->rx_max_coalesced_frames /
+ COALESCE_PENDING_LIMIT_UNIT);
+
+ intr_coal.rx_pending_limit_high =
+ (u8)(coal->rx_max_coalesced_frames_high /
+ COALESCE_PENDING_LIMIT_UNIT);
+
+ intr_coal.rx_pending_limit_low =
+ (u8)(coal->rx_max_coalesced_frames_low /
+ COALESCE_PENDING_LIMIT_UNIT);
+
+ /* coalesce timer or pending set to zero will disable coalesce */
+ if (!new_adaptive &&
+ (!intr_coal.coalesce_timer_cfg || !intr_coal.pending_limit))
+ netdev_info(netdev, "Coalesce will be disabled\n");
+
+ err = hinic3_set_hw_coal_param(netdev, &intr_coal, extack);
+ if (err) {
+ if (dim_stopped)
+ WRITE_ONCE(nic_dev->adaptive_rx_coal, old_adaptive);
+
+ return err;
+ }
+
+ WRITE_ONCE(nic_dev->adaptive_rx_coal, new_adaptive);
+
+ return 0;
+}
+
static const struct ethtool_ops hinic3_ethtool_ops = {
- .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
- ETHTOOL_COALESCE_PKT_RATE_RX_USECS,
+ .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
+ ETHTOOL_COALESCE_RX_MAX_FRAMES |
+ ETHTOOL_COALESCE_USE_ADAPTIVE_RX |
+ ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW |
+ ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH,
.get_link_ksettings = hinic3_get_link_ksettings,
.get_drvinfo = hinic3_get_drvinfo,
.get_msglevel = hinic3_get_msglevel,
.set_msglevel = hinic3_set_msglevel,
.get_link = ethtool_op_get_link,
+ .get_ringparam = hinic3_get_ringparam,
+ .set_ringparam = hinic3_set_ringparam,
+ .get_sset_count = hinic3_get_sset_count,
+ .get_ethtool_stats = hinic3_get_ethtool_stats,
+ .get_strings = hinic3_get_strings,
+ .get_eth_phy_stats = hinic3_get_eth_phy_stats,
+ .get_eth_mac_stats = hinic3_get_eth_mac_stats,
+ .get_eth_ctrl_stats = hinic3_get_eth_ctrl_stats,
+ .get_rmon_stats = hinic3_get_rmon_stats,
+ .get_pause_stats = hinic3_get_pause_stats,
+ .get_coalesce = hinic3_get_coalesce,
+ .set_coalesce = hinic3_set_coalesce,
+ .get_rxnfc = hinic3_get_rxnfc,
+ .set_rxnfc = hinic3_set_rxnfc,
+ .get_rx_ring_count = hinic3_get_rx_ring_count,
+ .get_channels = hinic3_get_channels,
+ .set_channels = hinic3_set_channels,
+ .get_rxfh_indir_size = hinic3_get_rxfh_indir_size,
+ .get_rxfh_key_size = hinic3_get_rxfh_key_size,
+ .get_rxfh = hinic3_get_rxfh,
+ .set_rxfh = hinic3_set_rxfh,
};
void hinic3_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
index cfc9daa3034feb..6b0f486ba590be 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
@@ -53,6 +53,17 @@ static inline void mgmt_msg_params_init_default(struct mgmt_msg_params *msg_para
msg_params->timeout_ms = 0;
}
+static inline void
+mgmt_msg_params_init_in_out(struct mgmt_msg_params *msg_params, void *in_buf,
+ void *out_buf, u32 in_buf_size, u32 out_buf_size)
+{
+ msg_params->buf_in = in_buf;
+ msg_params->buf_out = out_buf;
+ msg_params->in_size = in_buf_size;
+ msg_params->expected_out_size = out_buf_size;
+ msg_params->timeout_ms = 0;
+}
+
enum cfg_cmd {
CFG_CMD_GET_DEV_CAP = 0,
};
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
index e7d6c2033b451e..a5013dd8ade246 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_irq.c
@@ -20,7 +20,7 @@ static void hinic3_net_dim(struct hinic3_nic_dev *nic_dev,
struct dim_sample sample = {};
if (!test_bit(HINIC3_INTF_UP, &nic_dev->flags) ||
- !nic_dev->adaptive_rx_coal)
+ !READ_ONCE(nic_dev->adaptive_rx_coal))
return;
dim_update_sample(irq_cfg->total_events, rxq->rxq_stats.packets,
@@ -105,6 +105,7 @@ static int hinic3_request_irq(struct hinic3_irq_cfg *irq_cfg, u16 q_id)
info.coalesc_timer_cfg =
nic_dev->intr_coalesce[q_id].coalesce_timer_cfg;
info.resend_timer_cfg = nic_dev->intr_coalesce[q_id].resend_timer_cfg;
+
err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
if (err) {
netdev_err(netdev, "Failed to set RX interrupt coalescing attribute.\n");
@@ -148,13 +149,9 @@ static int hinic3_set_interrupt_moder(struct net_device *netdev, u16 q_id,
nic_dev->intr_coalesce[q_id].resend_timer_cfg;
err = hinic3_set_interrupt_cfg(nic_dev->hwdev, info);
- if (err) {
+ if (err)
netdev_err(netdev,
"Failed to modify moderation for Queue: %u\n", q_id);
- } else {
- nic_dev->rxqs[q_id].last_coalesc_timer_cfg = coalesc_timer_cfg;
- nic_dev->rxqs[q_id].last_pending_limit = pending_limit;
- }
return err;
}
@@ -162,17 +159,20 @@ static int hinic3_set_interrupt_moder(struct net_device *netdev, u16 q_id,
static void hinic3_update_queue_coal(struct net_device *netdev, u16 q_id,
u16 coal_timer, u16 coal_pkts)
{
+ u8 coalesc_timer_cfg, pending_limit, limit_low, limit_high;
struct hinic3_intr_coal_info *q_coal;
- u8 coalesc_timer_cfg, pending_limit;
struct hinic3_nic_dev *nic_dev;
nic_dev = netdev_priv(netdev);
q_coal = &nic_dev->intr_coalesce[q_id];
coalesc_timer_cfg = (u8)coal_timer;
+
+ limit_low = q_coal->rx_pending_limit_low;
+ limit_high = q_coal->rx_pending_limit_high;
+
pending_limit = clamp_t(u8, coal_pkts >> HINIC3_COAL_PKT_SHIFT,
- q_coal->rx_pending_limit_low,
- q_coal->rx_pending_limit_high);
+ limit_low, limit_high);
hinic3_set_interrupt_moder(nic_dev->netdev, q_id,
coalesc_timer_cfg, pending_limit);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
index 0a888fe4c975b2..a0618e6dc21971 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c
@@ -51,7 +51,7 @@ static void init_intr_coal_param(struct net_device *netdev)
info->rx_pending_limit_low = HINIC3_RX_PENDING_LIMIT_LOW;
}
- nic_dev->adaptive_rx_coal = 1;
+ WRITE_ONCE(nic_dev->adaptive_rx_coal, 1);
}
static int hinic3_init_intr_coalesce(struct net_device *netdev)
@@ -178,7 +178,7 @@ static int hinic3_sw_init(struct net_device *netdev)
u8 mac_addr[ETH_ALEN];
int err;
- mutex_init(&nic_dev->port_state_mutex);
+ mutex_init(&nic_dev->state_lock);
nic_dev->q_params.sq_depth = HINIC3_SQ_DEPTH;
nic_dev->q_params.rq_depth = HINIC3_RQ_DEPTH;
@@ -330,6 +330,8 @@ static void hinic3_link_status_change(struct net_device *netdev,
netif_carrier_off(netdev);
netdev_dbg(netdev, "Link is down\n");
}
+
+ return;
}
static void hinic3_port_module_event_handler(struct net_device *netdev,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
index c5bca3c4af961e..3c1263ff99ffbf 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
@@ -143,6 +143,41 @@ struct l2nic_cmd_set_dcb_state {
u8 rsvd[7];
};
+struct l2nic_port_stats_info {
+ struct mgmt_msg_head msg_head;
+ u16 func_id;
+ u16 rsvd1;
+};
+
+struct l2nic_vport_stats {
+ u64 tx_unicast_pkts_vport;
+ u64 tx_unicast_bytes_vport;
+ u64 tx_multicast_pkts_vport;
+ u64 tx_multicast_bytes_vport;
+ u64 tx_broadcast_pkts_vport;
+ u64 tx_broadcast_bytes_vport;
+
+ u64 rx_unicast_pkts_vport;
+ u64 rx_unicast_bytes_vport;
+ u64 rx_multicast_pkts_vport;
+ u64 rx_multicast_bytes_vport;
+ u64 rx_broadcast_pkts_vport;
+ u64 rx_broadcast_bytes_vport;
+
+ u64 tx_discard_vport;
+ u64 rx_discard_vport;
+ u64 tx_err_vport;
+ u64 rx_err_vport;
+};
+
+struct l2nic_cmd_vport_stats {
+ struct mgmt_msg_head msg_head;
+ u32 stats_size;
+ u32 rsvd1;
+ struct l2nic_vport_stats stats;
+ u64 rsvd2[6];
+};
+
struct l2nic_cmd_lro_config {
struct mgmt_msg_head msg_head;
u16 func_id;
@@ -234,6 +269,7 @@ enum l2nic_cmd {
L2NIC_CMD_SET_VPORT_ENABLE = 6,
L2NIC_CMD_SET_RX_MODE = 7,
L2NIC_CMD_SET_SQ_CI_ATTR = 8,
+ L2NIC_CMD_GET_VPORT_STAT = 9,
L2NIC_CMD_CLEAR_QP_RESOURCE = 11,
L2NIC_CMD_CFG_RX_LRO = 13,
L2NIC_CMD_CFG_LRO_TIMER = 14,
@@ -246,6 +282,7 @@ enum l2nic_cmd {
L2NIC_CMD_SET_VLAN_FILTER_EN = 26,
L2NIC_CMD_SET_RX_VLAN_OFFLOAD = 27,
L2NIC_CMD_CFG_RSS = 60,
+ L2NIC_CMD_GET_RSS_CTX_TBL = 62,
L2NIC_CMD_CFG_RSS_HASH_KEY = 63,
L2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,
L2NIC_CMD_SET_RSS_CTX_TBL = 65,
@@ -265,6 +302,7 @@ enum l2nic_ucode_cmd {
L2NIC_UCODE_CMD_MODIFY_QUEUE_CTX = 0,
L2NIC_UCODE_CMD_CLEAN_QUEUE_CTX = 1,
L2NIC_UCODE_CMD_SET_RSS_INDIR_TBL = 4,
+ L2NIC_UCODE_CMD_GET_RSS_INDIR_TBL = 6,
};
/* hilink mac group command */
@@ -272,6 +310,7 @@ enum mag_cmd {
MAG_CMD_SET_PORT_ENABLE = 6,
MAG_CMD_GET_LINK_STATUS = 7,
+ MAG_CMD_GET_PORT_STAT = 151,
MAG_CMD_GET_PORT_INFO = 153,
};
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index da73811641a9de..7f304d7ac47ba3 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -288,7 +288,8 @@ static void hinic3_free_channel_resources(struct net_device *netdev,
hinic3_free_qps(nic_dev, qp_params);
}
-static int hinic3_open_channel(struct net_device *netdev)
+static int hinic3_prepare_channel(struct net_device *netdev,
+ struct hinic3_dyna_txrxq_params *qp_params)
{
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
int err;
@@ -299,12 +300,25 @@ static int hinic3_open_channel(struct net_device *netdev)
return err;
}
- err = hinic3_configure_txrxqs(netdev, &nic_dev->q_params);
+ err = hinic3_configure_txrxqs(netdev, qp_params);
if (err) {
netdev_err(netdev, "Failed to configure txrxqs\n");
goto err_free_qp_ctxts;
}
+ return 0;
+
+err_free_qp_ctxts:
+ hinic3_free_qp_ctxts(nic_dev);
+
+ return err;
+}
+
+static int hinic3_open_channel(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ int err;
+
err = hinic3_qps_irq_init(netdev);
if (err) {
netdev_err(netdev, "Failed to init txrxq irq\n");
@@ -321,6 +335,7 @@ static int hinic3_open_channel(struct net_device *netdev)
err_uninit_qps_irq:
hinic3_qps_irq_uninit(netdev);
+
err_free_qp_ctxts:
hinic3_free_qp_ctxts(nic_dev);
@@ -341,9 +356,7 @@ static int hinic3_maybe_set_port_state(struct net_device *netdev, bool enable)
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
int err;
- mutex_lock(&nic_dev->port_state_mutex);
err = hinic3_set_port_enable(nic_dev->hwdev, enable);
- mutex_unlock(&nic_dev->port_state_mutex);
return err;
}
@@ -428,6 +441,76 @@ static void hinic3_vport_down(struct net_device *netdev)
}
}
+int
+hinic3_change_channel_settings(struct net_device *netdev,
+ struct hinic3_dyna_txrxq_params *trxq_params)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_dyna_txrxq_params cur_trxq_params = {};
+ struct hinic3_dyna_qp_params new_qp_params = {};
+ struct hinic3_dyna_qp_params cur_qp_params = {};
+ int err;
+
+ mutex_lock(&nic_dev->state_lock);
+ cur_trxq_params = nic_dev->q_params;
+
+ hinic3_config_num_qps(netdev, trxq_params);
+
+ err = hinic3_alloc_channel_resources(netdev, &new_qp_params,
+ trxq_params);
+ if (err) {
+ netdev_err(netdev, "Failed to alloc channel resources\n");
+ hinic3_config_num_qps(netdev, &cur_trxq_params);
+ mutex_unlock(&nic_dev->state_lock);
+ return err;
+ }
+
+ hinic3_vport_down(netdev);
+ hinic3_close_channel(netdev);
+ hinic3_get_cur_qps(nic_dev, &cur_qp_params);
+
+ hinic3_init_qps(nic_dev, &new_qp_params);
+
+ err = hinic3_prepare_channel(netdev, trxq_params);
+ if (err)
+ goto err_uninit_qps;
+
+ if (nic_dev->num_qp_irq > trxq_params->num_qps)
+ hinic3_qp_irq_change(netdev, trxq_params->num_qps);
+
+ nic_dev->q_params = *trxq_params;
+
+ err = hinic3_open_channel(netdev);
+ if (err)
+ goto err_qp_irq_reset;
+
+ err = hinic3_vport_up(netdev);
+ if (err)
+ goto err_close_channel;
+
+ hinic3_free_channel_resources(netdev, &cur_qp_params, &cur_trxq_params);
+
+ mutex_unlock(&nic_dev->state_lock);
+
+ return 0;
+
+err_close_channel:
+ hinic3_close_channel(netdev);
+err_qp_irq_reset:
+ nic_dev->q_params = cur_trxq_params;
+
+ if (trxq_params->num_qps > cur_trxq_params.num_qps)
+ hinic3_qp_irq_change(netdev, cur_trxq_params.num_qps);
+err_uninit_qps:
+ hinic3_get_cur_qps(nic_dev, &new_qp_params);
+ hinic3_config_num_qps(netdev, &cur_trxq_params);
+ hinic3_init_qps(nic_dev, &cur_qp_params);
+ hinic3_free_channel_resources(netdev, &new_qp_params, trxq_params);
+ mutex_unlock(&nic_dev->state_lock);
+
+ return err;
+}
+
static int hinic3_open(struct net_device *netdev)
{
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
@@ -458,6 +541,10 @@ static int hinic3_open(struct net_device *netdev)
hinic3_init_qps(nic_dev, &qp_params);
+ err = hinic3_prepare_channel(netdev, &nic_dev->q_params);
+ if (err)
+ goto err_uninit_qps;
+
err = hinic3_open_channel(netdev);
if (err)
goto err_uninit_qps;
@@ -473,7 +560,7 @@ static int hinic3_open(struct net_device *netdev)
err_close_channel:
hinic3_close_channel(netdev);
err_uninit_qps:
- hinic3_uninit_qps(nic_dev, &qp_params);
+ hinic3_get_cur_qps(nic_dev, &qp_params);
hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
err_destroy_num_qps:
hinic3_destroy_num_qps(netdev);
@@ -493,10 +580,15 @@ static int hinic3_close(struct net_device *netdev)
return 0;
}
+ mutex_lock(&nic_dev->state_lock);
hinic3_vport_down(netdev);
hinic3_close_channel(netdev);
- hinic3_uninit_qps(nic_dev, &qp_params);
- hinic3_free_channel_resources(netdev, &qp_params, &nic_dev->q_params);
+ hinic3_get_cur_qps(nic_dev, &qp_params);
+ hinic3_free_channel_resources(netdev, &qp_params,
+ &nic_dev->q_params);
+ hinic3_free_nicio_res(nic_dev);
+ hinic3_destroy_num_qps(netdev);
+ mutex_unlock(&nic_dev->state_lock);
return 0;
}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
index de5a7984d2cb95..1b14dc824ce1e9 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
@@ -639,6 +639,42 @@ int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up)
return 0;
}
+int hinic3_get_phy_port_stats(struct hinic3_hwdev *hwdev,
+ struct mag_cmd_port_stats *stats)
+{
+ struct mag_cmd_port_stats_info stats_info = {};
+ struct mag_cmd_get_port_stat *ps;
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ ps = kzalloc_obj(*ps);
+ if (!ps)
+ return -ENOMEM;
+
+ stats_info.port_id = hinic3_physical_port_id(hwdev);
+
+ mgmt_msg_params_init_in_out(&msg_params, &stats_info, ps,
+ sizeof(stats_info), sizeof(*ps));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_HILINK,
+ MAG_CMD_GET_PORT_STAT, &msg_params);
+
+ if (err || ps->head.status) {
+ dev_err(hwdev->dev,
+ "Failed to get port statistics, err: %d, status: 0x%x\n",
+ err, ps->head.status);
+ err = -EFAULT;
+ goto out;
+ }
+
+ memcpy(stats, &ps->counter, sizeof(*stats));
+
+out:
+ kfree(ps);
+
+ return err;
+}
+
int hinic3_get_port_info(struct hinic3_hwdev *hwdev,
struct hinic3_nic_port_info *port_info)
{
@@ -738,3 +774,31 @@ int hinic3_get_pause_info(struct hinic3_nic_dev *nic_dev,
return hinic3_cfg_hw_pause(nic_dev->hwdev, MGMT_MSG_CMD_OP_GET,
nic_pause);
}
+
+int hinic3_get_vport_stats(struct hinic3_hwdev *hwdev, u16 func_id,
+ struct l2nic_vport_stats *stats)
+{
+ struct l2nic_cmd_vport_stats vport_stats = {};
+ struct l2nic_port_stats_info stats_info = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ stats_info.func_id = func_id;
+
+ mgmt_msg_params_init_in_out(&msg_params, &stats_info, &vport_stats,
+ sizeof(stats_info), sizeof(vport_stats));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_CMD_GET_VPORT_STAT, &msg_params);
+
+ if (err || vport_stats.msg_head.status) {
+ dev_err(hwdev->dev,
+ "Failed to get function statistics, err: %d, status: 0x%x\n",
+ err, vport_stats.msg_head.status);
+ return -EFAULT;
+ }
+
+ memcpy(stats, &vport_stats.stats, sizeof(*stats));
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
index 5d52202a8d4eb1..80573c12153973 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
@@ -129,6 +129,110 @@ struct mag_cmd_get_xsfp_present {
u8 rsvd[2];
};
+struct mag_cmd_port_stats {
+ u64 mac_tx_fragment_pkt_num;
+ u64 mac_tx_undersize_pkt_num;
+ u64 mac_tx_undermin_pkt_num;
+ u64 mac_tx_64_oct_pkt_num;
+ u64 mac_tx_65_127_oct_pkt_num;
+ u64 mac_tx_128_255_oct_pkt_num;
+ u64 mac_tx_256_511_oct_pkt_num;
+ u64 mac_tx_512_1023_oct_pkt_num;
+ u64 mac_tx_1024_1518_oct_pkt_num;
+ u64 mac_tx_1519_2047_oct_pkt_num;
+ u64 mac_tx_2048_4095_oct_pkt_num;
+ u64 mac_tx_4096_8191_oct_pkt_num;
+ u64 mac_tx_8192_9216_oct_pkt_num;
+ u64 mac_tx_9217_12287_oct_pkt_num;
+ u64 mac_tx_12288_16383_oct_pkt_num;
+ u64 mac_tx_1519_max_bad_pkt_num;
+ u64 mac_tx_1519_max_good_pkt_num;
+ u64 mac_tx_oversize_pkt_num;
+ u64 mac_tx_jabber_pkt_num;
+ u64 mac_tx_bad_pkt_num;
+ u64 mac_tx_bad_oct_num;
+ u64 mac_tx_good_pkt_num;
+ u64 mac_tx_good_oct_num;
+ u64 mac_tx_total_pkt_num;
+ u64 mac_tx_total_oct_num;
+ u64 mac_tx_uni_pkt_num;
+ u64 mac_tx_multi_pkt_num;
+ u64 mac_tx_broad_pkt_num;
+ u64 mac_tx_pause_num;
+ u64 mac_tx_pfc_pkt_num;
+ u64 mac_tx_pfc_pri0_pkt_num;
+ u64 mac_tx_pfc_pri1_pkt_num;
+ u64 mac_tx_pfc_pri2_pkt_num;
+ u64 mac_tx_pfc_pri3_pkt_num;
+ u64 mac_tx_pfc_pri4_pkt_num;
+ u64 mac_tx_pfc_pri5_pkt_num;
+ u64 mac_tx_pfc_pri6_pkt_num;
+ u64 mac_tx_pfc_pri7_pkt_num;
+ u64 mac_tx_control_pkt_num;
+ u64 mac_tx_err_all_pkt_num;
+ u64 mac_tx_from_app_good_pkt_num;
+ u64 mac_tx_from_app_bad_pkt_num;
+
+ u64 mac_rx_fragment_pkt_num;
+ u64 mac_rx_undersize_pkt_num;
+ u64 mac_rx_undermin_pkt_num;
+ u64 mac_rx_64_oct_pkt_num;
+ u64 mac_rx_65_127_oct_pkt_num;
+ u64 mac_rx_128_255_oct_pkt_num;
+ u64 mac_rx_256_511_oct_pkt_num;
+ u64 mac_rx_512_1023_oct_pkt_num;
+ u64 mac_rx_1024_1518_oct_pkt_num;
+ u64 mac_rx_1519_2047_oct_pkt_num;
+ u64 mac_rx_2048_4095_oct_pkt_num;
+ u64 mac_rx_4096_8191_oct_pkt_num;
+ u64 mac_rx_8192_9216_oct_pkt_num;
+ u64 mac_rx_9217_12287_oct_pkt_num;
+ u64 mac_rx_12288_16383_oct_pkt_num;
+ u64 mac_rx_1519_max_bad_pkt_num;
+ u64 mac_rx_1519_max_good_pkt_num;
+ u64 mac_rx_oversize_pkt_num;
+ u64 mac_rx_jabber_pkt_num;
+ u64 mac_rx_bad_pkt_num;
+ u64 mac_rx_bad_oct_num;
+ u64 mac_rx_good_pkt_num;
+ u64 mac_rx_good_oct_num;
+ u64 mac_rx_total_pkt_num;
+ u64 mac_rx_total_oct_num;
+ u64 mac_rx_uni_pkt_num;
+ u64 mac_rx_multi_pkt_num;
+ u64 mac_rx_broad_pkt_num;
+ u64 mac_rx_pause_num;
+ u64 mac_rx_pfc_pkt_num;
+ u64 mac_rx_pfc_pri0_pkt_num;
+ u64 mac_rx_pfc_pri1_pkt_num;
+ u64 mac_rx_pfc_pri2_pkt_num;
+ u64 mac_rx_pfc_pri3_pkt_num;
+ u64 mac_rx_pfc_pri4_pkt_num;
+ u64 mac_rx_pfc_pri5_pkt_num;
+ u64 mac_rx_pfc_pri6_pkt_num;
+ u64 mac_rx_pfc_pri7_pkt_num;
+ u64 mac_rx_control_pkt_num;
+ u64 mac_rx_sym_err_pkt_num;
+ u64 mac_rx_fcs_err_pkt_num;
+ u64 mac_rx_send_app_good_pkt_num;
+ u64 mac_rx_send_app_bad_pkt_num;
+ u64 mac_rx_unfilter_pkt_num;
+};
+
+struct mag_cmd_port_stats_info {
+ struct mgmt_msg_head head;
+
+ u8 port_id;
+ u8 rsvd0[3];
+};
+
+struct mag_cmd_get_port_stat {
+ struct mgmt_msg_head head;
+
+ struct mag_cmd_port_stats counter;
+ u64 rsvd1[15];
+};
+
enum link_err_type {
LINK_ERR_MODULE_UNRECOGENIZED,
LINK_ERR_NUM,
@@ -209,6 +313,11 @@ int hinic3_get_port_info(struct hinic3_hwdev *hwdev,
struct hinic3_nic_port_info *port_info);
int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
bool enable);
+int hinic3_get_phy_port_stats(struct hinic3_hwdev *hwdev,
+ struct mag_cmd_port_stats *stats);
+int hinic3_get_vport_stats(struct hinic3_hwdev *hwdev, u16 func_id,
+ struct l2nic_vport_stats *stats);
+
int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
index 9502293ff710c6..ccef82d57fddc2 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
@@ -10,6 +10,9 @@
#include "hinic3_hw_cfg.h"
#include "hinic3_hwdev.h"
#include "hinic3_mgmt_interface.h"
+#include "hinic3_nic_io.h"
+#include "hinic3_tx.h"
+#include "hinic3_rx.h"
#define HINIC3_VLAN_BITMAP_BYTE_SIZE(nic_dev) (sizeof(*(nic_dev)->vlan_bitmap))
#define HINIC3_VLAN_BITMAP_SIZE(nic_dev) \
@@ -127,8 +130,8 @@ struct hinic3_nic_dev {
struct workqueue_struct *workq;
struct delayed_work periodic_work;
struct work_struct rx_mode_work;
- /* lock for enable/disable port */
- struct mutex port_state_mutex;
+ /* Protects Interface state */
+ struct mutex state_lock;
struct list_head uc_filter_list;
struct list_head mc_filter_list;
@@ -143,6 +146,10 @@ struct hinic3_nic_dev {
void hinic3_set_netdev_ops(struct net_device *netdev);
int hinic3_set_hw_features(struct net_device *netdev);
+int
+hinic3_change_channel_settings(struct net_device *netdev,
+ struct hinic3_dyna_txrxq_params *trxq_params);
+
int hinic3_qps_irq_init(struct net_device *netdev);
void hinic3_qps_irq_uninit(struct net_device *netdev);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c
index 87e736adba025c..0e7a0ccfba982c 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.c
@@ -484,8 +484,8 @@ void hinic3_init_qps(struct hinic3_nic_dev *nic_dev,
}
}
-void hinic3_uninit_qps(struct hinic3_nic_dev *nic_dev,
- struct hinic3_dyna_qp_params *qp_params)
+void hinic3_get_cur_qps(struct hinic3_nic_dev *nic_dev,
+ struct hinic3_dyna_qp_params *qp_params)
{
struct hinic3_nic_io *nic_io = nic_dev->nic_io;
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h
index 12eefabcf1dbbc..571b34d6395059 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_nic_io.h
@@ -14,6 +14,10 @@ struct hinic3_nic_dev;
#define HINIC3_RQ_WQEBB_SHIFT 3
#define HINIC3_SQ_WQEBB_SIZE BIT(HINIC3_SQ_WQEBB_SHIFT)
+#define HINIC3_MAX_TX_QUEUE_DEPTH 65536
+#define HINIC3_MAX_RX_QUEUE_DEPTH 16384
+#define HINIC3_MIN_QUEUE_DEPTH 128
+
/* ******************** RQ_CTRL ******************** */
enum hinic3_rq_wqe_type {
HINIC3_NORMAL_RQ_WQE = 1,
@@ -136,8 +140,8 @@ void hinic3_free_qps(struct hinic3_nic_dev *nic_dev,
struct hinic3_dyna_qp_params *qp_params);
void hinic3_init_qps(struct hinic3_nic_dev *nic_dev,
struct hinic3_dyna_qp_params *qp_params);
-void hinic3_uninit_qps(struct hinic3_nic_dev *nic_dev,
- struct hinic3_dyna_qp_params *qp_params);
+void hinic3_get_cur_qps(struct hinic3_nic_dev *nic_dev,
+ struct hinic3_dyna_qp_params *qp_params);
int hinic3_init_qp_ctxts(struct hinic3_nic_dev *nic_dev);
void hinic3_free_qp_ctxts(struct hinic3_nic_dev *nic_dev);
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c
index 25db74d8c7ddb7..62cf5f59a0cf52 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.c
@@ -155,7 +155,7 @@ static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,
L2NIC_CMD_SET_RSS_CTX_TBL, &msg_params);
if (ctx_tbl.msg_head.status == MGMT_STATUS_CMD_UNSUPPORTED) {
- return MGMT_STATUS_CMD_UNSUPPORTED;
+ return -EOPNOTSUPP;
} else if (err || ctx_tbl.msg_head.status) {
dev_err(hwdev->dev, "mgmt Failed to set rss context offload, err: %d, status: 0x%x\n",
err, ctx_tbl.msg_head.status);
@@ -165,6 +165,41 @@ static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,
return 0;
}
+static int hinic3_get_rss_type(struct hinic3_hwdev *hwdev,
+ struct hinic3_rss_type *rss_type)
+{
+ struct l2nic_cmd_rss_ctx_tbl ctx_tbl = {};
+ struct mgmt_msg_params msg_params = {};
+ int err;
+
+ ctx_tbl.func_id = hinic3_global_func_id(hwdev);
+
+ mgmt_msg_params_init_default(&msg_params, &ctx_tbl, sizeof(ctx_tbl));
+
+ err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_CMD_GET_RSS_CTX_TBL,
+ &msg_params);
+ if (ctx_tbl.msg_head.status == MGMT_STATUS_CMD_UNSUPPORTED) {
+ return -EOPNOTSUPP;
+ } else if (err || ctx_tbl.msg_head.status) {
+ dev_err(hwdev->dev, "Failed to get hash type, err: %d, status: 0x%x\n",
+ err, ctx_tbl.msg_head.status);
+ return -EINVAL;
+ }
+
+ rss_type->ipv4 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, IPV4);
+ rss_type->ipv6 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, IPV6);
+ rss_type->ipv6_ext = L2NIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT);
+ rss_type->tcp_ipv4 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4);
+ rss_type->tcp_ipv6 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6);
+ rss_type->tcp_ipv6_ext = L2NIC_RSS_TYPE_GET(ctx_tbl.context,
+ TCP_IPV6_EXT);
+ rss_type->udp_ipv4 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4);
+ rss_type->udp_ipv6 = L2NIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6);
+
+ return 0;
+}
+
static int hinic3_rss_cfg_hash_type(struct hinic3_hwdev *hwdev, u8 opcode,
enum hinic3_rss_hash_type *type)
{
@@ -264,7 +299,8 @@ static int hinic3_set_hw_rss_parameters(struct net_device *netdev, u8 rss_en)
if (err)
return err;
- hinic3_fillout_indir_tbl(netdev, nic_dev->rss_indir);
+ if (!netif_is_rxfh_configured(netdev))
+ hinic3_fillout_indir_tbl(netdev, nic_dev->rss_indir);
err = hinic3_config_rss_hw_resource(netdev, nic_dev->rss_indir);
if (err)
@@ -334,3 +370,524 @@ void hinic3_try_to_enable_rss(struct net_device *netdev)
clear_bit(HINIC3_RSS_ENABLE, &nic_dev->flags);
nic_dev->q_params.num_qps = nic_dev->max_qps;
}
+
+static int hinic3_set_l4_rss_hash_ops(const struct ethtool_rxnfc *cmd,
+ struct hinic3_rss_type *rss_type)
+{
+ u8 rss_l4_en;
+
+ switch (cmd->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+ case 0:
+ rss_l4_en = 0;
+ break;
+ case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+ rss_l4_en = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ rss_type->tcp_ipv4 = rss_l4_en;
+ break;
+ case TCP_V6_FLOW:
+ rss_type->tcp_ipv6 = rss_l4_en;
+ break;
+ case UDP_V4_FLOW:
+ rss_type->udp_ipv4 = rss_l4_en;
+ break;
+ case UDP_V6_FLOW:
+ rss_type->udp_ipv6 = rss_l4_en;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hinic3_update_rss_hash_opts(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd,
+ struct hinic3_rss_type *rss_type)
+{
+ int err;
+
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V4_FLOW:
+ case UDP_V6_FLOW:
+ err = hinic3_set_l4_rss_hash_ops(cmd, rss_type);
+ if (err)
+ return err;
+
+ break;
+ case IPV4_FLOW:
+ rss_type->ipv4 = (cmd->data != 0) ? 1 : 0;
+ break;
+ case IPV6_FLOW:
+ rss_type->ipv6 = (cmd->data != 0) ? 1 : 0;
+ break;
+ default:
+ netdev_err(netdev, "Unsupported flow type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hinic3_set_rss_hash_opts(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_rss_type rss_type;
+ int err;
+
+ if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags)) {
+ cmd->data = 0;
+ netdev_err(netdev, "RSS is disable, not support to set flow-hash\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* RSS only supports hashing of IP addresses and L4 ports */
+ if (cmd->data & ~(RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3))
+ return -EINVAL;
+
+ /* Both IP addresses must be part of the hash tuple */
+ if (cmd->data &&
+ (!(cmd->data & RXH_IP_SRC) || !(cmd->data & RXH_IP_DST)))
+ return -EINVAL;
+
+ /* L4 hash bits are not valid for pure L3 flow types */
+ if ((cmd->flow_type == IPV4_FLOW || cmd->flow_type == IPV6_FLOW) &&
+ (cmd->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)))
+ return -EINVAL;
+
+ err = hinic3_get_rss_type(nic_dev->hwdev, &rss_type);
+ if (err) {
+ netdev_err(netdev, "Failed to get rss type\n");
+ return err;
+ }
+
+ err = hinic3_update_rss_hash_opts(netdev, cmd, &rss_type);
+ if (err)
+ return err;
+
+ err = hinic3_set_rss_type(nic_dev->hwdev, rss_type);
+ if (err) {
+ netdev_err(netdev, "Failed to set rss type\n");
+ return err;
+ }
+
+ nic_dev->rss_type = rss_type;
+
+ return 0;
+}
+
+static void convert_rss_l3_type(u8 rss_opt, struct ethtool_rxnfc *cmd)
+{
+ if (!rss_opt)
+ cmd->data &= ~(RXH_IP_SRC | RXH_IP_DST);
+}
+
+static void convert_rss_l4_type(u8 rss_opt, struct ethtool_rxnfc *cmd)
+{
+ if (rss_opt)
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+}
+
+static int hinic3_convert_rss_type(struct net_device *netdev,
+ struct hinic3_rss_type *rss_type,
+ struct ethtool_rxnfc *cmd)
+{
+ cmd->data = RXH_IP_SRC | RXH_IP_DST;
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ convert_rss_l4_type(rss_type->tcp_ipv4, cmd);
+ break;
+ case TCP_V6_FLOW:
+ convert_rss_l4_type(rss_type->tcp_ipv6, cmd);
+ break;
+ case UDP_V4_FLOW:
+ convert_rss_l4_type(rss_type->udp_ipv4, cmd);
+ break;
+ case UDP_V6_FLOW:
+ convert_rss_l4_type(rss_type->udp_ipv6, cmd);
+ break;
+ case IPV4_FLOW:
+ convert_rss_l3_type(rss_type->ipv4, cmd);
+ break;
+ case IPV6_FLOW:
+ convert_rss_l3_type(rss_type->ipv6, cmd);
+ break;
+ default:
+ netdev_err(netdev, "Unsupported flow type\n");
+ cmd->data = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hinic3_get_rss_hash_opts(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ struct hinic3_rss_type rss_type;
+ int err;
+
+ cmd->data = 0;
+
+ if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags))
+ return 0;
+
+ err = hinic3_get_rss_type(nic_dev->hwdev, &rss_type);
+ if (err) {
+ netdev_err(netdev, "Failed to get rss type\n");
+ return err;
+ }
+
+ return hinic3_convert_rss_type(netdev, &rss_type, cmd);
+}
+
+u32 hinic3_get_rx_ring_count(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+ return nic_dev->q_params.num_qps;
+}
+
+int hinic3_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+ int err;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXFH:
+ err = hinic3_get_rss_hash_opts(netdev, cmd);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+int hinic3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
+{
+ int err;
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ err = hinic3_set_rss_hash_opts(netdev, cmd);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static u16 hinic3_max_channels(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u8 tcs = netdev_get_num_tc(netdev);
+
+ return tcs ? nic_dev->max_qps / tcs : nic_dev->max_qps;
+}
+
+static u16 hinic3_curr_channels(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+ if (netif_running(netdev))
+ return nic_dev->q_params.num_qps ?
+ nic_dev->q_params.num_qps : 1;
+ else
+ return min_t(u16, hinic3_max_channels(netdev),
+ nic_dev->q_params.num_qps);
+}
+
+void hinic3_get_channels(struct net_device *netdev,
+ struct ethtool_channels *channels)
+{
+ channels->max_rx = 0;
+ channels->max_tx = 0;
+ channels->max_other = 0;
+ /* report maximum channels */
+ channels->max_combined = hinic3_max_channels(netdev);
+ channels->rx_count = 0;
+ channels->tx_count = 0;
+ channels->other_count = 0;
+ /* report flow director queues as maximum channels */
+ channels->combined_count = hinic3_curr_channels(netdev);
+}
+
+static int
+hinic3_validate_channel_parameter(struct net_device *netdev,
+ const struct ethtool_channels *channels)
+{
+ u16 max_channel = hinic3_max_channels(netdev);
+ unsigned int count = channels->combined_count;
+
+ if (!count) {
+ netdev_err(netdev, "Unsupported combined_count=0\n");
+ return -EINVAL;
+ }
+
+ if (channels->tx_count || channels->rx_count || channels->other_count) {
+ netdev_err(netdev, "Setting rx/tx/other count not supported\n");
+ return -EINVAL;
+ }
+
+ if (count > max_channel) {
+ netdev_err(netdev, "Combined count %u exceed limit %u\n", count,
+ max_channel);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hinic3_rss_update_num_qps_and_reprogram(struct net_device *netdev)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ int err;
+
+ if (!netif_is_rxfh_configured(netdev)) {
+ hinic3_fillout_indir_tbl(netdev, nic_dev->rss_indir);
+ hinic3_rss_set_indir_tbl(nic_dev->hwdev, nic_dev->rss_indir);
+ }
+
+ if (!netif_running(netdev))
+ return 0;
+
+ err = hinic3_set_hw_rss_parameters(netdev, 1);
+ if (err)
+ netdev_err(netdev,
+ "Failed to update RSS parameters after changing channels\n");
+
+ return err;
+}
+
+int hinic3_set_channels(struct net_device *netdev,
+ struct ethtool_channels *channels)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ unsigned int count = channels->combined_count;
+ struct hinic3_dyna_txrxq_params q_params;
+ u16 old_num_qps;
+ int err;
+
+ err = hinic3_validate_channel_parameter(netdev, channels);
+ if (err)
+ return err;
+
+ if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags)) {
+ netdev_err(netdev, "This function doesn't support RSS, only support 1 queue pair\n");
+ return -EOPNOTSUPP;
+ }
+
+ old_num_qps = nic_dev->q_params.num_qps;
+
+ netdev_dbg(netdev, "Set max combined queue number from %u to %u\n",
+ old_num_qps, count);
+
+ if (netif_running(netdev)) {
+ q_params = nic_dev->q_params;
+ q_params.num_qps = (u16)count;
+ q_params.txqs_res = NULL;
+ q_params.rxqs_res = NULL;
+ q_params.irq_cfg = NULL;
+
+ err = hinic3_change_channel_settings(netdev, &q_params);
+ if (err) {
+ netdev_err(netdev, "Failed to change channel settings\n");
+ return err;
+ }
+ } else {
+ nic_dev->q_params.num_qps = (u16)count;
+ }
+
+ err = hinic3_rss_update_num_qps_and_reprogram(netdev);
+ if (err) {
+ if (netif_running(netdev)) {
+ q_params.num_qps = old_num_qps;
+ if (hinic3_change_channel_settings(netdev, &q_params))
+ netdev_err(netdev, "Failed to rollback channel settings after RSS error\n");
+ } else {
+ nic_dev->q_params.num_qps = old_num_qps;
+ }
+ return err;
+ }
+
+ return 0;
+}
+
+u32 hinic3_get_rxfh_indir_size(struct net_device *netdev)
+{
+ return L2NIC_RSS_INDIR_SIZE;
+}
+
+static int hinic3_set_rss_rxfh(struct net_device *netdev,
+ const u32 *indir, u8 *key)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u16 temp_indir[L2NIC_RSS_INDIR_SIZE];
+ int err;
+ u32 i;
+
+ if (indir) {
+ for (i = 0; i < L2NIC_RSS_INDIR_SIZE; i++)
+ temp_indir[i] = (u16)indir[i];
+
+ err = hinic3_rss_set_indir_tbl(nic_dev->hwdev, temp_indir);
+ if (err) {
+ netdev_err(netdev, "Failed to set rss indir table\n");
+ return err;
+ }
+
+ memcpy(nic_dev->rss_indir, temp_indir, sizeof(temp_indir));
+ }
+
+ if (key) {
+ err = hinic3_rss_set_hash_key(nic_dev->hwdev, key);
+ if (err) {
+ netdev_err(netdev, "Failed to set rss key\n");
+ return err;
+ }
+
+ memcpy(nic_dev->rss_hkey, key, L2NIC_RSS_KEY_SIZE);
+ }
+
+ return 0;
+}
+
+u32 hinic3_get_rxfh_key_size(struct net_device *netdev)
+{
+ return L2NIC_RSS_KEY_SIZE;
+}
+
+static int hinic3_rss_get_indir_tbl(struct hinic3_hwdev *hwdev,
+ u32 *indir_table)
+{
+ struct hinic3_cmd_buf_pair pair;
+ __le16 *indir_tbl = NULL;
+ int err, i;
+
+ err = hinic3_cmd_buf_pair_init(hwdev, &pair);
+ if (err) {
+ dev_err(hwdev->dev, "Failed to allocate cmd_buf.\n");
+ return err;
+ }
+
+ memset(pair.in->buf, 0, le16_to_cpu(pair.in->size));
+
+ err = hinic3_cmdq_detail_resp(hwdev, MGMT_MOD_L2NIC,
+ L2NIC_UCODE_CMD_GET_RSS_INDIR_TBL,
+ pair.in, pair.out, NULL);
+ if (err) {
+ dev_err(hwdev->dev, "Failed to get rss indir table\n");
+ goto err_get_indir_tbl;
+ }
+
+ indir_tbl = (__force __le16 *)pair.out->buf;
+ for (i = 0; i < L2NIC_RSS_INDIR_SIZE; i++)
+ indir_table[i] = le16_to_cpu(*(indir_tbl + i));
+
+err_get_indir_tbl:
+ hinic3_cmd_buf_pair_uninit(hwdev, &pair);
+
+ return err;
+}
+
+int hinic3_get_rxfh(struct net_device *netdev,
+ struct ethtool_rxfh_param *rxfh)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ int err = 0;
+
+ if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags)) {
+ netdev_err(netdev, "Rss is disabled\n");
+ return -EOPNOTSUPP;
+ }
+
+ rxfh->hfunc =
+ nic_dev->rss_hash_type == HINIC3_RSS_HASH_ENGINE_TYPE_XOR ?
+ ETH_RSS_HASH_XOR : ETH_RSS_HASH_TOP;
+
+ if (rxfh->indir) {
+ err = hinic3_rss_get_indir_tbl(nic_dev->hwdev, rxfh->indir);
+ if (err)
+ return err;
+ }
+
+ if (rxfh->key)
+ memcpy(rxfh->key, nic_dev->rss_hkey, L2NIC_RSS_KEY_SIZE);
+
+ return err;
+}
+
+static int hinic3_update_hash_func_type(struct net_device *netdev, u8 hfunc)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ enum hinic3_rss_hash_type new_rss_hash_type;
+ int err;
+
+ switch (hfunc) {
+ case ETH_RSS_HASH_NO_CHANGE:
+ return 0;
+ case ETH_RSS_HASH_XOR:
+ new_rss_hash_type = HINIC3_RSS_HASH_ENGINE_TYPE_XOR;
+ break;
+ case ETH_RSS_HASH_TOP:
+ new_rss_hash_type = HINIC3_RSS_HASH_ENGINE_TYPE_TOEP;
+ break;
+ default:
+ netdev_err(netdev, "Unsupported hash func %u\n", hfunc);
+ return -EOPNOTSUPP;
+ }
+
+ if (new_rss_hash_type == nic_dev->rss_hash_type)
+ return 0;
+
+ err = hinic3_rss_set_hash_type(nic_dev->hwdev, new_rss_hash_type);
+ if (err) {
+ netdev_err(netdev, "Failed to set RSS hash type to HW\n");
+ return err;
+ }
+
+ nic_dev->rss_hash_type = new_rss_hash_type;
+
+ return 0;
+}
+
+int hinic3_set_rxfh(struct net_device *netdev,
+ struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+ u8 old_hfunc;
+ int err;
+
+ if (!test_bit(HINIC3_RSS_ENABLE, &nic_dev->flags)) {
+ netdev_err(netdev, "Not support to set rss parameters when rss is disable\n");
+ return -EOPNOTSUPP;
+ }
+
+ old_hfunc = nic_dev->rss_hash_type;
+
+ err = hinic3_update_hash_func_type(netdev, rxfh->hfunc);
+ if (err)
+ return err;
+
+ err = hinic3_set_rss_rxfh(netdev, rxfh->indir, rxfh->key);
+ if (err)
+ hinic3_update_hash_func_type(netdev, old_hfunc);
+
+ return err;
+}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h
index 78d82c2aca0653..cf48a644940490 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rss.h
@@ -5,10 +5,30 @@
#define _HINIC3_RSS_H_
#include <linux/netdevice.h>
+#include <linux/ethtool.h>
int hinic3_rss_init(struct net_device *netdev);
void hinic3_rss_uninit(struct net_device *netdev);
void hinic3_try_to_enable_rss(struct net_device *netdev);
void hinic3_clear_rss_config(struct net_device *netdev);
+int hinic3_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs);
+int hinic3_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd);
+
+void hinic3_get_channels(struct net_device *netdev,
+ struct ethtool_channels *channels);
+int hinic3_set_channels(struct net_device *netdev,
+ struct ethtool_channels *channels);
+
+u32 hinic3_get_rxfh_indir_size(struct net_device *netdev);
+u32 hinic3_get_rxfh_key_size(struct net_device *netdev);
+
+u32 hinic3_get_rx_ring_count(struct net_device *netdev);
+int hinic3_get_rxfh(struct net_device *netdev,
+ struct ethtool_rxfh_param *rxfh);
+int hinic3_set_rxfh(struct net_device *netdev,
+ struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack);
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c
index 309ab590137979..a83a40202e356d 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.c
@@ -28,10 +28,6 @@
#define HINIC3_LRO_PKT_HDR_LEN_IPV4 66
#define HINIC3_LRO_PKT_HDR_LEN_IPV6 86
-#define HINIC3_LRO_PKT_HDR_LEN(cqe) \
- (RQ_CQE_OFFOLAD_TYPE_GET((cqe)->offload_type, IP_TYPE) == \
- HINIC3_RX_IPV6_PKT ? HINIC3_LRO_PKT_HDR_LEN_IPV6 : \
- HINIC3_LRO_PKT_HDR_LEN_IPV4)
static void hinic3_rxq_clean_stats(struct hinic3_rxq_stats *rxq_stats)
{
@@ -46,7 +42,6 @@ static void hinic3_rxq_clean_stats(struct hinic3_rxq_stats *rxq_stats)
rxq_stats->alloc_skb_err = 0;
rxq_stats->alloc_rx_buf_err = 0;
- rxq_stats->restore_drop_sge = 0;
u64_stats_update_end(&rxq_stats->syncp);
}
@@ -155,8 +150,12 @@ static u32 hinic3_rx_fill_buffers(struct hinic3_rxq *rxq)
err = rx_alloc_mapped_page(rxq->page_pool, rx_info,
rxq->buf_len);
- if (unlikely(err))
+ if (unlikely(err)) {
+ u64_stats_update_begin(&rxq->rxq_stats.syncp);
+ rxq->rxq_stats.alloc_rx_buf_err++;
+ u64_stats_update_end(&rxq->rxq_stats.syncp);
break;
+ }
dma_addr = page_pool_get_dma_addr(rx_info->page) +
rx_info->page_offset;
@@ -170,6 +169,10 @@ static u32 hinic3_rx_fill_buffers(struct hinic3_rxq *rxq)
rxq->next_to_update << HINIC3_NORMAL_RQ_WQE);
rxq->delta -= i;
rxq->next_to_alloc = rxq->next_to_update;
+ } else if (free_wqebbs == rxq->q_depth - 1) {
+ u64_stats_update_begin(&rxq->rxq_stats.syncp);
+ rxq->rxq_stats.rx_buf_empty++;
+ u64_stats_update_end(&rxq->rxq_stats.syncp);
}
return i;
@@ -330,11 +333,24 @@ static void hinic3_rx_csum(struct hinic3_rxq *rxq, u32 offload_type,
struct net_device *netdev = rxq->netdev;
bool l2_tunnel;
+ /* HW ensures that only 1 bit is set in csum_err. */
+ if (unlikely(csum_err == HINIC3_RX_CSUM_IPSU_OTHER_ERR)) {
+ u64_stats_update_begin(&rxq->rxq_stats.syncp);
+ rxq->rxq_stats.other_errors++;
+ u64_stats_update_end(&rxq->rxq_stats.syncp);
+ }
+
if (!(netdev->features & NETIF_F_RXCSUM))
return;
if (unlikely(csum_err)) {
/* pkt type is recognized by HW, and csum is wrong */
+ if (csum_err != HINIC3_RX_CSUM_HW_CHECK_NONE &&
+ csum_err != HINIC3_RX_CSUM_IPSU_OTHER_ERR) {
+ u64_stats_update_begin(&rxq->rxq_stats.syncp);
+ rxq->rxq_stats.csum_errors++;
+ u64_stats_update_end(&rxq->rxq_stats.syncp);
+ }
skb->ip_summed = CHECKSUM_NONE;
return;
}
@@ -387,8 +403,12 @@ static int recv_one_pkt(struct hinic3_rxq *rxq, struct hinic3_rq_cqe *rx_cqe,
u16 num_lro;
skb = hinic3_fetch_rx_buffer(rxq, pkt_len);
- if (unlikely(!skb))
+ if (unlikely(!skb)) {
+ u64_stats_update_begin(&rxq->rxq_stats.syncp);
+ rxq->rxq_stats.alloc_skb_err++;
+ u64_stats_update_end(&rxq->rxq_stats.syncp);
return -ENOMEM;
+ }
/* place header in linear portion of buffer */
if (skb_is_nonlinear(skb))
@@ -541,7 +561,7 @@ int hinic3_configure_rxqs(struct net_device *netdev, u16 num_rq,
rq_associate_cqes(rxq);
pkts = hinic3_rx_fill_buffers(rxq);
- if (!pkts) {
+ if (pkts < rxq->q_mask) {
netdev_err(netdev, "Failed to fill Rx buffer\n");
return -ENOMEM;
}
@@ -550,11 +570,29 @@ int hinic3_configure_rxqs(struct net_device *netdev, u16 num_rq,
return 0;
}
+void hinic3_rxq_get_stats(struct hinic3_rxq *rxq,
+ struct hinic3_rxq_stats *stats)
+{
+ struct hinic3_rxq_stats *rxq_stats = &rxq->rxq_stats;
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin(&rxq_stats->syncp);
+ stats->csum_errors = rxq_stats->csum_errors;
+ stats->other_errors = rxq_stats->other_errors;
+ stats->rx_buf_empty = rxq_stats->rx_buf_empty;
+ stats->alloc_skb_err = rxq_stats->alloc_skb_err;
+ stats->alloc_rx_buf_err = rxq_stats->alloc_rx_buf_err;
+ } while (u64_stats_fetch_retry(&rxq_stats->syncp, start));
+}
+
int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget)
{
struct hinic3_nic_dev *nic_dev = netdev_priv(rxq->netdev);
+ u32 ip_type, offload_type, pkt_hdr_len;
u32 sw_ci, status, pkt_len, vlan_len;
struct hinic3_rq_cqe *rx_cqe;
+ u64 rx_bytes = 0;
u32 num_wqe = 0;
int nr_pkts = 0;
u16 num_lro;
@@ -574,10 +612,20 @@ int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget)
if (recv_one_pkt(rxq, rx_cqe, pkt_len, vlan_len, status))
break;
+ rx_bytes += pkt_len;
nr_pkts++;
num_lro = RQ_CQE_STATUS_GET(status, NUM_LRO);
- if (num_lro)
+ if (num_lro) {
+ offload_type = le32_to_cpu(rx_cqe->offload_type);
+ ip_type = RQ_CQE_OFFOLAD_TYPE_GET(offload_type,
+ IP_TYPE);
+ pkt_hdr_len = ip_type == HINIC3_RX_IPV6_PKT ?
+ HINIC3_LRO_PKT_HDR_LEN_IPV6 :
+ HINIC3_LRO_PKT_HDR_LEN_IPV4;
+
+ rx_bytes += (num_lro - 1) * pkt_hdr_len;
num_wqe += hinic3_get_sge_num(rxq, pkt_len);
+ }
rx_cqe->status = 0;
@@ -588,5 +636,10 @@ int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget)
if (rxq->delta >= HINIC3_RX_BUFFER_WRITE)
hinic3_rx_fill_buffers(rxq);
+ u64_stats_update_begin(&rxq->rxq_stats.syncp);
+ rxq->rxq_stats.packets += (u64)nr_pkts;
+ rxq->rxq_stats.bytes += rx_bytes;
+ u64_stats_update_end(&rxq->rxq_stats.syncp);
+
return nr_pkts;
}
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
index 06d1b3299e7cf4..2ab691ed11a93b 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_rx.h
@@ -8,6 +8,17 @@
#include <linux/dim.h>
#include <linux/netdevice.h>
+/* rx cqe checksum err */
+#define HINIC3_RX_CSUM_IP_CSUM_ERR BIT(0)
+#define HINIC3_RX_CSUM_TCP_CSUM_ERR BIT(1)
+#define HINIC3_RX_CSUM_UDP_CSUM_ERR BIT(2)
+#define HINIC3_RX_CSUM_IGMP_CSUM_ERR BIT(3)
+#define HINIC3_RX_CSUM_ICMPV4_CSUM_ERR BIT(4)
+#define HINIC3_RX_CSUM_ICMPV6_CSUM_ERR BIT(5)
+#define HINIC3_RX_CSUM_SCTP_CRC_ERR BIT(6)
+#define HINIC3_RX_CSUM_HW_CHECK_NONE BIT(7)
+#define HINIC3_RX_CSUM_IPSU_OTHER_ERR BIT(8)
+
#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK GENMASK(4, 0)
#define RQ_CQE_OFFOLAD_TYPE_IP_TYPE_MASK GENMASK(6, 5)
#define RQ_CQE_OFFOLAD_TYPE_TUNNEL_PKT_FORMAT_MASK GENMASK(11, 8)
@@ -39,7 +50,6 @@ struct hinic3_rxq_stats {
u64 rx_buf_empty;
u64 alloc_skb_err;
u64 alloc_rx_buf_err;
- u64 restore_drop_sge;
struct u64_stats_sync syncp;
};
@@ -101,9 +111,6 @@ struct hinic3_rxq {
dma_addr_t cqe_start_paddr;
struct dim dim;
-
- u8 last_coalesc_timer_cfg;
- u8 last_pending_limit;
} ____cacheline_aligned;
struct hinic3_dyna_rxq_res {
@@ -123,6 +130,9 @@ void hinic3_free_rxqs_res(struct net_device *netdev, u16 num_rq,
u32 rq_depth, struct hinic3_dyna_rxq_res *rxqs_res);
int hinic3_configure_rxqs(struct net_device *netdev, u16 num_rq,
u32 rq_depth, struct hinic3_dyna_rxq_res *rxqs_res);
+
+void hinic3_rxq_get_stats(struct hinic3_rxq *rxq,
+ struct hinic3_rxq_stats *stats);
int hinic3_rx_poll(struct hinic3_rxq *rxq, int budget);
#endif
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c
index 9306bf0020cafe..5b7075e9fe5dca 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.c
@@ -97,8 +97,12 @@ static int hinic3_tx_map_skb(struct net_device *netdev, struct sk_buff *skb,
dma_info[0].dma = dma_map_single(&pdev->dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
- if (dma_mapping_error(&pdev->dev, dma_info[0].dma))
+ if (dma_mapping_error(&pdev->dev, dma_info[0].dma)) {
+ u64_stats_update_begin(&txq->txq_stats.syncp);
+ txq->txq_stats.map_frag_err++;
+ u64_stats_update_end(&txq->txq_stats.syncp);
return -EFAULT;
+ }
dma_info[0].len = skb_headlen(skb);
@@ -117,6 +121,9 @@ static int hinic3_tx_map_skb(struct net_device *netdev, struct sk_buff *skb,
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, dma_info[idx].dma)) {
+ u64_stats_update_begin(&txq->txq_stats.syncp);
+ txq->txq_stats.map_frag_err++;
+ u64_stats_update_end(&txq->txq_stats.syncp);
err = -EFAULT;
goto err_unmap_page;
}
@@ -260,9 +267,11 @@ static int hinic3_tx_csum(struct hinic3_txq *txq, struct hinic3_sq_task *task,
if (l4_proto != IPPROTO_UDP ||
((struct udphdr *)skb_transport_header(skb))->dest !=
VXLAN_OFFLOAD_PORT_LE) {
+ u64_stats_update_begin(&txq->txq_stats.syncp);
+ txq->txq_stats.unknown_tunnel_pkt++;
+ u64_stats_update_end(&txq->txq_stats.syncp);
/* Unsupported tunnel packet, disable csum offload */
- skb_checksum_help(skb);
- return 0;
+ return skb_checksum_help(skb);
}
}
@@ -412,6 +421,10 @@ static u32 hinic3_tx_offload(struct sk_buff *skb, struct hinic3_sq_task *task,
offload |= HINIC3_TX_OFFLOAD_TSO;
} else {
tso_cs_en = hinic3_tx_csum(txq, task, skb);
+ if (tso_cs_en < 0) {
+ offload = HINIC3_TX_OFFLOAD_INVALID;
+ return offload;
+ }
if (tso_cs_en)
offload |= HINIC3_TX_OFFLOAD_CSUM;
}
@@ -433,6 +446,27 @@ static u32 hinic3_tx_offload(struct sk_buff *skb, struct hinic3_sq_task *task,
return offload;
}
+static void hinic3_get_pkt_stats(struct hinic3_txq *txq, struct sk_buff *skb)
+{
+ u32 hdr_len, tx_bytes;
+ unsigned short pkts;
+
+ if (skb_is_gso(skb)) {
+ hdr_len = (skb_shinfo(skb)->gso_segs - 1) *
+ skb_tcp_all_headers(skb);
+ tx_bytes = skb->len + hdr_len;
+ pkts = skb_shinfo(skb)->gso_segs;
+ } else {
+ tx_bytes = skb->len > ETH_ZLEN ? skb->len : ETH_ZLEN;
+ pkts = 1;
+ }
+
+ u64_stats_update_begin(&txq->txq_stats.syncp);
+ txq->txq_stats.bytes += tx_bytes;
+ txq->txq_stats.packets += pkts;
+ u64_stats_update_end(&txq->txq_stats.syncp);
+}
+
static u16 hinic3_get_and_update_sq_owner(struct hinic3_io_queue *sq,
u16 curr_pi, u16 wqebb_cnt)
{
@@ -539,8 +573,12 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb,
int err;
if (unlikely(skb->len < MIN_SKB_LEN)) {
- if (skb_pad(skb, MIN_SKB_LEN - skb->len))
+ if (skb_pad(skb, MIN_SKB_LEN - skb->len)) {
+ u64_stats_update_begin(&txq->txq_stats.syncp);
+ txq->txq_stats.skb_pad_err++;
+ u64_stats_update_end(&txq->txq_stats.syncp);
goto err_out;
+ }
skb->len = MIN_SKB_LEN;
}
@@ -595,6 +633,7 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb,
txq->tx_stop_thrs,
txq->tx_start_thrs);
+ hinic3_get_pkt_stats(txq, skb);
hinic3_prepare_sq_ctrl(&wqe_combo, queue_info, num_sge, owner);
hinic3_write_db(txq->sq, 0, DB_CFLAG_DP_SQ,
hinic3_get_sq_local_pi(txq->sq));
@@ -604,6 +643,10 @@ static netdev_tx_t hinic3_send_one_skb(struct sk_buff *skb,
err_drop_pkt:
dev_kfree_skb_any(skb);
err_out:
+ u64_stats_update_begin(&txq->txq_stats.syncp);
+ txq->txq_stats.dropped++;
+ u64_stats_update_end(&txq->txq_stats.syncp);
+
return NETDEV_TX_OK;
}
@@ -754,6 +797,24 @@ int hinic3_configure_txqs(struct net_device *netdev, u16 num_sq,
return 0;
}
+void hinic3_txq_get_stats(struct hinic3_txq *txq,
+ struct hinic3_txq_stats *stats)
+{
+ struct hinic3_txq_stats *txq_stats = &txq->txq_stats;
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin(&txq_stats->syncp);
+ stats->busy = txq_stats->busy;
+ stats->skb_pad_err = txq_stats->skb_pad_err;
+ stats->frag_len_overflow = txq_stats->frag_len_overflow;
+ stats->offload_cow_skb_err = txq_stats->offload_cow_skb_err;
+ stats->map_frag_err = txq_stats->map_frag_err;
+ stats->unknown_tunnel_pkt = txq_stats->unknown_tunnel_pkt;
+ stats->frag_size_err = txq_stats->frag_size_err;
+ } while (u64_stats_fetch_retry(&txq_stats->syncp, start));
+}
+
bool hinic3_tx_poll(struct hinic3_txq *txq, int budget)
{
struct net_device *netdev = txq->netdev;
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
index 00194f2a1bcc1a..0a21c423618fe8 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_tx.h
@@ -157,6 +157,8 @@ int hinic3_configure_txqs(struct net_device *netdev, u16 num_sq,
u32 sq_depth, struct hinic3_dyna_txq_res *txqs_res);
netdev_tx_t hinic3_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
+void hinic3_txq_get_stats(struct hinic3_txq *txq,
+ struct hinic3_txq_stats *stats);
bool hinic3_tx_poll(struct hinic3_txq *txq, int budget);
void hinic3_flush_txqs(struct net_device *netdev);
|
| 3/2 | 2026/07/20 17:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38622 output: 35 thoughts: 620 Thoughts: **Fuzzing the hinic3 Driver: A Targeted Approach** Okay, so we have a patch applied to the `hinic3` driver, specifically within `drivers/net/ethernet/huawei/hinic3/`. My initial assessment is that this warrants serious fuzzing attention. The scope of this change is clearly significant; it's added a substantial set of ethtool operations. Let's break this down. We're talking about comprehensive additions: ring parameter management (`get_ringparam`, `set_ringparam`), statistical data retrieval (`get_sset_count`, `get_ethtool_stats`, `get_strings`, and various PHY, MAC, control, RMON, pause stats), flow control and RSS configuration (`get_coalesce`, `set_coalesce`, `get_rxnfc`, `set_rxnfc`, `get_rxfh_indir_size`, `get_rxfh_key_size`, `get_rxfh`, `set_rxfh`), channel settings (`get_channels`, `set_channels`), and potentially ring buffer counts. Plus, there are modifications in the tx/rx paths as well as related functions such as `hinic3_change_channel_settings`, `hinic3_set_channels`, `hinic3_set_rss_rxfh`, `hinic3_set_rss_hash_opts`, and `hinic3_set_coalesce`. Given my experience, that's a lot of new surface area for potential vulnerabilities. The first step is to identify the critical functions to target. We're going to want to focus our fuzzing efforts on the ethtool callbacks, and the modified functions, specifically the following: * `hinic3_get_ringparam` * `hinic3_set_ringparam` * `hinic3_get_sset_count` * `hinic3_get_ethtool_stats` * `hinic3_get_strings` * `hinic3_get_eth_phy_stats` * `hinic3_get_eth_mac_stats` * `hinic3_get_eth_ctrl_stats` * `hinic3_get_rmon_stats` * `hinic3_get_pause_stats` * `hinic3_get_coalesce` * `hinic3_set_coalesce` * `hinic3_get_rxnfc` * `hinic3_set_rxnfc` * `hinic3_get_rx_ring_count` * `hinic3_get_channels` * `hinic3_set_channels` * `hinic3_get_rxfh_indir_size` * `hinic3_get_rxfh_key_size` * `hinic3_get_rxfh` * `hinic3_set_rxfh` * `hinic3_change_channel_settings` Before we delve into the fuzzing itself, we also need to check the relevant Kconfig options to determine how this driver is enabled in different configurations. That will influence how we set up the testing environment and prioritize the fuzzing targets. |
| 4/2 | 2026/07/20 17:19 | tool | grepper |
0mArgs:{
"Expression": "hinic3",
"PathPrefix": "drivers/net/ethernet/huawei/Kconfig"
}Results: {
"Output": "drivers/net/ethernet/huawei/Kconfig=18=source \"drivers/net/ethernet/huawei/hinic/Kconfig\"\ndrivers/net/ethernet/huawei/Kconfig:19:source \"drivers/net/ethernet/huawei/hinic3/Kconfig\"\ndrivers/net/ethernet/huawei/Kconfig-20-\n"
} |
| 5/2 | 2026/07/20 17:19 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 40119 output: 43 thoughts: 22 Thoughts: Here's a summary of my thought process, as if I were analyzing that Kconfig file: **Investigating the hinic3 Kconfig** Okay, time to dive into `drivers/net/ethernet/huawei/hinic3/Kconfig`. I need to understand the configuration options for this Huawei network interface controller. I'm assuming this Kconfig file will define the build-time configurations, including dependencies, default values, and help texts for all the features related to this driver. I'll need to see what's exposed to the user and what's handled internally. I'll be looking for things like: * **Dependencies:** What other kernel features or hardware components does this driver rely on? Are there any specific hardware revisions or firmware versions? * **Options:** What granular options are presented to the user? Does it expose options for enabling/disabling specific features, varying buffer sizes, or controlling debugging outputs? Are there options related to specific hardware capabilities, like SR-IOV or RDMA? * **Defaults:** What are the default values for each configuration option? This will give me insight into the typical use case and performance profile. * **Help Texts:** The help texts should provide valuable context and explain the purpose of each option. I'll be paying close attention to these to understand the implications of each setting. * **Conditional Compilation:** I expect to see `if`, `select`, and `depends on` statements. I'll be looking at the logic to see how features are enabled or disabled based on other configuration settings or hardware capabilities. * **Triggers for Debug:** Do the options offer different levels of logging and debugging? * **Hardware Variants**: Does it have different options for different hardware versions, and different modes of operations? I'll be mentally parsing the structure to identify any potential gotchas or configuration conflicts. It's time to get a good handle on how this NIC is configured. |
| 6/2 | 2026/07/20 17:20 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/huawei/hinic3/Kconfig",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# Huawei driver configuration\n 4:\t#\n 5:\t\n 6:\tconfig HINIC3\n 7:\t\ttristate \"Huawei 3rd generation network adapters (HINIC3) support\"\n 8:\t\t# Fields of HW and management structures are little endian and are\n 9:\t\t# currently not converted\n 10:\t\tdepends on !CPU_BIG_ENDIAN\n 11:\t\tdepends on X86 || ARM64 || COMPILE_TEST\n 12:\t\tdepends on PCI_MSI \u0026\u0026 64BIT\n 13:\t\tselect AUXILIARY_BUS\n 14:\t\tselect DIMLIB\n 15:\t\tselect PAGE_POOL\n 16:\t\thelp\n 17:\t\t This driver supports HiNIC 3rd gen Network Adapter (HINIC3).\n 18:\t\t The driver is supported on X86_64 and ARM64 little endian.\n 19:\t\n 20:\t\t To compile this driver as a module, choose M here.\n 21:\t\t The module will be called hinic3.\n"
} |
| 7/2 | 2026/07/20 17:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 40895 output: 51 thoughts: 245 Thoughts: **Analyzing the HINIC3 Configuration** Okay, so the configuration I'm working with is `HINIC3`. First, I'll quickly scan the file to confirm that this is the *only* configuration present. Good, nothing else is defined. Now, let's get down to brass tacks: I need to identify the key functions related to this configuration. I've got a list here: `hinic3_set_ringparam`, `hinic3_set_coalesce`, `hinic3_set_rxnfc`, `hinic3_set_channels`, `hinic3_set_rxfh`, `hinic3_change_channel_settings`, `hinic3_get_ethtool_stats`, `hinic3_get_strings`, `hinic3_get_eth_phy_stats`, `hinic3_get_eth_mac_stats`, `hinic3_get_eth_ctrl_stats`, `hinic3_get_rmon_stats`, `hinic3_get_pause_stats`, `hinic3_get_coalesce`, `hinic3_get_rxnfc`, and `hinic3_get_rxfh`. Time to double-check that these functions are actually implemented and accessible. |
| 8/2 | 2026/07/20 17:20 | tool | grepper |
0mArgs:{
"Expression": "hinic3_set_ringparam",
"PathPrefix": "drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c"
}Results: {
"Output": "drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c=448=static int hinic3_check_ringparam_valid(struct net_device *netdev,\n--\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c-464-\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c:465:static int hinic3_set_ringparam(struct net_device *netdev,\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c-466-\t\t\t\tstruct ethtool_ringparam *ring,\n--\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c=1254=static const struct ethtool_ops hinic3_ethtool_ops = {\n--\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c-1265-\t.get_ringparam = hinic3_get_ringparam,\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c:1266:\t.set_ringparam = hinic3_set_ringparam,\ndrivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c-1267-\t.get_sset_count = hinic3_get_sset_count,\n"
} |
| 9/2 | 2026/07/20 17:20 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 41851 output: 296 thoughts: 9 Thoughts: Okay, here's the summary, assuming I'm the expert and considering how I would frame my own internal thought process: **Setting the Results: A Quick Assessment** Alright, that's what I needed to see. The data aligns with what I anticipated, no glaring red flags. It looks clean, the methodology is sound, and the initial analysis appears robust. Based on this, I'm confident in what I've seen. Time to finalize the results. I'll get to work and set the results. |
| 10/2 | 2026/07/20 17:20 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"HINIC3"
],
"FocusSymbols": [
"hinic3_set_ringparam",
"hinic3_set_coalesce",
"hinic3_set_rxnfc",
"hinic3_set_channels",
"hinic3_set_rxfh",
"hinic3_change_channel_settings",
"hinic3_get_ethtool_stats",
"hinic3_get_strings",
"hinic3_get_eth_phy_stats",
"hinic3_get_eth_mac_stats",
"hinic3_get_eth_ctrl_stats",
"hinic3_get_rmon_stats",
"hinic3_get_pause_stats",
"hinic3_get_coalesce",
"hinic3_get_rxnfc",
"hinic3_get_rxfh"
],
"Reasoning": "The patch adds multiple ethtool operations (ringparam, coalesce, rxnfc, channels, rxfh, stats) to the hinic3 network driver. These operations can be triggered via netlink/ioctl from userspace and modify the driver's internal state, so they should be fuzzed.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"HINIC3"
],
"FocusSymbols": [
"hinic3_set_ringparam",
"hinic3_set_coalesce",
"hinic3_set_rxnfc",
"hinic3_set_channels",
"hinic3_set_rxfh",
"hinic3_change_channel_settings",
"hinic3_get_ethtool_stats",
"hinic3_get_strings",
"hinic3_get_eth_phy_stats",
"hinic3_get_eth_mac_stats",
"hinic3_get_eth_ctrl_stats",
"hinic3_get_rmon_stats",
"hinic3_get_pause_stats",
"hinic3_get_coalesce",
"hinic3_get_rxnfc",
"hinic3_get_rxfh"
],
"Reasoning": "The patch adds multiple ethtool operations (ringparam, coalesce, rxnfc, channels, rxfh, stats) to the hinic3 network driver. These operations can be triggered via netlink/ioctl from userspace and modify the driver's internal state, so they should be fuzzed.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|