hinic: add vlan offload support
authorXue Chaojing <xuechaojing@huawei.com>
Sat, 29 Jun 2019 02:26:27 +0000 (02:26 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 29 Jun 2019 17:28:23 +0000 (10:28 -0700)
This patch adds vlan offload support for the HINIC driver.

Signed-off-by: Xue Chaojing <xuechaojing@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h
drivers/net/ethernet/huawei/hinic/hinic_main.c
drivers/net/ethernet/huawei/hinic/hinic_port.c
drivers/net/ethernet/huawei/hinic/hinic_port.h
drivers/net/ethernet/huawei/hinic/hinic_rx.c
drivers/net/ethernet/huawei/hinic/hinic_tx.c

index e83e3bf850d56d9f1a7dc162ebb3c8547fff254d..984c98f332589d5558669c16f5c82a862b241fd9 100644 (file)
@@ -45,6 +45,8 @@ enum hinic_port_cmd {
 
        HINIC_PORT_CMD_SET_RX_CSUM      = 26,
 
+       HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD = 27,
+
        HINIC_PORT_CMD_GET_PORT_STATISTICS = 28,
 
        HINIC_PORT_CMD_CLEAR_PORT_STATISTICS = 29,
index c6b809e24983f09a0fb7568c830db9d998245b0b..f4b6d2c1061ffb26aaa72b25225bca7b7a7bdc12 100644 (file)
 
 #define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_SHIFT             0
 #define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK              0xFFFU
+#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_SHIFT              21
+#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_MASK               0x1U
 
 #define RQ_CQE_OFFOLAD_TYPE_GET(val, member)           (((val) >> \
                                RQ_CQE_OFFOLAD_TYPE_##member##_SHIFT) & \
 #define HINIC_GET_RX_PKT_TYPE(offload_type)    \
                        RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_TYPE)
 
+#define HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)     \
+                       RQ_CQE_OFFOLAD_TYPE_GET(offload_type, VLAN_EN)
+
+#define RQ_CQE_SGE_VLAN_MASK                           0xFFFFU
+#define RQ_CQE_SGE_VLAN_SHIFT                          0
+
+#define RQ_CQE_SGE_GET(val, member)                    (((val) >> \
+                                       RQ_CQE_SGE_##member##_SHIFT) & \
+                                       RQ_CQE_SGE_##member##_MASK)
+
+#define HINIC_GET_RX_VLAN_TAG(vlan_len)        \
+               RQ_CQE_SGE_GET(vlan_len, VLAN)
+
 #define HINIC_RSS_TYPE_VALID_SHIFT                     23
 #define HINIC_RSS_TYPE_TCP_IPV6_EXT_SHIFT              24
 #define HINIC_RSS_TYPE_IPV6_EXT_SHIFT                  25
index ceb0e247f52d41a7b5c9c5972655bbcba23ea802..2411ad270c98e3a20e35ba840f1d121e0b70fab7 100644 (file)
@@ -836,14 +836,14 @@ static const struct net_device_ops hinic_netdev_ops = {
        .ndo_get_stats64 = hinic_get_stats64,
        .ndo_fix_features = hinic_fix_features,
        .ndo_set_features = hinic_set_features,
-
 };
 
 static void netdev_features_init(struct net_device *netdev)
 {
        netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
                              NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
-                             NETIF_F_RXCSUM | NETIF_F_LRO;
+                             NETIF_F_RXCSUM | NETIF_F_LRO |
+                             NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 
        netdev->vlan_features = netdev->hw_features;
 
@@ -923,6 +923,11 @@ static int set_features(struct hinic_dev *nic_dev,
                                             HINIC_LRO_MAX_WQE_NUM_DEFAULT);
        }
 
+       if (changed & NETIF_F_HW_VLAN_CTAG_RX)
+               err = hinic_set_rx_vlan_offload(nic_dev,
+                                               !!(features &
+                                                  NETIF_F_HW_VLAN_CTAG_RX));
+
        return err;
 }
 
index c07adf793215a7820d4faef5ae62c7f88d1f36de..1bbeb91be808aad3adec3b2c84b101227c4d2940 100644 (file)
@@ -431,6 +431,36 @@ int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
        return 0;
 }
 
