]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hinic3: Add .ndo_set_features and .ndo_fix_features
authorFan Gong <gongfan1@huawei.com>
Wed, 14 Jan 2026 08:38:23 +0000 (16:38 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 20 Jan 2026 09:34:31 +0000 (10:34 +0100)
Implement following callback function:
.ndo_set_features
.ndo_fix_features

The .ndo_set_features function includes five features: rx_csum,
tso, lro, rx_cvlan and vlan_filter.
Add these new features in netdev_feature_init.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
Link: https://patch.msgid.link/682734a08fde421413048bf70057dafe3cbe8497.1768375903.git.zhuyikai1@h-partners.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/huawei/hinic3/hinic3_main.c
drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h

index 2bd306f09cd595a41c8cf39fd08faeabb90bfdcc..6071d2023a5d744f2d18da0c0e78e85dedb1d95e 100644 (file)
@@ -227,6 +227,8 @@ static void hinic3_assign_netdev_ops(struct net_device *netdev)
 static void netdev_feature_init(struct net_device *netdev)
 {
        struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       netdev_features_t hw_features = 0;
+       netdev_features_t vlan_fts = 0;
        netdev_features_t cso_fts = 0;
        netdev_features_t tso_fts = 0;
        netdev_features_t dft_fts;
@@ -239,7 +241,29 @@ static void netdev_feature_init(struct net_device *netdev)
        if (hinic3_test_support(nic_dev, HINIC3_NIC_F_TSO))
                tso_fts |= NETIF_F_TSO | NETIF_F_TSO6;
 
-       netdev->features |= dft_fts | cso_fts | tso_fts;
+       if (hinic3_test_support(nic_dev, HINIC3_NIC_F_RX_VLAN_STRIP |
+                               HINIC3_NIC_F_TX_VLAN_INSERT))
+               vlan_fts |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+
+       if (hinic3_test_support(nic_dev, HINIC3_NIC_F_RX_VLAN_FILTER))
+               vlan_fts |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+       if (hinic3_test_support(nic_dev, HINIC3_NIC_F_VXLAN_OFFLOAD))
+               tso_fts |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;
+
+       /* LRO is disabled by default, only set hw features */
+       if (hinic3_test_support(nic_dev, HINIC3_NIC_F_LRO))
+               hw_features |= NETIF_F_LRO;
+
+       netdev->features |= dft_fts | cso_fts | tso_fts | vlan_fts;
+       netdev->vlan_features |= dft_fts | cso_fts | tso_fts;
+       hw_features |= netdev->hw_features | netdev->features;
+       netdev->hw_features = hw_features;
+       netdev->priv_flags |= IFF_UNICAST_FLT;
+
+       netdev->hw_enc_features |= dft_fts;
+       if (hinic3_test_support(nic_dev, HINIC3_NIC_F_VXLAN_OFFLOAD))
+               netdev->hw_enc_features |= cso_fts | tso_fts | NETIF_F_TSO_ECN;
 }
 
 static int hinic3_set_default_hw_feature(struct net_device *netdev)
@@ -254,6 +278,13 @@ static int hinic3_set_default_hw_feature(struct net_device *netdev)
                return err;
        }
 
+       err = hinic3_set_hw_features(netdev);
+       if (err) {
+               hinic3_update_nic_feature(nic_dev, 0);
+               hinic3_set_nic_feature_to_hw(nic_dev);
+               return err;
+       }
+
        return 0;
 }
 
index 3a6d3ee534d03988681ac4a787961a2c8120086d..69405715e734b5a95d13851dddaa20672e58151c 100644 (file)
@@ -56,6 +56,22 @@ struct l2nic_cmd_update_mac {
        u8                   new_mac[ETH_ALEN];
 };
 
