| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 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 Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | #include <linux/module.h> | 
|  | 17 | #include <linux/types.h> | 
|  | 18 | #include <linux/kernel.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 19 | #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> | 
|  | 33 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 34 | #define L2T(p,L)   ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log]) | 
|  | 35 | #define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log]) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 37 | #define POL_TAB_MASK     15 | 
|  | 38 | static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1]; | 
|  | 39 | static u32 police_idx_gen; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | static DEFINE_RWLOCK(police_lock); | 
|  | 41 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 42 | static struct tcf_hashinfo police_hash_info = { | 
|  | 43 | .htab	=	tcf_police_ht, | 
|  | 44 | .hmask	=	POL_TAB_MASK, | 
|  | 45 | .lock	=	&police_lock, | 
|  | 46 | }; | 
|  | 47 |  | 
| Patrick McHardy | 1e9b3d5 | 2006-11-30 19:54:05 -0800 | [diff] [blame] | 48 | /* old policer structure from before tc actions */ | 
|  | 49 | struct tc_police_compat | 
|  | 50 | { | 
|  | 51 | u32			index; | 
|  | 52 | int			action; | 
|  | 53 | u32			limit; | 
|  | 54 | u32			burst; | 
|  | 55 | u32			mtu; | 
|  | 56 | struct tc_ratespec	rate; | 
|  | 57 | struct tc_ratespec	peakrate; | 
|  | 58 | }; | 
|  | 59 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | /* Each policer is serialized by its individual spinlock */ | 
|  | 61 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | #ifdef CONFIG_NET_CLS_ACT | 
| Jamal Hadi Salim | 83b950c | 2006-04-06 22:24:22 -0700 | [diff] [blame] | 63 | static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb, | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 64 | int type, struct tc_action *a) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 | { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 66 | struct tcf_common *p; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; | 
|  | 68 | struct rtattr *r; | 
|  | 69 |  | 
|  | 70 | read_lock(&police_lock); | 
|  | 71 |  | 
|  | 72 | s_i = cb->args[0]; | 
|  | 73 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 74 | for (i = 0; i < (POL_TAB_MASK + 1); i++) { | 
|  | 75 | p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 77 | for (; p; p = p->tcfc_next) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | index++; | 
|  | 79 | if (index < s_i) | 
|  | 80 | continue; | 
|  | 81 | a->priv = p; | 
|  | 82 | a->order = index; | 
|  | 83 | r = (struct rtattr*) skb->tail; | 
|  | 84 | RTA_PUT(skb, a->order, 0, NULL); | 
|  | 85 | if (type == RTM_DELACTION) | 
|  | 86 | err = tcf_action_dump_1(skb, a, 0, 1); | 
|  | 87 | else | 
|  | 88 | err = tcf_action_dump_1(skb, a, 0, 0); | 
|  | 89 | if (err < 0) { | 
|  | 90 | index--; | 
|  | 91 | skb_trim(skb, (u8*)r - skb->data); | 
|  | 92 | goto done; | 
|  | 93 | } | 
|  | 94 | r->rta_len = skb->tail - (u8*)r; | 
|  | 95 | n_i++; | 
|  | 96 | } | 
|  | 97 | } | 
|  | 98 | done: | 
|  | 99 | read_unlock(&police_lock); | 
|  | 100 | if (n_i) | 
|  | 101 | cb->args[0] += n_i; | 
|  | 102 | return n_i; | 
|  | 103 |  | 
|  | 104 | rtattr_failure: | 
|  | 105 | skb_trim(skb, (u8*)r - skb->data); | 
|  | 106 | goto done; | 
|  | 107 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | #endif | 
|  | 109 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 | void tcf_police_destroy(struct tcf_police *p) | 
|  | 111 | { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 112 | unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); | 
|  | 113 | struct tcf_common **p1p; | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 114 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 115 | for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) { | 
|  | 116 | if (*p1p == &p->common) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | write_lock_bh(&police_lock); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 118 | *p1p = p->tcf_next; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | write_unlock_bh(&police_lock); | 
|  | 120 | #ifdef CONFIG_NET_ESTIMATOR | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 121 | gen_kill_estimator(&p->tcf_bstats, | 
|  | 122 | &p->tcf_rate_est); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 123 | #endif | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 124 | if (p->tcfp_R_tab) | 
|  | 125 | qdisc_put_rtab(p->tcfp_R_tab); | 
|  | 126 | if (p->tcfp_P_tab) | 
|  | 127 | qdisc_put_rtab(p->tcfp_P_tab); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 128 | kfree(p); | 
|  | 129 | return; | 
|  | 130 | } | 
|  | 131 | } | 
|  | 132 | BUG_TRAP(0); | 
|  | 133 | } | 
|  | 134 |  | 
|  | 135 | #ifdef CONFIG_NET_CLS_ACT | 
|  | 136 | static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 137 | struct tc_action *a, int ovr, int bind) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 | { | 
|  | 139 | unsigned h; | 
|  | 140 | int ret = 0, err; | 
|  | 141 | struct rtattr *tb[TCA_POLICE_MAX]; | 
|  | 142 | struct tc_police *parm; | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 143 | struct tcf_police *police; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 144 | struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; | 
| Patrick McHardy | 1e9b3d5 | 2006-11-30 19:54:05 -0800 | [diff] [blame] | 145 | int size; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 146 |  | 
|  | 147 | if (rta == NULL || rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) | 
|  | 148 | return -EINVAL; | 
|  | 149 |  | 
| Patrick McHardy | 1e9b3d5 | 2006-11-30 19:54:05 -0800 | [diff] [blame] | 150 | if (tb[TCA_POLICE_TBF-1] == NULL) | 
|  | 151 | return -EINVAL; | 
|  | 152 | size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]); | 
|  | 153 | if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 154 | return -EINVAL; | 
|  | 155 | parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); | 
|  | 156 |  | 
|  | 157 | if (tb[TCA_POLICE_RESULT-1] != NULL && | 
|  | 158 | RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) | 
|  | 159 | return -EINVAL; | 
|  | 160 | if (tb[TCA_POLICE_RESULT-1] != NULL && | 
|  | 161 | RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) | 
|  | 162 | return -EINVAL; | 
|  | 163 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 164 | if (parm->index) { | 
|  | 165 | struct tcf_common *pc; | 
|  | 166 |  | 
|  | 167 | pc = tcf_hash_lookup(parm->index, &police_hash_info); | 
|  | 168 | if (pc != NULL) { | 
|  | 169 | a->priv = pc; | 
|  | 170 | police = to_police(pc); | 
|  | 171 | if (bind) { | 
|  | 172 | police->tcf_bindcnt += 1; | 
|  | 173 | police->tcf_refcnt += 1; | 
|  | 174 | } | 
|  | 175 | if (ovr) | 
|  | 176 | goto override; | 
|  | 177 | return ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 | } | 
|  | 180 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 181 | police = kzalloc(sizeof(*police), GFP_KERNEL); | 
|  | 182 | if (police == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 183 | return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | ret = ACT_P_CREATED; | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 185 | police->tcf_refcnt = 1; | 
|  | 186 | spin_lock_init(&police->tcf_lock); | 
|  | 187 | police->tcf_stats_lock = &police->tcf_lock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 188 | if (bind) | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 189 | police->tcf_bindcnt = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | override: | 
|  | 191 | if (parm->rate.rate) { | 
|  | 192 | err = -ENOMEM; | 
|  | 193 | R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); | 
|  | 194 | if (R_tab == NULL) | 
|  | 195 | goto failure; | 
|  | 196 | if (parm->peakrate.rate) { | 
|  | 197 | P_tab = qdisc_get_rtab(&parm->peakrate, | 
|  | 198 | tb[TCA_POLICE_PEAKRATE-1]); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 199 | if (P_tab == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 | qdisc_put_rtab(R_tab); | 
|  | 201 | goto failure; | 
|  | 202 | } | 
|  | 203 | } | 
|  | 204 | } | 
|  | 205 | /* No failure allowed after this point */ | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 206 | spin_lock_bh(&police->tcf_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | if (R_tab != NULL) { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 208 | qdisc_put_rtab(police->tcfp_R_tab); | 
|  | 209 | police->tcfp_R_tab = R_tab; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 | } | 
|  | 211 | if (P_tab != NULL) { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 212 | qdisc_put_rtab(police->tcfp_P_tab); | 
|  | 213 | police->tcfp_P_tab = P_tab; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | } | 
|  | 215 |  | 
|  | 216 | if (tb[TCA_POLICE_RESULT-1]) | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 217 | police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); | 
|  | 218 | police->tcfp_toks = police->tcfp_burst = parm->burst; | 
|  | 219 | police->tcfp_mtu = parm->mtu; | 
|  | 220 | if (police->tcfp_mtu == 0) { | 
|  | 221 | police->tcfp_mtu = ~0; | 
|  | 222 | if (police->tcfp_R_tab) | 
|  | 223 | police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | } | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 225 | if (police->tcfp_P_tab) | 
|  | 226 | police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); | 
|  | 227 | police->tcf_action = parm->action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 228 |  | 
|  | 229 | #ifdef CONFIG_NET_ESTIMATOR | 
|  | 230 | if (tb[TCA_POLICE_AVRATE-1]) | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 231 | police->tcfp_ewma_rate = | 
|  | 232 | *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 233 | if (est) | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 234 | gen_replace_estimator(&police->tcf_bstats, | 
|  | 235 | &police->tcf_rate_est, | 
|  | 236 | police->tcf_stats_lock, est); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 237 | #endif | 
|  | 238 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 239 | spin_unlock_bh(&police->tcf_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 240 | if (ret != ACT_P_CREATED) | 
|  | 241 | return ret; | 
|  | 242 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 243 | PSCHED_GET_TIME(police->tcfp_t_c); | 
|  | 244 | police->tcf_index = parm->index ? parm->index : | 
|  | 245 | tcf_hash_new_index(&police_idx_gen, &police_hash_info); | 
|  | 246 | h = tcf_hash(police->tcf_index, POL_TAB_MASK); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 247 | write_lock_bh(&police_lock); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 248 | police->tcf_next = tcf_police_ht[h]; | 
|  | 249 | tcf_police_ht[h] = &police->common; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 | write_unlock_bh(&police_lock); | 
|  | 251 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 252 | a->priv = police; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | return ret; | 
|  | 254 |  | 
|  | 255 | failure: | 
|  | 256 | if (ret == ACT_P_CREATED) | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 257 | kfree(police); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 258 | return err; | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | static int tcf_act_police_cleanup(struct tc_action *a, int bind) | 
|  | 262 | { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 263 | struct tcf_police *p = a->priv; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 264 |  | 
|  | 265 | if (p != NULL) | 
|  | 266 | return tcf_police_release(p, bind); | 
|  | 267 | return 0; | 
|  | 268 | } | 
|  | 269 |  | 
| Patrick McHardy | f43c5a0 | 2006-01-08 22:15:34 -0800 | [diff] [blame] | 270 | static int tcf_act_police(struct sk_buff *skb, struct tc_action *a, | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 271 | struct tcf_result *res) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 272 | { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 273 | struct tcf_police *police = a->priv; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | psched_time_t now; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 275 | long toks; | 
|  | 276 | long ptoks = 0; | 
|  | 277 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 278 | spin_lock(&police->tcf_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 279 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 280 | police->tcf_bstats.bytes += skb->len; | 
|  | 281 | police->tcf_bstats.packets++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 282 |  | 
|  | 283 | #ifdef CONFIG_NET_ESTIMATOR | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 284 | if (police->tcfp_ewma_rate && | 
|  | 285 | police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { | 
|  | 286 | police->tcf_qstats.overlimits++; | 
|  | 287 | spin_unlock(&police->tcf_lock); | 
|  | 288 | return police->tcf_action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | } | 
|  | 290 | #endif | 
|  | 291 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 292 | if (skb->len <= police->tcfp_mtu) { | 
|  | 293 | if (police->tcfp_R_tab == NULL) { | 
|  | 294 | spin_unlock(&police->tcf_lock); | 
|  | 295 | return police->tcfp_result; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | } | 
|  | 297 |  | 
|  | 298 | PSCHED_GET_TIME(now); | 
|  | 299 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 300 | toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, | 
|  | 301 | police->tcfp_burst); | 
|  | 302 | if (police->tcfp_P_tab) { | 
|  | 303 | ptoks = toks + police->tcfp_ptoks; | 
|  | 304 | if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) | 
|  | 305 | ptoks = (long)L2T_P(police, police->tcfp_mtu); | 
|  | 306 | ptoks -= L2T_P(police, skb->len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 307 | } | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 308 | toks += police->tcfp_toks; | 
|  | 309 | if (toks > (long)police->tcfp_burst) | 
|  | 310 | toks = police->tcfp_burst; | 
|  | 311 | toks -= L2T(police, skb->len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | if ((toks|ptoks) >= 0) { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 313 | police->tcfp_t_c = now; | 
|  | 314 | police->tcfp_toks = toks; | 
|  | 315 | police->tcfp_ptoks = ptoks; | 
|  | 316 | spin_unlock(&police->tcf_lock); | 
|  | 317 | return police->tcfp_result; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 318 | } | 
|  | 319 | } | 
|  | 320 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 321 | police->tcf_qstats.overlimits++; | 
|  | 322 | spin_unlock(&police->tcf_lock); | 
|  | 323 | return police->tcf_action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 | } | 
|  | 325 |  | 
|  | 326 | static int | 
|  | 327 | tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) | 
|  | 328 | { | 
|  | 329 | unsigned char	 *b = skb->tail; | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 330 | struct tcf_police *police = a->priv; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 331 | struct tc_police opt; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 333 | opt.index = police->tcf_index; | 
|  | 334 | opt.action = police->tcf_action; | 
|  | 335 | opt.mtu = police->tcfp_mtu; | 
|  | 336 | opt.burst = police->tcfp_burst; | 
|  | 337 | opt.refcnt = police->tcf_refcnt - ref; | 
|  | 338 | opt.bindcnt = police->tcf_bindcnt - bind; | 
|  | 339 | if (police->tcfp_R_tab) | 
|  | 340 | opt.rate = police->tcfp_R_tab->rate; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 341 | else | 
|  | 342 | memset(&opt.rate, 0, sizeof(opt.rate)); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 343 | if (police->tcfp_P_tab) | 
|  | 344 | opt.peakrate = police->tcfp_P_tab->rate; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 345 | else | 
|  | 346 | memset(&opt.peakrate, 0, sizeof(opt.peakrate)); | 
|  | 347 | RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 348 | if (police->tcfp_result) | 
|  | 349 | RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), | 
|  | 350 | &police->tcfp_result); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 351 | #ifdef CONFIG_NET_ESTIMATOR | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 352 | if (police->tcfp_ewma_rate) | 
|  | 353 | RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 354 | #endif | 
|  | 355 | return skb->len; | 
|  | 356 |  | 
|  | 357 | rtattr_failure: | 
|  | 358 | skb_trim(skb, b - skb->data); | 
|  | 359 | return -1; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | MODULE_AUTHOR("Alexey Kuznetsov"); | 
|  | 363 | MODULE_DESCRIPTION("Policing actions"); | 
|  | 364 | MODULE_LICENSE("GPL"); | 
|  | 365 |  | 
|  | 366 | static struct tc_action_ops act_police_ops = { | 
|  | 367 | .kind		=	"police", | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 368 | .hinfo		=	&police_hash_info, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 369 | .type		=	TCA_ID_POLICE, | 
|  | 370 | .capab		=	TCA_CAP_NONE, | 
|  | 371 | .owner		=	THIS_MODULE, | 
|  | 372 | .act		=	tcf_act_police, | 
|  | 373 | .dump		=	tcf_act_police_dump, | 
|  | 374 | .cleanup	=	tcf_act_police_cleanup, | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 375 | .lookup		=	tcf_hash_search, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 376 | .init		=	tcf_act_police_locate, | 
| Jamal Hadi Salim | 83b950c | 2006-04-06 22:24:22 -0700 | [diff] [blame] | 377 | .walk		=	tcf_act_police_walker | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 378 | }; | 
|  | 379 |  | 
|  | 380 | static int __init | 
|  | 381 | police_init_module(void) | 
|  | 382 | { | 
|  | 383 | return tcf_register_action(&act_police_ops); | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 | static void __exit | 
|  | 387 | police_cleanup_module(void) | 
|  | 388 | { | 
|  | 389 | tcf_unregister_action(&act_police_ops); | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | module_init(police_init_module); | 
|  | 393 | module_exit(police_cleanup_module); | 
|  | 394 |  | 
| Patrick McHardy | 31bd06e | 2006-01-08 22:16:25 -0800 | [diff] [blame] | 395 | #else /* CONFIG_NET_CLS_ACT */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 396 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 397 | static struct tcf_common *tcf_police_lookup(u32 index) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 398 | { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 399 | struct tcf_hashinfo *hinfo = &police_hash_info; | 
|  | 400 | struct tcf_common *p; | 
|  | 401 |  | 
|  | 402 | read_lock(hinfo->lock); | 
|  | 403 | for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p; | 
|  | 404 | p = p->tcfc_next) { | 
|  | 405 | if (p->tcfc_index == index) | 
|  | 406 | break; | 
|  | 407 | } | 
|  | 408 | read_unlock(hinfo->lock); | 
|  | 409 |  | 
|  | 410 | return p; | 
|  | 411 | } | 
|  | 412 |  | 
|  | 413 | static u32 tcf_police_new_index(void) | 
|  | 414 | { | 
|  | 415 | u32 *idx_gen = &police_idx_gen; | 
|  | 416 | u32 val = *idx_gen; | 
|  | 417 |  | 
|  | 418 | do { | 
|  | 419 | if (++val == 0) | 
|  | 420 | val = 1; | 
|  | 421 | } while (tcf_police_lookup(val)); | 
|  | 422 |  | 
|  | 423 | return (*idx_gen = val); | 
|  | 424 | } | 
|  | 425 |  | 
|  | 426 | struct tcf_police *tcf_police_locate(struct rtattr *rta, struct rtattr *est) | 
|  | 427 | { | 
|  | 428 | unsigned int h; | 
|  | 429 | struct tcf_police *police; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 430 | struct rtattr *tb[TCA_POLICE_MAX]; | 
|  | 431 | struct tc_police *parm; | 
| Patrick McHardy | 1e9b3d5 | 2006-11-30 19:54:05 -0800 | [diff] [blame] | 432 | int size; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 433 |  | 
|  | 434 | if (rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) | 
|  | 435 | return NULL; | 
|  | 436 |  | 
| Patrick McHardy | 1e9b3d5 | 2006-11-30 19:54:05 -0800 | [diff] [blame] | 437 | if (tb[TCA_POLICE_TBF-1] == NULL) | 
|  | 438 | return NULL; | 
|  | 439 | size = RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]); | 
|  | 440 | if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 441 | return NULL; | 
|  | 442 |  | 
|  | 443 | parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); | 
|  | 444 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 445 | if (parm->index) { | 
|  | 446 | struct tcf_common *pc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 447 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 448 | pc = tcf_police_lookup(parm->index); | 
|  | 449 | if (pc) { | 
|  | 450 | police = to_police(pc); | 
|  | 451 | police->tcf_refcnt++; | 
|  | 452 | return police; | 
|  | 453 | } | 
|  | 454 | } | 
|  | 455 | police = kzalloc(sizeof(*police), GFP_KERNEL); | 
|  | 456 | if (unlikely(!police)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 457 | return NULL; | 
|  | 458 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 459 | police->tcf_refcnt = 1; | 
|  | 460 | spin_lock_init(&police->tcf_lock); | 
|  | 461 | police->tcf_stats_lock = &police->tcf_lock; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 462 | if (parm->rate.rate) { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 463 | police->tcfp_R_tab = | 
|  | 464 | qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); | 
|  | 465 | if (police->tcfp_R_tab == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 466 | goto failure; | 
|  | 467 | if (parm->peakrate.rate) { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 468 | police->tcfp_P_tab = | 
|  | 469 | qdisc_get_rtab(&parm->peakrate, | 
|  | 470 | tb[TCA_POLICE_PEAKRATE-1]); | 
|  | 471 | if (police->tcfp_P_tab == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 472 | goto failure; | 
|  | 473 | } | 
|  | 474 | } | 
|  | 475 | if (tb[TCA_POLICE_RESULT-1]) { | 
|  | 476 | if (RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) | 
|  | 477 | goto failure; | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 478 | police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 479 | } | 
|  | 480 | #ifdef CONFIG_NET_ESTIMATOR | 
|  | 481 | if (tb[TCA_POLICE_AVRATE-1]) { | 
|  | 482 | if (RTA_PAYLOAD(tb[TCA_POLICE_AVRATE-1]) != sizeof(u32)) | 
|  | 483 | goto failure; | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 484 | police->tcfp_ewma_rate = | 
|  | 485 | *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 486 | } | 
|  | 487 | #endif | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 488 | police->tcfp_toks = police->tcfp_burst = parm->burst; | 
|  | 489 | police->tcfp_mtu = parm->mtu; | 
|  | 490 | if (police->tcfp_mtu == 0) { | 
|  | 491 | police->tcfp_mtu = ~0; | 
|  | 492 | if (police->tcfp_R_tab) | 
|  | 493 | police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 494 | } | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 495 | if (police->tcfp_P_tab) | 
|  | 496 | police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); | 
|  | 497 | PSCHED_GET_TIME(police->tcfp_t_c); | 
|  | 498 | police->tcf_index = parm->index ? parm->index : | 
|  | 499 | tcf_police_new_index(); | 
|  | 500 | police->tcf_action = parm->action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 501 | #ifdef CONFIG_NET_ESTIMATOR | 
|  | 502 | if (est) | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 503 | gen_new_estimator(&police->tcf_bstats, &police->tcf_rate_est, | 
|  | 504 | police->tcf_stats_lock, est); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 505 | #endif | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 506 | h = tcf_hash(police->tcf_index, POL_TAB_MASK); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 507 | write_lock_bh(&police_lock); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 508 | police->tcf_next = tcf_police_ht[h]; | 
|  | 509 | tcf_police_ht[h] = &police->common; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 | write_unlock_bh(&police_lock); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 511 | return police; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 512 |  | 
|  | 513 | failure: | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 514 | if (police->tcfp_R_tab) | 
|  | 515 | qdisc_put_rtab(police->tcfp_R_tab); | 
|  | 516 | kfree(police); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 517 | return NULL; | 
|  | 518 | } | 
|  | 519 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 520 | int tcf_police(struct sk_buff *skb, struct tcf_police *police) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 521 | { | 
|  | 522 | psched_time_t now; | 
|  | 523 | long toks; | 
|  | 524 | long ptoks = 0; | 
|  | 525 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 526 | spin_lock(&police->tcf_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 527 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 528 | police->tcf_bstats.bytes += skb->len; | 
|  | 529 | police->tcf_bstats.packets++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 530 |  | 
|  | 531 | #ifdef CONFIG_NET_ESTIMATOR | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 532 | if (police->tcfp_ewma_rate && | 
|  | 533 | police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { | 
|  | 534 | police->tcf_qstats.overlimits++; | 
|  | 535 | spin_unlock(&police->tcf_lock); | 
|  | 536 | return police->tcf_action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 537 | } | 
|  | 538 | #endif | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 539 | if (skb->len <= police->tcfp_mtu) { | 
|  | 540 | if (police->tcfp_R_tab == NULL) { | 
|  | 541 | spin_unlock(&police->tcf_lock); | 
|  | 542 | return police->tcfp_result; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 543 | } | 
|  | 544 |  | 
|  | 545 | PSCHED_GET_TIME(now); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 546 | toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, | 
|  | 547 | police->tcfp_burst); | 
|  | 548 | if (police->tcfp_P_tab) { | 
|  | 549 | ptoks = toks + police->tcfp_ptoks; | 
|  | 550 | if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) | 
|  | 551 | ptoks = (long)L2T_P(police, police->tcfp_mtu); | 
|  | 552 | ptoks -= L2T_P(police, skb->len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 553 | } | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 554 | toks += police->tcfp_toks; | 
|  | 555 | if (toks > (long)police->tcfp_burst) | 
|  | 556 | toks = police->tcfp_burst; | 
|  | 557 | toks -= L2T(police, skb->len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 558 | if ((toks|ptoks) >= 0) { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 559 | police->tcfp_t_c = now; | 
|  | 560 | police->tcfp_toks = toks; | 
|  | 561 | police->tcfp_ptoks = ptoks; | 
|  | 562 | spin_unlock(&police->tcf_lock); | 
|  | 563 | return police->tcfp_result; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 564 | } | 
|  | 565 | } | 
|  | 566 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 567 | police->tcf_qstats.overlimits++; | 
|  | 568 | spin_unlock(&police->tcf_lock); | 
|  | 569 | return police->tcf_action; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 570 | } | 
| Patrick McHardy | 31bd06e | 2006-01-08 22:16:25 -0800 | [diff] [blame] | 571 | EXPORT_SYMBOL(tcf_police); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 572 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 573 | int tcf_police_dump(struct sk_buff *skb, struct tcf_police *police) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 574 | { | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 575 | unsigned char *b = skb->tail; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 576 | struct tc_police opt; | 
|  | 577 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 578 | opt.index = police->tcf_index; | 
|  | 579 | opt.action = police->tcf_action; | 
|  | 580 | opt.mtu = police->tcfp_mtu; | 
|  | 581 | opt.burst = police->tcfp_burst; | 
|  | 582 | if (police->tcfp_R_tab) | 
|  | 583 | opt.rate = police->tcfp_R_tab->rate; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 584 | else | 
|  | 585 | memset(&opt.rate, 0, sizeof(opt.rate)); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 586 | if (police->tcfp_P_tab) | 
|  | 587 | opt.peakrate = police->tcfp_P_tab->rate; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 588 | else | 
|  | 589 | memset(&opt.peakrate, 0, sizeof(opt.peakrate)); | 
|  | 590 | RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 591 | if (police->tcfp_result) | 
|  | 592 | RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), | 
|  | 593 | &police->tcfp_result); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 594 | #ifdef CONFIG_NET_ESTIMATOR | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 595 | if (police->tcfp_ewma_rate) | 
|  | 596 | RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 597 | #endif | 
|  | 598 | return skb->len; | 
|  | 599 |  | 
|  | 600 | rtattr_failure: | 
|  | 601 | skb_trim(skb, b - skb->data); | 
|  | 602 | return -1; | 
|  | 603 | } | 
|  | 604 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 605 | int tcf_police_dump_stats(struct sk_buff *skb, struct tcf_police *police) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | { | 
|  | 607 | struct gnet_dump d; | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 608 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 609 | if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 610 | TCA_XSTATS, police->tcf_stats_lock, | 
|  | 611 | &d) < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 612 | goto errout; | 
| YOSHIFUJI Hideaki | 10297b9 | 2007-02-09 23:25:16 +0900 | [diff] [blame] | 613 |  | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 614 | if (gnet_stats_copy_basic(&d, &police->tcf_bstats) < 0 || | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 615 | #ifdef CONFIG_NET_ESTIMATOR | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 616 | gnet_stats_copy_rate_est(&d, &police->tcf_rate_est) < 0 || | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 617 | #endif | 
| David S. Miller | e9ce1cd | 2006-08-21 23:54:55 -0700 | [diff] [blame] | 618 | gnet_stats_copy_queue(&d, &police->tcf_qstats) < 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 619 | goto errout; | 
|  | 620 |  | 
|  | 621 | if (gnet_stats_finish_copy(&d) < 0) | 
|  | 622 | goto errout; | 
|  | 623 |  | 
|  | 624 | return 0; | 
|  | 625 |  | 
|  | 626 | errout: | 
|  | 627 | return -1; | 
|  | 628 | } | 
|  | 629 |  | 
| Patrick McHardy | 31bd06e | 2006-01-08 22:16:25 -0800 | [diff] [blame] | 630 | #endif /* CONFIG_NET_CLS_ACT */ |