summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-08-28 15:56:11 -0700
committerJakub Kicinski <kuba@kernel.org>2026-08-28 15:56:12 -0700
commit7e1d6caa9c6bd8e89a0639ae44be8b23f43386fc (patch)
tree424b922a9fd79375a4ad2fe377b5b408bce1e60f
parent28a57fb2c5df4deb42a06e52fd36c14b37aa0034 (diff)
parent251367a0a3319fa565daf7468b0afd933b1f5ab1 (diff)
downloadlinux-7e1d6caa9c6bd8e89a0639ae44be8b23f43386fc.tar.gz
linux-7e1d6caa9c6bd8e89a0639ae44be8b23f43386fc.zip
Merge branch 'net-sched-fix-remaining-actions-notification-accounting-issues'
Victor Nogueira says: ==================== net/sched: Fix remaining actions notification accounting issues Commit 8e2efb3f45a5 ("net/sched: add get_fill_size callbacks for actions missing them") fixed the reported echo/notify skb overrun and noted that the pre-existing issues Sashiko pointed out [1] would be fixed separately. This is that series. Patch 1 makes tcf_action_shared_attrs_size() a real upper bound again. TCA_ACT_IN_HW_COUNT and TCA_STATS_BASIC_HW are emitted on every action dump and were never budgeted; TCA_STATS_PKT64 was budgeted once but can be emitted twice; TCA_ACT_USED_HW_STATS and the rate estimator attributes are conditional and also unaccounted. Patch 2 makes the RTM_GETACTION path use the attr_size that tca_action_gd() already computes and then throws away, so that "tc actions get" can read back an action whose dump is larger than NLMSG_GOODSIZE. Patch 3 wraps the reoffload delete notification in tcf_action_full_attrs_size() like every other notification path, and stops leaving a skip_sw action installed when that notification cannot be built. [1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260810164357.1653956-1-victor%40mojatatu.com ==================== Link: https://patch.msgid.link/20260824153903.4143642-1-victor@mojatatu.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/sched/act_api.c37
1 files changed, 26 insertions, 11 deletions
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index b4415d358c91..37eced84dfa5 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
+ nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
+ cookie_len /* TCA_ACT_COOKIE */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */
+ /* TCA_ACT_USED_HW_STATS */
+ + nla_total_size(sizeof(struct nla_bitfield32))
+ + nla_total_size(sizeof(u32)) /* TCA_ACT_IN_HW_COUNT */
+ nla_total_size(0) /* TCA_ACT_STATS nested */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */
/* TCA_STATS_BASIC */
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
- /* TCA_STATS_PKT64 */
- + nla_total_size_64bit(sizeof(u64))
+ /* TCA_STATS_BASIC_HW */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_basic))
+ /* TCA_STATS_PKT64, emitted by both of the basic copies above */
+ + 2 * nla_total_size_64bit(sizeof(u64))
+ /* TCA_STATS_RATE_EST */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est))
+ /* TCA_STATS_RATE_EST64 */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est64))
/* TCA_STATS_QUEUE */
+ nla_total_size_64bit(sizeof(struct gnet_stats_queue))
+ nla_total_size(0) /* TCA_ACT_OPTIONS nested */
@@ -1688,12 +1697,12 @@ out_nlmsg_trim:
static int
tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
- struct tc_action *actions[], int event,
+ struct tc_action *actions[], size_t attr_size, int event,
struct netlink_ext_ack *extack)
{
struct sk_buff *skb;
- skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
@@ -1858,11 +1867,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])
static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
struct tc_action *action)
{
- size_t attr_size = tcf_action_fill_size(action);
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
[0] = action,
};
struct sk_buff *skb;
+ size_t attr_size;
+
+ attr_size = tcf_action_full_attrs_size(tcf_action_fill_size(action));
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
@@ -1879,15 +1890,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
{
const struct tc_action_ops *ops = action->ops;
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
int ret;
- if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
- skb = NULL;
- } else {
+ if (rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
skb = tcf_reoffload_del_notify_msg(net, action);
+ /* The action has already lost its hardware instance and is
+ * skip_sw, so it must be released whether or not the
+ * notification can be built. Drop the notification rather
+ * than leave an action behind that processes no packets.
+ */
if (IS_ERR(skb))
- return PTR_ERR(skb);
+ skb = NULL;
}
ret = tcf_idr_release_unsafe(action);
@@ -2044,7 +2058,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
attr_size = tcf_action_full_attrs_size(attr_size);
if (event == RTM_GETACTION)
- ret = tcf_get_notify(net, portid, n, actions, event, extack);
+ ret = tcf_get_notify(net, portid, n, actions, attr_size, event,
+ extack);
else { /* delete */
ret = tcf_del_notify(net, n, actions, portid, attr_size, extack);
if (ret)