mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 01:23:00 -04:00
bridge: switchdev: Add forward mark support for stacked devices
switchdev_port_fwd_mark_set() is used to set the 'offload_fwd_mark' of port netdevs so that packets being flooded by the device won't be flooded twice. It works by assigning a unique identifier (the ifindex of the first bridge port) to bridge ports sharing the same parent ID. This prevents packets from being flooded twice by the same switch, but will flood packets through bridge ports belonging to a different switch. This method is problematic when stacked devices are taken into account, such as VLANs. In such cases, a physical port netdev can have upper devices being members in two different bridges, thus requiring two different 'offload_fwd_mark's to be configured on the port netdev, which is impossible. The main problem is that packet and netdev marking is performed at the physical netdev level, whereas flooding occurs between bridge ports, which are not necessarily port netdevs. Instead, packet and netdev marking should really be done in the bridge driver with the switch driver only telling it which packets it already forwarded. The bridge driver will mark such packets using the mark assigned to the ingress bridge port and will prevent the packet from being forwarded through any bridge port sharing the same mark (i.e. having the same parent ID). Remove the current switchdev 'offload_fwd_mark' implementation and instead implement the proposed method. In addition, make rocker - the sole user of the mark - use the proposed method. Signed-off-by: Ido Schimmel <[email protected]> Signed-off-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
This commit is contained in:
parent
5c326ab49e
commit
6bc506b4fb
14 changed files with 117 additions and 132 deletions
|
|
@ -20,4 +20,6 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
|
|||
|
||||
bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
|
||||
|
||||
bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
|
||||
|
||||
obj-$(CONFIG_NETFILTER) += netfilter/
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ static inline int should_deliver(const struct net_bridge_port *p,
|
|||
|
||||
vg = nbp_vlan_group_rcu(p);
|
||||
return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
|
||||
br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING;
|
||||
br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING &&
|
||||
nbp_switchdev_allowed_egress(p, skb);
|
||||
}
|
||||
|
||||
int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||
|
|
|
|||
|
|
@ -545,6 +545,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
|
|||
if (err)
|
||||
goto err5;
|
||||
|
||||
err = nbp_switchdev_mark_set(p);
|
||||
if (err)
|
||||
goto err6;
|
||||
|
||||
dev_disable_lro(dev);
|
||||
|
||||
list_add_rcu(&p->list, &br->port_list);
|
||||
|
|
@ -566,7 +570,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
|
|||
err = nbp_vlan_init(p);
|
||||
if (err) {
|
||||
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
|
||||
goto err6;
|
||||
goto err7;
|
||||
}
|
||||
|
||||
spin_lock_bh(&br->lock);
|
||||
|
|
@ -589,12 +593,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
|
|||
|
||||
return 0;
|
||||
|
||||
err6:
|
||||
err7:
|
||||
list_del_rcu(&p->list);
|
||||
br_fdb_delete_by_port(br, p, 0, 1);
|
||||
nbp_update_port_count(br);
|
||||
err6:
|
||||
netdev_upper_dev_unlink(dev, br->dev);
|
||||
|
||||
err5:
|
||||
dev->priv_flags &= ~IFF_BRIDGE_PORT;
|
||||
netdev_rx_handler_unregister(dev);
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
|
|||
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
|
||||
goto out;
|
||||
|
||||
nbp_switchdev_frame_mark(p, skb);
|
||||
|
||||
/* insert into forwarding database after filtering to avoid spoofing */
|
||||
br = p->br;
|
||||
if (p->flags & BR_LEARNING)
|
||||
|
|
|
|||
|
|
@ -251,6 +251,9 @@ struct net_bridge_port
|
|||
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
|
||||
struct net_bridge_vlan_group __rcu *vlgrp;
|
||||
#endif
|
||||
#ifdef CONFIG_NET_SWITCHDEV
|
||||
int offload_fwd_mark;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK)
|
||||
|
|
@ -359,6 +362,11 @@ struct net_bridge
|
|||
struct timer_list gc_timer;
|
||||
struct kobject *ifobj;
|
||||
u32 auto_cnt;
|
||||
|
||||
#ifdef CONFIG_NET_SWITCHDEV
|
||||
int offload_fwd_mark;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
|
||||
struct net_bridge_vlan_group __rcu *vlgrp;
|
||||
u8 vlan_enabled;
|
||||
|
|
@ -381,6 +389,10 @@ struct br_input_skb_cb {
|
|||
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
|
||||
bool vlan_filtered;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_SWITCHDEV
|
||||
int offload_fwd_mark;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb)
|
||||
|
|
@ -1034,4 +1046,29 @@ static inline int br_sysfs_addbr(struct net_device *dev) { return 0; }
|
|||
static inline void br_sysfs_delbr(struct net_device *dev) { return; }
|
||||
#endif /* CONFIG_SYSFS */
|
||||
|
||||
/* br_switchdev.c */
|
||||
#ifdef CONFIG_NET_SWITCHDEV
|
||||
int nbp_switchdev_mark_set(struct net_bridge_port *p);
|
||||
void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
|
||||
struct sk_buff *skb);
|
||||
bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
|
||||
const struct sk_buff *skb);
|
||||
#else
|
||||
static inline int nbp_switchdev_mark_set(struct net_bridge_port *p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_NET_SWITCHDEV */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
57
net/bridge/br_switchdev.c
Normal file
57
net/bridge/br_switchdev.c
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/switchdev.h>
|
||||
|
||||
#include "br_private.h"
|
||||
|
||||
static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
|
||||
/* dev is yet to be added to the port list. */
|
||||
list_for_each_entry(p, &br->port_list, list) {
|
||||
if (switchdev_port_same_parent_id(dev, p->dev))
|
||||
return p->offload_fwd_mark;
|
||||
}
|
||||
|
||||
return ++br->offload_fwd_mark;
|
||||
}
|
||||
|
||||
int nbp_switchdev_mark_set(struct net_bridge_port *p)
|
||||
{
|
||||
struct switchdev_attr attr = {
|
||||
.orig_dev = p->dev,
|
||||
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
|
||||
};
|
||||
int err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
err = switchdev_port_attr_get(p->dev, &attr);
|
||||
if (err) {
|
||||
if (err == -EOPNOTSUPP)
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
|
||||
BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
|
||||
}
|
||||
|
||||
bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
return !skb->offload_fwd_mark ||
|
||||
BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
|
||||
}
|
||||
Loading…
Reference in a new issue