Skip to content

Commit 227d07a

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: sja1105: Add support for traffic through standalone ports
In order to support this, we are creating a make-shift switch tag out of a VLAN trunk configured on the CPU port. Termination of normal traffic on switch ports only works when not under a vlan_filtering bridge. Termination of management (PTP, BPDU) traffic works under all circumstances because it uses a different tagging mechanism (incl_srcpt). We are making use of the generic CONFIG_NET_DSA_TAG_8021Q code and leveraging it from our own CONFIG_NET_DSA_TAG_SJA1105. There are two types of traffic: regular and link-local. The link-local traffic received on the CPU port is trapped from the switch's regular forwarding decisions because it matched one of the two DMAC filters for management traffic. On transmission, the switch requires special massaging for these link-local frames. Due to a weird implementation of the switching IP, by default it drops link-local frames that originate on the CPU port. It needs to be told where to forward them to, through an SPI command ("management route") that is valid for only a single frame. So when we're sending link-local traffic, we are using the dsa_defer_xmit mechanism. Signed-off-by: Vladimir Oltean <[email protected]> Reviewed-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent c362beb commit 227d07a

File tree

8 files changed

+308
-15
lines changed

8 files changed

+308
-15
lines changed

drivers/net/dsa/sja1105/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
config NET_DSA_SJA1105
22
tristate "NXP SJA1105 Ethernet switch family support"
33
depends on NET_DSA && SPI
4+
select NET_DSA_TAG_SJA1105
45
select PACKING
56
select CRC32
67
help

drivers/net/dsa/sja1105/sja1105.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <linux/dsa/sja1105.h>
99
#include <net/dsa.h>
10+
#include <linux/mutex.h>
1011
#include "sja1105_static_config.h"
1112

1213
#define SJA1105_NUM_PORTS 5
@@ -65,6 +66,11 @@ struct sja1105_private {
6566
struct gpio_desc *reset_gpio;
6667
struct spi_device *spidev;
6768
struct dsa_switch *ds;
69+
struct sja1105_port ports[SJA1105_NUM_PORTS];
70+
/* Serializes transmission of management frames so that
71+
* the switch doesn't confuse them with one another.
72+
*/
73+
struct mutex mgmt_lock;
6874
};
6975

7076
#include "sja1105_dynamic_config.h"

drivers/net/dsa/sja1105/sja1105_main.c

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/netdevice.h>
2121
#include <linux/if_bridge.h>
2222
#include <linux/if_ether.h>
23+
#include <linux/dsa/8021q.h>
2324
#include "sja1105.h"
2425

2526
static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len,
@@ -406,11 +407,14 @@ static int sja1105_init_general_params(struct sja1105_private *priv)
406407
.tpid2 = ETH_P_SJA1105,
407408
};
408409
struct sja1105_table *table;
409-
int i;
410+
int i, k = 0;
410411

411-
for (i = 0; i < SJA1105_NUM_PORTS; i++)
412+
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
412413
if (dsa_is_dsa_port(priv->ds, i))
413414
default_general_params.casc_port = i;
415+
else if (dsa_is_user_port(priv->ds, i))
416+
priv->ports[i].mgmt_slot = k++;
417+
}
414418

415419
table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
416420

@@ -1146,10 +1150,27 @@ static int sja1105_vlan_apply(struct sja1105_private *priv, int port, u16 vid,
11461150
return 0;
11471151
}
11481152

1153+
static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)
1154+
{
1155+
int rc, i;
1156+
1157+
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
1158+
rc = dsa_port_setup_8021q_tagging(ds, i, enabled);
1159+
if (rc < 0) {
1160+
dev_err(ds->dev, "Failed to setup VLAN tagging for port %d: %d\n",
1161+
i, rc);
1162+
return rc;
1163+
}
1164+
}
1165+
dev_info(ds->dev, "%s switch tagging\n",
1166+
enabled ? "Enabled" : "Disabled");
1167+
return 0;
1168+
}
1169+
11491170
static enum dsa_tag_protocol
11501171
sja1105_get_tag_protocol(struct dsa_switch *ds, int port)
11511172
{
1152-
return DSA_TAG_PROTO_NONE;
1173+
return DSA_TAG_PROTO_SJA1105;
11531174
}
11541175

11551176
/* This callback needs to be present */
@@ -1173,7 +1194,11 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
11731194
if (rc)
11741195
dev_err(ds->dev, "Failed to change VLAN Ethertype\n");
11751196

1176-
return rc;
1197+
/* Switch port identification based on 802.1Q is only passable
1198+
* if we are not under a vlan_filtering bridge. So make sure
1199+
* the two configurations are mutually exclusive.
1200+
*/
1201+
return sja1105_setup_8021q_tagging(ds, !enabled);
11771202
}
11781203

