Skip to content

Commit

Permalink
Merge branch 'sfc-tc-offload'
Browse files Browse the repository at this point in the history
Edward Cree says:

====================
sfc: bare bones TC offload

This series begins the work of supporting TC flower offload on EF100 NICs.
This is the absolute minimum viable TC implementation to get traffic to
 VFs and allow them to be tested; it supports no match fields besides
 ingress port, no actions besides mirred and drop, and no stats.
More matches, actions, and counters will be added in subsequent patches.

Changed in v2:
 - Add missing 'static' on declarations (kernel test robot, sparse)
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 28, 2022
2 parents c87e4ad + d902e1a commit b9a5cbf
Show file tree
Hide file tree
Showing 16 changed files with 980 additions and 3 deletions.
2 changes: 1 addition & 1 deletion drivers/net/ethernet/sfc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \
ef100_ethtool.o ef100_rx.o ef100_tx.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
mae.o tc.o tc_bindings.o

obj-$(CONFIG_SFC) += sfc.o

Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/sfc/ef100_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const struct ethtool_ops ef100_ethtool_ops = {
.get_pauseparam = efx_ethtool_get_pauseparam,
.set_pauseparam = efx_ethtool_set_pauseparam,
.get_sset_count = efx_ethtool_get_sset_count,
.get_priv_flags = efx_ethtool_get_priv_flags,
.set_priv_flags = efx_ethtool_set_priv_flags,
.self_test = efx_ethtool_self_test,
.get_strings = efx_ethtool_get_strings,
.get_link_ksettings = efx_ethtool_get_link_ksettings,
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/sfc/ef100_netdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "mcdi_filters.h"
#include "rx_common.h"
#include "ef100_sriov.h"
#include "tc_bindings.h"

static void ef100_update_name(struct efx_nic *efx)
{
Expand Down Expand Up @@ -246,6 +247,9 @@ static const struct net_device_ops ef100_netdev_ops = {
#ifdef CONFIG_RFS_ACCEL
.ndo_rx_flow_steer = efx_filter_rfs,
#endif
#ifdef CONFIG_SFC_SRIOV
.ndo_setup_tc = efx_tc_setup,
#endif
};

/* Netdev registration
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/ethernet/sfc/ef100_nic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,9 @@ int ef100_probe_netdev_pf(struct efx_nic *efx)
*/
netif_warn(efx, probe, net_dev, "Failed to probe MAE rc %d\n",
rc);
} else {
net_dev->features |= NETIF_F_HW_TC;
efx->fixed_features |= NETIF_F_HW_TC;
}
#endif
return 0;
Expand Down
18 changes: 17 additions & 1 deletion drivers/net/ethernet/sfc/ef100_rep.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "ef100_nic.h"
#include "mae.h"
#include "rx_common.h"
#include "tc_bindings.h"

#define EFX_EF100_REP_DRIVER "efx_ef100_rep"

Expand Down Expand Up @@ -107,6 +108,20 @@ static int efx_ef100_rep_get_phys_port_name(struct net_device *dev,
return 0;
}

static int efx_ef100_rep_setup_tc(struct net_device *net_dev,
enum tc_setup_type type, void *type_data)
{
struct efx_rep *efv = netdev_priv(net_dev);
struct efx_nic *efx = efv->parent;

if (type == TC_SETUP_CLSFLOWER)
return efx_tc_flower(efx, net_dev, type_data, efv);
if (type == TC_SETUP_BLOCK)
return efx_tc_setup_block(net_dev, efx, type_data, efv);

return -EOPNOTSUPP;
}

static void efx_ef100_rep_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
Expand All @@ -120,13 +135,14 @@ static void efx_ef100_rep_get_stats64(struct net_device *dev,
stats->tx_errors = atomic64_read(&efv->stats.tx_errors);
}

static const struct net_device_ops efx_ef100_rep_netdev_ops = {
const struct net_device_ops efx_ef100_rep_netdev_ops = {
.ndo_open = efx_ef100_rep_open,
.ndo_stop = efx_ef100_rep_close,
.ndo_start_xmit = efx_ef100_rep_xmit,
.ndo_get_port_parent_id = efx_ef100_rep_get_port_parent_id,
.ndo_get_phys_port_name = efx_ef100_rep_get_phys_port_name,
.ndo_get_stats64 = efx_ef100_rep_get_stats64,
.ndo_setup_tc = efx_ef100_rep_setup_tc,
};

static void efx_ef100_rep_get_drvinfo(struct net_device *dev,
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/sfc/ef100_rep.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ void efx_ef100_rep_rx_packet(struct efx_rep *efv, struct efx_rx_buffer *rx_buf);
* Caller must hold rcu_read_lock().
*/
struct efx_rep *efx_ef100_find_rep_by_mport(struct efx_nic *efx, u16 mport);
extern const struct net_device_ops efx_ef100_rep_netdev_ops;
#endif /* EF100_REP_H */
37 changes: 37 additions & 0 deletions drivers/net/ethernet/sfc/ethtool_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = {

#define EFX_ETHTOOL_SW_STAT_COUNT ARRAY_SIZE(efx_sw_stat_desc)

static const char efx_ethtool_priv_flags_strings[][ETH_GSTRING_LEN] = {
"log-tc-errors",
};

#define EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS BIT(0)

#define EFX_ETHTOOL_PRIV_FLAGS_COUNT ARRAY_SIZE(efx_ethtool_priv_flags_strings)

void efx_ethtool_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *info)
{
Expand Down Expand Up @@ -452,6 +460,8 @@ int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set)
efx_ptp_describe_stats(efx, NULL);
case ETH_SS_TEST:
return efx_ethtool_fill_self_tests(efx, NULL, NULL, NULL);
case ETH_SS_PRIV_FLAGS:
return EFX_ETHTOOL_PRIV_FLAGS_COUNT;
default:
return -EINVAL;
}
Expand All @@ -478,12 +488,39 @@ void efx_ethtool_get_strings(struct net_device *net_dev,
case ETH_SS_TEST:
efx_ethtool_fill_self_tests(efx, NULL, strings, NULL);
break;
case ETH_SS_PRIV_FLAGS:
for (i = 0; i < EFX_ETHTOOL_PRIV_FLAGS_COUNT; i++)
strscpy(strings + i * ETH_GSTRING_LEN,
efx_ethtool_priv_flags_strings[i],
ETH_GSTRING_LEN);
break;
default:
/* No other string sets */
break;
}
}

u32 efx_ethtool_get_priv_flags(struct net_device *net_dev)
{
struct efx_nic *efx = efx_netdev_priv(net_dev);
u32 ret_flags = 0;

if (efx->log_tc_errs)
ret_flags |= EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS;

return ret_flags;
}

int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags)
{
struct efx_nic *efx = efx_netdev_priv(net_dev);

efx->log_tc_errs =
!!(flags & EFX_ETHTOOL_PRIV_FLAGS_LOG_TC_ERRS);

return 0;
}

