Commit 3d93a144 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'vln-ocelot-fixes'



Vladimir Oltean says:

====================
VLAN fixes for Ocelot driver

This is a collection of patches I've gathered over the past several
months.

Patches 1-6/14 are supporting patches for selftests.

Patch 9/14 fixes PTP TX from a VLAN upper of a VLAN-aware bridge port
when using the "ocelot-8021q" tagging protocol. Patch 7/14 is its
supporting selftest.

Patch 10/14 fixes the QoS class used by PTP in the same case as above.
It is hard to quantify - there is no selftest.

Patch 11/14 fixes potential data corruption during PTP TX in the same
case as above. Again, there is no selftest.

Patch 13/14 fixes RX in the same case as above - 8021q upper of a
VLAN-aware bridge port, with the "ocelot-8021q" tagging protocol. Patch
12/14 is a supporting patch for this in the DSA core, and 7/14 is also
its selftest.

Patch 14/14 ensures that VLAN-aware bridges offloaded to Ocelot only
react to the ETH_P_8021Q TPID, and treat absolutely everything else as
VLAN-untagged, including ETH_P_8021AD. Patch 8/14 is the supporting
selftest.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b153b3c7 36dd1141
Loading
Loading
Loading
Loading
+120 −6
Original line number Diff line number Diff line
@@ -61,11 +61,46 @@ static int felix_cpu_port_for_conduit(struct dsa_switch *ds,
	return cpu_dp->index;
}

/**
 * felix_update_tag_8021q_rx_rule - Update VCAP ES0 tag_8021q rule after
 *				    vlan_filtering change
 * @outer_tagging_rule: Pointer to VCAP filter on which the update is performed
 * @vlan_filtering: Current bridge VLAN filtering setting
 *
 * Source port identification for tag_8021q is done using VCAP ES0 rules on the
 * CPU port(s). The ES0 tag B (inner tag from the packet) can be configured as
 * either:
 * - push_inner_tag=0: the inner tag is never pushed into the frame
 *		       (and we lose info about the classified VLAN). This is
 *		       good when the classified VLAN is a discardable quantity
 *		       for the software RX path: it is either set to
 *		       OCELOT_STANDALONE_PVID, or to
 *		       ocelot_vlan_unaware_pvid(bridge).
 * - push_inner_tag=1: the inner tag is always pushed. This is good when the
 *		       classified VLAN is not a discardable quantity (the port
 *		       is under a VLAN-aware bridge, and software needs to
 *		       continue processing the packet in the same VLAN as the
 *		       hardware).
 * The point is that what is good for a VLAN-unaware port is not good for a
 * VLAN-aware port, and vice versa. Thus, the RX tagging rules must be kept in
 * sync with the VLAN filtering state of the port.
 */
static void
felix_update_tag_8021q_rx_rule(struct ocelot_vcap_filter *outer_tagging_rule,
			       bool vlan_filtering)
{
	if (vlan_filtering)
		outer_tagging_rule->action.push_inner_tag = OCELOT_ES0_TAG;
	else
		outer_tagging_rule->action.push_inner_tag = OCELOT_NO_ES0_TAG;
}

/* 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 dsa_switch *ds, int port,
				       int upstream, u16 vid)
				       int upstream, u16 vid,
				       bool vlan_filtering)
{
	struct ocelot_vcap_filter *outer_tagging_rule;
	struct ocelot *ocelot = ds->priv;
@@ -96,6 +131,14 @@ static int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port,
	outer_tagging_rule->action.tag_a_tpid_sel = OCELOT_TAG_TPID_SEL_8021AD;
	outer_tagging_rule->action.tag_a_vid_sel = 1;
	outer_tagging_rule->action.vid_a_val = vid;
	felix_update_tag_8021q_rx_rule(outer_tagging_rule, vlan_filtering);
	outer_tagging_rule->action.tag_b_tpid_sel = OCELOT_TAG_TPID_SEL_8021Q;
	/* Leave TAG_B_VID_SEL at 0 (Classified VID + VID_B_VAL). Since we also
	 * leave VID_B_VAL at 0, this makes ES0 tag B (the inner tag) equal to
	 * the classified VID, which we need to see in the DSA tagger's receive
	 * path. Note: the inner tag is only visible in the packet when pushed
	 * (push_inner_tag == OCELOT_ES0_TAG).
	 */

	err = ocelot_vcap_filter_add(ocelot, outer_tagging_rule, NULL);
	if (err)
