Commit 08f329fc authored by David S. Miller's avatar David S. Miller

Merge branch 'tag_8021q-cross-chip'

Vladimir Olteans says:

====================
Proper cross-chip support for tag_8021q

The cross-chip bridging support for tag_8021q/sja1105 introduced here:
https://patchwork.ozlabs.org/project/netdev/cover/20200510163743.18032-1-olteanv@gmail.com/

took some shortcuts and is not reusable in other topologies except for
the one it was written for: disjoint DSA trees. A diagram of this
topology can be seen here:
https://patchwork.ozlabs.org/project/netdev/patch/20200510163743.18032-3-olteanv@gmail.com/

However there are sja1105 switches on other boards using other
topologies, most notably:

- Daisy chained:
                                             |
    sw0p0     sw0p1     sw0p2     sw0p3     sw0p4
 [  user ] [  user ] [  user ] [  dsa  ] [  cpu  ]
                                   |
                                   +---------+
                                             |
    sw1p0     sw1p1     sw1p2     sw1p3     sw1p4
 [  user ] [  user ] [  user ] [  dsa  ] [  dsa  ]
                                   |
                                   +---------+
                                             |
    sw2p0     sw2p1     sw2p2     sw2p3     sw2p4
 [  user ] [  user ] [  user ] [  user ] [  dsa  ]

- "H" topology:

         eth0                                                     eth1
          |                                                        |
       CPU port                                                CPU port
          |                        DSA link                        |
 sw0p0  sw0p1  sw0p2  sw0p3  sw0p4 -------- sw1p4  sw1p3  sw1p2  sw1p1  sw1p0
   |             |      |                            |      |             |
 user          user   user                         user   user          user
 port          port   port                         port   port          port

In fact, the current code for tag_8021q cross-chip links works for
neither of these 2 classes of topologies.

The main reasons are:
(a) The sja1105 driver does not treat DSA links. In the "disjoint trees"
    topology, the routing port towards any other switch is also the CPU
    port, and that was already configured so it already worked.
    This series does not deal with enabling DSA links in the sja1105
    driver, that is a fairly trivial task that will be dealt with
    separately.
(b) The tag_8021q code for cross-chip links assumes that any 2 switches
    between cross-chip forwarding needs to be enabled (i.e. which have
    user ports part of the same bridge) are at most 1 hop away from each
    other. This was true for the "disjoint trees" case because
    once a packet reached the CPU port, VLAN-unaware bridging was done
    by the DSA master towards the other switches based on destination
    MAC address, so the tag_8021q header was not interpreted in any way.
    However, in a daisy chain setup with 3 switches, all of them will
    interpret the tag_8021q header, and all tag_8021q VLANs need to be
    installed in all switches.

When looking at the O(n^2) real complexity of the problem, it is clear
that the current code had absolutely no chance of working in the general
case. So this patch series brings a redesign of tag_8021q, in light of
its new requirements. Anything with O(n^2) complexity (where n is the
number of switches in a DSA tree) is an obvious candidate for the DSA
cross-chip notifier support.

One by one, the patches are:
- The sja1105 driver is extremely entangled with tag_8021q, to be exact,
  with that driver's best_effort_vlan_filtering support. We drop this
  operating mode, which means that sja1105 temporarily loses network
  stack termination for VLAN-aware bridges. That operating mode raced
  itself to its own grave anyway due to some hardware limitations in
  combination with PTP reported by NXP customers. I can't say a lot
  more, but network stack termination for VLAN-aware bridges in sja1105
  will be reimplemented soon with a much, much better solution.
- What remains of tag_8021q in sja1105 is support for standalone ports
  mode and for VLAN-unaware bridging. We refactor the API surface of
  tag_8021q to a single pair of dsa_tag_8021q_{register,unregister}
  functions and we clean up everything else related to tag_8021q from
  sja1105 and felix.
- Then we move tag_8021q into the DSA core. I thought about this a lot,
  and there is really no other way to add a DSA_NOTIFIER_TAG_8021Q_VLAN_ADD
  cross-chip notifier if DSA has no way to know if the individual
  switches use tag_8021q or not. So it needs to be part of the core to
  use notifiers.