void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats,
u64 *data)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/sfc/ethtool_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ int efx_ethtool_fill_self_tests(struct efx_nic *efx,
int efx_ethtool_get_sset_count(struct net_device *net_dev, int string_set);
void efx_ethtool_get_strings(struct net_device *net_dev, u32 string_set,
u8 *strings);
u32 efx_ethtool_get_priv_flags(struct net_device *net_dev);
int efx_ethtool_set_priv_flags(struct net_device *net_dev, u32 flags);
void efx_ethtool_get_stats(struct net_device *net_dev,
struct ethtool_stats *stats __attribute__ ((unused)),
u64 *data);
Expand Down
165 changes: 165 additions & 0 deletions drivers/net/ethernet/sfc/mae.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,167 @@ int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
return 0;
}

static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
size_t outlen;
int rc;

BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN);

rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf,
sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < sizeof(outbuf))
return -EIO;
caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS);
return 0;
}

static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd,
u8 *field_support)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS));
MCDI_DECLARE_STRUCT_PTR(caps);
unsigned int count;
size_t outlen;
int rc, i;

BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN);

rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT);
memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS);
caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS);
/* We're only interested in the support status enum, not any other
* flags, so just extract that from each entry.
*/
for (i = 0; i < count; i++)
if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen)
field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS);
return 0;
}

int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
{
int rc;

rc = efx_mae_get_basic_caps(efx, caps);
if (rc)
return rc;
return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
caps->action_rule_fields);
}

/* Bit twiddling:
* Prefix: 1...110...0
* ~: 0...001...1
* + 1: 0...010...0 is power of two
* so (~x) & ((~x) + 1) == 0. Converse holds also.
*/
#define is_prefix_byte(_x) !(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1))

enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER };

static const char *mask_type_name(enum mask_type typ)
{
switch (typ) {
case MASK_ONES:
return "all-1s";
case MASK_ZEROES:
return "all-0s";
case MASK_PREFIX:
return "prefix";
case MASK_OTHER:
return "arbitrary";
default: /* can't happen */
return "unknown";
}
}

