From: Greg Kroah-Hartman Date: Thu, 12 Mar 2026 16:12:22 +0000 (+0100) Subject: 6.19-stable patches X-Git-Tag: v6.12.77~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=11d37581e2e7f9fbbce06635acd9c7860d4eb0e5;p=thirdparty%2Fkernel%2Fstable-queue.git 6.19-stable patches added patches: net-sched-act_gate-snapshot-parameters-with-rcu-on-replace.patch net-sched-only-allow-act_ct-to-bind-to-clsact-ingress-qdiscs-and-shared-blocks.patch --- diff --git a/queue-6.19/net-sched-act_gate-snapshot-parameters-with-rcu-on-replace.patch b/queue-6.19/net-sched-act_gate-snapshot-parameters-with-rcu-on-replace.patch new file mode 100644 index 00000000000..dbc5d928069 --- /dev/null +++ b/queue-6.19/net-sched-act_gate-snapshot-parameters-with-rcu-on-replace.patch @@ -0,0 +1,532 @@ +From 62413a9c3cb183afb9bb6e94dd68caf4e4145f4c Mon Sep 17 00:00:00 2001 +From: Paul Moses +Date: Mon, 23 Feb 2026 15:05:44 +0000 +Subject: net/sched: act_gate: snapshot parameters with RCU on replace + +From: Paul Moses + +commit 62413a9c3cb183afb9bb6e94dd68caf4e4145f4c upstream. + +The gate action can be replaced while the hrtimer callback or dump path is +walking the schedule list. + +Convert the parameters to an RCU-protected snapshot and swap updates under +tcf_lock, freeing the previous snapshot via call_rcu(). When REPLACE omits +the entry list, preserve the existing schedule so the effective state is +unchanged. + +Fixes: a51c328df310 ("net: qos: introduce a gate control flow action") +Cc: stable@vger.kernel.org +Signed-off-by: Paul Moses +Tested-by: Vladimir Oltean +Acked-by: Jamal Hadi Salim +Reviewed-by: Victor Nogueira +Link: https://patch.msgid.link/20260223150512.2251594-2-p@1g4.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman +--- + include/net/tc_act/tc_gate.h | 33 ++++- + net/sched/act_gate.c | 269 ++++++++++++++++++++++++++++++------------- + 2 files changed, 214 insertions(+), 88 deletions(-) + +--- 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; + }; + + #define GATE_ACT_GATE_OPEN BIT(0) +@@ -39,7 +40,7 @@ struct tcf_gate_params { + + 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; +@@ -51,47 +52,65 @@ struct tcf_gate { + + #define to_gate(a) ((struct tcf_gate *)a) + ++static inline struct tcf_gate_params *tcf_gate_params_locked(const struct tc_action *a) ++{ ++ struct tcf_gate *gact = to_gate(a); ++ ++ return rcu_dereference_protected(gact->param, ++ lockdep_is_held(&gact->tcf_lock)); ++} ++ + static inline s32 tcf_gate_prio(const struct tc_action *a) + { ++ struct tcf_gate_params *p; + s32 tcfg_prio; + +- tcfg_prio = to_gate(a)->param.tcfg_priority; ++ p = tcf_gate_params_locked(a); ++ tcfg_prio = p->tcfg_priority; + + return tcfg_prio; + } + + static inline u64 tcf_gate_basetime(const struct tc_action *a) + { ++ struct tcf_gate_params *p; + u64 tcfg_basetime; + +- tcfg_basetime = to_gate(a)->param.tcfg_basetime; ++ p = tcf_gate_params_locked(a); ++ tcfg_basetime = p->tcfg_basetime; + + return tcfg_basetime; + } + + static inline u64 tcf_gate_cycletime(const struct tc_action *a) + { ++ struct tcf_gate_params *p; + u64 tcfg_cycletime; + +- tcfg_cycletime = to_gate(a)->param.tcfg_cycletime; ++ p = tcf_gate_params_locked(a); ++ tcfg_cycletime = p->tcfg_cycletime; + + return tcfg_cycletime; + } + + static inline u64 tcf_gate_cycletimeext(const struct tc_action *a) + { ++ struct tcf_gate_params *p; + u64 tcfg_cycletimeext; + +- tcfg_cycletimeext = to_gate(a)->param.tcfg_cycletime_ext; ++ p = tcf_gate_params_locked(a); ++ tcfg_cycletimeext = p->tcfg_cycletime_ext; + + return tcfg_cycletimeext; + } + + static inline u32 tcf_gate_num_entries(const struct tc_action *a) + { ++ struct tcf_gate_params *p; + u32 num_entries; + +- num_entries = to_gate(a)->param.num_entries; ++ p = tcf_gate_params_locked(a); ++ num_entries = p->num_entries; + + return num_entries; + } +@@ -105,7 +124,7 @@ static inline struct action_gate_entry + u32 num_entries; + int i = 0; + +- p = &to_gate(a)->param; ++ p = tcf_gate_params_locked(a); + num_entries = p->num_entries; + + list_for_each_entry(entry, &p->entries, list) +--- a/net/sched/act_gate.c ++++ b/net/sched/act_gate.c +@@ -32,9 +32,12 @@ static ktime_t gate_get_time(struct tcf_ + return KTIME_MAX; + } + +-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 = &gact->param; + ktime_t now, base, cycle; + u64 n; + +@@ -69,12 +72,14 @@ static enum hrtimer_restart gate_timer_f + { + struct tcf_gate *gact = container_of(timer, struct tcf_gate, + hitimer); +- struct tcf_gate_params *p = &gact->param; + struct tcfg_gate_entry *next; ++ struct tcf_gate_params *p; + ktime_t close_time, now; + + spin_lock(&gact->tcf_lock); + ++ p = rcu_dereference_protected(gact->param, ++ lockdep_is_held(&gact->tcf_lock)); + next = gact->next_entry; + + /* cycle start, clear pending bit, clear total octets */ +@@ -225,6 +230,35 @@ static void release_entry_list(struct li + } + } + ++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 = 0; ++ ++ list_for_each_entry(entry, &src->entries, list) { ++ struct tcfg_gate_entry *new; ++ ++ new = kzalloc(sizeof(*new), GFP_ATOMIC); ++ if (!new) { ++ NL_SET_ERR_MSG(extack, "Not enough memory for entry"); ++ return -ENOMEM; ++ } ++ ++ new->index = entry->index; ++ new->gate_state = entry->gate_state; ++ new->interval = entry->interval; ++ new->ipv = entry->ipv; ++ new->maxoctets = entry->maxoctets; ++ list_add_tail(&new->list, &dst->entries); ++ i++; ++ } ++ ++ dst->num_entries = i; ++ return 0; ++} ++ + static int parse_gate_list(struct nlattr *list_attr, + struct tcf_gate_params *sched, + struct netlink_ext_ack *extack) +@@ -270,24 +304,44 @@ release_list: + return err; + } + +-static void gate_setup_timer(struct tcf_gate *gact, u64 basetime, +- enum tk_offsets tko, s32 clockid, +- bool do_init) +-{ +- if (!do_init) { +- if (basetime == gact->param.tcfg_basetime && +- tko == gact->tk_offset && +- clockid == gact->param.tcfg_clockid) +- return; +- +- spin_unlock_bh(&gact->tcf_lock); +- hrtimer_cancel(&gact->hitimer); +- spin_lock_bh(&gact->tcf_lock); ++static bool gate_timer_needs_cancel(u64 basetime, u64 old_basetime, ++ enum tk_offsets tko, ++ enum tk_offsets old_tko, ++ s32 clockid, s32 old_clockid) ++{ ++ return basetime != old_basetime || ++ clockid != old_clockid || ++ tko != old_tko; ++} ++ ++static int gate_clock_resolve(s32 clockid, enum tk_offsets *tko, ++ struct netlink_ext_ack *extack) ++{ ++ switch (clockid) { ++ case CLOCK_REALTIME: ++ *tko = TK_OFFS_REAL; ++ return 0; ++ case CLOCK_MONOTONIC: ++ *tko = TK_OFFS_MAX; ++ return 0; ++ case CLOCK_BOOTTIME: ++ *tko = TK_OFFS_BOOT; ++ return 0; ++ case CLOCK_TAI: ++ *tko = TK_OFFS_TAI; ++ return 0; ++ default: ++ NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); ++ return -EINVAL; + } +- gact->param.tcfg_basetime = basetime; +- gact->param.tcfg_clockid = clockid; +- gact->tk_offset = tko; +- hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, HRTIMER_MODE_ABS_SOFT); ++} ++ ++static void gate_setup_timer(struct tcf_gate *gact, s32 clockid, ++ enum tk_offsets tko) ++{ ++ WRITE_ONCE(gact->tk_offset, tko); ++ hrtimer_setup(&gact->hitimer, gate_timer_func, clockid, ++ HRTIMER_MODE_ABS_SOFT); + } + + static int tcf_gate_init(struct net *net, struct nlattr *nla, +@@ -296,15 +350,22 @@ static int tcf_gate_init(struct net *net + struct netlink_ext_ack *extack) + { + struct tc_action_net *tn = net_generic(net, act_gate_ops.net_id); +- enum tk_offsets tk_offset = TK_OFFS_TAI; ++ u64 cycletime = 0, basetime = 0, cycletime_ext = 0; ++ struct tcf_gate_params *p = NULL, *old_p = NULL; ++ enum tk_offsets old_tk_offset = TK_OFFS_TAI; ++ const struct tcf_gate_params *cur_p = NULL; + bool bind = flags & TCA_ACT_FLAGS_BIND; + struct nlattr *tb[TCA_GATE_MAX + 1]; ++ enum tk_offsets tko = TK_OFFS_TAI; + struct tcf_chain *goto_ch = NULL; +- u64 cycletime = 0, basetime = 0; +- struct tcf_gate_params *p; ++ s32 timer_clockid = CLOCK_TAI; ++ bool use_old_entries = false; ++ s32 old_clockid = CLOCK_TAI; ++ bool need_cancel = false; + s32 clockid = CLOCK_TAI; + struct tcf_gate *gact; + struct tc_gate *parm; ++ u64 old_basetime = 0; + int ret = 0, err; + u32 gflags = 0; + s32 prio = -1; +@@ -321,26 +382,8 @@ static int tcf_gate_init(struct net *net + if (!tb[TCA_GATE_PARMS]) + return -EINVAL; + +- if (tb[TCA_GATE_CLOCKID]) { ++ if (tb[TCA_GATE_CLOCKID]) + clockid = nla_get_s32(tb[TCA_GATE_CLOCKID]); +- switch (clockid) { +- case CLOCK_REALTIME: +- tk_offset = TK_OFFS_REAL; +- break; +- case CLOCK_MONOTONIC: +- tk_offset = TK_OFFS_MAX; +- break; +- case CLOCK_BOOTTIME: +- tk_offset = TK_OFFS_BOOT; +- break; +- case CLOCK_TAI: +- tk_offset = TK_OFFS_TAI; +- break; +- default: +- NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); +- return -EINVAL; +- } +- } + + parm = nla_data(tb[TCA_GATE_PARMS]); + index = parm->index; +@@ -366,6 +409,60 @@ static int tcf_gate_init(struct net *net + return -EEXIST; + } + ++ gact = to_gate(*a); ++ ++ err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); ++ if (err < 0) ++ goto release_idr; ++ ++ p = kzalloc(sizeof(*p), GFP_KERNEL); ++ if (!p) { ++ err = -ENOMEM; ++ goto chain_put; ++ } ++ INIT_LIST_HEAD(&p->entries); ++ ++ use_old_entries = !tb[TCA_GATE_ENTRY_LIST]; ++ if (!use_old_entries) { ++ err = parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); ++ if (err < 0) ++ goto err_free; ++ use_old_entries = !err; ++ } ++ ++ if (ret == ACT_P_CREATED && use_old_entries) { ++ NL_SET_ERR_MSG(extack, "The entry list is empty"); ++ err = -EINVAL; ++ goto err_free; ++ } ++ ++ if (ret != ACT_P_CREATED) { ++ rcu_read_lock(); ++ cur_p = rcu_dereference(gact->param); ++ ++ old_basetime = cur_p->tcfg_basetime; ++ old_clockid = cur_p->tcfg_clockid; ++ old_tk_offset = READ_ONCE(gact->tk_offset); ++ ++ basetime = old_basetime; ++ cycletime_ext = cur_p->tcfg_cycletime_ext; ++ prio = cur_p->tcfg_priority; ++ gflags = cur_p->tcfg_flags; ++ ++ if (!tb[TCA_GATE_CLOCKID]) ++ clockid = old_clockid; ++ ++ err = 0; ++ if (use_old_entries) { ++ err = tcf_gate_copy_entries(p, cur_p, extack); ++ if (!err && !tb[TCA_GATE_CYCLE_TIME]) ++ cycletime = cur_p->tcfg_cycletime; ++ } ++ rcu_read_unlock(); ++ if (err) ++ goto err_free; ++ } ++ + if (tb[TCA_GATE_PRIORITY]) + prio = nla_get_s32(tb[TCA_GATE_PRIORITY]); + +@@ -375,25 +472,26 @@ static int tcf_gate_init(struct net *net + if (tb[TCA_GATE_FLAGS]) + gflags = nla_get_u32(tb[TCA_GATE_FLAGS]); + +- gact = to_gate(*a); +- if (ret == ACT_P_CREATED) +- INIT_LIST_HEAD(&gact->param.entries); ++ if (tb[TCA_GATE_CYCLE_TIME]) ++ cycletime = nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); + +- err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); +- if (err < 0) +- goto release_idr; ++ if (tb[TCA_GATE_CYCLE_TIME_EXT]) ++ cycletime_ext = nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); + +- spin_lock_bh(&gact->tcf_lock); +- p = &gact->param; ++ err = gate_clock_resolve(clockid, &tko, extack); ++ if (err) ++ goto err_free; ++ timer_clockid = clockid; ++ ++ need_cancel = ret != ACT_P_CREATED && ++ gate_timer_needs_cancel(basetime, old_basetime, ++ tko, old_tk_offset, ++ timer_clockid, old_clockid); + +- if (tb[TCA_GATE_CYCLE_TIME]) +- cycletime = nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); ++ if (need_cancel) ++ hrtimer_cancel(&gact->hitimer); + +- if (tb[TCA_GATE_ENTRY_LIST]) { +- err = parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); +- if (err < 0) +- goto chain_put; +- } ++ spin_lock_bh(&gact->tcf_lock); + + if (!cycletime) { + struct tcfg_gate_entry *entry; +@@ -402,22 +500,20 @@ static int tcf_gate_init(struct net *net + list_for_each_entry(entry, &p->entries, list) + cycle = ktime_add_ns(cycle, entry->interval); + cycletime = cycle; +- if (!cycletime) { +- err = -EINVAL; +- goto chain_put; +- } + } + p->tcfg_cycletime = cycletime; ++ p->tcfg_cycletime_ext = cycletime_ext; + +- if (tb[TCA_GATE_CYCLE_TIME_EXT]) +- p->tcfg_cycletime_ext = +- nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); +- +- gate_setup_timer(gact, basetime, tk_offset, clockid, +- ret == ACT_P_CREATED); ++ if (need_cancel || ret == ACT_P_CREATED) ++ gate_setup_timer(gact, timer_clockid, tko); + p->tcfg_priority = prio; + p->tcfg_flags = gflags; +- gate_get_start_time(gact, &start); ++ p->tcfg_basetime = basetime; ++ p->tcfg_clockid = timer_clockid; ++ gate_get_start_time(gact, p, &start); ++ ++ old_p = rcu_replace_pointer(gact->param, p, ++ lockdep_is_held(&gact->tcf_lock)); + + gact->current_close_time = start; + gact->current_gate_status = GATE_ACT_GATE_OPEN | GATE_ACT_PENDING; +@@ -434,11 +530,15 @@ static int tcf_gate_init(struct net *net + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + ++ if (old_p) ++ call_rcu(&old_p->rcu, tcf_gate_params_free_rcu); ++ + return ret; + ++err_free: ++ release_entry_list(&p->entries); ++ kfree(p); + chain_put: +- spin_unlock_bh(&gact->tcf_lock); +- + if (goto_ch) + tcf_chain_put_by_act(goto_ch); + release_idr: +@@ -446,21 +546,29 @@ release_idr: + * without taking tcf_lock. + */ + if (ret == ACT_P_CREATED) +- gate_setup_timer(gact, gact->param.tcfg_basetime, +- gact->tk_offset, gact->param.tcfg_clockid, +- true); ++ gate_setup_timer(gact, timer_clockid, tko); ++ + tcf_idr_release(*a, bind); + return err; + } + ++static void tcf_gate_params_free_rcu(struct rcu_head *head) ++{ ++ struct tcf_gate_params *p = 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 = to_gate(a); + struct tcf_gate_params *p; + +- p = &gact->param; + hrtimer_cancel(&gact->hitimer); +- release_entry_list(&p->entries); ++ p = rcu_dereference_protected(gact->param, 1); ++ if (p) ++ call_rcu(&p->rcu, tcf_gate_params_free_rcu); + } + + static int dumping_entry(struct sk_buff *skb, +@@ -509,10 +617,9 @@ static int tcf_gate_dump(struct sk_buff + struct nlattr *entry_list; + struct tcf_t t; + +- spin_lock_bh(&gact->tcf_lock); +- opt.action = gact->tcf_action; +- +- p = &gact->param; ++ rcu_read_lock(); ++ opt.action = READ_ONCE(gact->tcf_action); ++ p = rcu_dereference(gact->param); + + if (nla_put(skb, TCA_GATE_PARMS, sizeof(opt), &opt)) + goto nla_put_failure; +@@ -552,12 +659,12 @@ static int tcf_gate_dump(struct sk_buff + tcf_tm_dump(&t, &gact->tcf_tm); + if (nla_put_64bit(skb, TCA_GATE_TM, sizeof(t), &t, TCA_GATE_PAD)) + goto nla_put_failure; +- spin_unlock_bh(&gact->tcf_lock); ++ rcu_read_unlock(); + + return skb->len; + + nla_put_failure: +- spin_unlock_bh(&gact->tcf_lock); ++ rcu_read_unlock(); + nlmsg_trim(skb, b); + return -1; + } diff --git a/queue-6.19/net-sched-only-allow-act_ct-to-bind-to-clsact-ingress-qdiscs-and-shared-blocks.patch b/queue-6.19/net-sched-only-allow-act_ct-to-bind-to-clsact-ingress-qdiscs-and-shared-blocks.patch new file mode 100644 index 00000000000..52901cc81eb --- /dev/null +++ b/queue-6.19/net-sched-only-allow-act_ct-to-bind-to-clsact-ingress-qdiscs-and-shared-blocks.patch @@ -0,0 +1,89 @@ +From 11cb63b0d1a0685e0831ae3c77223e002ef18189 Mon Sep 17 00:00:00 2001 +From: Victor Nogueira +Date: Wed, 25 Feb 2026 10:43:48 -0300 +Subject: net/sched: Only allow act_ct to bind to clsact/ingress qdiscs and shared blocks + +From: Victor Nogueira + +commit 11cb63b0d1a0685e0831ae3c77223e002ef18189 upstream. + +As Paolo said earlier [1]: + +"Since the blamed commit below, classify can return TC_ACT_CONSUMED while +the current skb being held by the defragmentation engine. As reported by +GangMin Kim, if such packet is that may cause a UaF when the defrag engine +later on tries to tuch again such packet." + +act_ct was never meant to be used in the egress path, however some users +are attaching it to egress today [2]. Attempting to reach a middle +ground, we noticed that, while most qdiscs are not handling +TC_ACT_CONSUMED, clsact/ingress qdiscs are. With that in mind, we +address the issue by only allowing act_ct to bind to clsact/ingress +qdiscs and shared blocks. That way it's still possible to attach act_ct to +egress (albeit only with clsact). + +[1] https://lore.kernel.org/netdev/674b8cbfc385c6f37fb29a1de08d8fe5c2b0fbee.1771321118.git.pabeni@redhat.com/ +[2] https://lore.kernel.org/netdev/cc6bfb4a-4a2b-42d8-b9ce-7ef6644fb22b@ovn.org/ + +Reported-by: GangMin Kim +Fixes: 3f14b377d01d ("net/sched: act_ct: fix skb leak and crash on ooo frags") +CC: stable@vger.kernel.org +Signed-off-by: Victor Nogueira +Acked-by: Jamal Hadi Salim +Link: https://patch.msgid.link/20260225134349.1287037-1-victor@mojatatu.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Greg Kroah-Hartman +--- + include/net/act_api.h | 1 + + net/sched/act_ct.c | 6 ++++++ + net/sched/cls_api.c | 7 +++++++ + 3 files changed, 14 insertions(+) + +--- a/include/net/act_api.h ++++ b/include/net/act_api.h +@@ -70,6 +70,7 @@ struct tc_action { + #define TCA_ACT_FLAGS_REPLACE (1U << (TCA_ACT_FLAGS_USER_BITS + 2)) + #define TCA_ACT_FLAGS_NO_RTNL (1U << (TCA_ACT_FLAGS_USER_BITS + 3)) + #define TCA_ACT_FLAGS_AT_INGRESS (1U << (TCA_ACT_FLAGS_USER_BITS + 4)) ++#define TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT (1U << (TCA_ACT_FLAGS_USER_BITS + 5)) + + /* Update lastuse only if needed, to avoid dirtying a cache line. + * We use a temp variable to avoid fetching jiffies twice. +--- a/net/sched/act_ct.c ++++ b/net/sched/act_ct.c +@@ -1358,6 +1358,12 @@ static int tcf_ct_init(struct net *net, + return -EINVAL; + } + ++ if (bind && !(flags & TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT)) { ++ NL_SET_ERR_MSG_MOD(extack, ++ "Attaching ct to a non ingress/clsact qdisc is unsupported"); ++ return -EOPNOTSUPP; ++ } ++ + err = nla_parse_nested(tb, TCA_CT_MAX, nla, ct_policy, extack); + if (err < 0) + return err; +--- a/net/sched/cls_api.c ++++ b/net/sched/cls_api.c +@@ -2228,6 +2228,11 @@ static bool is_qdisc_ingress(__u32 class + return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS)); + } + ++static bool is_ingress_or_clsact(struct tcf_block *block, struct Qdisc *q) ++{ ++ return tcf_block_shared(block) || (q && !!(q->flags & TCQ_F_INGRESS)); ++} ++ + static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n, + struct netlink_ext_ack *extack) + { +@@ -2420,6 +2425,8 @@ replay: + flags |= TCA_ACT_FLAGS_NO_RTNL; + if (is_qdisc_ingress(parent)) + flags |= TCA_ACT_FLAGS_AT_INGRESS; ++ if (is_ingress_or_clsact(block, q)) ++ flags |= TCA_ACT_FLAGS_AT_INGRESS_OR_CLSACT; + err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh, + flags, extack); + if (err == 0) { diff --git a/queue-6.19/series b/queue-6.19/series index e69de29bb2d..2698f26f8f9 100644 --- a/queue-6.19/series +++ b/queue-6.19/series @@ -0,0 +1,2 @@ +net-sched-act_gate-snapshot-parameters-with-rcu-on-replace.patch +net-sched-only-allow-act_ct-to-bind-to-clsact-ingress-qdiscs-and-shared-blocks.patch