From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D265E3A6418; Sat, 17 Oct 2026 16:50:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792255833; cv=none; b=qZsK1iKyQWOC2qdjge0NCzj7NLpmBpzv1CIXON0OeRshw+Csd+DKX+rjjAzQqJ01lxvmqdIiz5goOKVRUQ6nA/wvEe1N1Zo9CDAVHkaUg6kWf+tAvHRIf3Lm/TAtHSLvjuuhALm0BG11GXHxGWq2eoxjmTTtJ/e94twSn8CfTUI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792255833; c=relaxed/simple; bh=pOGwX7qCV3zD2gYzafRjQkdtDa329omM9uCpmt+Bglc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gaHhaE2OIlRZDFQBlOwTC2a1V8ioWkPfVPTv5EJrqLVlpc3LKBsaJEW4JGOsAhtxn3oKp3Py3VHjsSBO6TXArkib5jCGBDe8ToQ9Ep1yUn+JueTRrnJ0BlzL5OC2d2BtEasQIaDc+GgEenUgKIGC0BZpE03yNQa46NvcadA9AJY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=HqpfXxxs; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="HqpfXxxs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2FC551F000C4; Sat, 17 Oct 2026 16:50:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1792255831; bh=FV9zsSBzIyApVUNdSjamgE6UvvucE3QqOQb9a10eHHs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=HqpfXxxsD/pEHYvoRWRryEi5Gxo1zB1V7ibegDRURYsunLgEstsowaaJbfsaTXdH2 5obIaBgp+hUwfb4s+7EBBhSic3NTo7mcPmYmCIqwZi8ZFEOWclSuJP0iUIsTN4znqL zMHnC/FDKO2TDsNIq3dB22+TwEfi2gf3kjSbBewg= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Vega , Nikolay Aleksandrov , Yilin Zhu , Zhiling Zou , Jakub Kicinski , Sasha Levin Subject: [PATCH 6.6 0037/1674] net: bridge: use option bits for CFM/MRP frame handlers Date: Sat, 17 Oct 2026 17:38:29 +0200 Message-ID: <20261017160432.093227261@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261017160431.119607952@linuxfoundation.org> References: <20261017160431.119607952@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.6-stable review patch. If anyone has any objections, please let me know. ------------------ From: Zhiling Zou [ Upstream commit 7a49e6b16f36b8e085521699adbca3e321b6dd0c ] CFM and MRP register a global br_frame_type whose hlist_node is linked into the per-bridge frame_type_list when the first MEP/MRP instance is created. Enabling the protocol on multiple bridges therefore inserts the same node into multiple lists. Unregistering it on one bridge then corrupts list state belonging to another. These handlers can only be installed once per bridge, and they are uncommon. Track their per-bridge enable state with net_bridge option bits, which already live on the Rx hot cache line, and dispatch the matching handler directly from the receive path. Check both bits together first as an unlikely case. Remove the generic frame_type_list and br_frame_type helpers, which have had no other users since CFM and MRP were added. That shrinks struct net_bridge by 8 bytes and drops the list walk from the fast path. When neither protocol is compiled in, BR_CFM_MRP_OPTS is 0 and the compiler prunes the branch. Fixes: 90c628dd47ff ("net: bridge: extend the process of special frames") Fixes: dc32cbb3dbd7 ("bridge: cfm: Kernel space implementation of CFM. CCM frame RX added.") Cc: stable@vger.kernel.org Reported-by: Vega Suggested-by: Nikolay Aleksandrov Co-developed-by: Yilin Zhu Signed-off-by: Yilin Zhu Signed-off-by: Zhiling Zou Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/0345b9d5aa60ba416f6738ff1b87140f0a749cb8.1788417901.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski [ Adjusted bridge option enum context because BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION and BROPT_FDB_LOCAL_VLAN_0 are absent. ] Signed-off-by: Sasha Levin --- net/bridge/br_cfm.c | 11 +++-------- net/bridge/br_device.c | 1 - net/bridge/br_input.c | 35 ++++++++++++++--------------------- net/bridge/br_mrp.c | 13 +++---------- net/bridge/br_private.h | 26 +++++++++++++++----------- 5 files changed, 35 insertions(+), 51 deletions(-) diff --git a/net/bridge/br_cfm.c b/net/bridge/br_cfm.c index a89aec8314164..b9a3546fb4b44 100644 --- a/net/bridge/br_cfm.c +++ b/net/bridge/br_cfm.c @@ -367,7 +367,7 @@ static u32 ccm_tlv_extract(struct sk_buff *skb, u32 index, } /* note: already called with rcu_read_lock */ -static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) +int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) { u32 mdlevel, interval, size, index, max; const struct br_cfm_common_hdr *hdr; @@ -489,11 +489,6 @@ static int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb) return 1; } -static struct br_frame_type cfm_frame_type __read_mostly = { - .type = cpu_to_be16(ETH_P_CFM), - .frame_handler = br_cfm_frame_rx, -}; - int br_cfm_mep_create(struct net_bridge *br, const u32 instance, struct br_cfm_mep_create *const create, @@ -559,7 +554,7 @@ int br_cfm_mep_create(struct net_bridge *br, INIT_DELAYED_WORK(&mep->ccm_tx_dwork, ccm_tx_work_expired); if (hlist_empty(&br->mep_list)) - br_add_frame(br, &cfm_frame_type); + br_opt_toggle(br, BROPT_CFM_ENABLED, true); hlist_add_tail_rcu(&mep->head, &br->mep_list); @@ -588,7 +583,7 @@ static void mep_delete_implementation(struct net_bridge *br, kfree_rcu(mep, rcu); if (hlist_empty(&br->mep_list)) - br_del_frame(br, &cfm_frame_type); + br_opt_toggle(br, BROPT_CFM_ENABLED, false); } int br_cfm_mep_delete(struct net_bridge *br, diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index a4f6e56ef8729..19d2021926f3c 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -512,7 +512,6 @@ void br_dev_setup(struct net_device *dev) spin_lock_init(&br->lock); INIT_LIST_HEAD(&br->port_list); INIT_HLIST_HEAD(&br->fdb_list); - INIT_HLIST_HEAD(&br->frame_type_list); #if IS_ENABLED(CONFIG_BRIDGE_MRP) INIT_HLIST_HEAD(&br->mrp_list); #endif diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 7f9bb05048d95..6d6bedd32211d 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -303,17 +303,25 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb) return RX_HANDLER_CONSUMED; } +#define BR_CFM_MRP_OPTS \ + ((IS_ENABLED(CONFIG_BRIDGE_CFM) ? BIT(BROPT_CFM_ENABLED) : 0UL) | \ + (IS_ENABLED(CONFIG_BRIDGE_MRP) ? BIT(BROPT_MRP_ENABLED) : 0UL)) + /* Return 0 if the frame was not processed otherwise 1 * note: already called with rcu_read_lock */ static int br_process_frame_type(struct net_bridge_port *p, struct sk_buff *skb) { - struct br_frame_type *tmp; + struct net_bridge *br = p->br; + + if (skb->protocol == htons(ETH_P_CFM) && + br_opt_get(br, BROPT_CFM_ENABLED)) + return br_cfm_frame_rx(p, skb); - hlist_for_each_entry_rcu(tmp, &p->br->frame_type_list, list) - if (unlikely(tmp->type == skb->protocol)) - return tmp->frame_handler(p, skb); + if (skb->protocol == htons(ETH_P_MRP) && + br_opt_get(br, BROPT_MRP_ENABLED)) + return br_mrp_process(p, skb); return 0; } @@ -407,7 +415,8 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) } } - if (unlikely(br_process_frame_type(p, skb))) + if (unlikely((READ_ONCE(p->br->options) & BR_CFM_MRP_OPTS) && + br_process_frame_type(p, skb))) return RX_HANDLER_PASS; forward: @@ -448,19 +457,3 @@ rx_handler_func_t *br_get_rx_handler(const struct net_device *dev) return br_handle_frame; } - -void br_add_frame(struct net_bridge *br, struct br_frame_type *ft) -{ - hlist_add_head_rcu(&ft->list, &br->frame_type_list); -} - -void br_del_frame(struct net_bridge *br, struct br_frame_type *ft) -{ - struct br_frame_type *tmp; - - hlist_for_each_entry(tmp, &br->frame_type_list, list) - if (ft == tmp) { - hlist_del_rcu(&ft->list); - return; - } -} diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index a837ff7d6f36d..79d6bc62179e2 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -6,13 +6,6 @@ static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 }; static const u8 mrp_in_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x3 }; -static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); - -static struct br_frame_type mrp_frame_type __read_mostly = { - .type = cpu_to_be16(ETH_P_MRP), - .frame_handler = br_mrp_process, -}; - static bool br_mrp_is_ring_port(struct net_bridge_port *p_port, struct net_bridge_port *s_port, struct net_bridge_port *port) @@ -486,7 +479,7 @@ static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp) kfree_rcu(mrp, rcu); if (hlist_empty(&br->mrp_list)) - br_del_frame(br, &mrp_frame_type); + br_opt_toggle(br, BROPT_MRP_ENABLED, false); } /* Adds a new MRP instance. @@ -536,7 +529,7 @@ int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance) rcu_assign_pointer(mrp->s_port, p); if (hlist_empty(&br->mrp_list)) - br_add_frame(br, &mrp_frame_type); + br_opt_toggle(br, BROPT_MRP_ENABLED, true); INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired); INIT_DELAYED_WORK(&mrp->in_test_work, br_mrp_in_test_work_expired); @@ -1241,7 +1234,7 @@ static int br_mrp_rcv(struct net_bridge_port *p, * normal forwarding. * note: already called with rcu_read_lock */ -static int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) +int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) { /* If there is no MRP instance do normal forwarding */ if (likely(!(p->flags & BR_MRP_AWARE))) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4867464bde32b..958d12ebb660f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -483,12 +483,13 @@ enum net_bridge_opts { BROPT_VLAN_BRIDGE_BINDING, BROPT_MCAST_VLAN_SNOOPING_ENABLED, BROPT_MST_ENABLED, + BROPT_CFM_ENABLED, + BROPT_MRP_ENABLED, }; struct net_bridge { spinlock_t lock; spinlock_t hash_lock; - struct hlist_head frame_type_list; struct net_device *dev; unsigned long options; /* These fields are accessed on each packet */ @@ -914,16 +915,6 @@ int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev); int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb); rx_handler_func_t *br_get_rx_handler(const struct net_device *dev); -struct br_frame_type { - __be16 type; - int (*frame_handler)(struct net_bridge_port *port, - struct sk_buff *skb); - struct hlist_node list; -}; - -void br_add_frame(struct net_bridge *br, struct br_frame_type *ft); -void br_del_frame(struct net_bridge *br, struct br_frame_type *ft); - static inline bool br_rx_handler_check_rcu(const struct net_device *dev) { return rcu_dereference(dev->rx_handler) == br_get_rx_handler(dev); @@ -2009,6 +2000,7 @@ int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p, bool br_mrp_enabled(struct net_bridge *br); void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p); int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br); +int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb); #else static inline int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p, struct nlattr *attr, int cmd, @@ -2032,6 +2024,11 @@ static inline int br_mrp_fill_info(struct sk_buff *skb, struct net_bridge *br) return 0; } +static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb) +{ + return 0; +} + #endif /* br_cfm.c */ @@ -2040,6 +2037,7 @@ int br_cfm_parse(struct net_bridge *br, struct net_bridge_port *p, struct nlattr *attr, int cmd, struct netlink_ext_ack *extack); bool br_cfm_created(struct net_bridge *br); void br_cfm_port_del(struct net_bridge *br, struct net_bridge_port *p); +int br_cfm_frame_rx(struct net_bridge_port *port, struct sk_buff *skb); int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br); int br_cfm_status_fill_info(struct sk_buff *skb, struct net_bridge *br, @@ -2064,6 +2062,12 @@ static inline void br_cfm_port_del(struct net_bridge *br, { } +static inline int br_cfm_frame_rx(struct net_bridge_port *port, + struct sk_buff *skb) +{ + return 0; +} + static inline int br_cfm_config_fill_info(struct sk_buff *skb, struct net_bridge *br) { return -EOPNOTSUPP; -- 2.53.0