Commit 682a8c63 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dsa-multi-cpu-port-part-two'

Vladimir Oltean says:

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

As explained in part 1:
https://patchwork.kernel.org/project/netdevbpf/cover/20220511095020.562461-1-vladimir.oltean@nxp.com/


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 2 (of an unknown number) of that effort.

This series deals only with a minor bug fix (first patch) and with code
reorganization in the Felix DSA driver and in the Ocelot switch library.
Hopefully this will lay the ground for a clean introduction of new UAPI
for changing the DSA master of a user port in part 3.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1e39b27b a4e044dc
Loading
Loading
Loading
Loading
+81 −92
Original line number Diff line number Diff line
@@ -45,24 +45,26 @@ static struct net_device *felix_classify_db(struct dsa_db db)
/* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that
 * the tagger can perform RX source port identification.
 */
static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid)
static int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port,
				       int upstream, u16 vid)
{
	struct ocelot_vcap_filter *outer_tagging_rule;
	struct ocelot *ocelot = &felix->ocelot;
	struct dsa_switch *ds = felix->ds;
	int key_length, upstream, err;
	struct ocelot *ocelot = ds->priv;
	unsigned long cookie;
	int key_length, err;

	key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length;
	upstream = dsa_upstream_port(ds, port);

	outer_tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter),
				     GFP_KERNEL);
	if (!outer_tagging_rule)
		return -ENOMEM;

	cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream);

	outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY;
	outer_tagging_rule->prio = 1;
	outer_tagging_rule->id.cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port);
	outer_tagging_rule->id.cookie = cookie;
	outer_tagging_rule->id.tc_offload = false;
	outer_tagging_rule->block_id = VCAP_ES0;
	outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
@@ -83,16 +85,19 @@ static int felix_tag_8021q_vlan_add_rx(struct felix *felix, int port, u16 vid)
	return err;
}

static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid)
static int felix_tag_8021q_vlan_del_rx(struct dsa_switch *ds, int port,
				       int upstream, u16 vid)
{
	struct ocelot_vcap_filter *outer_tagging_rule;
	struct ocelot_vcap_block *block_vcap_es0;
	struct ocelot *ocelot = &felix->ocelot;
	struct ocelot *ocelot = ds->priv;
	unsigned long cookie;

	block_vcap_es0 = &ocelot->block[VCAP_ES0];
	cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream);

	outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
								 port, false);
								 cookie, false);
	if (!outer_tagging_rule)
		return -ENOENT;

@@ -102,12 +107,14 @@ static int felix_tag_8021q_vlan_del_rx(struct felix *felix, int port, u16 vid)
/* Set up VCAP IS1 rules for stripping the tag_8021q VLAN on TX and VCAP IS2
 * rules for steering those tagged packets towards the correct destination port
 */
static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
static int felix_tag_8021q_vlan_add_tx(struct dsa_switch *ds, int port,
				       u16 vid)
{
	struct ocelot_vcap_filter *untagging_rule, *redirect_rule;
	struct ocelot *ocelot = &felix->ocelot;
	struct dsa_switch *ds = felix->ds;
	int upstream, err;
	unsigned long cpu_ports = dsa_cpu_ports(ds);
	struct ocelot *ocelot = ds->priv;
	unsigned long cookie;
	int err;

	untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
	if (!untagging_rule)
@@ -119,14 +126,14 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
		return -ENOMEM;
	}

	upstream = dsa_upstream_port(ds, port);
	cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port);

	untagging_rule->key_type = OCELOT_VCAP_KEY_ANY;
	untagging_rule->ingress_port_mask = BIT(upstream);
	untagging_rule->ingress_port_mask = cpu_ports;
	untagging_rule->vlan.vid.value = vid;
	untagging_rule->vlan.vid.mask = VLAN_VID_MASK;
	untagging_rule->prio = 1;
	untagging_rule->id.cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port);
	untagging_rule->id.cookie = cookie;
	untagging_rule->id.tc_offload = false;
	untagging_rule->block_id = VCAP_IS1;
	untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
