forked from mirrors/linux
		
	sfc: bare bones TC offload on EF100
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.
Example usage:
    tc filter add dev $PF parent ffff: flower skip_sw \
        action mirred egress mirror dev $VFREP
    tc filter add dev $VFREP parent ffff: flower skip_sw \
        action mirred egress redirect dev $PF
 gives a VF unfiltered access to the network out the physical port ($PF
 acts here as a physical port representor).
More matches, actions, and counters will be added in subsequent patches.
Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									7ce3e235f2
								
							
						
					
					
						commit
						d902e1a737
					
				
					 5 changed files with 397 additions and 0 deletions
				
			
		| 
						 | 
					@ -168,6 +168,111 @@ int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
 | 
				
			||||||
				       caps->action_rule_fields);
 | 
									       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)
 | 
					static bool efx_mae_asl_id(u32 id)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return !!(id & BIT(31));
 | 
						return !!(id & BIT(31));
 | 
				
			||||||
| 
						 | 
					@ -335,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,
 | 
						MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
 | 
				
			||||||
			      match->mask.ingress_port);
 | 
								      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;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,10 @@ struct mae_caps {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps);
 | 
					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_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);
 | 
					int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,6 +201,12 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
 | 
				
			||||||
	((u8 *)(_buf) + (_offset))
 | 
						((u8 *)(_buf) + (_offset))
 | 
				
			||||||
#define MCDI_PTR(_buf, _field)						\
 | 
					#define MCDI_PTR(_buf, _field)						\
 | 
				
			||||||
	_MCDI_PTR(_buf, MC_CMD_ ## _field ## _OFST)
 | 
						_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)				\
 | 
					#define _MCDI_CHECK_ALIGN(_ofst, _align)				\
 | 
				
			||||||
	((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1)))
 | 
						((_ofst) + BUILD_BUG_ON_ZERO((_ofst) & (_align - 1)))
 | 
				
			||||||
#define _MCDI_DWORD(_buf, _field)					\
 | 
					#define _MCDI_DWORD(_buf, _field)					\
 | 
				
			||||||
| 
						 | 
					@ -208,6 +214,10 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
 | 
				
			||||||
#define _MCDI_STRUCT_DWORD(_buf, _field)				\
 | 
					#define _MCDI_STRUCT_DWORD(_buf, _field)				\
 | 
				
			||||||
	((_buf) + (_MCDI_CHECK_ALIGN(_field ## _OFST, 4) >> 2))
 | 
						((_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)						\
 | 
					#define MCDI_BYTE(_buf, _field)						\
 | 
				
			||||||
	((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1),	\
 | 
						((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1),	\
 | 
				
			||||||
	 *MCDI_PTR(_buf, _field))
 | 
						 *MCDI_PTR(_buf, _field))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
 * by the Free Software Foundation, incorporated herein by reference.
 | 
					 * by the Free Software Foundation, incorporated herein by reference.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <net/pkt_cls.h>
 | 
				
			||||||
#include "tc.h"
 | 
					#include "tc.h"
 | 
				
			||||||
#include "tc_bindings.h"
 | 
					#include "tc_bindings.h"
 | 
				
			||||||
#include "mae.h"
 | 
					#include "mae.h"
 | 
				
			||||||
| 
						 | 
					@ -42,6 +43,20 @@ static struct efx_rep *efx_tc_flower_lookup_efv(struct efx_nic *efx,
 | 
				
			||||||
	return efv;
 | 
						return efv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Convert a driver-internal vport ID into an external device (wire or VF) */
 | 
				
			||||||
 | 
					static s64 efx_tc_flower_external_mport(struct efx_nic *efx, struct efx_rep *efv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 mport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (IS_ERR(efv))
 | 
				
			||||||
 | 
							return PTR_ERR(efv);
 | 
				
			||||||
 | 
						if (!efv) /* device is PF (us) */
 | 
				
			||||||
 | 
							efx_mae_mport_wire(efx, &mport);
 | 
				
			||||||
 | 
						else /* device is repr */
 | 
				
			||||||
 | 
							efx_mae_mport_mport(efx, efv->mport, &mport);
 | 
				
			||||||
 | 
						return mport;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct rhashtable_params efx_tc_match_action_ht_params = {
 | 
					static const struct rhashtable_params efx_tc_match_action_ht_params = {
 | 
				
			||||||
	.key_len	= sizeof(unsigned long),
 | 
						.key_len	= sizeof(unsigned long),
 | 
				
			||||||
	.key_offset	= offsetof(struct efx_tc_flow_rule, cookie),
 | 
						.key_offset	= offsetof(struct efx_tc_flow_rule, cookie),
 | 
				
			||||||
| 
						 | 
					@ -109,6 +124,260 @@ static void efx_tc_flow_free(void *ptr, void *arg)
 | 
				
			||||||
	kfree(rule);
 | 
						kfree(rule);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int efx_tc_flower_parse_match(struct efx_nic *efx,
 | 
				
			||||||
 | 
									     struct flow_rule *rule,
 | 
				
			||||||
 | 
									     struct efx_tc_match *match,
 | 
				
			||||||
 | 
									     struct netlink_ext_ack *extack)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct flow_dissector *dissector = rule->match.dissector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
 | 
				
			||||||
 | 
							struct flow_match_control fm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							flow_rule_match_control(rule, &fm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fm.mask->flags) {
 | 
				
			||||||
 | 
								efx_tc_err(efx, "Unsupported match on control.flags %#x\n",
 | 
				
			||||||
 | 
									   fm.mask->flags);
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG_MOD(extack, "Unsupported match on control.flags");
 | 
				
			||||||
 | 
								return -EOPNOTSUPP;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (dissector->used_keys &
 | 
				
			||||||
 | 
						    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
 | 
				
			||||||
 | 
						      BIT(FLOW_DISSECTOR_KEY_BASIC))) {
 | 
				
			||||||
 | 
							efx_tc_err(efx, "Unsupported flower keys %#x\n", dissector->used_keys);
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG_MOD(extack, "Unsupported flower keys encountered");
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
 | 
				
			||||||
 | 
							struct flow_match_basic fm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							flow_rule_match_basic(rule, &fm);
 | 
				
			||||||
 | 
							if (fm.mask->n_proto) {
 | 
				
			||||||
 | 
								EFX_TC_ERR_MSG(efx, extack, "Unsupported eth_proto match\n");
 | 
				
			||||||
 | 
								return -EOPNOTSUPP;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (fm.mask->ip_proto) {
 | 
				
			||||||
 | 
								EFX_TC_ERR_MSG(efx, extack, "Unsupported ip_proto match\n");
 | 
				
			||||||
 | 
								return -EOPNOTSUPP;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int efx_tc_flower_replace(struct efx_nic *efx,
 | 
				
			||||||
 | 
									 struct net_device *net_dev,
 | 
				
			||||||
 | 
									 struct flow_cls_offload *tc,
 | 
				
			||||||
 | 
									 struct efx_rep *efv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct flow_rule *fr = flow_cls_offload_flow_rule(tc);
 | 
				
			||||||
 | 
						struct netlink_ext_ack *extack = tc->common.extack;
 | 
				
			||||||
 | 
						struct efx_tc_flow_rule *rule = NULL, *old;
 | 
				
			||||||
 | 
						struct efx_tc_action_set *act = NULL;
 | 
				
			||||||
 | 
						const struct flow_action_entry *fa;
 | 
				
			||||||
 | 
						struct efx_rep *from_efv, *to_efv;
 | 
				
			||||||
 | 
						struct efx_tc_match match;
 | 
				
			||||||
 | 
						s64 rc;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!tc_can_offload_extack(efx->net_dev, extack))
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						if (WARN_ON(!efx->tc))
 | 
				
			||||||
 | 
							return -ENETDOWN;
 | 
				
			||||||
 | 
						if (WARN_ON(!efx->tc->up))
 | 
				
			||||||
 | 
							return -ENETDOWN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						from_efv = efx_tc_flower_lookup_efv(efx, net_dev);
 | 
				
			||||||
 | 
						if (IS_ERR(from_efv)) {
 | 
				
			||||||
 | 
							/* Might be a tunnel decap rule from an indirect block.
 | 
				
			||||||
 | 
							 * Support for those not implemented yet.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (efv != from_efv) {
 | 
				
			||||||
 | 
							/* can't happen */
 | 
				
			||||||
 | 
							efx_tc_err(efx, "for %s efv is %snull but from_efv is %snull\n",
 | 
				
			||||||
 | 
								   netdev_name(net_dev), efv ? "non-" : "",
 | 
				
			||||||
 | 
								   from_efv ? "non-" : "");
 | 
				
			||||||
 | 
							if (efv)
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG_MOD(extack, "vfrep filter has PF net_dev (can't happen)");
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG_MOD(extack, "PF filter has vfrep net_dev (can't happen)");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Parse match */
 | 
				
			||||||
 | 
						memset(&match, 0, sizeof(match));
 | 
				
			||||||
 | 
						rc = efx_tc_flower_external_mport(efx, from_efv);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							EFX_TC_ERR_MSG(efx, extack, "Failed to identify ingress m-port");
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						match.value.ingress_port = rc;
 | 
				
			||||||
 | 
						match.mask.ingress_port = ~0;
 | 
				
			||||||
 | 
						rc = efx_tc_flower_parse_match(efx, fr, &match, extack);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tc->common.chain_index) {
 | 
				
			||||||
 | 
							EFX_TC_ERR_MSG(efx, extack, "No support for nonzero chain_index");
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						match.mask.recirc_id = 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = efx_mae_match_check_caps(efx, &match.mask, extack);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rule = kzalloc(sizeof(*rule), GFP_USER);
 | 
				
			||||||
 | 
						if (!rule)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&rule->acts.list);
 | 
				
			||||||
 | 
						rule->cookie = tc->cookie;
 | 
				
			||||||
 | 
						old = rhashtable_lookup_get_insert_fast(&efx->tc->match_action_ht,
 | 
				
			||||||
 | 
											&rule->linkage,
 | 
				
			||||||
 | 
											efx_tc_match_action_ht_params);
 | 
				
			||||||
 | 
						if (old) {
 | 
				
			||||||
 | 
							netif_dbg(efx, drv, efx->net_dev,
 | 
				
			||||||
 | 
								  "Already offloaded rule (cookie %lx)\n", tc->cookie);
 | 
				
			||||||
 | 
							rc = -EEXIST;
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG_MOD(extack, "Rule already offloaded");
 | 
				
			||||||
 | 
							goto release;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Parse actions */
 | 
				
			||||||
 | 
						act = kzalloc(sizeof(*act), GFP_USER);
 | 
				
			||||||
 | 
						if (!act) {
 | 
				
			||||||
 | 
							rc = -ENOMEM;
 | 
				
			||||||
 | 
							goto release;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flow_action_for_each(i, fa, &fr->action) {
 | 
				
			||||||
 | 
							struct efx_tc_action_set save;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!act) {
 | 
				
			||||||
 | 
								/* more actions after a non-pipe action */
 | 
				
			||||||
 | 
								EFX_TC_ERR_MSG(efx, extack, "Action follows non-pipe action");
 | 
				
			||||||
 | 
								rc = -EINVAL;
 | 
				
			||||||
 | 
								goto release;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (fa->id) {
 | 
				
			||||||
 | 
							case FLOW_ACTION_DROP:
 | 
				
			||||||
 | 
								rc = efx_mae_alloc_action_set(efx, act);
 | 
				
			||||||
 | 
								if (rc) {
 | 
				
			||||||
 | 
									EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (drop)");
 | 
				
			||||||
 | 
									goto release;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								list_add_tail(&act->list, &rule->acts.list);
 | 
				
			||||||
 | 
								act = NULL; /* end of the line */
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case FLOW_ACTION_REDIRECT:
 | 
				
			||||||
 | 
							case FLOW_ACTION_MIRRED:
 | 
				
			||||||
 | 
								save = *act;
 | 
				
			||||||
 | 
								to_efv = efx_tc_flower_lookup_efv(efx, fa->dev);
 | 
				
			||||||
 | 
								if (IS_ERR(to_efv)) {
 | 
				
			||||||
 | 
									EFX_TC_ERR_MSG(efx, extack, "Mirred egress device not on switch");
 | 
				
			||||||
 | 
									rc = PTR_ERR(to_efv);
 | 
				
			||||||
 | 
									goto release;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								rc = efx_tc_flower_external_mport(efx, to_efv);
 | 
				
			||||||
 | 
								if (rc < 0) {
 | 
				
			||||||
 | 
									EFX_TC_ERR_MSG(efx, extack, "Failed to identify egress m-port");
 | 
				
			||||||
 | 
									goto release;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								act->dest_mport = rc;
 | 
				
			||||||
 | 
								act->deliver = 1;
 | 
				
			||||||
 | 
								rc = efx_mae_alloc_action_set(efx, act);
 | 
				
			||||||
 | 
								if (rc) {
 | 
				
			||||||
 | 
									EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (mirred)");
 | 
				
			||||||
 | 
									goto release;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								list_add_tail(&act->list, &rule->acts.list);
 | 
				
			||||||
 | 
								act = NULL;
 | 
				
			||||||
 | 
								if (fa->id == FLOW_ACTION_REDIRECT)
 | 
				
			||||||
 | 
									break; /* end of the line */
 | 
				
			||||||
 | 
								/* Mirror, so continue on with saved act */
 | 
				
			||||||
 | 
								act = kzalloc(sizeof(*act), GFP_USER);
 | 
				
			||||||
 | 
								if (!act) {
 | 
				
			||||||
 | 
									rc = -ENOMEM;
 | 
				
			||||||
 | 
									goto release;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								*act = save;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								efx_tc_err(efx, "Unhandled action %u\n", fa->id);
 | 
				
			||||||
 | 
								rc = -EOPNOTSUPP;
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
 | 
				
			||||||
 | 
								goto release;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (act) {
 | 
				
			||||||
 | 
							/* Not shot/redirected, so deliver to default dest */
 | 
				
			||||||
 | 
							if (from_efv == EFX_EFV_PF)
 | 
				
			||||||
 | 
								/* Rule applies to traffic from the wire,
 | 
				
			||||||
 | 
								 * and default dest is thus the PF
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								efx_mae_mport_uplink(efx, &act->dest_mport);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								/* Representor, so rule applies to traffic from
 | 
				
			||||||
 | 
								 * representee, and default dest is thus the rep.
 | 
				
			||||||
 | 
								 * All reps use the same mport for delivery
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								efx_mae_mport_mport(efx, efx->tc->reps_mport_id,
 | 
				
			||||||
 | 
										    &act->dest_mport);
 | 
				
			||||||
 | 
							act->deliver = 1;
 | 
				
			||||||
 | 
							rc = efx_mae_alloc_action_set(efx, act);
 | 
				
			||||||
 | 
							if (rc) {
 | 
				
			||||||
 | 
								EFX_TC_ERR_MSG(efx, extack, "Failed to write action set to hw (deliver)");
 | 
				
			||||||
 | 
								goto release;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							list_add_tail(&act->list, &rule->acts.list);
 | 
				
			||||||
 | 
							act = NULL; /* Prevent double-free in error path */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						netif_dbg(efx, drv, efx->net_dev,
 | 
				
			||||||
 | 
							  "Successfully parsed filter (cookie %lx)\n",
 | 
				
			||||||
 | 
							  tc->cookie);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rule->match = match;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = efx_mae_alloc_action_set_list(efx, &rule->acts);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							EFX_TC_ERR_MSG(efx, extack, "Failed to write action set list to hw");
 | 
				
			||||||
 | 
							goto release;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rc = efx_mae_insert_rule(efx, &rule->match, EFX_TC_PRIO_TC,
 | 
				
			||||||
 | 
									 rule->acts.fw_id, &rule->fw_id);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							EFX_TC_ERR_MSG(efx, extack, "Failed to insert rule in hw");
 | 
				
			||||||
 | 
							goto release_acts;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					release_acts:
 | 
				
			||||||
 | 
						efx_mae_free_action_set_list(efx, &rule->acts);
 | 
				
			||||||
 | 
					release:
 | 
				
			||||||
 | 
						/* We failed to insert the rule, so free up any entries we created in
 | 
				
			||||||
 | 
						 * subsidiary tables.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (act)
 | 
				
			||||||
 | 
							efx_tc_free_action_set(efx, act, false);
 | 
				
			||||||
 | 
						if (rule) {
 | 
				
			||||||
 | 
							rhashtable_remove_fast(&efx->tc->match_action_ht,
 | 
				
			||||||
 | 
									       &rule->linkage,
 | 
				
			||||||
 | 
									       efx_tc_match_action_ht_params);
 | 
				
			||||||
 | 
							efx_tc_free_action_set_list(efx, &rule->acts, false);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						kfree(rule);
 | 
				
			||||||
 | 
						return rc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int efx_tc_flower_destroy(struct efx_nic *efx,
 | 
					static int efx_tc_flower_destroy(struct efx_nic *efx,
 | 
				
			||||||
				 struct net_device *net_dev,
 | 
									 struct net_device *net_dev,
 | 
				
			||||||
				 struct flow_cls_offload *tc)
 | 
									 struct flow_cls_offload *tc)
 | 
				
			||||||
| 
						 | 
					@ -151,6 +420,9 @@ int efx_tc_flower(struct efx_nic *efx, struct net_device *net_dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&efx->tc->mutex);
 | 
						mutex_lock(&efx->tc->mutex);
 | 
				
			||||||
	switch (tc->command) {
 | 
						switch (tc->command) {
 | 
				
			||||||
 | 
						case FLOW_CLS_REPLACE:
 | 
				
			||||||
 | 
							rc = efx_tc_flower_replace(efx, net_dev, tc, efv);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	case FLOW_CLS_DESTROY:
 | 
						case FLOW_CLS_DESTROY:
 | 
				
			||||||
		rc = efx_tc_flower_destroy(efx, net_dev, tc);
 | 
							rc = efx_tc_flower_destroy(efx, net_dev, tc);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@ struct efx_tc_action_set {
 | 
				
			||||||
struct efx_tc_match_fields {
 | 
					struct efx_tc_match_fields {
 | 
				
			||||||
	/* L1 */
 | 
						/* L1 */
 | 
				
			||||||
	u32 ingress_port;
 | 
						u32 ingress_port;
 | 
				
			||||||
 | 
						u8 recirc_id;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct efx_tc_match {
 | 
					struct efx_tc_match {
 | 
				
			||||||
| 
						 | 
					@ -64,6 +65,7 @@ struct efx_tc_flow_rule {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum efx_tc_rule_prios {
 | 
					enum efx_tc_rule_prios {
 | 
				
			||||||
 | 
						EFX_TC_PRIO_TC, /* Rule inserted by TC */
 | 
				
			||||||
	EFX_TC_PRIO_DFLT, /* Default switch rule; one of efx_tc_default_rules */
 | 
						EFX_TC_PRIO_DFLT, /* Default switch rule; one of efx_tc_default_rules */
 | 
				
			||||||
	EFX_TC_PRIO__NUM
 | 
						EFX_TC_PRIO__NUM
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue