blob: 580698db578a44333d9c8f7a074300464e8a7039 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/sched/police.c Input police filter.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 * J Hadi Salim (action changes)
11 */
12
13#include <asm/uaccess.h>
14#include <asm/system.h>
15#include <linux/bitops.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/module.h>
17#include <linux/types.h>
18#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/string.h>
20#include <linux/mm.h>
21#include <linux/socket.h>
22#include <linux/sockios.h>
23#include <linux/in.h>
24#include <linux/errno.h>
25#include <linux/interrupt.h>
26#include <linux/netdevice.h>
27#include <linux/skbuff.h>
28#include <linux/module.h>
29#include <linux/rtnetlink.h>
30#include <linux/init.h>
31#include <net/sock.h>
32#include <net/act_api.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070033#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
David S. Millere9ce1cd2006-08-21 23:54:55 -070035#define L2T(p,L) ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log])
36#define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log])
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
David S. Millere9ce1cd2006-08-21 23:54:55 -070038#define POL_TAB_MASK 15
39static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1];
40static u32 police_idx_gen;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041static DEFINE_RWLOCK(police_lock);
42
David S. Millere9ce1cd2006-08-21 23:54:55 -070043static struct tcf_hashinfo police_hash_info = {
44 .htab = tcf_police_ht,
45 .hmask = POL_TAB_MASK,
46 .lock = &police_lock,
47};
48
Patrick McHardy1e9b3d52006-11-30 19:54:05 -080049/* old policer structure from before tc actions */
50struct tc_police_compat
51{
52 u32 index;
53 int action;
54 u32 limit;
55 u32 burst;
56 u32 mtu;
57 struct tc_ratespec rate;
58 struct tc_ratespec peakrate;
59};
60
Linus Torvalds1da177e2005-04-16 15:20:36 -070061/* Each policer is serialized by its individual spinlock */
62
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#ifdef CONFIG_NET_CLS_ACT
Jamal Hadi Salim83b950c2006-04-06 22:24:22 -070064static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb,
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +090065 int type, struct tc_action *a)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
David S. Millere9ce1cd2006-08-21 23:54:55 -070067 struct tcf_common *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
69 struct rtattr *r;
70
71 read_lock(&police_lock);
72
73 s_i = cb->args[0];
74
David S. Millere9ce1cd2006-08-21 23:54:55 -070075 for (i = 0; i < (POL_TAB_MASK + 1); i++) {
76 p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)];
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
David S. Millere9ce1cd2006-08-21 23:54:55 -070078 for (; p; p = p->tcfc_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 index++;
80 if (index < s_i)
81 continue;
82 a->priv = p;
83 a->order = index;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -070084 r = (struct rtattr *)skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 RTA_PUT(skb, a->order, 0, NULL);
86 if (type == RTM_DELACTION)
87 err = tcf_action_dump_1(skb, a, 0, 1);
88 else
89 err = tcf_action_dump_1(skb, a, 0, 0);
90 if (err < 0) {
91 index--;
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070092 nlmsg_trim(skb, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 goto done;
94 }
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -070095 r->rta_len = skb_tail_pointer(skb) - (u8 *)r;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 n_i++;
97 }
98 }
99done:
100 read_unlock(&police_lock);
101 if (n_i)
102 cb->args[0] += n_i;
103 return n_i;
104
105rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -0700106 nlmsg_trim(skb, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 goto done;
108}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109#endif
110
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111void tcf_police_destroy(struct tcf_police *p)
112{
David S. Millere9ce1cd2006-08-21 23:54:55 -0700113 unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK);
114 struct tcf_common **p1p;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900115
David S. Millere9ce1cd2006-08-21 23:54:55 -0700116 for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
117 if (*p1p == &p->common) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 write_lock_bh(&police_lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700119 *p1p = p->tcf_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 write_unlock_bh(&police_lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700121 gen_kill_estimator(&p->tcf_bstats,
122 &p->tcf_rate_est);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700123 if (p->tcfp_R_tab)
124 qdisc_put_rtab(p->tcfp_R_tab);
125 if (p->tcfp_P_tab)
126 qdisc_put_rtab(p->tcfp_P_tab);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 kfree(p);
128 return;
129 }
130 }
131 BUG_TRAP(0);
132}
133
134#ifdef CONFIG_NET_CLS_ACT
135static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900136 struct tc_action *a, int ovr, int bind)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
138 unsigned h;
139 int ret = 0, err;
140 struct rtattr *tb[TCA_POLICE_MAX];
141 struct tc_police *parm;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700142 struct tcf_police *police;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL;
Patrick McHardy1e9b3d52006-11-30 19:54:05 -0800144 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146 if (rta == NULL || rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0)
147 return -EINVAL;
148
Patrick McHardy1e9b3d52006-11-30 19:54:05 -0800149 if (tb[TCA_POLICE_TBF-1] == NULL)
150 return -EINVAL;
151 size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]);
152 if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 return -EINVAL;
154 parm = RTA_DATA(tb[TCA_POLICE_TBF-1]);
155
156 if (tb[TCA_POLICE_RESULT-1] != NULL &&
157 RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32))
158 return -EINVAL;
159 if (tb[TCA_POLICE_RESULT-1] != NULL &&
160 RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32))
161 return -EINVAL;
162
David S. Millere9ce1cd2006-08-21 23:54:55 -0700163 if (parm->index) {
164 struct tcf_common *pc;
165
166 pc = tcf_hash_lookup(parm->index, &police_hash_info);
167 if (pc != NULL) {
168 a->priv = pc;
169 police = to_police(pc);
170 if (bind) {
171 police->tcf_bindcnt += 1;
172 police->tcf_refcnt += 1;
173 }
174 if (ovr)
175 goto override;
176 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 }
179
David S. Millere9ce1cd2006-08-21 23:54:55 -0700180 police = kzalloc(sizeof(*police), GFP_KERNEL);
181 if (police == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 ret = ACT_P_CREATED;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700184 police->tcf_refcnt = 1;
185 spin_lock_init(&police->tcf_lock);
186 police->tcf_stats_lock = &police->tcf_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 if (bind)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700188 police->tcf_bindcnt = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189override:
190 if (parm->rate.rate) {
191 err = -ENOMEM;
192 R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]);
193 if (R_tab == NULL)
194 goto failure;
195 if (parm->peakrate.rate) {
196 P_tab = qdisc_get_rtab(&parm->peakrate,
197 tb[TCA_POLICE_PEAKRATE-1]);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700198 if (P_tab == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 qdisc_put_rtab(R_tab);
200 goto failure;
201 }
202 }
203 }
204 /* No failure allowed after this point */
David S. Millere9ce1cd2006-08-21 23:54:55 -0700205 spin_lock_bh(&police->tcf_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 if (R_tab != NULL) {
David S. Millere9ce1cd2006-08-21 23:54:55 -0700207 qdisc_put_rtab(police->tcfp_R_tab);
208 police->tcfp_R_tab = R_tab;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 }
210 if (P_tab != NULL) {
David S. Millere9ce1cd2006-08-21 23:54:55 -0700211 qdisc_put_rtab(police->tcfp_P_tab);
212 police->tcfp_P_tab = P_tab;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 }
214
215 if (tb[TCA_POLICE_RESULT-1])
David S. Millere9ce1cd2006-08-21 23:54:55 -0700216 police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]);
217 police->tcfp_toks = police->tcfp_burst = parm->burst;
218 police->tcfp_mtu = parm->mtu;
219 if (police->tcfp_mtu == 0) {
220 police->tcfp_mtu = ~0;
221 if (police->tcfp_R_tab)
222 police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700224 if (police->tcfp_P_tab)
225 police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu);
226 police->tcf_action = parm->action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 if (tb[TCA_POLICE_AVRATE-1])
David S. Millere9ce1cd2006-08-21 23:54:55 -0700229 police->tcfp_ewma_rate =
230 *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 if (est)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700232 gen_replace_estimator(&police->tcf_bstats,
233 &police->tcf_rate_est,
234 police->tcf_stats_lock, est);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
David S. Millere9ce1cd2006-08-21 23:54:55 -0700236 spin_unlock_bh(&police->tcf_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 if (ret != ACT_P_CREATED)
238 return ret;
239
Patrick McHardy3bebcda2007-03-23 11:29:25 -0700240 police->tcfp_t_c = psched_get_time();
David S. Millere9ce1cd2006-08-21 23:54:55 -0700241 police->tcf_index = parm->index ? parm->index :
242 tcf_hash_new_index(&police_idx_gen, &police_hash_info);
243 h = tcf_hash(police->tcf_index, POL_TAB_MASK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 write_lock_bh(&police_lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700245 police->tcf_next = tcf_police_ht[h];
246 tcf_police_ht[h] = &police->common;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 write_unlock_bh(&police_lock);
248
David S. Millere9ce1cd2006-08-21 23:54:55 -0700249 a->priv = police;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 return ret;
251
252failure:
253 if (ret == ACT_P_CREATED)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700254 kfree(police);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 return err;
256}
257
258static int tcf_act_police_cleanup(struct tc_action *a, int bind)
259{
David S. Millere9ce1cd2006-08-21 23:54:55 -0700260 struct tcf_police *p = a->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 if (p != NULL)
263 return tcf_police_release(p, bind);
264 return 0;
265}
266
Patrick McHardyf43c5a02006-01-08 22:15:34 -0800267static int tcf_act_police(struct sk_buff *skb, struct tc_action *a,
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900268 struct tcf_result *res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
David S. Millere9ce1cd2006-08-21 23:54:55 -0700270 struct tcf_police *police = a->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 psched_time_t now;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 long toks;
273 long ptoks = 0;
274
David S. Millere9ce1cd2006-08-21 23:54:55 -0700275 spin_lock(&police->tcf_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
David S. Millere9ce1cd2006-08-21 23:54:55 -0700277 police->tcf_bstats.bytes += skb->len;
278 police->tcf_bstats.packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
David S. Millere9ce1cd2006-08-21 23:54:55 -0700280 if (police->tcfp_ewma_rate &&
281 police->tcf_rate_est.bps >= police->tcfp_ewma_rate) {
282 police->tcf_qstats.overlimits++;
283 spin_unlock(&police->tcf_lock);
284 return police->tcf_action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
David S. Millere9ce1cd2006-08-21 23:54:55 -0700287 if (skb->len <= police->tcfp_mtu) {
288 if (police->tcfp_R_tab == NULL) {
289 spin_unlock(&police->tcf_lock);
290 return police->tcfp_result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
292
Patrick McHardy3bebcda2007-03-23 11:29:25 -0700293 now = psched_get_time();
Patrick McHardy03cc45c2007-03-23 11:29:11 -0700294 toks = psched_tdiff_bounded(now, police->tcfp_t_c,
295 police->tcfp_burst);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700296 if (police->tcfp_P_tab) {
297 ptoks = toks + police->tcfp_ptoks;
298 if (ptoks > (long)L2T_P(police, police->tcfp_mtu))
299 ptoks = (long)L2T_P(police, police->tcfp_mtu);
300 ptoks -= L2T_P(police, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700302 toks += police->tcfp_toks;
303 if (toks > (long)police->tcfp_burst)
304 toks = police->tcfp_burst;
305 toks -= L2T(police, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 if ((toks|ptoks) >= 0) {
David S. Millere9ce1cd2006-08-21 23:54:55 -0700307 police->tcfp_t_c = now;
308 police->tcfp_toks = toks;
309 police->tcfp_ptoks = ptoks;
310 spin_unlock(&police->tcf_lock);
311 return police->tcfp_result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 }
313 }
314
David S. Millere9ce1cd2006-08-21 23:54:55 -0700315 police->tcf_qstats.overlimits++;
316 spin_unlock(&police->tcf_lock);
317 return police->tcf_action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318}
319
320static int
321tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
322{
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700323 unsigned char *b = skb_tail_pointer(skb);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700324 struct tcf_police *police = a->priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 struct tc_police opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
David S. Millere9ce1cd2006-08-21 23:54:55 -0700327 opt.index = police->tcf_index;
328 opt.action = police->tcf_action;
329 opt.mtu = police->tcfp_mtu;
330 opt.burst = police->tcfp_burst;
331 opt.refcnt = police->tcf_refcnt - ref;
332 opt.bindcnt = police->tcf_bindcnt - bind;
333 if (police->tcfp_R_tab)
334 opt.rate = police->tcfp_R_tab->rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 else
336 memset(&opt.rate, 0, sizeof(opt.rate));
David S. Millere9ce1cd2006-08-21 23:54:55 -0700337 if (police->tcfp_P_tab)
338 opt.peakrate = police->tcfp_P_tab->rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 else
340 memset(&opt.peakrate, 0, sizeof(opt.peakrate));
341 RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700342 if (police->tcfp_result)
343 RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int),
344 &police->tcfp_result);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700345 if (police->tcfp_ewma_rate)
346 RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 return skb->len;
348
349rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -0700350 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 return -1;
352}
353
354MODULE_AUTHOR("Alexey Kuznetsov");
355MODULE_DESCRIPTION("Policing actions");
356MODULE_LICENSE("GPL");
357
358static struct tc_action_ops act_police_ops = {
359 .kind = "police",
David S. Millere9ce1cd2006-08-21 23:54:55 -0700360 .hinfo = &police_hash_info,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 .type = TCA_ID_POLICE,
362 .capab = TCA_CAP_NONE,
363 .owner = THIS_MODULE,
364 .act = tcf_act_police,
365 .dump = tcf_act_police_dump,
366 .cleanup = tcf_act_police_cleanup,
David S. Millere9ce1cd2006-08-21 23:54:55 -0700367 .lookup = tcf_hash_search,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 .init = tcf_act_police_locate,
Jamal Hadi Salim83b950c2006-04-06 22:24:22 -0700369 .walk = tcf_act_police_walker
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370};
371
372static int __init
373police_init_module(void)
374{
375 return tcf_register_action(&act_police_ops);
376}
377
378static void __exit
379police_cleanup_module(void)
380{
381 tcf_unregister_action(&act_police_ops);
382}
383
384module_init(police_init_module);
385module_exit(police_cleanup_module);
386
Patrick McHardy31bd06e2006-01-08 22:16:25 -0800387#else /* CONFIG_NET_CLS_ACT */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
David S. Millere9ce1cd2006-08-21 23:54:55 -0700389static struct tcf_common *tcf_police_lookup(u32 index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
David S. Millere9ce1cd2006-08-21 23:54:55 -0700391 struct tcf_hashinfo *hinfo = &police_hash_info;
392 struct tcf_common *p;
393
394 read_lock(hinfo->lock);
395 for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p;
396 p = p->tcfc_next) {
397 if (p->tcfc_index == index)
398 break;
399 }
400 read_unlock(hinfo->lock);
401
402 return p;
403}
404
405static u32 tcf_police_new_index(void)
406{
407 u32 *idx_gen = &police_idx_gen;
408 u32 val = *idx_gen;
409
410 do {
411 if (++val == 0)
412 val = 1;
413 } while (tcf_police_lookup(val));
414
415 return (*idx_gen = val);
416}
417
418struct tcf_police *tcf_police_locate(struct rtattr *rta, struct rtattr *est)
419{
420 unsigned int h;
421 struct tcf_police *police;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 struct rtattr *tb[TCA_POLICE_MAX];
423 struct tc_police *parm;
Patrick McHardy1e9b3d52006-11-30 19:54:05 -0800424 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
426 if (rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0)
427 return NULL;
428
Patrick McHardy1e9b3d52006-11-30 19:54:05 -0800429 if (tb[TCA_POLICE_TBF-1] == NULL)
430 return NULL;
431 size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]);
432 if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 return NULL;
434
435 parm = RTA_DATA(tb[TCA_POLICE_TBF-1]);
436
David S. Millere9ce1cd2006-08-21 23:54:55 -0700437 if (parm->index) {
438 struct tcf_common *pc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
David S. Millere9ce1cd2006-08-21 23:54:55 -0700440 pc = tcf_police_lookup(parm->index);
441 if (pc) {
442 police = to_police(pc);
443 police->tcf_refcnt++;
444 return police;
445 }
446 }
447 police = kzalloc(sizeof(*police), GFP_KERNEL);
448 if (unlikely(!police))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 return NULL;
450
David S. Millere9ce1cd2006-08-21 23:54:55 -0700451 police->tcf_refcnt = 1;
452 spin_lock_init(&police->tcf_lock);
453 police->tcf_stats_lock = &police->tcf_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 if (parm->rate.rate) {
David S. Millere9ce1cd2006-08-21 23:54:55 -0700455 police->tcfp_R_tab =
456 qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]);
457 if (police->tcfp_R_tab == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 goto failure;
459 if (parm->peakrate.rate) {
David S. Millere9ce1cd2006-08-21 23:54:55 -0700460 police->tcfp_P_tab =
461 qdisc_get_rtab(&parm->peakrate,
462 tb[TCA_POLICE_PEAKRATE-1]);
463 if (police->tcfp_P_tab == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 goto failure;
465 }
466 }
467 if (tb[TCA_POLICE_RESULT-1]) {
468 if (RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32))
469 goto failure;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700470 police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 if (tb[TCA_POLICE_AVRATE-1]) {
473 if (RTA_PAYLOAD(tb[TCA_POLICE_AVRATE-1]) != sizeof(u32))
474 goto failure;
David S. Millere9ce1cd2006-08-21 23:54:55 -0700475 police->tcfp_ewma_rate =
476 *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700478 police->tcfp_toks = police->tcfp_burst = parm->burst;
479 police->tcfp_mtu = parm->mtu;
480 if (police->tcfp_mtu == 0) {
481 police->tcfp_mtu = ~0;
482 if (police->tcfp_R_tab)
483 police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700485 if (police->tcfp_P_tab)
486 police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu);
Patrick McHardy3bebcda2007-03-23 11:29:25 -0700487 police->tcfp_t_c = psched_get_time();
David S. Millere9ce1cd2006-08-21 23:54:55 -0700488 police->tcf_index = parm->index ? parm->index :
489 tcf_police_new_index();
490 police->tcf_action = parm->action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 if (est)
David S. Millere9ce1cd2006-08-21 23:54:55 -0700492 gen_new_estimator(&police->tcf_bstats, &police->tcf_rate_est,
493 police->tcf_stats_lock, est);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700494 h = tcf_hash(police->tcf_index, POL_TAB_MASK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 write_lock_bh(&police_lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700496 police->tcf_next = tcf_police_ht[h];
497 tcf_police_ht[h] = &police->common;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 write_unlock_bh(&police_lock);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700499 return police;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500
501failure:
David S. Millere9ce1cd2006-08-21 23:54:55 -0700502 if (police->tcfp_R_tab)
503 qdisc_put_rtab(police->tcfp_R_tab);
504 kfree(police);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 return NULL;
506}
507
David S. Millere9ce1cd2006-08-21 23:54:55 -0700508int tcf_police(struct sk_buff *skb, struct tcf_police *police)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 psched_time_t now;
511 long toks;
512 long ptoks = 0;
513
David S. Millere9ce1cd2006-08-21 23:54:55 -0700514 spin_lock(&police->tcf_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
David S. Millere9ce1cd2006-08-21 23:54:55 -0700516 police->tcf_bstats.bytes += skb->len;
517 police->tcf_bstats.packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
David S. Millere9ce1cd2006-08-21 23:54:55 -0700519 if (police->tcfp_ewma_rate &&
520 police->tcf_rate_est.bps >= police->tcfp_ewma_rate) {
521 police->tcf_qstats.overlimits++;
522 spin_unlock(&police->tcf_lock);
523 return police->tcf_action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700525 if (skb->len <= police->tcfp_mtu) {
526 if (police->tcfp_R_tab == NULL) {
527 spin_unlock(&police->tcf_lock);
528 return police->tcfp_result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 }
530
Patrick McHardy3bebcda2007-03-23 11:29:25 -0700531 now = psched_get_time();
Patrick McHardy03cc45c2007-03-23 11:29:11 -0700532 toks = psched_tdiff_bounded(now, police->tcfp_t_c,
533 police->tcfp_burst);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700534 if (police->tcfp_P_tab) {
535 ptoks = toks + police->tcfp_ptoks;
536 if (ptoks > (long)L2T_P(police, police->tcfp_mtu))
537 ptoks = (long)L2T_P(police, police->tcfp_mtu);
538 ptoks -= L2T_P(police, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 }
David S. Millere9ce1cd2006-08-21 23:54:55 -0700540 toks += police->tcfp_toks;
541 if (toks > (long)police->tcfp_burst)
542 toks = police->tcfp_burst;
543 toks -= L2T(police, skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 if ((toks|ptoks) >= 0) {
David S. Millere9ce1cd2006-08-21 23:54:55 -0700545 police->tcfp_t_c = now;
546 police->tcfp_toks = toks;
547 police->tcfp_ptoks = ptoks;
548 spin_unlock(&police->tcf_lock);
549 return police->tcfp_result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 }
551 }
552
David S. Millere9ce1cd2006-08-21 23:54:55 -0700553 police->tcf_qstats.overlimits++;
554 spin_unlock(&police->tcf_lock);
555 return police->tcf_action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556}
Patrick McHardy31bd06e2006-01-08 22:16:25 -0800557EXPORT_SYMBOL(tcf_police);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
David S. Millere9ce1cd2006-08-21 23:54:55 -0700559int tcf_police_dump(struct sk_buff *skb, struct tcf_police *police)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700561 unsigned char *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 struct tc_police opt;
563
David S. Millere9ce1cd2006-08-21 23:54:55 -0700564 opt.index = police->tcf_index;
565 opt.action = police->tcf_action;
566 opt.mtu = police->tcfp_mtu;
567 opt.burst = police->tcfp_burst;
568 if (police->tcfp_R_tab)
569 opt.rate = police->tcfp_R_tab->rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 else
571 memset(&opt.rate, 0, sizeof(opt.rate));
David S. Millere9ce1cd2006-08-21 23:54:55 -0700572 if (police->tcfp_P_tab)
573 opt.peakrate = police->tcfp_P_tab->rate;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 else
575 memset(&opt.peakrate, 0, sizeof(opt.peakrate));
576 RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700577 if (police->tcfp_result)
578 RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int),
579 &police->tcfp_result);
David S. Millere9ce1cd2006-08-21 23:54:55 -0700580 if (police->tcfp_ewma_rate)
581 RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 return skb->len;
583
584rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -0700585 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 return -1;
587}
588
David S. Millere9ce1cd2006-08-21 23:54:55 -0700589int tcf_police_dump_stats(struct sk_buff *skb, struct tcf_police *police)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
591 struct gnet_dump d;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900592
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,
David S. Millere9ce1cd2006-08-21 23:54:55 -0700594 TCA_XSTATS, police->tcf_stats_lock,
595 &d) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 goto errout;
YOSHIFUJI Hideaki10297b92007-02-09 23:25:16 +0900597
David S. Millere9ce1cd2006-08-21 23:54:55 -0700598 if (gnet_stats_copy_basic(&d, &police->tcf_bstats) < 0 ||
David S. Millere9ce1cd2006-08-21 23:54:55 -0700599 gnet_stats_copy_rate_est(&d, &police->tcf_rate_est) < 0 ||
David S. Millere9ce1cd2006-08-21 23:54:55 -0700600 gnet_stats_copy_queue(&d, &police->tcf_qstats) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 goto errout;
602
603 if (gnet_stats_finish_copy(&d) < 0)
604 goto errout;
605
606 return 0;
607
608errout:
609 return -1;
610}
611
Patrick McHardy31bd06e2006-01-08 22:16:25 -0800612#endif /* CONFIG_NET_CLS_ACT */