@@ -143,11 +150,13 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
		return err;
	}

	cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port);

	redirect_rule->key_type = OCELOT_VCAP_KEY_ANY;
	redirect_rule->ingress_port_mask = BIT(upstream);
	redirect_rule->ingress_port_mask = cpu_ports;
	redirect_rule->pag = port;
	redirect_rule->prio = 1;
	redirect_rule->id.cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port);
	redirect_rule->id.cookie = cookie;
	redirect_rule->id.tc_offload = false;
	redirect_rule->block_id = VCAP_IS2;
	redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
@@ -165,19 +174,21 @@ static int felix_tag_8021q_vlan_add_tx(struct felix *felix, int port, u16 vid)
	return 0;
}

static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid)
static int felix_tag_8021q_vlan_del_tx(struct dsa_switch *ds, int port, u16 vid)
{
	struct ocelot_vcap_filter *untagging_rule, *redirect_rule;
	struct ocelot_vcap_block *block_vcap_is1;
	struct ocelot_vcap_block *block_vcap_is2;
	struct ocelot *ocelot = &felix->ocelot;
	struct ocelot *ocelot = ds->priv;
	unsigned long cookie;
	int err;

	block_vcap_is1 = &ocelot->block[VCAP_IS1];
	block_vcap_is2 = &ocelot->block[VCAP_IS2];

	cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port);
	untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1,
							     port, false);
							     cookie, false);
	if (!untagging_rule)
		return -ENOENT;

@@ -185,8 +196,9 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid)
	if (err)
		return err;

	cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port);
	redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2,
							    port, false);
							    cookie, false);
	if (!redirect_rule)
		return -ENOENT;

@@ -196,7 +208,7 @@ static int felix_tag_8021q_vlan_del_tx(struct felix *felix, int port, u16 vid)
static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
				    u16 flags)
{
	struct ocelot *ocelot = ds->priv;
	struct dsa_port *cpu_dp;
	int err;

	/* tag_8021q.c assumes we are implementing this via port VLAN
@@ -206,74 +218,50 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
	if (!dsa_is_user_port(ds, port))
		return 0;

	err = felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid);
	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);
		if (err)
			return err;

	err = felix_tag_8021q_vlan_add_tx(ocelot_to_felix(ocelot), port, vid);
	if (err) {
		felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid);
		return err;
	}

	err = felix_tag_8021q_vlan_add_tx(ds, port, vid);
	if (err)
		goto add_tx_failed;

	return 0;

add_tx_failed:
	dsa_switch_for_each_cpu_port(cpu_dp, ds)
		felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid);

	return err;
}

static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
	struct ocelot *ocelot = ds->priv;
	struct dsa_port *cpu_dp;
	int err;

	if (!dsa_is_user_port(ds, port))
		return 0;

	err = felix_tag_8021q_vlan_del_rx(ocelot_to_felix(ocelot), port, vid);
	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		err = felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid);
		if (err)
			return err;

	err = felix_tag_8021q_vlan_del_tx(ocelot_to_felix(ocelot), port, vid);
	if (err) {
		felix_tag_8021q_vlan_add_rx(ocelot_to_felix(ocelot), port, vid);
		return err;
	}

	return 0;
}

/* Alternatively to using the NPI functionality, that same hardware MAC
 * 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
 * concerned, it thinks it is a "dumb switch" - the queues of the CPU port
 * module are now disconnected from it, but can still be accessed through
 * register-based MMIO.
 */
static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port)
{
	mutex_lock(&ocelot->fwd_domain_lock);

	ocelot_port_set_dsa_8021q_cpu(ocelot, port);

	/* Overwrite PGID_CPU with the non-tagging port */
	ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, PGID_CPU);

	ocelot_apply_bridge_fwd_mask(ocelot, true);

	mutex_unlock(&ocelot->fwd_domain_lock);
	}

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

	ocelot_port_unset_dsa_8021q_cpu(ocelot, port);
	err = felix_tag_8021q_vlan_del_tx(ds, port, vid);
	if (err)
		goto del_tx_failed;

	/* Restore PGID_CPU */
	ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID,
			 PGID_CPU);
	return 0;

	ocelot_apply_bridge_fwd_mask(ocelot, true);
