Commit 52412f55 authored by Paolo Abeni's avatar Paolo Abeni
Browse files

Merge branch 'dsa-changes-for-multiple-cpu-ports-part-3'

Vladimir Oltean says:

====================
DSA changes for multiple CPU ports (part 3)

Those who have been following part 1:
https://patchwork.kernel.org/project/netdevbpf/cover/20220511095020.562461-1-vladimir.oltean@nxp.com/
and part 2:
https://patchwork.kernel.org/project/netdevbpf/cover/20220521213743.2735445-1-vladimir.oltean@nxp.com/
will know that I am trying to enable the second internal port pair from
the NXP LS1028A Felix switch for DSA-tagged traffic via "ocelot-8021q".
This series represents part 3 of that effort.

Covered here are some preparations in DSA for handling multiple DSA
masters:
- when changing the tagging protocol via sysfs
- when the masters go down
as well as preparation for monitoring the upper devices of a DSA master
(to support DSA masters under a LAG).

There are also 2 small preparations for the ocelot driver, for the case
where multiple tag_8021q CPU ports are used in a LAG. Both those changes
have to do with PGID forwarding domains.

Compared to v1, the patches were trimmed down to just another
preparation stage, and the UAPI changes were pushed further out to part 4.
https://patchwork.kernel.org/project/netdevbpf/cover/20220523104256.3556016-1-olteanv@gmail.com/

Compared to v2, I had to export a symbol I forgot to
(ocelot_port_teardown_dsa_8021q_cpu), to avoid a build breakage when the
felix and seville drivers are built as modules.
====================

Link: https://lore.kernel.org/r/20220819174820.3585002-1-vladimir.oltean@nxp.com


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 139b5fbd 291ac151
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -445,6 +445,9 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
	if (err)
		return err;

	dsa_switch_for_each_cpu_port(dp, ds)
		ocelot_port_setup_dsa_8021q_cpu(ocelot, dp->index);

	dsa_switch_for_each_user_port(dp, ds)
		ocelot_port_assign_dsa_8021q_cpu(ocelot, dp->index,
						 dp->cpu_dp->index);
@@ -493,6 +496,9 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds)
	dsa_switch_for_each_user_port(dp, ds)
		ocelot_port_unassign_dsa_8021q_cpu(ocelot, dp->index);

	dsa_switch_for_each_cpu_port(dp, ds)
		ocelot_port_teardown_dsa_8021q_cpu(ocelot, dp->index);

	dsa_tag_8021q_unregister(ds);
}

+51 −32
Original line number Diff line number Diff line
@@ -2064,6 +2064,16 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond)
	return __ffs(bond_mask);
}

/* Returns the mask of user ports assigned to this DSA tag_8021q CPU port.
 * Note that when CPU ports are in a LAG, the user ports are assigned to the
 * 'primary' CPU port, the one whose physical port number gives the logical
 * port number of the LAG.
 *
 * We leave PGID_SRC poorly configured for the 'secondary' CPU port in the LAG
 * (to which no user port is assigned), but it appears that forwarding from
 * this secondary CPU port looks at the PGID_SRC associated with the logical
 * port ID that it's assigned to, which *is* configured properly.
 */
static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot,
					       struct ocelot_port *cpu)
{
@@ -2080,9 +2090,15 @@ static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot,
			mask |= BIT(port);
	}

	if (cpu->bond)
		mask &= ~ocelot_get_bond_mask(ocelot, cpu->bond);

	return mask;
}

/* Returns the DSA tag_8021q CPU port that the given port is assigned to,
 * or the bit mask of CPU ports if said CPU port is in a LAG.
 */
u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
@@ -2091,6 +2107,9 @@ u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port)
	if (!cpu_port)
		return 0;

	if (cpu_port->bond)
		return ocelot_get_bond_mask(ocelot, cpu_port->bond);

	return BIT(cpu_port->index);
}
EXPORT_SYMBOL_GPL(ocelot_port_assigned_dsa_8021q_cpu_mask);
@@ -2214,61 +2233,61 @@ static void ocelot_update_pgid_cpu(struct ocelot *ocelot)
	ocelot_write_rix(ocelot, pgid_cpu, ANA_PGID_PGID, PGID_CPU);
}

void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port,
				      int cpu)
void ocelot_port_setup_dsa_8021q_cpu(struct ocelot *ocelot, int cpu)
{
	struct ocelot_port *cpu_port = ocelot->ports[cpu];
	u16 vid;

	mutex_lock(&ocelot->fwd_domain_lock);

	ocelot->ports[port]->dsa_8021q_cpu = cpu_port;

	if (!cpu_port->is_dsa_8021q_cpu) {
	cpu_port->is_dsa_8021q_cpu = true;

	for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
		ocelot_vlan_member_add(ocelot, cpu, vid, true);

	ocelot_update_pgid_cpu(ocelot);
	}

	ocelot_apply_bridge_fwd_mask(ocelot, true);

	mutex_unlock(&ocelot->fwd_domain_lock);
}
EXPORT_SYMBOL_GPL(ocelot_port_assign_dsa_8021q_cpu);
EXPORT_SYMBOL_GPL(ocelot_port_setup_dsa_8021q_cpu);

