nfp: flower: offload tc flows of multiple conntrack zones
authorWentao Jia <wentao.jia@corigine.com>
Tue, 14 Mar 2023 06:36:10 +0000 (08:36 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 16 Mar 2023 05:16:23 +0000 (22:16 -0700)
If goto_chain action present in the post ct flow rule, merge flow rules
in this ct-zone, create a new pre_ct entry as the pre ct flow rule of
next ct-zone, but do not offload merged flow rules to firmware. Repeat
the process in the next ct-zone until no goto_chain action present in
the post ct flow rule in a certain ct-zone, merged all the flow rules.
Offload to firmware finally.

Signed-off-by: Wentao Jia <wentao.jia@corigine.com>
Acked-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Louis Peens <louis.peens@corigine.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/netronome/nfp/flower/conntrack.c
drivers/net/ethernet/netronome/nfp/flower/conntrack.h
drivers/net/ethernet/netronome/nfp/flower/offload.c

index ecffb6b..7303217 100644 (file)
@@ -522,6 +522,21 @@ static int nfp_ct_check_vlan_merge(struct flow_action_entry *a_in,
        return 0;
 }
 
+/* Extra check for multiple ct-zones merge
+ * currently surpport nft entries merge check in different zones
+ */
+static int nfp_ct_merge_extra_check(struct nfp_fl_ct_flow_entry *nft_entry,
+                                   struct nfp_fl_ct_tc_merge *tc_m_entry)
+{
+       struct nfp_fl_nft_tc_merge *prev_nft_m_entry;
+       struct nfp_fl_ct_flow_entry *pre_ct_entry;
+
+       pre_ct_entry = tc_m_entry->pre_ct_parent;
+       prev_nft_m_entry = pre_ct_entry->prev_m_entries[pre_ct_entry->num_prev_m_entries - 1];
+
+       return nfp_ct_merge_check(prev_nft_m_entry->nft_parent, nft_entry);
+}
+
 static int nfp_ct_merge_act_check(struct nfp_fl_ct_flow_entry *pre_ct_entry,
                                  struct nfp_fl_ct_flow_entry *post_ct_entry,
                                  struct nfp_fl_ct_flow_entry *nft_entry)
@@ -796,27 +811,34 @@ static int nfp_fl_ct_add_offload(struct nfp_fl_nft_tc_merge *m_entry)
 {
        enum nfp_flower_tun_type tun_type = NFP_FL_TUNNEL_NONE;
        struct nfp_fl_ct_zone_entry *zt = m_entry->zt;
+       struct flow_rule *rules[NFP_MAX_ENTRY_RULES];
+       struct nfp_fl_ct_flow_entry *pre_ct_entry;
        struct nfp_fl_key_ls key_layer, tmp_layer;
        struct nfp_flower_priv *priv = zt->priv;
        u16 key_map[_FLOW_PAY_LAYERS_MAX];
        struct nfp_fl_payload *flow_pay;
-
-       struct flow_rule *rules[_CT_TYPE_MAX];
-       int num_rules = _CT_TYPE_MAX;
        u8 *key, *msk, *kdata, *mdata;
        struct nfp_port *port = NULL;
+       int num_rules, err, i, j = 0;
        struct net_device *netdev;
        bool qinq_sup;
        u32 port_id;
        u16 offset;
-       int i, err;
 
        netdev = m_entry->netdev;
        qinq_sup = !!(priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ);
 
-       rules[CT_TYPE_PRE_CT] = m_entry->tc_m_parent->pre_ct_parent->rule;
-       rules[CT_TYPE_NFT] = m_entry->nft_parent->rule;
-       rules[CT_TYPE_POST_CT] = m_entry->tc_m_parent->post_ct_parent->rule;
+       pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent;
+       num_rules = pre_ct_entry->num_prev_m_entries * 2 + _CT_TYPE_MAX;
+
+       for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++) {
+               rules[j++] = pre_ct_entry->prev_m_entries[i]->tc_m_parent->pre_ct_parent->rule;
+               rules[j++] = pre_ct_entry->prev_m_entries[i]->nft_parent->rule;
+       }
+
+       rules[j++] = m_entry->tc_m_parent->pre_ct_parent->rule;
+       rules[j++] = m_entry->nft_parent->rule;
+       rules[j++] = m_entry->tc_m_parent->post_ct_parent->rule;
 
        memset(&key_layer, 0, sizeof(struct nfp_fl_key_ls));
        memset(&key_map, 0, sizeof(key_map));
@@ -1181,6 +1203,12 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt,
        if (err)
                return err;
 
+       if (pre_ct_entry->num_prev_m_entries > 0) {
+               err = nfp_ct_merge_extra_check(nft_entry, tc_m_entry);
+               if (err)
+                       return err;
+       }
+
        /* Combine tc_merge and nft cookies for this cookie. */
        new_cookie[0] = tc_m_entry->cookie[0];
        new_cookie[1] = tc_m_entry->cookie[1];
@@ -1211,11 +1239,6 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt,
        list_add(&nft_m_entry->tc_merge_list, &tc_m_entry->children);
        list_add(&nft_m_entry->nft_flow_list, &nft_entry->children);
 
-       /* Generate offload structure and send to nfp */
-       err = nfp_fl_ct_add_offload(nft_m_entry);
-       if (err)
-               goto err_nft_ct_offload;
-
        err = rhashtable_insert_fast(&zt->nft_merge_tb, &nft_m_entry->hash_node,
                                     nfp_nft_ct_merge_params);
        if (err)
@@ -1223,12 +1246,20 @@ static int nfp_ct_do_nft_merge(struct nfp_fl_ct_zone_entry *zt,
 
        zt->nft_merge_count++;
 
+       if (post_ct_entry->goto_chain_index > 0)
+               return nfp_fl_create_new_pre_ct(nft_m_entry);
+
+       /* Generate offload structure and send to nfp */
+       err = nfp_fl_ct_add_offload(nft_m_entry);
+       if (err)
+               goto err_nft_ct_offload;
+
        return err;
 
-err_nft_ct_merge_insert:
+err_nft_ct_offload:
        nfp_fl_ct_del_offload(zt->priv->app, nft_m_entry->tc_flower_cookie,
                              nft_m_entry->netdev);
-err_nft_ct_offload:
+err_nft_ct_merge_insert:
        list_del(&nft_m_entry->tc_merge_list);
        list_del(&nft_m_entry->nft_flow_list);
        kfree(nft_m_entry);
@@ -1474,7 +1505,7 @@ nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt,
 
        entry->zt = zt;
        entry->netdev = netdev;
-       entry->cookie = flow->cookie;
+       entry->cookie = flow->cookie > 0 ? flow->cookie : (unsigned long)entry;
        entry->chain_index = flow->common.chain_index;
        entry->tun_offset = NFP_FL_CT_NO_TUN;
 
@@ -1514,6 +1545,9 @@ nfp_fl_ct_flow_entry *nfp_fl_ct_add_flow(struct nfp_fl_ct_zone_entry *zt,
 
        INIT_LIST_HEAD(&entry->children);
 
+       if (flow->cookie == 0)
+               return entry;
+
        /* Now add a ct map entry to flower-priv */
        map = get_hashentry(&zt->priv->ct_map_table, &flow->cookie,
                            nfp_ct_map_params, sizeof(*map));
@@ -1572,6 +1606,14 @@ static void cleanup_nft_merge_entry(struct nfp_fl_nft_tc_merge *m_entry)
        list_del(&m_entry->tc_merge_list);
        list_del(&m_entry->nft_flow_list);
 
+       if (m_entry->next_pre_ct_entry) {
+               struct nfp_fl_ct_map_entry pre_ct_map_ent;
+
+               pre_ct_map_ent.ct_entry = m_entry->next_pre_ct_entry;
+               pre_ct_map_ent.cookie = 0;
+               nfp_fl_ct_del_flow(&pre_ct_map_ent);
+       }
+
        kfree(m_entry);
 }
 
@@ -1742,7 +1784,8 @@ nfp_ct_merge_nft_with_tc(struct nfp_fl_ct_flow_entry *nft_entry,
 int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv,
                            struct net_device *netdev,
                            struct flow_cls_offload *flow,
-                           struct netlink_ext_ack *extack)
+                           struct netlink_ext_ack *extack,
+                           struct nfp_fl_nft_tc_merge *m_entry)
 {
        struct flow_action_entry *ct_act, *ct_goto;
        struct nfp_fl_ct_flow_entry *ct_entry;
@@ -1787,6 +1830,20 @@ int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv,
        ct_entry->type = CT_TYPE_PRE_CT;
        ct_entry->chain_index = flow->common.chain_index;
        ct_entry->goto_chain_index = ct_goto->chain_index;
+
+       if (m_entry) {
+               struct nfp_fl_ct_flow_entry *pre_ct_entry;
+               int i;
+
+               pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent;
+               for (i = 0; i < pre_ct_entry->num_prev_m_entries; i++)
+                       ct_entry->prev_m_entries[i] = pre_ct_entry->prev_m_entries[i];
+               ct_entry->prev_m_entries[i++] = m_entry;
+               ct_entry->num_prev_m_entries = i;
+
+               m_entry->next_pre_ct_entry = ct_entry;
+       }
+
        list_add(&ct_entry->list_node, &zt->pre_ct_list);
        zt->pre_ct_count++;
 
@@ -1864,6 +1921,28 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv,
        return 0;
 }
 
+int nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge *m_entry)
+{
+       struct nfp_fl_ct_flow_entry *pre_ct_entry, *post_ct_entry;
+       struct flow_cls_offload new_pre_ct_flow;
+       int err;
+
+       pre_ct_entry = m_entry->tc_m_parent->pre_ct_parent;
+       if (pre_ct_entry->num_prev_m_entries >= NFP_MAX_RECIRC_CT_ZONES - 1)
+               return -1;
+
+       post_ct_entry = m_entry->tc_m_parent->post_ct_parent;
+       memset(&new_pre_ct_flow, 0, sizeof(struct flow_cls_offload));
+       new_pre_ct_flow.rule = post_ct_entry->rule;
+       new_pre_ct_flow.common.chain_index = post_ct_entry->chain_index;
+
+       err = nfp_fl_ct_handle_pre_ct(pre_ct_entry->zt->priv,
+                                     pre_ct_entry->netdev,
+                                     &new_pre_ct_flow, NULL,
+                                     m_entry);
+       return err;
+}
+
 static void
 nfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge *nft_merge,
                    enum ct_entry_type type, u64 *m_pkts,