+struct l2nic_cmd_vlan_offload {
+       struct mgmt_msg_head msg_head;
+       u16                  func_id;
+       u8                   vlan_offload;
+       u8                   rsvd1[5];
+};
+
+/* set vlan filter */
+struct l2nic_cmd_set_vlan_filter {
+       struct mgmt_msg_head msg_head;
+       u16                  func_id;
+       u8                   rsvd[2];
+       /* bit0:vlan filter en; bit1:broadcast_filter_en */
+       u32                  vlan_filter_ctrl;
+};
+
 struct l2nic_cmd_set_ci_attr {
        struct mgmt_msg_head msg_head;
        u16                  func_idx;
@@ -102,6 +118,26 @@ struct l2nic_cmd_set_dcb_state {
        u8                   rsvd[7];
 };
 
+struct l2nic_cmd_lro_config {
+       struct mgmt_msg_head msg_head;
+       u16                  func_id;
+       u8                   opcode;
+       u8                   rsvd1;
+       u8                   lro_ipv4_en;
+       u8                   lro_ipv6_en;
+       /* unit is 1K */
+       u8                   lro_max_pkt_len;
+       u8                   resv2[13];
+};
+
+struct l2nic_cmd_lro_timer {
+       struct mgmt_msg_head msg_head;
+       /* 1: set timer value, 0: get timer value */
+       u8                   opcode;
+       u8                   rsvd[3];
+       u32                  timer;
+};
+
 #define L2NIC_RSS_TYPE_VALID_MASK         BIT(23)
 #define L2NIC_RSS_TYPE_TCP_IPV6_EXT_MASK  BIT(24)
 #define L2NIC_RSS_TYPE_IPV6_EXT_MASK      BIT(25)
@@ -162,11 +198,15 @@ enum l2nic_cmd {
        L2NIC_CMD_SET_VPORT_ENABLE    = 6,
        L2NIC_CMD_SET_SQ_CI_ATTR      = 8,
        L2NIC_CMD_CLEAR_QP_RESOURCE   = 11,
+       L2NIC_CMD_CFG_RX_LRO          = 13,
+       L2NIC_CMD_CFG_LRO_TIMER       = 14,
        L2NIC_CMD_FEATURE_NEGO        = 15,
        L2NIC_CMD_GET_MAC             = 20,
        L2NIC_CMD_SET_MAC             = 21,
        L2NIC_CMD_DEL_MAC             = 22,
        L2NIC_CMD_UPDATE_MAC          = 23,
+       L2NIC_CMD_SET_VLAN_FILTER_EN  = 26,
+       L2NIC_CMD_SET_RX_VLAN_OFFLOAD = 27,
        L2NIC_CMD_CFG_RSS             = 60,
        L2NIC_CMD_CFG_RSS_HASH_KEY    = 63,
        L2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,
index 39091f4723727816f66ca07beef8fb4c6b3f851b..2e1ca9571e7c789ea9f06a98f82968ed340b7611 100644 (file)
@@ -12,6 +12,9 @@
 #include "hinic3_rx.h"
 #include "hinic3_tx.h"
 
+#define HINIC3_LRO_DEFAULT_COAL_PKT_SIZE  32
+#define HINIC3_LRO_DEFAULT_TIME_LIMIT     16
+
 /* try to modify the number of irq to the target number,
  * and return the actual number of irq.
  */
@@ -476,6 +479,162 @@ static int hinic3_close(struct net_device *netdev)
        return 0;
 }
 
+#define SET_FEATURES_OP_STR(op)  ((op) ? "Enable" : "Disable")
+
+static int hinic3_set_feature_rx_csum(struct net_device *netdev,
+                                     netdev_features_t wanted_features,
+                                     netdev_features_t features,
+                                     netdev_features_t *failed_features)
+{
+       netdev_features_t changed = wanted_features ^ features;
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+
+       if (changed & NETIF_F_RXCSUM)
+               dev_dbg(hwdev->dev, "%s rx csum success\n",
+                       SET_FEATURES_OP_STR(wanted_features & NETIF_F_RXCSUM));
+
+       return 0;
+}
+
+static int hinic3_set_feature_tso(struct net_device *netdev,
+                                 netdev_features_t wanted_features,
+                                 netdev_features_t features,
+                                 netdev_features_t *failed_features)
+{
+       netdev_features_t changed = wanted_features ^ features;
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+
+       if (changed & NETIF_F_TSO)
+               dev_dbg(hwdev->dev, "%s tso success\n",
+                       SET_FEATURES_OP_STR(wanted_features & NETIF_F_TSO));
+
+       return 0;
+}
+
+static int hinic3_set_feature_lro(struct net_device *netdev,
+                                 netdev_features_t wanted_features,
+                                 netdev_features_t features,
+                                 netdev_features_t *failed_features)
+{
+       netdev_features_t changed = wanted_features ^ features;
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+       bool en = !!(wanted_features & NETIF_F_LRO);
+       int err;
+
+       if (!(changed & NETIF_F_LRO))
+               return 0;
+
+       err = hinic3_set_rx_lro_state(hwdev, en,
+                                     HINIC3_LRO_DEFAULT_TIME_LIMIT,
+                                     HINIC3_LRO_DEFAULT_COAL_PKT_SIZE);
+       if (err) {
+               dev_err(hwdev->dev, "%s lro failed\n", SET_FEATURES_OP_STR(en));
+               *failed_features |= NETIF_F_LRO;
+       }
+
+       return err;
+}
+
+static int hinic3_set_feature_rx_cvlan(struct net_device *netdev,
+                                      netdev_features_t wanted_features,
+                                      netdev_features_t features,
+                                      netdev_features_t *failed_features)
+{
+       bool en = !!(wanted_features & NETIF_F_HW_VLAN_CTAG_RX);
+       netdev_features_t changed = wanted_features ^ features;
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+       int err;
+
+       if (!(changed & NETIF_F_HW_VLAN_CTAG_RX))
+               return 0;
+
+       err = hinic3_set_rx_vlan_offload(hwdev, en);
+       if (err) {
+               dev_err(hwdev->dev, "%s rx vlan offload failed\n",
+                       SET_FEATURES_OP_STR(en));
+               *failed_features |= NETIF_F_HW_VLAN_CTAG_RX;
+       }
+
+       return err;
+}
+
+static int hinic3_set_feature_vlan_filter(struct net_device *netdev,
+                                         netdev_features_t wanted_features,
+                                         netdev_features_t features,
+                                         netdev_features_t *failed_features)
+{
+       bool en = !!(wanted_features & NETIF_F_HW_VLAN_CTAG_FILTER);
+       netdev_features_t changed = wanted_features ^ features;
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_hwdev *hwdev = nic_dev->hwdev;
+       int err;
+
+       if (!(changed & NETIF_F_HW_VLAN_CTAG_FILTER))
+               return 0;
+
+       err = hinic3_set_vlan_filter(hwdev, en);
+       if (err) {
+               dev_err(hwdev->dev, "%s rx vlan filter failed\n",
+                       SET_FEATURES_OP_STR(en));
+               *failed_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+       }
+
+       return err;
+}
+
+static int hinic3_set_features(struct net_device *netdev,
+                              netdev_features_t curr,
+                              netdev_features_t wanted)
+{
+       netdev_features_t failed = 0;
+       int err;
+
+       err = hinic3_set_feature_rx_csum(netdev, wanted, curr, &failed) |
+             hinic3_set_feature_tso(netdev, wanted, curr, &failed) |
+             hinic3_set_feature_lro(netdev, wanted, curr, &failed) |
+             hinic3_set_feature_rx_cvlan(netdev, wanted, curr, &failed) |
+             hinic3_set_feature_vlan_filter(netdev, wanted, curr, &failed);
+       if (err) {
+               netdev->features = wanted ^ failed;
+               return err;
+       }
+
+       return 0;
+}
+
+static int hinic3_ndo_set_features(struct net_device *netdev,
+                                  netdev_features_t features)
+{
+       return hinic3_set_features(netdev, netdev->features, features);
+}
+
+static netdev_features_t hinic3_fix_features(struct net_device *netdev,
+                                            netdev_features_t features)
+{
+       netdev_features_t features_tmp = features;
+
+       /* If Rx checksum is disabled, then LRO should also be disabled */
+       if (!(features_tmp & NETIF_F_RXCSUM))
+               features_tmp &= ~NETIF_F_LRO;
+
+       return features_tmp;
+}
+
+int hinic3_set_hw_features(struct net_device *netdev)
+{
+       netdev_features_t wanted, curr;
+
+       wanted = netdev->features;
+       /* fake current features so all wanted are enabled */
+       curr = ~wanted;
+
+       return hinic3_set_features(netdev, curr, wanted);
+}
+
 static int hinic3_change_mtu(struct net_device *netdev, int new_mtu)
 {
        int err;
@@ -595,6 +754,8 @@ static void hinic3_get_stats64(struct net_device *netdev,
 static const struct net_device_ops hinic3_netdev_ops = {
        .ndo_open             = hinic3_open,
        .ndo_stop             = hinic3_close,
+       .ndo_set_features     = hinic3_ndo_set_features,
+       .ndo_fix_features     = hinic3_fix_features,
        .ndo_change_mtu       = hinic3_change_mtu,
        .ndo_set_mac_address  = hinic3_set_mac_addr,
        .ndo_tx_timeout       = hinic3_tx_timeout,
index 7fec13bbe60e7dfdd2f16be17e2a96281fcdb600..79494ac8f39550fb91c11d8d174934553185fbc9 100644 (file)
@@ -57,6 +57,136 @@ bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
        return (nic_dev->nic_io->feature_cap & feature_bits) == feature_bits;
 }
 
+static int hinic3_set_rx_lro(struct hinic3_hwdev *hwdev, u8 ipv4_en, u8 ipv6_en,
+                            u8 lro_max_pkt_len)
+{
+       struct l2nic_cmd_lro_config lro_cfg = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       lro_cfg.func_id = hinic3_global_func_id(hwdev);
+       lro_cfg.opcode = MGMT_MSG_CMD_OP_SET;
+       lro_cfg.lro_ipv4_en = ipv4_en;
+       lro_cfg.lro_ipv6_en = ipv6_en;
+       lro_cfg.lro_max_pkt_len = lro_max_pkt_len;
+
+       mgmt_msg_params_init_default(&msg_params, &lro_cfg,
+                                    sizeof(lro_cfg));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+                                      L2NIC_CMD_CFG_RX_LRO,
+                                      &msg_params);
+
+       if (err || lro_cfg.msg_head.status) {
+               dev_err(hwdev->dev, "Failed to set lro offload, err: %d, status: 0x%x\n",
+                       err, lro_cfg.msg_head.status);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int hinic3_set_rx_lro_timer(struct hinic3_hwdev *hwdev, u32 timer_value)
+{
+       struct l2nic_cmd_lro_timer lro_timer = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       lro_timer.opcode = MGMT_MSG_CMD_OP_SET;
+       lro_timer.timer = timer_value;
+
+       mgmt_msg_params_init_default(&msg_params, &lro_timer,
+                                    sizeof(lro_timer));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+                                      L2NIC_CMD_CFG_LRO_TIMER,
+                                      &msg_params);
+
+       if (err || lro_timer.msg_head.status) {
+               dev_err(hwdev->dev, "Failed to set lro timer, err: %d, status: 0x%x\n",
+                       err, lro_timer.msg_head.status);
+
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+int hinic3_set_rx_lro_state(struct hinic3_hwdev *hwdev, u8 lro_en,
+                           u32 lro_timer, u8 lro_max_pkt_len)
+{
+       u8 ipv4_en, ipv6_en;
+       int err;
+
+       ipv4_en = lro_en ? 1 : 0;
+       ipv6_en = lro_en ? 1 : 0;
+
+       dev_dbg(hwdev->dev, "Set LRO max coalesce packet size to %uK\n",
+               lro_max_pkt_len);
+
+       err = hinic3_set_rx_lro(hwdev, ipv4_en, ipv6_en, lro_max_pkt_len);
+       if (err)
+               return err;
+
+       /* we don't set LRO timer for VF */
+       if (HINIC3_IS_VF(hwdev))
+               return 0;
+
+       dev_dbg(hwdev->dev, "Set LRO timer to %u\n", lro_timer);
+
+       return hinic3_set_rx_lro_timer(hwdev, lro_timer);
+}
+
+int hinic3_set_rx_vlan_offload(struct hinic3_hwdev *hwdev, u8 en)
+{
+       struct l2nic_cmd_vlan_offload vlan_cfg = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       vlan_cfg.func_id = hinic3_global_func_id(hwdev);
+       vlan_cfg.vlan_offload = en;
+
+       mgmt_msg_params_init_default(&msg_params, &vlan_cfg,
+                                    sizeof(vlan_cfg));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+                                      L2NIC_CMD_SET_RX_VLAN_OFFLOAD,
+                                      &msg_params);
+
+       if (err || vlan_cfg.msg_head.status) {
+               dev_err(hwdev->dev, "Failed to set rx vlan offload, err: %d, status: 0x%x\n",
+                       err, vlan_cfg.msg_head.status);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+int hinic3_set_vlan_filter(struct hinic3_hwdev *hwdev, u32 vlan_filter_ctrl)
+{
+       struct l2nic_cmd_set_vlan_filter vlan_filter = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       vlan_filter.func_id = hinic3_global_func_id(hwdev);
+       vlan_filter.vlan_filter_ctrl = vlan_filter_ctrl;
+
+       mgmt_msg_params_init_default(&msg_params, &vlan_filter,
+                                    sizeof(vlan_filter));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+                                      L2NIC_CMD_SET_VLAN_FILTER_EN,
+                                      &msg_params);
+
+       if (err || vlan_filter.msg_head.status) {
+               dev_err(hwdev->dev, "Failed to set vlan filter, err: %d, status: 0x%x\n",
+                       err, vlan_filter.msg_head.status);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap)
 {
        nic_dev->nic_io->feature_cap = feature_cap;
index d4326937db4840821e3c03e69ac1b525346fe8f0..a17cd56bce7105f0c913f429a0484cbec72aff77 100644 (file)
@@ -57,6 +57,11 @@ bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
                         enum hinic3_nic_feature_cap feature_bits);
 void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap);
 
+int hinic3_set_rx_lro_state(struct hinic3_hwdev *hwdev, u8 lro_en,
+                           u32 lro_timer, u8 lro_max_pkt_len);
+int hinic3_set_rx_vlan_offload(struct hinic3_hwdev *hwdev, u8 en);
+int hinic3_set_vlan_filter(struct hinic3_hwdev *hwdev, u32 vlan_filter_ctrl);
+
 int hinic3_init_function_table(struct hinic3_nic_dev *nic_dev);
 int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu);
 
index b8c9c325a45abcc8a7eb0979afa2ce7158ff9f9c..a8e92e070d9e081f1d9149f3ebdb1109fb52ef88 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/netdevice.h>
 
 #include "hinic3_hw_cfg.h"
+#include "hinic3_hwdev.h"
 #include "hinic3_mgmt_interface.h"
 
 enum hinic3_flags {
@@ -98,6 +99,7 @@ struct hinic3_nic_dev {
 };
 
 void hinic3_set_netdev_ops(struct net_device *netdev);
+int hinic3_set_hw_features(struct net_device *netdev);
 int hinic3_qps_irq_init(struct net_device *netdev);
 void hinic3_qps_irq_uninit(struct net_device *netdev);