void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port)
void ocelot_port_teardown_dsa_8021q_cpu(struct ocelot *ocelot, int cpu)
{
	struct ocelot_port *cpu_port = ocelot->ports[port]->dsa_8021q_cpu;
	bool keep = false;
	struct ocelot_port *cpu_port = ocelot->ports[cpu];
	u16 vid;
	int p;

	mutex_lock(&ocelot->fwd_domain_lock);

	ocelot->ports[port]->dsa_8021q_cpu = NULL;

	for (p = 0; p < ocelot->num_phys_ports; p++) {
		if (!ocelot->ports[p])
			continue;

		if (ocelot->ports[p]->dsa_8021q_cpu == cpu_port) {
			keep = true;
			break;
		}
	}

	if (!keep) {
	cpu_port->is_dsa_8021q_cpu = false;

	for (vid = OCELOT_RSV_VLAN_RANGE_START; vid < VLAN_N_VID; vid++)
		ocelot_vlan_member_del(ocelot, cpu_port->index, vid);

	ocelot_update_pgid_cpu(ocelot);

	mutex_unlock(&ocelot->fwd_domain_lock);
}
EXPORT_SYMBOL_GPL(ocelot_port_teardown_dsa_8021q_cpu);

void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port,
				      int cpu)
{
	struct ocelot_port *cpu_port = ocelot->ports[cpu];

	mutex_lock(&ocelot->fwd_domain_lock);

	ocelot->ports[port]->dsa_8021q_cpu = cpu_port;
	ocelot_apply_bridge_fwd_mask(ocelot, true);

	mutex_unlock(&ocelot->fwd_domain_lock);
}
EXPORT_SYMBOL_GPL(ocelot_port_assign_dsa_8021q_cpu);

void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port)
{
	mutex_lock(&ocelot->fwd_domain_lock);

	ocelot->ports[port]->dsa_8021q_cpu = NULL;
	ocelot_apply_bridge_fwd_mask(ocelot, true);

	mutex_unlock(&ocelot->fwd_domain_lock);
+4 −0
Original line number Diff line number Diff line
@@ -559,6 +559,10 @@ static inline bool dsa_is_user_port(struct dsa_switch *ds, int p)
	list_for_each_entry((_dp), &(_dst)->ports, list) \
		if (dsa_port_is_user((_dp)))

#define dsa_tree_for_each_cpu_port(_dp, _dst) \
	list_for_each_entry((_dp), &(_dst)->ports, list) \
		if (dsa_port_is_cpu((_dp)))

#define dsa_switch_for_each_port(_dp, _ds) \
	list_for_each_entry((_dp), &(_ds)->dst->ports, list) \
		if ((_dp)->ds == (_ds))
+2 −0
Original line number Diff line number Diff line
@@ -1024,6 +1024,8 @@ void ocelot_deinit(struct ocelot *ocelot);
void ocelot_init_port(struct ocelot *ocelot, int port);
void ocelot_deinit_port(struct ocelot *ocelot, int port);

void ocelot_port_setup_dsa_8021q_cpu(struct ocelot *ocelot, int cpu);
void ocelot_port_teardown_dsa_8021q_cpu(struct ocelot *ocelot, int cpu);
void ocelot_port_assign_dsa_8021q_cpu(struct ocelot *ocelot, int port, int cpu);
void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port);
u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port);
+0 −20
Original line number Diff line number Diff line
@@ -568,26 +568,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
	    !is_valid_ether_addr(dev->dev_addr))
		return -EINVAL;

	/* Also don't allow bridging of net devices that are DSA masters, since
	 * the bridge layer rx_handler prevents the DSA fake ethertype handler
	 * to be invoked, so we don't get the chance to strip off and parse the
	 * DSA switch tag protocol header (the bridge layer just returns
	 * RX_HANDLER_CONSUMED, stopping RX processing for these frames).
	 * The only case where that would not be an issue is when bridging can
	 * already be offloaded, such as when the DSA master is itself a DSA
	 * or plain switchdev port, and is bridged only with other ports from
	 * the same hardware device.
	 */
	if (netdev_uses_dsa(dev)) {
		list_for_each_entry(p, &br->port_list, list) {
			if (!netdev_port_same_parent_id(dev, p->dev)) {
				NL_SET_ERR_MSG(extack,
					       "Cannot do software bridging with a DSA master");
				return -EINVAL;
			}
		}
	}

	/* No bridging of bridges */
	if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
		NL_SET_ERR_MSG(extack,
Loading