Commit beb21e3e authored by David S. Miller's avatar David S. Miller

Merge branch 'nfp-flower-rework'

Simon Horman says:

====================
nfp: flower: decap neighbour table rework

Louis Peens says:

This patch series reworks the way in which flow rules that outputs to
OVS internal ports gets handled by the nfp driver.

Previously this made use of a small pre_tun_table, but this only used
destination MAC addresses, and made the implicit assumption that there is
only a single source MAC":"destination MAC" mapping per tunnel. In
hindsight this seems to be a pretty obvious oversight, but this was hidden
in plain sight for quite some time.

This series changes the implementation to make use of the same Neighbour
table for decap that is in use for the tunnel encap solution. It stores
any new Neighbour updates in this table. Previously this path was only
triggered for encapsulation candidates, and the entries were send and
forget, not saved on the host as it is after this series. It also keeps
track of any flow rule that outputs to OVS internal ports (and some
other criteria not worth mentioning here), very similar to how it was
done previously, except now these flows are kept track of in a list.

When a new Neighbour entry gets added this list gets iterated for
potential matches, in which case the table gets updated with a reference
to the flow, and the Neighbour entry on the card gets updated with the
relevant host_ctx. The same happens when a new qualifying flow gets
added - the Neighbour table gets iterated for applicable matches, and
once again the firmware gets updated with the host_ctx when any matches
are found.

Since this also requires a firmware change we add a new capability bit,
and keep the old behaviour in case of older firmware without this bit
set.

