Commit 53c7bb55 authored by David S. Miller's avatar David S. Miller

Merge branch 'nfp-ct-offload'

Simon Horman says:

====================
Introduce conntrack offloading to the nfp driver

Louis Peens says:

This is the first in a series of patches to offload conntrack
to the nfp. The approach followed is to flatten out three
different flow rules into a single offloaded flow. The three
different flows are:

1) The rule sending the packet to conntrack (pre_ct)
2) The rule matching on +trk+est after a packet has been through
   conntrack. (post_ct)
3) The rule received via callback from the netfilter (nft)

In order to offload a flow we need a combination of all three flows, but
they could be added/deleted at different times and in different order.

To solve this we save potential offloadable CT flows in the driver,
and every time we receive a callback we check against these saved flows
for valid merges. Once we have a valid combination of all three flows
this will be offloaded to the NFP. This is demonstrated in the diagram
below.

	+-------------+                      +----------+
	| pre_ct flow +--------+             | nft flow |
	+-------------+        v             +------+---+
	                  +----------+              |
	                  | tc_merge +--------+     |
	                  +----------+        v     v
	+--------------+       ^           +-------------+
	| post_ct flow +-------+       +---+nft_tc merge |
	+--------------+               |   +-------------+
	                               |
	                               |
	                               |
	                               v
	                        Offload to nfp

This series is only up to the point of the pre_ct and post_ct
merges into the tc_merge. Follow up series will continue
to add the nft flows and merging of these flows with the result
of the pre_ct and post_ct merged flows.

Changes since v2:
- nfp: flower-ct: add zone table entry when handling pre/post_ct flows
    Fixed another docstring. Should finally have the patch check
    environment properly configured now to avoid more of these.
- nfp: flower-ct: add tc merge functionality
    Fixed warning found by "kernel test robot <lkp@intel.com>"
    Added code comment explaining chain_index comparison

Changes since v1:
- nfp: flower-ct: add ct zone table
    Fixed unused variable compile warning
    Fixed missing colon in struct description
