Blame view
net/sched/cls_matchall.c
10.3 KB
2874c5fd2 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
bf3994d2e net/sched: introd... |
2 3 4 5 |
/* * net/sched/cls_matchll.c Match-all classifier * * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> |
bf3994d2e net/sched: introd... |
6 7 8 9 10 |
*/ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> |
f88c19aab net_sched: add hi... |
11 |
#include <linux/percpu.h> |
bf3994d2e net/sched: introd... |
12 13 14 |
#include <net/sch_generic.h> #include <net/pkt_cls.h> |
fd62d9f5c net/sched: matcha... |
15 |
struct cls_mall_head { |
bf3994d2e net/sched: introd... |
16 17 18 |
struct tcf_exts exts; struct tcf_result res; u32 handle; |
b87f7936a net/sched: Add ma... |
19 |
u32 flags; |
0efd1b3a1 net: sched: cls_m... |
20 |
unsigned int in_hw_count; |
f88c19aab net_sched: add hi... |
21 |
struct tc_matchall_pcnt __percpu *pf; |
aaa908ffb net_sched: switch... |
22 |
struct rcu_work rwork; |
f517f2716 net: sched: cls_m... |
23 |
bool deleting; |
bf3994d2e net/sched: introd... |
24 25 26 27 28 29 |
}; static int mall_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res) { struct cls_mall_head *head = rcu_dereference_bh(tp->root); |
bf3994d2e net/sched: introd... |
30 |
|
25426043e cls_matchall: avo... |
31 32 |
if (unlikely(!head)) return -1; |
fd62d9f5c net/sched: matcha... |
33 |
if (tc_skip_sw(head->flags)) |
b87f7936a net/sched: Add ma... |
34 |
return -1; |
3ff4cbec8 net/sched: cls_ma... |
35 |
*res = head->res; |
f88c19aab net_sched: add hi... |
36 |
__this_cpu_inc(head->pf->rhit); |
fd62d9f5c net/sched: matcha... |
37 |
return tcf_exts_exec(skb, &head->exts, res); |
bf3994d2e net/sched: introd... |
38 39 40 41 |
} static int mall_init(struct tcf_proto *tp) { |
bf3994d2e net/sched: introd... |
42 43 |
return 0; } |
57767e785 cls_matchall: use... |
44 45 46 47 |
static void __mall_destroy(struct cls_mall_head *head) { tcf_exts_destroy(&head->exts); tcf_exts_put_net(&head->exts); |
f88c19aab net_sched: add hi... |
48 |
free_percpu(head->pf); |
57767e785 cls_matchall: use... |
49 50 |
kfree(head); } |
df2735ee8 net_sched: use tc... |
51 52 |
static void mall_destroy_work(struct work_struct *work) { |
aaa908ffb net_sched: switch... |
53 54 55 |
struct cls_mall_head *head = container_of(to_rcu_work(work), struct cls_mall_head, rwork); |
df2735ee8 net_sched: use tc... |
56 |
rtnl_lock(); |
57767e785 cls_matchall: use... |
57 |
__mall_destroy(head); |
df2735ee8 net_sched: use tc... |
58 59 |
rtnl_unlock(); } |
2447a96f8 net: sched: cls_m... |
60 61 |
static void mall_destroy_hw_filter(struct tcf_proto *tp, struct cls_mall_head *head, |
b505b29f6 cls_matchall: pro... |
62 63 |
unsigned long cookie, struct netlink_ext_ack *extack) |
b87f7936a net/sched: Add ma... |
64 |
{ |
de4784ca0 net: sched: get r... |
65 |
struct tc_cls_matchall_offload cls_mall = {}; |
2447a96f8 net: sched: cls_m... |
66 |
struct tcf_block *block = tp->chain->block; |
b87f7936a net/sched: Add ma... |
67 |
|
d6787147e net/sched: remove... |
68 |
tc_cls_common_offload_init(&cls_mall.common, tp, head->flags, extack); |
2447a96f8 net: sched: cls_m... |
69 |
cls_mall.command = TC_CLSMATCHALL_DESTROY; |
de4784ca0 net: sched: get r... |
70 |
cls_mall.cookie = cookie; |
b87f7936a net/sched: Add ma... |
71 |
|
401192113 net: sched: refac... |
72 73 |
tc_setup_cb_destroy(block, tp, TC_SETUP_CLSMATCHALL, &cls_mall, false, &head->flags, &head->in_hw_count, true); |
b87f7936a net/sched: Add ma... |
74 |
} |
2447a96f8 net: sched: cls_m... |
75 76 |
static int mall_replace_hw_filter(struct tcf_proto *tp, struct cls_mall_head *head, |
027981405 net: sched: cls_m... |
77 78 |
unsigned long cookie, struct netlink_ext_ack *extack) |
b87f7936a net/sched: Add ma... |
79 |
{ |
de4784ca0 net: sched: get r... |
80 |
struct tc_cls_matchall_offload cls_mall = {}; |
2447a96f8 net: sched: cls_m... |
81 82 83 |
struct tcf_block *block = tp->chain->block; bool skip_sw = tc_skip_sw(head->flags); int err; |
b87f7936a net/sched: Add ma... |
84 |
|
f00cbf196 net/sched: use th... |
85 86 87 |
cls_mall.rule = flow_rule_alloc(tcf_exts_num_actions(&head->exts)); if (!cls_mall.rule) return -ENOMEM; |
d6787147e net/sched: remove... |
88 |
tc_cls_common_offload_init(&cls_mall.common, tp, head->flags, extack); |
2447a96f8 net: sched: cls_m... |
89 |
cls_mall.command = TC_CLSMATCHALL_REPLACE; |
de4784ca0 net: sched: get r... |
90 |
cls_mall.cookie = cookie; |
b87f7936a net/sched: Add ma... |
91 |
|
b15e7a6e8 net: sched: don't... |
92 |
err = tc_setup_flow_action(&cls_mall.rule->action, &head->exts); |
f00cbf196 net/sched: use th... |
93 94 95 96 97 98 99 100 101 102 |
if (err) { kfree(cls_mall.rule); mall_destroy_hw_filter(tp, head, cookie, NULL); if (skip_sw) NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action"); else err = 0; return err; } |
401192113 net: sched: refac... |
103 104 |
err = tc_setup_cb_add(block, tp, TC_SETUP_CLSMATCHALL, &cls_mall, skip_sw, &head->flags, &head->in_hw_count, true); |
f2b795ea0 net: sched: cls_m... |
105 |
tc_cleanup_flow_action(&cls_mall.rule->action); |
f00cbf196 net/sched: use th... |
106 |
kfree(cls_mall.rule); |
401192113 net: sched: refac... |
107 |
if (err) { |
b505b29f6 cls_matchall: pro... |
108 |
mall_destroy_hw_filter(tp, head, cookie, NULL); |
2447a96f8 net: sched: cls_m... |
109 |
return err; |
2447a96f8 net: sched: cls_m... |
110 111 112 113 114 115 |
} if (skip_sw && !(head->flags & TCA_CLS_FLAGS_IN_HW)) return -EINVAL; return 0; |
b87f7936a net/sched: Add ma... |
116 |
} |
12db03b65 net: sched: exten... |
117 118 |
static void mall_destroy(struct tcf_proto *tp, bool rtnl_held, struct netlink_ext_ack *extack) |
bf3994d2e net/sched: introd... |
119 120 |
{ struct cls_mall_head *head = rtnl_dereference(tp->root); |
fd62d9f5c net/sched: matcha... |
121 |
if (!head) |
763dbf632 net_sched: move t... |
122 |
return; |
bf3994d2e net/sched: introd... |
123 |
|
a51c76b4d cls_matchall: fix... |
124 |
tcf_unbind_filter(tp, &head->res); |
2447a96f8 net: sched: cls_m... |
125 |
if (!tc_skip_hw(head->flags)) |
b505b29f6 cls_matchall: pro... |
126 |
mall_destroy_hw_filter(tp, head, (unsigned long) head, extack); |
b87f7936a net/sched: Add ma... |
127 |
|
57767e785 cls_matchall: use... |
128 |
if (tcf_exts_get_net(&head->exts)) |
aaa908ffb net_sched: switch... |
129 |
tcf_queue_work(&head->rwork, mall_destroy_work); |
57767e785 cls_matchall: use... |
130 131 |
else __mall_destroy(head); |
bf3994d2e net/sched: introd... |
132 |
} |
8113c0956 net_sched: use vo... |
133 |
static void *mall_get(struct tcf_proto *tp, u32 handle) |
bf3994d2e net/sched: introd... |
134 |
{ |
0db6f8bef net/sched: fix ->... |
135 136 137 138 |
struct cls_mall_head *head = rtnl_dereference(tp->root); if (head && head->handle == handle) return head; |
8113c0956 net_sched: use vo... |
139 |
return NULL; |
bf3994d2e net/sched: introd... |
140 141 142 143 144 |
} static const struct nla_policy mall_policy[TCA_MATCHALL_MAX + 1] = { [TCA_MATCHALL_UNSPEC] = { .type = NLA_UNSPEC }, [TCA_MATCHALL_CLASSID] = { .type = NLA_U32 }, |
1afa3cc90 net/sched: matcha... |
145 |
[TCA_MATCHALL_FLAGS] = { .type = NLA_U32 }, |
bf3994d2e net/sched: introd... |
146 147 148 |
}; static int mall_set_parms(struct net *net, struct tcf_proto *tp, |
fd62d9f5c net/sched: matcha... |
149 |
struct cls_mall_head *head, |
bf3994d2e net/sched: introd... |
150 |
unsigned long base, struct nlattr **tb, |
50a561900 net: sched: cls: ... |
151 152 |
struct nlattr *est, bool ovr, struct netlink_ext_ack *extack) |
bf3994d2e net/sched: introd... |
153 |
{ |
bf3994d2e net/sched: introd... |
154 |
int err; |
ec6743a10 net: sched: track... |
155 156 |
err = tcf_exts_validate(net, tp, tb, est, &head->exts, ovr, true, extack); |
bf3994d2e net/sched: introd... |
157 |
if (err < 0) |
a74cb3698 net: sched: cls_m... |
158 |
return err; |
bf3994d2e net/sched: introd... |
159 160 |
if (tb[TCA_MATCHALL_CLASSID]) { |
fd62d9f5c net/sched: matcha... |
161 162 |
head->res.classid = nla_get_u32(tb[TCA_MATCHALL_CLASSID]); tcf_bind_filter(tp, &head->res, base); |
bf3994d2e net/sched: introd... |
163 |
} |
bf3994d2e net/sched: introd... |
164 165 166 167 168 169 |
return 0; } static int mall_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, |
12db03b65 net: sched: exten... |
170 171 |
void **arg, bool ovr, bool rtnl_held, struct netlink_ext_ack *extack) |
bf3994d2e net/sched: introd... |
172 173 |
{ struct cls_mall_head *head = rtnl_dereference(tp->root); |
bf3994d2e net/sched: introd... |
174 |
struct nlattr *tb[TCA_MATCHALL_MAX + 1]; |
fd62d9f5c net/sched: matcha... |
175 |
struct cls_mall_head *new; |
b87f7936a net/sched: Add ma... |
176 |
u32 flags = 0; |
bf3994d2e net/sched: introd... |
177 178 179 180 |
int err; if (!tca[TCA_OPTIONS]) return -EINVAL; |
fd62d9f5c net/sched: matcha... |
181 182 |
if (head) return -EEXIST; |
bf3994d2e net/sched: introd... |
183 |
|
8cb081746 netlink: make val... |
184 185 |
err = nla_parse_nested_deprecated(tb, TCA_MATCHALL_MAX, tca[TCA_OPTIONS], mall_policy, NULL); |
bf3994d2e net/sched: introd... |
186 187 |
if (err < 0) return err; |
b87f7936a net/sched: Add ma... |
188 189 190 191 192 |
if (tb[TCA_MATCHALL_FLAGS]) { flags = nla_get_u32(tb[TCA_MATCHALL_FLAGS]); if (!tc_flags_valid(flags)) return -EINVAL; } |
fd62d9f5c net/sched: matcha... |
193 194 |
new = kzalloc(sizeof(*new), GFP_KERNEL); if (!new) |
bf3994d2e net/sched: introd... |
195 |
return -ENOBUFS; |
14215108a net_sched: initia... |
196 |
err = tcf_exts_init(&new->exts, net, TCA_MATCHALL_ACT, 0); |
ec2507d2a net/sched: cls_ma... |
197 198 |
if (err) goto err_exts_init; |
bf3994d2e net/sched: introd... |
199 200 201 |
if (!handle) handle = 1; |
fd62d9f5c net/sched: matcha... |
202 203 |
new->handle = handle; new->flags = flags; |
f88c19aab net_sched: add hi... |
204 205 206 207 208 |
new->pf = alloc_percpu(struct tc_matchall_pcnt); if (!new->pf) { err = -ENOMEM; goto err_alloc_percpu; } |
bf3994d2e net/sched: introd... |
209 |
|
50a561900 net: sched: cls: ... |
210 211 |
err = mall_set_parms(net, tp, new, base, tb, tca[TCA_RATE], ovr, extack); |
bf3994d2e net/sched: introd... |
212 |
if (err) |
ec2507d2a net/sched: cls_ma... |
213 |
goto err_set_parms; |
bf3994d2e net/sched: introd... |
214 |
|
2447a96f8 net: sched: cls_m... |
215 |
if (!tc_skip_hw(new->flags)) { |
027981405 net: sched: cls_m... |
216 217 |
err = mall_replace_hw_filter(tp, new, (unsigned long)new, extack); |
2447a96f8 net: sched: cls_m... |
218 219 |
if (err) goto err_replace_hw_filter; |
b87f7936a net/sched: Add ma... |
220 |
} |
c7d2b2f5e net/sched: cls_ma... |
221 222 223 |
if (!tc_in_hw(new->flags)) new->flags |= TCA_CLS_FLAGS_NOT_IN_HW; |
b87f7936a net/sched: Add ma... |
224 |
|
8113c0956 net_sched: use vo... |
225 |
*arg = head; |
fd62d9f5c net/sched: matcha... |
226 |
rcu_assign_pointer(tp->root, new); |
bf3994d2e net/sched: introd... |
227 |
return 0; |
ec2507d2a net/sched: cls_ma... |
228 229 |
err_replace_hw_filter: err_set_parms: |
f88c19aab net_sched: add hi... |
230 231 |
free_percpu(new->pf); err_alloc_percpu: |
e2160156b Merge git://git.k... |
232 |
tcf_exts_destroy(&new->exts); |
ec2507d2a net/sched: cls_ma... |
233 |
err_exts_init: |
fd62d9f5c net/sched: matcha... |
234 |
kfree(new); |
bf3994d2e net/sched: introd... |
235 236 |
return err; } |
571acf210 net: sched: cls: ... |
237 |
static int mall_delete(struct tcf_proto *tp, void *arg, bool *last, |
12db03b65 net: sched: exten... |
238 |
bool rtnl_held, struct netlink_ext_ack *extack) |
bf3994d2e net/sched: introd... |
239 |
{ |
f517f2716 net: sched: cls_m... |
240 241 242 243 244 |
struct cls_mall_head *head = rtnl_dereference(tp->root); head->deleting = true; *last = true; return 0; |
bf3994d2e net/sched: introd... |
245 |
} |
12db03b65 net: sched: exten... |
246 247 |
static void mall_walk(struct tcf_proto *tp, struct tcf_walker *arg, bool rtnl_held) |
bf3994d2e net/sched: introd... |
248 249 |
{ struct cls_mall_head *head = rtnl_dereference(tp->root); |
bf3994d2e net/sched: introd... |
250 251 252 |
if (arg->count < arg->skip) goto skip; |
d66022cd1 net: sched: match... |
253 |
|
f517f2716 net: sched: cls_m... |
254 |
if (!head || head->deleting) |
d66022cd1 net: sched: match... |
255 |
return; |
8113c0956 net_sched: use vo... |
256 |
if (arg->fn(tp, head, arg) < 0) |
bf3994d2e net/sched: introd... |
257 258 259 260 |
arg->stop = 1; skip: arg->count++; } |
a73233115 net: flow_offload... |
261 |
static int mall_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, |
0efd1b3a1 net: sched: cls_m... |
262 263 264 265 266 267 268 269 270 |
void *cb_priv, struct netlink_ext_ack *extack) { struct cls_mall_head *head = rtnl_dereference(tp->root); struct tc_cls_matchall_offload cls_mall = {}; struct tcf_block *block = tp->chain->block; int err; if (tc_skip_hw(head->flags)) return 0; |
f00cbf196 net/sched: use th... |
271 272 273 |
cls_mall.rule = flow_rule_alloc(tcf_exts_num_actions(&head->exts)); if (!cls_mall.rule) return -ENOMEM; |
d6787147e net/sched: remove... |
274 |
tc_cls_common_offload_init(&cls_mall.common, tp, head->flags, extack); |
0efd1b3a1 net: sched: cls_m... |
275 276 |
cls_mall.command = add ? TC_CLSMATCHALL_REPLACE : TC_CLSMATCHALL_DESTROY; |
0efd1b3a1 net: sched: cls_m... |
277 |
cls_mall.cookie = (unsigned long)head; |
b15e7a6e8 net: sched: don't... |
278 |
err = tc_setup_flow_action(&cls_mall.rule->action, &head->exts); |
f00cbf196 net/sched: use th... |
279 280 281 282 283 284 |
if (err) { kfree(cls_mall.rule); if (add && tc_skip_sw(head->flags)) { NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action"); return err; } |
5f0583683 net/sched: avoid ... |
285 |
return 0; |
f00cbf196 net/sched: use th... |
286 |
} |
401192113 net: sched: refac... |
287 288 289 |
err = tc_setup_cb_reoffload(block, tp, add, cb, TC_SETUP_CLSMATCHALL, &cls_mall, cb_priv, &head->flags, &head->in_hw_count); |
f2b795ea0 net: sched: cls_m... |
290 |
tc_cleanup_flow_action(&cls_mall.rule->action); |
f00cbf196 net/sched: use th... |
291 |
kfree(cls_mall.rule); |
401192113 net: sched: refac... |
292 293 |
if (err) return err; |
0efd1b3a1 net: sched: cls_m... |
294 295 296 |
return 0; } |
b7fe4ab8a net/sched: extend... |
297 298 299 300 301 302 |
static void mall_stats_hw_filter(struct tcf_proto *tp, struct cls_mall_head *head, unsigned long cookie) { struct tc_cls_matchall_offload cls_mall = {}; struct tcf_block *block = tp->chain->block; |
d6787147e net/sched: remove... |
303 |
tc_cls_common_offload_init(&cls_mall.common, tp, head->flags, NULL); |
b7fe4ab8a net/sched: extend... |
304 305 |
cls_mall.command = TC_CLSMATCHALL_STATS; cls_mall.cookie = cookie; |
401192113 net: sched: refac... |
306 |
tc_setup_cb_call(block, TC_SETUP_CLSMATCHALL, &cls_mall, false, true); |
b7fe4ab8a net/sched: extend... |
307 308 |
tcf_exts_stats_update(&head->exts, cls_mall.stats.bytes, |
4b61d3e8d net: qos offload ... |
309 310 |
cls_mall.stats.pkts, cls_mall.stats.drops, cls_mall.stats.lastused, |
93a129eb8 net: sched: expos... |
311 312 |
cls_mall.stats.used_hw_stats, cls_mall.stats.used_hw_stats_valid); |
b7fe4ab8a net/sched: extend... |
313 |
} |
8113c0956 net_sched: use vo... |
314 |
static int mall_dump(struct net *net, struct tcf_proto *tp, void *fh, |
12db03b65 net: sched: exten... |
315 |
struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) |
bf3994d2e net/sched: introd... |
316 |
{ |
f88c19aab net_sched: add hi... |
317 |
struct tc_matchall_pcnt gpf = {}; |
8113c0956 net_sched: use vo... |
318 |
struct cls_mall_head *head = fh; |
bf3994d2e net/sched: introd... |
319 |
struct nlattr *nest; |
f88c19aab net_sched: add hi... |
320 |
int cpu; |
bf3994d2e net/sched: introd... |
321 |
|
fd62d9f5c net/sched: matcha... |
322 |
if (!head) |
bf3994d2e net/sched: introd... |
323 |
return skb->len; |
b7fe4ab8a net/sched: extend... |
324 325 |
if (!tc_skip_hw(head->flags)) mall_stats_hw_filter(tp, head, (unsigned long)head); |
fd62d9f5c net/sched: matcha... |
326 |
t->tcm_handle = head->handle; |
bf3994d2e net/sched: introd... |
327 |
|
ae0be8de9 netlink: make nla... |
328 |
nest = nla_nest_start_noflag(skb, TCA_OPTIONS); |
bf3994d2e net/sched: introd... |
329 330 |
if (!nest) goto nla_put_failure; |
fd62d9f5c net/sched: matcha... |
331 332 |
if (head->res.classid && nla_put_u32(skb, TCA_MATCHALL_CLASSID, head->res.classid)) |
bf3994d2e net/sched: introd... |
333 |
goto nla_put_failure; |
7a335adad net/sched: cls_ma... |
334 335 |
if (head->flags && nla_put_u32(skb, TCA_MATCHALL_FLAGS, head->flags)) goto nla_put_failure; |
f88c19aab net_sched: add hi... |
336 337 338 339 340 341 342 343 344 345 |
for_each_possible_cpu(cpu) { struct tc_matchall_pcnt *pf = per_cpu_ptr(head->pf, cpu); gpf.rhit += pf->rhit; } if (nla_put_64bit(skb, TCA_MATCHALL_PCNT, sizeof(struct tc_matchall_pcnt), &gpf, TCA_MATCHALL_PAD)) goto nla_put_failure; |
fd62d9f5c net/sched: matcha... |
346 |
if (tcf_exts_dump(skb, &head->exts)) |
bf3994d2e net/sched: introd... |
347 348 349 |
goto nla_put_failure; nla_nest_end(skb, nest); |
fd62d9f5c net/sched: matcha... |
350 |
if (tcf_exts_dump_stats(skb, &head->exts) < 0) |
bf3994d2e net/sched: introd... |
351 352 353 354 355 356 357 358 |
goto nla_put_failure; return skb->len; nla_put_failure: nla_nest_cancel(skb, nest); return -1; } |
2e24cd755 net_sched: fix op... |
359 360 |
static void mall_bind_class(void *fh, u32 classid, unsigned long cl, void *q, unsigned long base) |
07d79fc7d net_sched: add re... |
361 362 |
{ struct cls_mall_head *head = fh; |
2e24cd755 net_sched: fix op... |
363 364 365 366 367 368 |
if (head && head->res.classid == classid) { if (cl) __tcf_bind_filter(q, &head->res, base); else __tcf_unbind_filter(q, &head->res); } |
07d79fc7d net_sched: add re... |
369 |
} |
bf3994d2e net/sched: introd... |
370 371 372 373 374 375 376 377 378 |
static struct tcf_proto_ops cls_mall_ops __read_mostly = { .kind = "matchall", .classify = mall_classify, .init = mall_init, .destroy = mall_destroy, .get = mall_get, .change = mall_change, .delete = mall_delete, .walk = mall_walk, |
0efd1b3a1 net: sched: cls_m... |
379 |
.reoffload = mall_reoffload, |
bf3994d2e net/sched: introd... |
380 |
.dump = mall_dump, |
07d79fc7d net_sched: add re... |
381 |
.bind_class = mall_bind_class, |
bf3994d2e net/sched: introd... |
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
.owner = THIS_MODULE, }; static int __init cls_mall_init(void) { return register_tcf_proto_ops(&cls_mall_ops); } static void __exit cls_mall_exit(void) { unregister_tcf_proto_ops(&cls_mall_ops); } module_init(cls_mall_init); module_exit(cls_mall_exit); MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); MODULE_DESCRIPTION("Match-all classifier"); MODULE_LICENSE("GPL v2"); |