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


Reply via email to