nfp: flower-ct: add nft flows to nft list
authorLouis Peens <louis.peens@corigine.com>
Wed, 16 Jun 2021 10:02:01 +0000 (12:02 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Jun 2021 19:42:52 +0000 (12:42 -0700)
Implement code to add and remove nft flows to the relevant list.
Registering and deregistering the callback function for the nft
table is quite complicated. The safest is to delete the callback
on the removal of the last pre_ct flow. This is because if this
is also the latest pre_ct flow in software it means that this
specific nft table will be freed, so there will not be a later
opportunity to do this. Another place where it looks possible
to delete the callback is when the last nft_flow is deleted,
but this happens under the flow_table lock, which is also taken
when deregistering the callback, leading to a deadlock situation.

This means the final solution here is to delete the callback
when removing the last pre_ct flow, and then clean up any
remaining nft_flow entries which may still be present, since
there will never be a callback now to do this, leaving them
orphaned if not cleaned up here as well.

Signed-off-by: Louis Peens <louis.peens@corigine.com>
Signed-off-by: Yinjun Zhang <yinjun.zhang@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/flower/conntrack.c
drivers/net/ethernet/netronome/nfp/flower/conntrack.h
drivers/net/ethernet/netronome/nfp/flower/metadata.c

index 7fb51e13faea35d0253b028b53e49ad592d58eda..1b527f0660a7770a9c4d2195106466a05298f78b 100644 (file)
@@ -165,6 +165,7 @@ nfp_fl_ct_zone_entry *get_nfp_zone_entry(struct nfp_flower_priv *priv,
        /* init the various hash tables and lists*/
        INIT_LIST_HEAD(&zt->pre_ct_list);
        INIT_LIST_HEAD(&zt->post_ct_list);
+       INIT_LIST_HEAD(&zt->nft_flows_list);
 
        err = rhashtable_init(&zt->tc_merge_tb, &nfp_tc_ct_merge_params);
        if (err)
@@ -500,13 +501,31 @@ int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv,
 static int
 nfp_fl_ct_offload_nft_flow(struct nfp_fl_ct_zone_entry *zt, struct flow_cls_offload *flow)
 {
+       struct nfp_fl_ct_map_entry *ct_map_ent;
+       struct nfp_fl_ct_flow_entry *ct_entry;
+       struct netlink_ext_ack *extack = NULL;
+
        ASSERT_RTNL();
 
+       extack = flow->common.extack;
        switch (flow->command) {
        case FLOW_CLS_REPLACE:
+               /* Netfilter can request offload multiple times for the same
+                * flow - protect against adding duplicates.
+                */
+               ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie,
+                                                   nfp_ct_map_params);
+               if (!ct_map_ent) {
+                       ct_entry = nfp_fl_ct_add_flow(zt, NULL, flow, extack);
+                       ct_entry->type = CT_TYPE_NFT;
+                       list_add(&ct_entry->list_node, &zt->nft_flows_list);
+                       zt->nft_flows_count++;
+               }
                return 0;
        case FLOW_CLS_DESTROY:
-               return 0;
+               ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table, &flow->cookie,
+                                                   nfp_ct_map_params);
+               return nfp_fl_ct_del_flow(ct_map_ent);
        case FLOW_CLS_STATS:
                return 0;
        default:
@@ -533,12 +552,30 @@ int nfp_fl_ct_handle_nft_flow(enum tc_setup_type type, void *type_data, void *cb
        return err;
 }
 
+static void
+nfp_fl_ct_clean_nft_entries(struct nfp_fl_ct_zone_entry *zt)
+{
+       struct nfp_fl_ct_flow_entry *nft_entry, *ct_tmp;
+       struct nfp_fl_ct_map_entry *ct_map_ent;
+
+       list_for_each_entry_safe(nft_entry, ct_tmp, &zt->nft_flows_list,
+                                list_node) {
+               ct_map_ent = rhashtable_lookup_fast(&zt->priv->ct_map_table,
+                                                   &nft_entry->cookie,
+                                                   nfp_ct_map_params);
+               nfp_fl_ct_del_flow(ct_map_ent);
+       }
+}
+
 int nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent)
 {
        struct nfp_fl_ct_flow_entry *ct_entry;
        struct nfp_fl_ct_zone_entry *zt;
        struct rhashtable *m_table;
 
+       if (!ct_map_ent)
+               return -ENOENT;
+
        zt = ct_map_ent->ct_entry->zt;
        ct_entry = ct_map_ent->ct_entry;
        m_table = &zt->priv->ct_map_table;
@@ -566,6 +603,7 @@ int nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent)
                                                     nfp_fl_ct_handle_nft_flow,
                                                     zt);
                        zt->nft = NULL;
