netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode
@ 2020-05-04 12:43 Vladimir Oltean
  2020-05-04 12:43 ` [RFC 1/6] net: dsa: sja1105: add packing ops for the Retagging Table Vladimir Oltean
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

This series is a draft for supporting as much as possible in terms of
traffic I/O from the network stack with the only dsa_8021q user thus
far, sja1105.

It doesn't support pushing a second VLAN tag to packets that are already
tagged, so our only option is to combine the dsa_8021q with the user tag
into a single tag and decode that on the CPU.

Posting this RFC only to get opinions on whether I'm way too deep down
the rabbit hole or whether this has a chance of getting accepted in
mainline. The assumption is that there is a type of use cases for which
7 VLANs per port are more than sufficient, and that there's another type
of use cases where the full 4096 entries are barely enough. Those use
cases are very different from one another, so I prefer trying to give
both the best experience by creating this best_effort_vlan_filtering
knob to select the mode in which they want to operate in.

Vladimir Oltean (6):
  net: dsa: sja1105: add packing ops for the Retagging Table
  net: dsa: sja1105: make HOSTPRIO a devlink param
  net: dsa: tag_8021q: allow DSA tags and VLAN filtering simultaneously
  net: dsa: tag_8021q: skip disabled ports
  net: dsa: sja1105: support up to 7 VLANs per port using retagging
  docs: net: dsa: sja1105: document the best_effort_vlan_filtering
    option

 .../networking/devlink-params-sja1105.txt     |  33 ++
 Documentation/networking/dsa/sja1105.rst      |  86 +++-
 MAINTAINERS                                   |   1 +
 drivers/net/dsa/sja1105/sja1105.h             |   4 +
 .../net/dsa/sja1105/sja1105_dynamic_config.c  |  33 ++
 drivers/net/dsa/sja1105/sja1105_main.c        | 415 +++++++++++++++++-
 .../net/dsa/sja1105/sja1105_static_config.c   |  62 ++-
 .../net/dsa/sja1105/sja1105_static_config.h   |  15 +
 include/linux/dsa/8021q.h                     |  38 ++
 include/linux/dsa/sja1105.h                   |   4 +
 net/dsa/tag_8021q.c                           | 130 +++++-
 net/dsa/tag_sja1105.c                         |  35 +-
 12 files changed, 818 insertions(+), 38 deletions(-)
 create mode 100644 Documentation/networking/devlink-params-sja1105.txt

-- 
2.17.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC 1/6] net: dsa: sja1105: add packing ops for the Retagging Table
  2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
@ 2020-05-04 12:43 ` Vladimir Oltean
  2020-05-04 12:43 ` [RFC 2/6] net: dsa: sja1105: make HOSTPRIO a devlink param Vladimir Oltean
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

The Retagging Table is an optional feature that allows the switch to
match frames against a {ingress port, egress port, vid} rule and change
their VLAN ID. The retagged frames are by default clones of the original
ones (since the hardware-foreseen use case was to mirror traffic for
debugging purposes and to tag it with a special VLAN for this purpose),
but we can force the original frames to be dropped by removing the
pre-retagging VLAN from the port membership list of the egress port.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 drivers/net/dsa/sja1105/sja1105.h             |  2 +
 .../net/dsa/sja1105/sja1105_dynamic_config.c  | 33 ++++++++++
 .../net/dsa/sja1105/sja1105_static_config.c   | 62 ++++++++++++++++++-
 .../net/dsa/sja1105/sja1105_static_config.h   | 15 +++++
 4 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h
index a12779c9fa19..f925f6a231e2 100644
--- a/drivers/net/dsa/sja1105/sja1105.h
+++ b/drivers/net/dsa/sja1105/sja1105.h
@@ -252,6 +252,8 @@ size_t sja1105et_l2_lookup_entry_packing(void *buf, void *entry_ptr,
 					 enum packing_op op);
 size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
 					 enum packing_op op);
+size_t sja1105_retagging_entry_packing(void *buf, void *entry_ptr,
+				       enum packing_op op);
 size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,
 					    enum packing_op op);
 size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr,
diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c
index bf9b36ff35bf..a1ade782beb1 100644
--- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c
+++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c
@@ -127,6 +127,9 @@
 #define SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD			\
 	(SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY)
 
+#define SJA1105_SIZE_RETAGGING_DYN_CMD				\
+	(SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_RETAGGING_ENTRY)
+
 #define SJA1105_MAX_DYN_CMD_SIZE				\
 	SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD
 
@@ -496,6 +499,20 @@ sja1105pqrs_avb_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
 	sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op);
 }
 
+static void
+sja1105_retagging_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
+			      enum packing_op op)
+{
+	u8 *p = buf + SJA1105_SIZE_RETAGGING_ENTRY;
+	const int size = SJA1105_SIZE_DYN_CMD;
+
+	sja1105_packing(p, &cmd->valid,    31, 31, size, op);
+	sja1105_packing(p, &cmd->errors,   30, 30, size, op);
+	sja1105_packing(p, &cmd->valident, 29, 29, size, op);
+	sja1105_packing(p, &cmd->rdwrset,  28, 28, size, op);
+	sja1105_packing(p, &cmd->index,     5,  0, size, op);
+}
+
 #define OP_READ		BIT(0)
 #define OP_WRITE	BIT(1)
 #define OP_DEL		BIT(2)
@@ -566,6 +583,14 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
 		.packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD,
 		.addr = 0x34,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.entry_packing = sja1105_retagging_entry_packing,
+		.cmd_packing = sja1105_retagging_cmd_packing,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+		.access = (OP_WRITE | OP_DEL),
+		.packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD,
+		.addr = 0x31,
+	},
 	[BLK_IDX_XMII_PARAMS] = {0},
 };
 
@@ -641,6 +666,14 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
 		.packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD,
 		.addr = 0x34,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.entry_packing = sja1105_retagging_entry_packing,
+		.cmd_packing = sja1105_retagging_cmd_packing,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+		.access = (OP_READ | OP_WRITE | OP_DEL),
+		.packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD,
+		.addr = 0x38,
+	},
 	[BLK_IDX_XMII_PARAMS] = {0},
 };
 
diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c
index bbfe034910a0..09125f1c064d 100644
--- a/drivers/net/dsa/sja1105/sja1105_static_config.c
+++ b/drivers/net/dsa/sja1105/sja1105_static_config.c
@@ -463,6 +463,22 @@ static size_t sja1105_xmii_params_entry_packing(void *buf, void *entry_ptr,
 	return size;
 }
 
+size_t sja1105_retagging_entry_packing(void *buf, void *entry_ptr,
+				       enum packing_op op)
+{
+	struct sja1105_retagging_entry *entry = entry_ptr;
+	const size_t size = SJA1105_SIZE_RETAGGING_ENTRY;
+
+	sja1105_packing(buf, &entry->egr_port,       63, 59, size, op);
+	sja1105_packing(buf, &entry->ing_port,       58, 54, size, op);
+	sja1105_packing(buf, &entry->vlan_ing,       53, 42, size, op);
+	sja1105_packing(buf, &entry->vlan_egr,       41, 30, size, op);
+	sja1105_packing(buf, &entry->do_not_learn,   29, 29, size, op);
+	sja1105_packing(buf, &entry->use_dest_ports, 28, 28, size, op);
+	sja1105_packing(buf, &entry->destports,      27, 23, size, op);
+	return size;
+}
+
 size_t sja1105_table_header_packing(void *buf, void *entry_ptr,
 				    enum packing_op op)
 {
@@ -521,6 +537,7 @@ static u64 blk_id_map[BLK_IDX_MAX] = {
 	[BLK_IDX_L2_FORWARDING_PARAMS] = BLKID_L2_FORWARDING_PARAMS,
 	[BLK_IDX_AVB_PARAMS] = BLKID_AVB_PARAMS,
 	[BLK_IDX_GENERAL_PARAMS] = BLKID_GENERAL_PARAMS,
+	[BLK_IDX_RETAGGING] = BLKID_RETAGGING,
 	[BLK_IDX_XMII_PARAMS] = BLKID_XMII_PARAMS,
 };
 
@@ -560,14 +577,19 @@ static sja1105_config_valid_t
 static_config_check_memory_size(const struct sja1105_table *tables)
 {
 	const struct sja1105_l2_forwarding_params_entry *l2_fwd_params;
-	int i, mem = 0;
+	int i, max_mem, mem = 0;
 
 	l2_fwd_params = tables[BLK_IDX_L2_FORWARDING_PARAMS].entries;
 
 	for (i = 0; i < 8; i++)
 		mem += l2_fwd_params->part_spc[i];
 
-	if (mem > SJA1105_MAX_FRAME_MEMORY)
+	if (tables[BLK_IDX_RETAGGING].entry_count)
+		max_mem = SJA1105_MAX_FRAME_MEMORY_RETAGGING;
+	else
+		max_mem = SJA1105_MAX_FRAME_MEMORY;
+
+	if (mem > max_mem)
 		return SJA1105_OVERCOMMITTED_FRAME_MEMORY;
 
 	return SJA1105_CONFIG_OK;
@@ -759,6 +781,12 @@ struct sja1105_table_ops sja1105e_table_ops[BLK_IDX_MAX] = {
 		.packed_entry_size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY,
 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.packing = sja1105_retagging_entry_packing,
+		.unpacked_entry_size = sizeof(struct sja1105_retagging_entry),
+		.packed_entry_size = SJA1105_SIZE_RETAGGING_ENTRY,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+	},
 	[BLK_IDX_XMII_PARAMS] = {
 		.packing = sja1105_xmii_params_entry_packing,
 		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
@@ -847,6 +875,12 @@ struct sja1105_table_ops sja1105t_table_ops[BLK_IDX_MAX] = {
 		.packed_entry_size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY,
 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.packing = sja1105_retagging_entry_packing,
+		.unpacked_entry_size = sizeof(struct sja1105_retagging_entry),
+		.packed_entry_size = SJA1105_SIZE_RETAGGING_ENTRY,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+	},
 	[BLK_IDX_XMII_PARAMS] = {
 		.packing = sja1105_xmii_params_entry_packing,
 		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
@@ -915,6 +949,12 @@ struct sja1105_table_ops sja1105p_table_ops[BLK_IDX_MAX] = {
 		.packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY,
 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.packing = sja1105_retagging_entry_packing,
+		.unpacked_entry_size = sizeof(struct sja1105_retagging_entry),
+		.packed_entry_size = SJA1105_SIZE_RETAGGING_ENTRY,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+	},
 	[BLK_IDX_XMII_PARAMS] = {
 		.packing = sja1105_xmii_params_entry_packing,
 		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
@@ -1003,6 +1043,12 @@ struct sja1105_table_ops sja1105q_table_ops[BLK_IDX_MAX] = {
 		.packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY,
 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.packing = sja1105_retagging_entry_packing,
+		.unpacked_entry_size = sizeof(struct sja1105_retagging_entry),
+		.packed_entry_size = SJA1105_SIZE_RETAGGING_ENTRY,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+	},
 	[BLK_IDX_XMII_PARAMS] = {
 		.packing = sja1105_xmii_params_entry_packing,
 		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
@@ -1071,6 +1117,12 @@ struct sja1105_table_ops sja1105r_table_ops[BLK_IDX_MAX] = {
 		.packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY,
 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.packing = sja1105_retagging_entry_packing,
+		.unpacked_entry_size = sizeof(struct sja1105_retagging_entry),
+		.packed_entry_size = SJA1105_SIZE_RETAGGING_ENTRY,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+	},
 	[BLK_IDX_XMII_PARAMS] = {
 		.packing = sja1105_xmii_params_entry_packing,
 		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
@@ -1159,6 +1211,12 @@ struct sja1105_table_ops sja1105s_table_ops[BLK_IDX_MAX] = {
 		.packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY,
 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
 	},
+	[BLK_IDX_RETAGGING] = {
+		.packing = sja1105_retagging_entry_packing,
+		.unpacked_entry_size = sizeof(struct sja1105_retagging_entry),
+		.packed_entry_size = SJA1105_SIZE_RETAGGING_ENTRY,
+		.max_entry_count = SJA1105_MAX_RETAGGING_COUNT,
+	},
 	[BLK_IDX_XMII_PARAMS] = {
 		.packing = sja1105_xmii_params_entry_packing,
 		.unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry),
diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.h b/drivers/net/dsa/sja1105/sja1105_static_config.h
index 8afafb6aef12..fa7bdd95cfd1 100644
--- a/drivers/net/dsa/sja1105/sja1105_static_config.h
+++ b/drivers/net/dsa/sja1105/sja1105_static_config.h
@@ -17,6 +17,7 @@
 #define SJA1105_SIZE_VLAN_LOOKUP_ENTRY			8
 #define SJA1105_SIZE_L2_FORWARDING_ENTRY		8
 #define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY		12
+#define SJA1105_SIZE_RETAGGING_ENTRY			8
 #define SJA1105_SIZE_XMII_PARAMS_ENTRY			4
 #define SJA1105_SIZE_SCHEDULE_PARAMS_ENTRY		12
 #define SJA1105_SIZE_SCHEDULE_ENTRY_POINTS_PARAMS_ENTRY	4
@@ -46,6 +47,7 @@ enum {
 	BLKID_L2_FORWARDING_PARAMS			= 0x0E,
 	BLKID_AVB_PARAMS				= 0x10,
 	BLKID_GENERAL_PARAMS				= 0x11,
+	BLKID_RETAGGING					= 0x12,
 	BLKID_XMII_PARAMS				= 0x4E,
 };
 
@@ -63,6 +65,7 @@ enum sja1105_blk_idx {
 	BLK_IDX_L2_FORWARDING_PARAMS,
 	BLK_IDX_AVB_PARAMS,
 	BLK_IDX_GENERAL_PARAMS,
+	BLK_IDX_RETAGGING,
 	BLK_IDX_XMII_PARAMS,
 	BLK_IDX_MAX,
 	/* Fake block indices that are only valid for dynamic access */
@@ -83,10 +86,12 @@ enum sja1105_blk_idx {
 #define SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT		1
 #define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT		1
 #define SJA1105_MAX_GENERAL_PARAMS_COUNT		1
+#define SJA1105_MAX_RETAGGING_COUNT			32
 #define SJA1105_MAX_XMII_PARAMS_COUNT			1
 #define SJA1105_MAX_AVB_PARAMS_COUNT			1
 
 #define SJA1105_MAX_FRAME_MEMORY			929
+#define SJA1105_MAX_FRAME_MEMORY_RETAGGING		910
 
 #define SJA1105E_DEVICE_ID				0x9C00000Cull
 #define SJA1105T_DEVICE_ID				0x9E00030Eull
@@ -257,6 +262,16 @@ struct sja1105_mac_config_entry {
 	u64 ingress;
 };
 
+struct sja1105_retagging_entry {
+	u64 egr_port;
+	u64 ing_port;
+	u64 vlan_ing;
+	u64 vlan_egr;
+	u64 do_not_learn;
+	u64 use_dest_ports;
+	u64 destports;
+};
+
 struct sja1105_xmii_params_entry {
 	u64 phy_mac[5];
 	u64 xmii_mode[5];
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 2/6] net: dsa: sja1105: make HOSTPRIO a devlink param
  2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
  2020-05-04 12:43 ` [RFC 1/6] net: dsa: sja1105: add packing ops for the Retagging Table Vladimir Oltean
