Commit e431e712 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'sfc-tc-encap-actions-offload'

Edward Cree says:

====================
sfc: TC encap actions offload

This series adds support for offloading TC tunnel_key set actions to the
 EF100 driver, supporting VxLAN and GENEVE tunnels over IPv4 or IPv6.
====================

Link: https://lore.kernel.org/r/cover.1686240142.git.ecree.xilinx@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents d457a0e3 a1e82162
......@@ -10,7 +10,8 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \
efx_devlink.o
sfc-$(CONFIG_SFC_MTD) += mtd.o
sfc-$(CONFIG_SFC_SRIOV) += sriov.o ef10_sriov.o ef100_sriov.o ef100_rep.o \
mae.o tc.o tc_bindings.o tc_counters.o
mae.o tc.o tc_bindings.o tc_counters.o \
tc_encap_actions.o
obj-$(CONFIG_SFC) += sfc.o
......
......@@ -24,6 +24,7 @@
#include "rx_common.h"
#include "ef100_sriov.h"
#include "tc_bindings.h"
#include "tc_encap_actions.h"
#include "efx_devlink.h"
static void ef100_update_name(struct efx_nic *efx)
......@@ -300,14 +301,38 @@ int ef100_netdev_event(struct notifier_block *this,
{
struct efx_nic *efx = container_of(this, struct efx_nic, netdev_notifier);
struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
struct ef100_nic_data *nic_data = efx->nic_data;
int err;
if (efx->net_dev == net_dev &&
(event == NETDEV_CHANGENAME || event == NETDEV_REGISTER))
ef100_update_name(efx);
if (!nic_data->grp_mae)
return NOTIFY_DONE;
err = efx_tc_netdev_event(efx, event, net_dev);
if (err & NOTIFY_STOP_MASK)
return err;
return NOTIFY_DONE;
}
static int ef100_netevent_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct efx_nic *efx = container_of(this, struct efx_nic, netevent_notifier);
struct ef100_nic_data *nic_data = efx->nic_data;
int err;
if (!nic_data->grp_mae)
return NOTIFY_DONE;
err = efx_tc_netevent_event(efx, event, ptr);
if (err & NOTIFY_STOP_MASK)
return err;
return NOTIFY_DONE;
};
static int ef100_register_netdev(struct efx_nic *efx)
{
struct net_device *net_dev = efx->net_dev;
......@@ -367,6 +392,7 @@ void ef100_remove_netdev(struct efx_probe_data *probe_data)
rtnl_unlock();
unregister_netdevice_notifier(&efx->netdev_notifier);
unregister_netevent_notifier(&efx->netevent_notifier);
#if defined(CONFIG_SFC_SRIOV)
if (!efx->type->is_vf)
efx_ef100_pci_sriov_disable(efx, true);
......@@ -487,6 +513,14 @@ int ef100_probe_netdev(struct efx_probe_data *probe_data)
goto fail;
}
efx->netevent_notifier.notifier_call = ef100_netevent_event;
rc = register_netevent_notifier(&efx->netevent_notifier);
if (rc) {
netif_err(efx, probe, efx->net_dev,
"Failed to register netevent notifier, rc=%d\n", rc);
goto fail;
}
efx_probe_devlink_unlock(efx);
return rc;
fail:
......
......@@ -15,6 +15,7 @@
#include "mcdi.h"
#include "mcdi_pcol.h"
#include "mcdi_pcol_mae.h"
#include "tc_encap_actions.h"
int efx_mae_allocate_mport(struct efx_nic *efx, u32 *id, u32 *label)
{
......@@ -610,6 +611,87 @@ static int efx_mae_encap_type_to_mae_type(enum efx_encap_type type)
}
}
int efx_mae_allocate_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LEN(EFX_TC_MAX_ENCAP_HDR));
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_LEN);
size_t inlen, outlen;
int rc;
rc = efx_mae_encap_type_to_mae_type(encap->type);
if (rc < 0)
return rc;
MCDI_SET_DWORD(inbuf, MAE_ENCAP_HEADER_ALLOC_IN_ENCAP_TYPE, rc);
inlen = MC_CMD_MAE_ENCAP_HEADER_ALLOC_IN_LEN(encap->encap_hdr_len);
if (WARN_ON(inlen > sizeof(inbuf))) /* can't happen */
return -EINVAL;
memcpy(MCDI_PTR(inbuf, MAE_ENCAP_HEADER_ALLOC_IN_HDR_DATA),
encap->encap_hdr,
encap->encap_hdr_len);
rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ENCAP_HEADER_ALLOC, inbuf,
inlen, outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < sizeof(outbuf))
return -EIO;
encap->fw_id = MCDI_DWORD(outbuf, MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID);
return 0;
}
int efx_mae_update_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ENCAP_HEADER_UPDATE_IN_LEN(EFX_TC_MAX_ENCAP_HDR));
size_t inlen;
int rc;
rc = efx_mae_encap_type_to_mae_type(encap->type);
if (rc < 0)
return rc;
MCDI_SET_DWORD(inbuf, MAE_ENCAP_HEADER_UPDATE_IN_ENCAP_TYPE, rc);
MCDI_SET_DWORD(inbuf, MAE_ENCAP_HEADER_UPDATE_IN_EH_ID,
encap->fw_id);
inlen = MC_CMD_MAE_ENCAP_HEADER_UPDATE_IN_LEN(encap->encap_hdr_len);
if (WARN_ON(inlen > sizeof(inbuf))) /* can't happen */
return -EINVAL;
memcpy(MCDI_PTR(inbuf, MAE_ENCAP_HEADER_UPDATE_IN_HDR_DATA),
encap->encap_hdr,
encap->encap_hdr_len);
BUILD_BUG_ON(MC_CMD_MAE_ENCAP_HEADER_UPDATE_OUT_LEN != 0);
return efx_mcdi_rpc(efx, MC_CMD_MAE_ENCAP_HEADER_UPDATE, inbuf,
inlen, NULL, 0, NULL);
}
int efx_mae_free_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ENCAP_HEADER_FREE_OUT_LEN(1));
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ENCAP_HEADER_FREE_IN_LEN(1));
size_t outlen;
int rc;
MCDI_SET_DWORD(inbuf, MAE_ENCAP_HEADER_FREE_IN_EH_ID, encap->fw_id);
rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ENCAP_HEADER_FREE, inbuf,
sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < sizeof(outbuf))
return -EIO;
/* FW freed a different ID than we asked for, should also never happen.
* Warn because it means we've now got a different idea to the FW of
* what encap_mds exist, which could cause mayhem later.
*/
if (WARN_ON(MCDI_DWORD(outbuf, MAE_ENCAP_HEADER_FREE_OUT_FREED_EH_ID) != encap->fw_id))
return -EIO;
/* We're probably about to free @encap, but let's just make sure its
* fw_id is blatted so that it won't look valid if it leaks out.
*/
encap->fw_id = MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL;
return 0;
}
int efx_mae_lookup_mport(struct efx_nic *efx, u32 vf_idx, u32 *id)
{
struct ef100_nic_data *nic_data = efx->nic_data;
......@@ -833,8 +915,12 @@ int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
MCDI_SET_WORD_BE(inbuf, MAE_ACTION_SET_ALLOC_IN_VLAN1_PROTO_BE,
act->vlan_proto[1]);
}
MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
if (act->encap_md)
MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
act->encap_md->fw_id);
else
MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
if (act->deliver)
MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
act->dest_mport);
......@@ -1229,6 +1315,29 @@ int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
return 0;
}
int efx_mae_update_rule(struct efx_nic *efx, u32 acts_id, u32 id)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_UPDATE_IN_LEN);
MCDI_DECLARE_STRUCT_PTR(response);
BUILD_BUG_ON(MC_CMD_MAE_ACTION_RULE_UPDATE_OUT_LEN);
response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_UPDATE_IN_RESPONSE);
MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_UPDATE_IN_AR_ID, id);
if (efx_mae_asl_id(acts_id)) {
MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
} else {
/* We only had one AS, so we didn't wrap it in an ASL */
MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
}
return efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_UPDATE, inbuf, sizeof(inbuf),
NULL, 0, NULL);
}
int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
......
......@@ -90,6 +90,13 @@ int efx_mae_check_encap_type_supported(struct efx_nic *efx,
int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt);
int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt);
int efx_mae_allocate_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap);
int efx_mae_update_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap);
int efx_mae_free_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap);
int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act);
int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id);
......@@ -105,6 +112,7 @@ int efx_mae_unregister_encap_match(struct efx_nic *efx,
int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
u32 prio, u32 acts_id, u32 *id);
int efx_mae_update_rule(struct efx_nic *efx, u32 acts_id, u32 id);
int efx_mae_delete_rule(struct efx_nic *efx, u32 id);
int efx_init_mae(struct efx_nic *efx);
......
......@@ -27,6 +27,7 @@
#include <linux/mtd/mtd.h>
#include <net/busy_poll.h>
#include <net/xdp.h>
#include <net/netevent.h>
#include "enum.h"
#include "bitfield.h"
......@@ -996,6 +997,7 @@ struct efx_mae;
* @xdp_rxq_info_failed: Have any of the rx queues failed to initialise their
* xdp_rxq_info structures?
* @netdev_notifier: Netdevice notifier.
* @netevent_notifier: Netevent notifier (for neighbour updates).
* @tc: state for TC offload (EF100).
* @devlink: reference to devlink structure owned by this device
* @dl_port: devlink port associated with the PF
......@@ -1183,6 +1185,7 @@ struct efx_nic {
bool xdp_rxq_info_failed;
struct notifier_block netdev_notifier;
struct notifier_block netevent_notifier;
struct efx_tc_state *tc;
struct devlink *devlink;
......
This diff is collapsed.
......@@ -25,6 +25,8 @@ static inline bool efx_ipv6_addr_all_ones(struct in6_addr *addr)
}
#endif
struct efx_tc_encap_action; /* see tc_encap_actions.h */
struct efx_tc_action_set {
u16 vlan_push:2;
u16 vlan_pop:2;
......@@ -33,6 +35,9 @@ struct efx_tc_action_set {
__be16 vlan_tci[2]; /* TCIs for vlan_push */
__be16 vlan_proto[2]; /* Ethertypes for vlan_push */
struct efx_tc_counter_index *count;
struct efx_tc_encap_action *encap_md; /* entry in tc_encap_ht table */
struct list_head encap_user; /* entry on encap_md->users list */
struct efx_tc_action_set_list *user; /* Only populated if encap_md */
u32 dest_mport;
u32 fw_id; /* index of this entry in firmware actions table */
struct list_head list;
......@@ -127,6 +132,7 @@ struct efx_tc_flow_rule {
struct rhash_head linkage;
struct efx_tc_match match;
struct efx_tc_action_set_list acts;
struct efx_tc_action_set_list *fallback; /* what to use when unready? */
u32 fw_id;
};
......@@ -144,8 +150,10 @@ enum efx_tc_rule_prios {
* @mutex: Used to serialise operations on TC hashtables
* @counter_ht: Hashtable of TC counters (FW IDs and counter values)
* @counter_id_ht: Hashtable mapping TC counter cookies to counters
* @encap_ht: Hashtable of TC encap actions
* @encap_match_ht: Hashtable of TC encap matches
* @match_action_ht: Hashtable of TC match-action rules
* @neigh_ht: Hashtable of neighbour watches (&struct efx_neigh_binder)
* @reps_mport_id: MAE port allocated for representor RX
* @reps_filter_uc: VNIC filter for representor unicast RX (promisc)
* @reps_filter_mc: VNIC filter for representor multicast RX (allmulti)
......@@ -160,6 +168,11 @@ enum efx_tc_rule_prios {
* %EFX_TC_PRIO_DFLT. Named by *ingress* port
* @dflt.pf: rule for traffic ingressing from PF (egresses to wire)
* @dflt.wire: rule for traffic ingressing from wire (egresses to PF)
* @facts: Fallback action-set-lists for unready rules. Named by *egress* port
* @facts.pf: action-set-list for unready rules on PF netdev, hence applying to
* traffic from wire, and egressing to PF
* @facts.reps: action-set-list for unready rules on representors, hence
* applying to traffic from representees, and egressing to the reps mport
* @up: have TC datastructures been set up?
*/
struct efx_tc_state {
......@@ -168,8 +181,10 @@ struct efx_tc_state {
struct mutex mutex;
struct rhashtable counter_ht;
struct rhashtable counter_id_ht;
struct rhashtable encap_ht;
struct rhashtable encap_match_ht;
struct rhashtable match_action_ht;
struct rhashtable neigh_ht;
u32 reps_mport_id, reps_mport_vport_id;
s32 reps_filter_uc, reps_filter_mc;
bool flush_counters;
......@@ -180,11 +195,19 @@ struct efx_tc_state {
struct efx_tc_flow_rule pf;
struct efx_tc_flow_rule wire;
} dflt;
struct {
struct efx_tc_action_set_list pf;
struct efx_tc_action_set_list reps;
} facts;
bool up;
};
struct efx_rep;
enum efx_encap_type efx_tc_indr_netdev_type(struct net_device *net_dev);
struct efx_rep *efx_tc_flower_lookup_efv(struct efx_nic *efx,
struct net_device *dev);
s64 efx_tc_flower_external_mport(struct efx_nic *efx, struct efx_rep *efv);
int efx_tc_configure_default_rule_rep(struct efx_rep *efv);
void efx_tc_deconfigure_default_rule(struct efx_nic *efx,
struct efx_tc_flow_rule *rule);
......
......@@ -10,6 +10,7 @@
#include "tc_bindings.h"
#include "tc.h"
#include "tc_encap_actions.h"
struct efx_tc_block_binding {
struct list_head list;
......@@ -226,3 +227,15 @@ int efx_tc_setup(struct net_device *net_dev, enum tc_setup_type type,
return -EOPNOTSUPP;
}
int efx_tc_netdev_event(struct efx_nic *efx, unsigned long event,
struct net_device *net_dev)
{
if (efx->type->is_vf)
return NOTIFY_DONE;
if (event == NETDEV_UNREGISTER)
efx_tc_unregister_egdev(efx, net_dev);
return NOTIFY_OK;
}
......@@ -26,4 +26,6 @@ int efx_tc_indr_setup_cb(struct net_device *net_dev, struct Qdisc *sch,
void *cb_priv, enum tc_setup_type type,
void *type_data, void *data,
void (*cleanup)(struct flow_block_cb *block_cb));
int efx_tc_netdev_event(struct efx_nic *efx, unsigned long event,
struct net_device *net_dev);
#endif /* EFX_TC_BINDINGS_H */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/****************************************************************************
* Driver for Solarflare network controllers and boards
* Copyright 2023, Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#ifndef EFX_TC_ENCAP_ACTIONS_H
#define EFX_TC_ENCAP_ACTIONS_H
#include "net_driver.h"
#include <linux/refcount.h>
#include <net/tc_act/tc_tunnel_key.h>
/**
* struct efx_neigh_binder - driver state for a neighbour entry
* @net: the network namespace in which this neigh resides
* @dst_ip: the IPv4 destination address resolved by this neigh
* @dst_ip6: the IPv6 destination address resolved by this neigh
* @ha: the hardware (Ethernet) address of the neighbour
* @n_valid: true if the neighbour is in NUD_VALID state
* @lock: protects @ha and @n_valid
* @ttl: Time To Live associated with the route used
* @dying: set when egdev is going away, to skip further updates
* @egdev: egress device from the route lookup. Holds a reference
* @dev_tracker: reference tracker entry for @egdev
* @ns_tracker: reference tracker entry for @ns
* @ref: counts encap actions referencing this entry
* @used: jiffies of last time traffic hit any encap action using this.
* When counter reads update this, a new neighbour event is sent to
* indicate that the neighbour entry is still in use.
* @users: list of &struct efx_tc_encap_action
* @linkage: entry in efx->neigh_ht (keys are @net, @dst_ip, @dst_ip6).
* @work: processes neighbour state changes, updates the encap actions
* @efx: owning NIC instance.
*
* Associates a neighbour entry with the encap actions that are
* interested in it, allowing the latter to be updated when the
* neighbour details change.
* Whichever of @dst_ip and @dst_ip6 is not in use will be all-zeroes,
* this distinguishes IPv4 from IPv6 entries.
*/
struct efx_neigh_binder {
struct net *net;
__be32 dst_ip;
struct in6_addr dst_ip6;
char ha[ETH_ALEN];
bool n_valid;
rwlock_t lock;
u8 ttl;
bool dying;
struct net_device *egdev;
netdevice_tracker dev_tracker;
netns_tracker ns_tracker;
refcount_t ref;
unsigned long used;
struct list_head users;
struct rhash_head linkage;
struct work_struct work;
struct efx_nic *efx;
};
/* This limit is arbitrary; current hardware (SN1022) handles encap headers
* of up to 126 bytes, but that limit is not enshrined in the MCDI protocol.
*/
#define EFX_TC_MAX_ENCAP_HDR 126
struct efx_tc_encap_action {
enum efx_encap_type type;
struct ip_tunnel_key key; /* 52 bytes */
u32 dest_mport; /* is copied into struct efx_tc_action_set */
u8 encap_hdr_len;
bool n_valid;
u8 encap_hdr[EFX_TC_MAX_ENCAP_HDR];
struct efx_neigh_binder *neigh;
struct list_head list; /* entry on neigh->users list */
struct list_head users; /* action sets using this encap_md */
struct rhash_head linkage; /* efx->tc_encap_ht */
refcount_t ref;
u32 fw_id; /* index of this entry in firmware encap table */
};
/* create/uncreate/teardown hashtables */
int efx_tc_init_encap_actions(struct efx_nic *efx);
void efx_tc_destroy_encap_actions(struct efx_nic *efx);
void efx_tc_fini_encap_actions(struct efx_nic *efx);
struct efx_tc_flow_rule;
bool efx_tc_check_ready(struct efx_nic *efx, struct efx_tc_flow_rule *rule);
struct efx_tc_encap_action *efx_tc_flower_create_encap_md(
struct efx_nic *efx, const struct ip_tunnel_info *info,
struct net_device *egdev, struct netlink_ext_ack *extack);
void efx_tc_flower_release_encap_md(struct efx_nic *efx,
struct efx_tc_encap_action *encap);
void efx_tc_unregister_egdev(struct efx_nic *efx, struct net_device *net_dev);
int efx_tc_netevent_event(struct efx_nic *efx, unsigned long event,
void *ptr);
#endif /* EFX_TC_ENCAP_ACTIONS_H */
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