@@ -227,6 +270,7 @@ static int felix_tag_8021q_vlan_del_tx(struct dsa_switch *ds, int port, u16 vid)
static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
				    u16 flags)
{
	struct dsa_port *dp = dsa_to_port(ds, port);
	struct dsa_port *cpu_dp;
	int err;

@@ -234,11 +278,12 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
	 * membership, which we aren't. So we don't need to add any VCAP filter
	 * for the CPU port.
	 */
	if (!dsa_is_user_port(ds, port))
	if (!dsa_port_is_user(dp))
		return 0;

	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);
		err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid,
						  dsa_port_is_vlan_filtering(dp));
		if (err)
			return err;
	}
@@ -258,10 +303,11 @@ static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,

static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
	struct dsa_port *dp = dsa_to_port(ds, port);
	struct dsa_port *cpu_dp;
	int err;

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

	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
@@ -278,11 +324,41 @@ static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)

del_tx_failed:
	dsa_switch_for_each_cpu_port(cpu_dp, ds)
		felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid);
		felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid,
					    dsa_port_is_vlan_filtering(dp));

	return err;
}

static int felix_update_tag_8021q_rx_rules(struct dsa_switch *ds, int port,
					   bool vlan_filtering)
{
	struct ocelot_vcap_filter *outer_tagging_rule;
	struct ocelot_vcap_block *block_vcap_es0;
	struct ocelot *ocelot = ds->priv;
	struct dsa_port *cpu_dp;
	unsigned long cookie;
	int err;

	block_vcap_es0 = &ocelot->block[VCAP_ES0];

	dsa_switch_for_each_cpu_port(cpu_dp, ds) {
		cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port,
							  cpu_dp->index);

		outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
									 cookie, false);

		felix_update_tag_8021q_rx_rule(outer_tagging_rule, vlan_filtering);

		err = ocelot_vcap_filter_replace(ocelot, outer_tagging_rule);
		if (err)
			return err;
	}

	return 0;
}

static int felix_trap_get_cpu_port(struct dsa_switch *ds,
				   const struct ocelot_vcap_filter *trap)
{
@@ -528,7 +604,19 @@ static int felix_tag_8021q_setup(struct dsa_switch *ds)
	 * so we need to be careful that there are no extra frames to be
	 * dequeued over MMIO, since we would never know to discard them.
	 */
	ocelot_lock_xtr_grp_bh(ocelot, 0);
	ocelot_drain_cpu_queue(ocelot, 0);
	ocelot_unlock_xtr_grp_bh(ocelot, 0);

	/* Problem: when using push_inner_tag=1 for ES0 tag B, we lose info
	 * about whether the received packets were VLAN-tagged on the wire,
	 * since they are always tagged on egress towards the CPU port.
	 *
	 * Since using push_inner_tag=1 is unavoidable for VLAN-aware bridges,
	 * we must work around the fallout by untagging in software to make
	 * untagged reception work more or less as expected.
	 */
	ds->untag_vlan_aware_bridge_pvid = true;

	return 0;
}
@@ -554,6 +642,8 @@ static void felix_tag_8021q_teardown(struct dsa_switch *ds)
		ocelot_port_teardown_dsa_8021q_cpu(ocelot, dp->index);

	dsa_tag_8021q_unregister(ds);

	ds->untag_vlan_aware_bridge_pvid = false;
}

static unsigned long felix_tag_8021q_get_host_fwd_mask(struct dsa_switch *ds)
@@ -1008,8 +1098,23 @@ static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
				struct netlink_ext_ack *extack)
{
	struct ocelot *ocelot = ds->priv;
	bool using_tag_8021q;
	struct felix *felix;
	int err;

	return ocelot_port_vlan_filtering(ocelot, port, enabled, extack);
	err = ocelot_port_vlan_filtering(ocelot, port, enabled, extack);
	if (err)
		return err;

	felix = ocelot_to_felix(ocelot);
	using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q;
	if (using_tag_8021q) {
		err = felix_update_tag_8021q_rx_rules(ds, port, enabled);
		if (err)
			return err;
	}

	return 0;
}

static int felix_vlan_add(struct dsa_switch *ds, int port,
@@ -1518,6 +1623,8 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
	int port = xmit_work->dp->index;
	int retries = 10;

	ocelot_lock_inj_grp(ocelot, 0);

	do {
		if (ocelot_can_inject(ocelot, 0))
			break;
@@ -1526,6 +1633,7 @@ static void felix_port_deferred_xmit(struct kthread_work *work)
	} while (--retries);

	if (!retries) {
		ocelot_unlock_inj_grp(ocelot, 0);
		dev_err(ocelot->dev, "port %d failed to inject skb\n",
			port);
		ocelot_port_purge_txtstamp_skb(ocelot, port, skb);
@@ -1535,6 +1643,8 @@ static void felix_port_deferred_xmit(struct kthread_work *work)

	ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);

	ocelot_unlock_inj_grp(ocelot, 0);

	consume_skb(skb);
	kfree(xmit_work);
}
@@ -1694,6 +1804,8 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot)
	if (!felix->info->quirk_no_xtr_irq)
		return false;

	ocelot_lock_xtr_grp(ocelot, grp);

	while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
		struct sk_buff *skb;
		unsigned int type;
@@ -1730,6 +1842,8 @@ static bool felix_check_xtr_pkt(struct ocelot *ocelot)
		ocelot_drain_cpu_queue(ocelot, 0);
	}

	ocelot_unlock_xtr_grp(ocelot, grp);

	return true;
}

+261 −18
Original line number Diff line number Diff line
@@ -453,8 +453,157 @@ static u16 ocelot_vlan_unaware_pvid(struct ocelot *ocelot,
	return VLAN_N_VID - bridge_num - 1;
}

/**
 * ocelot_update_vlan_reclassify_rule() - Make switch aware only to bridge VLAN TPID
 *
 * @ocelot: Switch private data structure
 * @port: Index of ingress port
 *
 * IEEE 802.1Q-2018 clauses "5.5 C-VLAN component conformance" and "5.6 S-VLAN
 * component conformance" suggest that a C-VLAN component should only recognize
 * and filter on C-Tags, and an S-VLAN component should only recognize and
 * process based on C-Tags.
 *
 * In Linux, as per commit 1a0b20b25732 ("Merge branch 'bridge-next'"), C-VLAN
 * components are largely represented by a bridge with vlan_protocol 802.1Q,
 * and S-VLAN components by a bridge with vlan_protocol 802.1ad.
 *
 * Currently the driver only offloads vlan_protocol 802.1Q, but the hardware
 * design is non-conformant, because the switch assigns each frame to a VLAN
 * based on an entirely different question, as detailed in figure "Basic VLAN
 * Classification Flow" from its manual and reproduced below.
 *
 * Set TAG_TYPE, PCP, DEI, VID to port-default values in VLAN_CFG register
 * if VLAN_AWARE_ENA[port] and frame has outer tag then:
 *   if VLAN_INNER_TAG_ENA[port] and frame has inner tag then:
 *     TAG_TYPE = (Frame.InnerTPID <> 0x8100)
 *     Set PCP, DEI, VID to values from inner VLAN header
 *   else:
 *     TAG_TYPE = (Frame.OuterTPID <> 0x8100)
 *     Set PCP, DEI, VID to values from outer VLAN header
 *   if VID == 0 then:
 *     VID = VLAN_CFG.VLAN_VID
 *
 * Summarized, the switch will recognize both 802.1Q and 802.1ad TPIDs as VLAN
 * "with equal rights", and just set the TAG_TYPE bit to 0 (if 802.1Q) or to 1
 * (if 802.1ad). It will classify based on whichever of the tags is "outer", no
 * matter what TPID that may have (or "inner", if VLAN_INNER_TAG_ENA[port]).
 *
 * In the VLAN Table, the TAG_TYPE information is not accessible - just the
 * classified VID is - so it is as if each VLAN Table entry is for 2 VLANs:
 * C-VLAN X, and S-VLAN X.
 *
 * Whereas the Linux bridge behavior is to only filter on frames with a TPID
 * equal to the vlan_protocol, and treat everything else as VLAN-untagged.
 *
 * Consider an ingress packet tagged with 802.1ad VID=3 and 802.1Q VID=5,
 * received on a bridge vlan_filtering=1 vlan_protocol=802.1Q port. This frame
 * should be treated as 802.1Q-untagged, and classified to the PVID of that
 * bridge port. Not to VID=3, and not to VID=5.
 *
 * The VCAP IS1 TCAM has everything we need to overwrite the choices made in
 * the basic VLAN classification pipeline: it can match on TAG_TYPE in the key,
 * and it can modify the classified VID in the action. Thus, for each port
 * under a vlan_filtering bridge, we can insert a rule in VCAP IS1 lookup 0 to
 * match on 802.1ad tagged frames and modify their classified VID to the 802.1Q
 * PVID of the port. This effectively makes it appear to the outside world as
 * if those packets were processed as VLAN-untagged.
 *
 * The rule needs to be updated each time the bridge PVID changes, and needs
 * to be deleted if the bridge PVID is deleted, or if the port becomes
 * VLAN-unaware.
 */
