aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIdo Schimmel <idosch@mellanox.com>2016-08-25 12:42:37 -0400
committerDavid S. Miller <davem@davemloft.net>2016-08-26 16:13:36 -0400
commit6bc506b4fb065eac3d89ca1ce37082e174493d9e (patch)
treeb2ce45bcdf47c388d9d0977af7bf511685a00e6e
parent5c326ab49e5ee014ba5314c076fe9b93fd8b0406 (diff)
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 <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/networking/switchdev.txt13
-rw-r--r--drivers/net/ethernet/rocker/rocker_main.c2
-rw-r--r--drivers/net/ethernet/rocker/rocker_ofdpa.c4
-rw-r--r--include/linux/netdevice.h5
-rw-r--r--include/linux/skbuff.h13
-rw-r--r--include/net/switchdev.h6
-rw-r--r--net/bridge/Makefile2
-rw-r--r--net/bridge/br_forward.c3
-rw-r--r--net/bridge/br_if.c10
-rw-r--r--net/bridge/br_input.c2
-rw-r--r--net/bridge/br_private.h37
-rw-r--r--net/bridge/br_switchdev.c57
-rw-r--r--net/core/dev.c10
-rw-r--r--net/switchdev/switchdev.c85
14 files changed, 117 insertions, 132 deletions
diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt
index 31c39115834d..44235e83799b 100644
--- a/Documentation/networking/switchdev.txt
+++ b/Documentation/networking/switchdev.txt
@@ -283,15 +283,10 @@ be sent to the port netdev for processing by the bridge driver. The
283bridge should not reflood the packet to the same ports the device flooded, 283bridge should not reflood the packet to the same ports the device flooded,
284otherwise there will be duplicate packets on the wire. 284otherwise there will be duplicate packets on the wire.
285 285
286To avoid duplicate packets, the device/driver should mark a packet as already 286To avoid duplicate packets, the switch driver should mark a packet as already
287forwarded using skb->offload_fwd_mark. The same mark is set on the device 287forwarded by setting the skb->offload_fwd_mark bit. The bridge driver will mark
288ports in the domain using dev->offload_fwd_mark. If the skb->offload_fwd_mark 288the skb using the ingress bridge port's mark and prevent it from being forwarded
289is non-zero and matches the forwarding egress port's dev->skb_mark, the kernel 289through any bridge port with the same mark.
290will drop the skb right before transmit on the egress port, with the
291understanding that the device already forwarded the packet on same egress port.
292The driver can use switchdev_port_fwd_mark_set() to set a globally unique mark
293for port's dev->offload_fwd_mark, based on the port's parent ID (switch ID) and
294a group ifindex.
295 290
296It is possible for the switch device to not handle flooding and push the 291It is possible for the switch device to not handle flooding and push the
297packets up to the bridge driver for flooding. This is not ideal as the number 292packets up to the bridge driver for flooding. This is not ideal as the number
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index f0b09b05ed3f..1f0c08602eba 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -2412,7 +2412,7 @@ static int rocker_port_rx_proc(const struct rocker *rocker,
2412 skb->protocol = eth_type_trans(skb, rocker_port->dev); 2412 skb->protocol = eth_type_trans(skb, rocker_port->dev);
2413 2413
2414 if (rx_flags & ROCKER_RX_FLAGS_FWD_OFFLOAD) 2414 if (rx_flags & ROCKER_RX_FLAGS_FWD_OFFLOAD)
2415 skb->offload_fwd_mark = rocker_port->dev->offload_fwd_mark; 2415 skb->offload_fwd_mark = 1;
2416 2416
2417 rocker_port->dev->stats.rx_packets++; 2417 rocker_port->dev->stats.rx_packets++;
2418 rocker_port->dev->stats.rx_bytes += skb->len; 2418 rocker_port->dev->stats.rx_bytes += skb->len;
diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c
index 1ca796316173..fcad907baecf 100644
--- a/drivers/net/ethernet/rocker/rocker_ofdpa.c
+++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c
@@ -2558,7 +2558,6 @@ static int ofdpa_port_init(struct rocker_port *rocker_port)
2558 struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 2558 struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
2559 int err; 2559 int err;
2560 2560
2561 switchdev_port_fwd_mark_set(ofdpa_port->dev, NULL, false);
2562 rocker_port_set_learning(rocker_port, 2561 rocker_port_set_learning(rocker_port,
2563 !!(ofdpa_port->brport_flags & BR_LEARNING)); 2562 !!(ofdpa_port->brport_flags & BR_LEARNING));
2564 2563
@@ -2817,7 +2816,6 @@ static int ofdpa_port_bridge_join(struct ofdpa_port *ofdpa_port,
2817 ofdpa_port_internal_vlan_id_get(ofdpa_port, bridge->ifindex); 2816 ofdpa_port_internal_vlan_id_get(ofdpa_port, bridge->ifindex);
2818 2817
2819 ofdpa_port->bridge_dev = bridge; 2818 ofdpa_port->bridge_dev = bridge;
2820 switchdev_port_fwd_mark_set(ofdpa_port->dev, bridge, true);
2821 2819
2822 return ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0); 2820 return ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0);
2823} 2821}
@@ -2836,8 +2834,6 @@ static int ofdpa_port_bridge_leave(struct ofdpa_port *ofdpa_port)
2836 ofdpa_port_internal_vlan_id_get(ofdpa_port, 2834 ofdpa_port_internal_vlan_id_get(ofdpa_port,
2837 ofdpa_port->dev->ifindex); 2835 ofdpa_port->dev->ifindex);
2838 2836
2839 switchdev_port_fwd_mark_set(ofdpa_port->dev, ofdpa_port->bridge_dev,
2840 false);
2841 ofdpa_port->bridge_dev = NULL; 2837 ofdpa_port->bridge_dev = NULL;
2842 2838
2843 err = ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0); 2839 err = ofdpa_port_vlan_add(ofdpa_port, NULL, OFDPA_UNTAGGED_VID, 0);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 794bb0733799..d122be9345c7 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1562,8 +1562,6 @@ enum netdev_priv_flags {
1562 * 1562 *
1563 * @xps_maps: XXX: need comments on this one 1563 * @xps_maps: XXX: need comments on this one
1564 * 1564 *
1565 * @offload_fwd_mark: Offload device fwding mark
1566 *
1567 * @watchdog_timeo: Represents the timeout that is used by 1565 * @watchdog_timeo: Represents the timeout that is used by
1568 * the watchdog (see dev_watchdog()) 1566 * the watchdog (see dev_watchdog())
1569 * @watchdog_timer: List of timers 1567 * @watchdog_timer: List of timers
@@ -1814,9 +1812,6 @@ struct net_device {
1814#ifdef CONFIG_NET_CLS_ACT 1812#ifdef CONFIG_NET_CLS_ACT
1815 struct tcf_proto __rcu *egress_cl_list; 1813 struct tcf_proto __rcu *egress_cl_list;
1816#endif 1814#endif
1817#ifdef CONFIG_NET_SWITCHDEV
1818 u32 offload_fwd_mark;
1819#endif
1820 1815
1821 /* These may be needed for future network-power-down code. */ 1816 /* These may be needed for future network-power-down code. */
1822 struct timer_list watchdog_timer; 1817 struct timer_list watchdog_timer;
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 7047448e8129..cfb7219be665 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -612,7 +612,6 @@ static inline bool skb_mstamp_after(const struct skb_mstamp *t1,
612 * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS 612 * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS
613 * @napi_id: id of the NAPI struct this skb came from 613 * @napi_id: id of the NAPI struct this skb came from
614 * @secmark: security marking 614 * @secmark: security marking
615 * @offload_fwd_mark: fwding offload mark
616 * @mark: Generic packet mark 615 * @mark: Generic packet mark
617 * @vlan_proto: vlan encapsulation protocol 616 * @vlan_proto: vlan encapsulation protocol
618 * @vlan_tci: vlan tag control information 617 * @vlan_tci: vlan tag control information
@@ -730,7 +729,10 @@ struct sk_buff {
730 __u8 ipvs_property:1; 729 __u8 ipvs_property:1;
731 __u8 inner_protocol_type:1; 730 __u8 inner_protocol_type:1;
732 __u8 remcsum_offload:1; 731 __u8 remcsum_offload:1;
733 /* 3 or 5 bit hole */ 732#ifdef CONFIG_NET_SWITCHDEV
733 __u8 offload_fwd_mark:1;
734#endif
735 /* 2, 4 or 5 bit hole */
734 736
735#ifdef CONFIG_NET_SCHED 737#ifdef CONFIG_NET_SCHED
736 __u16 tc_index; /* traffic control index */ 738 __u16 tc_index; /* traffic control index */
@@ -757,14 +759,9 @@ struct sk_buff {
757 unsigned int sender_cpu; 759 unsigned int sender_cpu;
758 }; 760 };
759#endif 761#endif
760 union {
761#ifdef CONFIG_NETWORK_SECMARK 762#ifdef CONFIG_NETWORK_SECMARK
762 __u32 secmark; 763 __u32 secmark;
763#endif 764#endif
764#ifdef CONFIG_NET_SWITCHDEV
765 __u32 offload_fwd_mark;
766#endif
767 };
768 765
769 union { 766 union {
770 __u32 mark; 767 __u32 mark;
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 62f6a967a1b7..82f5e0462021 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -347,12 +347,6 @@ static inline int switchdev_port_fdb_dump(struct sk_buff *skb,
347 return idx; 347 return idx;
348} 348}
349 349
350static inline void switchdev_port_fwd_mark_set(struct net_device *dev,
351 struct net_device *group_dev,
352 bool joining)
353{
354}
355
356static inline bool switchdev_port_same_parent_id(struct net_device *a, 350static inline bool switchdev_port_same_parent_id(struct net_device *a,
357 struct net_device *b) 351 struct net_device *b)
358{ 352{
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index a1cda5d4718d..0aefc011b668 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -20,4 +20,6 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
20 20
21bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o 21bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
22 22
23bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
24
23obj-$(CONFIG_NETFILTER) += netfilter/ 25obj-$(CONFIG_NETFILTER) += netfilter/
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 63a83d8d7da3..32a02de39cd2 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -29,7 +29,8 @@ static inline int should_deliver(const struct net_bridge_port *p,
29 29
30 vg = nbp_vlan_group_rcu(p); 30 vg = nbp_vlan_group_rcu(p);
31 return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) && 31 return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
32 br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING; 32 br_allowed_egress(vg, skb) && p->state == BR_STATE_FORWARDING &&
33 nbp_switchdev_allowed_egress(p, skb);
33} 34}
34 35
35int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) 36int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f2fede05d32c..1da3221845f1 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -545,6 +545,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
545 if (err) 545 if (err)
546 goto err5; 546 goto err5;
547 547
548 err = nbp_switchdev_mark_set(p);
549 if (err)
550 goto err6;
551
548 dev_disable_lro(dev); 552 dev_disable_lro(dev);
549 553
550 list_add_rcu(&p->list, &br->port_list); 554 list_add_rcu(&p->list, &br->port_list);
@@ -566,7 +570,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
566 err = nbp_vlan_init(p); 570 err = nbp_vlan_init(p);
567 if (err) { 571 if (err) {
568 netdev_err(dev, "failed to initialize vlan filtering on this port\n"); 572 netdev_err(dev, "failed to initialize vlan filtering on this port\n");
569 goto err6; 573 goto err7;
570 } 574 }
571 575
572 spin_lock_bh(&br->lock); 576 spin_lock_bh(&br->lock);
@@ -589,12 +593,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
589 593
590 return 0; 594 return 0;
591 595
592err6: 596err7:
593 list_del_rcu(&p->list); 597 list_del_rcu(&p->list);
594 br_fdb_delete_by_port(br, p, 0, 1); 598 br_fdb_delete_by_port(br, p, 0, 1);
595 nbp_update_port_count(br); 599 nbp_update_port_count(br);
600err6:
596 netdev_upper_dev_unlink(dev, br->dev); 601 netdev_upper_dev_unlink(dev, br->dev);
597
598err5: 602err5:
599 dev->priv_flags &= ~IFF_BRIDGE_PORT; 603 dev->priv_flags &= ~IFF_BRIDGE_PORT;
600 netdev_rx_handler_unregister(dev); 604 netdev_rx_handler_unregister(dev);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 8e486203d133..3132cfc80e9d 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -145,6 +145,8 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
145 if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid)) 145 if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), skb, &vid))
146 goto out; 146 goto out;
147 147
148 nbp_switchdev_frame_mark(p, skb);
149
148 /* insert into forwarding database after filtering to avoid spoofing */ 150 /* insert into forwarding database after filtering to avoid spoofing */
149 br = p->br; 151 br = p->br;
150 if (p->flags & BR_LEARNING) 152 if (p->flags & BR_LEARNING)
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index aac2a6e6b008..2379b2b865c9 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -251,6 +251,9 @@ struct net_bridge_port
251#ifdef CONFIG_BRIDGE_VLAN_FILTERING 251#ifdef CONFIG_BRIDGE_VLAN_FILTERING
252 struct net_bridge_vlan_group __rcu *vlgrp; 252 struct net_bridge_vlan_group __rcu *vlgrp;
253#endif 253#endif
254#ifdef CONFIG_NET_SWITCHDEV
255 int offload_fwd_mark;
256#endif
254}; 257};
255 258
256#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) 259#define br_auto_port(p) ((p)->flags & BR_AUTO_MASK)
@@ -359,6 +362,11 @@ struct net_bridge
359 struct timer_list gc_timer; 362 struct timer_list gc_timer;
360 struct kobject *ifobj; 363 struct kobject *ifobj;
361 u32 auto_cnt; 364 u32 auto_cnt;
365
366#ifdef CONFIG_NET_SWITCHDEV
367 int offload_fwd_mark;
368#endif
369
362#ifdef CONFIG_BRIDGE_VLAN_FILTERING 370#ifdef CONFIG_BRIDGE_VLAN_FILTERING
363 struct net_bridge_vlan_group __rcu *vlgrp; 371 struct net_bridge_vlan_group __rcu *vlgrp;
364 u8 vlan_enabled; 372 u8 vlan_enabled;
@@ -381,6 +389,10 @@ struct br_input_skb_cb {
381#ifdef CONFIG_BRIDGE_VLAN_FILTERING 389#ifdef CONFIG_BRIDGE_VLAN_FILTERING
382 bool vlan_filtered; 390 bool vlan_filtered;
383#endif 391#endif
392
393#ifdef CONFIG_NET_SWITCHDEV
394 int offload_fwd_mark;
395#endif
384}; 396};
385 397
386#define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb) 398#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; }
1034static inline void br_sysfs_delbr(struct net_device *dev) { return; } 1046static inline void br_sysfs_delbr(struct net_device *dev) { return; }
1035#endif /* CONFIG_SYSFS */ 1047#endif /* CONFIG_SYSFS */
1036 1048
1049/* br_switchdev.c */
1050#ifdef CONFIG_NET_SWITCHDEV
1051int nbp_switchdev_mark_set(struct net_bridge_port *p);
1052void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
1053 struct sk_buff *skb);
1054bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
1055 const struct sk_buff *skb);
1056#else
1057static inline int nbp_switchdev_mark_set(struct net_bridge_port *p)
1058{
1059 return 0;
1060}
1061
1062static inline void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
1063 struct sk_buff *skb)
1064{
1065}
1066
1067static inline bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
1068 const struct sk_buff *skb)
1069{
1070 return true;
1071}
1072#endif /* CONFIG_NET_SWITCHDEV */
1073
1037#endif 1074#endif
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
new file mode 100644
index 000000000000..f4097b900de1
--- /dev/null
+++ b/net/bridge/br_switchdev.c
@@ -0,0 +1,57 @@
1#include <linux/kernel.h>
2#include <linux/list.h>
3#include <linux/netdevice.h>
4#include <linux/rtnetlink.h>
5#include <linux/skbuff.h>
6#include <net/switchdev.h>
7
8#include "br_private.h"
9
10static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
11{
12 struct net_bridge_port *p;
13
14 /* dev is yet to be added to the port list. */
15 list_for_each_entry(p, &br->port_list, list) {
16 if (switchdev_port_same_parent_id(dev, p->dev))
17 return p->offload_fwd_mark;
18 }
19
20 return ++br->offload_fwd_mark;
21}
22
23int nbp_switchdev_mark_set(struct net_bridge_port *p)
24{
25 struct switchdev_attr attr = {
26 .orig_dev = p->dev,
27 .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
28 };
29 int err;
30
31 ASSERT_RTNL();
32
33 err = switchdev_port_attr_get(p->dev, &attr);
34 if (err) {
35 if (err == -EOPNOTSUPP)
36 return 0;
37 return err;
38 }
39
40 p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
41
42 return 0;
43}
44
45void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
46 struct sk_buff *skb)
47{
48 if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
49 BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
50}
51
52bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
53 const struct sk_buff *skb)
54{
55 return !skb->offload_fwd_mark ||
56 BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
57}
diff --git a/net/core/dev.c b/net/core/dev.c
index 7feae74ca928..1d5c6dda1988 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3355,16 +3355,6 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
3355 else 3355 else
3356 skb_dst_force(skb); 3356 skb_dst_force(skb);
3357 3357
3358#ifdef CONFIG_NET_SWITCHDEV
3359 /* Don't forward if offload device already forwarded */
3360 if (skb->offload_fwd_mark &&
3361 skb->offload_fwd_mark == dev->offload_fwd_mark) {
3362 consume_skb(skb);
3363 rc = NET_XMIT_SUCCESS;
3364 goto out;
3365 }
3366#endif
3367
3368 txq = netdev_pick_tx(dev, skb, accel_priv); 3358 txq = netdev_pick_tx(dev, skb, accel_priv);
3369 q = rcu_dereference_bh(txq->qdisc); 3359 q = rcu_dereference_bh(txq->qdisc);
3370 3360
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 2c683f24d557..1031a0327fff 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -1305,88 +1305,3 @@ bool switchdev_port_same_parent_id(struct net_device *a,
1305 return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid); 1305 return netdev_phys_item_id_same(&a_attr.u.ppid, &b_attr.u.ppid);
1306} 1306}
1307EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); 1307EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id);
1308
1309static u32 switchdev_port_fwd_mark_get(struct net_device *dev,
1310 struct net_device *group_dev)
1311{
1312 struct net_device *lower_dev;
1313 struct list_head *iter;
1314
1315 netdev_for_each_lower_dev(group_dev, lower_dev, iter) {
1316 if (lower_dev == dev)
1317 continue;
1318 if (switchdev_port_same_parent_id(dev, lower_dev))
1319 return lower_dev->offload_fwd_mark;
1320 return switchdev_port_fwd_mark_get(dev, lower_dev);
1321 }
1322
1323 return dev->ifindex;
1324}
1325
1326static void switchdev_port_fwd_mark_reset(struct net_device *group_dev,
1327 u32 old_mark, u32 *reset_mark)
1328{
1329 struct net_device *lower_dev;
1330 struct list_head *iter;
1331
1332 netdev_for_each_lower_dev(group_dev, lower_dev, iter) {
1333 if (lower_dev->offload_fwd_mark == old_mark) {
1334 if (!*reset_mark)
1335 *reset_mark = lower_dev->ifindex;
1336 lower_dev->offload_fwd_mark = *reset_mark;
1337 }
1338 switchdev_port_fwd_mark_reset(lower_dev, old_mark, reset_mark);
1339 }
1340}
1341
1342/**
1343 * switchdev_port_fwd_mark_set - Set port offload forwarding mark
1344 *
1345 * @dev: port device
1346 * @group_dev: containing device
1347 * @joining: true if dev is joining group; false if leaving group
1348 *
1349 * An ungrouped port's offload mark is just its ifindex. A grouped
1350 * port's (member of a bridge, for example) offload mark is the ifindex
1351 * of one of the ports in the group with the same parent (switch) ID.
1352 * Ports on the same device in the same group will have the same mark.
1353 *
1354 * Example:
1355 *
1356 * br0 ifindex=9
1357 * sw1p1 ifindex=2 mark=2
1358 * sw1p2 ifindex=3 mark=2
1359 * sw2p1 ifindex=4 mark=5
1360 * sw2p2 ifindex=5 mark=5
1361 *
1362 * If sw2p2 leaves the bridge, we'll have:
1363 *
1364 * br0 ifindex=9
1365 * sw1p1 ifindex=2 mark=2
1366 * sw1p2 ifindex=3 mark=2
1367 * sw2p1 ifindex=4 mark=4
1368 * sw2p2 ifindex=5 mark=5
1369 */
1370void switchdev_port_fwd_mark_set(struct net_device *dev,
1371 struct net_device *group_dev,
1372 bool joining)
1373{
1374 u32 mark = dev->ifindex;
1375 u32 reset_mark = 0;
1376
1377 if (group_dev) {
1378 ASSERT_RTNL();
1379 if (joining)
1380 mark = switchdev_port_fwd_mark_get(dev, group_dev);
1381 else if (dev->offload_fwd_mark == mark)
1382 /* Ohoh, this port was the mark reference port,
1383 * but it's leaving the group, so reset the
1384 * mark for the remaining ports in the group.
1385 */
1386 switchdev_port_fwd_mark_reset(group_dev, mark,
1387 &reset_mark);
1388 }
1389
1390 dev->offload_fwd_mark = mark;
1391}
1392EXPORT_SYMBOL_GPL(switchdev_port_fwd_mark_set);