del_tx_failed:
	dsa_switch_for_each_cpu_port(cpu_dp, ds)
		felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);

	mutex_unlock(&ocelot->fwd_domain_lock);
	return err;
}

static int felix_trap_get_cpu_port(struct dsa_switch *ds,
@@ -434,6 +422,13 @@ static unsigned long felix_tag_npi_get_host_fwd_mask(struct dsa_switch *ds)
	return BIT(ocelot->num_phys_ports);
}

/* Alternatively to using the NPI functionality, that same hardware MAC
 * 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
 * concerned, it thinks it is a "dumb switch" - the queues of the CPU port
 * module are now disconnected from it, but can still be accessed through
 * register-based MMIO.
 */
static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = {
	.setup			= felix_tag_npi_setup,
	.teardown		= felix_tag_npi_teardown,
@@ -443,21 +438,18 @@ static const struct felix_tag_proto_ops felix_tag_npi_proto_ops = {
static int felix_tag_8021q_setup(struct dsa_switch *ds)
{
	struct ocelot *ocelot = ds->priv;
	struct dsa_port *dp, *cpu_dp;
	struct dsa_port *dp;
	int err;

	err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD));
	if (err)
		return err;

	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		felix_8021q_cpu_port_init(ocelot, cpu_dp->index);
	dsa_switch_for_each_user_port(dp, ds)
		ocelot_port_assign_dsa_8021q_cpu(ocelot, dp->index,
						 dp->cpu_dp->index);

		/* TODO we could support multiple CPU ports in tag_8021q mode */
		break;
	}

	dsa_switch_for_each_available_port(dp, ds) {
	dsa_switch_for_each_available_port(dp, ds)
		/* This overwrites ocelot_init():
		 * Do not forward BPDU frames to the CPU port module,
		 * for 2 reasons:
@@ -471,7 +463,6 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
		ocelot_write_gix(ocelot,
				 ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0),
				 ANA_PORT_CPU_FWD_BPDU_CFG, dp->index);
	}

	/* The ownership of the CPU port module's queues might have just been
	 * transferred to the tag_8021q tagger from the NPI-based tagger.
@@ -488,9 +479,9 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
static void felix_tag_8021q_teardown(struct dsa_switch *ds)
{
	struct ocelot *ocelot = ds->priv;
	struct dsa_port *dp, *cpu_dp;
	struct dsa_port *dp;

	dsa_switch_for_each_available_port(dp, ds) {
	dsa_switch_for_each_available_port(dp, ds)
		/* Restore the logic from ocelot_init:
		 * do not forward BPDU frames to the front ports.
		 */
@@ -498,14 +489,9 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds)
				 ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),
				 ANA_PORT_CPU_FWD_BPDU_CFG,
				 dp->index);
	}

	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		felix_8021q_cpu_port_deinit(ocelot, cpu_dp->index);

		/* TODO we could support multiple CPU ports in tag_8021q mode */
		break;
	}
	dsa_switch_for_each_user_port(dp, ds)
		ocelot_port_unassign_dsa_8021q_cpu(ocelot, dp->index);

	dsa_tag_8021q_unregister(ds);
}
@@ -534,6 +520,9 @@ static void felix_set_host_flood(struct dsa_switch *ds, unsigned long mask,
	ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MC);
	ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV4);
	ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV6);

	val = bc ? mask : 0;
	ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_BC);
}