====================
Acked-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5debe0b3 3c863c30
......@@ -51,7 +51,8 @@ nfp-objs += \
flower/metadata.o \
flower/offload.o \
flower/tunnel_conf.o \
flower/qos_conf.o
flower/qos_conf.o \
flower/conntrack.o
endif
ifeq ($(CONFIG_BPF_SYSCALL),y)
......
This diff is collapsed.
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (C) 2021 Corigine, Inc. */
#ifndef __NFP_FLOWER_CONNTRACK_H__
#define __NFP_FLOWER_CONNTRACK_H__ 1
#include "main.h"
#define NFP_FL_CT_NO_TUN 0xff
extern const struct rhashtable_params nfp_zone_table_params;
extern const struct rhashtable_params nfp_ct_map_params;
extern const struct rhashtable_params nfp_tc_ct_merge_params;
/**
* struct nfp_fl_ct_zone_entry - Zone entry containing conntrack flow information
* @zone: The zone number, used as lookup key in hashtable
* @hash_node: Used by the hashtable
* @priv: Pointer to nfp_flower_priv data
* @nft: Pointer to nf_flowtable for this zone
*
* @pre_ct_list: The pre_ct_list of nfp_fl_ct_flow_entry entries
* @pre_ct_count: Keep count of the number of pre_ct entries
*
* @post_ct_list: The post_ct_list of nfp_fl_ct_flow_entry entries
* @post_ct_count: Keep count of the number of post_ct entries
*
* @tc_merge_tb: The table of merged tc flows
* @tc_merge_count: Keep count of the number of merged tc entries
*/
struct nfp_fl_ct_zone_entry {
u16 zone;
struct rhash_head hash_node;
struct nfp_flower_priv *priv;
struct nf_flowtable *nft;
struct list_head pre_ct_list;
unsigned int pre_ct_count;
struct list_head post_ct_list;
unsigned int post_ct_count;
struct rhashtable tc_merge_tb;
unsigned int tc_merge_count;
};
enum ct_entry_type {
CT_TYPE_PRE_CT,
CT_TYPE_NFT,
CT_TYPE_POST_CT,
};
/**
* struct nfp_fl_ct_flow_entry - Flow entry containing conntrack flow information
* @cookie: Flow cookie, same as original TC flow, used as key
* @list_node: Used by the list
* @chain_index: Chain index of the original 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
* @tun_offset: Used to indicate tunnel action offset in action list
*/
struct nfp_fl_ct_flow_entry {
unsigned long cookie;
struct list_head list_node;
u32 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;
u8 tun_offset; // Set to NFP_FL_CT_NO_TUN if no tun
};
/**
* struct nfp_fl_ct_tc_merge - Merge of two flows from tc
* @cookie: Flow cookie, combination of pre and post ct cookies
* @hash_node: Used by the hashtable
* @pre_ct_list: This entry is part of a pre_ct_list
* @post_ct_list: This entry is part of a post_ct_list
* @zt: Reference to the zone table this belongs to
* @pre_ct_parent: The pre_ct_parent
* @post_ct_parent: The post_ct_parent
* @children: List of nft merged entries
*/
struct nfp_fl_ct_tc_merge {
unsigned long cookie[2];
struct rhash_head hash_node;
struct list_head pre_ct_list;
struct list_head post_ct_list;
struct nfp_fl_ct_zone_entry *zt;
struct nfp_fl_ct_flow_entry *pre_ct_parent;
struct nfp_fl_ct_flow_entry *post_ct_parent;
struct list_head children;
};
/**
* struct nfp_fl_ct_map_entry - Map between flow cookie and specific ct_flow
* @cookie: Flow cookie, same as original TC flow, used as key
* @hash_node: Used by the hashtable
* @ct_entry: Pointer to corresponding ct_entry
*/
struct nfp_fl_ct_map_entry {
unsigned long cookie;
struct rhash_head hash_node;
struct nfp_fl_ct_flow_entry *ct_entry;
};
bool is_pre_ct_flow(struct flow_cls_offload *flow);
bool is_post_ct_flow(struct flow_cls_offload *flow);
/**
* nfp_fl_ct_handle_pre_ct() - Handles -trk conntrack rules
* @priv: Pointer to app priv
* @netdev: netdev structure.
* @flow: TC flower classifier offload structure.
* @extack: Extack pointer for errors
*
* Adds a new entry to the relevant zone table and tries to
* merge with other +trk+est entries and offload if possible.
*
* Return: negative value on error, 0 if configured successfully.
*/
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);
/**
* nfp_fl_ct_handle_post_ct() - Handles +trk+est conntrack rules
* @priv: Pointer to app priv
* @netdev: netdev structure.
* @flow: TC flower classifier offload structure.
* @extack: Extack pointer for errors
*
* Adds a new entry to the relevant zone table and tries to
* merge with other -trk entries and offload if possible.
*
* Return: negative value on error, 0 if configured successfully.
*/
int nfp_fl_ct_handle_post_ct(struct nfp_flower_priv *priv,
struct net_device *netdev,
struct flow_cls_offload *flow,
struct netlink_ext_ack *extack);
/**
* nfp_fl_ct_clean_flow_entry() - Free a nfp_fl_ct_flow_entry
* @entry: Flow entry to cleanup
*/
void nfp_fl_ct_clean_flow_entry(struct nfp_fl_ct_flow_entry *entry);
#endif
......@@ -193,6 +193,9 @@ struct nfp_fl_internal_ports {
* @qos_stats_lock: Lock on qos stats updates
* @pre_tun_rule_cnt: Number of pre-tunnel rules offloaded
* @merge_table: Hash table to store merged flows
* @ct_zone_table: Hash table used to store the different zones
* @ct_zone_wc: Special zone entry for wildcarded zone matches
* @ct_map_table: Hash table used to referennce ct flows
*/
struct nfp_flower_priv {
struct nfp_app *app;
......@@ -227,6 +230,9 @@ struct nfp_flower_priv {
spinlock_t qos_stats_lock; /* Protect the qos stats */
int pre_tun_rule_cnt;
struct rhashtable merge_table;
struct rhashtable ct_zone_table;
struct nfp_fl_ct_zone_entry *ct_zone_wc;
struct rhashtable ct_map_table;
};
/**
......
......@@ -9,6 +9,7 @@
#include <net/pkt_cls.h>
#include "cmsg.h"
#include "conntrack.h"
#include "main.h"
#include "../nfp_app.h"
......@@ -496,6 +497,20 @@ const struct rhashtable_params merge_table_params = {
.key_len = sizeof(u64),
};
const struct rhashtable_params nfp_zone_table_params = {
.head_offset = offsetof(struct nfp_fl_ct_zone_entry, hash_node),
.key_len = sizeof(u16),
.key_offset = offsetof(struct nfp_fl_ct_zone_entry, zone),
.automatic_shrinking = false,
};
const struct rhashtable_params nfp_ct_map_params = {
.head_offset = offsetof(struct nfp_fl_ct_map_entry, hash_node),
.key_len = sizeof(unsigned long),
.key_offset = offsetof(struct nfp_fl_ct_map_entry, cookie),
.automatic_shrinking = true,
};
int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
unsigned int host_num_mems)
{
......@@ -516,6 +531,14 @@ int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
if (err)
goto err_free_stats_ctx_table;
err = rhashtable_init(&priv->ct_zone_table, &nfp_zone_table_params);
if (err)
goto err_free_merge_table;
err = rhashtable_init(&priv->ct_map_table, &nfp_ct_map_params);
if (err)
goto err_free_ct_zone_table;
get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed));
/* Init ring buffer and unallocated mask_ids. */
......@@ -523,7 +546,7 @@ int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
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_merge_table;
goto err_free_ct_map_table;
priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1;
......@@ -560,6 +583,10 @@ int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
kfree(priv->mask_ids.last_used);
err_free_mask_id:
kfree(priv->mask_ids.mask_id_free_list.buf);
err_free_ct_map_table:
rhashtable_destroy(&priv->ct_map_table);
err_free_ct_zone_table:
rhashtable_destroy(&priv->ct_zone_table);
err_free_merge_table:
rhashtable_destroy(&priv->merge_table);
err_free_stats_ctx_table:
......@@ -569,6 +596,72 @@ int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
return -ENOMEM;
}
static void nfp_zone_table_entry_destroy(struct nfp_fl_ct_zone_entry *zt)
{
if (!zt)
return;
if (!list_empty(&zt->pre_ct_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, "pre_ct_list not empty as expected, cleaning up\n");
list_for_each_entry_safe(entry, tmp, &zt->pre_ct_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);
}
}
if (!list_empty(&zt->post_ct_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, "post_ct_list not empty as expected, cleaning up\n");
list_for_each_entry_safe(entry, tmp, &zt->post_ct_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);
kfree(zt);
}
static void nfp_free_zone_table_entry(void *ptr, void *arg)
{
struct nfp_fl_ct_zone_entry *zt = ptr;
nfp_zone_table_entry_destroy(zt);
}
static void nfp_free_map_table_entry(void *ptr, void *arg)
{
struct nfp_fl_ct_map_entry *map = ptr;
if (!map)
return;
kfree(map);
}
void nfp_flower_metadata_cleanup(struct nfp_app *app)
{
struct nfp_flower_priv *priv = app->priv;
......@@ -582,6 +675,12 @@ void nfp_flower_metadata_cleanup(struct nfp_app *app)
nfp_check_rhashtable_empty, NULL);
rhashtable_free_and_destroy(&priv->merge_table,
nfp_check_rhashtable_empty, NULL);
rhashtable_free_and_destroy(&priv->ct_zone_table,
nfp_free_zone_table_entry, NULL);
nfp_zone_table_entry_destroy(priv->ct_zone_wc);
rhashtable_free_and_destroy(&priv->ct_map_table,
nfp_free_map_table_entry, NULL);
kvfree(priv->stats);
kfree(priv->mask_ids.mask_id_free_list.buf);
kfree(priv->mask_ids.last_used);
......
......@@ -7,6 +7,7 @@
#include "cmsg.h"
#include "main.h"
#include "conntrack.h"
#include "../nfpcore/nfp_cpp.h"
#include "../nfpcore/nfp_nsp.h"
#include "../nfp_app.h"
......@@ -1276,6 +1277,20 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
return 0;
}
static bool offload_pre_check(struct flow_cls_offload *flow)
{
struct flow_rule *rule = flow_cls_offload_flow_rule(flow);
struct flow_dissector *dissector = rule->match.dissector;
if (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CT))
return false;
if (flow->common.chain_index)
return false;
return true;
}
/**
* nfp_flower_add_offload() - Adds a new flow to hardware.
* @app: Pointer to the APP handle
......@@ -1302,6 +1317,15 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
if (nfp_netdev_is_nfp_repr(netdev))
port = nfp_port_from_netdev(netdev);
if (is_pre_ct_flow(flow))
return nfp_fl_ct_handle_pre_ct(priv, netdev, flow, extack);
if (is_post_ct_flow(flow))
return nfp_fl_ct_handle_post_ct(priv, netdev, flow, extack);
if (!offload_pre_check(flow))
return -EOPNOTSUPP;
key_layer = kmalloc(sizeof(*key_layer), GFP_KERNEL);
if (!key_layer)
return -ENOMEM;
......@@ -1646,9 +1670,10 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev,
static int nfp_flower_setup_tc_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
struct flow_cls_common_offload *common = type_data;
struct nfp_repr *repr = cb_priv;
if (!tc_cls_can_offload_and_chain0(repr->netdev, type_data))
if (!tc_can_offload_extack(repr->netdev, common->extack))
return -EOPNOTSUPP;
switch (type) {
......@@ -1746,10 +1771,6 @@ static int nfp_flower_setup_indr_block_cb(enum tc_setup_type type,
void *type_data, void *cb_priv)
{
struct nfp_flower_indr_block_cb_priv *priv = cb_priv;
struct flow_cls_offload *flower = type_data;
if (flower->common.chain_index)
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_CLSFLOWER:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment