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

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
......@@ -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;
}
......
This diff is collapsed.
......@@ -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;
}
......
......@@ -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,
......
......@@ -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;
}
......
......@@ -5,6 +5,8 @@
#ifndef _NET_DSA_TAG_OCELOT_H
#define _NET_DSA_TAG_OCELOT_H
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/kthread.h>
#include <linux/packing.h>
#include <linux/skbuff.h>
......@@ -273,4 +275,49 @@ static inline u32 ocelot_ptp_rew_op(struct sk_buff *skb)
return rew_op;
}
/**
* ocelot_xmit_get_vlan_info: Determine VLAN_TCI and TAG_TYPE for injected frame
* @skb: Pointer to socket buffer
* @br: Pointer to bridge device that the port is under, if any
* @vlan_tci:
* @tag_type:
*
* If the port is under a VLAN-aware bridge, remove the VLAN header from the
* payload and move it into the DSA tag, which will make the switch classify
* the packet to the bridge VLAN. Otherwise, leave the classified VLAN at zero,
* which is the pvid of standalone ports (OCELOT_STANDALONE_PVID), although not
* of VLAN-unaware bridge ports (that would be ocelot_vlan_unaware_pvid()).
* Anyway, VID 0 is fine because it is stripped on egress for these port modes,
* and source address learning is not performed for packets injected from the
* CPU anyway, so it doesn't matter that the VID is "wrong".
*/
static inline void ocelot_xmit_get_vlan_info(struct sk_buff *skb,
struct net_device *br,
u64 *vlan_tci, u64 *tag_type)
{
struct vlan_ethhdr *hdr;
u16 proto, tci;
if (!br || !br_vlan_enabled(br)) {
*vlan_tci = 0;
*tag_type = IFH_TAG_TYPE_C;
return;
}
hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
br_vlan_get_proto(br, &proto);
if (ntohs(hdr->h_vlan_proto) == proto) {
vlan_remove_tag(skb, &tci);
*vlan_tci = tci;
} else {
rcu_read_lock();
br_vlan_get_pvid_rcu(br, &tci);
rcu_read_unlock();
*vlan_tci = tci;
}
*tag_type = (proto != ETH_P_8021Q) ? IFH_TAG_TYPE_S : IFH_TAG_TYPE_C;
}
#endif
......@@ -403,14 +403,18 @@ struct dsa_switch {
*/
u32 configure_vlan_while_not_filtering:1;
/* If the switch driver always programs the CPU port as egress tagged
* despite the VLAN configuration indicating otherwise, then setting
* @untag_bridge_pvid will force the DSA receive path to pop the
* bridge's default_pvid VLAN tagged frames to offer a consistent
* behavior between a vlan_filtering=0 and vlan_filtering=1 bridge
* device.
/* Pop the default_pvid of VLAN-unaware bridge ports from tagged frames.
* DEPRECATED: Do NOT set this field in new drivers. Instead look at
* the dsa_software_vlan_untag() comments.
*/
u32 untag_bridge_pvid:1;
/* Pop the default_pvid of VLAN-aware bridge ports from tagged frames.
* Useful if the switch cannot preserve the VLAN tag as seen on the
* wire for user port ingress, and chooses to send all frames as
* VLAN-tagged to the CPU, including those which were originally
* untagged.
*/
u32 untag_vlan_aware_bridge_pvid:1;
/* Let DSA manage the FDB entries towards the
* CPU, based on the software bridge database.
......
......@@ -813,6 +813,9 @@ struct ocelot {
const u32 *const *map;
struct list_head stats_regions;
spinlock_t inj_lock;
spinlock_t xtr_lock;
u32 pool_size[OCELOT_SB_NUM][OCELOT_SB_POOL_NUM];
int packet_buffer_size;
int num_frame_refs;
......@@ -966,10 +969,17 @@ void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target,
u32 val, u32 reg, u32 offset);
/* Packet I/O */
void ocelot_lock_inj_grp(struct ocelot *ocelot, int grp);
void ocelot_unlock_inj_grp(struct ocelot *ocelot, int grp);
void ocelot_lock_xtr_grp(struct ocelot *ocelot, int grp);
void ocelot_unlock_xtr_grp(struct ocelot *ocelot, int grp);
void ocelot_lock_xtr_grp_bh(struct ocelot *ocelot, int grp);
void ocelot_unlock_xtr_grp_bh(struct ocelot *ocelot, int grp);
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb);
void ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag);
void ocelot_ifh_set_basic(void *ifh, struct ocelot *ocelot, int port,
u32 rew_op, struct sk_buff *skb);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp);
void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
......
......@@ -13,6 +13,7 @@
*/
#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_IS1_VLAN_RECLASSIFY(ocelot, port) ((ocelot)->num_phys_ports + (port))
#define OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port) (port)
#define OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port) ((ocelot)->num_phys_ports + (port))
#define OCELOT_VCAP_IS2_MRP_TRAP(ocelot) ((ocelot)->num_phys_ports * 2)
......@@ -499,6 +500,7 @@ struct ocelot_vcap_key_vlan {
struct ocelot_vcap_u8 pcp; /* PCP (3 bit) */
enum ocelot_vcap_bit dei; /* DEI */
enum ocelot_vcap_bit tagged; /* Tagged/untagged frame */
enum ocelot_vcap_bit tpid;
};
struct ocelot_vcap_key_etype {
......
......@@ -105,8 +105,9 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
p = netdev_priv(skb->dev);
if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
nskb = dsa_untag_bridge_pvid(skb);
if (unlikely(cpu_dp->ds->untag_bridge_pvid ||
cpu_dp->ds->untag_vlan_aware_bridge_pvid)) {
nskb = dsa_software_vlan_untag(skb);
if (!nskb) {
kfree_skb(skb);
return 0;
......
......@@ -44,46 +44,81 @@ static inline struct net_device *dsa_conduit_find_user(struct net_device *dev,
return NULL;
}
/* If under a bridge with vlan_filtering=0, make sure to send pvid-tagged
* frames as untagged, since the bridge will not untag them.
/**
* dsa_software_untag_vlan_aware_bridge: Software untagging for VLAN-aware bridge
* @skb: Pointer to received socket buffer (packet)
* @br: Pointer to bridge upper interface of ingress port
* @vid: Parsed VID from packet
*
* The bridge can process tagged packets. Software like STP/PTP may not. The
* bridge can also process untagged packets, to the same effect as if they were
* tagged with the PVID of the ingress port. So packets tagged with the PVID of
* the bridge port must be software-untagged, to support both use cases.
*/
static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
static inline void dsa_software_untag_vlan_aware_bridge(struct sk_buff *skb,
struct net_device *br,
u16 vid)
{
struct dsa_port *dp = dsa_user_to_port(skb->dev);
struct net_device *br = dsa_port_bridge_dev_get(dp);
struct net_device *dev = skb->dev;
struct net_device *upper_dev;
u16 vid, pvid, proto;
u16 pvid, proto;
int err;
if (!br || br_vlan_enabled(br))
return skb;
err = br_vlan_get_proto(br, &proto);
if (err)
return skb;
return;
/* Move VLAN tag from data to hwaccel */
if (!skb_vlan_tag_present(skb) && skb->protocol == htons(proto)) {
skb = skb_vlan_untag(skb);
if (!skb)
return NULL;
}
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
if (err)
return;
if (!skb_vlan_tag_present(skb))
return skb;
if (vid == pvid && skb->vlan_proto == htons(proto))
__vlan_hwaccel_clear_tag(skb);
}
vid = skb_vlan_tag_get_id(skb);
/**
* dsa_software_untag_vlan_unaware_bridge: Software untagging for VLAN-unaware bridge
* @skb: Pointer to received socket buffer (packet)
* @br: Pointer to bridge upper interface of ingress port
* @vid: Parsed VID from packet
*
* The bridge ignores all VLAN tags. Software like STP/PTP may not (it may run
* on the plain port, or on a VLAN upper interface). Maybe packets are coming
* to software as tagged with a driver-defined VID which is NOT equal to the
* PVID of the bridge port (since the bridge is VLAN-unaware, its configuration
* should NOT be committed to hardware). DSA needs a method for this private
* VID to be communicated by software to it, and if packets are tagged with it,
* software-untag them. Note: the private VID may be different per bridge, to
* support the FDB isolation use case.
*
* FIXME: this is currently implemented based on the broken assumption that
* the "private VID" used by the driver in VLAN-unaware mode is equal to the
* bridge PVID. It should not be, except for a coincidence; the bridge PVID is
* irrelevant to the data path in the VLAN-unaware mode. Thus, the VID that
* this function removes is wrong.
*
* All users of ds->untag_bridge_pvid should fix their drivers, if necessary,
* to make the two independent. Only then, if there still remains a need to
* strip the private VID from packets, then a new ds->ops->get_private_vid()
* API shall be introduced to communicate to DSA what this VID is, which needs
* to be stripped here.
*/
static inline void dsa_software_untag_vlan_unaware_bridge(struct sk_buff *skb,
struct net_device *br,
u16 vid)
{
struct net_device *upper_dev;
u16 pvid, proto;
int err;
/* We already run under an RCU read-side critical section since
* we are called from netif_receive_skb_list_internal().
*/
err = br_vlan_get_pvid_rcu(dev, &pvid);
err = br_vlan_get_proto(br, &proto);
if (err)
return skb;
return;
if (vid != pvid)
return skb;
err = br_vlan_get_pvid_rcu(skb->dev, &pvid);
if (err)
return;
if (vid != pvid || skb->vlan_proto != htons(proto))
return;
/* The sad part about attempting to untag from DSA is that we
* don't know, unless we check, if the skb will end up in
......@@ -95,10 +130,50 @@ static inline struct sk_buff *dsa_untag_bridge_pvid(struct sk_buff *skb)
* definitely keep the tag, to make sure it keeps working.
*/
upper_dev = __vlan_find_dev_deep_rcu(br, htons(proto), vid);
if (upper_dev)
if (!upper_dev)
__vlan_hwaccel_clear_tag(skb);
}
/**
* dsa_software_vlan_untag: Software VLAN untagging in DSA receive path
* @skb: Pointer to socket buffer (packet)
*
* Receive path method for switches which cannot avoid tagging all packets
* towards the CPU port. Called when ds->untag_bridge_pvid (legacy) or
* ds->untag_vlan_aware_bridge_pvid is set to true.
*
* As a side effect of this method, any VLAN tag from the skb head is moved
* to hwaccel.
*/
static inline struct sk_buff *dsa_software_vlan_untag(struct sk_buff *skb)
{
struct dsa_port *dp = dsa_user_to_port(skb->dev);
struct net_device *br = dsa_port_bridge_dev_get(dp);
u16 vid;
/* software untagging for standalone ports not yet necessary */
if (!br)
return skb;
__vlan_hwaccel_clear_tag(skb);
/* Move VLAN tag from data to hwaccel */
if (!skb_vlan_tag_present(skb)) {
skb = skb_vlan_untag(skb);
if (!skb)
return NULL;
}
if (!skb_vlan_tag_present(skb))
return skb;
vid = skb_vlan_tag_get_id(skb);
if (br_vlan_enabled(br)) {
if (dp->ds->untag_vlan_aware_bridge_pvid)
dsa_software_untag_vlan_aware_bridge(skb, br, vid);
} else {
if (dp->ds->untag_bridge_pvid)
dsa_software_untag_vlan_unaware_bridge(skb, br, vid);
}
return skb;
}
......
......@@ -8,40 +8,6 @@
#define OCELOT_NAME "ocelot"
#define SEVILLE_NAME "seville"
/* If the port is under a VLAN-aware bridge, remove the VLAN header from the
* payload and move it into the DSA tag, which will make the switch classify
* the packet to the bridge VLAN. Otherwise, leave the classified VLAN at zero,
* which is the pvid of standalone and VLAN-unaware bridge ports.
*/
static void ocelot_xmit_get_vlan_info(struct sk_buff *skb, struct dsa_port *dp,
u64 *vlan_tci, u64 *tag_type)
{
struct net_device *br = dsa_port_bridge_dev_get(dp);
struct vlan_ethhdr *hdr;
u16 proto, tci;
if (!br || !br_vlan_enabled(br)) {
*vlan_tci = 0;
*tag_type = IFH_TAG_TYPE_C;
return;
}
hdr = skb_vlan_eth_hdr(skb);
br_vlan_get_proto(br, &proto);
if (ntohs(hdr->h_vlan_proto) == proto) {
vlan_remove_tag(skb, &tci);
*vlan_tci = tci;
} else {
rcu_read_lock();
br_vlan_get_pvid_rcu(br, &tci);
rcu_read_unlock();
*vlan_tci = tci;
}
*tag_type = (proto != ETH_P_8021Q) ? IFH_TAG_TYPE_S : IFH_TAG_TYPE_C;
}
static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
__be32 ifh_prefix, void **ifh)
{
......@@ -53,7 +19,8 @@ static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
u32 rew_op = 0;
u64 qos_class;
ocelot_xmit_get_vlan_info(skb, dp, &vlan_tci, &tag_type);
ocelot_xmit_get_vlan_info(skb, dsa_port_bridge_dev_get(dp), &vlan_tci,
&tag_type);
qos_class = netdev_get_num_tc(netdev) ?
netdev_get_prio_tc_map(netdev, skb->priority) : skb->priority;
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ALL_TESTS="ping_ipv4 ping_ipv6 learning flooding vlan_deletion extern_learn"
ALL_TESTS="ping_ipv4 ping_ipv6 learning flooding vlan_deletion extern_learn other_tpid"
NUM_NETIFS=4
CHECK_TC="yes"
source lib.sh
......@@ -142,6 +142,58 @@ extern_learn()
bridge fdb del de:ad:be:ef:13:37 dev $swp1 master vlan 1 &> /dev/null
}
other_tpid()
{
local mac=de:ad:be:ef:13:37
# Test that packets with TPID 802.1ad VID 3 + TPID 802.1Q VID 5 are
# classified as untagged by a bridge with vlan_protocol 802.1Q, and
# are processed in the PVID of the ingress port (here 1). Not VID 3,
# and not VID 5.
RET=0
tc qdisc add dev $h2 clsact
tc filter add dev $h2 ingress protocol all pref 1 handle 101 \
flower dst_mac $mac action drop
ip link set $h2 promisc on
ethtool -K $h2 rx-vlan-filter off rx-vlan-stag-filter off
$MZ -q $h1 -c 1 -b $mac -a own "88:a8 00:03 81:00 00:05 08:00 aa-aa-aa-aa-aa-aa-aa-aa-aa"
sleep 1
# Match on 'self' addresses as well, for those drivers which
# do not push their learned addresses to the bridge software
# database
bridge -j fdb show $swp1 | \
jq -e ".[] | select(.mac == \"$(mac_get $h1)\") | select(.vlan == 1)" &> /dev/null
check_err $? "FDB entry was not learned when it should"
log_test "FDB entry in PVID for VLAN-tagged with other TPID"
RET=0
tc -j -s filter show dev $h2 ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
check_err $? "Packet was not forwarded when it should"
log_test "Reception of VLAN with other TPID as untagged"
bridge vlan del dev $swp1 vid 1
$MZ -q $h1 -c 1 -b $mac -a own "88:a8 00:03 81:00 00:05 08:00 aa-aa-aa-aa-aa-aa-aa-aa-aa"
sleep 1
RET=0
tc -j -s filter show dev $h2 ingress \
| jq -e ".[] | select(.options.handle == 101) \
| select(.options.actions[0].stats.packets == 1)" &> /dev/null
check_err $? "Packet was forwarded when should not"
log_test "Reception of VLAN with other TPID as untagged (no PVID)"
bridge vlan add dev $swp1 vid 1 pvid untagged
ip link set $h2 promisc off
tc qdisc del dev $h2 clsact
}
trap cleanup EXIT
setup_prepare
......
......@@ -500,6 +500,11 @@ check_err_fail()
fi
}
xfail()
{
FAIL_TO_XFAIL=yes "$@"
}
xfail_on_slow()
{
if [[ $KSFT_MACHINE_SLOW = yes ]]; then
......@@ -1113,6 +1118,39 @@ mac_get()
ip -j link show dev $if_name | jq -r '.[]["address"]'
}
ether_addr_to_u64()
{
local addr="$1"
local order="$((1 << 40))"
local val=0
local byte
addr="${addr//:/ }"
for byte in $addr; do
byte="0x$byte"
val=$((val + order * byte))
order=$((order >> 8))
done
printf "0x%x" $val
}
u64_to_ether_addr()
{
local val=$1
local byte
local i
for ((i = 40; i >= 0; i -= 8)); do
byte=$(((val & (0xff << i)) >> i))
printf "%02x" $byte
if [ $i -ne 0 ]; then
printf ":"
fi
done
}
ipv6_lladdr_get()
{
local if_name=$1
......@@ -2229,3 +2267,22 @@ absval()
echo $((v > 0 ? v : -v))
}
has_unicast_flt()
{
local dev=$1; shift
local mac_addr=$(mac_get $dev)
local tmp=$(ether_addr_to_u64 $mac_addr)
local promisc
ip link set $dev up
ip link add link $dev name macvlan-tmp type macvlan mode private
ip link set macvlan-tmp address $(u64_to_ether_addr $((tmp + 1)))
ip link set macvlan-tmp up
promisc=$(ip -j -d link show dev $dev | jq -r '.[].promiscuity')
ip link del macvlan-tmp
[[ $promisc == 1 ]] && echo "no" || echo "yes"
}
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