From patchwork Thu May 11 01:56:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Qingfang Deng X-Patchwork-Id: 1779743 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.openwrt.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=ZPqxBPV0; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20221208 header.b=HGnlf/OK; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QGw7P6hsCz213w for ; Thu, 11 May 2023 11:59:27 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=UYcfBJbBUiW0+w05ibmAuw3mBHVulEnCukDdnQ5qa6g=; b=ZPqxBPV0BiTMiH 0ymzmwjv1p21xAdO/KJBME5Y+/gaE5mTcOczC+B3AFtPAY966opCeyKpk6HZOwcuL/RYyMREMq26M oNawki0mNXta6wmWE8EJSBU55or+YyrnPnvIpjIm/2HyFcQ3GI7XINVmqJ/BQkhXZxvHoHGuvDjyP h1fdzkyBr68gvCmXBvQee/NKmEt9ij7hdf2oR20rtfY/ujAJcJbslYTylFK5tFtSznr37ndf0aQ39 /xgOLq0jBmgnUEtMk+5GVlTxcFi4MhoyccIuP/281/CMQLnyoUnud+i9QVT63WA1qk6WeAeqHFDpS oE0IS4vRMefvtY7CSdbA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1pwvXS-007UV4-2G; Thu, 11 May 2023 01:56:14 +0000 Received: from mail-pg1-x531.google.com ([2607:f8b0:4864:20::531]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1pwvXP-007USs-1B for openwrt-devel@lists.openwrt.org; Thu, 11 May 2023 01:56:13 +0000 Received: by mail-pg1-x531.google.com with SMTP id 41be03b00d2f7-52c6504974dso7202512a12.2 for ; Wed, 10 May 2023 18:56:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1683770162; x=1686362162; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=f0iX5PZt2mmpyonc8+iwtb0h3Bx+uBwKC4+yWtmwgb0=; b=HGnlf/OKufFFE2JypPdHVc4zshL5sZ2l3ptwD53Ktf12FPbhGnxNhAxWhJVnm4XYgH 80px2Y5uRlVvAmLASPKeIMBB8NXyPLSFFXYA5LnMha6x5srKTcfyt8NZE2sNFcOfnGaK Tv1QIzeDfC0RAko3qm61kCsK1lLOlm6SUubVMYmKl0zjcnA+DhtNSCrlNNqqHBiGiyHt X2xMB3Oj7pTM7tRDUqIEP2UdlgpfeL3GAvh/nWh58xx28LS0Di1xvnug61KdW6FoiUb1 JDRYmsZ5wn4E758SdRhP9AlypEjvuWpem2Xs2IW+2AmJr4a4LW9sW7eW98FAbpzo7O0a 1AjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1683770162; x=1686362162; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=f0iX5PZt2mmpyonc8+iwtb0h3Bx+uBwKC4+yWtmwgb0=; b=JeDFmxxRO1BqTrIW+3EygHyyGx18/KDqyZo31rMISRHev9WHYlLdU7NFya5VaSQ0/g gfnGtBaDJEUwpb3epnBtCcO3xON9Vh5Rxodm+TQx+8P6swQ+4yZcs88b5gZgPno0dKSj Ei+AG4UpYW1Fu3GiD1hsIfpRhZdBJcwum3KTnXZdtPNJPoo/mKNPbpgyznaHrF8eY1pV g//tGmGNg2KInIf11xlCOH9cWJWRR5S12wYTH17I7qKM62egyp9fZlWdCikKoZY0kmKo 6o4JzPQWC8u58BN7OjtTnFwsCKMMsgon0wLV8PMAxiVfSWKbdLmf/OOA4Xs22UKaBa53 eMVg== X-Gm-Message-State: AC+VfDxQ9Wm1U2GjOEFgZR1PxrvaoBtO/8UX20Yy9SCjkNHclI2+SGCi y37Qb+KCKuCAcXdQegogxeMz2hcVrfFAVhj+ X-Google-Smtp-Source: ACHHUZ7FIMHPwNY/3tQ1OgeQmWLCYpVDc5y0Tu4Yo89rTDrbUbUg13eHg/GlgCFcWZgT/7CcsKC6uQ== X-Received: by 2002:a17:903:280b:b0:1ab:197d:2de1 with SMTP id kp11-20020a170903280b00b001ab197d2de1mr17371113plb.2.1683770162132; Wed, 10 May 2023 18:56:02 -0700 (PDT) Received: from DESKTOP-4R0U3NR.siflower.com ([222.65.110.68]) by smtp.gmail.com with ESMTPSA id f6-20020a170902ab8600b001a6a6169d45sm4505100plr.168.2023.05.10.18.56.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 May 2023 18:56:01 -0700 (PDT) From: Qingfang DENG To: openwrt-devel@lists.openwrt.org Cc: Felix Fietkau , Qingfang DENG Subject: [PATCH v2] kernel: fix scheduling while atomic in bridge offload Date: Thu, 11 May 2023 09:56:20 +0800 Message-Id: <20230511015620.1027-1-dqfext@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230510140504.520186-1-dqfext@gmail.com> References: <20230510140504.520186-1-dqfext@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230510_185611_409062_596E0F09 X-CRM114-Status: GOOD ( 16.16 ) X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Qingfang DENG br_offload_port_state is in a spinlock's critical section (&br->lock), but rhashtable_init/rhashtable_free_and_destroy/flush_work may sleep, causing scheduling while atomic bug. Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at https://www.dnswl.org/, no trust [2607:f8b0:4864:20:0:0:0:531 listed in] [list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider [dqfext[at]gmail.com] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org From: Qingfang DENG br_offload_port_state is in a spinlock's critical section (&br->lock), but rhashtable_init/rhashtable_free_and_destroy/flush_work may sleep, causing scheduling while atomic bug. To fix this, use workqueues to defer the init/destroy operations. To synchronize between rhashtable insert/destroy, synchronize_rcu is used to ensure all writers have left the RCU critical section before calling rhashtable_free_and_destroy. While at it, check for null pointers at the flow allocation. Fixes: 94b4da9b4aad ("kernel: add a fast path for the bridge code") Signed-off-by: Qingfang DENG --- v2: use my work email .../hack-5.10/600-bridge_offload.patch | 121 ++++++++++++++---- .../hack-5.15/600-bridge_offload.patch | 121 ++++++++++++++---- 2 files changed, 198 insertions(+), 44 deletions(-) diff --git a/target/linux/generic/hack-5.10/600-bridge_offload.patch b/target/linux/generic/hack-5.10/600-bridge_offload.patch index 82282627ea..1c03ac2cbd 100644 --- a/target/linux/generic/hack-5.10/600-bridge_offload.patch +++ b/target/linux/generic/hack-5.10/600-bridge_offload.patch @@ -153,16 +153,25 @@ Submitted-by: Felix Fietkau /* * Determine initial path cost based on speed. -@@ -427,7 +428,7 @@ static struct net_bridge_port *new_nbp(s +@@ -361,6 +362,7 @@ static void del_nbp(struct net_bridge_po + kobject_del(&p->kobj); + + br_netpoll_disable(p); ++ flush_work(&p->offload.deferred_work); + + call_rcu(&p->rcu, destroy_nbp_rcu); + } +@@ -427,7 +429,8 @@ static struct net_bridge_port *new_nbp(s p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; - p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_OFFLOAD; ++ br_offload_init_work(p); br_init_port(p); br_set_state(p, BR_STATE_DISABLED); br_stp_port_timer_init(p); -@@ -777,6 +778,9 @@ void br_port_flags_change(struct net_bri +@@ -777,6 +780,9 @@ void br_port_flags_change(struct net_bri if (mask & BR_NEIGH_SUPPRESS) br_recalculate_neigh_suppress_enabled(br); @@ -202,7 +211,7 @@ Submitted-by: Felix Fietkau nbp_vlan_group_rcu(p))) --- /dev/null +++ b/net/bridge/br_offload.c -@@ -0,0 +1,436 @@ +@@ -0,0 +1,491 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include @@ -236,6 +245,11 @@ Submitted-by: Felix Fietkau + struct rcu_head rcu; +}; + ++struct bridge_offload_deferred_item { ++ struct list_head list; ++ bool enable; ++}; ++ +static const struct rhashtable_params flow_params = { + .automatic_shrinking = true, + .head_offset = offsetof(struct bridge_flow, node), @@ -309,7 +323,7 @@ Submitted-by: Felix Fietkau + p->br->offload_cache_reserved) >= p->br->offload_cache_size; +} + -+static void ++void +br_offload_gc_work(struct work_struct *work) +{ + struct rhashtable_iter hti; @@ -357,11 +371,57 @@ Submitted-by: Felix Fietkau + +} + ++static struct bridge_offload_deferred_item * ++br_offload_deferred_dequeue(struct net_bridge_port *p) ++{ ++ struct bridge_offload_deferred_item *bi = NULL; ++ ++ spin_lock_bh(&p->br->lock); ++ if (list_empty(&p->offload.deferred_list)) ++ goto out; ++ ++ bi = list_first_entry(&p->offload.deferred_list, ++ struct bridge_offload_deferred_item, list); ++ list_del(&bi->list); ++out: ++ spin_unlock_bh(&p->br->lock); ++ return bi; ++} ++ ++void br_offload_deferred_work(struct work_struct *work) ++{ ++ struct bridge_offload_deferred_item *bi; ++ struct net_bridge_port_offload *o; ++ struct net_bridge_port *p; ++ ++ p = container_of(work, struct net_bridge_port, offload.deferred_work); ++ o = &p->offload; ++ while ((bi = br_offload_deferred_dequeue(p)) != NULL) { ++ if (bi->enable) { ++ rhashtable_init(&o->rht, &flow_params); ++ spin_lock_bh(&offload_lock); ++ o->enabled = true; ++ spin_unlock_bh(&offload_lock); ++ } else { ++ spin_lock_bh(&offload_lock); ++ o->enabled = false; ++ spin_unlock_bh(&offload_lock); ++ /* Ensure all rht users have finished */ ++ synchronize_rcu(); ++ cancel_work_sync(&o->gc_work); ++ rhashtable_free_and_destroy(&o->rht, ++ br_offload_destroy_cb, o); ++ } ++ kfree(bi); ++ } ++} ++ +void br_offload_port_state(struct net_bridge_port *p) +{ + struct net_bridge_port_offload *o = &p->offload; ++ struct bridge_offload_deferred_item *bi; ++ gfp_t gfp = GFP_ATOMIC; + bool enabled = true; -+ bool flush = false; + + if (p->state != BR_STATE_FORWARDING || + !(p->flags & BR_OFFLOAD)) @@ -371,22 +431,23 @@ Submitted-by: Felix Fietkau + if (o->enabled == enabled) + goto out; + -+ if (enabled) { -+ if (!o->gc_work.func) -+ INIT_WORK(&o->gc_work, br_offload_gc_work); -+ rhashtable_init(&o->rht, &flow_params); -+ } else { -+ flush = true; -+ rhashtable_free_and_destroy(&o->rht, br_offload_destroy_cb, o); -+ } ++ bi = kmalloc(sizeof(*bi), gfp); ++ if (!bi) { ++ if (enabled) ++ goto out; + -+ o->enabled = enabled; ++ /* If we are disabling (freeing the rht), retry with ++ * __GFP_MEMALLOC. Panic if that also fails. ++ */ ++ gfp |= __GFP_MEMALLOC | __GFP_NOWARN; ++ bi = kmalloc(sizeof(*bi), gfp); ++ } + ++ bi->enable = enabled; ++ list_add_tail(&bi->list, &o->deferred_list); ++ schedule_work(&o->deferred_work); +out: + spin_unlock_bh(&offload_lock); -+ -+ if (flush) -+ flush_work(&o->gc_work); +} + +void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb) @@ -478,6 +539,9 @@ Submitted-by: Felix Fietkau +#endif + + flow = kmem_cache_alloc(offload_cache, GFP_ATOMIC); ++ if (unlikely(!flow)) ++ goto out; ++ + flow->port = inp; + memcpy(&flow->key, &key, sizeof(key)); + @@ -656,20 +720,22 @@ Submitted-by: Felix Fietkau }; #define MDB_PG_FLAGS_PERMANENT BIT(0) -@@ -280,6 +286,12 @@ struct net_bridge_mdb_entry { +@@ -280,6 +286,14 @@ struct net_bridge_mdb_entry { struct rcu_head rcu; }; +struct net_bridge_port_offload { + struct rhashtable rht; + struct work_struct gc_work; ++ struct work_struct deferred_work; ++ struct list_head deferred_list; + bool enabled; +}; + struct net_bridge_port { struct net_bridge *br; struct net_device *dev; -@@ -337,6 +349,7 @@ struct net_bridge_port { +@@ -337,6 +351,7 @@ struct net_bridge_port { u16 backup_redirected_cnt; struct bridge_stp_xstats stp_xstats; @@ -677,7 +743,7 @@ Submitted-by: Felix Fietkau }; #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj) -@@ -475,6 +488,9 @@ struct net_bridge { +@@ -475,6 +490,9 @@ struct net_bridge { struct kobject *ifobj; u32 auto_cnt; @@ -687,7 +753,7 @@ Submitted-by: Felix Fietkau #ifdef CONFIG_NET_SWITCHDEV int offload_fwd_mark; #endif -@@ -501,6 +517,10 @@ struct br_input_skb_cb { +@@ -501,6 +519,10 @@ struct br_input_skb_cb { #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE u8 br_netfilter_broute:1; #endif @@ -700,7 +766,7 @@ Submitted-by: Felix Fietkau int offload_fwd_mark; --- /dev/null +++ b/net/bridge/br_private_offload.h -@@ -0,0 +1,21 @@ +@@ -0,0 +1,32 @@ +#ifndef __BR_OFFLOAD_H +#define __BR_OFFLOAD_H + @@ -712,6 +778,17 @@ Submitted-by: Felix Fietkau +void br_offload_fini(void); +int br_offload_set_cache_size(struct net_bridge *br, unsigned long val); +int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val); ++void br_offload_deferred_work(struct work_struct *work); ++void br_offload_gc_work(struct work_struct *work); ++ ++static inline void br_offload_init_work(struct net_bridge_port *p) ++{ ++ struct net_bridge_port_offload *o = &p->offload; ++ ++ INIT_LIST_HEAD(&o->deferred_list); ++ INIT_WORK(&o->deferred_work, br_offload_deferred_work); ++ INIT_WORK(&o->gc_work, br_offload_gc_work); ++} + +static inline void br_offload_skb_disable(struct sk_buff *skb) +{ diff --git a/target/linux/generic/hack-5.15/600-bridge_offload.patch b/target/linux/generic/hack-5.15/600-bridge_offload.patch index 9d71a741b2..118df44fec 100644 --- a/target/linux/generic/hack-5.15/600-bridge_offload.patch +++ b/target/linux/generic/hack-5.15/600-bridge_offload.patch @@ -150,16 +150,25 @@ Subject: [PATCH] net/bridge: add bridge offload /* * Determine initial path cost based on speed. -@@ -428,7 +429,7 @@ static struct net_bridge_port *new_nbp(s +@@ -362,6 +363,7 @@ static void del_nbp(struct net_bridge_po + kobject_del(&p->kobj); + + br_netpoll_disable(p); ++ flush_work(&p->offload.deferred_work); + + call_rcu(&p->rcu, destroy_nbp_rcu); + } +@@ -428,7 +430,8 @@ static struct net_bridge_port *new_nbp(s p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; - p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_OFFLOAD; ++ br_offload_init_work(p); br_init_port(p); br_set_state(p, BR_STATE_DISABLED); br_stp_port_timer_init(p); -@@ -771,6 +772,9 @@ void br_port_flags_change(struct net_bri +@@ -771,6 +774,9 @@ void br_port_flags_change(struct net_bri if (mask & BR_NEIGH_SUPPRESS) br_recalculate_neigh_suppress_enabled(br); @@ -199,7 +208,7 @@ Subject: [PATCH] net/bridge: add bridge offload --- /dev/null +++ b/net/bridge/br_offload.c -@@ -0,0 +1,438 @@ +@@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include @@ -233,6 +242,11 @@ Subject: [PATCH] net/bridge: add bridge offload + struct rcu_head rcu; +}; + ++struct bridge_offload_deferred_item { ++ struct list_head list; ++ bool enable; ++}; ++ +static const struct rhashtable_params flow_params = { + .automatic_shrinking = true, + .head_offset = offsetof(struct bridge_flow, node), @@ -306,7 +320,7 @@ Subject: [PATCH] net/bridge: add bridge offload + p->br->offload_cache_reserved) >= p->br->offload_cache_size; +} + -+static void ++void +br_offload_gc_work(struct work_struct *work) +{ + struct rhashtable_iter hti; @@ -354,11 +368,57 @@ Subject: [PATCH] net/bridge: add bridge offload + +} + ++static struct bridge_offload_deferred_item * ++br_offload_deferred_dequeue(struct net_bridge_port *p) ++{ ++ struct bridge_offload_deferred_item *bi = NULL; ++ ++ spin_lock_bh(&p->br->lock); ++ if (list_empty(&p->offload.deferred_list)) ++ goto out; ++ ++ bi = list_first_entry(&p->offload.deferred_list, ++ struct bridge_offload_deferred_item, list); ++ list_del(&bi->list); ++out: ++ spin_unlock_bh(&p->br->lock); ++ return bi; ++} ++ ++void br_offload_deferred_work(struct work_struct *work) ++{ ++ struct bridge_offload_deferred_item *bi; ++ struct net_bridge_port_offload *o; ++ struct net_bridge_port *p; ++ ++ p = container_of(work, struct net_bridge_port, offload.deferred_work); ++ o = &p->offload; ++ while ((bi = br_offload_deferred_dequeue(p)) != NULL) { ++ if (bi->enable) { ++ rhashtable_init(&o->rht, &flow_params); ++ spin_lock_bh(&offload_lock); ++ o->enabled = true; ++ spin_unlock_bh(&offload_lock); ++ } else { ++ spin_lock_bh(&offload_lock); ++ o->enabled = false; ++ spin_unlock_bh(&offload_lock); ++ /* Ensure all rht users have finished */ ++ synchronize_rcu(); ++ cancel_work_sync(&o->gc_work); ++ rhashtable_free_and_destroy(&o->rht, ++ br_offload_destroy_cb, o); ++ } ++ kfree(bi); ++ } ++} ++ +void br_offload_port_state(struct net_bridge_port *p) +{ + struct net_bridge_port_offload *o = &p->offload; ++ struct bridge_offload_deferred_item *bi; ++ gfp_t gfp = GFP_ATOMIC; + bool enabled = true; -+ bool flush = false; + + if (p->state != BR_STATE_FORWARDING || + !(p->flags & BR_OFFLOAD)) @@ -368,22 +428,23 @@ Subject: [PATCH] net/bridge: add bridge offload + if (o->enabled == enabled) + goto out; + -+ if (enabled) { -+ if (!o->gc_work.func) -+ INIT_WORK(&o->gc_work, br_offload_gc_work); -+ rhashtable_init(&o->rht, &flow_params); -+ } else { -+ flush = true; -+ rhashtable_free_and_destroy(&o->rht, br_offload_destroy_cb, o); -+ } ++ bi = kmalloc(sizeof(*bi), gfp); ++ if (!bi) { ++ if (enabled) ++ goto out; + -+ o->enabled = enabled; ++ /* If we are disabling (freeing the rht), retry with ++ * __GFP_MEMALLOC. Panic if that also fails. ++ */ ++ gfp |= __GFP_MEMALLOC | __GFP_NOWARN; ++ bi = kmalloc(sizeof(*bi), gfp); ++ } + ++ bi->enable = enabled; ++ list_add_tail(&bi->list, &o->deferred_list); ++ schedule_work(&o->deferred_work); +out: + spin_unlock_bh(&offload_lock); -+ -+ if (flush) -+ flush_work(&o->gc_work); +} + +void br_offload_fdb_update(const struct net_bridge_fdb_entry *fdb) @@ -475,6 +536,9 @@ Subject: [PATCH] net/bridge: add bridge offload +#endif + + flow = kmem_cache_alloc(offload_cache, GFP_ATOMIC); ++ if (unlikely(!flow)) ++ goto out; ++ + flow->port = inp; + memcpy(&flow->key, &key, sizeof(key)); + @@ -655,20 +719,22 @@ Subject: [PATCH] net/bridge: add bridge offload }; #define MDB_PG_FLAGS_PERMANENT BIT(0) -@@ -343,6 +349,12 @@ struct net_bridge_mdb_entry { +@@ -343,6 +349,14 @@ struct net_bridge_mdb_entry { struct rcu_head rcu; }; +struct net_bridge_port_offload { + struct rhashtable rht; + struct work_struct gc_work; ++ struct work_struct deferred_work; ++ struct list_head deferred_list; + bool enabled; +}; + struct net_bridge_port { struct net_bridge *br; struct net_device *dev; -@@ -403,6 +415,7 @@ struct net_bridge_port { +@@ -403,6 +417,7 @@ struct net_bridge_port { u16 backup_redirected_cnt; struct bridge_stp_xstats stp_xstats; @@ -676,7 +742,7 @@ Subject: [PATCH] net/bridge: add bridge offload }; #define kobj_to_brport(obj) container_of(obj, struct net_bridge_port, kobj) -@@ -519,6 +532,9 @@ struct net_bridge { +@@ -519,6 +534,9 @@ struct net_bridge { struct kobject *ifobj; u32 auto_cnt; @@ -686,7 +752,7 @@ Subject: [PATCH] net/bridge: add bridge offload #ifdef CONFIG_NET_SWITCHDEV /* Counter used to make sure that hardware domains get unique * identifiers in case a bridge spans multiple switchdev instances. -@@ -553,6 +569,10 @@ struct br_input_skb_cb { +@@ -553,6 +571,10 @@ struct br_input_skb_cb { #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE u8 br_netfilter_broute:1; #endif @@ -699,7 +765,7 @@ Subject: [PATCH] net/bridge: add bridge offload /* Set if TX data plane offloading is used towards at least one --- /dev/null +++ b/net/bridge/br_private_offload.h -@@ -0,0 +1,23 @@ +@@ -0,0 +1,34 @@ +#ifndef __BR_OFFLOAD_H +#define __BR_OFFLOAD_H + @@ -713,6 +779,17 @@ Subject: [PATCH] net/bridge: add bridge offload + struct netlink_ext_ack *extack); +int br_offload_set_cache_reserved(struct net_bridge *br, unsigned long val, + struct netlink_ext_ack *extack); ++void br_offload_deferred_work(struct work_struct *work); ++void br_offload_gc_work(struct work_struct *work); ++ ++static inline void br_offload_init_work(struct net_bridge_port *p) ++{ ++ struct net_bridge_port_offload *o = &p->offload; ++ ++ INIT_LIST_HEAD(&o->deferred_list); ++ INIT_WORK(&o->deferred_work, br_offload_deferred_work); ++ INIT_WORK(&o->gc_work, br_offload_gc_work); ++} + +static inline void br_offload_skb_disable(struct sk_buff *skb) +{