From a0337c0dee686acf9b38d50abb923d13e27f7e83 Mon Sep 17 00:00:00 2001 From: Luo bin Date: Sun, 28 Jun 2020 20:36:21 +0800 Subject: hinic: add support to set and get irq coalesce add support to set TX/RX irq coalesce params with ethtool -C and get these params with ethtool -c. Signed-off-by: Luo bin Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/net/ethernet/huawei/hinic/hinic_dev.h') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 48b40be3e84d..75d6dee948f5 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -49,6 +49,12 @@ enum hinic_rss_hash_type { HINIC_RSS_HASH_ENGINE_TYPE_MAX, }; +struct hinic_intr_coal_info { + u8 pending_limt; + u8 coalesce_timer_cfg; + u8 resend_timer_cfg; +}; + struct hinic_dev { struct net_device *netdev; struct hinic_hwdev *hwdev; @@ -82,6 +88,8 @@ struct hinic_dev { struct hinic_rss_type rss_type; u8 *rss_hkey_user; s32 *rss_indir_user; + struct hinic_intr_coal_info *rx_intr_coalesce; + struct hinic_intr_coal_info *tx_intr_coalesce; struct hinic_sriov_info sriov_info; }; -- cgit v1.2.3 From 4aa218a4fe77d57edd068210da4c90dd19523954 Mon Sep 17 00:00:00 2001 From: Luo bin Date: Sun, 28 Jun 2020 20:36:22 +0800 Subject: hinic: add self test support add support to excute internal and external loopback test with ethtool -t cmd. Signed-off-by: Luo bin Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 6 + drivers/net/ethernet/huawei/hinic/hinic_ethtool.c | 177 ++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 3 + drivers/net/ethernet/huawei/hinic/hinic_port.c | 27 ++++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 16 ++ drivers/net/ethernet/huawei/hinic/hinic_rx.c | 39 +++++ drivers/net/ethernet/huawei/hinic/hinic_tx.c | 61 ++++++++ drivers/net/ethernet/huawei/hinic/hinic_tx.h | 2 + 8 files changed, 331 insertions(+) (limited to 'drivers/net/ethernet/huawei/hinic/hinic_dev.h') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 75d6dee948f5..9adb755f0820 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -20,11 +20,14 @@ #define HINIC_DRV_NAME "hinic" +#define LP_PKT_CNT 64 + enum hinic_flags { HINIC_LINK_UP = BIT(0), HINIC_INTF_UP = BIT(1), HINIC_RSS_ENABLE = BIT(2), HINIC_LINK_DOWN = BIT(3), + HINIC_LP_TEST = BIT(4), }; struct hinic_rx_mode_work { @@ -91,6 +94,9 @@ struct hinic_dev { struct hinic_intr_coal_info *rx_intr_coalesce; struct hinic_intr_coal_info *tx_intr_coalesce; struct hinic_sriov_info sriov_info; + int lb_test_rx_idx; + int lb_pkt_len; + u8 *lb_test_rx_buf; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index 8e5c326c7687..2c94b48692c9 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -133,6 +133,16 @@ static struct hw2ethtool_link_mode }, }; +#define LP_DEFAULT_TIME 5 /* seconds */ +#define LP_PKT_LEN 1514 + +#define PORT_DOWN_ERR_IDX 0 +enum diag_test_index { + INTERNAL_LP_TEST = 0, + EXTERNAL_LP_TEST = 1, + DIAG_TEST_MAX = 2, +}; + static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, enum hinic_speed speed) { @@ -1244,6 +1254,11 @@ static struct hinic_stats hinic_function_stats[] = { HINIC_FUNC_STAT(rx_err_vport), }; +static char hinic_test_strings[][ETH_GSTRING_LEN] = { + "Internal lb test (on/offline)", + "External lb test (external_lb)", +}; + #define HINIC_PORT_STAT(_stat_item) { \ .name = #_stat_item, \ .size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \ @@ -1453,6 +1468,8 @@ static int hinic_get_sset_count(struct net_device *netdev, int sset) int count, q_num; switch (sset) { + case ETH_SS_TEST: + return ARRAY_LEN(hinic_test_strings); case ETH_SS_STATS: q_num = nic_dev->num_qps; count = ARRAY_LEN(hinic_function_stats) + @@ -1475,6 +1492,9 @@ static void hinic_get_strings(struct net_device *netdev, u16 i, j; switch (stringset) { + case ETH_SS_TEST: + memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings)); + return; case ETH_SS_STATS: for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) { memcpy(p, hinic_function_stats[i].name, @@ -1508,6 +1528,162 @@ static void hinic_get_strings(struct net_device *netdev, } } +static int hinic_run_lp_test(struct hinic_dev *nic_dev, u32 test_time) +{ + u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf; + struct net_device *netdev = nic_dev->netdev; + struct sk_buff *skb_tmp = NULL; + struct sk_buff *skb = NULL; + u32 cnt = test_time * 5; + u8 *test_data = NULL; + u32 i; + u8 j; + + skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC); + if (!skb_tmp) + return -ENOMEM; + + test_data = __skb_put(skb_tmp, LP_PKT_LEN); + + memset(test_data, 0xFF, 2 * ETH_ALEN); + test_data[ETH_ALEN] = 0xFE; + test_data[2 * ETH_ALEN] = 0x08; + test_data[2 * ETH_ALEN + 1] = 0x0; + + for (i = ETH_HLEN; i < LP_PKT_LEN; i++) + test_data[i] = i & 0xFF; + + skb_tmp->queue_mapping = 0; + skb_tmp->ip_summed = CHECKSUM_COMPLETE; + skb_tmp->dev = netdev; + + for (i = 0; i < cnt; i++) { + nic_dev->lb_test_rx_idx = 0; + memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN); + + for (j = 0; j < LP_PKT_CNT; j++) { + skb = pskb_copy(skb_tmp, GFP_ATOMIC); + if (!skb) { + dev_kfree_skb_any(skb_tmp); + netif_err(nic_dev, drv, netdev, + "Copy skb failed for loopback test\n"); + return -ENOMEM; + } + + /* mark index for every pkt */ + skb->data[LP_PKT_LEN - 1] = j; + + if (hinic_lb_xmit_frame(skb, netdev)) { + dev_kfree_skb_any(skb); + dev_kfree_skb_any(skb_tmp); + netif_err(nic_dev, drv, netdev, + "Xmit pkt failed for loopback test\n"); + return -EBUSY; + } + } + + /* wait till all pkts received to RX buffer */ + msleep(200); + + for (j = 0; j < LP_PKT_CNT; j++) { + if (memcmp(lb_test_rx_buf + j * LP_PKT_LEN, + skb_tmp->data, LP_PKT_LEN - 1) || + (*(lb_test_rx_buf + j * LP_PKT_LEN + + LP_PKT_LEN - 1) != j)) { + dev_kfree_skb_any(skb_tmp); + netif_err(nic_dev, drv, netdev, + "Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n", + j + i * LP_PKT_CNT, + LP_PKT_LEN - 1, + *(lb_test_rx_buf + j * LP_PKT_LEN + + LP_PKT_LEN - 1)); + return -EIO; + } + } + } + + dev_kfree_skb_any(skb_tmp); + return 0; +} + +static int do_lp_test(struct hinic_dev *nic_dev, u32 flags, u32 test_time, + enum diag_test_index *test_index) +{ + struct net_device *netdev = nic_dev->netdev; + u8 *lb_test_rx_buf = NULL; + int err = 0; + + if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) { + *test_index = INTERNAL_LP_TEST; + if (hinic_set_loopback_mode(nic_dev->hwdev, + HINIC_INTERNAL_LP_MODE, true)) { + netif_err(nic_dev, drv, netdev, + "Failed to set port loopback mode before loopback test\n"); + return -EIO; + } + } else { + *test_index = EXTERNAL_LP_TEST; + } + + lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN); + if (!lb_test_rx_buf) { + err = -ENOMEM; + } else { + nic_dev->lb_test_rx_buf = lb_test_rx_buf; + nic_dev->lb_pkt_len = LP_PKT_LEN; + nic_dev->flags |= HINIC_LP_TEST; + err = hinic_run_lp_test(nic_dev, test_time); + nic_dev->flags &= ~HINIC_LP_TEST; + msleep(100); + vfree(lb_test_rx_buf); + nic_dev->lb_test_rx_buf = NULL; + } + + if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) { + if (hinic_set_loopback_mode(nic_dev->hwdev, + HINIC_INTERNAL_LP_MODE, false)) { + netif_err(nic_dev, drv, netdev, + "Failed to cancel port loopback mode after loopback test\n"); + err = -EIO; + } + } + + return err; +} + +static void hinic_diag_test(struct net_device *netdev, + struct ethtool_test *eth_test, u64 *data) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + enum diag_test_index test_index = 0; + int err = 0; + + memset(data, 0, DIAG_TEST_MAX * sizeof(u64)); + + /* don't support loopback test when netdev is closed. */ + if (!(nic_dev->flags & HINIC_INTF_UP)) { + netif_err(nic_dev, drv, netdev, + "Do not support loopback test when netdev is closed\n"); + eth_test->flags |= ETH_TEST_FL_FAILED; + data[PORT_DOWN_ERR_IDX] = 1; + return; + } + + netif_carrier_off(netdev); + + err = do_lp_test(nic_dev, eth_test->flags, LP_DEFAULT_TIME, + &test_index); + if (err) { + eth_test->flags |= ETH_TEST_FL_FAILED; + data[test_index] = 1; + } + + err = hinic_port_link_state(nic_dev, &link_state); + if (!err && link_state == HINIC_LINK_STATE_UP) + netif_carrier_on(netdev); +} + static const struct ethtool_ops hinic_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_RX_MAX_FRAMES | @@ -1537,6 +1713,7 @@ static const struct ethtool_ops hinic_ethtool_ops = { .get_sset_count = hinic_get_sset_count, .get_ethtool_stats = hinic_get_ethtool_stats, .get_strings = hinic_get_strings, + .self_test = hinic_diag_test, }; static const struct ethtool_ops hinicvf_ethtool_ops = { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index ed3cc154ce18..c92c39a50b81 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -97,6 +97,9 @@ enum hinic_port_cmd { HINIC_PORT_CMD_FWCTXT_INIT = 69, + HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72, + HINIC_PORT_CMD_SET_LOOPBACK_MODE, + HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78, HINIC_PORT_CMD_GET_MGMT_VERSION = 88, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 8b007a268675..98ea3f781611 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -1241,3 +1241,30 @@ int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap) return 0; } + +int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable) +{ + struct hinic_port_loopback lb = {0}; + u16 out_size = sizeof(lb); + int err; + + lb.mode = mode; + lb.en = enable; + + if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) { + dev_err(&hwdev->hwif->pdev->dev, + "Invalid loopback mode %d to set\n", mode); + return -EINVAL; + } + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE, + &lb, sizeof(lb), &lb, &out_size); + if (err || !out_size || lb.status) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n", + mode, enable, err, lb.status, out_size); + return -EIO; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 7b17460d4e2c..b21956d7232e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -652,6 +652,20 @@ struct hinic_set_pfc { u8 rsvd1[4]; }; +/* get or set loopback mode, need to modify by base API */ +#define HINIC_INTERNAL_LP_MODE 5 +#define LOOP_MODE_MIN 1 +#define LOOP_MODE_MAX 6 + +struct hinic_port_loopback { + u8 status; + u8 version; + u8 rsvd[6]; + + u32 mode; + u32 en; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -749,6 +763,8 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap); +int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable); + int hinic_open(struct net_device *netdev); int hinic_close(struct net_device *netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index c9a65a1f0347..5bee951fe9d4 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -316,6 +316,39 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb, return num_wqes; } +static void hinic_copy_lp_data(struct hinic_dev *nic_dev, + struct sk_buff *skb) +{ + struct net_device *netdev = nic_dev->netdev; + u8 *lb_buf = nic_dev->lb_test_rx_buf; + int lb_len = nic_dev->lb_pkt_len; + int pkt_offset, frag_len, i; + void *frag_data = NULL; + + if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) { + nic_dev->lb_test_rx_idx = 0; + netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n"); + } + + if (skb->len != nic_dev->lb_pkt_len) { + netif_warn(nic_dev, drv, netdev, "Wrong packet length\n"); + nic_dev->lb_test_rx_idx++; + return; + } + + pkt_offset = nic_dev->lb_test_rx_idx * lb_len; + frag_len = (int)skb_headlen(skb); + memcpy(lb_buf + pkt_offset, skb->data, frag_len); + pkt_offset += frag_len; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]); + frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]); + memcpy((lb_buf + pkt_offset), frag_data, frag_len); + pkt_offset += frag_len; + } + nic_dev->lb_test_rx_idx++; +} + /** * rxq_recv - Rx handler * @rxq: rx queue @@ -330,6 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) u64 pkt_len = 0, rx_bytes = 0; struct hinic_rq *rq = rxq->rq; struct hinic_rq_wqe *rq_wqe; + struct hinic_dev *nic_dev; unsigned int free_wqebbs; struct hinic_rq_cqe *cqe; int num_wqes, pkts = 0; @@ -342,6 +376,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) u32 vlan_len; u16 vid; + nic_dev = netdev_priv(netdev); + while (pkts < budget) { num_wqes = 0; @@ -384,6 +420,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); } + if (unlikely(nic_dev->flags & HINIC_LP_TEST)) + hinic_copy_lp_data(nic_dev, skb); + skb_record_rx_queue(skb, qp->q_id); skb->protocol = eth_type_trans(skb, rxq->netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 0f6d27f29de5..a97498ee6914 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -459,6 +459,67 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task, return 0; } +netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 prod_idx, q_id = skb->queue_mapping; + struct netdev_queue *netdev_txq; + int nr_sges, err = NETDEV_TX_OK; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + struct hinic_txq *txq; + struct hinic_qp *qp; + + txq = &nic_dev->txqs[q_id]; + qp = container_of(txq->sq, struct hinic_qp, sq); + nr_sges = skb_shinfo(skb)->nr_frags + 1; + + err = tx_map_skb(nic_dev, skb, txq->sges); + if (err) + goto skb_error; + + wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); + + sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); + if (!sq_wqe) { + netif_stop_subqueue(netdev, qp->q_id); + + sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); + if (sq_wqe) { + netif_wake_subqueue(nic_dev->netdev, qp->q_id); + goto process_sq_wqe; + } + + tx_unmap_skb(nic_dev, skb, txq->sges); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_busy++; + u64_stats_update_end(&txq->txq_stats.syncp); + err = NETDEV_TX_BUSY; + wqe_size = 0; + goto flush_skbs; + } + +process_sq_wqe: + hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); + hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); + +flush_skbs: + netdev_txq = netdev_get_tx_queue(netdev, q_id); + if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq))) + hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); + + return err; + +skb_error: + dev_kfree_skb_any(skb); + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_dropped++; + u64_stats_update_end(&txq->txq_stats.syncp); + + return NETDEV_TX_OK; +} + netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct hinic_dev *nic_dev = netdev_priv(netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h index f158b7db7fb8..b3c8657774a7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -44,6 +44,8 @@ void hinic_txq_clean_stats(struct hinic_txq *txq); void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats); +netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev); + netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, -- cgit v1.2.3 From 5e126e7c4e52754e3aac0fbc5325abcbe1629388 Mon Sep 17 00:00:00 2001 From: Luo bin Date: Wed, 15 Jul 2020 15:30:23 +0800 Subject: hinic: add firmware update support add support to update firmware by the devlink flashing API Signed-off-by: Luo bin Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/huawei/hinic/Makefile | 2 +- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 5 + drivers/net/ethernet/huawei/hinic/hinic_devlink.c | 328 ++++++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_devlink.h | 115 ++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 28 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 34 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 4 +- drivers/net/ethernet/huawei/hinic/hinic_main.c | 28 +- drivers/net/ethernet/huawei/hinic/hinic_port.h | 25 ++ 9 files changed, 565 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_devlink.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_devlink.h (limited to 'drivers/net/ethernet/huawei/hinic/hinic_dev.h') diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 32a011ca44c3..67b59d0ba769 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_HINIC) += hinic.o hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ hinic_hw_io.o hinic_hw_qp.o hinic_hw_cmdq.o hinic_hw_wq.o \ hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o \ - hinic_common.o hinic_ethtool.o hinic_hw_mbox.o hinic_sriov.o + hinic_common.o hinic_ethtool.o hinic_devlink.o hinic_hw_mbox.o hinic_sriov.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 9adb755f0820..befd925c03dc 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -97,6 +97,11 @@ struct hinic_dev { int lb_test_rx_idx; int lb_pkt_len; u8 *lb_test_rx_buf; + struct devlink *devlink; +}; + +struct hinic_devlink_priv { + struct hinic_hwdev *hwdev; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.c b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c new file mode 100644 index 000000000000..a40a10ac1ee9 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ +#include +#include +#include + +#include "hinic_dev.h" +#include "hinic_port.h" +#include "hinic_devlink.h" + +static bool check_image_valid(struct hinic_devlink_priv *priv, const u8 *buf, + u32 image_size, struct host_image_st *host_image) +{ + struct fw_image_st *fw_image = NULL; + u32 len = 0; + u32 i; + + fw_image = (struct fw_image_st *)buf; + + if (fw_image->fw_magic != HINIC_MAGIC_NUM) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_magic read from file, fw_magic: 0x%x\n", + fw_image->fw_magic); + return false; + } + + if (fw_image->fw_info.fw_section_cnt > MAX_FW_TYPE_NUM) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_type_num read from file, fw_type_num: 0x%x\n", + fw_image->fw_info.fw_section_cnt); + return false; + } + + for (i = 0; i < fw_image->fw_info.fw_section_cnt; i++) { + len += fw_image->fw_section_info[i].fw_section_len; + memcpy(&host_image->image_section_info[i], + &fw_image->fw_section_info[i], + sizeof(struct fw_section_info_st)); + } + + if (len != fw_image->fw_len || + (fw_image->fw_len + UPDATEFW_IMAGE_HEAD_SIZE) != image_size) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong data size read from file\n"); + return false; + } + + host_image->image_info.up_total_len = fw_image->fw_len; + host_image->image_info.fw_version = fw_image->fw_version; + host_image->section_type_num = fw_image->fw_info.fw_section_cnt; + host_image->device_id = fw_image->device_id; + + return true; +} + +static bool check_image_integrity(struct hinic_devlink_priv *priv, + struct host_image_st *host_image, + u32 update_type) +{ + u32 collect_section_type = 0; + u32 i, type; + + for (i = 0; i < host_image->section_type_num; i++) { + type = host_image->image_section_info[i].fw_section_type; + if (collect_section_type & (1U << type)) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Duplicate section type: %u\n", + type); + return false; + } + collect_section_type |= (1U << type); + } + + if (update_type == FW_UPDATE_COLD && + (((collect_section_type & _IMAGE_COLD_SUB_MODULES_MUST_IN) == + _IMAGE_COLD_SUB_MODULES_MUST_IN) || + collect_section_type == _IMAGE_CFG_SUB_MODULES_MUST_IN)) + return true; + + if (update_type == FW_UPDATE_HOT && + (collect_section_type & _IMAGE_HOT_SUB_MODULES_MUST_IN) == + _IMAGE_HOT_SUB_MODULES_MUST_IN) + return true; + + if (update_type == FW_UPDATE_COLD) + dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid: 0x%x or 0x%lx, current: 0x%x\n", + _IMAGE_COLD_SUB_MODULES_MUST_IN, + _IMAGE_CFG_SUB_MODULES_MUST_IN, collect_section_type); + else + dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid:0x%x, current: 0x%x\n", + _IMAGE_HOT_SUB_MODULES_MUST_IN, collect_section_type); + + return false; +} + +static int check_image_device_type(struct hinic_devlink_priv *priv, + u32 image_device_type) +{ + struct hinic_comm_board_info board_info = {0}; + + if (hinic_get_board_info(priv->hwdev, &board_info)) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Get board info failed\n"); + return false; + } + + if (image_device_type == board_info.info.board_type) + return true; + + dev_err(&priv->hwdev->hwif->pdev->dev, "The device type of upgrade file doesn't match the device type of current firmware, please check the upgrade file\n"); + dev_err(&priv->hwdev->hwif->pdev->dev, "The image device type: 0x%x, firmware device type: 0x%x\n", + image_device_type, board_info.info.board_type); + + return false; +} + +static int hinic_flash_fw(struct hinic_devlink_priv *priv, const u8 *data, + struct host_image_st *host_image) +{ + u32 section_remain_send_len, send_fragment_len, send_pos, up_total_len; + struct hinic_cmd_update_fw *fw_update_msg = NULL; + u32 section_type, section_crc, section_version; + u32 i, len, section_len, section_offset; + u16 out_size = sizeof(*fw_update_msg); + int total_len_flag = 0; + int err; + + fw_update_msg = kzalloc(sizeof(*fw_update_msg), GFP_KERNEL); + if (!fw_update_msg) + return -ENOMEM; + + up_total_len = host_image->image_info.up_total_len; + + for (i = 0; i < host_image->section_type_num; i++) { + len = host_image->image_section_info[i].fw_section_len; + if (host_image->image_section_info[i].fw_section_type == + UP_FW_UPDATE_BOOT) { + up_total_len = up_total_len - len; + break; + } + } + + for (i = 0; i < host_image->section_type_num; i++) { + section_len = + host_image->image_section_info[i].fw_section_len; + section_offset = + host_image->image_section_info[i].fw_section_offset; + section_remain_send_len = section_len; + section_type = + host_image->image_section_info[i].fw_section_type; + section_crc = host_image->image_section_info[i].fw_section_crc; + section_version = + host_image->image_section_info[i].fw_section_version; + + if (section_type == UP_FW_UPDATE_BOOT) + continue; + + send_fragment_len = 0; + send_pos = 0; + + while (section_remain_send_len > 0) { + if (!total_len_flag) { + fw_update_msg->total_len = up_total_len; + total_len_flag = 1; + } else { + fw_update_msg->total_len = 0; + } + + memset(fw_update_msg->data, 0, MAX_FW_FRAGMENT_LEN); + + fw_update_msg->ctl_info.SF = + (section_remain_send_len == section_len) ? + true : false; + fw_update_msg->section_info.FW_section_CRC = section_crc; + fw_update_msg->fw_section_version = section_version; + fw_update_msg->ctl_info.flag = UP_TYPE_A; + + if (section_type <= UP_FW_UPDATE_UP_DATA_B) { + fw_update_msg->section_info.FW_section_type = + (section_type % 2) ? + UP_FW_UPDATE_UP_DATA : + UP_FW_UPDATE_UP_TEXT; + + fw_update_msg->ctl_info.flag = UP_TYPE_B; + if (section_type <= UP_FW_UPDATE_UP_DATA_A) + fw_update_msg->ctl_info.flag = UP_TYPE_A; + } else { + fw_update_msg->section_info.FW_section_type = + section_type - 0x2; + } + + fw_update_msg->setion_total_len = section_len; + fw_update_msg->section_offset = send_pos; + + if (section_remain_send_len <= MAX_FW_FRAGMENT_LEN) { + fw_update_msg->ctl_info.SL = true; + fw_update_msg->ctl_info.fragment_len = + section_remain_send_len; + send_fragment_len += section_remain_send_len; + } else { + fw_update_msg->ctl_info.SL = false; + fw_update_msg->ctl_info.fragment_len = + MAX_FW_FRAGMENT_LEN; + send_fragment_len += MAX_FW_FRAGMENT_LEN; + } + + memcpy(fw_update_msg->data, + data + UPDATEFW_IMAGE_HEAD_SIZE + + section_offset + send_pos, + fw_update_msg->ctl_info.fragment_len); + + err = hinic_port_msg_cmd(priv->hwdev, + HINIC_PORT_CMD_UPDATE_FW, + fw_update_msg, + sizeof(*fw_update_msg), + fw_update_msg, &out_size); + if (err || !out_size || fw_update_msg->status) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Failed to update firmware, err: %d, status: 0x%x, out size: 0x%x\n", + err, fw_update_msg->status, out_size); + err = fw_update_msg->status ? + fw_update_msg->status : -EIO; + kfree(fw_update_msg); + return err; + } + + send_pos = send_fragment_len; + section_remain_send_len = section_len - + send_fragment_len; + } + } + + kfree(fw_update_msg); + + return 0; +} + +static int hinic_firmware_update(struct hinic_devlink_priv *priv, + const struct firmware *fw, + struct netlink_ext_ack *extack) +{ + struct host_image_st host_image; + int err; + + memset(&host_image, 0, sizeof(struct host_image_st)); + + if (!check_image_valid(priv, fw->data, fw->size, &host_image) || + !check_image_integrity(priv, &host_image, FW_UPDATE_COLD) || + !check_image_device_type(priv, host_image.device_id)) { + NL_SET_ERR_MSG_MOD(extack, "Check image failed"); + return -EINVAL; + } + + dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware begin\n"); + + err = hinic_flash_fw(priv, fw->data, &host_image); + if (err) { + if (err == HINIC_FW_DISMATCH_ERROR) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Firmware image doesn't match this card, please use newer image, err: %d\n", + err); + NL_SET_ERR_MSG_MOD(extack, + "Firmware image doesn't match this card, please use newer image"); + } else { + dev_err(&priv->hwdev->hwif->pdev->dev, "Send firmware image data failed, err: %d\n", + err); + NL_SET_ERR_MSG_MOD(extack, "Send firmware image data failed"); + } + + return err; + } + + dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware end\n"); + + return 0; +} + +static int hinic_devlink_flash_update(struct devlink *devlink, + const char *file_name, + const char *component, + struct netlink_ext_ack *extack) +{ + struct hinic_devlink_priv *priv = devlink_priv(devlink); + const struct firmware *fw; + int err; + + if (component) + return -EOPNOTSUPP; + + err = request_firmware_direct(&fw, file_name, + &priv->hwdev->hwif->pdev->dev); + if (err) + return err; + + err = hinic_firmware_update(priv, fw, extack); + release_firmware(fw); + + return err; +} + +static const struct devlink_ops hinic_devlink_ops = { + .flash_update = hinic_devlink_flash_update, +}; + +struct devlink *hinic_devlink_alloc(void) +{ + return devlink_alloc(&hinic_devlink_ops, sizeof(struct hinic_dev)); +} + +void hinic_devlink_free(struct devlink *devlink) +{ + devlink_free(devlink); +} + +int hinic_devlink_register(struct devlink *devlink, struct device *dev) +{ + return devlink_register(devlink, dev); +} + +void hinic_devlink_unregister(struct devlink *devlink) +{ + devlink_unregister(devlink); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.h b/drivers/net/ethernet/huawei/hinic/hinic_devlink.h new file mode 100644 index 000000000000..604e95a7c5ce --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#ifndef __HINIC_DEVLINK_H__ +#define __HINIC_DEVLINK_H__ + +#include + +#define MAX_FW_TYPE_NUM 30 +#define HINIC_MAGIC_NUM 0x18221100 +#define UPDATEFW_IMAGE_HEAD_SIZE 1024 +#define FW_UPDATE_COLD 0 +#define FW_UPDATE_HOT 1 + +#define UP_TYPE_A 0x0 +#define UP_TYPE_B 0x1 + +#define MAX_FW_FRAGMENT_LEN 1536 +#define HINIC_FW_DISMATCH_ERROR 10 + +enum hinic_fw_type { + UP_FW_UPDATE_UP_TEXT_A = 0x0, + UP_FW_UPDATE_UP_DATA_A, + UP_FW_UPDATE_UP_TEXT_B, + UP_FW_UPDATE_UP_DATA_B, + UP_FW_UPDATE_UP_DICT, + + UP_FW_UPDATE_HLINK_ONE = 0x5, + UP_FW_UPDATE_HLINK_TWO, + UP_FW_UPDATE_HLINK_THR, + UP_FW_UPDATE_PHY, + UP_FW_UPDATE_TILE_TEXT, + + UP_FW_UPDATE_TILE_DATA = 0xa, + UP_FW_UPDATE_TILE_DICT, + UP_FW_UPDATE_PPE_STATE, + UP_FW_UPDATE_PPE_BRANCH, + UP_FW_UPDATE_PPE_EXTACT, + + UP_FW_UPDATE_CLP_LEGACY = 0xf, + UP_FW_UPDATE_PXE_LEGACY, + UP_FW_UPDATE_ISCSI_LEGACY, + UP_FW_UPDATE_CLP_EFI, + UP_FW_UPDATE_PXE_EFI, + + UP_FW_UPDATE_ISCSI_EFI = 0x14, + UP_FW_UPDATE_CFG, + UP_FW_UPDATE_BOOT, + UP_FW_UPDATE_VPD, + FILE_TYPE_TOTAL_NUM +}; + +#define _IMAGE_UP_ALL_IN ((1 << UP_FW_UPDATE_UP_TEXT_A) | \ + (1 << UP_FW_UPDATE_UP_DATA_A) | \ + (1 << UP_FW_UPDATE_UP_TEXT_B) | \ + (1 << UP_FW_UPDATE_UP_DATA_B) | \ + (1 << UP_FW_UPDATE_UP_DICT) | \ + (1 << UP_FW_UPDATE_BOOT) | \ + (1 << UP_FW_UPDATE_HLINK_ONE) | \ + (1 << UP_FW_UPDATE_HLINK_TWO) | \ + (1 << UP_FW_UPDATE_HLINK_THR)) + +#define _IMAGE_UCODE_ALL_IN ((1 << UP_FW_UPDATE_TILE_TEXT) | \ + (1 << UP_FW_UPDATE_TILE_DICT) | \ + (1 << UP_FW_UPDATE_PPE_STATE) | \ + (1 << UP_FW_UPDATE_PPE_BRANCH) | \ + (1 << UP_FW_UPDATE_PPE_EXTACT)) + +#define _IMAGE_COLD_SUB_MODULES_MUST_IN (_IMAGE_UP_ALL_IN | _IMAGE_UCODE_ALL_IN) +#define _IMAGE_HOT_SUB_MODULES_MUST_IN (_IMAGE_UP_ALL_IN | _IMAGE_UCODE_ALL_IN) +#define _IMAGE_CFG_SUB_MODULES_MUST_IN BIT(UP_FW_UPDATE_CFG) +#define UP_FW_UPDATE_UP_TEXT 0x0 +#define UP_FW_UPDATE_UP_DATA 0x1 +#define UP_FW_UPDATE_VPD_B 0x15 + +struct fw_section_info_st { + u32 fw_section_len; + u32 fw_section_offset; + u32 fw_section_version; + u32 fw_section_type; + u32 fw_section_crc; +}; + +struct fw_image_st { + u32 fw_version; + u32 fw_len; + u32 fw_magic; + struct { + u32 fw_section_cnt:16; + u32 resd:16; + } fw_info; + struct fw_section_info_st fw_section_info[MAX_FW_TYPE_NUM]; + u32 device_id; + u32 res[101]; + void *bin_data; +}; + +struct host_image_st { + struct fw_section_info_st image_section_info[MAX_FW_TYPE_NUM]; + struct { + u32 up_total_len; + u32 fw_version; + } image_info; + u32 section_type_num; + u32 device_id; +}; + +struct devlink *hinic_devlink_alloc(void); +void hinic_devlink_free(struct devlink *devlink); +int hinic_devlink_register(struct devlink *devlink, struct device *dev); +void hinic_devlink_unregister(struct devlink *devlink); + +#endif /* __HINIC_DEVLINK_H__ */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 47c60d9752e4..9831c14324e6 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -897,6 +897,8 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) set_resources_state(hwdev, HINIC_RES_CLEAN); + hinic_vf_func_free(hwdev); + free_pfhwdev(pfhwdev); hinic_aeqs_free(&hwdev->aeqs); @@ -1047,3 +1049,29 @@ void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index, { hinic_set_msix_state(hwdev->hwif, msix_index, flag); } + +int hinic_get_board_info(struct hinic_hwdev *hwdev, + struct hinic_comm_board_info *board_info) +{ + u16 out_size = sizeof(*board_info); + struct hinic_pfhwdev *pfhwdev; + int err; + + if (!hwdev || !board_info) + return -EINVAL; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_GET_BOARD_INFO, + board_info, sizeof(*board_info), + board_info, &out_size, HINIC_MGMT_MSG_SYNC); + if (err || board_info->status || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to get board info, err: %d, status: 0x%x, out size: 0x%x\n", + err, board_info->status, out_size); + return -EIO; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 958ea1a6a60d..94593a8ad667 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -116,6 +116,8 @@ enum hinic_port_cmd { HINIC_PORT_CMD_SET_TSO = 112, + HINIC_PORT_CMD_UPDATE_FW = 114, + HINIC_PORT_CMD_SET_RQ_IQ_MAP = 115, HINIC_PORT_CMD_LINK_STATUS_REPORT = 160, @@ -307,6 +309,35 @@ struct hinic_msix_config { u8 rsvd1[3]; }; +struct hinic_board_info { + u32 board_type; + u32 port_num; + u32 port_speed; + u32 pcie_width; + u32 host_num; + u32 pf_num; + u32 vf_total_num; + u32 tile_num; + u32 qcm_num; + u32 core_num; + u32 work_mode; + u32 service_mode; + u32 pcie_mode; + u32 cfg_addr; + u32 boot_sel; + u32 board_id; +}; + +struct hinic_comm_board_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct hinic_board_info info; + + u32 rsvd1[4]; +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; @@ -407,4 +438,7 @@ int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, struct hinic_msix_config *interrupt_info); +int hinic_get_board_info(struct hinic_hwdev *hwdev, + struct hinic_comm_board_info *board_info); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index e8a55b53c855..21b93b654d6b 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -87,7 +87,9 @@ enum hinic_comm_cmd { HINIC_COMM_CMD_PAGESIZE_SET = 0x50, - HINIC_COMM_CMD_MAX = 0x51, + HINIC_COMM_CMD_GET_BOARD_INFO = 0x52, + + HINIC_COMM_CMD_MAX, }; enum hinic_mgmt_cb_state { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 834a20a0043c..c4c6f9c29f0e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include "hinic_hw_qp.h" #include "hinic_hw_dev.h" +#include "hinic_devlink.h" #include "hinic_port.h" #include "hinic_tx.h" #include "hinic_rx.h" @@ -1075,9 +1077,11 @@ static int nic_dev_init(struct pci_dev *pdev) struct hinic_rx_mode_work *rx_mode_work; struct hinic_txq_stats *tx_stats; struct hinic_rxq_stats *rx_stats; + struct hinic_devlink_priv *priv; struct hinic_dev *nic_dev; struct net_device *netdev; struct hinic_hwdev *hwdev; + struct devlink *devlink; int err, num_qps; hwdev = hinic_init_hwdev(pdev); @@ -1086,6 +1090,16 @@ static int nic_dev_init(struct pci_dev *pdev) return PTR_ERR(hwdev); } + devlink = hinic_devlink_alloc(); + if (!devlink) { + dev_err(&pdev->dev, "Hinic devlink alloc failed\n"); + err = -ENOMEM; + goto err_devlink_alloc; + } + + priv = devlink_priv(devlink); + priv->hwdev = hwdev; + num_qps = hinic_hwdev_num_qps(hwdev); if (num_qps <= 0) { dev_err(&pdev->dev, "Invalid number of QPS\n"); @@ -1121,6 +1135,7 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->sriov_info.hwdev = hwdev; nic_dev->sriov_info.pdev = pdev; nic_dev->max_qps = num_qps; + nic_dev->devlink = devlink; hinic_set_ethtool_ops(netdev); @@ -1146,6 +1161,10 @@ static int nic_dev_init(struct pci_dev *pdev) goto err_workq; } + err = hinic_devlink_register(devlink, &pdev->dev); + if (err) + goto err_devlink_reg; + pci_set_drvdata(pdev, netdev); err = hinic_port_get_mac(nic_dev, netdev->dev_addr); @@ -1223,9 +1242,11 @@ err_set_features: cancel_work_sync(&rx_mode_work->work); err_set_mtu: -err_get_mac: + hinic_port_del_mac(nic_dev, netdev->dev_addr, 0); err_add_mac: +err_get_mac: pci_set_drvdata(pdev, NULL); +err_devlink_reg: destroy_workqueue(nic_dev->workq); err_workq: @@ -1234,6 +1255,7 @@ err_vlan_bitmap: err_alloc_etherdev: err_num_qps: +err_devlink_alloc: hinic_free_hwdev(hwdev); return err; } @@ -1342,9 +1364,11 @@ static void hinic_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); + hinic_devlink_unregister(nic_dev->devlink); + destroy_workqueue(nic_dev->workq); - hinic_vf_func_free(nic_dev->hwdev); + hinic_devlink_free(nic_dev->devlink); hinic_free_hwdev(nic_dev->hwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 0e444d2c02bb..14931adaffb8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -703,6 +703,31 @@ struct hinic_cmd_get_std_sfp_info { u8 sfp_info[STD_SFP_INFO_MAX_SIZE]; }; +struct hinic_cmd_update_fw { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct { + u32 SL:1; + u32 SF:1; + u32 flag:1; + u32 reserved:13; + u32 fragment_len:16; + } ctl_info; + + struct { + u32 FW_section_CRC; + u32 FW_section_type; + } section_info; + + u32 total_len; + u32 setion_total_len; + u32 fw_section_version; + u32 section_offset; + u32 data[384]; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); -- cgit v1.2.3 From c15850c709eb5c7771785c9174e4908d1806a0f0 Mon Sep 17 00:00:00 2001 From: Luo bin Date: Sat, 25 Jul 2020 15:11:18 +0800 Subject: hinic: add support to handle hw abnormal event add support to handle hw abnormal event such as hardware failure, cable unplugged,link error Signed-off-by: Luo bin Signed-off-by: David S. Miller --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 4 + drivers/net/ethernet/huawei/hinic/hinic_devlink.c | 286 +++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_devlink.h | 8 +- drivers/net/ethernet/huawei/hinic/hinic_ethtool.c | 20 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 148 ++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 148 ++++++++++- drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 10 + drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 4 + drivers/net/ethernet/huawei/hinic/hinic_main.c | 87 +++++-- drivers/net/ethernet/huawei/hinic/hinic_port.h | 25 ++ 10 files changed, 704 insertions(+), 36 deletions(-) (limited to 'drivers/net/ethernet/huawei/hinic/hinic_dev.h') diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index befd925c03dc..0a1e20edf7cf 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -98,10 +98,14 @@ struct hinic_dev { int lb_pkt_len; u8 *lb_test_rx_buf; struct devlink *devlink; + bool cable_unplugged; + bool module_unrecognized; }; struct hinic_devlink_priv { struct hinic_hwdev *hwdev; + struct devlink_health_reporter *hw_fault_reporter; + struct devlink_health_reporter *fw_fault_reporter; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.c b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c index a40a10ac1ee9..c6adc776f3c8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_devlink.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c @@ -16,9 +16,9 @@ #include #include -#include "hinic_dev.h" #include "hinic_port.h" #include "hinic_devlink.h" +#include "hinic_hw_dev.h" static bool check_image_valid(struct hinic_devlink_priv *priv, const u8 *buf, u32 image_size, struct host_image_st *host_image) @@ -317,12 +317,292 @@ void hinic_devlink_free(struct devlink *devlink) devlink_free(devlink); } -int hinic_devlink_register(struct devlink *devlink, struct device *dev) +int hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev) { + struct devlink *devlink = priv_to_devlink(priv); + return devlink_register(devlink, dev); } -void hinic_devlink_unregister(struct devlink *devlink) +void hinic_devlink_unregister(struct hinic_devlink_priv *priv) { + struct devlink *devlink = priv_to_devlink(priv); + devlink_unregister(devlink); } + +static int chip_fault_show(struct devlink_fmsg *fmsg, + struct hinic_fault_event *event) +{ + char fault_level[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { + "fatal", "reset", "flr", "general", "suggestion"}; + char level_str[FAULT_SHOW_STR_LEN + 1] = {0}; + u8 level; + int err; + + level = event->event.chip.err_level; + if (level < FAULT_LEVEL_MAX) + strncpy(level_str, fault_level[level], strlen(fault_level[level])); + else + strncpy(level_str, "Unknown", strlen("Unknown")); + + if (level == FAULT_LEVEL_SERIOUS_FLR) { + err = devlink_fmsg_u32_pair_put(fmsg, "Function level err func_id", + (u32)event->event.chip.func_id); + if (err) + return err; + } + + err = devlink_fmsg_u8_pair_put(fmsg, "module_id", event->event.chip.node_id); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_type", (u32)event->event.chip.err_type); + if (err) + return err; + + err = devlink_fmsg_string_pair_put(fmsg, "err_level", level_str); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_addr", + event->event.chip.err_csr_addr); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_value", + event->event.chip.err_csr_value); + if (err) + return err; + + return 0; +} + +static int fault_report_show(struct devlink_fmsg *fmsg, + struct hinic_fault_event *event) +{ + char fault_type[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { + "chip", "ucode", "mem rd timeout", "mem wr timeout", + "reg rd timeout", "reg wr timeout", "phy fault"}; + char type_str[FAULT_SHOW_STR_LEN + 1] = {0}; + int err; + + if (event->type < FAULT_TYPE_MAX) + strncpy(type_str, fault_type[event->type], strlen(fault_type[event->type])); + else + strncpy(type_str, "Unknown", strlen("Unknown")); + + err = devlink_fmsg_string_pair_put(fmsg, "Fault type", type_str); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Fault raw data", + event->event.val, sizeof(event->event.val)); + if (err) + return err; + + switch (event->type) { + case FAULT_TYPE_CHIP: + err = chip_fault_show(fmsg, event); + if (err) + return err; + break; + case FAULT_TYPE_UCODE: + err = devlink_fmsg_u8_pair_put(fmsg, "Cause_id", event->event.ucode.cause_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "core_id", event->event.ucode.core_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "c_id", event->event.ucode.c_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "epc", event->event.ucode.epc); + if (err) + return err; + break; + case FAULT_TYPE_MEM_RD_TIMEOUT: + case FAULT_TYPE_MEM_WR_TIMEOUT: + err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr_ctrl", + event->event.mem_timeout.err_csr_ctrl); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_data", + event->event.mem_timeout.err_csr_data); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "ctrl_tab", + event->event.mem_timeout.ctrl_tab); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "mem_index", + event->event.mem_timeout.mem_index); + if (err) + return err; + break; + case FAULT_TYPE_REG_RD_TIMEOUT: + case FAULT_TYPE_REG_WR_TIMEOUT: + err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr", event->event.reg_timeout.err_csr); + if (err) + return err; + break; + case FAULT_TYPE_PHY_FAULT: + err = devlink_fmsg_u8_pair_put(fmsg, "Op_type", event->event.phy_fault.op_type); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "port_id", event->event.phy_fault.port_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "dev_ad", event->event.phy_fault.dev_ad); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "csr_addr", event->event.phy_fault.csr_addr); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "op_data", event->event.phy_fault.op_data); + if (err) + return err; + break; + default: + break; + } + + return 0; +} + +static int hinic_hw_reporter_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + if (priv_ctx) + return fault_report_show(fmsg, priv_ctx); + + return 0; +} + +static int mgmt_watchdog_report_show(struct devlink_fmsg *fmsg, + struct hinic_mgmt_watchdog_info *watchdog_info) +{ + int err; + + err = devlink_fmsg_u32_pair_put(fmsg, "Mgmt deadloop time_h", watchdog_info->curr_time_h); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "time_l", watchdog_info->curr_time_l); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "task_id", watchdog_info->task_id); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "sp", watchdog_info->sp); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_current_used", watchdog_info->curr_used); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "peak_used", watchdog_info->peak_used); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "\n Overflow_flag", watchdog_info->is_overflow); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_top", watchdog_info->stack_top); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_bottom", watchdog_info->stack_bottom); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "mgmt_pc", watchdog_info->pc); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "lr", watchdog_info->lr); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "cpsr", watchdog_info->cpsr); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt register info", + watchdog_info->reg, sizeof(watchdog_info->reg)); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt dump stack(start from sp)", + watchdog_info->data, sizeof(watchdog_info->data)); + if (err) + return err; + + return 0; +} + +static int hinic_fw_reporter_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + if (priv_ctx) + return mgmt_watchdog_report_show(fmsg, priv_ctx); + + return 0; +} + +static const struct devlink_health_reporter_ops hinic_hw_fault_reporter_ops = { + .name = "hw", + .dump = hinic_hw_reporter_dump, +}; + +static const struct devlink_health_reporter_ops hinic_fw_fault_reporter_ops = { + .name = "fw", + .dump = hinic_fw_reporter_dump, +}; + +int hinic_health_reporters_create(struct hinic_devlink_priv *priv) +{ + struct devlink *devlink = priv_to_devlink(priv); + + priv->hw_fault_reporter = + devlink_health_reporter_create(devlink, &hinic_hw_fault_reporter_ops, + 0, priv); + if (IS_ERR(priv->hw_fault_reporter)) { + dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create hw fault reporter, err: %ld\n", + PTR_ERR(priv->hw_fault_reporter)); + return PTR_ERR(priv->hw_fault_reporter); + } + + priv->fw_fault_reporter = + devlink_health_reporter_create(devlink, &hinic_fw_fault_reporter_ops, + 0, priv); + if (IS_ERR(priv->fw_fault_reporter)) { + dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create fw fault reporter, err: %ld\n", + PTR_ERR(priv->fw_fault_reporter)); + devlink_health_reporter_destroy(priv->hw_fault_reporter); + priv->hw_fault_reporter = NULL; + return PTR_ERR(priv->fw_fault_reporter); + } + + return 0; +} + +void hinic_health_reporters_destroy(struct hinic_devlink_priv *priv) +{ + if (!IS_ERR_OR_NULL(priv->fw_fault_reporter)) { + devlink_health_reporter_destroy(priv->fw_fault_reporter); + priv->fw_fault_reporter = NULL; + } + + if (!IS_ERR_OR_NULL(priv->hw_fault_reporter)) { + devlink_health_reporter_destroy(priv->hw_fault_reporter); + priv->hw_fault_reporter = NULL; + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.h b/drivers/net/ethernet/huawei/hinic/hinic_devlink.h index 604e95a7c5ce..a090ebcfaabb 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_devlink.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.h @@ -7,6 +7,7 @@ #define __HINIC_DEVLINK_H__ #include +#include "hinic_dev.h" #define MAX_FW_TYPE_NUM 30 #define HINIC_MAGIC_NUM 0x18221100 @@ -109,7 +110,10 @@ struct host_image_st { struct devlink *hinic_devlink_alloc(void); void hinic_devlink_free(struct devlink *devlink); -int hinic_devlink_register(struct devlink *devlink, struct device *dev); -void hinic_devlink_unregister(struct devlink *devlink); +int hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev); +void hinic_devlink_unregister(struct hinic_devlink_priv *priv); + +int hinic_health_reporters_create(struct hinic_devlink_priv *priv); +void hinic_health_reporters_destroy(struct hinic_devlink_priv *priv); #endif /* __HINIC_DEVLINK_H__ */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index cb5ebae54f73..6bb65ade1d77 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -1766,6 +1766,25 @@ static int hinic_get_module_eeprom(struct net_device *netdev, return 0; } +static int +hinic_get_link_ext_state(struct net_device *netdev, + struct ethtool_link_ext_state_info *link_ext_state_info) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + + if (netif_carrier_ok(netdev)) + return -ENODATA; + + if (nic_dev->cable_unplugged) + link_ext_state_info->link_ext_state = + ETHTOOL_LINK_EXT_STATE_NO_CABLE; + else if (nic_dev->module_unrecognized) + link_ext_state_info->link_ext_state = + ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH; + + return 0; +} + static const struct ethtool_ops hinic_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_RX_MAX_FRAMES | @@ -1776,6 +1795,7 @@ static const struct ethtool_ops hinic_ethtool_ops = { .set_link_ksettings = hinic_set_link_ksettings, .get_drvinfo = hinic_get_drvinfo, .get_link = ethtool_op_get_link, + .get_link_ext_state = hinic_get_link_ext_state, .get_ringparam = hinic_get_ringparam, .set_ringparam = hinic_set_ringparam, .get_coalesce = hinic_get_coalesce, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 9831c14324e6..55672ee18aab 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -16,8 +16,11 @@ #include #include #include +#include +#include "hinic_devlink.h" #include "hinic_sriov.h" +#include "hinic_dev.h" #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" @@ -621,6 +624,113 @@ static void nic_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in, nic_cb->cb_state &= ~HINIC_CB_RUNNING; } +static void hinic_comm_recv_mgmt_self_cmd_reg(struct hinic_pfhwdev *pfhwdev, + u8 cmd, + comm_mgmt_self_msg_proc proc) +{ + u8 cmd_idx; + + cmd_idx = pfhwdev->proc.cmd_num; + if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) { + dev_err(&pfhwdev->hwdev.hwif->pdev->dev, + "Register recv mgmt process failed, cmd: 0x%x\n", cmd); + return; + } + + pfhwdev->proc.info[cmd_idx].cmd = cmd; + pfhwdev->proc.info[cmd_idx].proc = proc; + pfhwdev->proc.cmd_num++; +} + +static void hinic_comm_recv_mgmt_self_cmd_unreg(struct hinic_pfhwdev *pfhwdev, + u8 cmd) +{ + u8 cmd_idx; + + cmd_idx = pfhwdev->proc.cmd_num; + if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) { + dev_err(&pfhwdev->hwdev.hwif->pdev->dev, "Unregister recv mgmt process failed, cmd: 0x%x\n", + cmd); + return; + } + + for (cmd_idx = 0; cmd_idx < HINIC_COMM_SELF_CMD_MAX; cmd_idx++) { + if (cmd == pfhwdev->proc.info[cmd_idx].cmd) { + pfhwdev->proc.info[cmd_idx].cmd = 0; + pfhwdev->proc.info[cmd_idx].proc = NULL; + pfhwdev->proc.cmd_num--; + } + } +} + +static void comm_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in, + u16 in_size, void *buf_out, u16 *out_size) +{ + struct hinic_pfhwdev *pfhwdev = handle; + u8 cmd_idx; + + for (cmd_idx = 0; cmd_idx < pfhwdev->proc.cmd_num; cmd_idx++) { + if (cmd == pfhwdev->proc.info[cmd_idx].cmd) { + if (!pfhwdev->proc.info[cmd_idx].proc) { + dev_warn(&pfhwdev->hwdev.hwif->pdev->dev, + "PF recv mgmt comm msg handle null, cmd: 0x%x\n", + cmd); + } else { + pfhwdev->proc.info[cmd_idx].proc + (&pfhwdev->hwdev, buf_in, in_size, + buf_out, out_size); + } + + return; + } + } + + dev_warn(&pfhwdev->hwdev.hwif->pdev->dev, "Received unknown mgmt cpu event: 0x%x\n", + cmd); + + *out_size = 0; +} + +/* pf fault report event */ +static void pf_fault_event_handler(void *dev, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cmd_fault_event *fault_event = buf_in; + struct hinic_hwdev *hwdev = dev; + + if (in_size != sizeof(*fault_event)) { + dev_err(&hwdev->hwif->pdev->dev, "Invalid fault event report, length: %d, should be %zu\n", + in_size, sizeof(*fault_event)); + return; + } + + if (!hwdev->devlink_dev || IS_ERR_OR_NULL(hwdev->devlink_dev->hw_fault_reporter)) + return; + + devlink_health_report(hwdev->devlink_dev->hw_fault_reporter, + "HW fatal error reported", &fault_event->event); +} + +static void mgmt_watchdog_timeout_event_handler(void *dev, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_mgmt_watchdog_info *watchdog_info = buf_in; + struct hinic_hwdev *hwdev = dev; + + if (in_size != sizeof(*watchdog_info)) { + dev_err(&hwdev->hwif->pdev->dev, "Invalid mgmt watchdog report, length: %d, should be %zu\n", + in_size, sizeof(*watchdog_info)); + return; + } + + if (!hwdev->devlink_dev || IS_ERR_OR_NULL(hwdev->devlink_dev->fw_fault_reporter)) + return; + + devlink_health_report(hwdev->devlink_dev->fw_fault_reporter, + "FW fatal error reported", watchdog_info); +} + /** * init_pfhwdev - Initialize the extended components of PF * @pfhwdev: the HW device for PF @@ -640,20 +750,37 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) return err; } + err = hinic_devlink_register(hwdev->devlink_dev, &pdev->dev); + if (err) { + dev_err(&hwif->pdev->dev, "Failed to register devlink\n"); + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); + return err; + } + err = hinic_func_to_func_init(hwdev); if (err) { dev_err(&hwif->pdev->dev, "Failed to init mailbox\n"); + hinic_devlink_unregister(hwdev->devlink_dev); hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); return err; } - if (!HINIC_IS_VF(hwif)) + if (!HINIC_IS_VF(hwif)) { hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, pfhwdev, nic_mgmt_msg_handler); - else + hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + pfhwdev, comm_mgmt_msg_handler); + hinic_comm_recv_mgmt_self_cmd_reg(pfhwdev, + HINIC_COMM_CMD_FAULT_REPORT, + pf_fault_event_handler); + hinic_comm_recv_mgmt_self_cmd_reg + (pfhwdev, HINIC_COMM_CMD_WATCHDOG_INFO, + mgmt_watchdog_timeout_event_handler); + } else { hinic_register_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC, nic_mgmt_msg_handler); + } hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE); @@ -670,14 +797,23 @@ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT); - if (!HINIC_IS_VF(hwdev->hwif)) + if (!HINIC_IS_VF(hwdev->hwif)) { + hinic_comm_recv_mgmt_self_cmd_unreg(pfhwdev, + HINIC_COMM_CMD_WATCHDOG_INFO); + hinic_comm_recv_mgmt_self_cmd_unreg(pfhwdev, + HINIC_COMM_CMD_FAULT_REPORT); + hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, + HINIC_MOD_COMM); hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC); - else + } else { hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC); + } hinic_func_to_func_free(hwdev); + hinic_devlink_unregister(hwdev->devlink_dev); + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); } @@ -777,7 +913,7 @@ int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, * * Initialize the NIC HW device and return a pointer to it **/ -struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev, struct devlink *devlink) { struct hinic_pfhwdev *pfhwdev; struct hinic_hwdev *hwdev; @@ -802,6 +938,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) hwdev = &pfhwdev->hwdev; hwdev->hwif = hwif; + hwdev->devlink_dev = devlink_priv(devlink); + hwdev->devlink_dev->hwdev = hwdev; err = init_msix(hwdev); if (err) { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 94593a8ad667..2fb5f784f116 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" @@ -164,9 +165,12 @@ enum hinic_ucode_cmd { #define NIC_RSS_CMD_TEMP_FREE 0x02 enum hinic_mgmt_msg_cmd { - HINIC_MGMT_MSG_CMD_BASE = 160, + HINIC_MGMT_MSG_CMD_BASE = 0xA0, - HINIC_MGMT_MSG_CMD_LINK_STATUS = 160, + HINIC_MGMT_MSG_CMD_LINK_STATUS = 0xA0, + + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT = 0xE5, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT = 0xE6, HINIC_MGMT_MSG_CMD_MAX, }; @@ -348,6 +352,7 @@ struct hinic_hwdev { struct hinic_cap nic_cap; u8 port_id; + struct hinic_devlink_priv *devlink_dev; }; struct hinic_nic_cb { @@ -359,12 +364,29 @@ struct hinic_nic_cb { unsigned long cb_state; }; +#define HINIC_COMM_SELF_CMD_MAX 4 + +typedef void (*comm_mgmt_self_msg_proc)(void *handle, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + +struct comm_mgmt_self_msg_sub_info { + u8 cmd; + comm_mgmt_self_msg_proc proc; +}; + +struct comm_mgmt_self_msg_info { + u8 cmd_num; + struct comm_mgmt_self_msg_sub_info info[HINIC_COMM_SELF_CMD_MAX]; +}; + struct hinic_pfhwdev { struct hinic_hwdev hwdev; struct hinic_pf_to_mgmt pf_to_mgmt; struct hinic_nic_cb nic_cb[HINIC_MGMT_NUM_MSG_CMD]; + + struct comm_mgmt_self_msg_info proc; }; struct hinic_dev_cap { @@ -386,6 +408,126 @@ struct hinic_dev_cap { u8 rsvd3[204]; }; +union hinic_fault_hw_mgmt { + u32 val[4]; + /* valid only type == FAULT_TYPE_CHIP */ + struct { + u8 node_id; + u8 err_level; + u16 err_type; + u32 err_csr_addr; + u32 err_csr_value; + /* func_id valid only if err_level == FAULT_LEVEL_SERIOUS_FLR */ + u16 func_id; + u16 rsvd2; + } chip; + + /* valid only if type == FAULT_TYPE_UCODE */ + struct { + u8 cause_id; + u8 core_id; + u8 c_id; + u8 rsvd3; + u32 epc; + u32 rsvd4; + u32 rsvd5; + } ucode; + + /* valid only if type == FAULT_TYPE_MEM_RD_TIMEOUT || + * FAULT_TYPE_MEM_WR_TIMEOUT + */ + struct { + u32 err_csr_ctrl; + u32 err_csr_data; + u32 ctrl_tab; + u32 mem_index; + } mem_timeout; + + /* valid only if type == FAULT_TYPE_REG_RD_TIMEOUT || + * FAULT_TYPE_REG_WR_TIMEOUT + */ + struct { + u32 err_csr; + u32 rsvd6; + u32 rsvd7; + u32 rsvd8; + } reg_timeout; + + struct { + /* 0: read; 1: write */ + u8 op_type; + u8 port_id; + u8 dev_ad; + u8 rsvd9; + u32 csr_addr; + u32 op_data; + u32 rsvd10; + } phy_fault; +}; + +struct hinic_fault_event { + u8 type; + u8 fault_level; + u8 rsvd0[2]; + union hinic_fault_hw_mgmt event; +}; + +struct hinic_cmd_fault_event { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct hinic_fault_event event; +}; + +enum hinic_fault_type { + FAULT_TYPE_CHIP, + FAULT_TYPE_UCODE, + FAULT_TYPE_MEM_RD_TIMEOUT, + FAULT_TYPE_MEM_WR_TIMEOUT, + FAULT_TYPE_REG_RD_TIMEOUT, + FAULT_TYPE_REG_WR_TIMEOUT, + FAULT_TYPE_PHY_FAULT, + FAULT_TYPE_MAX, +}; + +#define FAULT_SHOW_STR_LEN 16 + +enum hinic_fault_err_level { + FAULT_LEVEL_FATAL, + FAULT_LEVEL_SERIOUS_RESET, + FAULT_LEVEL_SERIOUS_FLR, + FAULT_LEVEL_GENERAL, + FAULT_LEVEL_SUGGESTION, + FAULT_LEVEL_MAX +}; + +struct hinic_mgmt_watchdog_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + u32 curr_time_h; + u32 curr_time_l; + u32 task_id; + u32 rsv; + + u32 reg[13]; + u32 pc; + u32 lr; + u32 cpsr; + + u32 stack_top; + u32 stack_bottom; + u32 sp; + u32 curr_used; + u32 peak_used; + u32 is_overflow; + + u32 stack_actlen; + u8 data[1024]; +}; + void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev, enum hinic_mgmt_msg_cmd cmd, void *handle, void (*handler)(void *handle, void *buf_in, @@ -407,7 +549,7 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev, u16 sq_depth, u16 rq_depth); void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev); -struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev, struct devlink *devlink); void hinic_free_hwdev(struct hinic_hwdev *hwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c index e0f5a81d8620..f5a46d5bb007 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -12,8 +12,10 @@ #include #include #include +#include #include +#include "hinic_devlink.h" #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_api_cmd.h" @@ -617,10 +619,15 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, if (HINIC_IS_VF(hwif)) return 0; + err = hinic_health_reporters_create(hwdev->devlink_dev); + if (err) + return err; + sema_init(&pf_to_mgmt->sync_msg_lock, 1); pf_to_mgmt->workq = create_singlethread_workqueue("hinic_mgmt"); if (!pf_to_mgmt->workq) { dev_err(&pdev->dev, "Failed to initialize MGMT workqueue\n"); + hinic_health_reporters_destroy(hwdev->devlink_dev); return -ENOMEM; } pf_to_mgmt->sync_msg_id = 0; @@ -628,12 +635,14 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, err = alloc_msg_buf(pf_to_mgmt); if (err) { dev_err(&pdev->dev, "Failed to allocate msg buffers\n"); + hinic_health_reporters_destroy(hwdev->devlink_dev); return err; } err = hinic_api_cmd_init(pf_to_mgmt->cmd_chain, hwif); if (err) { dev_err(&pdev->dev, "Failed to initialize cmd chains\n"); + hinic_health_reporters_destroy(hwdev->devlink_dev); return err; } @@ -658,4 +667,5 @@ void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt) hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU); hinic_api_cmd_free(pf_to_mgmt->cmd_chain); destroy_workqueue(pf_to_mgmt->workq); + hinic_health_reporters_destroy(hwdev->devlink_dev); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index 21b93b654d6b..f626100b85c1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -81,6 +81,8 @@ enum hinic_comm_cmd { HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP, HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP, + HINIC_COMM_CMD_FAULT_REPORT = 0x37, + HINIC_COMM_CMD_SET_LED_STATUS = 0x4a, HINIC_COMM_CMD_L2NIC_RESET = 0x4b, @@ -89,6 +91,8 @@ enum hinic_comm_cmd { HINIC_COMM_CMD_GET_BOARD_INFO = 0x52, + HINIC_COMM_CMD_WATCHDOG_INFO = 0x56, + HINIC_COMM_CMD_MAX, }; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index c4c6f9c29f0e..501056fd32ee 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -444,8 +444,11 @@ int hinic_open(struct net_device *netdev) if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) hinic_notify_all_vfs_link_changed(nic_dev->hwdev, link_state); - if (link_state == HINIC_LINK_STATE_UP) + if (link_state == HINIC_LINK_STATE_UP) { nic_dev->flags |= HINIC_LINK_UP; + nic_dev->cable_unplugged = false; + nic_dev->module_unrecognized = false; + } nic_dev->flags |= HINIC_INTF_UP; @@ -935,6 +938,8 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, down(&nic_dev->mgmt_lock); nic_dev->flags |= HINIC_LINK_UP; + nic_dev->cable_unplugged = false; + nic_dev->module_unrecognized = false; if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) == (HINIC_LINK_UP | HINIC_INTF_UP)) { @@ -971,6 +976,39 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, *out_size = sizeof(*ret_link_status); } +static void cable_plug_event(void *handle, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cable_plug_event *plug_event = buf_in; + struct hinic_dev *nic_dev = handle; + + nic_dev->cable_unplugged = plug_event->plugged ? false : true; + + *out_size = sizeof(*plug_event); + plug_event = buf_out; + plug_event->status = 0; +} + +static void link_err_event(void *handle, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_link_err_event *link_err = buf_in; + struct hinic_dev *nic_dev = handle; + + if (link_err->err_type >= LINK_ERR_NUM) + netif_info(nic_dev, link, nic_dev->netdev, + "Link failed, Unknown error type: 0x%x\n", + link_err->err_type); + else + nic_dev->module_unrecognized = true; + + *out_size = sizeof(*link_err); + link_err = buf_out; + link_err->status = 0; +} + static int set_features(struct hinic_dev *nic_dev, netdev_features_t pre_features, netdev_features_t features, bool force_change) @@ -1077,28 +1115,24 @@ static int nic_dev_init(struct pci_dev *pdev) struct hinic_rx_mode_work *rx_mode_work; struct hinic_txq_stats *tx_stats; struct hinic_rxq_stats *rx_stats; - struct hinic_devlink_priv *priv; struct hinic_dev *nic_dev; struct net_device *netdev; struct hinic_hwdev *hwdev; struct devlink *devlink; int err, num_qps; - hwdev = hinic_init_hwdev(pdev); - if (IS_ERR(hwdev)) { - dev_err(&pdev->dev, "Failed to initialize HW device\n"); - return PTR_ERR(hwdev); - } - devlink = hinic_devlink_alloc(); if (!devlink) { dev_err(&pdev->dev, "Hinic devlink alloc failed\n"); - err = -ENOMEM; - goto err_devlink_alloc; + return -ENOMEM; } - priv = devlink_priv(devlink); - priv->hwdev = hwdev; + hwdev = hinic_init_hwdev(pdev, devlink); + if (IS_ERR(hwdev)) { + dev_err(&pdev->dev, "Failed to initialize HW device\n"); + hinic_devlink_free(devlink); + return PTR_ERR(hwdev); + } num_qps = hinic_hwdev_num_qps(hwdev); if (num_qps <= 0) { @@ -1161,10 +1195,6 @@ static int nic_dev_init(struct pci_dev *pdev) goto err_workq; } - err = hinic_devlink_register(devlink, &pdev->dev); - if (err) - goto err_devlink_reg; - pci_set_drvdata(pdev, netdev); err = hinic_port_get_mac(nic_dev, netdev->dev_addr); @@ -1206,6 +1236,12 @@ static int nic_dev_init(struct pci_dev *pdev) hinic_hwdev_cb_register(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS, nic_dev, link_status_event_handler); + hinic_hwdev_cb_register(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT, + nic_dev, cable_plug_event); + hinic_hwdev_cb_register(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT, + nic_dev, link_err_event); err = set_features(nic_dev, 0, nic_dev->netdev->features, true); if (err) @@ -1237,6 +1273,10 @@ err_reg_netdev: err_init_intr: err_set_pfc: err_set_features: + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT); + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT); hinic_hwdev_cb_unregister(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS); cancel_work_sync(&rx_mode_work->work); @@ -1246,17 +1286,15 @@ err_set_mtu: err_add_mac: err_get_mac: pci_set_drvdata(pdev, NULL); -err_devlink_reg: destroy_workqueue(nic_dev->workq); - err_workq: err_vlan_bitmap: free_netdev(netdev); err_alloc_etherdev: err_num_qps: -err_devlink_alloc: hinic_free_hwdev(hwdev); + hinic_devlink_free(devlink); return err; } @@ -1343,6 +1381,7 @@ static void hinic_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct hinic_dev *nic_dev = netdev_priv(netdev); + struct devlink *devlink = nic_dev->devlink; struct hinic_rx_mode_work *rx_mode_work; if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) { @@ -1356,6 +1395,10 @@ static void hinic_remove(struct pci_dev *pdev) hinic_port_del_mac(nic_dev, netdev->dev_addr, 0); + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT); + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT); hinic_hwdev_cb_unregister(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS); @@ -1364,16 +1407,14 @@ static void hinic_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); - hinic_devlink_unregister(nic_dev->devlink); - destroy_workqueue(nic_dev->workq); - hinic_devlink_free(nic_dev->devlink); - hinic_free_hwdev(nic_dev->hwdev); free_netdev(netdev); + hinic_devlink_free(devlink); + pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 14931adaffb8..9c3cbe45c9ec 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -189,6 +189,31 @@ struct hinic_port_link_status { u8 port_id; }; +struct hinic_cable_plug_event { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 plugged; /* 0: unplugged, 1: plugged */ + u8 port_id; +}; + +enum link_err_type { + LINK_ERR_MODULE_UNRECOGENIZED, + LINK_ERR_NUM, +}; + +struct hinic_link_err_event { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 err_type; + u8 port_id; +}; + struct hinic_port_func_state_cmd { u8 status; u8 version; -- cgit v1.2.3