11791204
static void sja1105_vlan_add(struct dsa_switch *ds, int port,
@@ -1276,7 +1301,98 @@ static int sja1105_setup(struct dsa_switch *ds)
12761301
*/
12771302
ds->vlan_filtering_is_global = true;
12781303

1279-
return 0;
1304+
/* The DSA/switchdev model brings up switch ports in standalone mode by
1305+
* default, and that means vlan_filtering is 0 since they're not under
1306+
* a bridge, so it's safe to set up switch tagging at this time.
1307+
*/
1308+
return sja1105_setup_8021q_tagging(ds, true);
1309+
}
1310+
1311+
static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
1312+
struct sk_buff *skb)
1313+
{
1314+
struct sja1105_mgmt_entry mgmt_route = {0};
1315+
struct sja1105_private *priv = ds->priv;
1316+
struct ethhdr *hdr;
1317+
int timeout = 10;
1318+
int rc;
1319+
1320+
hdr = eth_hdr(skb);
1321+
1322+
mgmt_route.macaddr = ether_addr_to_u64(hdr->h_dest);
1323+
mgmt_route.destports = BIT(port);
1324+
mgmt_route.enfport = 1;
1325+
1326+
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE,
1327+
slot, &mgmt_route, true);
1328+
if (rc < 0) {
1329+
kfree_skb(skb);
1330+
return rc;
1331+
}
1332+
1333+
/* Transfer skb to the host port. */
1334+
dsa_enqueue_skb(skb, ds->ports[port].slave);
1335+
1336+
/* Wait until the switch has processed the frame */
1337+
do {
1338+
rc = sja1105_dynamic_config_read(priv, BLK_IDX_MGMT_ROUTE,
1339+
slot, &mgmt_route);
1340+
if (rc < 0) {
1341+
dev_err_ratelimited(priv->ds->dev,
1342+
"failed to poll for mgmt route\n");
1343+
continue;
1344+
}
1345+
1346+
/* UM10944: The ENFPORT flag of the respective entry is
1347+
* cleared when a match is found. The host can use this
1348+
* flag as an acknowledgment.
1349+
*/
1350+
cpu_relax();
1351+
} while (mgmt_route.enfport && --timeout);
1352+
1353+
if (!timeout) {
1354+
/* Clean up the management route so that a follow-up
1355+
* frame may not match on it by mistake.
1356+
*/
1357+
sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE,
1358+
slot, &mgmt_route, false);
1359+
dev_err_ratelimited(priv->ds->dev, "xmit timed out\n");
1360+
}
1361+
1362+
return NETDEV_TX_OK;
1363+
}
1364+
1365+
/* Deferred work is unfortunately necessary because setting up the management
1366+
* route cannot be done from atomit context (SPI transfer takes a sleepable
1367+
* lock on the bus)
1368+
*/
1369+
static netdev_tx_t sja1105_port_deferred_xmit(struct dsa_switch *ds, int port,
1370+
struct sk_buff *skb)
1371+
{
1372+
struct sja1105_private *priv = ds->priv;
1373+
struct sja1105_port *sp = &priv->ports[port];
1374+
int slot = sp->mgmt_slot;
1375+
1376+
/* The tragic fact about the switch having 4x2 slots for installing
1377+
* management routes is that all of them except one are actually
1378+
* useless.
1379+
* If 2 slots are simultaneously configured for two BPDUs sent to the
1380+
* same (multicast) DMAC but on different egress ports, the switch
1381+
* would confuse them and redirect first frame it receives on the CPU
1382+
* port towards the port configured on the numerically first slot
1383+
* (therefore wrong port), then second received frame on second slot
1384+
* (also wrong port).
1385+
* So for all practical purposes, there needs to be a lock that
1386+
* prevents that from happening. The slot used here is utterly useless
1387+
* (could have simply been 0 just as fine), but we are doing it
1388+
* nonetheless, in case a smarter idea ever comes up in the future.
1389+
*/
1390+
mutex_lock(&priv->mgmt_lock);
1391+
1392+
sja1105_mgmt_xmit(ds, port, slot, skb);
1393+
1394+
mutex_unlock(&priv->mgmt_lock);
1395+
return NETDEV_TX_OK;
12801396
}
12811397

12821398
/* The MAXAGE setting belongs to the L2 Forwarding Parameters table,
@@ -1324,6 +1440,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
13241440
.port_mdb_prepare = sja1105_mdb_prepare,
13251441
.port_mdb_add = sja1105_mdb_add,
13261442
.port_mdb_del = sja1105_mdb_del,
1443+
.port_deferred_xmit = sja1105_port_deferred_xmit,
13271444
};
13281445

13291446
static int sja1105_check_device_id(struct sja1105_private *priv)
@@ -1367,7 +1484,7 @@ static int sja1105_probe(struct spi_device *spi)
13671484
struct device *dev = &spi->dev;
13681485
struct sja1105_private *priv;
13691486
struct dsa_switch *ds;
1370-
int rc;
1487+
int rc, i;
13711488

13721489
if (!dev->of_node) {
13731490
dev_err(dev, "No DTS bindings for SJA1105 driver\n");
@@ -1418,6 +1535,15 @@ static int sja1105_probe(struct spi_device *spi)
14181535
ds->priv = priv;
14191536
priv->ds = ds;
14201537

1538+
/* Connections between dsa_port and sja1105_port */
1539+
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
1540+
struct sja1105_port *sp = &priv->ports[i];
1541+
1542+
ds->ports[i].priv = sp;
1543+
sp->dp = &ds->ports[i];
1544+
}
1545+
mutex_init(&priv->mgmt_lock);
1546+
14211547
return dsa_register_switch(priv->ds);
14221548
}
14231549

