Merge branch 'net-sched-fix-remaining-actions-notification-accounting-issues'

Victor Nogueira says:

====================
net/sched: Fix remaining actions notification accounting issues

Commit 8e2efb3f45 ("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>
This commit is contained in:
Jakub Kicinski 2026-08-28 15:56:11 -07:00
commit 7e1d6caa9c

View File

@ -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 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],
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)