static int ocelot_update_vlan_reclassify_rule(struct ocelot *ocelot, int port)
{
	unsigned long cookie = OCELOT_VCAP_IS1_VLAN_RECLASSIFY(ocelot, port);
	struct ocelot_vcap_block *block_vcap_is1 = &ocelot->block[VCAP_IS1];
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	const struct ocelot_bridge_vlan *pvid_vlan;
	struct ocelot_vcap_filter *filter;
	int err, val, pcp, dei;
	bool vid_replace_ena;
	u16 vid;

	pvid_vlan = ocelot_port->pvid_vlan;
	vid_replace_ena = ocelot_port->vlan_aware && pvid_vlan;

	filter = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, cookie,
						     false);
	if (!vid_replace_ena) {
		/* If the reclassification filter doesn't need to exist, delete
		 * it if it was previously installed, and exit doing nothing
		 * otherwise.
		 */
		if (filter)
			return ocelot_vcap_filter_del(ocelot, filter);

		return 0;
	}

	/* The reclassification rule must apply. See if it already exists
	 * or if it must be created.
	 */

	/* Treating as VLAN-untagged means using as classified VID equal to
	 * the bridge PVID, and PCP/DEI set to the port default QoS values.
	 */
	vid = pvid_vlan->vid;
	val = ocelot_read_gix(ocelot, ANA_PORT_QOS_CFG, port);
	pcp = ANA_PORT_QOS_CFG_QOS_DEFAULT_VAL_X(val);
	dei = !!(val & ANA_PORT_QOS_CFG_DP_DEFAULT_VAL);

	if (filter) {
		bool changed = false;

		/* Filter exists, just update it */
		if (filter->action.vid != vid) {
			filter->action.vid = vid;
			changed = true;
		}
		if (filter->action.pcp != pcp) {
			filter->action.pcp = pcp;
			changed = true;
		}
		if (filter->action.dei != dei) {
			filter->action.dei = dei;
			changed = true;
		}

		if (!changed)
			return 0;

		return ocelot_vcap_filter_replace(ocelot, filter);
	}

	/* Filter doesn't exist, create it */
	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
	if (!filter)
		return -ENOMEM;

	filter->key_type = OCELOT_VCAP_KEY_ANY;
	filter->ingress_port_mask = BIT(port);
	filter->vlan.tpid = OCELOT_VCAP_BIT_1;
	filter->prio = 1;
	filter->id.cookie = cookie;
	filter->id.tc_offload = false;
	filter->block_id = VCAP_IS1;
	filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
	filter->lookup = 0;
	filter->action.vid_replace_ena = true;
	filter->action.pcp_dei_ena = true;
	filter->action.vid = vid;
	filter->action.pcp = pcp;
	filter->action.dei = dei;

	err = ocelot_vcap_filter_add(ocelot, filter, NULL);
	if (err)
		kfree(filter);

	return err;
}

/* Default vlan to clasify for untagged frames (may be zero) */
static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
static int ocelot_port_set_pvid(struct ocelot *ocelot, int port,
				const struct ocelot_bridge_vlan *pvid_vlan)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