/* Checks a (big-endian) bytestring is a bit prefix */
static enum mask_type classify_mask(const u8 *mask, size_t len)
{
bool zeroes = true; /* All bits seen so far are zeroes */
bool ones = true; /* All bits seen so far are ones */
bool prefix = true; /* Valid prefix so far */
size_t i;

for (i = 0; i < len; i++) {
if (ones) {
if (!is_prefix_byte(mask[i]))
prefix = false;
} else if (mask[i]) {
prefix = false;
}
if (mask[i] != 0xff)
ones = false;
if (mask[i])
zeroes = false;
}
if (ones)
return MASK_ONES;
if (zeroes)
return MASK_ZEROES;
if (prefix)
return MASK_PREFIX;
return MASK_OTHER;
}

static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ)
{
switch (support) {
case MAE_FIELD_UNSUPPORTED:
case MAE_FIELD_SUPPORTED_MATCH_NEVER:
if (typ == MASK_ZEROES)
return 0;
return -EOPNOTSUPP;
case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
if (typ == MASK_ZEROES)
return 0;
fallthrough;
case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
if (typ == MASK_ONES)
return 0;
return -EINVAL;
case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
if (typ == MASK_OTHER)
return -EOPNOTSUPP;
return 0;
case MAE_FIELD_SUPPORTED_MATCH_MASK:
return 0;
default:
return -EIO;
}
}

int efx_mae_match_check_caps(struct efx_nic *efx,
const struct efx_tc_match_fields *mask,
struct netlink_ext_ack *extack)
{
const u8 *supported_fields = efx->tc->caps->action_rule_fields;
__be32 ingress_port = cpu_to_be32(mask->ingress_port);
enum mask_type ingress_port_mask_type;
int rc;

/* Check for _PREFIX assumes big-endian, so we need to convert */
ingress_port_mask_type = classify_mask((const u8 *)&ingress_port,
sizeof(ingress_port));
rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
ingress_port_mask_type);
if (rc) {
efx_tc_err(efx, "No support for %s mask in field ingress_port\n",
mask_type_name(ingress_port_mask_type));
NL_SET_ERR_MSG_MOD(extack, "Unsupported mask type for ingress_port");
return rc;
}
return 0;
}

static bool efx_mae_asl_id(u32 id)
{
return !!(id & BIT(31));
Expand Down Expand Up @@ -279,6 +440,10 @@ static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
}
MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
match->mask.ingress_port);
MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
match->value.recirc_id);
MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
match->mask.recirc_id);
return 0;
}

Expand Down
14 changes: 14 additions & 0 deletions drivers/net/ethernet/sfc/mae.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ void efx_mae_mport_mport(struct efx_nic *efx, u32 mport_id, u32 *out);

int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id);

#define MAE_NUM_FIELDS (MAE_FIELD_ENC_VNET_ID + 1)

struct mae_caps {
u32 match_field_count;
u32 action_prios;
u8 action_rule_fields[MAE_NUM_FIELDS];
};

int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps);

int efx_mae_match_check_caps(struct efx_nic *efx,
const struct efx_tc_match_fields *mask,
struct netlink_ext_ack *extack);

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);

Expand Down
10 changes: 10 additions & 0 deletions drivers/net/ethernet/sfc/mcdi.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,23 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
((u8 *)(_buf) + (_offset))
#define MCDI_PTR(_buf, _field) \
_MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST)
/* Use MCDI_STRUCT_ functions to access members of MCDI structuredefs.
* _buf should point to the start of the structure, typically obtained with
* MCDI_DECLARE_STRUCT_PTR(structure) = _MCDI_DWORD(mcdi_buf, FIELD_WHICH_IS_STRUCT);
*/
#define MCDI_STRUCT_PTR(_buf, _field) \
_MCDI_PTR(_buf, _field ## _OFST)
#define _MCDI_CHECK_ALIGN(_ofst, _align) \
((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1)))
#define _MCDI_DWORD(_buf, _field) \
((_buf) + (_MCDI_CHECK_ALIGN(MC_CMD_ ## _field ## _OFST, 4) >> 2))
#define _MCDI_STRUCT_DWORD(_buf, _field) \
((_buf) + (_MCDI_CHECK_ALIGN(_field ## _OFST, 4) >> 2))

#define MCDI_STRUCT_SET_BYTE(_buf, _field, _value) do { \
BUILD_BUG_ON(_field ## _LEN != 1); \
*(u8 *)MCDI_STRUCT_PTR(_buf, _field) = _value; \
} while (0)
#define MCDI_BYTE(_buf, _field) \
((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \
*MCDI_PTR(_buf, _field))
Expand Down
Loading

0 comments on commit b9a5cbf

Please sign in to comment.