static void
+2 −1
Original line number Diff line number Diff line
@@ -2162,7 +2162,8 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
			if (ocelot->npi >= 0)
				mask |= BIT(ocelot->npi);
			else
				mask |= ocelot_get_dsa_8021q_cpu_mask(ocelot);
				mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot,
										port);
		}

		/* Calculate the minimum link speed, among the ports that are
+116 −46
Original line number Diff line number Diff line
@@ -2046,57 +2046,68 @@ static int ocelot_bond_get_id(struct ocelot *ocelot, struct net_device *bond)
	return __ffs(bond_mask);
}

u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port)
static u32 ocelot_dsa_8021q_cpu_assigned_ports(struct ocelot *ocelot,
					       struct ocelot_port *cpu)
{
	struct ocelot_port *ocelot_port = ocelot->ports[src_port];
	const struct net_device *bridge;
	u32 mask = 0;
	int port;

	if (!ocelot_port || ocelot_port->stp_state != BR_STATE_FORWARDING)
		return 0;

	bridge = ocelot_port->bridge;
	if (!bridge)
		return 0;

	for (port = 0; port < ocelot->num_phys_ports; port++) {
		ocelot_port = ocelot->ports[port];
		struct ocelot_port *ocelot_port = ocelot->ports[port];

		if (!ocelot_port)
			continue;

		if (ocelot_port->stp_state == BR_STATE_FORWARDING &&
		    ocelot_port->bridge == bridge)
		if (ocelot_port->dsa_8021q_cpu == cpu)
			mask |= BIT(port);
	}

	return mask;
}
EXPORT_SYMBOL_GPL(ocelot_get_bridge_fwd_mask);

u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot)
u32 ocelot_port_assigned_dsa_8021q_cpu_mask(struct ocelot *ocelot, int port)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	struct ocelot_port *cpu_port = ocelot_port->dsa_8021q_cpu;

	if (!cpu_port)
		return 0;

	return BIT(cpu_port->index);
}
EXPORT_SYMBOL_GPL(ocelot_port_assigned_dsa_8021q_cpu_mask);

u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port)
{
	struct ocelot_port *ocelot_port = ocelot->ports[src_port];
	const struct net_device *bridge;
	u32 mask = 0;
	int port;

	if (!ocelot_port || ocelot_port->stp_state != BR_STATE_FORWARDING)
		return 0;

	bridge = ocelot_port->bridge;
	if (!bridge)
		return 0;

	for (port = 0; port < ocelot->num_phys_ports; port++) {
		struct ocelot_port *ocelot_port = ocelot->ports[port];
		ocelot_port = ocelot->ports[port];

		if (!ocelot_port)
			continue;

		if (ocelot_port->is_dsa_8021q_cpu)
		if (ocelot_port->stp_state == BR_STATE_FORWARDING &&
		    ocelot_port->bridge == bridge)
			mask |= BIT(port);
	}

	return mask;
}
EXPORT_SYMBOL_GPL(ocelot_get_dsa_8021q_cpu_mask);
EXPORT_SYMBOL_GPL(ocelot_get_bridge_fwd_mask);

void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
static void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
{
	unsigned long cpu_fwd_mask;
	int port;

	lockdep_assert_held(&ocelot->fwd_domain_lock);
@@ -2108,15 +2119,6 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
	if (joining && ocelot->ops->cut_through_fwd)
		ocelot->ops->cut_through_fwd(ocelot);

	/* 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.
	 */
@@ -2129,17 +2131,19 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
			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
			 * forward packets to all ports assigned to them.
			 */
			mask = GENMASK(ocelot->num_phys_ports - 1, 0);
			mask &= ~cpu_fwd_mask;
			mask = ocelot_dsa_8021q_cpu_assigned_ports(ocelot,
								   ocelot_port);
		} else if (ocelot_port->bridge) {
			struct net_device *bond = ocelot_port->bond;

			mask = ocelot_get_bridge_fwd_mask(ocelot, port);
			mask |= cpu_fwd_mask;
			mask &= ~BIT(port);

			mask |= ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot,
									port);

			if (bond)
				mask &= ~ocelot_get_bond_mask(ocelot, bond);
		} else {
@@ -2147,7 +2151,8 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
			 * ports (if those exist), or to the hardware CPU port
			 * module otherwise.
			 */
			mask = cpu_fwd_mask;
			mask = ocelot_port_assigned_dsa_8021q_cpu_mask(ocelot,
								       port);
		}

		ocelot_write_rix(ocelot, mask, ANA_PGID_PGID, PGID_SRC + port);
