Merge tag 'exfat-for-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linki...
[linux-2.6-microblaze.git] / net / dsa / tag_8021q.c
index 780b2a1..8e3e8a5 100644 (file)
@@ -146,15 +146,15 @@ EXPORT_SYMBOL_GPL(vid_is_dsa_8021q);
  * 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,
+static int dsa_8021q_vid_apply(struct dsa_8021q_context *ctx, int port, u16 vid,
                               u16 flags, bool enabled)
 {
-       struct dsa_port *dp = dsa_to_port(ds, port);
+       struct dsa_port *dp = dsa_to_port(ctx->ds, port);
 
        if (enabled)
-               return dsa_port_vid_add(dp, vid, flags);
+               return ctx->ops->vlan_add(ctx->ds, dp->index, vid, flags);
 
-       return dsa_port_vid_del(dp, vid);
+       return ctx->ops->vlan_del(ctx->ds, dp->index, vid);
 }
 
 /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single
@@ -209,25 +209,29 @@ static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid,
  * +-+-----+-+-----+-+-----+-+-----+-+    +-+-----+-+-----+-+-----+-+-----+-+
  *   swp0    swp1    swp2    swp3           swp0    swp1    swp2    swp3
  */
-int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
+static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port,
+                               bool enabled)
 {
-       int upstream = dsa_upstream_port(ds, port);
-       u16 rx_vid = dsa_8021q_rx_vid(ds, port);
-       u16 tx_vid = dsa_8021q_tx_vid(ds, port);
-       int i, err;
+       int upstream = dsa_upstream_port(ctx->ds, port);
+       u16 rx_vid = dsa_8021q_rx_vid(ctx->ds, port);
+       u16 tx_vid = dsa_8021q_tx_vid(ctx->ds, port);
+       struct net_device *master;
+       int i, err, subvlan;
 
        /* The CPU port is implicitly configured by
         * configuring the front-panel ports
         */
-       if (!dsa_is_user_port(ds, port))
+       if (!dsa_is_user_port(ctx->ds, port))
                return 0;
 
+       master = dsa_to_port(ctx->ds, port)->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.
         */
-       for (i = 0; i < ds->num_ports; i++) {
+       for (i = 0; i < ctx->ds->num_ports; i++) {
                u16 flags;
 
                if (i == upstream)
@@ -240,9 +244,10 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
                        /* The RX VID is a regular VLAN on all others */
                        flags = BRIDGE_VLAN_INFO_UNTAGGED;
 
-               err = dsa_8021q_vid_apply(ds, i, rx_vid, flags, enabled);
+               err = dsa_8021q_vid_apply(ctx, i, rx_vid, flags, enabled);
                if (err) {
-                       dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n",
+                       dev_err(ctx->ds->dev,
+                               "Failed to apply RX VID %d to port %d: %d\n",
                                rx_vid, port, err);
                        return err;
                }
@@ -251,80 +256,115 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled)
        /* 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_8021q_vid_apply(ctx, upstream, rx_vid, 0, enabled);
        if (err) {
-               dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n",
+               dev_err(ctx->ds->dev,
+                       "Failed to apply RX VID %d to port %d: %d\n",
                        rx_vid, port, err);
                return err;
        }
 
+       /* Add to the master's RX filter not only @rx_vid, but in fact
+        * the entire subvlan range, just in case this DSA switch might
+        * want to use sub-VLANs.
+        */
+       for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) {
+               u16 vid = dsa_8021q_rx_vid_subvlan(ctx->ds, port, subvlan);
+
+               if (enabled)
+                       vlan_vid_add(master, ctx->proto, vid);
+               else
+                       vlan_vid_del(master, ctx->proto, 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,
+       err = dsa_8021q_vid_apply(ctx, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED,
                                  enabled);
        if (err) {
-               dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n",
+               dev_err(ctx->ds->dev,
+                       "Failed to apply TX VID %d on port %d: %d\n",
                        tx_vid, port, err);
                return err;
        }
-       err = dsa_8021q_vid_apply(ds, upstream, tx_vid, 0, enabled);
+       err = dsa_8021q_vid_apply(ctx, upstream, tx_vid, 0, enabled);
        if (err) {
-               dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n",
+               dev_err(ctx->ds->dev,
+                       "Failed to apply TX VID %d on port %d: %d\n",
                        tx_vid, upstream, err);
                return err;
        }
 
        return err;
 }
-EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging);
 
-static int dsa_8021q_crosschip_link_apply(struct dsa_switch *ds, int port,
-                                         struct dsa_switch *other_ds,
+int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled)
+{
+       int rc, port;
+
+       ASSERT_RTNL();
+
+       for (port = 0; port < ctx->ds->num_ports; port++) {
+               rc = dsa_8021q_setup_port(ctx, port, enabled);
+               if (rc < 0) {
+                       dev_err(ctx->ds->dev,
+                               "Failed to setup VLAN tagging for port %d: %d\n",
+                               port, rc);
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dsa_8021q_setup);
+
+static int dsa_8021q_crosschip_link_apply(struct dsa_8021q_context *ctx,
+                                         int port,
+                                         struct dsa_8021q_context *other_ctx,
                                          int other_port, bool enabled)
 {
-       u16 rx_vid = dsa_8021q_rx_vid(ds, port);
+       u16 rx_vid = dsa_8021q_rx_vid(ctx->ds, 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,
+       return dsa_8021q_vid_apply(other_ctx, 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 list_head *crosschip_links)
+static int dsa_8021q_crosschip_link_add(struct dsa_8021q_context *ctx, int port,
+                                       struct dsa_8021q_context *other_ctx,
+                                       int other_port)
 {
        struct dsa_8021q_crosschip_link *c;
 
-       list_for_each_entry(c, crosschip_links, list) {
-               if (c->port == port && c->other_ds == other_ds &&
+       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);
+       dev_dbg(ctx->ds->dev,
+               "adding crosschip link from port %d to %s port %d\n",
+               port, dev_name(other_ctx->ds->dev), other_port);
 
        c = kzalloc(sizeof(*c), GFP_KERNEL);
        if (!c)
                return -ENOMEM;
 
        c->port = port;
-       c->other_ds = other_ds;
+       c->other_ctx = other_ctx;
        c->other_port = other_port;
        refcount_set(&c->refcount, 1);
 
-       list_add(&c->list, crosschip_links);
+       list_add(&c->list, &ctx->crosschip_links);
 
        return 0;
 }
 
-static void dsa_8021q_crosschip_link_del(struct dsa_switch *ds,
+static void dsa_8021q_crosschip_link_del(struct dsa_8021q_context *ctx,
                                         struct dsa_8021q_crosschip_link *c,
-                                        struct list_head *crosschip_links,
                                         bool *keep)
 {
        *keep = !refcount_dec_and_test(&c->refcount);
@@ -332,9 +372,9 @@ static void dsa_8021q_crosschip_link_del(struct dsa_switch *ds,
        if (*keep)
                return;
 
-       dev_dbg(ds->dev,
+       dev_dbg(ctx->ds->dev,
                "deleting crosschip link from port %d to %s port %d\n",
-               c->port, dev_name(c->other_ds->dev), c->other_port);
+               c->port, dev_name(c->other_ctx->ds->dev), c->other_port);
 
        list_del(&c->list);
        kfree(c);
@@ -347,64 +387,58 @@ static void dsa_8021q_crosschip_link_del(struct dsa_switch *ds,
  * 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,
-                                   struct list_head *crosschip_links)
+int dsa_8021q_crosschip_bridge_join(struct dsa_8021q_context *ctx, int port,
+                                   struct dsa_8021q_context *other_ctx,
+                                   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 other_upstream = dsa_upstream_port(other_ctx->ds, other_port);
        int rc;
 
-       rc = dsa_8021q_crosschip_link_add(ds, port, other_ds,
-                                         other_port, crosschip_links);
+       rc = dsa_8021q_crosschip_link_add(ctx, port, other_ctx, other_port);
        if (rc)
                return rc;
 
-       rc = dsa_8021q_crosschip_link_apply(ds, port, other_ds,
+       rc = dsa_8021q_crosschip_link_apply(ctx, port, other_ctx,
                                            other_port, true);
        if (rc)
                return rc;
 
-       rc = dsa_8021q_crosschip_link_add(ds, port, other_ds,
-                                         other_upstream,
-                                         crosschip_links);
+       rc = dsa_8021q_crosschip_link_add(ctx, port, other_ctx, other_upstream);
        if (rc)
                return rc;
 
-       return dsa_8021q_crosschip_link_apply(ds, port, other_ds,
+       return dsa_8021q_crosschip_link_apply(ctx, port, other_ctx,
                                              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 list_head *crosschip_links)
+int dsa_8021q_crosschip_bridge_leave(struct dsa_8021q_context *ctx, int port,
+                                    struct dsa_8021q_context *other_ctx,
+                                    int other_port)
 {
-       int other_upstream = dsa_upstream_port(other_ds, other_port);
+       int other_upstream = dsa_upstream_port(other_ctx->ds, other_port);
        struct dsa_8021q_crosschip_link *c, *n;
 
-       list_for_each_entry_safe(c, n, crosschip_links, list) {
-               if (c->port == port && c->other_ds == other_ds &&
+       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)) {
-                       struct dsa_switch *other_ds = c->other_ds;
+                       struct dsa_8021q_context *other_ctx = c->other_ctx;
                        int other_port = c->other_port;
                        bool keep;
                        int rc;
 
-                       dsa_8021q_crosschip_link_del(ds, c, crosschip_links,
-                                                    &keep);
+                       dsa_8021q_crosschip_link_del(ctx, c, &keep);
                        if (keep)
                                continue;
 
-                       rc = dsa_8021q_crosschip_link_apply(ds, port,
-                                                           other_ds,
+                       rc = dsa_8021q_crosschip_link_apply(ctx, port,
+                                                           other_ctx,
                                                            other_port,
                                                            false);
                        if (rc)