net: hns3: Add flow director initialization
authorJian Shen <shenjian15@huawei.com>
Mon, 1 Oct 2018 11:46:41 +0000 (12:46 +0100)
committerDavid S. Miller <davem@davemloft.net>
Tue, 2 Oct 2018 05:57:45 +0000 (22:57 -0700)
Flow director is a new feature supported by hardware with revision 0x21.
This patch adds flow direcor initialization for each PF. It queries flow
director mode and tcam resource from firmware, selects tuples used for
input key.

Signed-off-by: Jian Shen <shenjian15@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h

index dea422c53738b96a79aeaec9802b43370ed1fc46..d3e5190ce7010d2301231e920e29033c88dd8913 100644 (file)
@@ -51,6 +51,7 @@
 #define HNAE3_KNIC_CLIENT_INITED_B             0x3
 #define HNAE3_UNIC_CLIENT_INITED_B             0x4
 #define HNAE3_ROCE_CLIENT_INITED_B             0x5
+#define HNAE3_DEV_SUPPORT_FD_B                 0x6
 
 #define HNAE3_DEV_SUPPORT_ROCE_DCB_BITS (BIT(HNAE3_DEV_SUPPORT_DCB_B) |\
                BIT(HNAE3_DEV_SUPPORT_ROCE_B))
@@ -61,6 +62,9 @@
 #define hnae3_dev_dcb_supported(hdev) \
        hnae3_get_bit(hdev->ae_dev->flag, HNAE3_DEV_SUPPORT_DCB_B)
 
+#define hnae3_dev_fd_supported(hdev) \
+       hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B)
+
 #define ring_ptr_move_fw(ring, p) \
        ((ring)->p = ((ring)->p + 1) % (ring)->desc_num)
 #define ring_ptr_move_bw(ring, p) \
index 1050652274a0e448dcd241d7a57bf58bd51c9edf..605f029427ee000aeb9047c5a92e606f79ad8800 100644 (file)
@@ -1622,6 +1622,13 @@ static void hns3_disable_sriov(struct pci_dev *pdev)
        pci_disable_sriov(pdev);
 }
 
+static void hns3_get_dev_capability(struct pci_dev *pdev,
+                                   struct hnae3_ae_dev *ae_dev)
+{
+       if (pdev->revision >= 0x21)
+               hnae3_set_bit(ae_dev->flag, HNAE3_DEV_SUPPORT_FD_B, 1);
+}
+
 /* hns3_probe - Device initialization routine
  * @pdev: PCI device information struct
  * @ent: entry in hns3_pci_tbl
@@ -1647,6 +1654,7 @@ static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        ae_dev->pdev = pdev;
        ae_dev->flag = ent->driver_data;
        ae_dev->dev_type = HNAE3_DEV_KNIC;
+       hns3_get_dev_capability(pdev, ae_dev);
        pci_set_drvdata(pdev, ae_dev);
 
        hnae3_register_ae_dev(ae_dev);
index 842dc3f052471ce08c2214b93940226e275480ed..827bc51745ca8846442811c98a9b667137d56774 100644 (file)
@@ -190,6 +190,11 @@ enum hclge_opcode_type {
        HCLGE_OPC_VLAN_FILTER_PF_CFG    = 0x1101,
        HCLGE_OPC_VLAN_FILTER_VF_CFG    = 0x1102,
 
+       /* Flow Director commands */
+       HCLGE_OPC_FD_MODE_CTRL          = 0x1200,
+       HCLGE_OPC_FD_GET_ALLOCATION     = 0x1201,
+       HCLGE_OPC_FD_KEY_CONFIG         = 0x1202,
+
        /* MDIO command */
        HCLGE_OPC_MDIO_CONFIG           = 0x1900,
 
@@ -819,6 +824,33 @@ struct hclge_set_led_state_cmd {
        u8 rsv2[20];
 };
 
+struct hclge_get_fd_mode_cmd {
+       u8 mode;
+       u8 enable;
+       u8 rsv[22];
+};
+
+struct hclge_get_fd_allocation_cmd {
+       __le32 stage1_entry_num;
+       __le32 stage2_entry_num;
+       __le16 stage1_counter_num;
+       __le16 stage2_counter_num;
+       u8 rsv[12];
+};
+
+struct hclge_set_fd_key_config_cmd {
+       u8 stage;
+       u8 key_select;
+       u8 inner_sipv6_word_en;
+       u8 inner_dipv6_word_en;
+       u8 outer_sipv6_word_en;
+       u8 outer_dipv6_word_en;
+       u8 rsv1[2];
+       __le32 tuple_mask;
+       __le32 meta_data_mask;
+       u8 rsv2[8];
+};
+
 int hclge_cmd_init(struct hclge_dev *hdev);
 static inline void hclge_write_reg(void __iomem *base, u32 reg, u32 value)
 {
index c17ceeefa4534aefdb50792091d4f4c54d9460d4..348714dbb4cf8503a0a12d5d058feb0321f5fb7c 100644 (file)
@@ -3328,6 +3328,149 @@ static void hclge_set_promisc_mode(struct hnae3_handle *handle, bool en_uc_pmc,
        hclge_cmd_set_promisc_mode(hdev, &param);
 }
 
+static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode)
+{
+       struct hclge_get_fd_mode_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true);
+
+       req = (struct hclge_get_fd_mode_cmd *)desc.data;
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "get fd mode fail, ret=%d\n", ret);
+               return ret;
+       }
+
+       *fd_mode = req->mode;
+
+       return ret;
+}
+
+static int hclge_get_fd_allocation(struct hclge_dev *hdev,
+                                  u32 *stage1_entry_num,
+                                  u32 *stage2_entry_num,
+                                  u16 *stage1_counter_num,
+                                  u16 *stage2_counter_num)
+{
+       struct hclge_get_fd_allocation_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_GET_ALLOCATION, true);
+
+       req = (struct hclge_get_fd_allocation_cmd *)desc.data;
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=%d\n",
+                       ret);
+               return ret;
+       }
+
+       *stage1_entry_num = le32_to_cpu(req->stage1_entry_num);
+       *stage2_entry_num = le32_to_cpu(req->stage2_entry_num);
+       *stage1_counter_num = le16_to_cpu(req->stage1_counter_num);
+       *stage2_counter_num = le16_to_cpu(req->stage2_counter_num);
+
+       return ret;
+}
+
+static int hclge_set_fd_key_config(struct hclge_dev *hdev, int stage_num)
+{
+       struct hclge_set_fd_key_config_cmd *req;
+       struct hclge_fd_key_cfg *stage;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_KEY_CONFIG, false);
+
+       req = (struct hclge_set_fd_key_config_cmd *)desc.data;
+       stage = &hdev->fd_cfg.key_cfg[stage_num];
+       req->stage = stage_num;
+       req->key_select = stage->key_sel;
+       req->inner_sipv6_word_en = stage->inner_sipv6_word_en;
+       req->inner_dipv6_word_en = stage->inner_dipv6_word_en;
+       req->outer_sipv6_word_en = stage->outer_sipv6_word_en;
+       req->outer_dipv6_word_en = stage->outer_dipv6_word_en;
+       req->tuple_mask = cpu_to_le32(~stage->tuple_active);
+       req->meta_data_mask = cpu_to_le32(~stage->meta_data_active);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret)
+               dev_err(&hdev->pdev->dev, "set fd key fail, ret=%d\n", ret);
+
+       return ret;
+}
+
+static int hclge_init_fd_config(struct hclge_dev *hdev)
+{
+#define LOW_2_WORDS            0x03
+       struct hclge_fd_key_cfg *key_cfg;
+       int ret;
+
+       if (!hnae3_dev_fd_supported(hdev))
+               return 0;
+
+       ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode);
+       if (ret)
+               return ret;
+
+       switch (hdev->fd_cfg.fd_mode) {
+       case HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1:
+               hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH;
+               break;
+       case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1:
+               hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH / 2;
+               break;
+       default:
+               dev_err(&hdev->pdev->dev,
+                       "Unsupported flow director mode %d\n",
+                       hdev->fd_cfg.fd_mode);
+               return -EOPNOTSUPP;
+       }
+
+       hdev->fd_cfg.fd_en = true;
+       hdev->fd_cfg.proto_support =
+               TCP_V4_FLOW | UDP_V4_FLOW | SCTP_V4_FLOW | TCP_V6_FLOW |
+               UDP_V6_FLOW | SCTP_V6_FLOW | IPV4_USER_FLOW | IPV6_USER_FLOW;
+       key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1];
+       key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE,
+       key_cfg->inner_sipv6_word_en = LOW_2_WORDS;
+       key_cfg->inner_dipv6_word_en = LOW_2_WORDS;
+       key_cfg->outer_sipv6_word_en = 0;
+       key_cfg->outer_dipv6_word_en = 0;
+
+       key_cfg->tuple_active = BIT(INNER_VLAN_TAG_FST) | BIT(INNER_ETH_TYPE) |
+                               BIT(INNER_IP_PROTO) | BIT(INNER_IP_TOS) |
+                               BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
+                               BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
+
+       /* If use max 400bit key, we can support tuples for ether type */
+       if (hdev->fd_cfg.max_key_length == MAX_KEY_LENGTH) {
+               hdev->fd_cfg.proto_support |= ETHER_FLOW;
+               key_cfg->tuple_active |=
+                               BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC);
+       }
+
+       /* roce_type is used to filter roce frames
+        * dst_vport is used to specify the rule
+        */
+       key_cfg->meta_data_active = BIT(ROCE_TYPE) | BIT(DST_VPORT);
+
+       ret = hclge_get_fd_allocation(hdev,
+                                     &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1],
+                                     &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_2],
+                                     &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1],
+                                     &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_2]);
+       if (ret)
+               return ret;
+
+       return hclge_set_fd_key_config(hdev, HCLGE_FD_STAGE_1);
+}
+
 static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
 {
        struct hclge_desc desc;
@@ -5502,6 +5645,13 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
                goto err_mdiobus_unreg;
        }
 
+       ret = hclge_init_fd_config(hdev);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "fd table init fail, ret=%d\n", ret);
+               goto err_mdiobus_unreg;
+       }
+
        hclge_dcb_ops_set(hdev);
 
        timer_setup(&hdev->service_timer, hclge_service_timer, 0);
@@ -5608,6 +5758,13 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
+       ret = hclge_init_fd_config(hdev);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "fd table init fail, ret=%d\n", ret);
+               return ret;
+       }
+
        dev_info(&pdev->dev, "Reset done, %s driver initialization finished.\n",
                 HCLGE_DRIVER_NAME);
 
index 7841b830a71665cf091ec43b14bc378c9c0e3d2c..1e7ad5a637a9244d889bf2367cbf376341e4ce5d 100644 (file)
@@ -359,6 +359,152 @@ struct hclge_vlan_type_cfg {
        u16 tx_in_vlan_type;
 };
 
+enum HCLGE_FD_MODE {
+       HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1,
+       HCLGE_FD_MODE_DEPTH_1K_WIDTH_400B_STAGE_2,
+       HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1,
+       HCLGE_FD_MODE_DEPTH_2K_WIDTH_200B_STAGE_2,
+};
+
+enum HCLGE_FD_KEY_TYPE {
+       HCLGE_FD_KEY_BASE_ON_PTYPE,
+       HCLGE_FD_KEY_BASE_ON_TUPLE,
+};
+
+enum HCLGE_FD_STAGE {
+       HCLGE_FD_STAGE_1,
+       HCLGE_FD_STAGE_2,
+};
+
+/* OUTER_XXX indicates tuples in tunnel header of tunnel packet
+ * INNER_XXX indicate tuples in tunneled header of tunnel packet or
+ *           tuples of non-tunnel packet
+ */
+enum HCLGE_FD_TUPLE {
+       OUTER_DST_MAC,
+       OUTER_SRC_MAC,
+       OUTER_VLAN_TAG_FST,
+       OUTER_VLAN_TAG_SEC,
+       OUTER_ETH_TYPE,
+       OUTER_L2_RSV,
+       OUTER_IP_TOS,
+       OUTER_IP_PROTO,
+       OUTER_SRC_IP,
+       OUTER_DST_IP,
+       OUTER_L3_RSV,
+       OUTER_SRC_PORT,
+       OUTER_DST_PORT,
+       OUTER_L4_RSV,
+       OUTER_TUN_VNI,
+       OUTER_TUN_FLOW_ID,
+       INNER_DST_MAC,
+       INNER_SRC_MAC,
+       INNER_VLAN_TAG_FST,
+       INNER_VLAN_TAG_SEC,
+       INNER_ETH_TYPE,
+       INNER_L2_RSV,
+       INNER_IP_TOS,
+       INNER_IP_PROTO,
+       INNER_SRC_IP,
+       INNER_DST_IP,
+       INNER_L3_RSV,
+       INNER_SRC_PORT,
+       INNER_DST_PORT,
+       INNER_L4_RSV,
+       MAX_TUPLE,
+};
+
+enum HCLGE_FD_META_DATA {
+       PACKET_TYPE_ID,
+       IP_FRAGEMENT,
+       ROCE_TYPE,
+       NEXT_KEY,
+       VLAN_NUMBER,
+       SRC_VPORT,
+       DST_VPORT,
+       TUNNEL_PACKET,
+       MAX_META_DATA,
+};
+
+struct key_info {
+       u8 key_type;
+       u8 key_length;
+};
+
+static const struct key_info meta_data_key_info[] = {
+       { PACKET_TYPE_ID, 6},
+       { IP_FRAGEMENT, 1},
+       { ROCE_TYPE, 1},
+       { NEXT_KEY, 5},
+       { VLAN_NUMBER, 2},
+       { SRC_VPORT, 12},
+       { DST_VPORT, 12},
+       { TUNNEL_PACKET, 1},
+};
+
+static const struct key_info tuple_key_info[] = {
+       { OUTER_DST_MAC, 48},
+       { OUTER_SRC_MAC, 48},
+       { OUTER_VLAN_TAG_FST, 16},
+       { OUTER_VLAN_TAG_SEC, 16},
+       { OUTER_ETH_TYPE, 16},
+       { OUTER_L2_RSV, 16},
+       { OUTER_IP_TOS, 8},
+       { OUTER_IP_PROTO, 8},
+       { OUTER_SRC_IP, 32},
+       { OUTER_DST_IP, 32},
+       { OUTER_L3_RSV, 16},
+       { OUTER_SRC_PORT, 16},
+       { OUTER_DST_PORT, 16},
+       { OUTER_L4_RSV, 32},
+       { OUTER_TUN_VNI, 24},
+       { OUTER_TUN_FLOW_ID, 8},
+       { INNER_DST_MAC, 48},
+       { INNER_SRC_MAC, 48},
+       { INNER_VLAN_TAG_FST, 16},
+       { INNER_VLAN_TAG_SEC, 16},
+       { INNER_ETH_TYPE, 16},
+       { INNER_L2_RSV, 16},
+       { INNER_IP_TOS, 8},
+       { INNER_IP_PROTO, 8},
+       { INNER_SRC_IP, 32},
+       { INNER_DST_IP, 32},
+       { INNER_L3_RSV, 16},
+       { INNER_SRC_PORT, 16},
+       { INNER_DST_PORT, 16},
+       { INNER_L4_RSV, 32},
+};
+
+#define MAX_KEY_LENGTH 400
+#define MAX_KEY_DWORDS DIV_ROUND_UP(MAX_KEY_LENGTH / 8, 4)
+#define MAX_KEY_BYTES  (MAX_KEY_DWORDS * 4)
+#define MAX_META_DATA_LENGTH   32
+
+enum HCLGE_FD_PACKET_TYPE {
+       NIC_PACKET,
+       ROCE_PACKET,
+};
+
+struct hclge_fd_key_cfg {
+       u8 key_sel;
+       u8 inner_sipv6_word_en;
+       u8 inner_dipv6_word_en;
+       u8 outer_sipv6_word_en;
+       u8 outer_dipv6_word_en;
+       u32 tuple_active;
+       u32 meta_data_active;
+};
+
+struct hclge_fd_cfg {
+       u8 fd_mode;
+       u8 fd_en;
+       u16 max_key_length;
+       u32 proto_support;
+       u32 rule_num[2]; /* rule entry number */
+       u16 cnt_num[2]; /* rule hit counter number */
+       struct hclge_fd_key_cfg key_cfg[2];
+};
+
 #define HCLGE_VPORT_NUM 256
 struct hclge_dev {
        struct pci_dev *pdev;
@@ -448,6 +594,8 @@ struct hclge_dev {
        struct hclge_vlan_type_cfg vlan_type_cfg;
 
        unsigned long vlan_table[VLAN_N_VID][BITS_TO_LONGS(HCLGE_VPORT_NUM)];
+
+       struct hclge_fd_cfg fd_cfg;
 };
 
 /* VPort level vlan tag configuration for TX direction */