Skip to content

Commit b72137e

Browse files
committed
Merge branch 'net-sched-conditional-notification-of-events-for-cls-and-act'
Pedro Tammela says: ==================== net/sched: conditional notification of events for cls and act This is an optimization we have been leveraging on P4TC but we believe it will benefit rtnl users in general. It's common to allocate an skb, build a notification message and then broadcast an event. In the absence of any user space listeners, these resources (cpu and memory operations) are wasted. In cases where the subsystem is lockless (such as in tc-flower) this waste is more prominent. For the scenarios where the rtnl_lock is held it is not as prominent. The idea is simple. Build and send the notification iif: - The user requests via NLM_F_ECHO or - Someone is listening to the rtnl group (tc mon) On a simple test with tc-flower adding 1M entries, using just a single core, there's already a noticeable difference in the cycles spent in tc_new_tfilter with this patchset. before: - 43.68% tc_new_tfilter + 31.73% fl_change + 6.35% tfilter_notify + 1.62% nlmsg_notify 0.66% __tcf_qdisc_find.part.0 0.64% __tcf_chain_get 0.54% fl_get + 0.53% tcf_proto_lookup_ops after: - 39.20% tc_new_tfilter + 34.58% fl_change 0.69% __tcf_qdisc_find.part.0 0.67% __tcf_chain_get + 0.61% tcf_proto_lookup_ops Note, the above test is using iproute2:tc which execs a shell. We expect people using netlink directly to observe even greater reductions. The qdisc side needs some refactoring of the notification routines to fit in this new model, so they will be sent in a later patchset. ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 70028b2 + 9377559 commit b72137e

File tree

3 files changed

+128
-46
lines changed

3 files changed

+128
-46
lines changed

include/linux/rtnetlink.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
#include <uapi/linux/rtnetlink.h>
1111

1212
extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo);
13+
14+
static inline int rtnetlink_maybe_send(struct sk_buff *skb, struct net *net,
15+
u32 pid, u32 group, int echo)
16+
{
17+
return !skb ? 0 : rtnetlink_send(skb, net, pid, group, echo);
18+
}
19+
1320
extern int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid);
1421
extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid,
1522
u32 group, const struct nlmsghdr *nlh, gfp_t flags);
@@ -130,4 +137,26 @@ extern int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
130137

131138
extern void rtnl_offload_xstats_notify(struct net_device *dev);
132139

140+
static inline int rtnl_has_listeners(const struct net *net, u32 group)
141+
{
142+
struct sock *rtnl = net->rtnl;
143+
144+
return netlink_has_listeners(rtnl, group);
145+
}
146+
147+
/**
148+
* rtnl_notify_needed - check if notification is needed
149+
* @net: Pointer to the net namespace
150+
* @nlflags: netlink ingress message flags
151+
* @group: rtnl group
152+
*
153+
* Based on the ingress message flags and rtnl group, returns true
154+
* if a notification is needed, false otherwise.
155+
*/
156+
static inline bool
157+
rtnl_notify_needed(const struct net *net, u16 nlflags, u32 group)
158+
{
159+
return (nlflags & NLM_F_ECHO) || rtnl_has_listeners(net, group);
160+
}
161+
133162
#endif /* __LINUX_RTNETLINK_H */

net/sched/act_api.c

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,31 +1785,45 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])
17851785
return 0;
17861786
}
17871787

1788-
static int
1789-
tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
1788+
static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
1789+
struct tc_action *action)
17901790
{
17911791
size_t attr_size = tcf_action_fill_size(action);
17921792
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
17931793
[0] = action,
17941794
};
1795-
const struct tc_action_ops *ops = action->ops;
17961795
struct sk_buff *skb;
1797-
int ret;
17981796

1799-
skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
1800-
GFP_KERNEL);
1797+
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
18011798
if (!skb)
1802-
return -ENOBUFS;
1799+
return ERR_PTR(-ENOBUFS);
18031800

18041801
if (tca_get_fill(skb, actions, 0, 0, 0, RTM_DELACTION, 0, 1, NULL) <= 0) {
18051802
kfree_skb(skb);
1806-
return -EINVAL;
1803+
return ERR_PTR(-EINVAL);
1804+
}
1805+
1806+
return skb;
1807+
}
1808+
1809+
static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
1810+
{
1811+
const struct tc_action_ops *ops = action->ops;
1812+
struct sk_buff *skb;
1813+
int ret;
1814+
1815+
if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
1816+
skb = NULL;
1817+
} else {
1818+
skb = tcf_reoffload_del_notify_msg(net, action);
1819+
if (IS_ERR(skb))
1820+
return PTR_ERR(skb);
18071821
}
18081822