+int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en)
+{
+       struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       struct hinic_vlan_cfg vlan_cfg;
+       struct hinic_hwif *hwif;
+       struct pci_dev *pdev;
+       u16 out_size;
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       hwif = hwdev->hwif;
+       pdev = hwif->pdev;
+       vlan_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
+       vlan_cfg.vlan_rx_offload = en;
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD,
+                                &vlan_cfg, sizeof(vlan_cfg),
+                                &vlan_cfg, &out_size);
+       if (err || !out_size || vlan_cfg.status) {
+               dev_err(&pdev->dev,
+                       "Failed to set rx vlan offload, err: %d, status: 0x%x, out size: 0x%x\n",
+                       err, vlan_cfg.status, out_size);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs)
 {
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
index 16140a13000b54676c3327143ac40b576263d817..1bc47c7a5c002080fe031a22e17cb508bd0d276a 100644 (file)
@@ -223,6 +223,16 @@ struct hinic_lro_timer {
        u32     timer;
 };
 
+struct hinic_vlan_cfg {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_id;
+       u8      vlan_rx_offload;
+       u8      rsvd1[5];
+};
+
 struct hinic_rss_template_mgmt {
        u8      status;
        u8      version;
@@ -558,4 +568,7 @@ int hinic_get_phy_port_stats(struct hinic_dev *nic_dev,
 
 int hinic_get_vport_stats(struct hinic_dev *nic_dev,
                          struct hinic_vport_stats *stats);
+
+int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en);
+
 #endif
index 609ad4333cddd955a40fd9b198fb742d370686f1..56ea6d692f1c3dda7fd329c555d1a70696c0dd1a 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/prefetch.h>
 #include <linux/cpumask.h>
+#include <linux/if_vlan.h>
 #include <asm/barrier.h>
 
 #include "hinic_common.h"
@@ -325,6 +326,7 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb,
 static int rxq_recv(struct hinic_rxq *rxq, int budget)
 {
        struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq);
+       struct net_device *netdev = rxq->netdev;
        u64 pkt_len = 0, rx_bytes = 0;
        struct hinic_rq *rq = rxq->rq;
        struct hinic_rq_wqe *rq_wqe;
@@ -334,8 +336,11 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
        struct hinic_sge sge;
        unsigned int status;
        struct sk_buff *skb;
+       u32 offload_type;
        u16 ci, num_lro;
        u16 num_wqe = 0;
+       u32 vlan_len;
+       u16 vid;
 
        while (pkts < budget) {
                num_wqes = 0;
@@ -368,6 +373,14 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
                hinic_rq_put_wqe(rq, ci,
                                 (num_wqes + 1) * HINIC_RQ_WQE_SIZE);
 
+               offload_type = be32_to_cpu(cqe->offload_type);
+               vlan_len = be32_to_cpu(cqe->len);
+               if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                   HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)) {
+                       vid = HINIC_GET_RX_VLAN_TAG(vlan_len);
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
+               }
+
                skb_record_rx_queue(skb, qp->q_id);
                skb->protocol = eth_type_trans(skb, rxq->netdev);
 
index f4f76370cd65104e46653b23dd413dbcf32e7795..9c78251f9c39fb600cf5d771013e8759df1d9a56 100644 (file)
@@ -407,10 +407,20 @@ static int offload_csum(struct hinic_sq_task *task, u32 *queue_info,
        return 1;
 }
 
+static void offload_vlan(struct hinic_sq_task *task, u32 *queue_info,
+                        u16 vlan_tag, u16 vlan_pri)
+{
+       task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(vlan_tag, VLAN_TAG) |
+                               HINIC_SQ_TASK_INFO0_SET(1U, VLAN_OFFLOAD);
+
+       *queue_info |= HINIC_SQ_CTRL_SET(vlan_pri, QUEUE_INFO_PRI);
+}
+
 static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task,
                            u32 *queue_info)
 {
        enum hinic_offload_type offload = 0;
+       u16 vlan_tag;
        int enabled;
 
        enabled = offload_tso(task, queue_info, skb);
@@ -424,6 +434,13 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task,
                return -EPROTONOSUPPORT;
        }
 
+       if (unlikely(skb_vlan_tag_present(skb))) {
+               vlan_tag = skb_vlan_tag_get(skb);
+               offload_vlan(task, queue_info, vlan_tag,
+                            vlan_tag >> VLAN_PRIO_SHIFT);
+               offload |= TX_OFFLOAD_VLAN;
+       }
+
        if (offload)
                hinic_task_set_l2hdr(task, skb_network_offset(skb));