@@ -475,15 +624,23 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
	 * happens automatically), but also 802.1p traffic which gets
	 * classified to VLAN 0, but that is always in our RX filter, so it
	 * would get accepted were it not for this setting.
	 *
	 * Also, we only support the bridge 802.1Q VLAN protocol, so
	 * 802.1ad-tagged frames (carrying S-Tags) should be considered
	 * 802.1Q-untagged, and also dropped.
	 */
	if (!pvid_vlan && ocelot_port->vlan_aware)
		val = ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
		      ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
		      ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA |
		      ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA;

	ocelot_rmw_gix(ocelot, val,
		       ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
		       ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
		       ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA |
		       ANA_PORT_DROP_CFG_DROP_S_TAGGED_ENA,
		       ANA_PORT_DROP_CFG, port);

	return ocelot_update_vlan_reclassify_rule(ocelot, port);
}

static struct ocelot_bridge_vlan *ocelot_bridge_vlan_find(struct ocelot *ocelot,
@@ -631,7 +788,10 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
		       ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
		       ANA_PORT_VLAN_CFG, port);

	ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
	err = ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
	if (err)
		return err;

	ocelot_port_manage_port_tag(ocelot, port);

	return 0;
@@ -684,9 +844,12 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
		return err;

	/* Default ingress vlan classification */
	if (pvid)
		ocelot_port_set_pvid(ocelot, port,
	if (pvid) {
		err = ocelot_port_set_pvid(ocelot, port,
					   ocelot_bridge_vlan_find(ocelot, vid));
		if (err)
			return err;
	}

	/* Untagged egress vlan clasification */
	ocelot_port_manage_port_tag(ocelot, port);
@@ -712,8 +875,11 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
		return err;

	/* Ingress */
	if (del_pvid)
		ocelot_port_set_pvid(ocelot, port, NULL);
	if (del_pvid) {
		err = ocelot_port_set_pvid(ocelot, port, NULL);
		if (err)
			return err;
	}

	/* Egress */
	ocelot_port_manage_port_tag(ocelot, port);
@@ -1099,6 +1265,48 @@ void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
}
EXPORT_SYMBOL(ocelot_ptp_rx_timestamp);

void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp)
			 __acquires(&ocelot->inj_lock)
{
	spin_lock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_inj_grp);

void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp)
			   __releases(&ocelot->inj_lock)
{
	spin_unlock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_inj_grp);

void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp)
			 __acquires(&ocelot->inj_lock)
{
	spin_lock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_xtr_grp);

void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp)
			   __releases(&ocelot->inj_lock)
{
	spin_unlock(&ocelot->inj_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_xtr_grp);

void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp)
			    __acquires(&ocelot->xtr_lock)
{
	spin_lock_bh(&ocelot->xtr_lock);
}
EXPORT_SYMBOL_GPL(ocelot_lock_xtr_grp_bh);

void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp)
			      __releases(&ocelot->xtr_lock)
{
	spin_unlock_bh(&ocelot->xtr_lock);
}
EXPORT_SYMBOL_GPL(ocelot_unlock_xtr_grp_bh);

int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
	u64 timestamp, src_port, len;
@@ -1109,6 +1317,8 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
	u32 val, *buf;
	int err;

	lockdep_assert_held(&ocelot->xtr_lock);

	err = ocelot_xtr_poll_xfh(ocelot, grp, xfh);
	if (err)
		return err;
@@ -1184,6 +1394,8 @@ bool ocelot_can_inject(struct ocelot *ocelot, int grp)
{
	u32 val = ocelot_read(ocelot, QS_INJ_STATUS);

	lockdep_assert_held(&ocelot->inj_lock);

	if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))))
		return false;
	if (val & QS_INJ_STATUS_WMARK_REACHED(BIT(grp)))
@@ -1193,28 +1405,55 @@ bool ocelot_can_inject(struct ocelot *ocelot, int grp)
}
EXPORT_SYMBOL(ocelot_can_inject);

void ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag)
/**
 * ocelot_ifh_set_basic - Set basic information in Injection Frame Header
 * @ifh: Pointer to Injection Frame Header memory
 * @ocelot: Switch private data structure
 * @port: Egress port number
 * @rew_op: Egress rewriter operation for PTP
 * @skb: Pointer to socket buffer (packet)
 *
 * Populate the Injection Frame Header with basic information for this skb: the
 * analyzer bypass bit, destination port, VLAN info, egress rewriter info.
 */