include/linux/dsa/sja1105.h

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,39 @@
22
* Copyright (c) 2019, Vladimir Oltean <[email protected]>
33
*/
44

5-
/* Included by drivers/net/dsa/sja1105/sja1105.h */
5+
/* Included by drivers/net/dsa/sja1105/sja1105.h and net/dsa/tag_sja1105.c */
66

77
#ifndef _NET_DSA_SJA1105_H
88
#define _NET_DSA_SJA1105_H
99

10+
#include <linux/skbuff.h>
1011
#include <linux/etherdevice.h>
12+
#include <net/dsa.h>
1113

1214
#define ETH_P_SJA1105 ETH_P_DSA_8021Q
1315

14-
/* The switch can only be convinced to stay in unmanaged mode and not trap any
15-
* link-local traffic by actually telling it to filter frames sent at the
16-
* 00:00:00:00:00:00 destination MAC.
17-
*/
18-
#define SJA1105_LINKLOCAL_FILTER_A 0x000000000000ull
19-
#define SJA1105_LINKLOCAL_FILTER_A_MASK 0xFFFFFFFFFFFFull
20-
#define SJA1105_LINKLOCAL_FILTER_B 0x000000000000ull
21-
#define SJA1105_LINKLOCAL_FILTER_B_MASK 0xFFFFFFFFFFFFull
16+
/* IEEE 802.3 Annex 57A: Slow Protocols PDUs (01:80:C2:xx:xx:xx) */
17+
#define SJA1105_LINKLOCAL_FILTER_A 0x0180C2000000ull
18+
#define SJA1105_LINKLOCAL_FILTER_A_MASK 0xFFFFFF000000ull
19+
/* IEEE 1588 Annex F: Transport of PTP over Ethernet (01:1B:19:xx:xx:xx) */
20+
#define SJA1105_LINKLOCAL_FILTER_B 0x011B19000000ull
21+
#define SJA1105_LINKLOCAL_FILTER_B_MASK 0xFFFFFF000000ull
22+
23+
enum sja1105_frame_type {
24+
SJA1105_FRAME_TYPE_NORMAL = 0,
25+
SJA1105_FRAME_TYPE_LINK_LOCAL,
26+
};
27+
28+
struct sja1105_skb_cb {
29+
enum sja1105_frame_type type;
30+
};
31+
32+
#define SJA1105_SKB_CB(skb) \
33+
((struct sja1105_skb_cb *)DSA_SKB_CB_PRIV(skb))
34+
35+
struct sja1105_port {
36+
struct dsa_port *dp;
37+
int mgmt_slot;
38+
};
2239

2340
#endif /* _NET_DSA_SJA1105_H */

include/net/dsa.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct phylink_link_state;
4343
#define DSA_TAG_PROTO_QCA_VALUE 10
4444
#define DSA_TAG_PROTO_TRAILER_VALUE 11
4545
#define DSA_TAG_PROTO_8021Q_VALUE 12
46+
#define DSA_TAG_PROTO_SJA1105_VALUE 13
4647

4748
enum dsa_tag_protocol {
4849
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -58,6 +59,7 @@ enum dsa_tag_protocol {
5859
DSA_TAG_PROTO_QCA = DSA_TAG_PROTO_QCA_VALUE,
5960
DSA_TAG_PROTO_TRAILER = DSA_TAG_PROTO_TRAILER_VALUE,
6061
DSA_TAG_PROTO_8021Q = DSA_TAG_PROTO_8021Q_VALUE,
62+
DSA_TAG_PROTO_SJA1105 = DSA_TAG_PROTO_SJA1105_VALUE,
6163
};
6264

6365
struct packet_type;

net/dsa/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ config NET_DSA_TAG_LAN9303
102102
Say Y or M if you want to enable support for tagging frames for the
103103
SMSC/Microchip LAN9303 family of switches.
104104

105+
config NET_DSA_TAG_SJA1105
106+
tristate "Tag driver for NXP SJA1105 switches"
107+
select NET_DSA_TAG_8021Q
108+
help
109+
Say Y or M if you want to enable support for tagging frames with the
110+
NXP SJA1105 switch family. Both the native tagging protocol (which
111+
is only for link-local traffic) as well as non-native tagging (based
112+
on a custom 802.1Q VLAN header) are available.
113+
105114
config NET_DSA_TAG_TRAILER
106115
tristate "Tag driver for switches using a trailer tag"
107116
help

net/dsa/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ obj-$(CONFIG_NET_DSA_TAG_KSZ_COMMON) += tag_ksz.o
1313
obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
1414
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
1515
obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
16+
obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
1617
obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o

0 commit comments

Comments
 (0)