Commit e21268ef authored by Vladimir Oltean's avatar Vladimir Oltean Committed by Jakub Kicinski

net: dsa: felix: perform switch setup for tag_8021q

Unlike sja1105, the only other user of the software-defined tag_8021q.c
tagger format, the implementation we choose for the Felix DSA switch
driver preserves full functionality under a vlan_filtering bridge
(i.e. IP termination works through the DSA user ports under all
circumstances).

The tag_8021q protocol just wants:
- Identifying the ingress switch port based on the RX VLAN ID, as seen
  by the CPU. We achieve this by using the TCAM engines (which are also
  used for tc-flower offload) to push the RX VLAN as a second, outer
  tag, on egress towards the CPU port.
- Steering traffic injected into the switch from the network stack
  towards the correct front port based on the TX VLAN, and consuming
  (popping) that header on the switch's egress.

A tc-flower pseudocode of the static configuration done by the driver
would look like this:

$ tc qdisc add dev <cpu-port> clsact
$ for eth in swp0 swp1 swp2 swp3; do \
	tc filter add dev <cpu-port> egress flower indev ${eth} \
		action vlan push id <rxvlan> protocol 802.1ad; \
	tc filter add dev <cpu-port> ingress protocol 802.1Q flower
		vlan_id <txvlan> action vlan pop \
		action mirred egress redirect dev ${eth}; \
done

but of course since DSA does not register network interfaces for the CPU
port, this configuration would be impossible for the user to do. Also,
due to the same reason, it is impossible for the user to inadvertently
delete these rules using tc. These rules do not collide in any way with
tc-flower, they just consume some TCAM space, which is something we can
live with.
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 7c83a7c5
This diff is collapsed.
......@@ -48,6 +48,7 @@ struct felix {
struct lynx_pcs **pcs;
resource_size_t switch_base;
resource_size_t imdio_base;
struct dsa_8021q_context *dsa_8021q_ctx;
enum dsa_tag_protocol tag_proto;
};
......
......@@ -889,18 +889,60 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
}
EXPORT_SYMBOL(ocelot_get_ts_info);
static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
static u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot)
{
u32 mask = 0;
int port;
for (port = 0; port < ocelot->num_phys_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port];
if (!ocelot_port)
continue;
if (ocelot_port->is_dsa_8021q_cpu)
mask |= BIT(port);
}
return mask;
}
void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
{
unsigned long cpu_fwd_mask;
int port;
/* If a DSA tag_8021q CPU exists, it needs to be included in the
* regular forwarding path of the front ports regardless of whether
* those are bridged or standalone.
* If DSA tag_8021q is not used, this returns 0, which is fine because
* the hardware-based CPU port module can be a destination for packets
* even if it isn't part of PGID_SRC.
*/
cpu_fwd_mask = ocelot_get_dsa_8021q_cpu_mask(ocelot);
/* Apply FWD mask. The loop is needed to add/remove the current port as
* a source for the other ports.
*/
for (port = 0; port < ocelot->num_phys_ports; port++) {
if (ocelot->bridge_fwd_mask & BIT(port)) {
unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(port);
struct ocelot_port *ocelot_port = ocelot->ports[port];
unsigned long mask;
if (!ocelot_port) {
/* Unused ports can't send anywhere */
mask = 0;
} else if (ocelot_port->is_dsa_8021q_cpu) {
/* The DSA tag_8021q CPU ports need to be able to
* forward packets to all other ports except for
* themselves
*/
mask = GENMASK(ocelot->num_phys_ports - 1, 0);
mask &= ~cpu_fwd_mask;
} else if (ocelot->bridge_fwd_mask & BIT(port)) {
int lag;
mask = ocelot->bridge_fwd_mask & ~BIT(port);
for (lag = 0; lag < ocelot->num_phys_ports; lag++) {
unsigned long bond_mask = ocelot->lags[lag];
......@@ -912,15 +954,18 @@ static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
break;
}
}
ocelot_write_rix(ocelot, mask,
ANA_PGID_PGID, PGID_SRC + port);
} else {
ocelot_write_rix(ocelot, 0,
ANA_PGID_PGID, PGID_SRC + port);
/* Standalone ports forward only to DSA tag_8021q CPU
* ports (if those exist), or to the hardware CPU port
* module otherwise.
*/
mask = cpu_fwd_mask;
}
ocelot_write_rix(ocelot, mask, ANA_PGID_PGID, PGID_SRC + port);
}
}
EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
......
......@@ -1009,6 +1009,7 @@ ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block, int cookie,
return NULL;
}
EXPORT_SYMBOL(ocelot_vcap_block_find_filter_by_id);
/* If @on=false, then SNAP, ARP, IP and OAM frames will not match on keys based
* on destination and source MAC addresses, but only on higher-level protocol
......
......@@ -14,9 +14,6 @@
int ocelot_vcap_filter_stats_update(struct ocelot *ocelot,
struct ocelot_vcap_filter *rule);
struct ocelot_vcap_filter *
ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block, int id,
bool tc_offload);
void ocelot_detect_vcap_constants(struct ocelot *ocelot);
int ocelot_vcap_init(struct ocelot *ocelot);
......
......@@ -610,6 +610,7 @@ struct ocelot_port {
phy_interface_t phy_mode;
u8 *xmit_template;
bool is_dsa_8021q_cpu;
};
struct ocelot {
......@@ -760,6 +761,7 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
struct phy_device *phydev);
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
......
......@@ -693,5 +693,8 @@ int ocelot_vcap_filter_add(struct ocelot *ocelot,
struct netlink_ext_ack *extack);
int ocelot_vcap_filter_del(struct ocelot *ocelot,
struct ocelot_vcap_filter *rule);
struct ocelot_vcap_filter *
ocelot_vcap_block_find_filter_by_id(struct ocelot_vcap_block *block, int id,
bool tc_offload);
#endif /* _OCELOT_VCAP_H_ */
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