Commit dfacc5a2 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller
Browse files

net: dsa: sja1105: support flow-based redirection via virtual links



Implement tc-flower offloads for redirect, trap and drop using
non-critical virtual links.

Commands which were tested to work are:

  # Send frames received on swp2 with a DA of 42:be:24:9b:76:20 to the
  # CPU and to swp3. This type of key (DA only) when the port's VLAN
  # awareness state is off.
  tc qdisc add dev swp2 clsact
  tc filter add dev swp2 ingress flower skip_sw dst_mac 42:be:24:9b:76:20 \
          action mirred egress redirect dev swp3 \
          action trap

  # Drop frames received on swp2 with a DA of 42:be:24:9b:76:20, a VID
  # of 100 and a PCP of 0.
  tc filter add dev swp2 ingress protocol 802.1Q flower skip_sw \
          dst_mac 42:be:24:9b:76:20 vlan_id 100 vlan_prio 0 action drop

Under the hood, all rules match on DMAC, VID and PCP, but when VLAN
filtering is disabled, those are set internally by the driver to the
port-based defaults. Because we would be put in an awkward situation if
the user were to change the VLAN filtering state while there are active
rules (packets would no longer match on the specified keys), we simply
deny changing vlan_filtering unless the list of flows offloaded via
virtual links is empty. Then the user can re-add new rules.

Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b70bb8d4
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -34,3 +34,12 @@ config NET_DSA_SJA1105_TAS
	  This enables support for the TTEthernet-based egress scheduling
	  engine in the SJA1105 DSA driver, which is controlled using a
	  hardware offload of the tc-tqprio qdisc.

config NET_DSA_SJA1105_VL
	bool "Support for Virtual Links on NXP SJA1105"
	depends on NET_DSA_SJA1105_TAS
	help
	  This enables support for flow classification using capable devices
	  (SJA1105T, SJA1105Q, SJA1105S). The following actions are supported:
	  - redirect, trap, drop
	  - time-based ingress policing, via the tc-gate action
+4 −0
Original line number Diff line number Diff line
@@ -17,3 +17,7 @@ endif
ifdef CONFIG_NET_DSA_SJA1105_TAS
sja1105-objs += sja1105_tas.o
endif

