From nobody Mon Feb 9 11:47:53 2026 Received: from mail-08.mail-europe.com (mail-08.mail-europe.com [57.129.93.249]) (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 9167339341C; Wed, 21 Jan 2026 13:21:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=57.129.93.249 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769001669; cv=none; b=gUrIkSvFF7tndlsiGn4pSlX5fFza5UpAuuhPB1OdLTxcJtWbl7/bI3VIONzyh4OXhscbbLz0KcJtAZWzO3VqHlAOszy4glAa1CTTeuUQF+ppHlw+w8VMTUsapEoaGmZqL+eEFgHiltGF/e/7aRpRr1fbIoMP77xMzxJQW4EPz30= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769001669; c=relaxed/simple; bh=jCFQmfh2EdjZed4puszkJhORKGrQiKPW5zwgnkEzpF0=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=EKzVlJ7KKF3ZkkhLFTbFoKwwO7VsG/rym6O/LLmriBaTWx6KDlZxaqomN4U+dx87q8m5wQ1pGHPr/8Qg/k+DicrRRvjpaiw15cgt8QKbN9waBxnrUF545WrF1n+Uq1QHC9xO9zz01BDTSaXJUrbqT96RWfmwejA3XihNw21BKEU= 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=R0JkSntQ; arc=none smtp.client-ip=57.129.93.249 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="R0JkSntQ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1g4.org; s=protonmail2; t=1769001649; x=1769260849; bh=ByI5Y6LRa442jW3BYasuSZquiCEX29TTkkQTPTu1ss0=; 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=R0JkSntQYEYp+tgw1ssgQjsb/HLdcCsobbOCAQ/PuBn75W2VaRVBQ5v8Y/lFDxjSX Zu9QELgC+JhguZShs/55Im2BcHTqZ8ahDYmkd/vKjOzVHZN4p1am8jG+d4RFXx9NCa oUIrciqLrmp+d6VFYev+oO1G4cQF6g2iDjEFfzbDLMeHOq4tIcSHHI3SHXG67AH38f PnKza24GT+lkheUtU3zCO7AuwIS93cVtEntVkpfneOt79YsqiFuSdVJQvqalrDUJr8 tH+m9kLedKUT4qxqzokUHeh9x2+KJW6RQe+VbJ3NjmdS88N5EdODvT8QwVdd+1MdYQ LW8MmN9XDuoFg== Date: Wed, 21 Jan 2026 13:20:43 +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 3/7] net/sched: act_gate: build schedule and RCU-swap Message-ID: <20260121131954.2710459-4-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: e04293c8aae82946e238d7ce6cced05fb0fcd6c0 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" Build a fresh params snapshot and swap it in with rcu_replace_pointer(), then free the old snapshot via call_rcu(). This is the same publish+defer pattern used in taprio sched swapping (sch_taprio.c, commit d5c4546062fd6f) and in act_pedit param updates (act_pedit.c, commit 52cf89f78c01bf). When REPLACE omits TCA_GATE_ENTRY_LIST, carry forward the old snapshot fiel= ds (basetime/clockid/flags/cycletime/priority) and only override provided attr= s, so partial updates don=E2=80=99t reset unrelated state. Parse entry lists with GFP_KERNEL and explicit error handling, matching tap= rio=E2=80=99s schedule parsing (sch_taprio.c, commit 5a781ccbd19e46). Fixes: a51c328df310 ("net: qos: introduce a gate control flow action") Signed-off-by: Paul Moses Cc: stable@vger.kernel.org --- net/sched/act_gate.c | 185 ++++++++++++++++++++++++++++++++----------- 1 file changed, 140 insertions(+), 45 deletions(-) diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c index faaf34bcaff5d..016708c10a8e0 100644 --- a/net/sched/act_gate.c +++ b/net/sched/act_gate.c @@ -32,10 +32,12 @@ static ktime_t gate_get_time(struct tcf_gate *gact) return KTIME_MAX; } =20 -static void gate_get_start_time(struct tcf_gate *gact, ktime_t *start) +static void tcf_gate_params_free_rcu(struct rcu_head *head); + +static void gate_get_start_time(struct tcf_gate *gact, + const struct tcf_gate_params *param, + ktime_t *start) { - 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 @@ -228,13 +230,44 @@ static void release_entry_list(struct list_head *entr= ies) } } =20 +static int tcf_gate_copy_entries(struct tcf_gate_params *dst, + const struct tcf_gate_params *src, + struct netlink_ext_ack *extack) +{ + struct tcfg_gate_entry *entry; + int i =3D 0; + + list_for_each_entry(entry, &src->entries, list) { + struct tcfg_gate_entry *new; + + new =3D kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + NL_SET_ERR_MSG(extack, "Not enough memory for entry"); + return -ENOMEM; + } + + new->index =3D entry->index; + new->gate_state =3D entry->gate_state; + new->interval =3D entry->interval; + new->ipv =3D entry->ipv; + new->maxoctets =3D entry->maxoctets; + INIT_LIST_HEAD(&new->list); + list_add_tail(&new->list, &dst->entries); + i++; + } + + dst->num_entries =3D i; + + return i; +} + static int parse_gate_list(struct nlattr *list_attr, struct tcf_gate_params *sched, struct netlink_ext_ack *extack) { struct tcfg_gate_entry *entry; struct nlattr *n; - int err, rem; + int err =3D -EINVAL, rem; int i =3D 0; =20 if (!list_attr) @@ -246,7 +279,7 @@ static int parse_gate_list(struct nlattr *list_attr, continue; } =20 - entry =3D kzalloc(sizeof(*entry), GFP_ATOMIC); + entry =3D kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) { NL_SET_ERR_MSG(extack, "Not enough memory for entry"); err =3D -ENOMEM; @@ -269,6 +302,7 @@ static int parse_gate_list(struct nlattr *list_attr, =20 release_list: release_entry_list(&sched->entries); + sched->num_entries =3D 0; =20 return err; } @@ -291,12 +325,6 @@ static void gate_setup_timer(struct tcf_gate *gact, u6= 4 basetime, hrtimer_cancel(&gact->hitimer); spin_lock_bh(&gact->tcf_lock); } - 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); } @@ -307,20 +335,20 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, struct netlink_ext_ack *extack) { struct tc_action_net *tn =3D net_generic(net, act_gate_ops.net_id); - enum tk_offsets tk_offset =3D TK_OFFS_TAI; - bool bind =3D flags & TCA_ACT_FLAGS_BIND; - struct nlattr *tb[TCA_GATE_MAX + 1]; + struct tcf_gate_params *p, *old_p =3D NULL; struct tcf_chain *goto_ch =3D NULL; - u64 cycletime =3D 0, basetime =3D 0; - struct tcf_gate_params *p; - s32 clockid =3D CLOCK_TAI; struct tcf_gate *gact; struct tc_gate *parm; - int ret =3D 0, err; - u32 gflags =3D 0; - s32 prio =3D -1; + struct nlattr *tb[TCA_GATE_MAX + 1]; + enum tk_offsets tk_offset =3D TK_OFFS_TAI; + u64 cycletime =3D 0, basetime =3D 0, cycletime_ext =3D 0; ktime_t start; + s32 clockid =3D CLOCK_TAI; + s32 prio =3D -1; + u32 gflags =3D 0; u32 index; + int ret =3D 0, err; + bool bind =3D flags & TCA_ACT_FLAGS_BIND; =20 if (!nla) return -EINVAL; @@ -388,32 +416,92 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, =20 gact =3D to_gate(*a); =20 - err =3D tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); - if (err < 0) + p =3D kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + err =3D -ENOMEM; goto release_idr; + } + INIT_LIST_HEAD(&p->entries); =20 - spin_lock_bh(&gact->tcf_lock); + if (!tb[TCA_GATE_ENTRY_LIST] && ret !=3D ACT_P_CREATED) { + const struct tcf_gate_params *old_p_local; =20 - if (ret =3D=3D ACT_P_CREATED) { - p =3D kzalloc(sizeof(*p), GFP_ATOMIC); - if (!p) { - err =3D -ENOMEM; - goto chain_put; + old_p_local =3D rcu_dereference_protected(gact->param, + lockdep_rtnl_is_held()); + if (!old_p_local) { + NL_SET_ERR_MSG(extack, "Missing schedule entries"); + err =3D -EINVAL; + goto release_mem; } - 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)); + + if (!tb[TCA_GATE_PRIORITY]) + prio =3D old_p_local->tcfg_priority; + + if (!tb[TCA_GATE_BASE_TIME]) + basetime =3D old_p_local->tcfg_basetime; + + if (!tb[TCA_GATE_FLAGS]) + gflags =3D old_p_local->tcfg_flags; + + if (!tb[TCA_GATE_CLOCKID]) { + clockid =3D old_p_local->tcfg_clockid; + switch (clockid) { + case CLOCK_REALTIME: + tk_offset =3D TK_OFFS_REAL; + break; + case CLOCK_MONOTONIC: + tk_offset =3D TK_OFFS_MAX; + break; + case CLOCK_BOOTTIME: + tk_offset =3D TK_OFFS_BOOT; + break; + case CLOCK_TAI: + tk_offset =3D TK_OFFS_TAI; + break; + default: + NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); + err =3D -EINVAL; + goto release_mem; + } + } + + if (!tb[TCA_GATE_CYCLE_TIME]) + cycletime =3D old_p_local->tcfg_cycletime; + + if (!tb[TCA_GATE_CYCLE_TIME_EXT]) + cycletime_ext =3D old_p_local->tcfg_cycletime_ext; } =20 + p->tcfg_priority =3D prio; + p->tcfg_flags =3D gflags; + p->tcfg_basetime =3D basetime; + p->tcfg_clockid =3D clockid; + if (tb[TCA_GATE_CYCLE_TIME]) cycletime =3D nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); =20 if (tb[TCA_GATE_ENTRY_LIST]) { err =3D parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); if (err < 0) - goto chain_put; + goto release_mem; + } else if (ret =3D=3D ACT_P_CREATED) { + NL_SET_ERR_MSG(extack, "The entry list is empty"); + err =3D -EINVAL; + goto release_mem; + } else { + const struct tcf_gate_params *old_p_local; + + old_p_local =3D rcu_dereference_protected(gact->param, + lockdep_rtnl_is_held()); + if (!old_p_local) { + NL_SET_ERR_MSG(extack, "Missing schedule entries"); + err =3D -EINVAL; + goto release_mem; + } + + err =3D tcf_gate_copy_entries(p, old_p_local, extack); + if (err < 0) + goto release_mem; } =20 if (!cycletime) { @@ -425,20 +513,26 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, cycletime =3D cycle; if (!cycletime) { err =3D -EINVAL; - goto chain_put; + goto release_mem; } } p->tcfg_cycletime =3D cycletime; =20 if (tb[TCA_GATE_CYCLE_TIME_EXT]) - p->tcfg_cycletime_ext =3D - nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); + cycletime_ext =3D nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); + p->tcfg_cycletime_ext =3D cycletime_ext; =20 + err =3D tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); + if (err < 0) + goto release_mem; + + spin_lock_bh(&gact->tcf_lock); gate_setup_timer(gact, basetime, tk_offset, clockid, ret =3D=3D ACT_P_CREATED); - p->tcfg_priority =3D prio; - p->tcfg_flags =3D gflags; - gate_get_start_time(gact, &start); + gate_get_start_time(gact, p, &start); + + old_p =3D rcu_replace_pointer(gact->param, p, + lockdep_is_held(&gact->tcf_lock)); =20 gact->current_close_time =3D start; gact->current_gate_status =3D GATE_ACT_GATE_OPEN | GATE_ACT_PENDING; @@ -455,13 +549,14 @@ static int tcf_gate_init(struct net *net, struct nlat= tr *nla, if (goto_ch) tcf_chain_put_by_act(goto_ch); =20 - return ret; + if (old_p) + call_rcu(&old_p->rcu, tcf_gate_params_free_rcu); =20 -chain_put: - spin_unlock_bh(&gact->tcf_lock); + return ret; =20 - if (goto_ch) - tcf_chain_put_by_act(goto_ch); +release_mem: + release_entry_list(&p->entries); + kfree(p); release_idr: /* action is not inserted in any list: it's safe to init hitimer * without taking tcf_lock. --=20 2.52.GIT