+                       nfp_fl_ct_clean_nft_entries(zt);
                }
                break;
        case CT_TYPE_POST_CT:
@@ -575,6 +613,12 @@ int nfp_fl_ct_del_flow(struct nfp_fl_ct_map_entry *ct_map_ent)
                nfp_fl_ct_clean_flow_entry(ct_entry);
                kfree(ct_map_ent);
                break;
+       case CT_TYPE_NFT:
+               zt->nft_flows_count--;
+               rhashtable_remove_fast(m_table, &ct_map_ent->hash_node,
+                                      nfp_ct_map_params);
+               nfp_fl_ct_clean_flow_entry(ct_map_ent->ct_entry);
+               kfree(ct_map_ent);
        default:
                break;
        }
index b6e750dad9290fef9cd080fa5597a82401b56c6a..def95c3e8bb75124f1b2dbcecd3bb5da0849246d 100644 (file)
@@ -28,6 +28,9 @@ extern const struct rhashtable_params nfp_tc_ct_merge_params;
  *
  * @tc_merge_tb:       The table of merged tc flows
  * @tc_merge_count:    Keep count of the number of merged tc entries
+ *
+ * @nft_flows_list:    The list of nft relatednfp_fl_ct_flow_entry entries
+ * @nft_flows_count:   Keep count of the number of nft_flow entries
  */
 struct nfp_fl_ct_zone_entry {
        u16 zone;
@@ -44,6 +47,9 @@ struct nfp_fl_ct_zone_entry {
 
        struct rhashtable tc_merge_tb;
        unsigned int tc_merge_count;
+
+       struct list_head nft_flows_list;
+       unsigned int nft_flows_count;
 };
 
 enum ct_entry_type {
index 8658c5cedf915755c8a9346e9b3c533fd546eb04..a0a0242567a670768e98469daca320aa5047591c 100644 (file)
@@ -639,6 +639,32 @@ static void nfp_zone_table_entry_destroy(struct nfp_fl_ct_zone_entry *zt)
                }
        }
 
+       if (zt->nft) {
+               nf_flow_table_offload_del_cb(zt->nft,
+                                            nfp_fl_ct_handle_nft_flow,
+                                            zt);
+               zt->nft = NULL;
+       }
+
+       if (!list_empty(&zt->nft_flows_list)) {
+               struct rhashtable *m_table = &zt->priv->ct_map_table;
+               struct nfp_fl_ct_flow_entry *entry, *tmp;
+               struct nfp_fl_ct_map_entry *map;
+
+               WARN_ONCE(1, "nft_flows_list not empty as expected, cleaning up\n");
+               list_for_each_entry_safe(entry, tmp, &zt->nft_flows_list,
+                                        list_node) {
+                       map = rhashtable_lookup_fast(m_table,
+                                                    &entry->cookie,
+                                                    nfp_ct_map_params);
+                       WARN_ON_ONCE(rhashtable_remove_fast(m_table,
+                                                           &map->hash_node,
+                                                           nfp_ct_map_params));
+                       nfp_fl_ct_clean_flow_entry(entry);
+                       kfree(map);
+               }
+       }
+
        rhashtable_free_and_destroy(&zt->tc_merge_tb,
                                    nfp_check_rhashtable_empty, NULL);