diff options
-rw-r--r-- | Documentation/devicetree/bindings/net/dsa/dsa.yaml | 9 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 99 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 21 | ||||
-rw-r--r-- | include/net/dsa.h | 5 | ||||
-rw-r--r-- | net/dsa/dsa2.c | 103 | ||||
-rw-r--r-- | net/dsa/switch.c | 25 |
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 |