@ 2020-05-04 12:43 ` Vladimir Oltean
  2020-05-04 12:43 ` [RFC 3/6] net: dsa: tag_8021q: allow DSA tags and VLAN filtering simultaneously Vladimir Oltean
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Unfortunately with this hardware, there is no way to transmit in-band
QoS hints with management frames (i.e. VLAN PCP is ignored). The traffic
class for these is fixed in the static config (which in turn requires a
reset to change).

The switch has 2 MAC filters for link-local management traffic. They are
hardcoded them in the driver to 01-80-C2-xx-xx-xx and 01-1B-19-xx-xx-xx
so that STP and PTP work by default. The switch checks the DMAC of
frames against these masks very early in the packet processing pipeline,
and if they match, they are trapped to the CPU.  In fact, the match is
so early that the analyzer module is bypassed and the frames do not get
classified to a TC based on any QoS classification rules. The hardware
designers recognized that this might be a problem, so they just invented
a knob called HOSTPRIO, which all frames that are trapped to the CPU get
assigned.  On xmit, the MAC filters are active on the CPU port as well.
So the switch wants to trap the link-local frames coming from the CPU
and redirect them to the CPU, which it won't do because it's configured
to avoid hairpinning. So it drops those frames when they come from the
CPU port, due to lack of valid destinations. So the hardware designers
invented another concept called "management routes" which are meant to
bypass the MAC filters (which themselves bypass L2 forwarding). You
pre-program a one-shot "management route" in the switch for a frame
matching a certain DMAC, then you send it, then the switch figures out
it matches this "management route" and properly sends it out the correct
front-panel port. The point is that on xmit, the switch uses HOSTPRIO
for the "management route" frames as well.

With the new ability to add time gates for individual traffic classes,
there is a real danger that the user might unknowingly turn off the
traffic class for PTP, BPDUs, LLDP etc. Also, users might have certain
use cases which require mapping link-local traffic to other TCs than 7.
This will become even more obvious when we add offload for tc-cbs, where
only 2 traffic classes per port support the SR class A and B timing
requirements.

So we need to manage this situation the best we can. There isn't any
knob in Linux for this, so create a driver-specific devlink param which
is a runtime u8. The default value is 7 (the highest priority traffic
class).

Patch is largely inspired by the mv88e6xxx ATU_hash devlink param
implementation.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
Please ignore this patch for now, it is not conceptually part of the
series. I included it because it adds devlink plumbing to sja1105, and I
didn't want to rebase the next patches.

 .../networking/devlink-params-sja1105.txt     |  9 ++
 Documentation/networking/dsa/sja1105.rst      | 19 +++-
 MAINTAINERS                                   |  1 +
 drivers/net/dsa/sja1105/sja1105.h             |  1 +
 drivers/net/dsa/sja1105/sja1105_main.c        | 94 +++++++++++++++++++
 5 files changed, 122 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/networking/devlink-params-sja1105.txt

diff --git a/Documentation/networking/devlink-params-sja1105.txt b/Documentation/networking/devlink-params-sja1105.txt
new file mode 100644
index 000000000000..5096a4cf923c
--- /dev/null
+++ b/Documentation/networking/devlink-params-sja1105.txt
@@ -0,0 +1,9 @@
+hostprio		[DEVICE, DRIVER-SPECIFIC]
+			Configure the traffic class which will be used for
+			management (link-local) traffic injected and trapped
+			to/from the CPU. This includes STP, PTP, LLDP etc, as
+			well as hardware-specific meta frames with RX
+			timestamps.  Higher is better as long as you care about
+			your PTP frames.
+			Configuration mode: runtime
+			Type: u8. 0-7 valid.
diff --git a/Documentation/networking/dsa/sja1105.rst b/Documentation/networking/dsa/sja1105.rst
index 64553d8d91cb..35d0643f1377 100644
--- a/Documentation/networking/dsa/sja1105.rst
+++ b/Documentation/networking/dsa/sja1105.rst
@@ -181,8 +181,23 @@ towards the switch, with the VLAN PCP bits set appropriately.
 Management traffic (having DMAC 01-80-C2-xx-xx-xx or 01-19-1B-xx-xx-xx) is the
 notable exception: the switch always treats it with a fixed priority and
 disregards any VLAN PCP bits even if present. The traffic class for management