This series starts by doing some preparation, then adding the new list
and table entries. Next the functionality to link/unlink these entries
are added, and finally this new functionality is enabled by adding the
DECAP_V2 bit to the driver feature list.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 76a84269 a7da2a86
......@@ -220,7 +220,8 @@ nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
}
output->port = cpu_to_be32(NFP_FL_LAG_OUT | gid);
} else if (nfp_flower_internal_port_can_offload(app, out_dev)) {
if (!(priv->flower_ext_feats & NFP_FL_FEATS_PRE_TUN_RULES)) {
if (!(priv->flower_ext_feats & NFP_FL_FEATS_PRE_TUN_RULES) &&
!(priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2)) {
NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules not supported in loaded firmware");
return -EOPNOTSUPP;
}
......
......@@ -51,6 +51,7 @@ struct nfp_app;
#define NFP_FL_FEATS_VLAN_QINQ BIT(8)
#define NFP_FL_FEATS_QOS_PPS BIT(9)
#define NFP_FL_FEATS_QOS_METER BIT(10)
#define NFP_FL_FEATS_DECAP_V2 BIT(11)
#define NFP_FL_FEATS_HOST_ACK BIT(31)
#define NFP_FL_ENABLE_FLOW_MERGE BIT(0)
......@@ -67,7 +68,8 @@ struct nfp_app;
NFP_FL_FEATS_IPV6_TUN | \
NFP_FL_FEATS_VLAN_QINQ | \
NFP_FL_FEATS_QOS_PPS | \
NFP_FL_FEATS_QOS_METER)
NFP_FL_FEATS_QOS_METER | \
NFP_FL_FEATS_DECAP_V2)
struct nfp_fl_mask_id {
struct circ_buf mask_id_free_list;
......@@ -86,12 +88,8 @@ struct nfp_fl_stats_id {
* @offloaded_macs: Hashtable of the offloaded MAC addresses
* @ipv4_off_list: List of IPv4 addresses to offload
* @ipv6_off_list: List of IPv6 addresses to offload
* @neigh_off_list_v4: List of IPv4 neighbour offloads
* @neigh_off_list_v6: List of IPv6 neighbour offloads
* @ipv4_off_lock: Lock for the IPv4 address list
* @ipv6_off_lock: Lock for the IPv6 address list
* @neigh_off_lock_v4: Lock for the IPv4 neighbour address list
* @neigh_off_lock_v6: Lock for the IPv6 neighbour address list
* @mac_off_ids: IDA to manage id assignment for offloaded MACs
* @neigh_nb: Notifier to monitor neighbour state
*/
......@@ -99,16 +97,94 @@ struct nfp_fl_tunnel_offloads {
struct rhashtable offloaded_macs;
struct list_head ipv4_off_list;
struct list_head ipv6_off_list;
struct list_head neigh_off_list_v4;
struct list_head neigh_off_list_v6;
struct mutex ipv4_off_lock;
struct mutex ipv6_off_lock;
spinlock_t neigh_off_lock_v4;
spinlock_t neigh_off_lock_v6;
struct ida mac_off_ids;
struct notifier_block neigh_nb;
};
/**
* struct nfp_tun_neigh - basic neighbour data
* @dst_addr: Destination MAC address
* @src_addr: Source MAC address
* @port_id: NFP port to output packet on - associated with source IPv4
*/
struct nfp_tun_neigh {
u8 dst_addr[ETH_ALEN];
u8 src_addr[ETH_ALEN];
__be32 port_id;
};
/**
* struct nfp_tun_neigh_ext - extended neighbour data
* @vlan_tpid: VLAN_TPID match field
* @vlan_tci: VLAN_TCI match field
* @host_ctx: Host context ID to be saved here
*/
struct nfp_tun_neigh_ext {
__be16 vlan_tpid;
__be16 vlan_tci;
__be32 host_ctx;
};
/**
* struct nfp_tun_neigh_v4 - neighbour/route entry on the NFP for IPv4
* @dst_ipv4: Destination IPv4 address
* @src_ipv4: Source IPv4 address
* @common: Neighbour/route common info
* @ext: Neighbour/route extended info
*/
struct nfp_tun_neigh_v4 {
__be32 dst_ipv4;
__be32 src_ipv4;
struct nfp_tun_neigh common;
struct nfp_tun_neigh_ext ext;
};
/**
* struct nfp_tun_neigh_v6 - neighbour/route entry on the NFP for IPv6
* @dst_ipv6: Destination IPv6 address
* @src_ipv6: Source IPv6 address
* @common: Neighbour/route common info
* @ext: Neighbour/route extended info
*/
struct nfp_tun_neigh_v6 {
struct in6_addr dst_ipv6;
struct in6_addr src_ipv6;
struct nfp_tun_neigh common;
struct nfp_tun_neigh_ext ext;
};
/**
* struct nfp_neigh_entry
* @neigh_cookie: Cookie for hashtable lookup
* @ht_node: rhash_head entry for hashtable
* @list_head: Needed as member of linked_nn_entries list
* @payload: The neighbour info payload
* @flow: Linked flow rule
* @is_ipv6: Flag to indicate if payload is ipv6 or ipv4
*/
struct nfp_neigh_entry {
unsigned long neigh_cookie;
struct rhash_head ht_node;
struct list_head list_head;
char *payload;
struct nfp_predt_entry *flow;
bool is_ipv6;
};
/**
* struct nfp_predt_entry
* @list_head: List head to attach to predt_list
* @flow_pay: Direct link to flow_payload
* @nn_list: List of linked nfp_neigh_entries
*/
struct nfp_predt_entry {
struct list_head list_head;
struct nfp_fl_payload *flow_pay;
struct list_head nn_list;
};
/**
* struct nfp_mtu_conf - manage MTU setting
* @portnum: NFP port number of repr with requested MTU change
......@@ -202,6 +278,9 @@ struct nfp_fl_internal_ports {
* @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
* @predt_list: List to keep track of decap pretun flows
* @neigh_table: Table to keep track of neighbor entries
* @predt_lock: Lock to serialise predt/neigh table updates
*/
struct nfp_flower_priv {
struct nfp_app *app;
......@@ -241,6 +320,9 @@ struct nfp_flower_priv {
struct rhashtable ct_zone_table;
struct nfp_fl_ct_zone_entry *ct_zone_wc;
struct rhashtable ct_map_table;
struct list_head predt_list;
struct rhashtable neigh_table;
spinlock_t predt_lock; /* Lock to serialise predt/neigh table updates */
};
/**
......@@ -344,9 +426,14 @@ struct nfp_fl_payload {
struct list_head linked_flows;
bool in_hw;
struct {
struct nfp_predt_entry *predt;
struct net_device *dev;
__be16 vlan_tpid;
__be16 vlan_tci;
__be16 port_idx;
u8 loc_mac[ETH_ALEN];
u8 rem_mac[ETH_ALEN];
bool is_ipv6;
} pre_tun_rule;
};
......@@ -369,6 +456,7 @@ struct nfp_fl_payload_link {
extern const struct rhashtable_params nfp_flower_table_params;
extern const struct rhashtable_params merge_table_params;
extern const struct rhashtable_params neigh_table_params;
struct nfp_merge_info {
u64 parent_ctx;
......@@ -580,6 +668,10 @@ void
nfp_flower_non_repr_priv_put(struct nfp_app *app, struct net_device *netdev);
u32 nfp_flower_get_port_id_from_netdev(struct nfp_app *app,
struct net_device *netdev);
void nfp_tun_link_and_update_nn_entries(struct nfp_app *app,
struct nfp_predt_entry *predt);
void nfp_tun_unlink_and_update_nn_entries(struct nfp_app *app,
struct nfp_predt_entry *predt);
int nfp_flower_xmit_pre_tun_flow(struct nfp_app *app,
struct nfp_fl_payload *flow);
int nfp_flower_xmit_pre_tun_del_flow(struct nfp_app *app,
......
......@@ -502,6 +502,12 @@ const struct rhashtable_params nfp_ct_map_params = {
.automatic_shrinking = true,
};
const struct rhashtable_params neigh_table_params = {
.key_offset = offsetof(struct nfp_neigh_entry, neigh_cookie),
.head_offset = offsetof(struct nfp_neigh_entry, ht_node),
.key_len = sizeof(unsigned long),
};
int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
unsigned int host_num_mems)
{
......@@ -530,6 +536,12 @@ int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
if (err)
goto err_free_ct_zone_table;
err = rhashtable_init(&priv->neigh_table, &neigh_table_params);
if (err)
goto err_free_ct_map_table;
INIT_LIST_HEAD(&priv->predt_list);
get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed));
/* Init ring buffer and unallocated mask_ids. */
......@@ -537,7 +549,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_ct_map_table;
goto err_free_neigh_table;
priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1;
......@@ -565,6 +577,7 @@ int nfp_flower_metadata_init(struct nfp_app *app, u64 host_ctx_count,
goto err_free_ring_buf;
spin_lock_init(&priv->stats_lock);
spin_lock_init(&priv->predt_lock);
return 0;
......@@ -574,6 +587,8 @@ 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_neigh_table:
rhashtable_destroy(&priv->neigh_table);
err_free_ct_map_table:
rhashtable_destroy(&priv->ct_map_table);
err_free_ct_zone_table:
......@@ -700,6 +715,8 @@ void nfp_flower_metadata_cleanup(struct nfp_app *app)
rhashtable_free_and_destroy(&priv->ct_map_table,
nfp_free_map_table_entry, NULL);
rhashtable_free_and_destroy(&priv->neigh_table,
nfp_check_rhashtable_empty, NULL);
kvfree(priv->stats);
kfree(priv->mask_ids.mask_id_free_list.buf);
kfree(priv->mask_ids.last_used);
......
......@@ -1170,6 +1170,11 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
return -EOPNOTSUPP;
}
if (key_layer & NFP_FLOWER_LAYER_IPV6)
flow->pre_tun_rule.is_ipv6 = true;
else
flow->pre_tun_rule.is_ipv6 = false;
/* Skip fields known to exist. */
mask += sizeof(struct nfp_flower_meta_tci);
ext += sizeof(struct nfp_flower_meta_tci);
......@@ -1180,13 +1185,6 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
mask += sizeof(struct nfp_flower_in_port);
ext += sizeof(struct nfp_flower_in_port);
/* Ensure destination MAC address matches pre_tun_dev. */
mac = (struct nfp_flower_mac_mpls *)ext;
if (memcmp(&mac->mac_dst[0], flow->pre_tun_rule.dev->dev_addr, 6)) {
NL_SET_ERR_MSG_MOD(extack, "unsupported pre-tunnel rule: dest MAC must match output dev MAC");
return -EOPNOTSUPP;
}
/* Ensure destination MAC address is fully matched. */
mac = (struct nfp_flower_mac_mpls *)mask;
if (!is_broadcast_ether_addr(&mac->mac_dst[0])) {
......@@ -1194,11 +1192,36 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
return -EOPNOTSUPP;
}
/* Ensure source MAC address is fully matched. This is only needed
* for firmware with the DECAP_V2 feature enabled. Don't do this
* for firmware without this feature to keep old behaviour.
*/
if (priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2) {
mac = (struct nfp_flower_mac_mpls *)mask;
if (!is_broadcast_ether_addr(&mac->mac_src[0])) {
NL_SET_ERR_MSG_MOD(extack,
"unsupported pre-tunnel rule: source MAC field must not be masked");
return -EOPNOTSUPP;
}
}
if (mac->mpls_lse) {
NL_SET_ERR_MSG_MOD(extack, "unsupported pre-tunnel rule: MPLS not supported");
return -EOPNOTSUPP;
}
/* Ensure destination MAC address matches pre_tun_dev. */
mac = (struct nfp_flower_mac_mpls *)ext;
if (memcmp(&mac->mac_dst[0], flow->pre_tun_rule.dev->dev_addr, 6)) {
NL_SET_ERR_MSG_MOD(extack,
"unsupported pre-tunnel rule: dest MAC must match output dev MAC");
return -EOPNOTSUPP;
}
/* Save mac addresses in pre_tun_rule entry for later use */
memcpy(&flow->pre_tun_rule.loc_mac, &mac->mac_dst[0], ETH_ALEN);
memcpy(&flow->pre_tun_rule.rem_mac, &mac->mac_src[0], ETH_ALEN);
mask += sizeof(struct nfp_flower_mac_mpls);
ext += sizeof(struct nfp_flower_mac_mpls);
if (key_layer & NFP_FLOWER_LAYER_IPV4 ||
......@@ -1227,17 +1250,21 @@ nfp_flower_validate_pre_tun_rule(struct nfp_app *app,
if ((priv->flower_ext_feats & NFP_FL_FEATS_VLAN_QINQ)) {
if (key_ls->key_layer_two & NFP_FLOWER_LAYER2_QINQ) {
struct nfp_flower_vlan *vlan_tags;
u16 vlan_tpid;
u16 vlan_tci;
vlan_tags = (struct nfp_flower_vlan *)ext;
vlan_tci = be16_to_cpu(vlan_tags->outer_tci);
vlan_tpid = be16_to_cpu(vlan_tags->outer_tpid);
vlan_tci &= ~NFP_FLOWER_MASK_VLAN_PRESENT;
flow->pre_tun_rule.vlan_tci = cpu_to_be16(vlan_tci);
flow->pre_tun_rule.vlan_tpid = cpu_to_be16(vlan_tpid);
vlan = true;
} else {
flow->pre_tun_rule.vlan_tci = cpu_to_be16(0xffff);
flow->pre_tun_rule.vlan_tpid = cpu_to_be16(0xffff);
}
}
......@@ -1362,11 +1389,30 @@ nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev,
goto err_release_metadata;
}
if (flow_pay->pre_tun_rule.dev)
err = nfp_flower_xmit_pre_tun_flow(app, flow_pay);
else
if (flow_pay->pre_tun_rule.dev) {
if (priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2) {
struct nfp_predt_entry *predt;
predt = kzalloc(sizeof(*predt), GFP_KERNEL);
if (!predt) {
err = -ENOMEM;
goto err_remove_rhash;
}
predt->flow_pay = flow_pay;
INIT_LIST_HEAD(&predt->nn_list);
spin_lock_bh(&priv->predt_lock);
list_add(&predt->list_head, &priv->predt_list);
flow_pay->pre_tun_rule.predt = predt;
nfp_tun_link_and_update_nn_entries(app, predt);
spin_unlock_bh(&priv->predt_lock);
} else {
err = nfp_flower_xmit_pre_tun_flow(app, flow_pay);
}
} else {
err = nfp_flower_xmit_flow(app, flow_pay,
NFP_FLOWER_CMSG_TYPE_FLOW_ADD);
}
if (err)
goto err_remove_rhash;
......@@ -1538,11 +1584,25 @@ nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev,
goto err_free_merge_flow;
}
if (nfp_flow->pre_tun_rule.dev)
err = nfp_flower_xmit_pre_tun_del_flow(app, nfp_flow);
else
if (nfp_flow->pre_tun_rule.dev) {
if (priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2) {
struct nfp_predt_entry *predt;
predt = nfp_flow->pre_tun_rule.predt;
if (predt) {
spin_lock_bh(&priv->predt_lock);
nfp_tun_unlink_and_update_nn_entries(app, predt);
list_del(&predt->list_head);
spin_unlock_bh(&priv->predt_lock);
kfree(predt);
}
} else {
err = nfp_flower_xmit_pre_tun_del_flow(app, nfp_flow);
}
} else {
err = nfp_flower_xmit_flow(app, nfp_flow,
NFP_FLOWER_CMSG_TYPE_FLOW_DEL);
}
/* Fall through on error. */
err_free_merge_flow:
......
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