net: sched: act_police: fix sparse errors in tcf_police_dump()
[linux-2.6-block.git] / net / sched / act_police.c
CommitLineData
2874c5fd 1// SPDX-License-Identifier: GPL-2.0-or-later
1da177e4 2/*
0c6965dd 3 * net/sched/act_police.c Input police filter
1da177e4 4 *
1da177e4
LT
5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6 * J Hadi Salim (action changes)
7 */
8
1da177e4
LT
9#include <linux/module.h>
10#include <linux/types.h>
11#include <linux/kernel.h>
1da177e4 12#include <linux/string.h>
1da177e4 13#include <linux/errno.h>
1da177e4 14#include <linux/skbuff.h>
1da177e4
LT
15#include <linux/rtnetlink.h>
16#include <linux/init.h>
5a0e3ad6 17#include <linux/slab.h>
1da177e4 18#include <net/act_api.h>
dc5fc579 19#include <net/netlink.h>
d6124d6b 20#include <net/pkt_cls.h>
fa762da9 21#include <net/tc_act/tc_police.h>
871cf386 22#include <net/tc_wrapper.h>
1e9b3d53 23
e9ce1cd3 24/* Each policer is serialized by its individual spinlock */
1da177e4 25
a85a970a 26static struct tc_action_ops act_police_ops;
ddf97ccd 27
53b2bf3f
PM
28static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
29 [TCA_POLICE_RATE] = { .len = TC_RTAB_SIZE },
30 [TCA_POLICE_PEAKRATE] = { .len = TC_RTAB_SIZE },
31 [TCA_POLICE_AVRATE] = { .type = NLA_U32 },
32 [TCA_POLICE_RESULT] = { .type = NLA_U32 },
d1967e49
DD
33 [TCA_POLICE_RATE64] = { .type = NLA_U64 },
34 [TCA_POLICE_PEAKRATE64] = { .type = NLA_U64 },
2ffe0395
BZ
35 [TCA_POLICE_PKTRATE64] = { .type = NLA_U64, .min = 1 },
36 [TCA_POLICE_PKTBURST64] = { .type = NLA_U64, .min = 1 },
53b2bf3f
PM
37};
38
2ac06347 39static int tcf_police_init(struct net *net, struct nlattr *nla,
a85a970a 40 struct nlattr *est, struct tc_action **a,
abbb0d33 41 struct tcf_proto *tp, u32 flags,
589dad6d 42 struct netlink_ext_ack *extack)
1da177e4 43{
fd6d4338 44 int ret = 0, tcfp_result = TC_ACT_OK, err, size;
695176bf 45 bool bind = flags & TCA_ACT_FLAGS_BIND;
7ba699c6 46 struct nlattr *tb[TCA_POLICE_MAX + 1];
d6124d6b 47 struct tcf_chain *goto_ch = NULL;
1da177e4 48 struct tc_police *parm;
e9ce1cd3 49 struct tcf_police *police;
1da177e4 50 struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL;
acd0a7ab 51 struct tc_action_net *tn = net_generic(net, act_police_ops.net_id);
2d550dba 52 struct tcf_police_params *new;
0852e455 53 bool exists = false;
7be8ef2c 54 u32 index;
d1967e49 55 u64 rate64, prate64;
2ffe0395 56 u64 pps, ppsburst;
1da177e4 57
cee63723 58 if (nla == NULL)
1da177e4
LT
59 return -EINVAL;
60
8cb08174
JB
61 err = nla_parse_nested_deprecated(tb, TCA_POLICE_MAX, nla,
62 police_policy, NULL);
cee63723
PM
63 if (err < 0)
64 return err;
65
7ba699c6 66 if (tb[TCA_POLICE_TBF] == NULL)
1e9b3d53 67 return -EINVAL;
7ba699c6 68 size = nla_len(tb[TCA_POLICE_TBF]);
1e9b3d53 69 if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat))
1da177e4 70 return -EINVAL;
0852e455 71
7ba699c6 72 parm = nla_data(tb[TCA_POLICE_TBF]);
7be8ef2c
DL
73 index = parm->index;
74 err = tcf_idr_check_alloc(tn, &index, a, bind);
0190c1d4
VB
75 if (err < 0)
76 return err;
77 exists = err;
0852e455
WC
78 if (exists && bind)
79 return 0;
1da177e4 80
0852e455 81 if (!exists) {
7be8ef2c 82 ret = tcf_idr_create(tn, index, NULL, a,
40bd094d 83 &act_police_ops, bind, true, flags);
0190c1d4 84 if (ret) {
7be8ef2c 85 tcf_idr_cleanup(tn, index);
a03e6fe5 86 return ret;
0190c1d4 87 }
a03e6fe5 88 ret = ACT_P_CREATED;
484afd1b 89 spin_lock_init(&(to_police(*a)->tcfp_lock));
695176bf 90 } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) {
65a206c0 91 tcf_idr_release(*a, bind);
4e8ddd7f 92 return -EEXIST;
1da177e4 93 }
d6124d6b
DC
94 err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
95 if (err < 0)
96 goto release_idr;
1da177e4 97
a85a970a 98 police = to_police(*a);
1da177e4
LT
99 if (parm->rate.rate) {
100 err = -ENOMEM;
e9bc3fa2 101 R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE], NULL);
1da177e4
LT
102 if (R_tab == NULL)
103 goto failure;
c1b56878 104
1da177e4
LT
105 if (parm->peakrate.rate) {
106 P_tab = qdisc_get_rtab(&parm->peakrate,
e9bc3fa2 107 tb[TCA_POLICE_PEAKRATE], NULL);
71bcb09a 108 if (P_tab == NULL)
1da177e4 109 goto failure;
1da177e4
LT
110 }
111 }
71bcb09a 112
71bcb09a 113 if (est) {
93be42f9
DC
114 err = gen_replace_estimator(&police->tcf_bstats,
115 police->common.cpu_bstats,
71bcb09a 116 &police->tcf_rate_est,
edb09eb1 117 &police->tcf_lock,
29cbcd85 118 false, est);
71bcb09a 119 if (err)
74030603 120 goto failure;
a883bf56
JP
121 } else if (tb[TCA_POLICE_AVRATE] &&
122 (ret == ACT_P_CREATED ||
1c0d32fd 123 !gen_estimator_active(&police->tcf_rate_est))) {
a883bf56 124 err = -EINVAL;
74030603 125 goto failure;
71bcb09a
SH
126 }
127
fd6d4338
DC
128 if (tb[TCA_POLICE_RESULT]) {
129 tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]);
130 if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) {
131 NL_SET_ERR_MSG(extack,
132 "goto chain not allowed on fallback");
133 err = -EINVAL;
134 goto failure;
135 }
136 }
137
2ffe0395
BZ
138 if ((tb[TCA_POLICE_PKTRATE64] && !tb[TCA_POLICE_PKTBURST64]) ||
139 (!tb[TCA_POLICE_PKTRATE64] && tb[TCA_POLICE_PKTBURST64])) {
140 NL_SET_ERR_MSG(extack,
141 "Both or neither packet-per-second burst and rate must be provided");
142 err = -EINVAL;
143 goto failure;
144 }
145
146 if (tb[TCA_POLICE_PKTRATE64] && R_tab) {
147 NL_SET_ERR_MSG(extack,
148 "packet-per-second and byte-per-second rate limits not allowed in same action");
149 err = -EINVAL;
150 goto failure;
151 }
152
2d550dba
DC
153 new = kzalloc(sizeof(*new), GFP_KERNEL);
154 if (unlikely(!new)) {
155 err = -ENOMEM;
156 goto failure;
157 }
158
71bcb09a 159 /* No failure allowed after this point */
fd6d4338 160 new->tcfp_result = tcfp_result;
2d550dba
DC
161 new->tcfp_mtu = parm->mtu;
162 if (!new->tcfp_mtu) {
163 new->tcfp_mtu = ~0;
c6d14ff1 164 if (R_tab)
2d550dba 165 new->tcfp_mtu = 255 << R_tab->rate.cell_log;
c6d14ff1
JP
166 }
167 if (R_tab) {
2d550dba 168 new->rate_present = true;
d1967e49
DD
169 rate64 = tb[TCA_POLICE_RATE64] ?
170 nla_get_u64(tb[TCA_POLICE_RATE64]) : 0;
171 psched_ratecfg_precompute(&new->rate, &R_tab->rate, rate64);
c6d14ff1
JP
172 qdisc_put_rtab(R_tab);
173 } else {
2d550dba 174 new->rate_present = false;
1da177e4 175 }
c6d14ff1 176 if (P_tab) {
2d550dba 177 new->peak_present = true;
d1967e49
DD
178 prate64 = tb[TCA_POLICE_PEAKRATE64] ?
179 nla_get_u64(tb[TCA_POLICE_PEAKRATE64]) : 0;
180 psched_ratecfg_precompute(&new->peak, &P_tab->rate, prate64);
c6d14ff1
JP
181 qdisc_put_rtab(P_tab);
182 } else {
2d550dba 183 new->peak_present = false;
1da177e4
LT
184 }
185
2d550dba 186 new->tcfp_burst = PSCHED_TICKS2NS(parm->burst);
f2cbd485 187 if (new->peak_present)
2d550dba
DC
188 new->tcfp_mtu_ptoks = (s64)psched_l2t_ns(&new->peak,
189 new->tcfp_mtu);
1da177e4 190
7ba699c6 191 if (tb[TCA_POLICE_AVRATE])
2d550dba 192 new->tcfp_ewma_rate = nla_get_u32(tb[TCA_POLICE_AVRATE]);
1da177e4 193
2ffe0395
BZ
194 if (tb[TCA_POLICE_PKTRATE64]) {
195 pps = nla_get_u64(tb[TCA_POLICE_PKTRATE64]);
196 ppsburst = nla_get_u64(tb[TCA_POLICE_PKTBURST64]);
197 new->pps_present = true;
198 new->tcfp_pkt_burst = PSCHED_TICKS2NS(ppsburst);
199 psched_ppscfg_precompute(&new->ppsrate, pps);
200 }
201
2d550dba 202 spin_lock_bh(&police->tcf_lock);
f2cbd485
DC
203 spin_lock_bh(&police->tcfp_lock);
204 police->tcfp_t_c = ktime_get_ns();
205 police->tcfp_toks = new->tcfp_burst;
206 if (new->peak_present)
207 police->tcfp_ptoks = new->tcfp_mtu_ptoks;
208 spin_unlock_bh(&police->tcfp_lock);
d6124d6b 209 goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
445d3749
PM
210 new = rcu_replace_pointer(police->params,
211 new,
212 lockdep_is_held(&police->tcf_lock));
e9ce1cd3 213 spin_unlock_bh(&police->tcf_lock);
1da177e4 214
d6124d6b
DC
215 if (goto_ch)
216 tcf_chain_put_by_act(goto_ch);
2d550dba
DC
217 if (new)
218 kfree_rcu(new, rcu);
1da177e4 219
1da177e4
LT
220 return ret;
221
222failure:
3b69a4c9
YY
223 qdisc_put_rtab(P_tab);
224 qdisc_put_rtab(R_tab);
d6124d6b
DC
225 if (goto_ch)
226 tcf_chain_put_by_act(goto_ch);
227release_idr:
4e8ddd7f 228 tcf_idr_release(*a, bind);
1da177e4
LT
229 return err;
230}
231
4ddc844e
DC
232static bool tcf_police_mtu_check(struct sk_buff *skb, u32 limit)
233{
234 u32 len;
235
236 if (skb_is_gso(skb))
237 return skb_gso_validate_mac_len(skb, limit);
238
239 len = qdisc_pkt_len(skb);
240 if (skb_at_tc_ingress(skb))
241 len += skb->mac_len;
242
243 return len <= limit;
244}
245
871cf386
PT
246TC_INDIRECT_SCOPE int tcf_police_act(struct sk_buff *skb,
247 const struct tc_action *a,
248 struct tcf_result *res)
1da177e4 249{
a85a970a 250 struct tcf_police *police = to_police(a);
2ffe0395 251 s64 now, toks, ppstoks = 0, ptoks = 0;
2d550dba 252 struct tcf_police_params *p;
93be42f9 253 int ret;
1da177e4 254
3d3ed181 255 tcf_lastuse_update(&police->tcf_tm);
50dc9a85 256 bstats_update(this_cpu_ptr(police->common.cpu_bstats), skb);
1da177e4 257
2d550dba
DC
258 ret = READ_ONCE(police->tcf_action);
259 p = rcu_dereference_bh(police->params);
260
261 if (p->tcfp_ewma_rate) {
1c0d32fd
ED
262 struct gnet_stats_rate_est64 sample;
263
264 if (!gen_estimator_read(&police->tcf_rate_est, &sample) ||
2d550dba 265 sample.bps >= p->tcfp_ewma_rate)
93be42f9 266 goto inc_overlimits;
1da177e4 267 }
1da177e4 268
4ddc844e 269 if (tcf_police_mtu_check(skb, p->tcfp_mtu)) {
2ffe0395 270 if (!p->rate_present && !p->pps_present) {
2d550dba
DC
271 ret = p->tcfp_result;
272 goto end;
1da177e4
LT
273 }
274
d2de875c 275 now = ktime_get_ns();
f2cbd485
DC
276 spin_lock_bh(&police->tcfp_lock);
277 toks = min_t(s64, now - police->tcfp_t_c, p->tcfp_burst);
2d550dba 278 if (p->peak_present) {
f2cbd485 279 ptoks = toks + police->tcfp_ptoks;
2d550dba
DC
280 if (ptoks > p->tcfp_mtu_ptoks)
281 ptoks = p->tcfp_mtu_ptoks;
282 ptoks -= (s64)psched_l2t_ns(&p->peak,
283 qdisc_pkt_len(skb));
1da177e4 284 }
2ffe0395
BZ
285 if (p->rate_present) {
286 toks += police->tcfp_toks;
287 if (toks > p->tcfp_burst)
288 toks = p->tcfp_burst;
289 toks -= (s64)psched_l2t_ns(&p->rate, qdisc_pkt_len(skb));
290 } else if (p->pps_present) {
291 ppstoks = min_t(s64, now - police->tcfp_t_c, p->tcfp_pkt_burst);
292 ppstoks += police->tcfp_pkttoks;
293 if (ppstoks > p->tcfp_pkt_burst)
294 ppstoks = p->tcfp_pkt_burst;
295 ppstoks -= (s64)psched_pkt2t_ns(&p->ppsrate, 1);
296 }
297 if ((toks | ptoks | ppstoks) >= 0) {
f2cbd485
DC
298 police->tcfp_t_c = now;
299 police->tcfp_toks = toks;
300 police->tcfp_ptoks = ptoks;
2ffe0395 301 police->tcfp_pkttoks = ppstoks;
f2cbd485 302 spin_unlock_bh(&police->tcfp_lock);
2d550dba 303 ret = p->tcfp_result;
93be42f9 304 goto inc_drops;
1da177e4 305 }
f2cbd485 306 spin_unlock_bh(&police->tcfp_lock);
1da177e4 307 }
93be42f9
DC
308
309inc_overlimits:
310 qstats_overlimit_inc(this_cpu_ptr(police->common.cpu_qstats));
311inc_drops:
312 if (ret == TC_ACT_SHOT)
313 qstats_drop_inc(this_cpu_ptr(police->common.cpu_qstats));
2d550dba 314end:
93be42f9 315 return ret;
1da177e4
LT
316}
317
2d550dba
DC
318static void tcf_police_cleanup(struct tc_action *a)
319{
320 struct tcf_police *police = to_police(a);
321 struct tcf_police_params *p;
322
323 p = rcu_dereference_protected(police->params, 1);
324 if (p)
325 kfree_rcu(p, rcu);
326}
327
12f02b6b 328static void tcf_police_stats_update(struct tc_action *a,
4b61d3e8 329 u64 bytes, u64 packets, u64 drops,
12f02b6b
PJV
330 u64 lastuse, bool hw)
331{
332 struct tcf_police *police = to_police(a);
333 struct tcf_t *tm = &police->tcf_tm;
334
4b61d3e8 335 tcf_action_update_stats(a, bytes, packets, drops, hw);
12f02b6b
PJV
336 tm->lastuse = max_t(u64, tm->lastuse, lastuse);
337}
338
2ac06347 339static int tcf_police_dump(struct sk_buff *skb, struct tc_action *a,
5a7a5555 340 int bind, int ref)
1da177e4 341{
27a884dc 342 unsigned char *b = skb_tail_pointer(skb);
a85a970a 343 struct tcf_police *police = to_police(a);
2d550dba 344 struct tcf_police_params *p;
0f04cfd0
JM
345 struct tc_police opt = {
346 .index = police->tcf_index,
036bb443
VB
347 .refcnt = refcount_read(&police->tcf_refcnt) - ref,
348 .bindcnt = atomic_read(&police->tcf_bindcnt) - bind,
0f04cfd0 349 };
3d3ed181 350 struct tcf_t t;
0f04cfd0 351
e329bc42
VB
352 spin_lock_bh(&police->tcf_lock);
353 opt.action = police->tcf_action;
2d550dba
DC
354 p = rcu_dereference_protected(police->params,
355 lockdep_is_held(&police->tcf_lock));
356 opt.mtu = p->tcfp_mtu;
357 opt.burst = PSCHED_NS2TICKS(p->tcfp_burst);
d1967e49 358 if (p->rate_present) {
2d550dba 359 psched_ratecfg_getrate(&opt.rate, &p->rate);
682881ee 360 if ((p->rate.rate_bytes_ps >= (1ULL << 32)) &&
d1967e49 361 nla_put_u64_64bit(skb, TCA_POLICE_RATE64,
682881ee 362 p->rate.rate_bytes_ps,
d1967e49
DD
363 TCA_POLICE_PAD))
364 goto nla_put_failure;
365 }
366 if (p->peak_present) {
2d550dba 367 psched_ratecfg_getrate(&opt.peakrate, &p->peak);
682881ee 368 if ((p->peak.rate_bytes_ps >= (1ULL << 32)) &&
d1967e49 369 nla_put_u64_64bit(skb, TCA_POLICE_PEAKRATE64,
682881ee 370 p->peak.rate_bytes_ps,
d1967e49
DD
371 TCA_POLICE_PAD))
372 goto nla_put_failure;
373 }
2ffe0395
BZ
374 if (p->pps_present) {
375 if (nla_put_u64_64bit(skb, TCA_POLICE_PKTRATE64,
682881ee 376 p->ppsrate.rate_pkts_ps,
2ffe0395
BZ
377 TCA_POLICE_PAD))
378 goto nla_put_failure;
379 if (nla_put_u64_64bit(skb, TCA_POLICE_PKTBURST64,
380 PSCHED_NS2TICKS(p->tcfp_pkt_burst),
381 TCA_POLICE_PAD))
382 goto nla_put_failure;
383 }
1b34ec43
DM
384 if (nla_put(skb, TCA_POLICE_TBF, sizeof(opt), &opt))
385 goto nla_put_failure;
2d550dba
DC
386 if (p->tcfp_result &&
387 nla_put_u32(skb, TCA_POLICE_RESULT, p->tcfp_result))
1b34ec43 388 goto nla_put_failure;
2d550dba
DC
389 if (p->tcfp_ewma_rate &&
390 nla_put_u32(skb, TCA_POLICE_AVRATE, p->tcfp_ewma_rate))
1b34ec43 391 goto nla_put_failure;
3d3ed181 392
985fd98a 393 tcf_tm_dump(&t, &police->tcf_tm);
3d3ed181
JHS
394 if (nla_put_64bit(skb, TCA_POLICE_TM, sizeof(t), &t, TCA_POLICE_PAD))
395 goto nla_put_failure;
e329bc42 396 spin_unlock_bh(&police->tcf_lock);
3d3ed181 397
1da177e4
LT
398 return skb->len;
399
7ba699c6 400nla_put_failure:
e329bc42 401 spin_unlock_bh(&police->tcf_lock);
dc5fc579 402 nlmsg_trim(skb, b);
1da177e4
LT
403 return -1;
404}
405
b50e462b
IS
406static int tcf_police_act_to_flow_act(int tc_act, u32 *extval,
407 struct netlink_ext_ack *extack)
b8cd5831
JL
408{
409 int act_id = -EOPNOTSUPP;
410
411 if (!TC_ACT_EXT_OPCODE(tc_act)) {
412 if (tc_act == TC_ACT_OK)
413 act_id = FLOW_ACTION_ACCEPT;
414 else if (tc_act == TC_ACT_SHOT)
415 act_id = FLOW_ACTION_DROP;
416 else if (tc_act == TC_ACT_PIPE)
417 act_id = FLOW_ACTION_PIPE;
b50e462b
IS
418 else if (tc_act == TC_ACT_RECLASSIFY)
419 NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform/exceed action is \"reclassify\"");
420 else
421 NL_SET_ERR_MSG_MOD(extack, "Unsupported conform/exceed action offload");
b8cd5831
JL
422 } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_GOTO_CHAIN)) {
423 act_id = FLOW_ACTION_GOTO;
424 *extval = tc_act & TC_ACT_EXT_VAL_MASK;
425 } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_JUMP)) {
426 act_id = FLOW_ACTION_JUMP;
427 *extval = tc_act & TC_ACT_EXT_VAL_MASK;
b50e462b 428 } else if (tc_act == TC_ACT_UNSPEC) {
052f744f 429 act_id = FLOW_ACTION_CONTINUE;
b50e462b
IS
430 } else {
431 NL_SET_ERR_MSG_MOD(extack, "Unsupported conform/exceed action offload");
b8cd5831
JL
432 }
433
434 return act_id;
435}
436
c54e1d92 437static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data,
c2ccf84e
IS
438 u32 *index_inc, bool bind,
439 struct netlink_ext_ack *extack)
c54e1d92
BZ
440{
441 if (bind) {
442 struct flow_action_entry *entry = entry_data;
b8cd5831
JL
443 struct tcf_police *police = to_police(act);
444 struct tcf_police_params *p;
445 int act_id;
446
447 p = rcu_dereference_protected(police->params,
448 lockdep_is_held(&police->tcf_lock));
c54e1d92
BZ
449
450 entry->id = FLOW_ACTION_POLICE;
451 entry->police.burst = tcf_police_burst(act);
452 entry->police.rate_bytes_ps =
453 tcf_police_rate_bytes_ps(act);
b8cd5831
JL
454 entry->police.peakrate_bytes_ps = tcf_police_peakrate_bytes_ps(act);
455 entry->police.avrate = tcf_police_tcfp_ewma_rate(act);
456 entry->police.overhead = tcf_police_rate_overhead(act);
c54e1d92
BZ
457 entry->police.burst_pkt = tcf_police_burst_pkt(act);
458 entry->police.rate_pkt_ps =
459 tcf_police_rate_pkt_ps(act);
460 entry->police.mtu = tcf_police_tcfp_mtu(act);
b8cd5831
JL
461
462 act_id = tcf_police_act_to_flow_act(police->tcf_action,
b50e462b
IS
463 &entry->police.exceed.extval,
464 extack);
b8cd5831
JL
465 if (act_id < 0)
466 return act_id;
467
468 entry->police.exceed.act_id = act_id;
469
470 act_id = tcf_police_act_to_flow_act(p->tcfp_result,
b50e462b
IS
471 &entry->police.notexceed.extval,
472 extack);
b8cd5831
JL
473 if (act_id < 0)
474 return act_id;
475
476 entry->police.notexceed.act_id = act_id;
477
c54e1d92
BZ
478 *index_inc = 1;
479 } else {
8cbfe939
BZ
480 struct flow_offload_action *fl_action = entry_data;
481
482 fl_action->id = FLOW_ACTION_POLICE;
c54e1d92
BZ
483 }
484
485 return 0;
486}
487
1da177e4
LT
488MODULE_AUTHOR("Alexey Kuznetsov");
489MODULE_DESCRIPTION("Policing actions");
490MODULE_LICENSE("GPL");
491
492static struct tc_action_ops act_police_ops = {
493 .kind = "police",
eddd2cf1 494 .id = TCA_ID_POLICE,
1da177e4 495 .owner = THIS_MODULE,
12f02b6b 496 .stats_update = tcf_police_stats_update,
2ac06347
JHS
497 .act = tcf_police_act,
498 .dump = tcf_police_dump,
499 .init = tcf_police_init,
2d550dba 500 .cleanup = tcf_police_cleanup,
c54e1d92 501 .offload_act_setup = tcf_police_offload_act_setup,
a85a970a 502 .size = sizeof(struct tcf_police),
ddf97ccd
WC
503};
504
505static __net_init int police_init_net(struct net *net)
506{
acd0a7ab 507 struct tc_action_net *tn = net_generic(net, act_police_ops.net_id);
ddf97ccd 508
981471bd 509 return tc_action_net_init(net, tn, &act_police_ops);
ddf97ccd
WC
510}
511
039af9c6 512static void __net_exit police_exit_net(struct list_head *net_list)
ddf97ccd 513{
acd0a7ab 514 tc_action_net_exit(net_list, act_police_ops.net_id);
ddf97ccd
WC
515}
516
517static struct pernet_operations police_net_ops = {
518 .init = police_init_net,
039af9c6 519 .exit_batch = police_exit_net,
acd0a7ab 520 .id = &act_police_ops.net_id,
ddf97ccd 521 .size = sizeof(struct tc_action_net),
1da177e4
LT
522};
523
5a7a5555 524static int __init police_init_module(void)
1da177e4 525{
ddf97ccd 526 return tcf_register_action(&act_police_ops, &police_net_ops);
1da177e4
LT
527}
528
5a7a5555 529static void __exit police_cleanup_module(void)
1da177e4 530{
ddf97ccd 531 tcf_unregister_action(&act_police_ops, &police_net_ops);
1da177e4
LT
532}
533
534module_init(police_init_module);
535module_exit(police_cleanup_module);