Commit c3d6d0ce authored by David S. Miller's avatar David S. Miller

Merge branch 'DSA-with-VLAN-filtering-and-offloading-masters'

Vladimir Oltean says:

====================
DSA with VLAN filtering and offloading masters

This series attempts to make DSA VLANs work in the presence of a master
interface that is:
- filtering, so it drops VLANs that aren't explicitly added to its
  filter list
- offloading, so the old assumptions in the tagging code about there
  being a VLAN tag in the skb are not necessarily true anymore.

For more context:
https://lore.kernel.org/netdev/20200910150738.mwhh2i6j2qgacqev@skbuf/

This probably marks the beginning of a series of patches in which DSA
starts paying much more attention to its upper interfaces, not only for
VLAN purposes but also for address filtering and for management of the
CPU flooding domain. There was a comment from Florian on whether we
could factor some of the mlxsw logic into some common functionality, but
it doesn't look so. This seems bound to be open-coded, but frankly there
isn't a lot to it.

Changes in v2:
Applied Florian's cosmetic suggestion in patch 4/9.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 098c2fc6 88525fc0
...@@ -3038,7 +3038,11 @@ static int sja1105_setup(struct dsa_switch *ds) ...@@ -3038,7 +3038,11 @@ static int sja1105_setup(struct dsa_switch *ds)
* default, and that means vlan_filtering is 0 since they're not under * default, and that means vlan_filtering is 0 since they're not under
* a bridge, so it's safe to set up switch tagging at this time. * a bridge, so it's safe to set up switch tagging at this time.
*/ */
return sja1105_setup_8021q_tagging(ds, true); rtnl_lock();
rc = sja1105_setup_8021q_tagging(ds, true);
rtnl_unlock();
return rc;
} }
static void sja1105_teardown(struct dsa_switch *ds) static void sja1105_teardown(struct dsa_switch *ds)
...@@ -3532,6 +3536,7 @@ static int sja1105_probe(struct spi_device *spi) ...@@ -3532,6 +3536,7 @@ static int sja1105_probe(struct spi_device *spi)
return -ENOMEM; return -ENOMEM;
priv->dsa_8021q_ctx->ops = &sja1105_dsa_8021q_ops; priv->dsa_8021q_ctx->ops = &sja1105_dsa_8021q_ops;
priv->dsa_8021q_ctx->proto = htons(ETH_P_8021Q);
priv->dsa_8021q_ctx->ds = ds; priv->dsa_8021q_ctx->ds = ds;
INIT_LIST_HEAD(&priv->dsa_8021q_ctx->crosschip_links); INIT_LIST_HEAD(&priv->dsa_8021q_ctx->crosschip_links);
......
...@@ -31,6 +31,8 @@ struct dsa_8021q_context { ...@@ -31,6 +31,8 @@ struct dsa_8021q_context {
const struct dsa_8021q_ops *ops; const struct dsa_8021q_ops *ops;
struct dsa_switch *ds; struct dsa_switch *ds;
struct list_head crosschip_links; struct list_head crosschip_links;
/* EtherType of RX VID, used for filtering on master interface */
__be16 proto;
}; };
#define DSA_8021Q_N_SUBVLAN 8 #define DSA_8021Q_N_SUBVLAN 8
......
...@@ -193,11 +193,44 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) ...@@ -193,11 +193,44 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
dsa_port_set_state_now(dp, BR_STATE_FORWARDING); dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
} }
/* Must be called under rcu_read_lock() */
static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
bool vlan_filtering) bool vlan_filtering)
{ {
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
int i; int err, i;
/* VLAN awareness was off, so the question is "can we turn it on".
* We may have had 8021q uppers, those need to go. Make sure we don't
* enter an inconsistent state: deny changing the VLAN awareness state
* as long as we have 8021q uppers.
*/
if (vlan_filtering && dsa_is_user_port(ds, dp->index)) {
struct net_device *upper_dev, *slave = dp->slave;
struct net_device *br = dp->bridge_dev;
struct list_head *iter;
netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
struct bridge_vlan_info br_info;
u16 vid;
if (!is_vlan_dev(upper_dev))
continue;
vid = vlan_dev_vlan_id(upper_dev);
/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
* device, respectively the VID is not found, returning
* 0 means success, which is a failure for us here.
*/
err = br_vlan_get_info(br, vid, &br_info);
if (err == 0) {
dev_err(ds->dev, "Must remove upper %s first\n",
upper_dev->name);
return false;
}
}
}
if (!ds->vlan_filtering_is_global) if (!ds->vlan_filtering_is_global)
return true; return true;
...@@ -232,15 +265,24 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, ...@@ -232,15 +265,24 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
int err; int err;
/* bridge skips -EOPNOTSUPP, so skip the prepare phase */ if (switchdev_trans_ph_prepare(trans)) {
if (switchdev_trans_ph_prepare(trans)) bool apply;
return 0;
if (!ds->ops->port_vlan_filtering) if (!ds->ops->port_vlan_filtering)
return 0; return -EOPNOTSUPP;
if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering)) /* We are called from dsa_slave_switchdev_blocking_event(),
return -EINVAL; * which is not under rcu_read_lock(), unlike
* dsa_slave_switchdev_event().
*/
rcu_read_lock();
apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering);
rcu_read_unlock();
if (!apply)
return -EINVAL;
return 0;
}
if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
return 0; return 0;
......
...@@ -303,13 +303,36 @@ static int dsa_slave_port_attr_set(struct net_device *dev, ...@@ -303,13 +303,36 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
return ret; return ret;
} }
/* Must be called under rcu_read_lock() */
static int
dsa_slave_vlan_check_for_8021q_uppers(struct net_device *slave,
const struct switchdev_obj_port_vlan *vlan)
{
struct net_device *upper_dev;
struct list_head *iter;
netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
u16 vid;
if (!is_vlan_dev(upper_dev))
continue;
vid = vlan_dev_vlan_id(upper_dev);
if (vid >= vlan->vid_begin && vid <= vlan->vid_end)
return -EBUSY;
}
return 0;
}
static int dsa_slave_vlan_add(struct net_device *dev, static int dsa_slave_vlan_add(struct net_device *dev,
const struct switchdev_obj *obj, const struct switchdev_obj *obj,
struct switchdev_trans *trans) struct switchdev_trans *trans)
{ {
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_port *dp = dsa_slave_to_port(dev);
struct switchdev_obj_port_vlan vlan; struct switchdev_obj_port_vlan vlan;
int err; int vid, err;
if (obj->orig_dev != dev) if (obj->orig_dev != dev)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -319,6 +342,17 @@ static int dsa_slave_vlan_add(struct net_device *dev, ...@@ -319,6 +342,17 @@ static int dsa_slave_vlan_add(struct net_device *dev,
vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj); vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj);
/* Deny adding a bridge VLAN when there is already an 802.1Q upper with
* the same VID.
*/
if (trans->ph_prepare && br_vlan_enabled(dp->bridge_dev)) {
rcu_read_lock();
err = dsa_slave_vlan_check_for_8021q_uppers(dev, &vlan);
rcu_read_unlock();
if (err)
return err;
}
err = dsa_port_vlan_add(dp, &vlan, trans); err = dsa_port_vlan_add(dp, &vlan, trans);
if (err) if (err)
return err; return err;
...@@ -333,6 +367,12 @@ static int dsa_slave_vlan_add(struct net_device *dev, ...@@ -333,6 +367,12 @@ static int dsa_slave_vlan_add(struct net_device *dev,
if (err) if (err)
return err; return err;
for (vid = vlan.vid_begin; vid <= vlan.vid_end; vid++) {
err = vlan_vid_add(master, htons(ETH_P_8021Q), vid);
if (err)
return err;
}
return 0; return 0;
} }
...@@ -376,7 +416,10 @@ static int dsa_slave_port_obj_add(struct net_device *dev, ...@@ -376,7 +416,10 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
static int dsa_slave_vlan_del(struct net_device *dev, static int dsa_slave_vlan_del(struct net_device *dev,
const struct switchdev_obj *obj) const struct switchdev_obj *obj)
{ {
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_port *dp = dsa_slave_to_port(dev);
struct switchdev_obj_port_vlan *vlan;
int vid, err;
if (obj->orig_dev != dev) if (obj->orig_dev != dev)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -384,10 +427,19 @@ static int dsa_slave_vlan_del(struct net_device *dev, ...@@ -384,10 +427,19 @@ static int dsa_slave_vlan_del(struct net_device *dev,
if (dsa_port_skip_vlan_configuration(dp)) if (dsa_port_skip_vlan_configuration(dp))
return 0; return 0;
vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
/* Do not deprogram the CPU port as it may be shared with other user /* Do not deprogram the CPU port as it may be shared with other user
* ports which can be members of this VLAN as well. * ports which can be members of this VLAN as well.
*/ */
return dsa_port_vlan_del(dp, SWITCHDEV_OBJ_PORT_VLAN(obj)); err = dsa_port_vlan_del(dp, vlan);
if (err)
return err;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
vlan_vid_del(master, htons(ETH_P_8021Q), vid);
return 0;
} }
static int dsa_slave_port_obj_del(struct net_device *dev, static int dsa_slave_port_obj_del(struct net_device *dev,
...@@ -1232,6 +1284,7 @@ static int dsa_slave_get_ts_info(struct net_device *dev, ...@@ -1232,6 +1284,7 @@ static int dsa_slave_get_ts_info(struct net_device *dev,
static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
u16 vid) u16 vid)
{ {
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_port *dp = dsa_slave_to_port(dev);
struct switchdev_obj_port_vlan vlan = { struct switchdev_obj_port_vlan vlan = {
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
...@@ -1240,26 +1293,9 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, ...@@ -1240,26 +1293,9 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
/* This API only allows programming tagged, non-PVID VIDs */ /* This API only allows programming tagged, non-PVID VIDs */
.flags = 0, .flags = 0,
}; };
struct bridge_vlan_info info;
struct switchdev_trans trans; struct switchdev_trans trans;
int ret; int ret;
/* Check for a possible bridge VLAN entry now since there is no
* need to emulate the switchdev prepare + commit phase.
*/
if (dp->bridge_dev) {
if (dsa_port_skip_vlan_configuration(dp))
return 0;
/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
* device, respectively the VID is not found, returning
* 0 means success, which is a failure for us here.
*/
ret = br_vlan_get_info(dp->bridge_dev, vid, &info);
if (ret == 0)
return -EBUSY;
}
/* User port... */ /* User port... */
trans.ph_prepare = true; trans.ph_prepare = true;
ret = dsa_port_vlan_add(dp, &vlan, &trans); ret = dsa_port_vlan_add(dp, &vlan, &trans);
...@@ -1282,12 +1318,13 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, ...@@ -1282,12 +1318,13 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
if (ret) if (ret)
return ret; return ret;
return 0; return vlan_vid_add(master, proto, vid);
} }
static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
u16 vid) u16 vid)
{ {
struct net_device *master = dsa_slave_to_master(dev);
struct dsa_port *dp = dsa_slave_to_port(dev); struct dsa_port *dp = dsa_slave_to_port(dev);
struct switchdev_obj_port_vlan vlan = { struct switchdev_obj_port_vlan vlan = {
.vid_begin = vid, .vid_begin = vid,
...@@ -1295,29 +1332,18 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, ...@@ -1295,29 +1332,18 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
/* This API only allows programming tagged, non-PVID VIDs */ /* This API only allows programming tagged, non-PVID VIDs */
.flags = 0, .flags = 0,
}; };
struct bridge_vlan_info info; int err;
int ret;
/* Check for a possible bridge VLAN entry now since there is no
* need to emulate the switchdev prepare + commit phase.
*/
if (dp->bridge_dev) {
if (dsa_port_skip_vlan_configuration(dp))
return 0;
/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
* device, respectively the VID is not found, returning
* 0 means success, which is a failure for us here.
*/
ret = br_vlan_get_info(dp->bridge_dev, vid, &info);
if (ret == 0)
return -EBUSY;
}
/* Do not deprogram the CPU port as it may be shared with other user /* Do not deprogram the CPU port as it may be shared with other user
* ports which can be members of this VLAN as well. * ports which can be members of this VLAN as well.
*/ */
return dsa_port_vlan_del(dp, &vlan); err = dsa_port_vlan_del(dp, &vlan);
if (err)
return err;
vlan_vid_del(master, proto, vid);
return 0;
} }
struct dsa_hw_port { struct dsa_hw_port {
...@@ -1895,9 +1921,9 @@ static int dsa_slave_changeupper(struct net_device *dev, ...@@ -1895,9 +1921,9 @@ static int dsa_slave_changeupper(struct net_device *dev,
return err; return err;
} }
static int dsa_slave_upper_vlan_check(struct net_device *dev, static int
struct netdev_notifier_changeupper_info * dsa_prevent_bridging_8021q_upper(struct net_device *dev,
info) struct netdev_notifier_changeupper_info *info)
{ {
struct netlink_ext_ack *ext_ack; struct netlink_ext_ack *ext_ack;
struct net_device *slave; struct net_device *slave;
...@@ -1927,14 +1953,56 @@ static int dsa_slave_upper_vlan_check(struct net_device *dev, ...@@ -1927,14 +1953,56 @@ static int dsa_slave_upper_vlan_check(struct net_device *dev,
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static int
dsa_slave_check_8021q_upper(struct net_device *dev,
struct netdev_notifier_changeupper_info *info)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
struct net_device *br = dp->bridge_dev;
struct bridge_vlan_info br_info;
struct netlink_ext_ack *extack;
int err = NOTIFY_DONE;
u16 vid;
if (!br || !br_vlan_enabled(br))
return NOTIFY_DONE;
extack = netdev_notifier_info_to_extack(&info->info);
vid = vlan_dev_vlan_id(info->upper_dev);
/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
* device, respectively the VID is not found, returning
* 0 means success, which is a failure for us here.
*/
err = br_vlan_get_info(br, vid, &br_info);
if (err == 0) {
NL_SET_ERR_MSG_MOD(extack,
"This VLAN is already configured by the bridge");
return notifier_from_errno(-EBUSY);
}
return NOTIFY_DONE;
}
static int dsa_slave_netdevice_event(struct notifier_block *nb, static int dsa_slave_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
if (event == NETDEV_CHANGEUPPER) { switch (event) {
case NETDEV_PRECHANGEUPPER: {
struct netdev_notifier_changeupper_info *info = ptr;
if (!dsa_slave_dev_check(dev))
return dsa_prevent_bridging_8021q_upper(dev, ptr);
if (is_vlan_dev(info->upper_dev))
return dsa_slave_check_8021q_upper(dev, ptr);
break;
}
case NETDEV_CHANGEUPPER:
if (!dsa_slave_dev_check(dev)) if (!dsa_slave_dev_check(dev))
return dsa_slave_upper_vlan_check(dev, ptr); return NOTIFY_DONE;
return dsa_slave_changeupper(dev, ptr); return dsa_slave_changeupper(dev, ptr);
} }
......
...@@ -232,43 +232,6 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds, ...@@ -232,43 +232,6 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds,
return 0; return 0;
} }
static int dsa_port_vlan_device_check(struct net_device *vlan_dev,
int vlan_dev_vid,
void *arg)
{
struct switchdev_obj_port_vlan *vlan = arg;
u16 vid;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
if (vid == vlan_dev_vid)
return -EBUSY;
}
return 0;
}
static int dsa_port_vlan_check(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
const struct dsa_port *dp = dsa_to_port(ds, port);
int err = 0;
/* Device is not bridged, let it proceed with the VLAN device
* creation.
*/
if (!dp->bridge_dev)
return err;
/* dsa_slave_vlan_rx_{add,kill}_vid() cannot use the prepare phase and
* already checks whether there is an overlapping bridge VLAN entry
* with the same VID, so here we only need to check that if we are
* adding a bridge VLAN entry there is not an overlapping VLAN device
* claiming that VID.
*/
return vlan_for_each(dp->slave, dsa_port_vlan_device_check,
(void *)vlan);
}
static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port, static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
struct dsa_notifier_vlan_info *info) struct dsa_notifier_vlan_info *info)
{ {
...@@ -291,10 +254,6 @@ static int dsa_switch_vlan_prepare(struct dsa_switch *ds, ...@@ -291,10 +254,6 @@ static int dsa_switch_vlan_prepare(struct dsa_switch *ds,
for (port = 0; port < ds->num_ports; port++) { for (port = 0; port < ds->num_ports; port++) {
if (dsa_switch_vlan_match(ds, port, info)) { if (dsa_switch_vlan_match(ds, port, info)) {
err = dsa_port_vlan_check(ds, port, info->vlan);
if (err)
return err;
err = ds->ops->port_vlan_prepare(ds, port, info->vlan); err = ds->ops->port_vlan_prepare(ds, port, info->vlan);
if (err) if (err)
return err; return err;
......
...@@ -215,7 +215,8 @@ static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port, ...@@ -215,7 +215,8 @@ static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port,
int upstream = dsa_upstream_port(ctx->ds, port); int upstream = dsa_upstream_port(ctx->ds, port);
u16 rx_vid = dsa_8021q_rx_vid(ctx->ds, port); u16 rx_vid = dsa_8021q_rx_vid(ctx->ds, port);
u16 tx_vid = dsa_8021q_tx_vid(ctx->ds, port); u16 tx_vid = dsa_8021q_tx_vid(ctx->ds, port);
int i, err; struct net_device *master;
int i, err, subvlan;
/* The CPU port is implicitly configured by /* The CPU port is implicitly configured by
* configuring the front-panel ports * configuring the front-panel ports
...@@ -223,6 +224,8 @@ static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port, ...@@ -223,6 +224,8 @@ static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port,
if (!dsa_is_user_port(ctx->ds, port)) if (!dsa_is_user_port(ctx->ds, port))
return 0; return 0;
master = dsa_to_port(ctx->ds, port)->cpu_dp->master;
/* Add this user port's RX VID to the membership list of all others /* Add this user port's RX VID to the membership list of all others
* (including itself). This is so that bridging will not be hindered. * (including itself). This is so that bridging will not be hindered.
* L2 forwarding rules still take precedence when there are no VLAN * L2 forwarding rules still take precedence when there are no VLAN
...@@ -261,6 +264,19 @@ static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port, ...@@ -261,6 +264,19 @@ static int dsa_8021q_setup_port(struct dsa_8021q_context *ctx, int port,
return err; return err;
} }
/* Add to the master's RX filter not only @rx_vid, but in fact
* the entire subvlan range, just in case this DSA switch might
* want to use sub-VLANs.
*/
for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) {
u16 vid = dsa_8021q_rx_vid_subvlan(ctx->ds, port, subvlan);
if (enabled)
vlan_vid_add(master, ctx->proto, vid);
else
vlan_vid_del(master, ctx->proto, vid);
}
/* Finally apply the TX VID on this port and on the CPU port */ /* Finally apply the TX VID on this port and on the CPU port */
err = dsa_8021q_vid_apply(ctx, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED, err = dsa_8021q_vid_apply(ctx, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED,
enabled); enabled);
...@@ -285,6 +301,8 @@ int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled) ...@@ -285,6 +301,8 @@ int dsa_8021q_setup(struct dsa_8021q_context *ctx, bool enabled)
{ {
int rc, port; int rc, port;
ASSERT_RTNL();
for (port = 0; port < ctx->ds->num_ports; port++) { for (port = 0; port < ctx->ds->num_ports; port++) {
rc = dsa_8021q_setup_port(ctx, port, enabled); rc = dsa_8021q_setup_port(ctx, port, enabled);
if (rc < 0) { if (rc < 0) {
......
...@@ -72,14 +72,21 @@ static inline bool sja1105_is_meta_frame(const struct sk_buff *skb) ...@@ -72,14 +72,21 @@ static inline bool sja1105_is_meta_frame(const struct sk_buff *skb)
static bool sja1105_can_use_vlan_as_tags(const struct sk_buff *skb) static bool sja1105_can_use_vlan_as_tags(const struct sk_buff *skb)
{ {
struct vlan_ethhdr *hdr = vlan_eth_hdr(skb); struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
u16 vlan_tci;
if (hdr->h_vlan_proto == htons(ETH_P_SJA1105)) if (hdr->h_vlan_proto == htons(ETH_P_SJA1105))
return true; return true;
if (hdr->h_vlan_proto != htons(ETH_P_8021Q)) if (hdr->h_vlan_proto != htons(ETH_P_8021Q) &&
!skb_vlan_tag_present(skb))
return false; return false;
return vid_is_dsa_8021q(ntohs(hdr->h_vlan_TCI) & VLAN_VID_MASK); if (skb_vlan_tag_present(skb))
vlan_tci = skb_vlan_tag_get(skb);
else
vlan_tci = ntohs(hdr->h_vlan_TCI);
return vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK);
} }
/* This is the first time the tagger sees the frame on RX. /* This is the first time the tagger sees the frame on RX.
...@@ -283,7 +290,8 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -283,7 +290,8 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
hdr = eth_hdr(skb); hdr = eth_hdr(skb);
tpid = ntohs(hdr->h_proto); tpid = ntohs(hdr->h_proto);
is_tagged = (tpid == ETH_P_SJA1105 || tpid == ETH_P_8021Q); is_tagged = (tpid == ETH_P_SJA1105 || tpid == ETH_P_8021Q ||
skb_vlan_tag_present(skb));
is_link_local = sja1105_is_link_local(skb); is_link_local = sja1105_is_link_local(skb);
is_meta = sja1105_is_meta_frame(skb); is_meta = sja1105_is_meta_frame(skb);
...@@ -292,7 +300,12 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -292,7 +300,12 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
if (is_tagged) { if (is_tagged) {
/* Normal traffic path. */ /* Normal traffic path. */
skb_push_rcsum(skb, ETH_HLEN); skb_push_rcsum(skb, ETH_HLEN);
__skb_vlan_pop(skb, &tci); if (skb_vlan_tag_present(skb)) {
tci = skb_vlan_tag_get(skb);
__vlan_hwaccel_clear_tag(skb);
} else {
__skb_vlan_pop(skb, &tci);
}
skb_pull_rcsum(skb, ETH_HLEN); skb_pull_rcsum(skb, ETH_HLEN);
skb_reset_network_header(skb); skb_reset_network_header(skb);
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
......
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