@@ -1909,6 +1988,32 @@ nfp_fl_ct_sub_stats(struct nfp_fl_nft_tc_merge *nft_merge,
                                  0, priv->stats[ctx_id].used,
                                  FLOW_ACTION_HW_STATS_DELAYED);
        }
+
+       /* Update previous pre_ct/post_ct/nft flow stats */
+       if (nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries > 0) {
+               struct nfp_fl_nft_tc_merge *tmp_nft_merge;
+               int i;
+
+               for (i = 0; i < nft_merge->tc_m_parent->pre_ct_parent->num_prev_m_entries; i++) {
+                       tmp_nft_merge = nft_merge->tc_m_parent->pre_ct_parent->prev_m_entries[i];
+                       flow_stats_update(&tmp_nft_merge->tc_m_parent->pre_ct_parent->stats,
+                                         priv->stats[ctx_id].bytes,
+                                         priv->stats[ctx_id].pkts,
+                                         0, priv->stats[ctx_id].used,
+                                         FLOW_ACTION_HW_STATS_DELAYED);
+                       flow_stats_update(&tmp_nft_merge->tc_m_parent->post_ct_parent->stats,
+                                         priv->stats[ctx_id].bytes,
+                                         priv->stats[ctx_id].pkts,
+                                         0, priv->stats[ctx_id].used,
+                                         FLOW_ACTION_HW_STATS_DELAYED);
+                       flow_stats_update(&tmp_nft_merge->nft_parent->stats,
+                                         priv->stats[ctx_id].bytes,
+                                         priv->stats[ctx_id].pkts,
+                                         0, priv->stats[ctx_id].used,
+                                         FLOW_ACTION_HW_STATS_DELAYED);
+               }
+       }
+
        /* Reset stats from the nfp */
        priv->stats[ctx_id].pkts = 0;
        priv->stats[ctx_id].bytes = 0;
