aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2021-04-20 16:51:20 -0700
committerDavid S. Miller <davem@davemloft.net>2021-04-20 16:51:20 -0700
commitebbcac3dc26b51d8eeaa228cc2a969cfe4cc5bb3 (patch)
treee46e9584e38c0047315990f22b8625e2cddeb361
parentMerge tag 'mac80211-next-for-net-next-2021-04-20' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next (diff)
parentdt-bindings: net: dsa: Document dsa-tag-protocol property (diff)
downloadwireguard-linux-ebbcac3dc26b51d8eeaa228cc2a969cfe4cc5bb3.tar.xz
wireguard-linux-ebbcac3dc26b51d8eeaa228cc2a969cfe4cc5bb3.zip
Merge branch 'dsa-tag-override'
Tobias Waldekranz says: ==================== net: dsa: Allow default tag protocol to be overridden from DT This is a continuation of the work started in this patch: https://lore.kernel.org/netdev/20210323102326.3677940-1-tobias@waldekranz.com/ In addition to the mv88e6xxx support to dynamically change the protocol, it is now possible to override the protocol from the device tree. This means that when a board vendor finds an incompatibility, they can specify a working protocol in the DT, and users will not have to worry about it. Some background information: In a system using an NXP T1023 SoC connected to a 6390X switch, we noticed that TO_CPU frames where not reaching the CPU. This only happened on hardware port 8. Looking at the DSA master interface (dpaa-ethernet) we could see that an Rx error counter was bumped at the same rate. The logs indicated a parser error. It just so happens that a TO_CPU coming in on device 0, port 8, will result in the first two bytes of the DSA tag being one of: 00 40 00 44 00 46 My guess was that since these values looked like 802.3 length fields, the controller's parser would signal an error if the frame length did not match what was in the header. This was later confirmed using two different workarounds provided by Vladimir. Unfortunately these either bypass or ignore the hardware parser and thus robs working combinations of the ability to do RSS and other nifty things. It was therefore decided to go with the option of a DT override. v1 -> v2: - Fail if the device does not support changing protocols instead of falling back to the default. (Andrew) - Only call change_tag_protocol on CPU ports. (Andrew/Vladimir) - Only allow changing the protocol on chips that have at least "undocumented" level of support for EDSA. (Andrew). - List the supported protocols in the binding documentation. I opted for only listing the protocols that I have tested. As more people test their drivers, they can add them. (Rob) v2 -> v3: - Rename "dsa,tag-protocol" -> "dsa-tag-protocol". (Rob) - Some cleanups to 4/5. (Vladimir) - Add a comment detailing how tree/driver agreement on the tag protocol is enforced. (Vladimir). ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/devicetree/bindings/net/dsa/dsa.yaml9
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c99
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h21
-rw-r--r--include/net/dsa.h5
-rw-r--r--net/dsa/dsa2.c103
-rw-r--r--net/dsa/switch.c25
6 files changed, 192 insertions, 70 deletions
diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.yaml b/Documentation/devicetree/bindings/net/dsa/dsa.yaml
index 8a3494db4d8d..16aa192c118e 100644
--- a/Documentation/devicetree/bindings/net/dsa/dsa.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/dsa.yaml
@@ -70,6 +70,15 @@ patternProperties:
device is what the switch port is connected to
$ref: /schemas/types.yaml#/definitions/phandle
+ dsa-tag-protocol:
+ description:
+ Instead of the default, the switch will use this tag protocol if
+ possible. Useful when a device supports multiple protcols and
+ the default is incompatible with the Ethernet device.
+ enum:
+ - dsa
+ - edsa
+
phy-handle: true
phy-mode: true
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 9c4f8517c34b..9ff1a10993b1 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -2531,10 +2531,10 @@ static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port)
return mv88e6xxx_set_port_mode_normal(chip, port);
/* Setup CPU port mode depending on its supported tag format */
- if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA)
+ if (chip->tag_protocol == DSA_TAG_PROTO_DSA)
return mv88e6xxx_set_port_mode_dsa(chip, port);
- if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA)
+ if (chip->tag_protocol == DSA_TAG_PROTO_EDSA)
return mv88e6xxx_set_port_mode_edsa(chip, port);
return -EINVAL;
@@ -4786,7 +4786,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ops = &mv88e6085_ops,
},
@@ -4807,7 +4806,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g1_irqs = 8,
.atu_move_port_mask = 0xf,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ops = &mv88e6095_ops,
},
@@ -4830,7 +4828,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6097_ops,
},
@@ -4853,7 +4851,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6123_ops,
},
@@ -4874,7 +4872,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g1_irqs = 9,
.atu_move_port_mask = 0xf,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ops = &mv88e6131_ops,
},
@@ -4898,7 +4895,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g2_irqs = 10,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6141_ops,
},
@@ -4921,7 +4918,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.ops = &mv88e6161_ops,
},
@@ -4945,7 +4942,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6165_ops,
},
@@ -4969,7 +4965,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6171_ops,
},
@@ -4993,7 +4989,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6172_ops,
},
@@ -5016,7 +5012,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6175_ops,
},
@@ -5040,7 +5036,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6176_ops,
},
@@ -5061,7 +5057,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g1_irqs = 8,
.atu_move_port_mask = 0xf,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6185_ops,
},
@@ -5079,7 +5075,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.phy_base_addr = 0x0,
.global1_addr = 0x1b,
.global2_addr = 0x1c,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.age_time_coeff = 3750,
.g1_irqs = 9,
.g2_irqs = 14,
@@ -5109,7 +5104,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ops = &mv88e6190x_ops,
},
@@ -5132,7 +5126,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6191_ops,
},
@@ -5155,7 +5148,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6393x_ops,
},
@@ -5178,7 +5170,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6393x_ops,
},
@@ -5205,7 +5196,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g2_irqs = 10,
.atu_move_port_mask = 0xf,
.dual_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6250_ops,
},
@@ -5230,7 +5220,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.ops = &mv88e6240_ops,
},
@@ -5252,7 +5242,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g2_irqs = 10,
.atu_move_port_mask = 0xf,
.dual_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6250_ops,
},
@@ -5276,7 +5265,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6290_ops,
},
@@ -5301,7 +5289,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.ops = &mv88e6320_ops,
},
@@ -5325,7 +5313,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g2_irqs = 10,
.atu_move_port_mask = 0xf,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.ops = &mv88e6321_ops,
},
@@ -5350,7 +5338,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.g2_irqs = 10,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.ops = &mv88e6341_ops,
},
@@ -5374,7 +5362,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6350_ops,
},
@@ -5397,7 +5385,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ops = &mv88e6351_ops,
},
@@ -5421,7 +5409,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_EDSA,
+ .edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.ops = &mv88e6352_ops,
},
@@ -5445,7 +5433,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
+ .edsa_support = MV88E6XXX_EDSA_UNDOCUMENTED,
.ptp_support = true,
.ops = &mv88e6390_ops,
},
@@ -5469,7 +5457,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
+ .edsa_support = MV88E6XXX_EDSA_UNDOCUMENTED,
.ptp_support = true,
.ops = &mv88e6390x_ops,
},
@@ -5492,7 +5480,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
- .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6393x_ops,
},
@@ -5561,7 +5548,45 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
{
struct mv88e6xxx_chip *chip = ds->priv;
- return chip->info->tag_protocol;
+ return chip->tag_protocol;
+}
+
+static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port,
+ enum dsa_tag_protocol proto)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ enum dsa_tag_protocol old_protocol;
+ int err;
+
+ switch (proto) {
+ case DSA_TAG_PROTO_EDSA:
+ switch (chip->info->edsa_support) {
+ case MV88E6XXX_EDSA_UNSUPPORTED:
+ return -EPROTONOSUPPORT;
+ case MV88E6XXX_EDSA_UNDOCUMENTED:
+ dev_warn(chip->dev, "Relying on undocumented EDSA tagging behavior\n");
+ fallthrough;
+ case MV88E6XXX_EDSA_SUPPORTED:
+ break;
+ }
+ break;
+ case DSA_TAG_PROTO_DSA:
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ old_protocol = chip->tag_protocol;
+ chip->tag_protocol = proto;
+
+ mv88e6xxx_reg_lock(chip);
+ err = mv88e6xxx_setup_port_mode(chip, port);
+ mv88e6xxx_reg_unlock(chip);
+
+ if (err)
+ chip->tag_protocol = old_protocol;
+
+ return err;
}
static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
@@ -6026,6 +6051,7 @@ static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
+ .change_tag_protocol = mv88e6xxx_change_tag_protocol,
.setup = mv88e6xxx_setup,
.teardown = mv88e6xxx_teardown,
.phylink_validate = mv88e6xxx_validate,
@@ -6206,6 +6232,11 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
if (err)
goto out;
+ if (chip->info->edsa_support == MV88E6XXX_EDSA_SUPPORTED)
+ chip->tag_protocol = DSA_TAG_PROTO_EDSA;
+ else
+ chip->tag_protocol = DSA_TAG_PROTO_DSA;
+
mv88e6xxx_phy_init(chip);
if (chip->info->ops->get_eeprom) {
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index bce6e0dc8535..4f116f73a74b 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -96,6 +96,22 @@ enum mv88e6xxx_family {
MV88E6XXX_FAMILY_6393, /* 6191X 6193X 6393X */
};
+/**
+ * enum mv88e6xxx_edsa_support - Ethertype DSA tag support level
+ * @MV88E6XXX_EDSA_UNSUPPORTED: Device has no support for EDSA tags
+ * @MV88E6XXX_EDSA_UNDOCUMENTED: Documentation indicates that
+ * egressing FORWARD frames with an EDSA
+ * tag is reserved for future use, but
+ * empirical data shows that this mode
+ * is supported.
+ * @MV88E6XXX_EDSA_SUPPORTED: EDSA tags are fully supported.
+ */
+enum mv88e6xxx_edsa_support {
+ MV88E6XXX_EDSA_UNSUPPORTED = 0,
+ MV88E6XXX_EDSA_UNDOCUMENTED,
+ MV88E6XXX_EDSA_SUPPORTED,
+};
+
struct mv88e6xxx_ops;
struct mv88e6xxx_info {
@@ -133,7 +149,7 @@ struct mv88e6xxx_info {
*/
bool dual_chip;
- enum dsa_tag_protocol tag_protocol;
+ enum mv88e6xxx_edsa_support edsa_support;
/* Mask for FromPort and ToPort value of PortVec used in ATU Move
* operation. 0 means that the ATU Move operation is not supported.
@@ -261,6 +277,9 @@ struct mv88e6xxx_region_priv {
struct mv88e6xxx_chip {
const struct mv88e6xxx_info *info;
+ /* Currently configured tagging protocol */
+ enum dsa_tag_protocol tag_protocol;
+
/* The dsa_switch this private structure is related to */
struct dsa_switch *ds;
diff --git a/include/net/dsa.h b/include/net/dsa.h
index b52e9b057be4..507082959aa4 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -149,6 +149,11 @@ struct dsa_switch_tree {
/* Tagging protocol operations */
const struct dsa_device_ops *tag_ops;
+ /* Default tagging protocol preferred by the switches in this
+ * tree.
+ */
+ enum dsa_tag_protocol default_proto;
+
/*
* Configuration data for the platform device that owns
* this dsa switch tree instance.
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index d7c22e3a1fbf..b71e87909f0e 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -668,6 +668,30 @@ static const struct devlink_ops dsa_devlink_ops = {
.sb_occ_tc_port_bind_get = dsa_devlink_sb_occ_tc_port_bind_get,
};
+static int dsa_switch_setup_tag_protocol(struct dsa_switch *ds)
+{
+ const struct dsa_device_ops *tag_ops = ds->dst->tag_ops;
+ struct dsa_switch_tree *dst = ds->dst;
+ int port, err;
+
+ if (tag_ops->proto == dst->default_proto)
+ return 0;
+
+ for (port = 0; port < ds->num_ports; port++) {
+ if (!dsa_is_cpu_port(ds, port))
+ continue;
+
+ err = ds->ops->change_tag_protocol(ds, port, tag_ops->proto);
+ if (err) {
+ dev_err(ds->dev, "Unable to use tag protocol \"%s\": %pe\n",
+ tag_ops->name, ERR_PTR(err));
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int dsa_switch_setup(struct dsa_switch *ds)
{
struct dsa_devlink_priv *dl_priv;
@@ -718,6 +742,10 @@ static int dsa_switch_setup(struct dsa_switch *ds)
if (err < 0)
goto unregister_notifier;
+ err = dsa_switch_setup_tag_protocol(ds);
+ if (err)
+ goto teardown;
+
devlink_params_publish(ds->devlink);
if (!ds->slave_mii_bus && ds->ops->phy_read) {
@@ -1068,34 +1096,60 @@ static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp,
return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol);
}
-static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
+static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master,
+ const char *user_protocol)
{
struct dsa_switch *ds = dp->ds;
struct dsa_switch_tree *dst = ds->dst;
const struct dsa_device_ops *tag_ops;
- enum dsa_tag_protocol tag_protocol;
+ enum dsa_tag_protocol default_proto;
+
+ /* Find out which protocol the switch would prefer. */
+ default_proto = dsa_get_tag_protocol(dp, master);
+ if (dst->default_proto) {
+ if (dst->default_proto != default_proto) {
+ dev_err(ds->dev,
+ "A DSA switch tree can have only one tagging protocol\n");
+ return -EINVAL;
+ }
+ } else {
+ dst->default_proto = default_proto;
+ }
+
+ /* See if the user wants to override that preference. */
+ if (user_protocol) {
+ if (!ds->ops->change_tag_protocol) {
+ dev_err(ds->dev, "Tag protocol cannot be modified\n");
+ return -EINVAL;
+ }
+
+ tag_ops = dsa_find_tagger_by_name(user_protocol);
+ } else {
+ tag_ops = dsa_tag_driver_get(default_proto);
+ }
+
+ if (IS_ERR(tag_ops)) {
+ if (PTR_ERR(tag_ops) == -ENOPROTOOPT)
+ return -EPROBE_DEFER;
+
+ dev_warn(ds->dev, "No tagger for this switch\n");
+ return PTR_ERR(tag_ops);
+ }
- tag_protocol = dsa_get_tag_protocol(dp, master);
if (dst->tag_ops) {
- if (dst->tag_ops->proto != tag_protocol) {
+ if (dst->tag_ops != tag_ops) {
dev_err(ds->dev,
"A DSA switch tree can have only one tagging protocol\n");
+
+ dsa_tag_driver_put(tag_ops);
return -EINVAL;
}
+
/* In the case of multiple CPU ports per switch, the tagging
- * protocol is still reference-counted only per switch tree, so
- * nothing to do here.
+ * protocol is still reference-counted only per switch tree.
*/
+ dsa_tag_driver_put(tag_ops);
} else {
- tag_ops = dsa_tag_driver_get(tag_protocol);
- if (IS_ERR(tag_ops)) {
- if (PTR_ERR(tag_ops) == -ENOPROTOOPT)
- return -EPROBE_DEFER;
- dev_warn(ds->dev, "No tagger for this switch\n");
- dp->master = NULL;
- return PTR_ERR(tag_ops);
- }
-
dst->tag_ops = tag_ops;
}
@@ -1104,6 +1158,19 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)
dsa_port_set_tag_protocol(dp, dst->tag_ops);
dp->dst = dst;
+ /* At this point, the tree may be configured to use a different
+ * tagger than the one chosen by the switch driver during
+ * .setup, in the case when a user selects a custom protocol
+ * through the DT.
+ *
+ * This is resolved by syncing the driver with the tree in
+ * dsa_switch_setup_tag_protocol once .setup has run and the
+ * driver is ready to accept calls to .change_tag_protocol. If
+ * the driver does not support the custom protocol at that
+ * point, the tree is wholly rejected, thereby ensuring that the
+ * tree and driver are always in agreement on the protocol to
+ * use.
+ */
return 0;
}
@@ -1117,12 +1184,14 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
if (ethernet) {
struct net_device *master;
+ const char *user_protocol;
master = of_find_net_device_by_node(ethernet);
if (!master)
return -EPROBE_DEFER;
- return dsa_port_parse_cpu(dp, master);
+ user_protocol = of_get_property(dn, "dsa-tag-protocol", NULL);
+ return dsa_port_parse_cpu(dp, master, user_protocol);
}
if (link)
@@ -1234,7 +1303,7 @@ static int dsa_port_parse(struct dsa_port *dp, const char *name,
dev_put(master);
- return dsa_port_parse_cpu(dp, master);
+ return dsa_port_parse_cpu(dp, master, NULL);
}
if (!strcmp(name, "dsa"))
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 32963276452f..9bf8e20ecdf3 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -323,15 +323,6 @@ static int dsa_switch_vlan_del(struct dsa_switch *ds,
return 0;
}
-static bool dsa_switch_tag_proto_match(struct dsa_switch *ds, int port,
- struct dsa_notifier_tag_proto_info *info)
-{
- if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
- return true;
-
- return false;
-}
-
static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
struct dsa_notifier_tag_proto_info *info)
{
@@ -344,16 +335,14 @@ static int dsa_switch_change_tag_proto(struct dsa_switch *ds,
ASSERT_RTNL();
for (port = 0; port < ds->num_ports; port++) {
- if (dsa_switch_tag_proto_match(ds, port, info)) {
- err = ds->ops->change_tag_protocol(ds, port,
- tag_ops->proto);
- if (err)
- return err;
+ if (!dsa_is_cpu_port(ds, port))
+ continue;
- if (dsa_is_cpu_port(ds, port))
- dsa_port_set_tag_protocol(dsa_to_port(ds, port),
- tag_ops);
- }
+ err = ds->ops->change_tag_protocol(ds, port, tag_ops->proto);
+ if (err)
+ return err;
+
+ dsa_port_set_tag_protocol(dsa_to_port(ds, port), tag_ops);
}
/* Now that changing the tag protocol can no longer fail, let's update