diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/Makefile | 2 | ||||
-rw-r--r-- | net/bridge/br_forward.c | 3 | ||||
-rw-r--r-- | net/bridge/br_if.c | 10 | ||||
-rw-r--r-- | net/bridge/br_input.c | 2 | ||||
-rw-r--r-- | net/bridge/br_private.h | 37 | ||||
-rw-r--r-- | net/bridge/br_switchdev.c | 57 | ||||
-rw-r--r-- | net/core/dev.c | 10 | ||||
-rw-r--r-- | net/switchdev/switchdev.c | 85 |
8 files changed, 107 insertions, 99 deletions
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 | ||
21 | bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o | 21 | bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o |
22 | 22 | ||
23 | bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o | ||
24 | |||
23 | obj-$(CONFIG_NETFILTER) += netfilter/ | 25 | obj-$(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 | ||
35 | int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) | 36 | int 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 | ||
592 | err6: | 596 | err7: |
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); |
600 | err6: | ||
596 | netdev_upper_dev_unlink(dev, br->dev); | 601 | netdev_upper_dev_unlink(dev, br->dev); |
597 | |||
598 | err5: | 602 | err5: |
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; } | |||
1034 | static inline void br_sysfs_delbr(struct net_device *dev) { return; } | 1046 | static 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 | ||
1051 | int nbp_switchdev_mark_set(struct net_bridge_port *p); | ||
1052 | void nbp_switchdev_frame_mark(const struct net_bridge_port *p, | ||
1053 | struct sk_buff *skb); | ||
1054 | bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, | ||
1055 | const struct sk_buff *skb); | ||
1056 | #else | ||
1057 | static inline int nbp_switchdev_mark_set(struct net_bridge_port *p) | ||
1058 | { | ||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | static inline void nbp_switchdev_frame_mark(const struct net_bridge_port *p, | ||
1063 | struct sk_buff *skb) | ||
1064 | { | ||
1065 | } | ||
1066 | |||
1067 | static 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 | |||
10 | static 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 | |||
23 | int 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 | |||
45 | void 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 | |||
52 | bool 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 | } |
1307 | EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); | 1307 | EXPORT_SYMBOL_GPL(switchdev_port_same_parent_id); |
1308 | |||
1309 | static 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 | |||
1326 | static 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 | */ | ||
1370 | void 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 | } | ||
1392 | EXPORT_SYMBOL_GPL(switchdev_port_fwd_mark_set); | ||