From: Zixuan Chai <[email protected]>

br_multicast_sg_del_exclude_ports() removes automatically added STAR_EXCL
port groups by calling br_multicast_del_pg(). For S,G entries, that
deletion calls back into br_multicast_sg_del_exclude_ports(), so a large
number of STAR_EXCL ports consumes one kernel stack frame per port and can
hit the stack guard page.

Keep the complete port-group deletion path for cleanup, but suppress the
S,G exclude-port cleanup while that helper is already walking the same
entry. Normal deletion callers retain the existing behavior.

Fixes: 8266a0491e92 ("net: bridge: mcast: handle port group filter modes")
Cc: [email protected]
Reported-by: Vega <[email protected]>
Assisted-by: LLM
Signed-off-by: Zixuan Chai <[email protected]>
Signed-off-by: Ren Wei <[email protected]>
---
 net/bridge/br_multicast.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 2f9bb30e1a1f..3fbad7b59769 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -81,6 +81,10 @@ __br_multicast_add_group(struct net_bridge_mcast *brmctx,
                         bool blocked);
 static void br_multicast_find_del_pg(struct net_bridge *br,
                                     struct net_bridge_port_group *pg);
+static void __br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
+                                 struct net_bridge_port_group *pg,
+                                 struct net_bridge_port_group __rcu **pp,
+                                 bool sg_del_exclude_ports);
 static void __br_multicast_stop(struct net_bridge_mcast *brmctx);
 
 static int br_mc_disabled_update(struct net_device *dev, bool value,
@@ -458,7 +462,7 @@ static void br_multicast_sg_del_exclude_ports(struct 
net_bridge_mdb_entry *sgmp)
        for (pp = &sgmp->ports;
             (p = mlock_dereference(*pp, sgmp->br)) != NULL;) {
                if (!(p->flags & MDB_PG_FLAGS_PERMANENT))
-                       br_multicast_del_pg(sgmp, p, pp);
+                       __br_multicast_del_pg(sgmp, p, pp, false);
                else
                        pp = &p->next;
        }
@@ -799,9 +803,10 @@ static void br_multicast_destroy_port_group(struct 
net_bridge_mcast_gc *gc)
        kfree_rcu(pg, rcu);
 }
 
-void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
-                        struct net_bridge_port_group *pg,
-                        struct net_bridge_port_group __rcu **pp)
+static void __br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
+                                 struct net_bridge_port_group *pg,
+                                 struct net_bridge_port_group __rcu **pp,
+                                 bool sg_del_exclude_ports)
 {
        struct net_bridge *br = pg->key.port->br;
        struct net_bridge_group_src *ent;
@@ -820,7 +825,8 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
        if (!br_multicast_is_star_g(&mp->addr)) {
                rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode,
                                       br_sg_port_rht_params);
-               br_multicast_sg_del_exclude_ports(mp);
+               if (sg_del_exclude_ports)
+                       br_multicast_sg_del_exclude_ports(mp);
        } else {
                br_multicast_star_g_handle_mode(pg, MCAST_INCLUDE);
        }
@@ -832,6 +838,13 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
                mod_timer(&mp->timer, jiffies);
 }
 
+void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
+                        struct net_bridge_port_group *pg,
+                        struct net_bridge_port_group __rcu **pp)
+{
+       __br_multicast_del_pg(mp, pg, pp, true);
+}
+
 static void br_multicast_find_del_pg(struct net_bridge *br,
                                     struct net_bridge_port_group *pg)
 {
-- 
2.34.1

Reply via email to