void ocelot_ifh_set_basic(void *ifh, struct ocelot *ocelot, int port,
			  u32 rew_op, struct sk_buff *skb)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	struct net_device *dev = skb->dev;
	u64 vlan_tci, tag_type;
	int qos_class;

	ocelot_xmit_get_vlan_info(skb, ocelot_port->bridge, &vlan_tci,
				  &tag_type);

	qos_class = netdev_get_num_tc(dev) ?
		    netdev_get_prio_tc_map(dev, skb->priority) : skb->priority;

	memset(ifh, 0, OCELOT_TAG_LEN);
	ocelot_ifh_set_bypass(ifh, 1);
	ocelot_ifh_set_src(ifh, BIT_ULL(ocelot->num_phys_ports));
	ocelot_ifh_set_dest(ifh, BIT_ULL(port));
	ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C);
	if (vlan_tag)
		ocelot_ifh_set_vlan_tci(ifh, vlan_tag);
	ocelot_ifh_set_qos_class(ifh, qos_class);
	ocelot_ifh_set_tag_type(ifh, tag_type);
	ocelot_ifh_set_vlan_tci(ifh, vlan_tci);
	if (rew_op)
		ocelot_ifh_set_rew_op(ifh, rew_op);
}
EXPORT_SYMBOL(ocelot_ifh_port_set);
EXPORT_SYMBOL(ocelot_ifh_set_basic);

void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
			      u32 rew_op, struct sk_buff *skb)
{
	u32 ifh[OCELOT_TAG_LEN / 4] = {0};
	u32 ifh[OCELOT_TAG_LEN / 4];
	unsigned int i, count, last;

	lockdep_assert_held(&ocelot->inj_lock);

	ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
			 QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);

	ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
	ocelot_ifh_set_basic(ifh, ocelot, port, rew_op, skb);

	for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
		ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp);
@@ -1247,6 +1486,8 @@ EXPORT_SYMBOL(ocelot_port_inject_frame);

void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp)
{
	lockdep_assert_held(&ocelot->xtr_lock);

	while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp))
		ocelot_read_rix(ocelot, QS_XTR_RD, grp);
}
@@ -2532,7 +2773,7 @@ int ocelot_port_set_default_prio(struct ocelot *ocelot, int port, u8 prio)
		       ANA_PORT_QOS_CFG,
		       port);

	return 0;
	return ocelot_update_vlan_reclassify_rule(ocelot, port);
}
EXPORT_SYMBOL_GPL(ocelot_port_set_default_prio);

@@ -2929,6 +3170,8 @@ int ocelot_init(struct ocelot *ocelot)
	mutex_init(&ocelot->fwd_domain_lock);
	spin_lock_init(&ocelot->ptp_clock_lock);
	spin_lock_init(&ocelot->ts_id_lock);
	spin_lock_init(&ocelot->inj_lock);
	spin_lock_init(&ocelot->xtr_lock);

	ocelot->owq = alloc_ordered_workqueue("ocelot-owq", 0);
	if (!ocelot->owq)
+1 −2
Original line number Diff line number Diff line
@@ -665,8 +665,7 @@ static int ocelot_fdma_prepare_skb(struct ocelot *ocelot, int port, u32 rew_op,

	ifh = skb_push(skb, OCELOT_TAG_LEN);
	skb_put(skb, ETH_FCS_LEN);
	memset(ifh, 0, OCELOT_TAG_LEN);
	ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
	ocelot_ifh_set_basic(ifh, ocelot, port, rew_op, skb);

	return 0;
}
+1 −0
Original line number Diff line number Diff line
@@ -695,6 +695,7 @@ static void is1_entry_set(struct ocelot *ocelot, int ix,
	vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_MC, filter->dmac_mc);
	vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_L2_BC, filter->dmac_bc);
	vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_VLAN_TAGGED, tag->tagged);
	vcap_key_bit_set(vcap, &data, VCAP_IS1_HK_TPID, tag->tpid);
	vcap_key_set(vcap, &data, VCAP_IS1_HK_VID,
		     tag->vid.value, tag->vid.mask);
	vcap_key_set(vcap, &data, VCAP_IS1_HK_PCP,
+4 −0
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
	struct ocelot *ocelot = arg;
	int grp = 0, err;

	ocelot_lock_xtr_grp(ocelot, grp);

	while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) {
		struct sk_buff *skb;

@@ -69,6 +71,8 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
	if (err < 0)
		ocelot_drain_cpu_queue(ocelot, 0);

	ocelot_unlock_xtr_grp(ocelot, grp);

	return IRQ_HANDLED;
}

Loading