VLAN flooding already provides the exact port-VLAN entry associated with
each destination port. Use that entry directly for the egress state check
and VLAN handling instead of resolving it again from the packet VLAN ID.
Keep the existing lookup path for forwarding destinations which do not
provide a port-VLAN entry. This removes two VLAN hash lookups for every
flooded egress port. Use __always_inline for should_deliver as it provides
a measurable 3% additional gain, should_deliver wasn't inlined even though
it had the inline specifier, the bridge module size increases by 173 bytes.
The series were tested in a two CPU VM with packet generation and bridge
forwarding on separate pinned CPUs. The results are medians of seven runs
with 300000 64b tagged broadcast packets.
Mpps br_flood TSC cycles/input
VIDs Ports before after gain before after reduction
1 8/2 1.209298 1.636580 35.3% 1444 845 41.5%
1 8/4 0.843175 0.986541 17.0% 3036 2527 16.8%
1 8/8 0.508857 0.559844 10.0% 5999 5465 8.9%
1 32/2 0.893896 1.586532 77.5% 3013 841 72.1%
1 32/16 0.250567 0.282339 12.7% 13670 11137 18.5%
1 32/32 0.135351 0.148461 9.7% 25429 22320 12.2%
1 64/2 0.555010 1.603776 189.0% 5001 837 83.3%
1 64/32 0.123831 0.149639 20.8% 27674 22602 18.3%
1 64/64 0.069122 0.076047 10.0% 51508 45138 12.4%
64 8/2 1.044185 1.500468 43.7% 1528 851 44.3%
64 8/4 0.802680 0.977700 21.8% 3178 2546 19.9%
64 8/8 0.479748 0.555419 15.8% 6283 5635 10.3%
64 32/2 0.861074 1.561266 81.3% 3063 849 72.3%
64 32/16 0.234011 0.280027 19.7% 14332 11165 22.1%
64 32/32 0.126145 0.148085 17.4% 27225 22613 16.9%
64 64/2 0.530587 1.496179 182.0% 4377 826 81.1%
64 64/32 0.121105 0.147943 22.2% 29286 22541 23.0%
64 64/64 0.063626 0.077051 21.1% 56147 45520 18.9%
1024 8/2 1.008693 1.358408 34.7% 1538 879 42.8%
1024 8/4 0.785636 0.943545 20.1% 3280 2704 17.6%
1024 8/8 0.448194 0.521765 16.4% 6678 6011 10.0%
1024 32/2 0.840864 1.417311 68.6% 2756 859 68.8%
1024 32/16 0.195786 0.247129 26.2% 16269 12639 22.3%
1024 32/32 0.097978 0.122742 25.3% 35249 29273 17.0%
1024 64/2 0.536947 1.400985 160.9% 4421 849 80.8%
1024 64/32 0.093962 0.123383 31.3% 37425 29197 22.0%
1024 64/64 0.045147 0.057867 28.2% 72626 59198 18.5%
Signed-off-by: Nikolay Aleksandrov <[email protected]>
---
net/bridge/br_forward.c | 23 ++++++++++++++++-------
net/bridge/br_input.c | 2 +-
net/bridge/br_private.h | 7 +++++++
net/bridge/br_vlan.c | 31 +++++++++++++++++--------------
4 files changed, 41 insertions(+), 22 deletions(-)
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 845193baf992..d89f1b7d6def 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -21,17 +21,26 @@ struct br_fwd_dst {
struct net_bridge_vlan *vlan;
};
+static bool should_deliver_vlan(const struct br_fwd_dst *fwd,
+ const struct sk_buff *skb)
+{
+ if (fwd->vlan)
+ return br_vlan_state_allowed(br_vlan_get_state(fwd->vlan),
+ false);
+
+ return br_allowed_egress(nbp_vlan_group_rcu(fwd->port), skb);
+}
+
/* Don't forward packets to originating port or forwarding disabled */
-static inline int should_deliver(const struct br_fwd_dst *fwd,
- const struct sk_buff *skb)
+static __always_inline bool should_deliver(const struct br_fwd_dst *fwd,
+ const struct sk_buff *skb)
{
const struct net_bridge_port *p = fwd->port;
- struct net_bridge_vlan_group *vg;
- vg = nbp_vlan_group_rcu(p);
return (test_bit(BR_HAIRPIN_MODE_BIT, &p->flags) || skb->dev != p->dev)
&&
(br_mst_is_enabled(p) || p->state == BR_STATE_FORWARDING) &&
- br_allowed_egress(vg, skb) && nbp_switchdev_allowed_egress(p,
skb) &&
+ should_deliver_vlan(fwd, skb) &&
+ nbp_switchdev_allowed_egress(p, skb) &&
!br_skb_isolated(p, skb);
}
@@ -89,8 +98,8 @@ static void __br_forward(const struct br_fwd_dst *fwd,
*/
nbp_switchdev_frame_mark_tx_fwd_offload(to, skb);
- vg = nbp_vlan_group_rcu(to);
- skb = br_handle_vlan(to->br, to, vg, skb);
+ vg = fwd->vlan ? NULL : nbp_vlan_group_rcu(to);
+ skb = br_handle_vlan(to->br, to, vg, fwd->vlan, skb);
if (!skb)
return;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index b20c7c182a80..4357d78524a6 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -58,7 +58,7 @@ static int br_pass_frame_up(struct sk_buff *skb, bool promisc)
indev = skb->dev;
skb->dev = brdev;
- skb = br_handle_vlan(br, NULL, vg, skb);
+ skb = br_handle_vlan(br, NULL, vg, NULL, skb);
if (!skb)
return NET_RX_DROP;
/* update the multicast stats if the packet is IGMP/MLD */
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index a33da6e9765f..7c0b1d3e7931 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1606,6 +1606,7 @@ bool br_should_learn(struct net_bridge_port *p, struct
sk_buff *skb, u16 *vid);
struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_bridge_port *port,
struct net_bridge_vlan_group *vg,
+ struct net_bridge_vlan *vlan,
struct sk_buff *skb);
int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags,
bool *changed, struct netlink_ext_ack *extack);
@@ -1743,6 +1744,7 @@ static inline bool br_should_learn(struct net_bridge_port
*p,
static inline struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_bridge_port *port,
struct net_bridge_vlan_group *vg,
+ struct net_bridge_vlan *vlan,
struct sk_buff *skb)
{
return skb;
@@ -1953,6 +1955,11 @@ static inline bool br_vlan_state_allowed(u8 state, bool
learn_allow)
return false;
}
}
+#else
+static inline bool br_vlan_state_allowed(u8 state, bool learn_allow)
+{
+ return false;
+}
#endif
/* br_mst.c */
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index d750581df64d..ce5aa15c4540 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -520,10 +520,10 @@ static void __vlan_flush(const struct net_bridge *br,
struct sk_buff *br_handle_vlan(struct net_bridge *br,
const struct net_bridge_port *p,
struct net_bridge_vlan_group *vg,
+ struct net_bridge_vlan *v,
struct sk_buff *skb)
{
struct pcpu_sw_netstats *stats;
- struct net_bridge_vlan *v;
u16 vid;
/* If this packet was not filtered at input, let it pass */
@@ -534,19 +534,22 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
* a valid vlan id. If the vlan id has untagged flag set,
* send untagged; otherwise, send tagged.
*/
- br_vlan_get_tag(skb, &vid);
- v = br_vlan_find(vg, vid);
- /* Vlan entry must be configured at this point. The
- * only exception is the bridge is set in promisc mode and the
- * packet is destined for the bridge device. In this case
- * pass the packet as is.
- */
- if (!v || !br_vlan_should_use(v)) {
- if ((br->dev->flags & IFF_PROMISC) && skb->dev == br->dev) {
- goto out;
- } else {
- kfree_skb(skb);
- return NULL;
+ if (!v) {
+ br_vlan_get_tag(skb, &vid);
+ v = br_vlan_find(vg, vid);
+ /* Vlan entry must be configured at this point. The
+ * only exception is the bridge is set in promisc mode and the
+ * packet is destined for the bridge device. In this case
+ * pass the packet as is.
+ */
+ if (!v || !br_vlan_should_use(v)) {
+ if ((br->dev->flags & IFF_PROMISC) &&
+ skb->dev == br->dev) {
+ goto out;
+ } else {
+ kfree_skb(skb);
+ return NULL;
+ }
}
}
if (br_opt_get(br, BROPT_VLAN_STATS_ENABLED)) {
--
2.47.3