-traffic has a value of 7 (highest priority) at the moment, which is not
-configurable in the driver.
+traffic is configurable through a driver-specific devlink param called
+``hostprio``, which by default has a value of 7 (highest priority)::
+
+    devlink dev param show
+    spi/spi0.1:
+      name hostprio type driver-specific
+        values:
+          cmode runtime value 7
+
+    devlink dev param set spi/spi0.1 name hostprio value 5 cmode runtime
+    [  389.903342] sja1105 spi0.1: Reset switch and programmed static config. Reason: Link-local traffic class
+
+    devlink dev param show
+    spi/spi0.1:
+      name hostprio type driver-specific
+        values:
+          cmode runtime value 5
 
 Below is an example of configuring a 500 us cyclic schedule on egress port
 ``swp5``. The traffic class gate for management traffic (7) is open for 100 us,
diff --git a/MAINTAINERS b/MAINTAINERS
index db7a6d462dff..c7c465a12935 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12137,6 +12137,7 @@ M:	Vladimir Oltean <olteanv@gmail.com>
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 F:	drivers/net/dsa/sja1105
+F:	Documentation/networking/devlink-params-sja1105.txt
 
 NXP TDA998X DRM DRIVER
 M:	Russell King <linux@armlinux.org.uk>
diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h
index f925f6a231e2..2a21cab0888c 100644
--- a/drivers/net/dsa/sja1105/sja1105.h
+++ b/drivers/net/dsa/sja1105/sja1105.h
@@ -163,6 +163,7 @@ enum sja1105_reset_reason {
 	SJA1105_AGEING_TIME,
 	SJA1105_SCHEDULING,
 	SJA1105_BEST_EFFORT_POLICING,
+	SJA1105_HOSTPRIO,
 };
 
 int sja1105_static_config_reload(struct sja1105_private *priv,
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index f75ceabb4bf9..8a444e6949fd 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -1591,6 +1591,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_HOSTPRIO] = "Link-local traffic class",
 };
 
 /* For situations where we need to change a setting at runtime that is only
@@ -2020,6 +2021,92 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
 	return 0;
 }
 
+static int sja1105_hostprio_get(struct sja1105_private *priv, u8 *hostprio)
+{
+	struct sja1105_general_params_entry *general_params;
+	struct sja1105_table *table;
+
+	table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
+	general_params = table->entries;
+	*hostprio = general_params->hostprio;
+
+	return 0;
+}
+
+static int sja1105_hostprio_set(struct sja1105_private *priv, u8 hostprio)
+{
+	struct sja1105_general_params_entry *general_params;
+	struct sja1105_table *table;
+
+	if (hostprio >= SJA1105_NUM_TC)
+		return -ERANGE;
+
+	table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
+	general_params = table->entries;
+	general_params->hostprio = hostprio;
+
+	return sja1105_static_config_reload(priv, SJA1105_HOSTPRIO);
+}
+
+enum sja1105_devlink_param_id {
+	SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+	SJA1105_DEVLINK_PARAM_ID_HOSTPRIO,
+};
+
+static int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id,
+				     struct devlink_param_gset_ctx *ctx)
+{
+	struct sja1105_private *priv = ds->priv;
+	int err;
+
+	switch (id) {
+	case SJA1105_DEVLINK_PARAM_ID_HOSTPRIO:
+		err = sja1105_hostprio_get(priv, &ctx->val.vu8);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id,
+				     struct devlink_param_gset_ctx *ctx)
+{
+	struct sja1105_private *priv = ds->priv;
+	int err;
+
+	switch (id) {
+	case SJA1105_DEVLINK_PARAM_ID_HOSTPRIO:
+		err = sja1105_hostprio_set(priv, ctx->val.vu8);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static const struct devlink_param sja1105_devlink_params[] = {
+	DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_HOSTPRIO,
+				 "hostprio", DEVLINK_PARAM_TYPE_U8,
+				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
+};
+
+static int sja1105_setup_devlink_params(struct dsa_switch *ds)
+{
+	return dsa_devlink_params_register(ds, sja1105_devlink_params,
+					   ARRAY_SIZE(sja1105_devlink_params));
+}
+
+static void sja1105_teardown_devlink_params(struct dsa_switch *ds)
+{
+	dsa_devlink_params_unregister(ds, sja1105_devlink_params,
+				      ARRAY_SIZE(sja1105_devlink_params));
+}
+
 /* The programming model for the SJA1105 switch is "all-at-once" via static
  * configuration tables. Some of these can be dynamically modified at runtime,
  * but not the xMII mode parameters table.
@@ -2085,6 +2172,10 @@ static int sja1105_setup(struct dsa_switch *ds)
 
 	ds->mtu_enforcement_ingress = true;
 
+	rc = sja1105_setup_devlink_params(ds);
+	if (rc < 0)
+		return rc;
+
 	/* The DSA/switchdev model brings up switch ports in standalone mode by
 	 * default, and that means vlan_filtering is 0 since they're not under
 	 * a bridge, so it's safe to set up switch tagging at this time.
@@ -2107,6 +2198,7 @@ static void sja1105_teardown(struct dsa_switch *ds)
 			kthread_destroy_worker(sp->xmit_worker);
 	}
 
+	sja1105_teardown_devlink_params(ds);
 	sja1105_flower_teardown(ds);
 	sja1105_tas_teardown(ds);
 	sja1105_ptp_clock_unregister(ds);
@@ -2445,6 +2537,8 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
 	.port_policer_del	= sja1105_port_policer_del,
 	.cls_flower_add		= sja1105_cls_flower_add,
 	.cls_flower_del		= sja1105_cls_flower_del,
+	.devlink_param_get	= sja1105_devlink_param_get,
+	.devlink_param_set	= sja1105_devlink_param_set,
 	.crosschip_bridge_join	= sja1105_crosschip_bridge_join,
 	.crosschip_bridge_leave	= sja1105_crosschip_bridge_leave,
 };
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 3/6] net: dsa: tag_8021q: allow DSA tags and VLAN filtering simultaneously
  2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
  2020-05-04 12:43 ` [RFC 1/6] net: dsa: sja1105: add packing ops for the Retagging Table Vladimir Oltean
  2020-05-04 12:43 ` [RFC 2/6] net: dsa: sja1105: make HOSTPRIO a devlink param Vladimir Oltean
@ 2020-05-04 12:43 ` Vladimir Oltean
  2020-05-04 12:43 ` [RFC 4/6] net: dsa: tag_8021q: skip disabled ports Vladimir Oltean
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

There are very good reasons to want this, but there are also very good
reasons for not enabling it by default. So a devlink param named
best_effort_vlan_filtering, currently driver-specific and exported only
by sja1105, is used to configure this.

In practice, this is perhaps the way that most users are going to use
the switch in. Best-effort untagged traffic can be bridged with any net
device in the system or terminated locally, and VLAN-tagged streams are
forwarded autonomously in a time-sensitive manner according to their
PCP (they need not transit the CPU). For those cases where the CPU needs
to terminate some VLAN-tagged traffic, the next patch will also address
that, via dsa_8021q sub-VLANs.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 Documentation/networking/dsa/sja1105.rst | 21 +++---
 drivers/net/dsa/sja1105/sja1105.h        |  1 +
 drivers/net/dsa/sja1105/sja1105_main.c   | 81 ++++++++++++++++++++++--
 include/linux/dsa/8021q.h                |  7 ++
 include/linux/dsa/sja1105.h              |  2 +
 net/dsa/tag_8021q.c                      | 62 ++++++++++++++++++
 net/dsa/tag_sja1105.c                    | 16 ++---
 7 files changed, 167 insertions(+), 23 deletions(-)

diff --git a/Documentation/networking/dsa/sja1105.rst b/Documentation/networking/dsa/sja1105.rst
index 35d0643f1377..4a8639cba1f3 100644
--- a/Documentation/networking/dsa/sja1105.rst
+++ b/Documentation/networking/dsa/sja1105.rst
@@ -85,15 +85,18 @@ functionality.
 
 The following traffic modes are supported over the switch netdevices:
 
-+--------------------+------------+------------------+------------------+
-|                    | Standalone | Bridged with     | Bridged with     |
-|                    | ports      | vlan_filtering 0 | vlan_filtering 1 |
-+====================+============+==================+==================+
-| Regular traffic    |     Yes    |       Yes        |  No (use master) |
-+--------------------+------------+------------------+------------------+
-| Management traffic |     Yes    |       Yes        |       Yes        |
-| (BPDU, PTP)        |            |                  |                  |
-+--------------------+------------+------------------+------------------+
++-------------+------------+----------------+----------------+----------------------------+
+|             | Standalone |  Bridged with  |  Bridged with  |        Bridged with        |
+|             |    ports   | vlan_filtering | vlan_filtering | best_effort_vlan_filtering |
+|             |            |        0       |        1       |              1             |
++=============+============+================+================+============================+
+|   Regular   |     Yes    |       Yes      |       No       |     Partial (untagged),    |
+|   traffic   |            |                |  (use master)  |    use master for tagged   |
++-------------+------------+----------------+----------------+----------------------------+
+| Management  |     Yes    |       Yes      |      Yes       |             Yes            |
+|  traffic    |            |                |                |                            |
+| (BPDU, PTP) |            |                |                |                            |
++-------------+------------+----------------+----------------+----------------------------+
 
 Switching features
 ==================
diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h
index 2a21cab0888c..8fedcaa99f3b 100644
--- a/drivers/net/dsa/sja1105/sja1105.h
+++ b/drivers/net/dsa/sja1105/sja1105.h
@@ -132,6 +132,7 @@ struct sja1105_private {
 	struct sja1105_static_config static_config;
 	bool rgmii_rx_delay[SJA1105_NUM_PORTS];
 	bool rgmii_tx_delay[SJA1105_NUM_PORTS];
+	bool best_effort_vlan_filtering;
 	const struct sja1105_info *info;
 	struct gpio_desc *reset_gpio;
 	struct spi_device *spidev;
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 8a444e6949fd..edbe5dd4af37 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -1901,10 +1901,27 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port,
 	return DSA_TAG_PROTO_SJA1105;
 }
 
-/* This callback needs to be present */
 static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
 				const struct switchdev_obj_port_vlan *vlan)
 {
+	struct sja1105_private *priv = ds->priv;
+	u16 vid;
+	int rc;
+
+	if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port)) ||
+	    !priv->best_effort_vlan_filtering)
+		return 0;
+
+	/* If the user wants best-effort VLAN filtering (aka vlan_filtering
+	 * bridge plus tagging), be sure to at least deny alterations to the
+	 * configuration done by dsa_8021q.
+	 */
+	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+		rc = dsa_8021q_vid_validate(ds, port, vid, vlan->flags);
+		if (rc < 0)
+			return rc;
+	}
+
 	return 0;
 }
 
@@ -1918,6 +1935,7 @@ 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;
+	bool want_tagging;
 	u16 tpid, tpid2;
 	int rc;
 
@@ -1943,8 +1961,10 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
 	general_params->incl_srcpt1 = enabled;
 	general_params->incl_srcpt0 = enabled;
 
+	want_tagging = priv->best_effort_vlan_filtering || !enabled;
+
 	/* VLAN filtering => independent VLAN learning.
-	 * No VLAN filtering => shared VLAN learning.
+	 * No VLAN filtering (or best effort) => shared VLAN learning.
 	 *
 	 * In shared VLAN learning mode, untagged traffic still gets
 	 * pvid-tagged, and the FDB table gets populated with entries
@@ -1963,7 +1983,7 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
 	 */
 	table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS];
 	l2_lookup_params = table->entries;
-	l2_lookup_params->shared_learn = !enabled;
+	l2_lookup_params->shared_learn = want_tagging;
 
 	rc = sja1105_static_config_reload(priv, SJA1105_VLAN_FILTERING);
 	if (rc)
@@ -1971,11 +1991,24 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
 
 	/* Switch port identification based on 802.1Q is only passable
 	 * if we are not under a vlan_filtering bridge. So make sure
-	 * the two configurations are mutually exclusive.
+	 * the two configurations are mutually exclusive (of course, the
+	 * user may know better, i.e. best_effort_vlan_filtering).
 	 */
-	return sja1105_setup_8021q_tagging(ds, !enabled);
+	return sja1105_setup_8021q_tagging(ds, want_tagging);
 }
 
+bool sja1105_can_use_vlan_as_tags(struct dsa_port *dp)
+{
+	struct dsa_switch *ds = dp->ds;
+	struct sja1105_private *priv = ds->priv;
+
+	if (dsa_port_is_vlan_filtering(dp) && !priv->best_effort_vlan_filtering)
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(sja1105_can_use_vlan_as_tags);
+
 static void sja1105_vlan_add(struct dsa_switch *ds, int port,
 			     const struct switchdev_obj_port_vlan *vlan)
 {
@@ -2048,9 +2081,35 @@ static int sja1105_hostprio_set(struct sja1105_private *priv, u8 hostprio)
 	return sja1105_static_config_reload(priv, SJA1105_HOSTPRIO);
 }
 
+static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv,
+						  bool *be_vlan)
+{
+	*be_vlan = priv->best_effort_vlan_filtering;
+
+	return 0;
+}
+
+static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
+						  bool be_vlan)
+{
+	struct dsa_switch *ds = priv->ds;
+	bool vlan_filtering;
+	int rc;
+
+	vlan_filtering = dsa_port_is_vlan_filtering(dsa_to_port(ds, 0));
+	priv->best_effort_vlan_filtering = be_vlan;
+
+	rtnl_lock();
+	rc = sja1105_vlan_filtering(ds, 0, vlan_filtering);
+	rtnl_unlock();
+
+	return rc;
+}
+
 enum sja1105_devlink_param_id {
 	SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
 	SJA1105_DEVLINK_PARAM_ID_HOSTPRIO,
+	SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING,
 };
 
 static int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id,
@@ -2063,6 +2122,10 @@ static int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id,
 	case SJA1105_DEVLINK_PARAM_ID_HOSTPRIO:
 		err = sja1105_hostprio_get(priv, &ctx->val.vu8);
 		break;
+	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING:
+		err = sja1105_best_effort_vlan_filtering_get(priv,
+							     &ctx->val.vbool);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -2081,6 +2144,10 @@ static int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id,
 	case SJA1105_DEVLINK_PARAM_ID_HOSTPRIO:
 		err = sja1105_hostprio_set(priv, ctx->val.vu8);
 		break;
+	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING:
+		err = sja1105_best_effort_vlan_filtering_set(priv,
+							     ctx->val.vbool);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -2093,6 +2160,10 @@ static const struct devlink_param sja1105_devlink_params[] = {
 	DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_HOSTPRIO,
 				 "hostprio", DEVLINK_PARAM_TYPE_U8,
 				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
+	DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING,
+				 "best_effort_vlan_filtering",
+				 DEVLINK_PARAM_TYPE_BOOL,
+				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
 };
 
 static int sja1105_setup_devlink_params(struct dsa_switch *ds)
diff --git a/include/linux/dsa/8021q.h b/include/linux/dsa/8021q.h
index b8daaec0896e..dfbd5b62f67a 100644
--- a/include/linux/dsa/8021q.h
+++ b/include/linux/dsa/8021q.h
@@ -25,6 +25,8 @@ struct dsa_8021q_crosschip_link {
 int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
 				 bool enabled);
 
+int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags);
+
 int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port,
 				   struct dsa_switch *other_ds,
 				   int other_port, bool enabled);
@@ -58,6 +60,11 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
 	return 0;
 }
 
+int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
+{
+	return 0;
+}
+
 int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port,
 				   struct dsa_switch *other_ds,
 				   int other_port, bool enabled)
diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h
index fa5735c353cd..a609fdbe1355 100644
--- a/include/linux/dsa/sja1105.h
+++ b/include/linux/dsa/sja1105.h
@@ -61,4 +61,6 @@ struct sja1105_port {
 	bool hwts_tx_en;
 };
 
+bool sja1105_can_use_vlan_as_tags(struct dsa_port *dp);
+
 #endif /* _NET_DSA_SJA1105_H */
diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c
index ff9c5bf64bda..158584153e15 100644
--- a/net/dsa/tag_8021q.c
+++ b/net/dsa/tag_8021q.c
@@ -289,6 +289,68 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
 }
 EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging);
 
+int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
+{
+	int upstream = dsa_upstream_port(ds, port);
+	int rx_vid_of = ds->num_ports;
+	int tx_vid_of = ds->num_ports;
+	int other_port;
+
+	/* @vid wants to be a pvid of @port, but is not equal to its rx_vid */
+	if ((flags & BRIDGE_VLAN_INFO_PVID) &&
+	    vid != dsa_8021q_rx_vid(ds, port))
+		return -EPERM;
+
+	for (other_port = 0; other_port < ds->num_ports; other_port++) {
+		if (vid == dsa_8021q_rx_vid(ds, other_port)) {
+			rx_vid_of = other_port;
+			break;
+		}
+		if (vid == dsa_8021q_tx_vid(ds, other_port)) {
+			tx_vid_of = other_port;
+			break;
+		}
+	}
+
+	/* @vid is a TX VLAN of the @tx_vid_of port */
+	if (tx_vid_of != ds->num_ports) {
+		if (tx_vid_of == port) {
+			if (flags != BRIDGE_VLAN_INFO_UNTAGGED)
+				return -EPERM;
+			/* Fall through on proper flags */
+		} else if (port == upstream) {
+			if (flags != 0)
+				return -EPERM;
+			/* Fall through on proper flags */
+		} else {
+			/* Trying to configure on other port */
+			return -EPERM;
+		}
+	}
+
+	/* @vid is an RX VLAN of the @rx_vid_of port */
+	if (rx_vid_of != ds->num_ports) {
+		if (rx_vid_of == port) {
+			if (flags != (BRIDGE_VLAN_INFO_UNTAGGED |
+				      BRIDGE_VLAN_INFO_PVID))
+				return -EPERM;
+			/* Fall through on proper flags */
+		} else if (port == upstream) {
+			if (flags != 0)
+				return -EPERM;
+			/* Fall through on proper flags */
+		} else if (flags != BRIDGE_VLAN_INFO_UNTAGGED) {
+			/* Trying to configure on other port, but with
+			 * invalid flags.
+			 */
+			return -EPERM;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dsa_8021q_vid_validate);
+
 int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port,
 				   struct dsa_switch *other_ds,
 				   int other_port, bool enabled)
diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c
index d553bf36bd41..72d76743c272 100644
--- a/net/dsa/tag_sja1105.c
+++ b/net/dsa/tag_sja1105.c
@@ -74,7 +74,7 @@ static inline bool sja1105_is_meta_frame(const struct sk_buff *skb)
  */
 static bool sja1105_filter(const struct sk_buff *skb, struct net_device *dev)
 {
-	if (!dsa_port_is_vlan_filtering(dev->dsa_ptr))
+	if (sja1105_can_use_vlan_as_tags(skb->dev->dsa_ptr))
 		return true;
 	if (sja1105_is_link_local(skb))
 		return true;
@@ -103,6 +103,7 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
 	u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index);
 	u16 queue_mapping = skb_get_queue_mapping(skb);
 	u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
+	u16 tpid;
 
 	/* Transmitting management traffic does not rely upon switch tagging,
 	 * but instead SPI-installed management routes. Part 2 of this
@@ -111,15 +112,12 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
 	if (unlikely(sja1105_is_link_local(skb)))
 		return sja1105_defer_xmit(dp->priv, skb);
 
-	/* If we are under a vlan_filtering bridge, IP termination on
-	 * switch ports based on 802.1Q tags is simply too brittle to
-	 * be passable. So just defer to the dsa_slave_notag_xmit
-	 * implementation.
-	 */
 	if (dsa_port_is_vlan_filtering(dp))
-		return skb;
+		tpid = ETH_P_8021Q;
+	else
+		tpid = ETH_P_SJA1105;
 
-	return dsa_8021q_xmit(skb, netdev, ETH_P_SJA1105,
+	return dsa_8021q_xmit(skb, netdev, tpid,
 			     ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
 }
 
@@ -258,7 +256,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 
 	hdr = eth_hdr(skb);
 	tpid = ntohs(hdr->h_proto);
-	is_tagged = (tpid == ETH_P_SJA1105);
+	is_tagged = (tpid == ETH_P_SJA1105 || tpid == ETH_P_8021Q);
 	is_link_local = sja1105_is_link_local(skb);
 	is_meta = sja1105_is_meta_frame(skb);
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 4/6] net: dsa: tag_8021q: skip disabled ports
  2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
                   ` (2 preceding siblings ...)
  2020-05-04 12:43 ` [RFC 3/6] net: dsa: tag_8021q: allow DSA tags and VLAN filtering simultaneously Vladimir Oltean
@ 2020-05-04 12:43 ` Vladimir Oltean
  2020-05-04 12:43 ` [RFC 5/6] net: dsa: sja1105: support up to 7 VLANs per port using retagging Vladimir Oltean
  2020-05-04 12:43 ` [RFC 6/6] docs: net: dsa: sja1105: document the best_effort_vlan_filtering option Vladimir Oltean
  5 siblings, 0 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Not only is it not needed to program dsa_8021q VLANs into ports that are
not used, but it also makes validation impossible, given the fact that
port == dsa_upstream_port(ds, port) for a port that is disabled. So when
a user port wants to install its rx_vid into us (we the disabled port),
think that we are in fact the CPU port, so we want the flags to be
egress-tagged (which they aren't). So instead of trying to make
dsa_upstream_port return something more sensible for disabled ports,
skip this nonsense altogether.

Because we didn't have VID validation until now, there's no reason to
treat this patch as a bugfix.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 net/dsa/tag_8021q.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c
index 158584153e15..3958f426d60e 100644
--- a/net/dsa/tag_8021q.c
+++ b/net/dsa/tag_8021q.c
@@ -239,7 +239,7 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
 	for (i = 0; i < ds->num_ports; i++) {
 		u16 flags;
 
-		if (i == upstream)
+		if (!dsa_is_user_port(ds, i))
 			continue;
 		else if (i == port)
 			/* The RX VID is pvid on this port */
@@ -302,6 +302,8 @@ int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
 		return -EPERM;
 
 	for (other_port = 0; other_port < ds->num_ports; other_port++) {
+		if (!dsa_is_user_port(ds, other_port))
+			continue;
 		if (vid == dsa_8021q_rx_vid(ds, other_port)) {
 			rx_vid_of = other_port;
 			break;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 5/6] net: dsa: sja1105: support up to 7 VLANs per port using retagging
  2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
                   ` (3 preceding siblings ...)
  2020-05-04 12:43 ` [RFC 4/6] net: dsa: tag_8021q: skip disabled ports Vladimir Oltean
@ 2020-05-04 12:43 ` Vladimir Oltean
  2020-05-04 12:43 ` [RFC 6/6] docs: net: dsa: sja1105: document the best_effort_vlan_filtering option Vladimir Oltean
  5 siblings, 0 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

For switches that support VLAN retagging, such as sja1105, we extend
dsa_8021q by encoding a "sub-VLAN" into the remaining 3 free bits in the
dsa_8021q tag.

A sub-VLAN is nothing more than a number in the range 0-7, which serves
as an index into a per-port driver lookup table. The sub-VLAN value of
zero means that traffic is untagged (this is also backwards-compatible
with dsa_8021q without retagging).

The switch is configured to retag VLAN-tagged traffic that gets
transmitted towards the CPU port (and towards the CPU only). Example:

bridge vlan add dev sw1p0 vid 100

The switch retags frames received on port 0, going to the CPU, and
having VID 100, to the VID of 1104 0x0450. In dsa_8021q language, 0x0450
means:

 | 11  | 10  |  9  |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
 +-----------+-----+-----------------+-----------+-----------------------+
 |    DIR    | SVL |    SWITCH_ID    |  SUBVLAN  |          PORT         |
 +-----------+-----+-----------------+-----------+-----------------------+

aka:
 - DIR = 0b01: this is an RX VLAN
 - SUBVLAN = 0b001: this is subvlan #1
 - SWITCH_ID = 0b001: this is switch 1 (see the name "sw1p0")
 - PORT = 0b0000: this is port 0 (see the name "sw1p0")

The driver also remembers the "1 -> 100" mapping. In the hotpath, if the
sub-VLAN from the tag encodes a non-untagged frame, this mapping is used
to create a VLAN hwaccel tag, with the value of 100.

There are some performance-related concerns, since all VLAN-retagged
traffic cannot exceed 1Gbps due to the way it is implemented in hardware.
This should not be an issue, because:
- We only support retagging towards the CPU port, which is limited at
  1Gbps anyway.
- VLAN-tagged traffic between ports on the same chip works without
  retagging.
- Untagged traffic, autonomously forwarded as well as terminated
  locally, works without retagging.

On xmit from Linux, transmitting VLAN-tagged traffic is possible by
adding a second VLAN tag with the tx_vid (which encode the destination
port). But this tag needs to be transmitted using a different TPID than
the plain ETH_P_8021Q, because otherwise, the switch thinks we're trying
to do VLAN hopping, it freaks out and drops our frame. By using a TPID
of ETH_P_8021AD, it looks at the S-tag only, which is what we want.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 drivers/net/dsa/sja1105/sja1105_main.c | 244 ++++++++++++++++++++++++-
 include/linux/dsa/8021q.h              |  31 ++++
 include/linux/dsa/sja1105.h            |   2 +
 net/dsa/tag_8021q.c                    |  80 ++++++--
 net/dsa/tag_sja1105.c                  |  21 ++-
 5 files changed, 357 insertions(+), 21 deletions(-)

diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index edbe5dd4af37..106182103b19 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -387,7 +387,8 @@ static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv)
 		/* Disallow dynamic reconfiguration of vlan_pmap */
 		.max_dynp = 0,
 		/* Use a single memory partition for all ingress queues */
-		.part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 },
+		.part_spc = { SJA1105_MAX_FRAME_MEMORY_RETAGGING,
+			      0, 0, 0, 0, 0, 0, 0 },
 	};
 	struct sja1105_table *table;
 
@@ -1733,6 +1734,31 @@ static int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid)
 	return -1;
 }
 
+/* The Retagging Table generates packet *clones* with the new VLAN. This is a
+ * very odd hardware quirk which we need to suppress by dropping the original
+ * packet. We do that by removing the pre-retagging VID from the port
+ * membership of the egress port. For this strategy to be effective, we need a
+ * blacklist to ensure that nobody can add that VID back on the destination
+ * port, otherwise we'll see duplicates (with the old and the new VID).
+ */
+static bool sja1105_vlan_is_blacklisted(struct sja1105_private *priv, int port,
+					u16 vid)
+{
+	struct sja1105_retagging_entry *retagging;
+	struct sja1105_table *table;
+	int i;
+
+	table = &priv->static_config.tables[BLK_IDX_RETAGGING];
+	retagging = table->entries;
+
+	for (i = 0; i < table->entry_count; i++)
+		if ((retagging[i].egr_port & BIT(port)) &&
+		    (retagging[i].vlan_ing == vid))
+			return true;
+
+	return false;
+}
+
 static int sja1105_vlan_apply(struct sja1105_private *priv, int port, u16 vid,
 			      bool enabled, bool untagged)
 {
@@ -1741,6 +1767,9 @@ static int sja1105_vlan_apply(struct sja1105_private *priv, int port, u16 vid,
 	bool keep = true;
 	int match, rc;
 
+	if (enabled && sja1105_vlan_is_blacklisted(priv, port, vid))
+		return 0;
+
 	table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP];
 
 	match = sja1105_is_vlan_configured(priv, vid);
@@ -1793,6 +1822,194 @@ static int sja1105_vlan_apply(struct sja1105_private *priv, int port, u16 vid,
 	return 0;
 }
 
+static int sja1105_find_retagging_entry(struct sja1105_private *priv,
+					int from_port, u16 from_vid,
+					int to_port, u16 to_vid)
+{
+	struct sja1105_retagging_entry *retagging;
+	struct sja1105_table *table;
+	int i;
+
+	table = &priv->static_config.tables[BLK_IDX_RETAGGING];
+	retagging = table->entries;
+
+	for (i = 0; i < table->entry_count; i++)
+		if (retagging[i].ing_port & BIT(from_port) &&
+		    retagging[i].egr_port & BIT(to_port) &&
+		    retagging[i].vlan_ing == from_vid &&
+		    retagging[i].vlan_egr == to_vid)
+			return i;
+
+	return -1;
+}
+
+static int sja1105_setup_retagging_vid(struct sja1105_private *priv,
+				       int from_port, u16 from_vid, int to_port,
+				       u16 to_vid, bool keep, bool untagged)
+{
+	int rc;
+
+	rc = sja1105_vlan_apply(priv, from_port, to_vid, keep, true);
+	if (rc)
+		return rc;
+
+	rc = sja1105_vlan_apply(priv, to_port, to_vid, keep, untagged);
+	if (rc)
+		return rc;
+
+	return sja1105_vlan_apply(priv, to_port, from_vid, false, false);
+}
+
+static int sja1105_retagging_apply(struct sja1105_private *priv, int from_port,
+				   u16 from_vid, int to_port, u16 to_vid,
+				   bool keep, bool untagged)
+{
+	struct sja1105_retagging_entry *retagging;
+	struct sja1105_table *table;
+	int rc, match;
+
+	rc = sja1105_setup_retagging_vid(priv, from_port, from_vid, to_port,
+					 to_vid, keep, untagged);
+	if (rc)
+		return rc;
+
+	table = &priv->static_config.tables[BLK_IDX_RETAGGING];
+
+	match = sja1105_find_retagging_entry(priv, from_port, from_vid,
+					     to_port, to_vid);
+	if (match < 0) {
+		/* Can't delete a missing entry. */
+		if (!keep) {
+			dev_err(priv->ds->dev, "can't delete a missing entry\n");
+			return 0;
+		}
+
+		/* No match => new entry */
+		rc = sja1105_table_resize(table, table->entry_count + 1);
+		if (rc) {
+			dev_err(priv->ds->dev, "failed to resize retagging table: %d\n", rc);
+			return rc;
+		}
+
+		match = table->entry_count - 1;
+	}
+
+	/* Assign pointer after the resize (it may be new memory) */
+	retagging = table->entries;
+
+	if (keep) {
+		retagging[match].egr_port = BIT(to_port);
+		retagging[match].ing_port = BIT(from_port);
+		retagging[match].vlan_ing = from_vid;
+		retagging[match].vlan_egr = to_vid;
+		retagging[match].do_not_learn = false;
+		retagging[match].use_dest_ports = true;
+		retagging[match].destports = BIT(to_port);
+
+		dev_err(priv->ds->dev,
+			"%s: entry %d egr_port 0x%llx ing_port 0x%llx vlan_ing %lld vlan_egr %lld do_not_learn %lld use_dest_ports %lld destports %lld\n",
+			__func__, match, retagging[match].egr_port, retagging[match].ing_port, retagging[match].vlan_ing, retagging[match].vlan_egr,
+			retagging[match].do_not_learn, retagging[match].use_dest_ports, retagging[match].destports);
+		return sja1105_dynamic_config_write(priv, BLK_IDX_RETAGGING,
+						    match, &retagging[match],
+						    true);
+	}
+
+	/* To remove, the strategy is to overwrite the element with
+	 * the last one, and then reduce the array size by 1
+	 */
+	retagging[match] = retagging[table->entry_count - 1];
+
+	rc = sja1105_dynamic_config_write(priv, BLK_IDX_RETAGGING,
+					  table->entry_count - 1,
+					  &retagging[table->entry_count - 1],
+					  false);
+	if (rc)
+		return rc;
+
+	rc = sja1105_dynamic_config_write(priv, BLK_IDX_RETAGGING, match,
+					  &retagging[match], true);
+	if (rc)
+		return rc;
+
+	return sja1105_table_resize(table, table->entry_count - 1);
+}
+
+static int sja1105_find_free_subvlan(struct sja1105_private *priv, int port)
+{
+	struct sja1105_port *sp = &priv->ports[port];
+	int subvlan;
+
+	for (subvlan = 1; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++)
+		if (sp->subvlan_map[subvlan] == VLAN_N_VID)
+			return subvlan;
+
+	return -1;
+}
+
+static int sja1105_find_subvlan(struct sja1105_private *priv, int port, u16 vid)
+{
+	struct sja1105_port *sp = &priv->ports[port];
+	int subvlan;
+
+	for (subvlan = 1; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++)
+		if (sp->subvlan_map[subvlan] == vid)
+			return subvlan;
+
+	return -1;
+}
+
+static int sja1105_subvlan_apply(struct sja1105_private *priv, int port,
+				 u16 vid, bool pvid, bool keep)
+{
+	struct sja1105_port *sp = &priv->ports[port];
+	int cpu = dsa_upstream_port(priv->ds, port);
+	int rc, subvlan;
+	u16 rx_vid;
+
+	/* There are several situations when we don't want to add a subvlan */
+	if (!priv->best_effort_vlan_filtering)
+		return 0;
+	if (vid_is_dsa_8021q(vid))
+		return 0;
+	if (!dsa_is_user_port(priv->ds, port))
+		return 0;
+
+	if (keep) {
+		subvlan = sja1105_find_free_subvlan(priv, port);
+		if (subvlan < 0) {
+			dev_err(priv->ds->dev, "No more free subvlans\n");
+			return -ENOSPC;
+		}
+	} else {
+		subvlan = sja1105_find_subvlan(priv, port, vid);
+		if (subvlan < 0)
+			/* A subvlan may not be found because either we ran out
+			 * (and that's ok, after all, we only support up to 7
+			 * per port), or because the VID was added prior to
+			 * best_effort_vlan_filtering getting toggled. So it's
+			 * perfectly fine, don't do anything.
+			 */
+			return 0;
+	}
+
+	if (pvid)
+		rx_vid = dsa_8021q_rx_vid(priv->ds, port);
+	else
+		rx_vid = dsa_8021q_rx_vid_subvlan(priv->ds, port, subvlan);
+
+	rc = sja1105_retagging_apply(priv, port, vid, cpu, rx_vid, keep, false);
+	if (rc)
+		return rc;
+
+	if (keep)
+		sp->subvlan_map[subvlan] = vid;
+	else
+		sp->subvlan_map[subvlan] = VLAN_N_VID;
+
+	return 0;
+}
+
 static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,
 					 int tree_index, int sw_index,
 					 int other_port, struct net_device *br)
@@ -1918,8 +2135,13 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
 	 */
 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
 		rc = dsa_8021q_vid_validate(ds, port, vid, vlan->flags);
-		if (rc < 0)
-			return rc;
+		/* Suppress the "wrong pvid" error. We can (and will) retag the
+		 * pvid requested by the bridge to the dsa_8021q pvid. Untagged
+		 * traffic is still tagged with the dsa_8021q pvid directly and
+		 * does not require retagging.
+		 */
+		if (rc < 0 && rc != DSA_8021Q_WRONG_PVID)
+			return -EPERM;
 	}
 
 	return 0;
@@ -2017,6 +2239,8 @@ static void sja1105_vlan_add(struct dsa_switch *ds, int port,
 	int rc;
 
 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+		bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+
 		rc = sja1105_vlan_apply(priv, port, vid, true, vlan->flags &
 					BRIDGE_VLAN_INFO_UNTAGGED);
 		if (rc < 0) {
@@ -2024,7 +2248,7 @@ static void sja1105_vlan_add(struct dsa_switch *ds, int port,
 				vid, port, rc);
 			return;
 		}
-		if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+		if (pvid) {
 			rc = sja1105_pvid_apply(ds->priv, port, vid);
 			if (rc < 0) {
 				dev_err(ds->dev, "Failed to set pvid %d on port %d: %d\n",
@@ -2032,6 +2256,9 @@ static void sja1105_vlan_add(struct dsa_switch *ds, int port,
 				return;
 			}
 		}
+		rc = sja1105_subvlan_apply(priv, port, vid, pvid, true);
+		if (rc)
+			return;
 	}
 }
 
@@ -2043,6 +2270,8 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
 	int rc;
 
 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
+		bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+
 		rc = sja1105_vlan_apply(priv, port, vid, false, vlan->flags &
 					BRIDGE_VLAN_INFO_UNTAGGED);
 		if (rc < 0) {
@@ -2050,6 +2279,9 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
 				vid, port, rc);
 			return rc;
 		}
+		rc = sja1105_subvlan_apply(priv, port, vid, pvid, false);
+		if (rc)
+			return rc;
 	}
 	return 0;
 }
@@ -2728,6 +2960,7 @@ static int sja1105_probe(struct spi_device *spi)
 		struct sja1105_port *sp = &priv->ports[port];
 		struct dsa_port *dp = dsa_to_port(ds, port);
 		struct net_device *slave;
+		int subvlan;
 
 		if (!dsa_is_user_port(ds, port))
 			continue;
@@ -2747,6 +2980,9 @@ static int sja1105_probe(struct spi_device *spi)
 			goto out;
 		}
 		skb_queue_head_init(&sp->xmit_queue);
+
+		for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++)
+			sp->subvlan_map[subvlan] = VLAN_N_VID;
 	}
 
 	return 0;
diff --git a/include/linux/dsa/8021q.h b/include/linux/dsa/8021q.h
index dfbd5b62f67a..40d85d3cdf15 100644
--- a/include/linux/dsa/8021q.h
+++ b/include/linux/dsa/8021q.h
@@ -20,6 +20,16 @@ struct dsa_8021q_crosschip_link {
 	refcount_t refcount;
 };
 
+enum dsa_8021q_vid_error {
+	DSA_8021Q_VID_OK = 0,
+	DSA_8021Q_WRONG_PVID = -1,
+	DSA_8021Q_TX_VLAN_WRONG_FLAGS = -2,
+	DSA_8021Q_TX_VLAN_WRONG_PORT = -3,
+	DSA_8021Q_RX_VLAN_WRONG_FLAGS = -4,
+};
+
+#define DSA_8021Q_N_SUBVLAN			8
+
 #if IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q)
 
 int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
@@ -48,10 +58,16 @@ u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port);
 
 u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port);
 
+u16 dsa_8021q_rx_vid_subvlan(struct dsa_switch *ds, int port, u16 subvlan);
+
 int dsa_8021q_rx_switch_id(u16 vid);
 
 int dsa_8021q_rx_source_port(u16 vid);
 
+u16 dsa_8021q_rx_subvlan(u16 vid);
+
+bool vid_is_dsa_8021q(u16 vid);
+
 #else
 
 int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
@@ -104,6 +120,11 @@ u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port)
 	return 0;
 }
 
+u16 dsa_8021q_rx_vid_subvlan(struct dsa_switch *ds, int port, u16 subvlan)
+{
+	return 0;
+}
+
 int dsa_8021q_rx_switch_id(u16 vid)
 {
 	return 0;
@@ -114,6 +135,16 @@ int dsa_8021q_rx_source_port(u16 vid)
 	return 0;
 }
 
+u16 dsa_8021q_rx_subvlan(u16 vid)
+{
+	return 0;
+}
+
+bool vid_is_dsa_8021q(u16 vid)
+{
+	return false;
+}
+
 #endif /* IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q) */
 
 #endif /* _NET_DSA_8021Q_H */
diff --git a/include/linux/dsa/sja1105.h b/include/linux/dsa/sja1105.h
index a609fdbe1355..ef04625087ef 100644
--- a/include/linux/dsa/sja1105.h
+++ b/include/linux/dsa/sja1105.h
@@ -9,6 +9,7 @@
 
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
+#include <linux/dsa/8021q.h>
 #include <net/dsa.h>
 
 #define ETH_P_SJA1105				ETH_P_DSA_8021Q
@@ -53,6 +54,7 @@ struct sja1105_skb_cb {
 	((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb))
 
 struct sja1105_port {
+	u16 subvlan_map[DSA_8021Q_N_SUBVLAN];
 	struct kthread_worker *xmit_worker;
 	struct kthread_work xmit_work;
 	struct sk_buff_head xmit_queue;
diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c
index 3958f426d60e..48d4cb42763f 100644
--- a/net/dsa/tag_8021q.c
+++ b/net/dsa/tag_8021q.c
@@ -17,7 +17,7 @@
  *
  * | 11  | 10  |  9  |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
  * +-----------+-----+-----------------+-----------+-----------------------+
- * |    DIR    | RSV |    SWITCH_ID    |    RSV    |          PORT         |
+ * |    DIR    | SVL |    SWITCH_ID    |  SUBVLAN  |          PORT         |
  * +-----------+-----+-----------------+-----------+-----------------------+
  *
  * DIR - VID[11:10]:
@@ -27,17 +27,24 @@
  *	These values make the special VIDs of 0, 1 and 4095 to be left
  *	unused by this coding scheme.
  *
- * RSV - VID[9]:
- *	To be used for further expansion of SWITCH_ID or for other purposes.
- *	Must be transmitted as zero and ignored on receive.
+ * SVL/SUBVLAN - { VID[9], VID[5:4] }:
+ *	Sub-VLAN encoding. Valid only when DIR indicates an RX VLAN.
+ *	* 0 (0b000): Field does not encode a sub-VLAN, either because
+ *	received traffic is untagged, PVID-tagged or because a second
+ *	VLAN tag is present after this tag and not inside of it.
+ *	* 1 (0b001): Received traffic is tagged with a VID value private
+ *	to the host. This field encodes the index in the host's lookup
+ *	table through which the value of the ingress VLAN ID can be
+ *	recovered.
+ *	* 2 (0b010): Field encodes a sub-VLAN.
+ *	...
+ *	* 7 (0b111): Field encodes a sub-VLAN.
+ *	When DIR indicates a TX VLAN, SUBVLAN must be transmitted as zero
+ *	(by the host) and ignored on receive (by the switch).
  *
  * SWITCH_ID - VID[8:6]:
  *	Index of switch within DSA tree. Must be between 0 and 7.
  *
- * RSV - VID[5:4]:
- *	To be used for further expansion of PORT or for other purposes.
- *	Must be transmitted as zero and ignored on receive.
- *
  * PORT - VID[3:0]:
  *	Index of switch port. Must be between 0 and 15.
  */
@@ -54,6 +61,18 @@
 #define DSA_8021Q_SWITCH_ID(x)		(((x) << DSA_8021Q_SWITCH_ID_SHIFT) & \
 						 DSA_8021Q_SWITCH_ID_MASK)
 
+#define DSA_8021Q_SUBVLAN_HI_SHIFT	9
+#define DSA_8021Q_SUBVLAN_HI_MASK	GENMASK(9, 9)
+#define DSA_8021Q_SUBVLAN_LO_SHIFT	4
+#define DSA_8021Q_SUBVLAN_LO_MASK	GENMASK(4, 3)
+#define DSA_8021Q_SUBVLAN_HI(x)		(((x) & GENMASK(2, 2)) >> 2)
+#define DSA_8021Q_SUBVLAN_LO(x)		((x) & GENMASK(1, 0))
+#define DSA_8021Q_SUBVLAN(x)		\
+		(((DSA_8021Q_SUBVLAN_LO(x) << DSA_8021Q_SUBVLAN_LO_SHIFT) & \
+		  DSA_8021Q_SUBVLAN_LO_MASK) | \
+		 ((DSA_8021Q_SUBVLAN_HI(x) << DSA_8021Q_SUBVLAN_HI_SHIFT) & \
+		  DSA_8021Q_SUBVLAN_HI_MASK))
+
 #define DSA_8021Q_PORT_SHIFT		0
 #define DSA_8021Q_PORT_MASK		GENMASK(3, 0)
 #define DSA_8021Q_PORT(x)		(((x) << DSA_8021Q_PORT_SHIFT) & \
@@ -79,6 +98,13 @@ u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port)
 }
 EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid);
 
+u16 dsa_8021q_rx_vid_subvlan(struct dsa_switch *ds, int port, u16 subvlan)
+{
+	return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(ds->index) |
+	       DSA_8021Q_PORT(port) | DSA_8021Q_SUBVLAN(subvlan);
+}
+EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid_subvlan);
+
 /* Returns the decoded switch ID from the RX VID. */
 int dsa_8021q_rx_switch_id(u16 vid)
 {
@@ -93,6 +119,27 @@ int dsa_8021q_rx_source_port(u16 vid)
 }
 EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port);
 
+/* Returns the decoded subvlan from the RX VID. */
+u16 dsa_8021q_rx_subvlan(u16 vid)
+{
+	u16 svl_hi, svl_lo;
+
+	svl_hi = (vid & DSA_8021Q_SUBVLAN_HI_MASK) >>
+		 DSA_8021Q_SUBVLAN_HI_SHIFT;
+	svl_lo = (vid & DSA_8021Q_SUBVLAN_LO_MASK) >>
+		 DSA_8021Q_SUBVLAN_LO_SHIFT;
+
+	return (svl_hi << 2) | svl_lo;
+}
+EXPORT_SYMBOL_GPL(dsa_8021q_rx_subvlan);
+
+bool vid_is_dsa_8021q(u16 vid)
+{
+	return ((vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_RX ||
+		(vid & DSA_8021Q_DIR_MASK) == DSA_8021Q_DIR_TX);
+}
+EXPORT_SYMBOL_GPL(vid_is_dsa_8021q);
+
 static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port)
 {
 	struct bridge_vlan_info vinfo;
@@ -289,7 +336,8 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
 }
 EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging);
 
-int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
+enum dsa_8021q_vid_error dsa_8021q_vid_validate(struct dsa_switch *ds, int port,
+						u16 vid, u16 flags)
 {
 	int upstream = dsa_upstream_port(ds, port);
 	int rx_vid_of = ds->num_ports;
@@ -299,7 +347,7 @@ int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
 	/* @vid wants to be a pvid of @port, but is not equal to its rx_vid */
 	if ((flags & BRIDGE_VLAN_INFO_PVID) &&
 	    vid != dsa_8021q_rx_vid(ds, port))
-		return -EPERM;
+		return DSA_8021Q_WRONG_PVID;
 
 	for (other_port = 0; other_port < ds->num_ports; other_port++) {
 		if (!dsa_is_user_port(ds, other_port))
@@ -318,15 +366,15 @@ int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
 	if (tx_vid_of != ds->num_ports) {
 		if (tx_vid_of == port) {
 			if (flags != BRIDGE_VLAN_INFO_UNTAGGED)
-				return -EPERM;
+				return DSA_8021Q_TX_VLAN_WRONG_FLAGS;
 			/* Fall through on proper flags */
 		} else if (port == upstream) {
 			if (flags != 0)
-				return -EPERM;
+				return DSA_8021Q_TX_VLAN_WRONG_FLAGS;
 			/* Fall through on proper flags */
 		} else {
 			/* Trying to configure on other port */
-			return -EPERM;
+			return DSA_8021Q_TX_VLAN_WRONG_PORT;
 		}
 	}
 
@@ -335,17 +383,17 @@ int dsa_8021q_vid_validate(struct dsa_switch *ds, int port, u16 vid, u16 flags)
 		if (rx_vid_of == port) {
 			if (flags != (BRIDGE_VLAN_INFO_UNTAGGED |
 				      BRIDGE_VLAN_INFO_PVID))
-				return -EPERM;
+				return DSA_8021Q_RX_VLAN_WRONG_FLAGS;
 			/* Fall through on proper flags */
 		} else if (port == upstream) {
 			if (flags != 0)
-				return -EPERM;
+				return DSA_8021Q_RX_VLAN_WRONG_FLAGS;
 			/* Fall through on proper flags */
 		} else if (flags != BRIDGE_VLAN_INFO_UNTAGGED) {
 			/* Trying to configure on other port, but with
 			 * invalid flags.
 			 */
-			return -EPERM;
+			return DSA_8021Q_RX_VLAN_WRONG_FLAGS;
 		}
 	}
 
diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c
index 72d76743c272..b47c38cd5fcc 100644
--- a/net/dsa/tag_sja1105.c
+++ b/net/dsa/tag_sja1105.c
@@ -113,7 +113,7 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
 		return sja1105_defer_xmit(dp->priv, skb);
 
 	if (dsa_port_is_vlan_filtering(dp))
-		tpid = ETH_P_8021Q;
+		tpid = ETH_P_8021AD;
 	else
 		tpid = ETH_P_SJA1105;
 
@@ -242,6 +242,20 @@ static struct sk_buff
 	return skb;
 }
 
+static void sja1105_decode_subvlan(struct sk_buff *skb, u16 subvlan)
+{
+	struct dsa_port *dp = dsa_slave_to_port(skb->dev);
+	struct sja1105_port *sp = dp->priv;
+	u16 vid = sp->subvlan_map[subvlan];
+	u16 vlan_tci;
+
+	if (vid == VLAN_N_VID)
+		return;
+
+	vlan_tci = (skb->priority << VLAN_PRIO_SHIFT) | vid;
+	__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
+}
+
 static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 				   struct net_device *netdev,
 				   struct packet_type *pt)
@@ -251,6 +265,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 	struct ethhdr *hdr;
 	u16 tpid, vid, tci;
 	bool is_link_local;
+	u16 subvlan = 0;
 	bool is_tagged;
 	bool is_meta;
 
@@ -274,6 +289,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 		source_port = dsa_8021q_rx_source_port(vid);
 		switch_id = dsa_8021q_rx_switch_id(vid);
 		skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+		subvlan = dsa_8021q_rx_subvlan(vid);
 	} else if (is_link_local) {
 		/* Management traffic path. Switch embeds the switch ID and
 		 * port ID into bytes of the destination MAC, courtesy of
@@ -298,6 +314,9 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 		return NULL;
 	}
 
+	if (subvlan)
+		sja1105_decode_subvlan(skb, subvlan);
+
 	return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local,
 					      is_meta);
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 6/6] docs: net: dsa: sja1105: document the best_effort_vlan_filtering option
  2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
                   ` (4 preceding siblings ...)
  2020-05-04 12:43 ` [RFC 5/6] net: dsa: sja1105: support up to 7 VLANs per port using retagging Vladimir Oltean
@ 2020-05-04 12:43 ` Vladimir Oltean
  5 siblings, 0 replies; 7+ messages in thread
From: Vladimir Oltean @ 2020-05-04 12:43 UTC (permalink / raw)
  To: andrew, f.fainelli, vivien.didelot
  Cc: davem, jiri, idosch, kuba, netdev, nikolay, roopa, georg.waibel,
	o.rempel, christian.herber

From: Vladimir Oltean <vladimir.oltean@nxp.com>

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 .../networking/devlink-params-sja1105.txt     | 24 ++++++++++
 Documentation/networking/dsa/sja1105.rst      | 46 +++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/Documentation/networking/devlink-params-sja1105.txt b/Documentation/networking/devlink-params-sja1105.txt
index 5096a4cf923c..576dcc6e2d96 100644
--- a/Documentation/networking/devlink-params-sja1105.txt
+++ b/Documentation/networking/devlink-params-sja1105.txt
@@ -7,3 +7,27 @@ hostprio		[DEVICE, DRIVER-SPECIFIC]
 			your PTP frames.
 			Configuration mode: runtime
 			Type: u8. 0-7 valid.
+
+best_effort_vlan_filtering
+			[DEVICE, DRIVER-SPECIFIC]
+			Allow plain ETH_P_8021Q headers to be used as DSA tags.
+			Benefits:
+			- Can terminate untagged traffic over switch net
+			  devices even when enslaved to a bridge with
+			  vlan_filtering=1.
+			- Can terminate VLAN-tagged traffic over switch net
+			  devices even when enslaved to a bridge with
+			  vlan_filtering=1, with some constraints (no more than
+			  7 VLANs per user port).
+			- Can do QoS based on VLAN PCP and VLAN membership
+			  admission control for autonomously forwarded frames
+			  (regardless of whether they can be terminated on the
+			  CPU or not).
+			Drawbacks:
+			- User cannot use VLANs in range 1024-3071. If the
+			  switch receives frames with such VIDs, it will
+			  misinterpret them as DSA tags.
+			- Switch uses Shared VLAN Learning (FDB lookup uses
+			  only DMAC as key).
+			Configuration mode: runtime
+			Type: bool.
diff --git a/Documentation/networking/dsa/sja1105.rst b/Documentation/networking/dsa/sja1105.rst
index 4a8639cba1f3..d963ff2ac1c9 100644
--- a/Documentation/networking/dsa/sja1105.rst
+++ b/Documentation/networking/dsa/sja1105.rst
@@ -77,6 +77,52 @@ change.
 The TPID is restored when ``vlan_filtering`` is requested by the user through
 the bridge layer, and general IP termination becomes no longer possible through
 the switch netdevices in this mode.
+There exists a third configuration option, via ``best_effort_vlan_filtering``.
+This permits termination of some traffic on switch net devices, at the expense
+of losing some VLAN filtering abilities: reduced range of usable VIDs and
+shared VLAN learning.
+The frames which can be terminated on the CPU in this mode are:
+- All untagged frames
+- VLAN-tagged frames, up to 7 different VLANs per user port
+This operating mode is slightly insane to be collated with the default
+``vlan_filtering``, so it is an opt-in that needs to be enabled using a devlink
+parameter. To enable it::
+
+  ip link set dev br0 type bridge vlan_filtering 1
+  [   61.204770] sja1105 spi0.1: Reset switch and programmed static config. Reason: VLAN filtering
+  [   61.239944] sja1105 spi0.1: Disabled switch tagging
+  devlink dev param set spi/spi0.1 name best_effort_vlan_filtering value true cmode runtime
+  [   64.682927] sja1105 spi0.1: Reset switch and programmed static config. Reason: VLAN filtering
+  [   64.711925] sja1105 spi0.1: Enabled switch tagging
+  bridge vlan add dev swp2 vid 1025 untagged pvid
+  RTNETLINK answers: Operation not permitted
+  bridge vlan add dev swp2 vid 100
+  bridge vlan add dev swp2 vid 101 untagged
+  bridge vlan
+  port    vlan ids
+  swp5     1 PVID Egress Untagged
+
+  swp2     1 PVID Egress Untagged
+           100
+           101 Egress Untagged
+
+  swp3     1 PVID Egress Untagged
+
+  swp4     1 PVID Egress Untagged
+
+  br0      1 PVID Egress Untagged
+  bridge vlan add dev swp2 vid 102
+  bridge vlan add dev swp2 vid 103
+  bridge vlan add dev swp2 vid 104
+  bridge vlan add dev swp2 vid 105
+  bridge vlan add dev swp2 vid 106
+  bridge vlan add dev swp2 vid 107
+  [ 3885.216832] sja1105 spi0.1: No more free subvlans
+
+The "No more free subvlans" warning message means that once the capacity is
+exceeded, frames tagged with newly added VLANs (in this case 107) are not able
+to be terminated on the CPU. They are still accepted and forwarded
+autonomously.
 
 The switches have two programmable filters for link-local destination MACs.
 These are used to trap BPDUs and PTP traffic to the master netdevice, and are
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-05-04 12:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-05-04 12:43 [RFC 0/6] Traffic support for dsa_8021q in vlan_filtering=1 mode Vladimir Oltean
2020-05-04 12:43 ` [RFC 1/6] net: dsa: sja1105: add packing ops for the Retagging Table Vladimir Oltean
2020-05-04 12:43 ` [RFC 2/6] net: dsa: sja1105: make HOSTPRIO a devlink param Vladimir Oltean
2020-05-04 12:43 ` [RFC 3/6] net: dsa: tag_8021q: allow DSA tags and VLAN filtering simultaneously Vladimir Oltean
2020-05-04 12:43 ` [RFC 4/6] net: dsa: tag_8021q: skip disabled ports Vladimir Oltean
2020-05-04 12:43 ` [RFC 5/6] net: dsa: sja1105: support up to 7 VLANs per port using retagging Vladimir Oltean
2020-05-04 12:43 ` [RFC 6/6] docs: net: dsa: sja1105: document the best_effort_vlan_filtering option Vladimir Oltean

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).