- Then we modify tag_8021q to update dynamically on bridge_{join,leave}
  events, instead of what we have today which is simply installing the
  VLANs on all ports of a switch and leaving port isolation up to
  somebody else. This change is necessary because port isolation over a
  DSA link cannot be done in any other way except based on VLAN
  membership, as opposed to bridging within the same switch which had 2
  choices (at least on sja1105).
- Finally we add 2 new cross-chip notifiers for adding and deleting a
  tag_8021q VLAN, which is properly refcounted similar to the bridge FDB
  and MDB code, and complete cleanup is done on teardown (note that this
  is unlike regular bridge VLANs, where we currently cannot do
  refcounting because the user can run "bridge vlan add dev swp0 vid 100"
  a gazillion times, and "bridge vlan del dev swp0 vid 100" just once,
  and for some reason expect that the VLAN will be deleted. But I digress).
  With this opportunity we remove a lot of hard-to-digest code and
  replace it with much more idiomatic DSA-style code.

This series was regression-tested on:
- Single-switch boards with SJA1105T
- Disjoint-tree boards with SJA1105S and Felix (using ocelot-8021q)
- H topology boards using SJA1110A
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c18e9405 c64b9c05
...@@ -231,11 +231,6 @@ static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) ...@@ -231,11 +231,6 @@ static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
return 0; return 0;
} }
static const struct dsa_8021q_ops felix_tag_8021q_ops = {
.vlan_add = felix_tag_8021q_vlan_add,
.vlan_del = felix_tag_8021q_vlan_del,
};
/* Alternatively to using the NPI functionality, that same hardware MAC /* Alternatively to using the NPI functionality, that same hardware MAC
* connected internally to the enetc or fman DSA master can be configured to * connected internally to the enetc or fman DSA master can be configured to
* use the software-defined tag_8021q frame format. As far as the hardware is * use the software-defined tag_8021q frame format. As far as the hardware is
...@@ -425,29 +420,18 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu) ...@@ -425,29 +420,18 @@ static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC); ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC);
ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_BC); ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_BC);
felix->dsa_8021q_ctx = kzalloc(sizeof(*felix->dsa_8021q_ctx), err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD));
GFP_KERNEL);
if (!felix->dsa_8021q_ctx)
return -ENOMEM;
felix->dsa_8021q_ctx->ops = &felix_tag_8021q_ops;
felix->dsa_8021q_ctx->proto = htons(ETH_P_8021AD);
felix->dsa_8021q_ctx->ds = ds;
err = dsa_8021q_setup(felix->dsa_8021q_ctx, true);
if (err) if (err)
goto out_free_dsa_8021_ctx; return err;
err = felix_setup_mmio_filtering(felix); err = felix_setup_mmio_filtering(felix);
if (err) if (err)
goto out_teardown_dsa_8021q; goto out_tag_8021q_unregister;
return 0; return 0;
out_teardown_dsa_8021q: out_tag_8021q_unregister:
dsa_8021q_setup(felix->dsa_8021q_ctx, false); dsa_tag_8021q_unregister(ds);
out_free_dsa_8021_ctx:
kfree(felix->dsa_8021q_ctx);
return err; return err;
} }
...@@ -462,11 +446,7 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu) ...@@ -462,11 +446,7 @@ static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu)
dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d", dev_err(ds->dev, "felix_teardown_mmio_filtering returned %d",
err); err);
err = dsa_8021q_setup(felix->dsa_8021q_ctx, false); dsa_tag_8021q_unregister(ds);
if (err)
dev_err(ds->dev, "dsa_8021q_setup returned %d", err);
kfree(felix->dsa_8021q_ctx);
for (port = 0; port < ds->num_ports; port++) { for (port = 0; port < ds->num_ports; port++) {
if (dsa_is_unused_port(ds, port)) if (dsa_is_unused_port(ds, port))
...@@ -1679,6 +1659,8 @@ const struct dsa_switch_ops felix_switch_ops = { ...@@ -1679,6 +1659,8 @@ const struct dsa_switch_ops felix_switch_ops = {
.port_mrp_del = felix_mrp_del, .port_mrp_del = felix_mrp_del,
.port_mrp_add_ring_role = felix_mrp_add_ring_role, .port_mrp_add_ring_role = felix_mrp_add_ring_role,
.port_mrp_del_ring_role = felix_mrp_del_ring_role, .port_mrp_del_ring_role = felix_mrp_del_ring_role,
.tag_8021q_vlan_add = felix_tag_8021q_vlan_add,
.tag_8021q_vlan_del = felix_tag_8021q_vlan_del,
}; };
struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)
......
...@@ -60,7 +60,6 @@ struct felix { ...@@ -60,7 +60,6 @@ struct felix {
struct lynx_pcs **pcs; struct lynx_pcs **pcs;
resource_size_t switch_base; resource_size_t switch_base;
resource_size_t imdio_base; resource_size_t imdio_base;
struct dsa_8021q_context *dsa_8021q_ctx;
enum dsa_tag_protocol tag_proto; enum dsa_tag_protocol tag_proto;
}; };
......
...@@ -234,19 +234,13 @@ struct sja1105_bridge_vlan { ...@@ -234,19 +234,13 @@ struct sja1105_bridge_vlan {
bool untagged; bool untagged;
}; };
enum sja1105_vlan_state {
SJA1105_VLAN_UNAWARE,
SJA1105_VLAN_BEST_EFFORT,
SJA1105_VLAN_FILTERING_FULL,
};
struct sja1105_private { struct sja1105_private {
struct sja1105_static_config static_config; struct sja1105_static_config static_config;
bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS]; bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS];
bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS]; bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS];
phy_interface_t phy_mode[SJA1105_MAX_NUM_PORTS]; phy_interface_t phy_mode[SJA1105_MAX_NUM_PORTS];
bool fixed_link[SJA1105_MAX_NUM_PORTS]; bool fixed_link[SJA1105_MAX_NUM_PORTS];
bool best_effort_vlan_filtering; bool vlan_aware;
unsigned long learn_ena; unsigned long learn_ena;
unsigned long ucast_egress_floods; unsigned long ucast_egress_floods;
unsigned long bcast_egress_floods; unsigned long bcast_egress_floods;
...@@ -263,8 +257,6 @@ struct sja1105_private { ...@@ -263,8 +257,6 @@ struct sja1105_private {
* the switch doesn't confuse them with one another. * the switch doesn't confuse them with one another.
*/ */
struct mutex mgmt_lock; struct mutex mgmt_lock;
struct dsa_8021q_context *dsa_8021q_ctx;
enum sja1105_vlan_state vlan_state;
struct devlink_region **regions; struct devlink_region **regions;
struct sja1105_cbs_entry *cbs; struct sja1105_cbs_entry *cbs;
struct mii_bus *mdio_base_t1; struct mii_bus *mdio_base_t1;
...@@ -311,10 +303,6 @@ int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val); ...@@ -311,10 +303,6 @@ int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val);
/* From sja1105_devlink.c */ /* From sja1105_devlink.c */
int sja1105_devlink_setup(struct dsa_switch *ds); int sja1105_devlink_setup(struct dsa_switch *ds);
void sja1105_devlink_teardown(struct dsa_switch *ds); void sja1105_devlink_teardown(struct dsa_switch *ds);
int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx);
int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx);
int sja1105_devlink_info_get(struct dsa_switch *ds, int sja1105_devlink_info_get(struct dsa_switch *ds,
struct devlink_info_req *req, struct devlink_info_req *req,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
......
...@@ -115,105 +115,6 @@ static void sja1105_teardown_devlink_regions(struct dsa_switch *ds) ...@@ -115,105 +115,6 @@ static void sja1105_teardown_devlink_regions(struct dsa_switch *ds)
kfree(priv->regions); kfree(priv->regions);
} }
static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv,
bool *be_vlan)
{
*be_vlan = priv->best_effort_vlan_filtering;
return 0;
}
static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
bool be_vlan)
{
struct dsa_switch *ds = priv->ds;
bool vlan_filtering;
int port;
int rc;
priv->best_effort_vlan_filtering = be_vlan;
rtnl_lock();
for (port = 0; port < ds->num_ports; port++) {
struct dsa_port *dp;
if (!dsa_is_user_port(ds, port))
continue;
dp = dsa_to_port(ds, port);
vlan_filtering = dsa_port_is_vlan_filtering(dp);
rc = sja1105_vlan_filtering(ds, port, vlan_filtering, NULL);
if (rc)
break;
}
rtnl_unlock();
return rc;
}
enum sja1105_devlink_param_id {
SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING,
};
int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct sja1105_private *priv = ds->priv;
int err;
switch (id) {
case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING:
err = sja1105_best_effort_vlan_filtering_get(priv,
&ctx->val.vbool);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx)
{
struct sja1105_private *priv = ds->priv;
int err;
switch (id) {
case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING:
err = sja1105_best_effort_vlan_filtering_set(priv,
ctx->val.vbool);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static const struct devlink_param sja1105_devlink_params[] = {
DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING,
"best_effort_vlan_filtering",
DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
};
static int sja1105_setup_devlink_params(struct dsa_switch *ds)
{
return dsa_devlink_params_register(ds, sja1105_devlink_params,
ARRAY_SIZE(sja1105_devlink_params));
}
static void sja1105_teardown_devlink_params(struct dsa_switch *ds)
{
dsa_devlink_params_unregister(ds, sja1105_devlink_params,
ARRAY_SIZE(sja1105_devlink_params));
}
int sja1105_devlink_info_get(struct dsa_switch *ds, int sja1105_devlink_info_get(struct dsa_switch *ds,
struct devlink_info_req *req, struct devlink_info_req *req,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
...@@ -233,23 +134,10 @@ int sja1105_devlink_info_get(struct dsa_switch *ds, ...@@ -233,23 +134,10 @@ int sja1105_devlink_info_get(struct dsa_switch *ds,
int sja1105_devlink_setup(struct dsa_switch *ds) int sja1105_devlink_setup(struct dsa_switch *ds)
{ {
int rc; return sja1105_setup_devlink_regions(ds);
rc = sja1105_setup_devlink_params(ds);
if (rc)
return rc;
rc = sja1105_setup_devlink_regions(ds);
if (rc < 0) {
sja1105_teardown_devlink_params(ds);
return rc;
}
return 0;
} }
void sja1105_devlink_teardown(struct dsa_switch *ds) void sja1105_devlink_teardown(struct dsa_switch *ds)
{ {
sja1105_teardown_devlink_params(ds);
sja1105_teardown_devlink_regions(ds); sja1105_teardown_devlink_regions(ds);
} }
This diff is collapsed.
...@@ -496,14 +496,11 @@ int sja1105_vl_redirect(struct sja1105_private *priv, int port, ...@@ -496,14 +496,11 @@ int sja1105_vl_redirect(struct sja1105_private *priv, int port,
struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
int rc; int rc;
if (priv->vlan_state == SJA1105_VLAN_UNAWARE && if (!priv->vlan_aware && key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Can only redirect based on DMAC"); "Can only redirect based on DMAC");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT || } else if (priv->vlan_aware && key->type != SJA1105_KEY_VLAN_AWARE_VL) {
priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
key->type != SJA1105_KEY_VLAN_AWARE_VL) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Can only redirect based on {DMAC, VID, PCP}"); "Can only redirect based on {DMAC, VID, PCP}");
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -595,14 +592,11 @@ int sja1105_vl_gate(struct sja1105_private *priv, int port, ...@@ -595,14 +592,11 @@ int sja1105_vl_gate(struct sja1105_private *priv, int port,
return -ERANGE; return -ERANGE;
} }
if (priv->vlan_state == SJA1105_VLAN_UNAWARE && if (!priv->vlan_aware && key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Can only gate based on DMAC"); "Can only gate based on DMAC");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT || } else if (priv->vlan_aware && key->type != SJA1105_KEY_VLAN_AWARE_VL) {
priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
key->type != SJA1105_KEY_VLAN_AWARE_VL) {
NL_SET_ERR_MSG_MOD(extack, NL_SET_ERR_MSG_MOD(extack,
"Can only gate based on {DMAC, VID, PCP}"); "Can only gate based on {DMAC, VID, PCP}");
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -11,60 +11,38 @@ ...@@ -11,60 +11,38 @@
struct dsa_switch; struct dsa_switch;
struct sk_buff; struct sk_buff;
struct net_device; struct net_device;
struct packet_type;
struct dsa_8021q_context;
struct dsa_8021q_crosschip_link { struct dsa_tag_8021q_vlan {
struct list_head list; struct list_head list;
int port; int port;
struct dsa_8021q_context *other_ctx; u16 vid;
int other_port;
refcount_t refcount; refcount_t refcount;
}; };
struct dsa_8021q_ops {
int (*vlan_add)(struct dsa_switch *ds, int port, u16 vid, u16 flags);
int (*vlan_del)(struct dsa_switch *ds, int port, u16 vid);
};
struct dsa_8021q_context { struct dsa_8021q_context {
const struct dsa_8021q_ops *ops;
struct dsa_switch *ds; struct dsa_switch *ds;
struct list_head crosschip_links; struct list_head vlans;
/* EtherType of RX VID, used for filtering on master interface */ /* EtherType of RX VID, used for filtering on master interface */
__be16 proto; __be16 proto;
}; };
#define DSA_8021Q_N_SUBVLAN 8 int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto);
int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled);
int dsa_8021q_crosschip_bridge_join(struct dsa_8021q_context *ctx, int port, void dsa_tag_8021q_unregister(struct dsa_switch *ds);
struct dsa_8021q_context *other_ctx,
int other_port);
int dsa_8021q_crosschip_bridge_leave(struct dsa_8021q_context *ctx, int port,
struct dsa_8021q_context *other_ctx,
int other_port);
struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
u16 tpid, u16 tci); u16 tpid, u16 tci);
void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id, void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id);
int *subvlan);
u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port); u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port);
u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port); u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port);
u16 dsa_8021q_rx_vid_subvlan(struct dsa_switch *ds, int port, u16 subvlan);
int dsa_8021q_rx_switch_id(u16 vid); int dsa_8021q_rx_switch_id(u16 vid);
int dsa_8021q_rx_source_port(u16 vid); int dsa_8021q_rx_source_port(u16 vid);
u16 dsa_8021q_rx_subvlan(u16 vid);
bool vid_is_dsa_8021q_rxvlan(u16 vid); bool vid_is_dsa_8021q_rxvlan(u16 vid);
bool vid_is_dsa_8021q_txvlan(u16 vid); bool vid_is_dsa_8021q_txvlan(u16 vid);
......
...@@ -59,7 +59,6 @@ struct sja1105_skb_cb { ...@@ -59,7 +59,6 @@ struct sja1105_skb_cb {
((struct sja1105_skb_cb *)((skb)->cb)) ((struct sja1105_skb_cb *)((skb)->cb))
struct sja1105_port { struct sja1105_port {
u16 subvlan_map[DSA_8021Q_N_SUBVLAN];
struct kthread_worker *xmit_worker; struct kthread_worker *xmit_worker;
struct kthread_work xmit_work; struct kthread_work xmit_work;
struct sk_buff_head xmit_queue; struct sk_buff_head xmit_queue;
......
...@@ -352,6 +352,9 @@ struct dsa_switch { ...@@ -352,6 +352,9 @@ struct dsa_switch {
unsigned int ageing_time_min; unsigned int ageing_time_min;
unsigned int ageing_time_max; unsigned int ageing_time_max;
/* Storage for drivers using tag_8021q */
struct dsa_8021q_context *tag_8021q_ctx;
/* devlink used to represent this switch device */ /* devlink used to represent this switch device */
struct devlink *devlink; struct devlink *devlink;
...@@ -869,6 +872,13 @@ struct dsa_switch_ops { ...@@ -869,6 +872,13 @@ struct dsa_switch_ops {
const struct switchdev_obj_ring_role_mrp *mrp); const struct switchdev_obj_ring_role_mrp *mrp);
int (*port_mrp_del_ring_role)(struct dsa_switch *ds, int port, int (*port_mrp_del_ring_role)(struct dsa_switch *ds, int port,
const struct switchdev_obj_ring_role_mrp *mrp); const struct switchdev_obj_ring_role_mrp *mrp);
/*
* tag_8021q operations
*/
int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
u16 flags);
int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
}; };
#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \ #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
......
...@@ -18,16 +18,6 @@ if NET_DSA ...@@ -18,16 +18,6 @@ if NET_DSA
# Drivers must select the appropriate tagging format(s) # Drivers must select the appropriate tagging format(s)
config NET_DSA_TAG_8021Q
tristate
select VLAN_8021Q
help
Unlike the other tagging protocols, the 802.1Q config option simply
provides helpers for other tagging implementations that might rely on
VLAN in one way or another. It is not a complete solution.
Drivers which use these helpers should select this as dependency.
config NET_DSA_TAG_AR9331 config NET_DSA_TAG_AR9331
tristate "Tag driver for Atheros AR9331 SoC with built-in switch" tristate "Tag driver for Atheros AR9331 SoC with built-in switch"
help help
...@@ -126,7 +116,6 @@ config NET_DSA_TAG_OCELOT_8021Q ...@@ -126,7 +116,6 @@ config NET_DSA_TAG_OCELOT_8021Q
tristate "Tag driver for Ocelot family of switches, using VLAN" tristate "Tag driver for Ocelot family of switches, using VLAN"
depends on MSCC_OCELOT_SWITCH_LIB || \ depends on MSCC_OCELOT_SWITCH_LIB || \
(MSCC_OCELOT_SWITCH_LIB=n && COMPILE_TEST) (MSCC_OCELOT_SWITCH_LIB=n && COMPILE_TEST)
select NET_DSA_TAG_8021Q
help help
Say Y or M if you want to enable support for tagging frames with a Say Y or M if you want to enable support for tagging frames with a
custom VLAN-based header. Frames that require timestamping, such as custom VLAN-based header. Frames that require timestamping, such as
...@@ -149,7 +138,6 @@ config NET_DSA_TAG_LAN9303 ...@@ -149,7 +138,6 @@ config NET_DSA_TAG_LAN9303
config NET_DSA_TAG_SJA1105 config NET_DSA_TAG_SJA1105
tristate "Tag driver for NXP SJA1105 switches" tristate "Tag driver for NXP SJA1105 switches"
select NET_DSA_TAG_8021Q
select PACKING select PACKING
help help
Say Y or M if you want to enable support for tagging frames with the Say Y or M if you want to enable support for tagging frames with the
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# the core # the core
obj-$(CONFIG_NET_DSA) += dsa_core.o obj-$(CONFIG_NET_DSA) += dsa_core.o
dsa_core-y += dsa.o dsa2.o master.o port.o slave.o switch.o dsa_core-y += dsa.o dsa2.o master.o port.o slave.o switch.o tag_8021q.o
# tagging formats # tagging formats
obj-$(CONFIG_NET_DSA_TAG_8021Q) += tag_8021q.o
obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o obj-$(CONFIG_NET_DSA_TAG_AR9331) += tag_ar9331.o
obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o
obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o obj-$(CONFIG_NET_DSA_TAG_DSA_COMMON) += tag_dsa.o
......
...@@ -39,6 +39,8 @@ enum { ...@@ -39,6 +39,8 @@ enum {
DSA_NOTIFIER_MRP_DEL, DSA_NOTIFIER_MRP_DEL,
DSA_NOTIFIER_MRP_ADD_RING_ROLE, DSA_NOTIFIER_MRP_ADD_RING_ROLE,
DSA_NOTIFIER_MRP_DEL_RING_ROLE, DSA_NOTIFIER_MRP_DEL_RING_ROLE,
DSA_NOTIFIER_TAG_8021Q_VLAN_ADD,
DSA_NOTIFIER_TAG_8021Q_VLAN_DEL,
}; };
/* DSA_NOTIFIER_AGEING_TIME */ /* DSA_NOTIFIER_AGEING_TIME */
...@@ -113,6 +115,14 @@ struct dsa_notifier_mrp_ring_role_info { ...@@ -113,6 +115,14 @@ struct dsa_notifier_mrp_ring_role_info {
int port; 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_switchdev_event_work {
struct dsa_switch *ds; struct dsa_switch *ds;
int port; int port;
...@@ -253,6 +263,8 @@ int dsa_port_link_register_of(struct dsa_port *dp); ...@@ -253,6 +263,8 @@ int dsa_port_link_register_of(struct dsa_port *dp);
void dsa_port_link_unregister_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); 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); 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; extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp, static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp,
...@@ -386,6 +398,16 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, ...@@ -386,6 +398,16 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
const struct dsa_device_ops *tag_ops, const struct dsa_device_ops *tag_ops,
const struct dsa_device_ops *old_tag_ops); const struct dsa_device_ops *old_tag_ops);
/* tag_8021q.c */
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; extern struct list_head dsa_tree_list;
#endif #endif
...@@ -1217,3 +1217,31 @@ void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr) ...@@ -1217,3 +1217,31 @@ void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr)
if (err) if (err)
pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n"); 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));
}
...@@ -90,18 +90,25 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds, ...@@ -90,18 +90,25 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
struct dsa_notifier_bridge_info *info) struct dsa_notifier_bridge_info *info)
{ {
struct dsa_switch_tree *dst = ds->dst; struct dsa_switch_tree *dst = ds->dst;
int err;
if (dst->index == info->tree_index && ds->index == info->sw_index && if (dst->index == info->tree_index && ds->index == info->sw_index &&
ds->ops->port_bridge_join) ds->ops->port_bridge_join) {
return ds->ops->port_bridge_join(ds, info->port, info->br); err = ds->ops->port_bridge_join(ds, info->port, info->br);
if (err)
return err;
}
if ((dst->index != info->tree_index || ds->index != info->sw_index) && if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
ds->ops->crosschip_bridge_join) ds->ops->crosschip_bridge_join) {
return ds->ops->crosschip_bridge_join(ds, info->tree_index, err = ds->ops->crosschip_bridge_join(ds, info->tree_index,
info->sw_index, info->sw_index,
info->port, info->br); info->port, info->br);
if (err)
return err;
}
return 0; return dsa_tag_8021q_bridge_join(ds, info);
} }
static int dsa_switch_bridge_leave(struct dsa_switch *ds, static int dsa_switch_bridge_leave(struct dsa_switch *ds,
...@@ -151,7 +158,8 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, ...@@ -151,7 +158,8 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
if (err && err != EOPNOTSUPP) if (err && err != EOPNOTSUPP)
return err; return err;
} }
return 0;
return dsa_tag_8021q_bridge_leave(ds, info);
} }
/* Matches for all upstream-facing ports (the CPU port and all upstream-facing /* Matches for all upstream-facing ports (the CPU port and all upstream-facing
...@@ -726,6 +734,12 @@ static int dsa_switch_event(struct notifier_block *nb, ...@@ -726,6 +734,12 @@ static int dsa_switch_event(struct notifier_block *nb,
case DSA_NOTIFIER_MRP_DEL_RING_ROLE: case DSA_NOTIFIER_MRP_DEL_RING_ROLE:
err = dsa_switch_mrp_del_ring_role(ds, info); err = dsa_switch_mrp_del_ring_role(ds, info);
break; 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: default:
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
break; break;
......
This diff is collapsed.
...@@ -41,9 +41,9 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ...@@ -41,9 +41,9 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
struct net_device *netdev, struct net_device *netdev,
struct packet_type *pt) struct packet_type *pt)
{ {
int src_port, switch_id, subvlan; int src_port, switch_id;
dsa_8021q_rcv(skb, &src_port, &switch_id, &subvlan); dsa_8021q_rcv(skb, &src_port, &switch_id);
skb->dev = dsa_master_find_slave(netdev, switch_id, src_port); skb->dev = dsa_master_find_slave(netdev, switch_id, src_port);
if (!skb->dev) if (!skb->dev)
......
...@@ -358,20 +358,6 @@ static struct sk_buff ...@@ -358,20 +358,6 @@ static struct sk_buff
return skb; return skb;
} }
static void sja1105_decode_subvlan(struct sk_buff *skb, u16 subvlan)
{
struct dsa_port *dp = dsa_slave_to_port(skb->dev);
struct sja1105_port *sp = dp->priv;
u16 vid = sp->subvlan_map[subvlan];
u16 vlan_tci;
if (vid == VLAN_N_VID)
return;
vlan_tci = (skb->priority << VLAN_PRIO_SHIFT) | vid;
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
}
static bool sja1105_skb_has_tag_8021q(const struct sk_buff *skb) static bool sja1105_skb_has_tag_8021q(const struct sk_buff *skb)
{ {
u16 tpid = ntohs(eth_hdr(skb)->h_proto); u16 tpid = ntohs(eth_hdr(skb)->h_proto);
...@@ -389,8 +375,8 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -389,8 +375,8 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
struct net_device *netdev, struct net_device *netdev,
struct packet_type *pt) struct packet_type *pt)
{ {
int source_port, switch_id, subvlan = 0;
struct sja1105_meta meta = {0}; struct sja1105_meta meta = {0};
int source_port, switch_id;
struct ethhdr *hdr; struct ethhdr *hdr;
bool is_link_local; bool is_link_local;
bool is_meta; bool is_meta;
...@@ -403,7 +389,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -403,7 +389,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
if (sja1105_skb_has_tag_8021q(skb)) { if (sja1105_skb_has_tag_8021q(skb)) {
/* Normal traffic path. */ /* Normal traffic path. */
dsa_8021q_rcv(skb, &source_port, &switch_id, &subvlan); dsa_8021q_rcv(skb, &source_port, &switch_id);
} else if (is_link_local) { } else if (is_link_local) {
/* Management traffic path. Switch embeds the switch ID and /* Management traffic path. Switch embeds the switch ID and
* port ID into bytes of the destination MAC, courtesy of * port ID into bytes of the destination MAC, courtesy of
...@@ -428,9 +414,6 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -428,9 +414,6 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
return NULL; return NULL;
} }
if (subvlan)
sja1105_decode_subvlan(skb, subvlan);
return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local, return sja1105_rcv_meta_state_machine(skb, &meta, is_link_local,
is_meta); is_meta);
} }
...@@ -538,7 +521,7 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, ...@@ -538,7 +521,7 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
struct net_device *netdev, struct net_device *netdev,
struct packet_type *pt) struct packet_type *pt)
{ {
int source_port = -1, switch_id = -1, subvlan = 0; int source_port = -1, switch_id = -1;
skb->offload_fwd_mark = 1; skb->offload_fwd_mark = 1;
...@@ -551,7 +534,7 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, ...@@ -551,7 +534,7 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
/* Packets with in-band control extensions might still have RX VLANs */ /* Packets with in-band control extensions might still have RX VLANs */
if (likely(sja1105_skb_has_tag_8021q(skb))) if (likely(sja1105_skb_has_tag_8021q(skb)))
dsa_8021q_rcv(skb, &source_port, &switch_id, &subvlan); dsa_8021q_rcv(skb, &source_port, &switch_id);
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
if (!skb->dev) { if (!skb->dev) {
...@@ -561,9 +544,6 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, ...@@ -561,9 +544,6 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
return NULL; return NULL;
} }
if (subvlan)
sja1105_decode_subvlan(skb, subvlan);
return skb; return skb;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment