From nobody Mon Feb 9 16:18:08 2026 Received: from mail-244102.protonmail.ch (mail-244102.protonmail.ch [109.224.244.102]) (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 566C32638BA for ; Wed, 21 Jan 2026 13:30:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.224.244.102 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769002226; cv=none; b=pKzl2t7G64vLdVgLZTN3AHumLOfdAv1Az8oWSTxsE8nd06gKjQ26bsiZyzgK/FuoxNWWw5/XpDfwPbWB3eX+HyVFXShIzbPC9j01UikEw2Nl1SRQ5TimLAlJhfmFhurpa2S2585jtsuyR/ra3aYj21co7uIfAunvw9bYRABczns= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769002226; c=relaxed/simple; bh=CByq8Rjc7VnWJfDcxQGqjCghF/JuTIVolRuNii0tPg4=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=riHn3hDGHgdHxsB2uirCjpozZqGX8r4Oy9kNKsvouLadaFVO14AT7XUDFeiWziJh/ppNCbG6ZWDmEjCzbOw60OJ5jS/rii1jFpZlkHf0OX4bl9i7WJwmek7H4/88CEueJgyWl4oLeAqvxAXWA5N1AVtdbmFh7+QgMOKk7UruwFk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=1g4.org; spf=pass smtp.mailfrom=1g4.org; dkim=pass (2048-bit key) header.d=1g4.org header.i=@1g4.org header.b=VCfohR/n; arc=none smtp.client-ip=109.224.244.102 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=1g4.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=1g4.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=1g4.org header.i=@1g4.org header.b="VCfohR/n" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1g4.org; s=protonmail2; t=1769001643; x=1769260843; bh=U370DSssnpJ04+nHhdTe4tH1gCzAdqbKpil7xi9UpNU=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=VCfohR/nwz52wB3iLx37KLtEnpqLoKsBh2rvzOM8SOIA2+kAsV9ES5i/9osHfqS7c 2Tnet479Ed/fWeohSy2qA2a19D3sPx+HUQ29jv7pbxwkt6TEE2SDn5IpXvE8S74up4 /Oy8NcltDzfd3nNlrmjVNYHMF6jrPktM2rYEViTCWfcRA+HVEUHD2TFZghYR6z8Ofy tnCIJdHKT+Fn2AbABu2Bz0L+IzFjdJDnaSDTR2Vl8QmfXnfuvl+cvdRbyGNPSUcRdt KZ3BnmomXTIQuShiP5jOnfGOprQ6dPxUkQL4KsutmCsvER5e/eCQbs8x/kAw38yraL rE0NV6t/IN7Lg== Date: Wed, 21 Jan 2026 13:20:39 +0000 To: netdev@vger.kernel.org From: Paul Moses Cc: Jamal Hadi Salim , Cong Wang , Jiri Pirko , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , linux-kernel@vger.kernel.org, Paul Moses , stable@vger.kernel.org Subject: [PATCH net v3 2/7] net/sched: act_gate: add RCU support for parameter update Message-ID: <20260121131954.2710459-3-p@1g4.org> In-Reply-To: <20260121131954.2710459-1-p@1g4.org> References: <20260121131954.2710459-1-p@1g4.org> Feedback-ID: 8253658:user:proton X-Pm-Message-ID: 13488128ec5e4befd59ea54409ca5ada35b66313 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Make gact->param RCU-protected and reclaim old params via call_rcu(). This follows the pattern used by other actions: act_pedit swaps params with rcu_replace_pointer() and defers free via call_rcu() (commit 52cf89f78c01bf= ), act_connmark uses rcu_replace_pointer() under tcf_lock (commit 288864effe33= 88), and act_tunnel_key does the same under lockdep (commit 445d3749315f34). Dump readers in act_ct and act_pedit already use rcu_read_lock() + rcu_dereference() (commits 554e66bad84ce4 and 9d096746572616), so act_gate must keep old params alive past updates as well. Fixes: a51c328df310 ("net: qos: introduce a gate control flow action") Signed-off-by: Paul Moses Cc: stable@vger.kernel.org --- include/net/tc_act/tc_gate.h | 31 ++++++++++++++----- net/sched/act_gate.c | 59 +++++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/include/net/tc_act/tc_gate.h b/include/net/tc_act/tc_gate.h index c1a67149c6b62..05968b3822392 100644 --- a/include/net/tc_act/tc_gate.h +++ b/include/net/tc_act/tc_gate.h @@ -32,6 +32,7 @@ struct tcf_gate_params { s32 tcfg_clockid; size_t num_entries; struct list_head entries; + struct rcu_head rcu; }; =20 #define GATE_ACT_GATE_OPEN BIT(0) @@ -39,7 +40,7 @@ struct tcf_gate_params { =20 struct tcf_gate { struct tc_action common; - struct tcf_gate_params param; + struct tcf_gate_params __rcu *param; u8 current_gate_status; ktime_t current_close_time; u32 current_entry_octets; @@ -54,8 +55,11 @@ struct tcf_gate { static inline s32 tcf_gate_prio(const struct tc_action *a) { s32 tcfg_prio; + struct tcf_gate_params *p; =20 - tcfg_prio =3D to_gate(a)->param.tcfg_priority; + p =3D rcu_dereference_protected(to_gate(a)->param, + lockdep_rtnl_is_held()); + tcfg_prio =3D p->tcfg_priority; =20 return tcfg_prio; } @@ -63,8 +67,11 @@ static inline s32 tcf_gate_prio(const struct tc_action *= a) static inline u64 tcf_gate_basetime(const struct tc_action *a) { u64 tcfg_basetime; + struct tcf_gate_params *p; =20 - tcfg_basetime =3D to_gate(a)->param.tcfg_basetime; + p =3D rcu_dereference_protected(to_gate(a)->param, + lockdep_rtnl_is_held()); + tcfg_basetime =3D p->tcfg_basetime; =20 return tcfg_basetime; } @@ -72,8 +79,11 @@ static inline u64 tcf_gate_basetime(const struct tc_acti= on *a) static inline u64 tcf_gate_cycletime(const struct tc_action *a) { u64 tcfg_cycletime; + struct tcf_gate_params *p; =20 - tcfg_cycletime =3D to_gate(a)->param.tcfg_cycletime; + p =3D rcu_dereference_protected(to_gate(a)->param, + lockdep_rtnl_is_held()); + tcfg_cycletime =3D p->tcfg_cycletime; =20 return tcfg_cycletime; } @@ -81,8 +91,11 @@ static inline u64 tcf_gate_cycletime(const struct tc_act= ion *a) static inline u64 tcf_gate_cycletimeext(const struct tc_action *a) { u64 tcfg_cycletimeext; + struct tcf_gate_params *p; =20 - tcfg_cycletimeext =3D to_gate(a)->param.tcfg_cycletime_ext; + p =3D rcu_dereference_protected(to_gate(a)->param, + lockdep_rtnl_is_held()); + tcfg_cycletimeext =3D p->tcfg_cycletime_ext; =20 return tcfg_cycletimeext; } @@ -90,8 +103,11 @@ static inline u64 tcf_gate_cycletimeext(const struct tc= _action *a) static inline u32 tcf_gate_num_entries(const struct tc_action *a) { u32 num_entries; + struct tcf_gate_params *p; =20 - num_entries =3D to_gate(a)->param.num_entries; + p =3D rcu_dereference_protected(to_gate(a)->param, + lockdep_rtnl_is_held()); + num_entries =3D p->num_entries; =20 return num_entries; } @@ -105,7 +121,8 @@ static inline struct action_gate_entry u32 num_entries; int i =3D 0; =20 - p =3D &to_gate(a)->param; + p =3D rcu_dereference_protected(to_gate(a)->param, + lockdep_rtnl_is_held()); num_entries =3D p->num_entries; =20 list_for_each_entry(entry, &p->entries, list) diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index aacd57e5f4374..faaf34bcaff5d 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -34,7 +34,8 @@ static ktime_t gate_get_time(struct tcf_gate *gact) =20 static void gate_get_start_time(struct tcf_gate *gact, ktime_t *start) { - struct tcf_gate_params *param =3D &gact->param; + struct tcf_gate_params *param =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); ktime_t now, base, cycle; u64 n; =20 @@ -69,12 +70,14 @@ static enum hrtimer_restart gate_timer_func(struct hrti= mer *timer) { struct tcf_gate *gact =3D container_of(timer, struct tcf_gate, hitimer); - struct tcf_gate_params *p =3D &gact->param; + struct tcf_gate_params *p; struct tcfg_gate_entry *next; ktime_t close_time, now; =20 spin_lock(&gact->tcf_lock); =20 + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); next =3D gact->next_entry; =20 /* cycle start, clear pending bit, clear total octets */ @@ -274,18 +277,26 @@ static void gate_setup_timer(struct tcf_gate *gact, u= 64 basetime, enum tk_offsets tko, s32 clockid, bool do_init) { + struct tcf_gate_params *p; + if (!do_init) { - if (basetime =3D=3D gact->param.tcfg_basetime && + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); + if (basetime =3D=3D p->tcfg_basetime && tko =3D=3D gact->tk_offset && - clockid =3D=3D gact->param.tcfg_clockid) + clockid =3D=3D p->tcfg_clockid) return; =20 spin_unlock_bh(&gact->tcf_lock); hrtimer_cancel(&gact->hitimer); spin_lock_bh(&gact->tcf_lock); } - gact->param.tcfg_basetime =3D basetime; - gact->param.tcfg_clockid =3D clockid; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); + if (p) { + p->tcfg_basetime =3D basetime; + p->tcfg_clockid =3D clockid; + } gact->tk_offset =3D tko; hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, HRTIMER_MODE_ABS_= SOFT); } @@ -376,15 +387,25 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, gflags =3D nla_get_u32(tb[TCA_GATE_FLAGS]); =20 gact =3D to_gate(*a); - if (ret =3D=3D ACT_P_CREATED) - INIT_LIST_HEAD(&gact->param.entries); =20 err =3D tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); if (err < 0) goto release_idr; =20 spin_lock_bh(&gact->tcf_lock); - p =3D &gact->param; + + if (ret =3D=3D ACT_P_CREATED) { + p =3D kzalloc(sizeof(*p), GFP_ATOMIC); + if (!p) { + err =3D -ENOMEM; + goto chain_put; + } + INIT_LIST_HEAD(&p->entries); + rcu_assign_pointer(gact->param, p); + } else { + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); + } =20 if (tb[TCA_GATE_CYCLE_TIME]) cycletime =3D nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); @@ -446,21 +467,30 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, * without taking tcf_lock. */ if (ret =3D=3D ACT_P_CREATED) - gate_setup_timer(gact, gact->param.tcfg_basetime, - gact->tk_offset, gact->param.tcfg_clockid, + gate_setup_timer(gact, 0, + gact->tk_offset, 0, true); tcf_idr_release(*a, bind); return err; } =20 +static void tcf_gate_params_free_rcu(struct rcu_head *head) +{ + struct tcf_gate_params *p =3D container_of(head, struct tcf_gate_params, = rcu); + + release_entry_list(&p->entries); + kfree(p); +} + static void tcf_gate_cleanup(struct tc_action *a) { struct tcf_gate *gact =3D to_gate(a); struct tcf_gate_params *p; =20 - p =3D &gact->param; + p =3D rcu_replace_pointer(gact->param, NULL, lockdep_rtnl_is_held()); hrtimer_cancel(&gact->hitimer); - release_entry_list(&p->entries); + if (p) + call_rcu(&p->rcu, tcf_gate_params_free_rcu); } =20 static int dumping_entry(struct sk_buff *skb, @@ -512,7 +542,8 @@ static int tcf_gate_dump(struct sk_buff *skb, struct tc= _action *a, spin_lock_bh(&gact->tcf_lock); opt.action =3D gact->tcf_action; =20 - p =3D &gact->param; + p =3D rcu_dereference_protected(gact->param, + lockdep_is_held(&gact->tcf_lock)); =20 if (nla_put(skb, TCA_GATE_PARMS, sizeof(opt), &opt)) goto nla_put_failure; --=20 2.52.GIT