* @flow_table:                Hash table used to store flower rules
  * @stats:             Stored stats updates for flower rules
  * @stats_lock:                Lock for flower rule stats updates
+ * @stats_ctx_table:   Hash table to map stats contexts to its flow rule
  * @cmsg_work:         Workqueue for control messages processing
  * @cmsg_skbs_high:    List of higher priority skbs for control message
  *                     processing
        struct rhashtable flow_table;
        struct nfp_fl_stats *stats;
        spinlock_t stats_lock; /* lock stats */
+       struct rhashtable stats_ctx_table;
        struct work_struct cmsg_work;
        struct sk_buff_head cmsg_skbs_high;
        struct sk_buff_head cmsg_skbs_low;
 nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie,
                           struct net_device *netdev);
 struct nfp_fl_payload *
+nfp_flower_get_fl_payload_from_ctx(struct nfp_app *app, u32 ctx_id);
+struct nfp_fl_payload *
 nfp_flower_remove_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie);
 
 void nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb);
 
        unsigned long cookie;
 };
 
+struct nfp_fl_stats_ctx_to_flow {
+       struct rhash_head ht_node;
+       u32 stats_cxt;
+       struct nfp_fl_payload *flow;
+};
+
+static const struct rhashtable_params stats_ctx_table_params = {
+       .key_offset     = offsetof(struct nfp_fl_stats_ctx_to_flow, stats_cxt),
+       .head_offset    = offsetof(struct nfp_fl_stats_ctx_to_flow, ht_node),
+       .key_len        = sizeof(u32),
+};
+
 static int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id)
 {
        struct nfp_flower_priv *priv = app->priv;
                              struct nfp_fl_payload *nfp_flow,
                              struct net_device *netdev)
 {
+       struct nfp_fl_stats_ctx_to_flow *ctx_entry;
        struct nfp_flower_priv *priv = app->priv;
        struct nfp_fl_payload *check_entry;
        u8 new_mask_id;
        u32 stats_cxt;
+       int err;
 
-       if (nfp_get_stats_entry(app, &stats_cxt))
-               return -ENOENT;
+       err = nfp_get_stats_entry(app, &stats_cxt);
+       if (err)
+               return err;
 
        nfp_flow->meta.host_ctx_id = cpu_to_be32(stats_cxt);
        nfp_flow->meta.host_cookie = cpu_to_be64(flow->cookie);
        nfp_flow->ingress_dev = netdev;
 
+       ctx_entry = kzalloc(sizeof(*ctx_entry), GFP_KERNEL);
+       if (!ctx_entry) {
+               err = -ENOMEM;
+               goto err_release_stats;
+       }
+
+       ctx_entry->stats_cxt = stats_cxt;
+       ctx_entry->flow = nfp_flow;
+
+       if (rhashtable_insert_fast(&priv->stats_ctx_table, &ctx_entry->ht_node,
+                                  stats_ctx_table_params)) {
+               err = -ENOMEM;
+               goto err_free_ctx_entry;
+       }
+
        new_mask_id = 0;
        if (!nfp_check_mask_add(app, nfp_flow->mask_data,
                                nfp_flow->meta.mask_len,
                                &nfp_flow->meta.flags, &new_mask_id)) {
-               if (nfp_release_stats_entry(app, stats_cxt))
-                       return -EINVAL;
-               return -ENOENT;
+               err = -ENOENT;
+               goto err_remove_rhash;
        }
 
        nfp_flow->meta.flow_version = cpu_to_be64(priv->flower_version);
 
        check_entry = nfp_flower_search_fl_table(app, flow->cookie, netdev);
        if (check_entry) {
-               if (nfp_release_stats_entry(app, stats_cxt))
-                       return -EINVAL;
-
-               if (!nfp_check_mask_remove(app, nfp_flow->mask_data,
-                                          nfp_flow->meta.mask_len,
-                                          NULL, &new_mask_id))
-                       return -EINVAL;
-
-               return -EEXIST;
+               err = -EEXIST;
+               goto err_remove_mask;
        }
 
        return 0;
+
+err_remove_mask:
+       nfp_check_mask_remove(app, nfp_flow->mask_data, nfp_flow->meta.mask_len,
+                             NULL, &new_mask_id);
+err_remove_rhash:
+       WARN_ON_ONCE(rhashtable_remove_fast(&priv->stats_ctx_table,
+                                           &ctx_entry->ht_node,
+                                           stats_ctx_table_params));
+err_free_ctx_entry:
+       kfree(ctx_entry);
+err_release_stats:
+       nfp_release_stats_entry(app, stats_cxt);
+
+       return err;
 }
 
 int nfp_modify_flow_metadata(struct nfp_app *app,
                             struct nfp_fl_payload *nfp_flow)
 {
+       struct nfp_fl_stats_ctx_to_flow *ctx_entry;
        struct nfp_flower_priv *priv = app->priv;
        u8 new_mask_id = 0;
        u32 temp_ctx_id;
        /* Update flow payload with mask ids. */
        nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id;
 
-       /* Release the stats ctx id. */
+       /* Release the stats ctx id and ctx to flow table entry. */
        temp_ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id);
 
+       ctx_entry = rhashtable_lookup_fast(&priv->stats_ctx_table, &temp_ctx_id,
+                                          stats_ctx_table_params);
+       if (!ctx_entry)
+               return -ENOENT;
+
+       WARN_ON_ONCE(rhashtable_remove_fast(&priv->stats_ctx_table,
+                                           &ctx_entry->ht_node,
+                                           stats_ctx_table_params));
+       kfree(ctx_entry);
+
        return nfp_release_stats_entry(app, temp_ctx_id);
 }
 
+struct nfp_fl_payload *
+nfp_flower_get_fl_payload_from_ctx(struct nfp_app *app, u32 ctx_id)
+{
+       struct nfp_fl_stats_ctx_to_flow *ctx_entry;
+       struct nfp_flower_priv *priv = app->priv;
+
+       ctx_entry = rhashtable_lookup_fast(&priv->stats_ctx_table, &ctx_id,
+                                          stats_ctx_table_params);
+       if (!ctx_entry)
+               return NULL;
+
+       return ctx_entry->flow;
+}
+
 static int nfp_fl_obj_cmpfn(struct rhashtable_compare_arg *arg,
                            const void *obj)
 {
        if (err)
                return err;
 
+       err = rhashtable_init(&priv->stats_ctx_table, &stats_ctx_table_params);
+       if (err)
+               goto err_free_flow_table;
+
        get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed));
 
        /* Init ring buffer and unallocated mask_ids. */
                kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS,
                              NFP_FLOWER_MASK_ELEMENT_RS, GFP_KERNEL);
        if (!priv->mask_ids.mask_id_free_list.buf)
-               goto err_free_flow_table;
+               goto err_free_stats_ctx_table;
 
        priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1;
 
        kfree(priv->mask_ids.last_used);
 err_free_mask_id:
        kfree(priv->mask_ids.mask_id_free_list.buf);
+err_free_stats_ctx_table:
+       rhashtable_destroy(&priv->stats_ctx_table);
 err_free_flow_table:
        rhashtable_destroy(&priv->flow_table);
        return -ENOMEM;
 
        rhashtable_free_and_destroy(&priv->flow_table,
                                    nfp_check_rhashtable_empty, NULL);
+       rhashtable_free_and_destroy(&priv->stats_ctx_table,
+                                   nfp_check_rhashtable_empty, NULL);
        kvfree(priv->stats);
        kfree(priv->mask_ids.mask_id_free_list.buf);
        kfree(priv->mask_ids.last_used);