@@ -2163,29 +2168,94 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining)
	if (!joining && ocelot->ops->cut_through_fwd)
		ocelot->ops->cut_through_fwd(ocelot);
}
EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);

void ocelot_port_set_dsa_8021q_cpu(struct ocelot *ocelot, int port)
/* Update PGID_CPU which is the destination port mask used for whitelisting
 * unicast addresses filtered towards the host. In the normal and NPI modes,
 * this points to the analyzer entry for the CPU port module, while in DSA
 * tag_8021q mode, it is a bit mask of all active CPU ports.
 * PGID_SRC will take care of forwarding a packet from one user port to
 * no more than a single CPU port.
 */
static void ocelot_update_pgid_cpu(struct ocelot *ocelot)
{
	int pgid_cpu = 0;
	int port;

	for (port = 0; port < ocelot->num_phys_ports; port++) {
		struct ocelot_port *ocelot_port = ocelot->ports[port];

		if (!ocelot_port || !ocelot_port->is_dsa_8021q_cpu)
			continue;

		pgid_cpu |= BIT(port);
	}

	if (!pgid_cpu)
		pgid_cpu = BIT(ocelot->num_phys_ports);

	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)
{
	struct ocelot_port *cpu_port = ocelot->ports[cpu];
	u16 vid;

	ocelot->ports[port]->is_dsa_8021q_cpu = true;
	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, port, vid, true);
			ocelot_vlan_member_add(ocelot, cpu, vid, true);

		ocelot_update_pgid_cpu(ocelot);
	}
EXPORT_SYMBOL_GPL(ocelot_port_set_dsa_8021q_cpu);

void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int 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)
{
	struct ocelot_port *cpu_port = ocelot->ports[port]->dsa_8021q_cpu;
	bool keep = false;
	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;

	ocelot->ports[port]->is_dsa_8021q_cpu = false;
		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, port, vid);
			ocelot_vlan_member_del(ocelot, cpu_port->index, vid);

		ocelot_update_pgid_cpu(ocelot);
	}

	ocelot_apply_bridge_fwd_mask(ocelot, true);

	mutex_unlock(&ocelot->fwd_domain_lock);
}
EXPORT_SYMBOL_GPL(ocelot_port_unset_dsa_8021q_cpu);
EXPORT_SYMBOL_GPL(ocelot_port_unassign_dsa_8021q_cpu);

void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
+7 −4
Original line number Diff line number Diff line
@@ -654,6 +654,8 @@ struct ocelot_mirror {
	int to;
};

struct ocelot_port;

struct ocelot_port {
	struct ocelot			*ocelot;

@@ -662,6 +664,8 @@ struct ocelot_port {
	struct net_device		*bond;
	struct net_device		*bridge;

	struct ocelot_port		*dsa_8021q_cpu;

	/* VLAN that untagged frames are classified to, on ingress */
	const struct ocelot_bridge_vlan	*pvid_vlan;

@@ -865,8 +869,9 @@ 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_set_dsa_8021q_cpu(struct ocelot *ocelot, int port);
void ocelot_port_unset_dsa_8021q_cpu(struct ocelot *ocelot, int port);
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);

/* DSA callbacks */
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data);
@@ -878,9 +883,7 @@ void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled,
			       struct netlink_ext_ack *extack);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot);
u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port);
void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot, bool joining);
int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
				 struct switchdev_brport_flags val);
void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
/* Cookie definitions for private VCAP filters installed by the driver.
 * Must be unique per VCAP block.
 */
#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port)		(port)
#define OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream) ((upstream) << 16 | (port))
#define OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port)		(port)
#define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port)		(port)
#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)		((ocelot)->num_phys_ports + (port))