18091823
ret = tcf_idr_release_unsafe(action);
18101824
if (ret == ACT_P_DELETED) {
18111825
module_put(ops->owner);
1812-
ret = rtnetlink_send(skb, net, 0, RTNLGRP_TC, 0);
1826+
ret = rtnetlink_maybe_send(skb, net, 0, RTNLGRP_TC, 0);
18131827
} else {
18141828
kfree_skb(skb);
18151829
}
@@ -1875,23 +1889,41 @@ int tcf_action_reoffload_cb(flow_indr_block_bind_cb_t *cb,
18751889
return 0;
18761890
}
18771891

1878-
static int
1879-
tcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
1880-
u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
1892+
static struct sk_buff *tcf_del_notify_msg(struct net *net, struct nlmsghdr *n,
1893+
struct tc_action *actions[],
1894+
u32 portid, size_t attr_size,
1895+
struct netlink_ext_ack *extack)
18811896
{
1882-
int ret;
18831897
struct sk_buff *skb;
18841898

1885-
skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
1886-
GFP_KERNEL);
1899+
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
18871900
if (!skb)
1888-
return -ENOBUFS;
1901+
return ERR_PTR(-ENOBUFS);
18891902

18901903
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
18911904
0, 2, extack) <= 0) {
18921905
NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes");
18931906
kfree_skb(skb);
1894-
return -EINVAL;
1907+
return ERR_PTR(-EINVAL);
1908+
}
1909+
1910+
return skb;
1911+
}
1912+
1913+
static int tcf_del_notify(struct net *net, struct nlmsghdr *n,
1914+
struct tc_action *actions[], u32 portid,
1915+
size_t attr_size, struct netlink_ext_ack *extack)
1916+
{
1917+
struct sk_buff *skb;
1918+
int ret;
1919+
1920+
if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC)) {
1921+
skb = NULL;
1922+
} else {
1923+
skb = tcf_del_notify_msg(net, n, actions, portid, attr_size,
1924+
extack);
1925+
if (IS_ERR(skb))
1926+
return PTR_ERR(skb);
18951927
}
18961928

18971929
/* now do the delete */
@@ -1902,9 +1934,8 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
19021934
return ret;
19031935
}
19041936

1905-
ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1906-
n->nlmsg_flags & NLM_F_ECHO);
1907-
return ret;
1937+
return rtnetlink_maybe_send(skb, net, portid, RTNLGRP_TC,
1938+
n->nlmsg_flags & NLM_F_ECHO);
19081939
}
19091940

19101941
static int
@@ -1955,26 +1986,44 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
19551986
return ret;
19561987
}
19571988

1958-
static int
1959-
tcf_add_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
1960-
u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
1989+
static struct sk_buff *tcf_add_notify_msg(struct net *net, struct nlmsghdr *n,
1990+
struct tc_action *actions[],
1991+
u32 portid, size_t attr_size,
1992+
struct netlink_ext_ack *extack)
19611993
{
19621994
struct sk_buff *skb;
19631995

1964-
skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
1965-
GFP_KERNEL);
1996+
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
19661997
if (!skb)
1967-
return -ENOBUFS;
1998+
return ERR_PTR(-ENOBUFS);
19681999

19692000
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
19702001
RTM_NEWACTION, 0, 0, extack) <= 0) {
19712002
NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
19722003
kfree_skb(skb);
1973-
return -EINVAL;
2004+
return ERR_PTR(-EINVAL);
2005+
}
2006+
2007+
return skb;
2008+
}
2009+
2010+
static int tcf_add_notify(struct net *net, struct nlmsghdr *n,
2011+
struct tc_action *actions[], u32 portid,
2012+
size_t attr_size, struct netlink_ext_ack *extack)
2013+
{
2014+
struct sk_buff *skb;
2015+
2016+
if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC)) {
2017+
skb = NULL;
2018+
} else {
2019+
skb = tcf_add_notify_msg(net, n, actions, portid, attr_size,
2020+
extack);
2021+
if (IS_ERR(skb))
2022+
return PTR_ERR(skb);
19742023
}
19752024

1976-
return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1977-
n->nlmsg_flags & NLM_F_ECHO);
2025+
return rtnetlink_maybe_send(skb, net, portid, RTNLGRP_TC,
2026+
n->nlmsg_flags & NLM_F_ECHO);
19782027
}
19792028

19802029
static int tcf_action_add(struct net *net, struct nlattr *nla,

net/sched/cls_api.c

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
650650
static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
651651
void *tmplt_priv, u32 chain_index,
652652
struct tcf_block *block, struct sk_buff *oskb,
653-
u32 seq, u16 flags, bool unicast);
653+
u32 seq, u16 flags);
654654

655655
static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
656656
bool explicitly_created)
@@ -685,8 +685,7 @@ static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
685685
if (non_act_refcnt == chain->explicitly_created && !by_act) {
686686
if (non_act_refcnt == 0)
687687
tc_chain_notify_delete(tmplt_ops, tmplt_priv,
688-
chain->index, block, NULL, 0, 0,
689-
false);
688+
chain->index, block, NULL, 0, 0);
690689
/* Last reference to chain, no need to lock. */
691690
chain->flushing = false;
692691
}
@@ -2053,6 +2052,9 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
20532052
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
20542053
int err = 0;
20552054

2055+
if (!unicast && !rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
2056+
return 0;
2057+
20562058
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
20572059
if (!skb)
20582060
return -ENOBUFS;
@@ -2075,13 +2077,16 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
20752077
static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
20762078
struct nlmsghdr *n, struct tcf_proto *tp,
20772079
struct tcf_block *block, struct Qdisc *q,
2078-
u32 parent, void *fh, bool unicast, bool *last,
2079-
bool rtnl_held, struct netlink_ext_ack *extack)
2080+
u32 parent, void *fh, bool *last, bool rtnl_held,
2081+
struct netlink_ext_ack *extack)
20802082
{
20812083
struct sk_buff *skb;
20822084
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
20832085
int err;
20842086

2087+
if (!rtnl_notify_needed(net, n->nlmsg_flags, RTNLGRP_TC))
2088+
return tp->ops->delete(tp, fh, last, rtnl_held, extack);
2089+
20852090
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
20862091
if (!skb)
20872092
return -ENOBUFS;
@@ -2100,11 +2105,8 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
21002105
return err;
21012106
}
21022107

2103-
if (unicast)
2104-
err = rtnl_unicast(skb, net, portid);
2105-
else
2106-
err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
2107-
n->nlmsg_flags & NLM_F_ECHO);
2108+
err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
2109+
n->nlmsg_flags & NLM_F_ECHO);
21082110
if (err < 0)
21092111
NL_SET_ERR_MSG(extack, "Failed to send filter delete notification");
21102112

@@ -2499,9 +2501,8 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
24992501
} else {
25002502
bool last;
25012503

2502-
err = tfilter_del_notify(net, skb, n, tp, block,
2503-
q, parent, fh, false, &last,
2504-
rtnl_held, extack);
2504+
err = tfilter_del_notify(net, skb, n, tp, block, q, parent, fh,
2505+
&last, rtnl_held, extack);
25052506

25062507
if (err)
25072508
goto errout;
@@ -2906,6 +2907,9 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
29062907
struct sk_buff *skb;
29072908
int err = 0;
29082909

2910+
if (!unicast && !rtnl_notify_needed(net, flags, RTNLGRP_TC))
2911+
return 0;
2912+
29092913
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
29102914
if (!skb)
29112915
return -ENOBUFS;
@@ -2929,12 +2933,15 @@ static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
29292933
static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
29302934
void *tmplt_priv, u32 chain_index,
29312935
struct tcf_block *block, struct sk_buff *oskb,
2932-
u32 seq, u16 flags, bool unicast)
2936+
u32 seq, u16 flags)
29332937
{
29342938
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
29352939
struct net *net = block->net;
29362940
struct sk_buff *skb;
29372941

2942+
if (!rtnl_notify_needed(net, flags, RTNLGRP_TC))
2943+
return 0;
2944+
29382945
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
29392946
if (!skb)
29402947
return -ENOBUFS;
@@ -2945,9 +2952,6 @@ static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
29452952
return -EINVAL;
29462953
}
29472954

2948-
if (unicast)
2949-
return rtnl_unicast(skb, net, portid);
2950-
29512955
return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
29522956
}
29532957

0 commit comments

Comments
 (0)