ifdef CONFIG_NET_DSA_SJA1105_VL
sja1105-objs += sja1105_vl.o
endif
+18 −0
Original line number Diff line number Diff line
@@ -126,6 +126,13 @@ struct sja1105_key {
enum sja1105_rule_type {
	SJA1105_RULE_BCAST_POLICER,
	SJA1105_RULE_TC_POLICER,
	SJA1105_RULE_VL,
};

enum sja1105_vl_type {
	SJA1105_VL_NONCRITICAL,
	SJA1105_VL_RATE_CONSTRAINED,
	SJA1105_VL_TIME_TRIGGERED,
};

struct sja1105_rule {
@@ -135,6 +142,7 @@ struct sja1105_rule {
	struct sja1105_key key;
	enum sja1105_rule_type type;

	/* Action */
	union {
		/* SJA1105_RULE_BCAST_POLICER */
		struct {
@@ -145,12 +153,19 @@ struct sja1105_rule {
		struct {
			int sharindx;
		} tc_pol;

		/* SJA1105_RULE_VL */
		struct {
			unsigned long destports;
			enum sja1105_vl_type type;
		} vl;
	};
};

struct sja1105_flow_block {
	struct list_head rules;
	bool l2_policer_used[SJA1105_NUM_L2_POLICERS];
	int num_virtual_links;
};

struct sja1105_private {
@@ -187,6 +202,7 @@ enum sja1105_reset_reason {
	SJA1105_AGEING_TIME,
	SJA1105_SCHEDULING,
	SJA1105_BEST_EFFORT_POLICING,
	SJA1105_VIRTUAL_LINKS,
};

int sja1105_static_config_reload(struct sja1105_private *priv,
@@ -290,5 +306,7 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port,
			   struct flow_cls_offload *cls, bool ingress);
void sja1105_flower_setup(struct dsa_switch *ds);
void sja1105_flower_teardown(struct dsa_switch *ds);
struct sja1105_rule *sja1105_rule_find(struct sja1105_private *priv,
				       unsigned long cookie);

#endif
+52 −5
Original line number Diff line number Diff line
@@ -2,8 +2,9 @@
/* Copyright 2020, NXP Semiconductors
 */
#include "sja1105.h"
#include "sja1105_vl.h"

static struct sja1105_rule *sja1105_rule_find(struct sja1105_private *priv,
struct sja1105_rule *sja1105_rule_find(struct sja1105_private *priv,
				       unsigned long cookie)
{
	struct sja1105_rule *rule;
@@ -173,7 +174,8 @@ out:

static int sja1105_flower_policer(struct sja1105_private *priv, int port,
				  struct netlink_ext_ack *extack,
				  unsigned long cookie, struct sja1105_key *key,
				  unsigned long cookie,
				  struct sja1105_key *key,
				  u64 rate_bytes_per_sec,
				  s64 burst)
{
@@ -308,6 +310,7 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port,
	const struct flow_action_entry *act;
	unsigned long cookie = cls->cookie;
	struct sja1105_key key;
	bool vl_rule = false;
	int rc, i;

	rc = sja1105_flower_parse_key(priv, extack, cls, &key);
@@ -319,13 +322,50 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port,
	flow_action_for_each(i, act, &rule->action) {
		switch (act->id) {
		case FLOW_ACTION_POLICE:
			rc = sja1105_flower_policer(priv, port,
						    extack, cookie, &key,
			rc = sja1105_flower_policer(priv, port, extack, cookie,
						    &key,
						    act->police.rate_bytes_ps,
						    act->police.burst);
			if (rc)
				goto out;
			break;
		case FLOW_ACTION_TRAP: {
			int cpu = dsa_upstream_port(ds, port);

			vl_rule = true;

			rc = sja1105_vl_redirect(priv, port, extack, cookie,
						 &key, BIT(cpu), true);
			if (rc)
				goto out;
			break;
		}
		case FLOW_ACTION_REDIRECT: {
			struct dsa_port *to_dp;

			to_dp = dsa_port_from_netdev(act->dev);
			if (IS_ERR(to_dp)) {
				NL_SET_ERR_MSG_MOD(extack,
						   "Destination not a switch port");
				return -EOPNOTSUPP;
			}

			vl_rule = true;

			rc = sja1105_vl_redirect(priv, port, extack, cookie,
						 &key, BIT(to_dp->index), true);
			if (rc)
				goto out;
			break;
		}
		case FLOW_ACTION_DROP:
			vl_rule = true;

			rc = sja1105_vl_redirect(priv, port, extack, cookie,
						 &key, 0, false);
			if (rc)
				goto out;
			break;
		default:
			NL_SET_ERR_MSG_MOD(extack,
					   "Action not supported");
@@ -333,6 +373,10 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port,
			goto out;
		}
	}

	if (vl_rule && !rc)
		rc = sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS);

out:
	return rc;
}
@@ -348,6 +392,9 @@ int sja1105_cls_flower_del(struct dsa_switch *ds, int port,
	if (!rule)
		return 0;

	if (rule->type == SJA1105_RULE_VL)
		return sja1105_vl_delete(priv, port, rule, cls->common.extack);

	policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries;

	if (rule->type == SJA1105_RULE_BCAST_POLICER) {
+11 −1
Original line number Diff line number Diff line
@@ -445,7 +445,7 @@ static int sja1105_init_general_params(struct sja1105_private *priv)
		 */
		.casc_port = SJA1105_NUM_PORTS,
		/* No TTEthernet */
		.vllupformat = 0,
		.vllupformat = SJA1105_VL_FORMAT_PSFP,
		.vlmarker = 0,
		.vlmask = 0,
		/* Only update correctionField for 1-step PTP (L2 transport) */
@@ -1589,6 +1589,7 @@ static const char * const sja1105_reset_reasons[] = {
	[SJA1105_AGEING_TIME] = "Ageing time",
	[SJA1105_SCHEDULING] = "Time-aware scheduling",
	[SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing",
	[SJA1105_VIRTUAL_LINKS] = "Virtual links",
};

/* For situations where we need to change a setting at runtime that is only
@@ -1831,9 +1832,18 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
	struct sja1105_general_params_entry *general_params;
	struct sja1105_private *priv = ds->priv;
	struct sja1105_table *table;
	struct sja1105_rule *rule;
	u16 tpid, tpid2;
	int rc;

	list_for_each_entry(rule, &priv->flow_block.rules, list) {
		if (rule->type == SJA1105_RULE_VL) {
			dev_err(ds->dev,
				"Cannot change VLAN filtering state while VL rules are active\n");
			return -EBUSY;
		}
	}

	if (enabled) {
		/* Enable VLAN filtering. */
		tpid  = ETH_P_8021Q;
Loading