@@ -2113,10 +2218,12 @@ int nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent)
        switch (ct_entry->type) {
        case CT_TYPE_PRE_CT:
                zt->pre_ct_count--;
-               rhashtable_remove_fast(m_table, &ct_map_ent->hash_node,
-                                      nfp_ct_map_params);
+               if (ct_map_ent->cookie > 0)
+                       rhashtable_remove_fast(m_table, &ct_map_ent->hash_node,
+                                              nfp_ct_map_params);
                nfp_fl_ct_clean_flow_entry(ct_entry);
-               kfree(ct_map_ent);
+               if (ct_map_ent->cookie > 0)
+                       kfree(ct_map_ent);
 
                if (!zt->pre_ct_count) {
                        zt->nft = NULL;
index 9440ab7..c4ec783 100644 (file)
@@ -86,6 +86,9 @@ enum ct_entry_type {
        _CT_TYPE_MAX,
 };
 
+#define NFP_MAX_RECIRC_CT_ZONES 4
+#define NFP_MAX_ENTRY_RULES  (NFP_MAX_RECIRC_CT_ZONES * 2 + 1)
+
 enum nfp_nfp_layer_name {
        FLOW_PAY_META_TCI =    0,
        FLOW_PAY_INPORT,
@@ -114,27 +117,31 @@ enum nfp_nfp_layer_name {
  * @chain_index:       Chain index of the original flow
  * @goto_chain_index:  goto chain index of the flow
  * @netdev:    netdev structure.
- * @type:      Type of pre-entry from enum ct_entry_type
  * @zt:                Reference to the zone table this belongs to
  * @children:  List of tc_merge flows this flow forms part of
  * @rule:      Reference to the original TC flow rule
  * @stats:     Used to cache stats for updating
+ * @prev_m_entries:    Array of all previous nft_tc_merge entries
+ * @num_prev_m_entries:        The number of all previous nft_tc_merge entries
  * @tun_offset: Used to indicate tunnel action offset in action list
  * @flags:     Used to indicate flow flag like NAT which used by merge.
+ * @type:      Type of ct-entry from enum ct_entry_type
  */
 struct nfp_fl_ct_flow_entry {
        unsigned long cookie;
        struct list_head list_node;
        u32 chain_index;
        u32 goto_chain_index;
-       enum ct_entry_type type;
        struct net_device *netdev;
        struct nfp_fl_ct_zone_entry *zt;
        struct list_head children;
        struct flow_rule *rule;
        struct flow_stats stats;
+       struct nfp_fl_nft_tc_merge *prev_m_entries[NFP_MAX_RECIRC_CT_ZONES - 1];
+       u8 num_prev_m_entries;
        u8 tun_offset;          // Set to NFP_FL_CT_NO_TUN if no tun
        u8 flags;
+       u8 type;
 };
 
 /**
@@ -171,6 +178,7 @@ struct nfp_fl_ct_tc_merge {
  * @nft_parent:        The nft_entry parent
  * @tc_flower_cookie:  The cookie of the flow offloaded to the nfp
  * @flow_pay:  Reference to the offloaded flow struct
+ * @next_pre_ct_entry: Reference to the next ct zone pre ct entry
  */
 struct nfp_fl_nft_tc_merge {
        struct net_device *netdev;
@@ -183,6 +191,7 @@ struct nfp_fl_nft_tc_merge {
        struct nfp_fl_ct_flow_entry *nft_parent;
        unsigned long tc_flower_cookie;
        struct nfp_fl_payload *flow_pay;
+       struct nfp_fl_ct_flow_entry *next_pre_ct_entry;
 };
 
 /**
@@ -206,6 +215,7 @@ bool is_post_ct_flow(struct flow_cls_offload *flow);
  * @netdev:    netdev structure.
  * @flow:      TC flower classifier offload structure.
  * @extack:    Extack pointer for errors
+ * @m_entry:previous nfp_fl_nft_tc_merge entry
  *
  * Adds a new entry to the relevant zone table and tries to
  * merge with other +trk+est entries and offload if possible.
@@ -215,7 +225,8 @@ bool is_post_ct_flow(struct flow_cls_offload *flow);
 int nfp_fl_ct_handle_pre_ct(struct nfp_flower_priv *priv,
                            struct net_device *netdev,
                            struct flow_cls_offload *flow,
-                           struct netlink_ext_ack *extack);
+                           struct netlink_ext_ack *extack,
+                           struct nfp_fl_nft_tc_merge *m_entry);
 /**
  * nfp_fl_ct_handle_post_ct() - Handles +trk+est conntrack rules
  * @priv:      Pointer to app priv
@@ -234,6 +245,19 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv,
                             struct netlink_ext_ack *extack);
 
 /**
+ * nfp_fl_create_new_pre_ct() - create next ct_zone -trk conntrack rules
+ * @m_entry:previous nfp_fl_nft_tc_merge entry
+ *
+ * Create a new pre_ct entry from previous nfp_fl_nft_tc_merge entry
+ * to the next relevant zone table. Try to merge with other +trk+est
+ * entries and offload if possible. The created new pre_ct entry is
+ * linked to the previous nfp_fl_nft_tc_merge entry.
+ *
+ * Return: negative value on error, 0 if configured successfully.
+ */
+int nfp_fl_create_new_pre_ct(struct nfp_fl_nft_tc_merge *m_entry);
+
+/**
  * nfp_fl_ct_clean_flow_entry() - Free a nfp_fl_ct_flow_entry
  * @entry:     Flow entry to cleanup
  */
index 8593caf..18328eb 100644 (file)
@@ -1344,7 +1344,7 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
                port = nfp_port_from_netdev(netdev);
 
        if (is_pre_ct_flow(flow))
-               return nfp_fl_ct_handle_pre_ct(priv, netdev, flow, extack);
+               return nfp_fl_ct_handle_pre_ct(priv, netdev, flow, extack, NULL);
 
        if (is_post_ct_flow(flow))
                return nfp_fl_ct_handle_post_ct(priv, netdev, flow, extack);