diff options
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_main.c | 132 | ||||
-rw-r--r-- | include/linux/dsa/8021q.h | 16 | ||||
-rw-r--r-- | net/dsa/dsa_priv.h | 16 | ||||
-rw-r--r-- | net/dsa/port.c | 28 | ||||
-rw-r--r-- | net/dsa/switch.c | 6 | ||||
-rw-r--r-- | net/dsa/tag_8021q.c | 398 |
6 files changed, 256 insertions, 340 deletions
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 6b56c1ada3ee..6618abba23b3 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1990,61 +1990,6 @@ static int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid) &mac[port], true); } -static int sja1105_crosschip_bridge_join(struct dsa_switch *ds, - int tree_index, int sw_index, - int other_port, struct net_device *br) -{ - struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index); - int port, rc; - - if (other_ds->ops != &sja1105_switch_ops) - return 0; - - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_user_port(ds, port)) - continue; - if (dsa_to_port(ds, port)->bridge_dev != br) - continue; - - rc = dsa_8021q_crosschip_bridge_join(ds, port, other_ds, - other_port); - if (rc) - return rc; - - rc = dsa_8021q_crosschip_bridge_join(other_ds, other_port, - ds, port); - if (rc) - return rc; - } - - return 0; -} - -static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds, - int tree_index, int sw_index, - int other_port, - struct net_device *br) -{ - struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index); - int port; - - if (other_ds->ops != &sja1105_switch_ops) - return; - - for (port = 0; port < ds->num_ports; port++) { - if (!dsa_is_user_port(ds, port)) - continue; - if (dsa_to_port(ds, port)->bridge_dev != br) - continue; - - dsa_8021q_crosschip_bridge_leave(ds, port, other_ds, - other_port); - - dsa_8021q_crosschip_bridge_leave(other_ds, other_port, - ds, port); - } -} - static enum dsa_tag_protocol sja1105_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) @@ -2135,11 +2080,6 @@ static int sja1105_commit_vlans(struct sja1105_private *priv, return 0; } -struct sja1105_crosschip_switch { - struct list_head list; - struct dsa_8021q_context *other_ctx; -}; - static int sja1105_commit_pvid(struct sja1105_private *priv) { struct sja1105_bridge_vlan *v; @@ -2205,59 +2145,7 @@ sja1105_build_dsa_8021q_vlans(struct sja1105_private *priv, return 0; } -static int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify); - -static int sja1105_notify_crosschip_switches(struct sja1105_private *priv) -{ - struct dsa_8021q_context *ctx = priv->ds->tag_8021q_ctx; - struct sja1105_crosschip_switch *s, *pos; - struct list_head crosschip_switches; - struct dsa_8021q_crosschip_link *c; - int rc = 0; - - INIT_LIST_HEAD(&crosschip_switches); - - list_for_each_entry(c, &ctx->crosschip_links, list) { - bool already_added = false; - - list_for_each_entry(s, &crosschip_switches, list) { - if (s->other_ctx == c->other_ctx) { - already_added = true; - break; - } - } - - if (already_added) - continue; - - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (!s) { - dev_err(priv->ds->dev, "Failed to allocate memory\n"); - rc = -ENOMEM; - goto out; - } - s->other_ctx = c->other_ctx; - list_add(&s->list, &crosschip_switches); - } - - list_for_each_entry(s, &crosschip_switches, list) { - struct sja1105_private *other_priv = s->other_ctx->ds->priv; - - rc = sja1105_build_vlan_table(other_priv, false); - if (rc) - goto out; - } - -out: - list_for_each_entry_safe(s, pos, &crosschip_switches, list) { - list_del(&s->list); - kfree(s); - } - - return rc; -} - -static int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify) +static int sja1105_build_vlan_table(struct sja1105_private *priv) { struct sja1105_vlan_lookup_entry *new_vlan; struct sja1105_table *table; @@ -2296,12 +2184,6 @@ static int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify) if (rc) goto out; - if (notify) { - rc = sja1105_notify_crosschip_switches(priv); - if (rc) - goto out; - } - out: kfree(new_vlan); @@ -2389,7 +2271,7 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, l2_lookup_params = table->entries; l2_lookup_params->shared_learn = !priv->vlan_aware; - rc = sja1105_build_vlan_table(priv, false); + rc = sja1105_build_vlan_table(priv); if (rc) return rc; @@ -2485,7 +2367,7 @@ static int sja1105_vlan_add(struct dsa_switch *ds, int port, if (!vlan_table_changed) return 0; - return sja1105_build_vlan_table(priv, true); + return sja1105_build_vlan_table(priv); } static int sja1105_vlan_del(struct dsa_switch *ds, int port, @@ -2502,7 +2384,7 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port, if (!vlan_table_changed) return 0; - return sja1105_build_vlan_table(priv, true); + return sja1105_build_vlan_table(priv); } static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, @@ -2515,7 +2397,7 @@ static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, if (rc <= 0) return rc; - return sja1105_build_vlan_table(priv, true); + return sja1105_build_vlan_table(priv); } static int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) @@ -2527,7 +2409,7 @@ static int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) if (!rc) return 0; - return sja1105_build_vlan_table(priv, true); + return sja1105_build_vlan_table(priv); } /* The programming model for the SJA1105 switch is "all-at-once" via static @@ -3132,8 +3014,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .cls_flower_add = sja1105_cls_flower_add, .cls_flower_del = sja1105_cls_flower_del, .cls_flower_stats = sja1105_cls_flower_stats, - .crosschip_bridge_join = sja1105_crosschip_bridge_join, - .crosschip_bridge_leave = sja1105_crosschip_bridge_leave, .devlink_info_get = sja1105_devlink_info_get, .tag_8021q_vlan_add = sja1105_dsa_8021q_vlan_add, .tag_8021q_vlan_del = sja1105_dsa_8021q_vlan_del, diff --git a/include/linux/dsa/8021q.h b/include/linux/dsa/8021q.h index 9cf2c99eb668..ec5abfcdefd1 100644 --- a/include/linux/dsa/8021q.h +++ b/include/linux/dsa/8021q.h @@ -11,19 +11,17 @@ struct dsa_switch; struct sk_buff; struct net_device; -struct dsa_8021q_context; -struct dsa_8021q_crosschip_link { +struct dsa_tag_8021q_vlan { struct list_head list; int port; - struct dsa_8021q_context *other_ctx; - int other_port; + u16 vid; refcount_t refcount; }; struct dsa_8021q_context { struct dsa_switch *ds; - struct list_head crosschip_links; + struct list_head vlans; /* EtherType of RX VID, used for filtering on master interface */ __be16 proto; }; @@ -32,14 +30,6 @@ int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto); void dsa_tag_8021q_unregister(struct dsa_switch *ds); -int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port, - struct dsa_switch *other_ds, - int other_port); - -int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_switch *other_ds, - int other_port); - struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, u16 tpid, u16 tci); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 28c4d1107b6d..efd6bca78d2f 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -39,6 +39,8 @@ enum { DSA_NOTIFIER_MRP_DEL, DSA_NOTIFIER_MRP_ADD_RING_ROLE, DSA_NOTIFIER_MRP_DEL_RING_ROLE, + DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, + DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, }; /* DSA_NOTIFIER_AGEING_TIME */ @@ -113,6 +115,14 @@ struct dsa_notifier_mrp_ring_role_info { int port; }; +/* DSA_NOTIFIER_TAG_8021Q_VLAN_* */ +struct dsa_notifier_tag_8021q_vlan_info { + int tree_index; + int sw_index; + int port; + u16 vid; +}; + struct dsa_switchdev_event_work { struct dsa_switch *ds; int port; @@ -253,6 +263,8 @@ int dsa_port_link_register_of(struct dsa_port *dp); void dsa_port_link_unregister_of(struct dsa_port *dp); int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr); void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr); +int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid); +void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid); extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp, @@ -391,6 +403,10 @@ int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, struct dsa_notifier_bridge_info *info); int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, struct dsa_notifier_bridge_info *info); +int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, + struct dsa_notifier_tag_8021q_vlan_info *info); +int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, + struct dsa_notifier_tag_8021q_vlan_info *info); extern struct list_head dsa_tree_list; diff --git a/net/dsa/port.c b/net/dsa/port.c index 28b45b7e66df..982e18771d76 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1217,3 +1217,31 @@ void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) if (err) pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n"); } + +int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid) +{ + struct dsa_notifier_tag_8021q_vlan_info info = { + .tree_index = dp->ds->dst->index, + .sw_index = dp->ds->index, + .port = dp->index, + .vid = vid, + }; + + return dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info); +} + +void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid) +{ + struct dsa_notifier_tag_8021q_vlan_info info = { + .tree_index = dp->ds->dst->index, + .sw_index = dp->ds->index, + .port = dp->index, + .vid = vid, + }; + int err; + + err = dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info); + if (err) + pr_err("DSA: failed to notify tag_8021q VLAN deletion: %pe\n", + ERR_PTR(err)); +} diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 38560de99b80..fd1a1c6bf9cf 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -734,6 +734,12 @@ static int dsa_switch_event(struct notifier_block *nb, case DSA_NOTIFIER_MRP_DEL_RING_ROLE: err = dsa_switch_mrp_del_ring_role(ds, info); break; + case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD: + err = dsa_switch_tag_8021q_vlan_add(ds, info); + break; + case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: + err = dsa_switch_tag_8021q_vlan_del(ds, info); + break; default: err = -EOPNOTSUPP; break; diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c index 0946169033a5..51dcde7db26b 100644 --- a/net/dsa/tag_8021q.c +++ b/net/dsa/tag_8021q.c @@ -107,21 +107,152 @@ bool vid_is_dsa_8021q(u16 vid) } EXPORT_SYMBOL_GPL(vid_is_dsa_8021q); -/* If @enabled is true, installs @vid with @flags into the switch port's HW - * filter. - * If @enabled is false, deletes @vid (ignores @flags) from the port. Had the - * user explicitly configured this @vid through the bridge core, then the @vid - * is installed again, but this time with the flags from the bridge layer. - */ -static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, - u16 flags, bool enabled) +static struct dsa_tag_8021q_vlan * +dsa_tag_8021q_vlan_find(struct dsa_8021q_context *ctx, int port, u16 vid) +{ + struct dsa_tag_8021q_vlan *v; + + list_for_each_entry(v, &ctx->vlans, list) + if (v->vid == vid && v->port == port) + return v; + + return NULL; +} + +static int dsa_switch_do_tag_8021q_vlan_add(struct dsa_switch *ds, int port, + u16 vid, u16 flags) { + struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_tag_8021q_vlan *v; + int err; + + /* No need to bother with refcounting for user ports */ + if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) + return ds->ops->tag_8021q_vlan_add(ds, port, vid, flags); + + v = dsa_tag_8021q_vlan_find(ctx, port, vid); + if (v) { + refcount_inc(&v->refcount); + return 0; + } + + v = kzalloc(sizeof(*v), GFP_KERNEL); + if (!v) + return -ENOMEM; + + err = ds->ops->tag_8021q_vlan_add(ds, port, vid, flags); + if (err) { + kfree(v); + return err; + } + + v->vid = vid; + v->port = port; + refcount_set(&v->refcount, 1); + list_add_tail(&v->list, &ctx->vlans); + + return 0; +} + +static int dsa_switch_do_tag_8021q_vlan_del(struct dsa_switch *ds, int port, + u16 vid) +{ + struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; + struct dsa_port *dp = dsa_to_port(ds, port); + struct dsa_tag_8021q_vlan *v; + int err; + + /* No need to bother with refcounting for user ports */ + if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) + return ds->ops->tag_8021q_vlan_del(ds, port, vid); + + v = dsa_tag_8021q_vlan_find(ctx, port, vid); + if (!v) + return -ENOENT; + + if (!refcount_dec_and_test(&v->refcount)) + return 0; + + err = ds->ops->tag_8021q_vlan_del(ds, port, vid); + if (err) { + refcount_inc(&v->refcount); + return err; + } + + list_del(&v->list); + kfree(v); + + return 0; +} - if (enabled) - return ds->ops->tag_8021q_vlan_add(ds, dp->index, vid, flags); +static bool +dsa_switch_tag_8021q_vlan_match(struct dsa_switch *ds, int port, + struct dsa_notifier_tag_8021q_vlan_info *info) +{ + if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) + return true; + + if (ds->dst->index == info->tree_index && ds->index == info->sw_index) + return port == info->port; + + return false; +} + +int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds, + struct dsa_notifier_tag_8021q_vlan_info *info) +{ + int port, err; + + /* Since we use dsa_broadcast(), there might be other switches in other + * trees which don't support tag_8021q, so don't return an error. + * Or they might even support tag_8021q but have not registered yet to + * use it (maybe they use another tagger currently). + */ + if (!ds->ops->tag_8021q_vlan_add || !ds->tag_8021q_ctx) + return 0; - return ds->ops->tag_8021q_vlan_del(ds, dp->index, vid); + for (port = 0; port < ds->num_ports; port++) { + if (dsa_switch_tag_8021q_vlan_match(ds, port, info)) { + u16 flags = 0; + + if (dsa_is_user_port(ds, port)) + flags |= BRIDGE_VLAN_INFO_UNTAGGED; + + if (vid_is_dsa_8021q_rxvlan(info->vid) && + dsa_8021q_rx_switch_id(info->vid) == ds->index && + dsa_8021q_rx_source_port(info->vid) == port) + flags |= BRIDGE_VLAN_INFO_PVID; + + err = dsa_switch_do_tag_8021q_vlan_add(ds, port, + info->vid, + flags); + if (err) + return err; + } + } + + return 0; +} + +int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds, + struct dsa_notifier_tag_8021q_vlan_info *info) +{ + int port, err; + + if (!ds->ops->tag_8021q_vlan_del || !ds->tag_8021q_ctx) + return 0; + + for (port = 0; port < ds->num_ports; port++) { + if (dsa_switch_tag_8021q_vlan_match(ds, port, info)) { + err = dsa_switch_do_tag_8021q_vlan_del(ds, port, + info->vid); + if (err) + return err; + } + } + + return 0; } /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single @@ -192,6 +323,7 @@ int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, struct dsa_notifier_bridge_info *info) { struct dsa_switch *targeted_ds; + struct dsa_port *targeted_dp; u16 targeted_rx_vid; int err, port; @@ -199,23 +331,23 @@ int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, return 0; targeted_ds = dsa_switch_find(info->tree_index, info->sw_index); + targeted_dp = dsa_to_port(targeted_ds, info->port); targeted_rx_vid = dsa_8021q_rx_vid(targeted_ds, info->port); for (port = 0; port < ds->num_ports; port++) { + struct dsa_port *dp = dsa_to_port(ds, port); u16 rx_vid = dsa_8021q_rx_vid(ds, port); if (!dsa_tag_8021q_bridge_match(ds, port, info)) continue; /* Install the RX VID of the targeted port in our VLAN table */ - err = dsa_8021q_vid_apply(ds, port, targeted_rx_vid, - BRIDGE_VLAN_INFO_UNTAGGED, true); + err = dsa_port_tag_8021q_vlan_add(dp, targeted_rx_vid); if (err) return err; /* Install our RX VID into the targeted port's VLAN table */ - err = dsa_8021q_vid_apply(targeted_ds, info->port, rx_vid, - BRIDGE_VLAN_INFO_UNTAGGED, true); + err = dsa_port_tag_8021q_vlan_add(targeted_dp, rx_vid); if (err) return err; } @@ -227,46 +359,39 @@ int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, struct dsa_notifier_bridge_info *info) { struct dsa_switch *targeted_ds; + struct dsa_port *targeted_dp; u16 targeted_rx_vid; - int err, port; + int port; if (!ds->tag_8021q_ctx) return 0; targeted_ds = dsa_switch_find(info->tree_index, info->sw_index); + targeted_dp = dsa_to_port(targeted_ds, info->port); targeted_rx_vid = dsa_8021q_rx_vid(targeted_ds, info->port); for (port = 0; port < ds->num_ports; port++) { + struct dsa_port *dp = dsa_to_port(ds, port); u16 rx_vid = dsa_8021q_rx_vid(ds, port); if (!dsa_tag_8021q_bridge_match(ds, port, info)) continue; /* Remove the RX VID of the targeted port from our VLAN table */ - err = dsa_8021q_vid_apply(ds, port, targeted_rx_vid, - BRIDGE_VLAN_INFO_UNTAGGED, false); - if (err) - dev_err(ds->dev, - "port %d failed to delete tag_8021q VLAN: %pe\n", - port, ERR_PTR(err)); + dsa_port_tag_8021q_vlan_del(dp, targeted_rx_vid); /* Remove our RX VID from the targeted port's VLAN table */ - err = dsa_8021q_vid_apply(targeted_ds, info->port, rx_vid, - BRIDGE_VLAN_INFO_UNTAGGED, false); - if (err) - dev_err(targeted_ds->dev, - "port %d failed to delete tag_8021q VLAN: %pe\n", - info->port, ERR_PTR(err)); + dsa_port_tag_8021q_vlan_del(targeted_dp, rx_vid); } return 0; } /* Set up a port's tag_8021q RX and TX VLAN for standalone mode operation */ -static int dsa_8021q_setup_port(struct dsa_switch *ds, int port, bool enabled) +static int dsa_tag_8021q_port_setup(struct dsa_switch *ds, int port) { struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; - int upstream = dsa_upstream_port(ds, port); + struct dsa_port *dp = dsa_to_port(ds, port); u16 rx_vid = dsa_8021q_rx_vid(ds, port); u16 tx_vid = dsa_8021q_tx_vid(ds, port); struct net_device *master; @@ -275,29 +400,17 @@ static int dsa_8021q_setup_port(struct dsa_switch *ds, int port, bool enabled) /* The CPU port is implicitly configured by * configuring the front-panel ports */ - if (!dsa_is_user_port(ds, port)) + if (!dsa_port_is_user(dp)) return 0; - master = dsa_to_port(ds, port)->cpu_dp->master; + master = dp->cpu_dp->master; /* Add this user port's RX VID to the membership list of all others * (including itself). This is so that bridging will not be hindered. * L2 forwarding rules still take precedence when there are no VLAN * restrictions, so there are no concerns about leaking traffic. */ - err = dsa_8021q_vid_apply(ds, port, rx_vid, BRIDGE_VLAN_INFO_UNTAGGED | - BRIDGE_VLAN_INFO_PVID, enabled); - if (err) { - dev_err(ds->dev, - "Failed to apply RX VID %d to port %d: %pe\n", - rx_vid, port, ERR_PTR(err)); - return err; - } - - /* CPU port needs to see this port's RX VID - * as tagged egress. - */ - err = dsa_8021q_vid_apply(ds, upstream, rx_vid, 0, enabled); + err = dsa_port_tag_8021q_vlan_add(dp, rx_vid); if (err) { dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %pe\n", @@ -306,39 +419,51 @@ static int dsa_8021q_setup_port(struct dsa_switch *ds, int port, bool enabled) } /* Add @rx_vid to the master's RX filter. */ - if (enabled) - vlan_vid_add(master, ctx->proto, rx_vid); - else - vlan_vid_del(master, ctx->proto, rx_vid); + vlan_vid_add(master, ctx->proto, rx_vid); /* Finally apply the TX VID on this port and on the CPU port */ - err = dsa_8021q_vid_apply(ds, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED, - enabled); + err = dsa_port_tag_8021q_vlan_add(dp, tx_vid); if (err) { dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %pe\n", tx_vid, port, ERR_PTR(err)); return err; } - err = dsa_8021q_vid_apply(ds, upstream, tx_vid, 0, enabled); - if (err) { - dev_err(ds->dev, - "Failed to apply TX VID %d on port %d: %pe\n", - tx_vid, upstream, ERR_PTR(err)); - return err; - } return err; } -static int dsa_8021q_setup(struct dsa_switch *ds, bool enabled) +static void dsa_tag_8021q_port_teardown(struct dsa_switch *ds, int port) +{ + struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; + struct dsa_port *dp = dsa_to_port(ds, port); + u16 rx_vid = dsa_8021q_rx_vid(ds, port); + u16 tx_vid = dsa_8021q_tx_vid(ds, port); + struct net_device *master; + + /* The CPU port is implicitly configured by + * configuring the front-panel ports + */ + if (!dsa_port_is_user(dp)) + return; + + master = dp->cpu_dp->master; + + dsa_port_tag_8021q_vlan_del(dp, rx_vid); + + vlan_vid_del(master, ctx->proto, rx_vid); + + dsa_port_tag_8021q_vlan_del(dp, tx_vid); +} + +static int dsa_tag_8021q_setup(struct dsa_switch *ds) { int err, port; ASSERT_RTNL(); for (port = 0; port < ds->num_ports; port++) { - err = dsa_8021q_setup_port(ds, port, enabled); + err = dsa_tag_8021q_port_setup(ds, port); if (err < 0) { dev_err(ds->dev, "Failed to setup VLAN tagging for port %d: %pe\n", @@ -350,140 +475,15 @@ static int dsa_8021q_setup(struct dsa_switch *ds, bool enabled) return 0; } -static int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port, - struct dsa_switch *other_ds, - int other_port, bool enabled) +static void dsa_tag_8021q_teardown(struct dsa_switch *ds) { - u16 rx_vid = dsa_8021q_rx_vid(ds, port); + int port; - /* @rx_vid of local @ds port @port goes to @other_port of - * @other_ds - */ - return dsa_8021q_vid_apply(other_ds, other_port, rx_vid, - BRIDGE_VLAN_INFO_UNTAGGED, enabled); -} - -static int dsa_8021q_crosschip_link_add(struct dsa_switch *ds, int port, - struct dsa_switch *other_ds, - int other_port) -{ - struct dsa_8021q_context *other_ctx = other_ds->tag_8021q_ctx; - struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; - struct dsa_8021q_crosschip_link *c; - - list_for_each_entry(c, &ctx->crosschip_links, list) { - if (c->port == port && c->other_ctx == other_ctx && - c->other_port == other_port) { - refcount_inc(&c->refcount); - return 0; - } - } - - dev_dbg(ds->dev, - "adding crosschip link from port %d to %s port %d\n", - port, dev_name(other_ds->dev), other_port); - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - - c->port = port; - c->other_ctx = other_ctx; - c->other_port = other_port; - refcount_set(&c->refcount, 1); - - list_add(&c->list, &ctx->crosschip_links); - - return 0; -} - -static void dsa_8021q_crosschip_link_del(struct dsa_switch *ds, - struct dsa_8021q_crosschip_link *c, - bool *keep) -{ - *keep = !refcount_dec_and_test(&c->refcount); - - if (*keep) - return; - - dev_dbg(ds->dev, - "deleting crosschip link from port %d to %s port %d\n", - c->port, dev_name(c->other_ctx->ds->dev), c->other_port); - - list_del(&c->list); - kfree(c); -} - -/* Make traffic from local port @port be received by remote port @other_port. - * This means that our @rx_vid needs to be installed on @other_ds's upstream - * and user ports. The user ports should be egress-untagged so that they can - * pop the dsa_8021q VLAN. But the @other_upstream can be either egress-tagged - * or untagged: it doesn't matter, since it should never egress a frame having - * our @rx_vid. - */ -int dsa_8021q_crosschip_bridge_join(struct dsa_switch *ds, int port, - struct dsa_switch *other_ds, - int other_port) -{ - /* @other_upstream is how @other_ds reaches us. If we are part - * of disjoint trees, then we are probably connected through - * our CPU ports. If we're part of the same tree though, we should - * probably use dsa_towards_port. - */ - int other_upstream = dsa_upstream_port(other_ds, other_port); - int err; - - err = dsa_8021q_crosschip_link_add(ds, port, other_ds, other_port); - if (err) - return err; - - err = dsa_8021q_crosschip_link_apply(ds, port, other_ds, - other_port, true); - if (err) - return err; - - err = dsa_8021q_crosschip_link_add(ds, port, other_ds, other_upstream); - if (err) - return err; - - return dsa_8021q_crosschip_link_apply(ds, port, other_ds, - other_upstream, true); -} -EXPORT_SYMBOL_GPL(dsa_8021q_crosschip_bridge_join); - -int dsa_8021q_crosschip_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_switch *other_ds, - int other_port) -{ - struct dsa_8021q_context *other_ctx = other_ds->tag_8021q_ctx; - int other_upstream = dsa_upstream_port(other_ds, other_port); - struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; - struct dsa_8021q_crosschip_link *c, *n; - - list_for_each_entry_safe(c, n, &ctx->crosschip_links, list) { - if (c->port == port && c->other_ctx == other_ctx && - (c->other_port == other_port || - c->other_port == other_upstream)) { - int other_port = c->other_port; - bool keep; - int err; - - dsa_8021q_crosschip_link_del(ds, c, &keep); - if (keep) - continue; - - err = dsa_8021q_crosschip_link_apply(ds, port, - other_ds, - other_port, - false); - if (err) - return err; - } - } + ASSERT_RTNL(); - return 0; + for (port = 0; port < ds->num_ports; port++) + dsa_tag_8021q_port_teardown(ds, port); } -EXPORT_SYMBOL_GPL(dsa_8021q_crosschip_bridge_leave); int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto) { @@ -496,28 +496,24 @@ int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto) ctx->proto = proto; ctx->ds = ds; - INIT_LIST_HEAD(&ctx->crosschip_links); + INIT_LIST_HEAD(&ctx->vlans); ds->tag_8021q_ctx = ctx; - return dsa_8021q_setup(ds, true); + return dsa_tag_8021q_setup(ds); } EXPORT_SYMBOL_GPL(dsa_tag_8021q_register); void dsa_tag_8021q_unregister(struct dsa_switch *ds) { struct dsa_8021q_context *ctx = ds->tag_8021q_ctx; - struct dsa_8021q_crosschip_link *c, *n; - int err; + struct dsa_tag_8021q_vlan *v, *n; - err = dsa_8021q_setup(ds, false); - if (err) - dev_err(ds->dev, "failed to tear down tag_8021q VLANs: %pe\n", - ERR_PTR(err)); + dsa_tag_8021q_teardown(ds); - list_for_each_entry_safe(c, n, &ctx->crosschip_links, list) { - list_del(&c->list); - kfree(c); + list_for_each_entry_safe(v, n, &ctx->vlans, list) { + list_del(&v->list); + kfree(v); } ds->tag_8021q_ctx = NULL; |