.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * net/sched/act_sample.c - Packet sampling tc action |
---|
3 | 4 | * Copyright (c) 2017 Yotam Gigi <yotamg@mellanox.com> |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 as |
---|
7 | | - * published by the Free Software Foundation. |
---|
8 | 5 | */ |
---|
9 | 6 | |
---|
10 | 7 | #include <linux/types.h> |
---|
.. | .. |
---|
22 | 19 | #include <linux/tc_act/tc_sample.h> |
---|
23 | 20 | #include <net/tc_act/tc_sample.h> |
---|
24 | 21 | #include <net/psample.h> |
---|
| 22 | +#include <net/pkt_cls.h> |
---|
25 | 23 | |
---|
26 | 24 | #include <linux/if_arp.h> |
---|
27 | 25 | |
---|
.. | .. |
---|
37 | 35 | |
---|
38 | 36 | static int tcf_sample_init(struct net *net, struct nlattr *nla, |
---|
39 | 37 | struct nlattr *est, struct tc_action **a, int ovr, |
---|
40 | | - int bind, bool rtnl_held, |
---|
41 | | - struct netlink_ext_ack *extack) |
---|
| 38 | + int bind, bool rtnl_held, struct tcf_proto *tp, |
---|
| 39 | + u32 flags, struct netlink_ext_ack *extack) |
---|
42 | 40 | { |
---|
43 | 41 | struct tc_action_net *tn = net_generic(net, sample_net_id); |
---|
44 | 42 | struct nlattr *tb[TCA_SAMPLE_MAX + 1]; |
---|
45 | 43 | struct psample_group *psample_group; |
---|
46 | 44 | u32 psample_group_num, rate, index; |
---|
| 45 | + struct tcf_chain *goto_ch = NULL; |
---|
47 | 46 | struct tc_sample *parm; |
---|
48 | 47 | struct tcf_sample *s; |
---|
49 | 48 | bool exists = false; |
---|
.. | .. |
---|
51 | 50 | |
---|
52 | 51 | if (!nla) |
---|
53 | 52 | return -EINVAL; |
---|
54 | | - ret = nla_parse_nested(tb, TCA_SAMPLE_MAX, nla, sample_policy, NULL); |
---|
| 53 | + ret = nla_parse_nested_deprecated(tb, TCA_SAMPLE_MAX, nla, |
---|
| 54 | + sample_policy, NULL); |
---|
55 | 55 | if (ret < 0) |
---|
56 | 56 | return ret; |
---|
57 | | - if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] || |
---|
58 | | - !tb[TCA_SAMPLE_PSAMPLE_GROUP]) |
---|
| 57 | + |
---|
| 58 | + if (!tb[TCA_SAMPLE_PARMS]) |
---|
59 | 59 | return -EINVAL; |
---|
60 | 60 | |
---|
61 | 61 | parm = nla_data(tb[TCA_SAMPLE_PARMS]); |
---|
.. | .. |
---|
69 | 69 | |
---|
70 | 70 | if (!exists) { |
---|
71 | 71 | ret = tcf_idr_create(tn, index, est, a, |
---|
72 | | - &act_sample_ops, bind, true); |
---|
| 72 | + &act_sample_ops, bind, true, flags); |
---|
73 | 73 | if (ret) { |
---|
74 | 74 | tcf_idr_cleanup(tn, index); |
---|
75 | 75 | return ret; |
---|
.. | .. |
---|
80 | 80 | return -EEXIST; |
---|
81 | 81 | } |
---|
82 | 82 | |
---|
| 83 | + if (!tb[TCA_SAMPLE_RATE] || !tb[TCA_SAMPLE_PSAMPLE_GROUP]) { |
---|
| 84 | + NL_SET_ERR_MSG(extack, "sample rate and group are required"); |
---|
| 85 | + err = -EINVAL; |
---|
| 86 | + goto release_idr; |
---|
| 87 | + } |
---|
| 88 | + |
---|
| 89 | + err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); |
---|
| 90 | + if (err < 0) |
---|
| 91 | + goto release_idr; |
---|
| 92 | + |
---|
83 | 93 | rate = nla_get_u32(tb[TCA_SAMPLE_RATE]); |
---|
84 | 94 | if (!rate) { |
---|
85 | 95 | NL_SET_ERR_MSG(extack, "invalid sample rate"); |
---|
86 | | - tcf_idr_release(*a, bind); |
---|
87 | | - return -EINVAL; |
---|
| 96 | + err = -EINVAL; |
---|
| 97 | + goto put_chain; |
---|
88 | 98 | } |
---|
89 | 99 | psample_group_num = nla_get_u32(tb[TCA_SAMPLE_PSAMPLE_GROUP]); |
---|
90 | 100 | psample_group = psample_group_get(net, psample_group_num); |
---|
91 | 101 | if (!psample_group) { |
---|
92 | | - tcf_idr_release(*a, bind); |
---|
93 | | - return -ENOMEM; |
---|
| 102 | + err = -ENOMEM; |
---|
| 103 | + goto put_chain; |
---|
94 | 104 | } |
---|
95 | 105 | |
---|
96 | 106 | s = to_sample(*a); |
---|
97 | 107 | |
---|
98 | 108 | spin_lock_bh(&s->tcf_lock); |
---|
99 | | - s->tcf_action = parm->action; |
---|
| 109 | + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); |
---|
100 | 110 | s->rate = rate; |
---|
101 | 111 | s->psample_group_num = psample_group_num; |
---|
102 | | - rcu_swap_protected(s->psample_group, psample_group, |
---|
103 | | - lockdep_is_held(&s->tcf_lock)); |
---|
| 112 | + psample_group = rcu_replace_pointer(s->psample_group, psample_group, |
---|
| 113 | + lockdep_is_held(&s->tcf_lock)); |
---|
104 | 114 | |
---|
105 | 115 | if (tb[TCA_SAMPLE_TRUNC_SIZE]) { |
---|
106 | 116 | s->truncate = true; |
---|
.. | .. |
---|
110 | 120 | |
---|
111 | 121 | if (psample_group) |
---|
112 | 122 | psample_group_put(psample_group); |
---|
113 | | - if (ret == ACT_P_CREATED) |
---|
114 | | - tcf_idr_insert(tn, *a); |
---|
| 123 | + if (goto_ch) |
---|
| 124 | + tcf_chain_put_by_act(goto_ch); |
---|
| 125 | + |
---|
115 | 126 | return ret; |
---|
| 127 | +put_chain: |
---|
| 128 | + if (goto_ch) |
---|
| 129 | + tcf_chain_put_by_act(goto_ch); |
---|
| 130 | +release_idr: |
---|
| 131 | + tcf_idr_release(*a, bind); |
---|
| 132 | + return err; |
---|
116 | 133 | } |
---|
117 | 134 | |
---|
118 | 135 | static void tcf_sample_cleanup(struct tc_action *a) |
---|
.. | .. |
---|
234 | 251 | return tcf_generic_walker(tn, skb, cb, type, ops, extack); |
---|
235 | 252 | } |
---|
236 | 253 | |
---|
237 | | -static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index, |
---|
238 | | - struct netlink_ext_ack *extack) |
---|
| 254 | +static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index) |
---|
239 | 255 | { |
---|
240 | 256 | struct tc_action_net *tn = net_generic(net, sample_net_id); |
---|
241 | 257 | |
---|
242 | 258 | return tcf_idr_search(tn, a, index); |
---|
243 | 259 | } |
---|
244 | 260 | |
---|
| 261 | +static void tcf_psample_group_put(void *priv) |
---|
| 262 | +{ |
---|
| 263 | + struct psample_group *group = priv; |
---|
| 264 | + |
---|
| 265 | + psample_group_put(group); |
---|
| 266 | +} |
---|
| 267 | + |
---|
| 268 | +static struct psample_group * |
---|
| 269 | +tcf_sample_get_group(const struct tc_action *a, |
---|
| 270 | + tc_action_priv_destructor *destructor) |
---|
| 271 | +{ |
---|
| 272 | + struct tcf_sample *s = to_sample(a); |
---|
| 273 | + struct psample_group *group; |
---|
| 274 | + |
---|
| 275 | + group = rcu_dereference_protected(s->psample_group, |
---|
| 276 | + lockdep_is_held(&s->tcf_lock)); |
---|
| 277 | + if (group) { |
---|
| 278 | + psample_group_take(group); |
---|
| 279 | + *destructor = tcf_psample_group_put; |
---|
| 280 | + } |
---|
| 281 | + |
---|
| 282 | + return group; |
---|
| 283 | +} |
---|
| 284 | + |
---|
245 | 285 | static struct tc_action_ops act_sample_ops = { |
---|
246 | 286 | .kind = "sample", |
---|
247 | | - .type = TCA_ACT_SAMPLE, |
---|
| 287 | + .id = TCA_ID_SAMPLE, |
---|
248 | 288 | .owner = THIS_MODULE, |
---|
249 | 289 | .act = tcf_sample_act, |
---|
250 | 290 | .dump = tcf_sample_dump, |
---|
.. | .. |
---|
252 | 292 | .cleanup = tcf_sample_cleanup, |
---|
253 | 293 | .walk = tcf_sample_walker, |
---|
254 | 294 | .lookup = tcf_sample_search, |
---|
| 295 | + .get_psample_group = tcf_sample_get_group, |
---|
255 | 296 | .size = sizeof(struct tcf_sample), |
---|
256 | 297 | }; |
---|
257 | 298 | |
---|