diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 881d866d687a..252c46977ed5 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1776,6 +1776,28 @@ static void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx, #endif } +static void br_multicast_port_query_queue_work(struct work_struct *work) +{ + struct net_bridge_mcast_port *pmctx; + struct sk_buff *skb; + + pmctx = container_of(work, struct net_bridge_mcast_port, + query_queue_work); + while ((skb = skb_dequeue(&pmctx->query_queue))) + NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, dev_net(skb->dev), + NULL, skb, NULL, skb->dev, br_dev_queue_push_xmit); +} + +static void br_multicast_query_queue_work(struct work_struct *work) +{ + struct net_bridge_mcast *brmctx; + struct sk_buff *skb; + + brmctx = container_of(work, struct net_bridge_mcast, query_queue_work); + while ((skb = skb_dequeue(&brmctx->query_queue))) + netif_rx(skb); +} + static void __br_multicast_send_query(struct net_bridge_mcast *brmctx, struct net_bridge_mcast_port *pmctx, struct net_bridge_port_group *pg, @@ -1804,9 +1826,8 @@ static void __br_multicast_send_query(struct net_bridge_mcast *brmctx, skb->dev = pmctx->port->dev; br_multicast_count(brmctx->br, pmctx->port, skb, igmp_type, BR_MCAST_DIR_TX); - NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, - dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev, - br_dev_queue_push_xmit); + skb_queue_tail(&pmctx->query_queue, skb); + queue_work(system_highpri_wq, &pmctx->query_queue_work); if (over_lmqt && with_srcs && sflag) { over_lmqt = false; @@ -1816,7 +1837,8 @@ static void __br_multicast_send_query(struct net_bridge_mcast *brmctx, br_multicast_select_own_querier(brmctx, group, skb); br_multicast_count(brmctx->br, NULL, skb, igmp_type, BR_MCAST_DIR_RX); - netif_rx(skb); + skb_queue_tail(&brmctx->query_queue, skb); + queue_work(system_highpri_wq, &brmctx->query_queue_work); } } @@ -1999,6 +2021,10 @@ void br_multicast_port_ctx_init(struct net_bridge_port *port, pmctx->port = port; pmctx->vlan = vlan; pmctx->multicast_router = MDB_RTR_TYPE_TEMP_QUERY; + + skb_queue_head_init(&pmctx->query_queue); + INIT_WORK(&pmctx->query_queue_work, br_multicast_port_query_queue_work); + timer_setup(&pmctx->ip4_mc_router_timer, br_ip4_multicast_router_expired, 0); timer_setup(&pmctx->ip4_own_query.timer, @@ -2038,6 +2064,7 @@ void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx) del |= br_ip4_multicast_rport_del(pmctx); br_multicast_rport_del_notify(pmctx, del); spin_unlock_bh(&br->multicast_lock); + flush_work(&pmctx->query_queue_work); } int br_multicast_add_port(struct net_bridge_port *port) @@ -4111,6 +4138,9 @@ void br_multicast_ctx_init(struct net_bridge *br, seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock); #endif + skb_queue_head_init(&brmctx->query_queue); + INIT_WORK(&brmctx->query_queue_work, br_multicast_query_queue_work); + timer_setup(&brmctx->ip4_mc_router_timer, br_ip4_multicast_local_router_expired, 0); timer_setup(&brmctx->ip4_other_query.timer, @@ -4134,6 +4164,7 @@ void br_multicast_ctx_init(struct net_bridge *br, void br_multicast_ctx_deinit(struct net_bridge_mcast *brmctx) { __br_multicast_stop(brmctx); + flush_work(&brmctx->query_queue_work); } void br_multicast_init(struct net_bridge *br) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 361a9b84451e..2695b9128705 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -131,6 +131,8 @@ struct net_bridge_mcast_port { unsigned char multicast_router; u32 mdb_n_entries; u32 mdb_max_entries; + struct sk_buff_head query_queue; + struct work_struct query_queue_work; #endif /* CONFIG_BRIDGE_IGMP_SNOOPING */ }; @@ -167,6 +169,8 @@ struct net_bridge_mcast { struct bridge_mcast_own_query ip6_own_query; struct bridge_mcast_querier ip6_querier; #endif /* IS_ENABLED(CONFIG_IPV6) */ + struct sk_buff_head query_queue; + struct work_struct query_queue_work; #endif /* CONFIG_BRIDGE_IGMP_SNOOPING */ };