Blame view

net/sched/cls_u32.c 16.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
   * net/sched/cls_u32.c	Ugly (or Universal) 32bit key Packet Classifier.
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   *
   *	The filters are packed to hash tables of key nodes
   *	with a set of 32bit key/mask pairs at every node.
   *	Nodes reference next level hash tables etc.
   *
   *	This scheme is the best universal classifier I managed to
   *	invent; it is not super-fast, but it is not slow (provided you
   *	program it correctly), and general enough.  And its relative
   *	speed grows as the number of rules becomes larger.
   *
   *	It seems that it represents the best middle point between
   *	speed and manageability both by human and by machine.
   *
   *	It is especially useful for link sharing combined with QoS;
   *	pure RSVP doesn't need such a general approach and can use
   *	much simpler (and faster) schemes, sort of cls_rsvp.c.
   *
   *	JHS: We should remove the CONFIG_NET_CLS_IND from here
   *	eventually when the meta match extension is made available
   *
   *	nfmark match added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
33
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  #include <linux/rtnetlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  #include <linux/skbuff.h>
0ba480538   Patrick McHardy   [NET_SCHED]: Remo...
40
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
  #include <net/act_api.h>
  #include <net/pkt_cls.h>
cc7ec456f   Eric Dumazet   net_sched: cleanups
43
  struct tc_u_knode {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  	struct tc_u_knode	*next;
  	u32			handle;
  	struct tc_u_hnode	*ht_up;
  	struct tcf_exts		exts;
  #ifdef CONFIG_NET_CLS_IND
  	char                     indev[IFNAMSIZ];
  #endif
  	u8			fshift;
  	struct tcf_result	res;
  	struct tc_u_hnode	*ht_down;
  #ifdef CONFIG_CLS_U32_PERF
  	struct tc_u32_pcnt	*pf;
  #endif
  #ifdef CONFIG_CLS_U32_MARK
  	struct tc_u32_mark	mark;
  #endif
  	struct tc_u32_sel	sel;
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
62
  struct tc_u_hnode {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
  	struct tc_u_hnode	*next;
  	u32			handle;
  	u32			prio;
  	struct tc_u_common	*tp_c;
  	int			refcnt;
cc7ec456f   Eric Dumazet   net_sched: cleanups
68
  	unsigned int		divisor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
  	struct tc_u_knode	*ht[1];
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
71
  struct tc_u_common {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
  	struct tc_u_hnode	*hlist;
  	struct Qdisc		*q;
  	int			refcnt;
  	u32			hgenerator;
  };
5239008b0   Patrick McHardy   [NET_SCHED]: Cons...
77
  static const struct tcf_ext_map u32_ext_map = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
  	.action = TCA_U32_ACT,
  	.police = TCA_U32_POLICE
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
81
82
83
  static inline unsigned int u32_hash_fold(__be32 key,
  					 const struct tc_u32_sel *sel,
  					 u8 fshift)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
85
  	unsigned int h = ntohl(key & sel->hmask) >> fshift;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
88
  
  	return h;
  }
dc7f9f6e8   Eric Dumazet   net: sched: const...
89
  static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
  {
  	struct {
  		struct tc_u_knode *knode;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
93
  		unsigned int	  off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  	} stack[TC_U32_MAXDEPTH];
cc7ec456f   Eric Dumazet   net_sched: cleanups
95
  	struct tc_u_hnode *ht = (struct tc_u_hnode *)tp->root;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
96
  	unsigned int off = skb_network_offset(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  	struct tc_u_knode *n;
  	int sdepth = 0;
  	int off2 = 0;
  	int sel = 0;
  #ifdef CONFIG_CLS_U32_PERF
  	int j;
  #endif
  	int i, r;
  
  next_ht:
  	n = ht->ht[sel];
  
  next_knode:
  	if (n) {
  		struct tc_u32_key *key = n->sel.keys;
  
  #ifdef CONFIG_CLS_U32_PERF
cc7ec456f   Eric Dumazet   net_sched: cleanups
114
  		n->pf->rcnt += 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
  		j = 0;
  #endif
  
  #ifdef CONFIG_CLS_U32_MARK
82e91ffef   Thomas Graf   [NET]: Turn nfmar...
119
  		if ((skb->mark & n->mark.mask) != n->mark.val) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
  			n = n->next;
  			goto next_knode;
  		} else {
  			n->mark.success++;
  		}
  #endif
cc7ec456f   Eric Dumazet   net_sched: cleanups
126
  		for (i = n->sel.nkeys; i > 0; i--, key++) {
66d50d255   stephen hemminger   u32: negative off...
127
  			int toff = off + key->off + (off2 & key->offmask);
86fce3ba1   stephen hemminger   cls_u32: fix spar...
128
  			__be32 *data, hdata;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
129

4e18b3edf   Dan Carpenter   cls_u32: signedne...
130
  			if (skb_headroom(skb) + toff > INT_MAX)
66d50d255   stephen hemminger   u32: negative off...
131
  				goto out;
86fce3ba1   stephen hemminger   cls_u32: fix spar...
132
  			data = skb_header_pointer(skb, toff, 4, &hdata);
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
133
134
135
  			if (!data)
  				goto out;
  			if ((*data ^ key->val) & key->mask) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
  				n = n->next;
  				goto next_knode;
  			}
  #ifdef CONFIG_CLS_U32_PERF
cc7ec456f   Eric Dumazet   net_sched: cleanups
140
  			n->pf->kcnts[j] += 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
  			j++;
  #endif
  		}
  		if (n->ht_down == NULL) {
  check_terminal:
cc7ec456f   Eric Dumazet   net_sched: cleanups
146
  			if (n->sel.flags & TC_U32_TERMINAL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
154
155
  
  				*res = n->res;
  #ifdef CONFIG_NET_CLS_IND
  				if (!tcf_match_indev(skb, n->indev)) {
  					n = n->next;
  					goto next_knode;
  				}
  #endif
  #ifdef CONFIG_CLS_U32_PERF
cc7ec456f   Eric Dumazet   net_sched: cleanups
156
  				n->pf->rhit += 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  #endif
  				r = tcf_exts_exec(skb, &n->exts, res);
  				if (r < 0) {
  					n = n->next;
  					goto next_knode;
  				}
  
  				return r;
  			}
  			n = n->next;
  			goto next_knode;
  		}
  
  		/* PUSH */
  		if (sdepth >= TC_U32_MAXDEPTH)
  			goto deadloop;
  		stack[sdepth].knode = n;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
174
  		stack[sdepth].off = off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
  		sdepth++;
  
  		ht = n->ht_down;
  		sel = 0;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
179
  		if (ht->divisor) {
86fce3ba1   stephen hemminger   cls_u32: fix spar...
180
  			__be32 *data, hdata;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
181
182
  
  			data = skb_header_pointer(skb, off + n->sel.hoff, 4,
86fce3ba1   stephen hemminger   cls_u32: fix spar...
183
  						  &hdata);
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
184
185
186
187
188
  			if (!data)
  				goto out;
  			sel = ht->divisor & u32_hash_fold(*data, &n->sel,
  							  n->fshift);
  		}
cc7ec456f   Eric Dumazet   net_sched: cleanups
189
  		if (!(n->sel.flags & (TC_U32_VAROFFSET | TC_U32_OFFSET | TC_U32_EAT)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  			goto next_ht;
cc7ec456f   Eric Dumazet   net_sched: cleanups
191
  		if (n->sel.flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
  			off2 = n->sel.off + 3;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
193
  			if (n->sel.flags & TC_U32_VAROFFSET) {
86fce3ba1   stephen hemminger   cls_u32: fix spar...
194
  				__be16 *data, hdata;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
195
196
197
  
  				data = skb_header_pointer(skb,
  							  off + n->sel.offoff,
86fce3ba1   stephen hemminger   cls_u32: fix spar...
198
  							  2, &hdata);
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
199
200
201
202
203
  				if (!data)
  					goto out;
  				off2 += ntohs(n->sel.offmask & *data) >>
  					n->sel.offshift;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
  			off2 &= ~3;
  		}
cc7ec456f   Eric Dumazet   net_sched: cleanups
206
  		if (n->sel.flags & TC_U32_EAT) {
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
207
  			off += off2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
  			off2 = 0;
  		}
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
210
  		if (off < skb->len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
213
214
215
216
217
  			goto next_ht;
  	}
  
  	/* POP */
  	if (sdepth--) {
  		n = stack[sdepth].knode;
  		ht = n->ht_up;
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
218
  		off = stack[sdepth].off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
  		goto check_terminal;
  	}
fbc2e7d9c   Changli Gao   cls_u32: use skb_...
221
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
  	return -1;
  
  deadloop:
  	if (net_ratelimit())
cc7ec456f   Eric Dumazet   net_sched: cleanups
226
227
  		pr_warning("cls_u32: dead loop
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
229
  	return -1;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
230
  static struct tc_u_hnode *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
235
236
237
238
239
240
  u32_lookup_ht(struct tc_u_common *tp_c, u32 handle)
  {
  	struct tc_u_hnode *ht;
  
  	for (ht = tp_c->hlist; ht; ht = ht->next)
  		if (ht->handle == handle)
  			break;
  
  	return ht;
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
241
  static struct tc_u_knode *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
  u32_lookup_key(struct tc_u_hnode *ht, u32 handle)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
244
  	unsigned int sel;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  	struct tc_u_knode *n = NULL;
  
  	sel = TC_U32_HASH(handle);
  	if (sel > ht->divisor)
  		goto out;
  
  	for (n = ht->ht[sel]; n; n = n->next)
  		if (n->handle == handle)
  			break;
  out:
  	return n;
  }
  
  
  static unsigned long u32_get(struct tcf_proto *tp, u32 handle)
  {
  	struct tc_u_hnode *ht;
  	struct tc_u_common *tp_c = tp->data;
  
  	if (TC_U32_HTID(handle) == TC_U32_ROOT)
  		ht = tp->root;
  	else
  		ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle));
  
  	if (!ht)
  		return 0;
  
  	if (TC_U32_KEY(handle) == 0)
  		return (unsigned long)ht;
  
  	return (unsigned long)u32_lookup_key(ht, handle);
  }
  
  static void u32_put(struct tcf_proto *tp, unsigned long f)
  {
  }
  
  static u32 gen_new_htid(struct tc_u_common *tp_c)
  {
  	int i = 0x800;
  
  	do {
  		if (++tp_c->hgenerator == 0x7FF)
  			tp_c->hgenerator = 1;
cc7ec456f   Eric Dumazet   net_sched: cleanups
289
  	} while (--i > 0 && u32_lookup_ht(tp_c, (tp_c->hgenerator|0x800)<<20));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
295
296
297
  
  	return i > 0 ? (tp_c->hgenerator|0x800)<<20 : 0;
  }
  
  static int u32_init(struct tcf_proto *tp)
  {
  	struct tc_u_hnode *root_ht;
  	struct tc_u_common *tp_c;
72b25a913   David S. Miller   pkt_sched: Get ri...
298
  	tp_c = tp->q->u32_node;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299

0da974f4f   Panagiotis Issaris   [NET]: Conversion...
300
  	root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
  	if (root_ht == NULL)
  		return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
  	root_ht->divisor = 0;
  	root_ht->refcnt++;
  	root_ht->handle = tp_c ? gen_new_htid(tp_c) : 0x80000000;
  	root_ht->prio = tp->prio;
  
  	if (tp_c == NULL) {
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
309
  		tp_c = kzalloc(sizeof(*tp_c), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
313
  		if (tp_c == NULL) {
  			kfree(root_ht);
  			return -ENOBUFS;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
  		tp_c->q = tp->q;
72b25a913   David S. Miller   pkt_sched: Get ri...
315
  		tp->q->u32_node = tp_c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
  	}
  
  	tp_c->refcnt++;
  	root_ht->next = tp_c->hlist;
  	tp_c->hlist = root_ht;
  	root_ht->tp_c = tp_c;
  
  	tp->root = root_ht;
  	tp->data = tp_c;
  	return 0;
  }
  
  static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n)
  {
  	tcf_unbind_filter(tp, &n->res);
  	tcf_exts_destroy(tp, &n->exts);
  	if (n->ht_down)
  		n->ht_down->refcnt--;
  #ifdef CONFIG_CLS_U32_PERF
1ae39a430   Patrick McHardy   [NET_SCHED]: cls_...
335
  	kfree(n->pf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  #endif
  	kfree(n);
  	return 0;
  }
  
  static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key)
  {
  	struct tc_u_knode **kp;
  	struct tc_u_hnode *ht = key->ht_up;
  
  	if (ht) {
  		for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) {
  			if (*kp == key) {
  				tcf_tree_lock(tp);
  				*kp = key->next;
  				tcf_tree_unlock(tp);
  
  				u32_destroy_key(tp, key);
  				return 0;
  			}
  		}
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
358
  	WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
364
  	return 0;
  }
  
  static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
  {
  	struct tc_u_knode *n;
cc7ec456f   Eric Dumazet   net_sched: cleanups
365
  	unsigned int h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366

cc7ec456f   Eric Dumazet   net_sched: cleanups
367
  	for (h = 0; h <= ht->divisor; h++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
370
371
372
373
374
375
376
377
378
379
  		while ((n = ht->ht[h]) != NULL) {
  			ht->ht[h] = n->next;
  
  			u32_destroy_key(tp, n);
  		}
  	}
  }
  
  static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
  {
  	struct tc_u_common *tp_c = tp->data;
  	struct tc_u_hnode **hn;
547b792ca   Ilpo Järvinen   net: convert BUG_...
380
  	WARN_ON(ht->refcnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
383
384
385
386
387
388
389
390
  
  	u32_clear_hnode(tp, ht);
  
  	for (hn = &tp_c->hlist; *hn; hn = &(*hn)->next) {
  		if (*hn == ht) {
  			*hn = ht->next;
  			kfree(ht);
  			return 0;
  		}
  	}
547b792ca   Ilpo Järvinen   net: convert BUG_...
391
  	WARN_ON(1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
397
  	return -ENOENT;
  }
  
  static void u32_destroy(struct tcf_proto *tp)
  {
  	struct tc_u_common *tp_c = tp->data;
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
398
  	struct tc_u_hnode *root_ht = tp->root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399

547b792ca   Ilpo Järvinen   net: convert BUG_...
400
  	WARN_ON(root_ht == NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
405
406
  
  	if (root_ht && --root_ht->refcnt == 0)
  		u32_destroy_hnode(tp, root_ht);
  
  	if (--tp_c->refcnt == 0) {
  		struct tc_u_hnode *ht;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407

72b25a913   David S. Miller   pkt_sched: Get ri...
408
  		tp->q->u32_node = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409

e56cfad13   Jarek Poplawski   [NET_SCHED] cls_u...
410
411
  		for (ht = tp_c->hlist; ht; ht = ht->next) {
  			ht->refcnt--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  			u32_clear_hnode(tp, ht);
e56cfad13   Jarek Poplawski   [NET_SCHED] cls_u...
413
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
  
  		while ((ht = tp_c->hlist) != NULL) {
  			tp_c->hlist = ht->next;
547b792ca   Ilpo Järvinen   net: convert BUG_...
417
  			WARN_ON(ht->refcnt != 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
  
  			kfree(ht);
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
420
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
425
426
427
428
429
  
  		kfree(tp_c);
  	}
  
  	tp->data = NULL;
  }
  
  static int u32_delete(struct tcf_proto *tp, unsigned long arg)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
430
  	struct tc_u_hnode *ht = (struct tc_u_hnode *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
432
433
434
435
  
  	if (ht == NULL)
  		return 0;
  
  	if (TC_U32_KEY(ht->handle))
cc7ec456f   Eric Dumazet   net_sched: cleanups
436
  		return u32_delete_key(tp, (struct tc_u_knode *)ht);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
  
  	if (tp->root == ht)
  		return -EINVAL;
e56cfad13   Jarek Poplawski   [NET_SCHED] cls_u...
440
441
  	if (ht->refcnt == 1) {
  		ht->refcnt--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  		u32_destroy_hnode(tp, ht);
e56cfad13   Jarek Poplawski   [NET_SCHED] cls_u...
443
444
445
  	} else {
  		return -EBUSY;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
449
450
451
452
  
  	return 0;
  }
  
  static u32 gen_new_kid(struct tc_u_hnode *ht, u32 handle)
  {
  	struct tc_u_knode *n;
cc7ec456f   Eric Dumazet   net_sched: cleanups
453
  	unsigned int i = 0x7FF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454

cc7ec456f   Eric Dumazet   net_sched: cleanups
455
  	for (n = ht->ht[TC_U32_HASH(handle)]; n; n = n->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
457
458
  		if (i < TC_U32_NODE(n->handle))
  			i = TC_U32_NODE(n->handle);
  	i++;
cc7ec456f   Eric Dumazet   net_sched: cleanups
459
  	return handle | (i > 0xFFF ? 0xFFF : i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
  }
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
461
462
463
464
465
466
467
468
469
  static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
  	[TCA_U32_CLASSID]	= { .type = NLA_U32 },
  	[TCA_U32_HASH]		= { .type = NLA_U32 },
  	[TCA_U32_LINK]		= { .type = NLA_U32 },
  	[TCA_U32_DIVISOR]	= { .type = NLA_U32 },
  	[TCA_U32_SEL]		= { .len = sizeof(struct tc_u32_sel) },
  	[TCA_U32_INDEV]		= { .type = NLA_STRING, .len = IFNAMSIZ },
  	[TCA_U32_MARK]		= { .len = sizeof(struct tc_u32_mark) },
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
  static int u32_set_parms(struct tcf_proto *tp, unsigned long base,
  			 struct tc_u_hnode *ht,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
472
473
  			 struct tc_u_knode *n, struct nlattr **tb,
  			 struct nlattr *est)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
475
476
477
478
479
480
481
482
  {
  	int err;
  	struct tcf_exts e;
  
  	err = tcf_exts_validate(tp, tb, est, &e, &u32_ext_map);
  	if (err < 0)
  		return err;
  
  	err = -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
483
  	if (tb[TCA_U32_LINK]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
484
  		u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
485
  		struct tc_u_hnode *ht_down = NULL, *ht_old;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
488
489
490
491
492
493
494
495
496
497
498
  
  		if (TC_U32_KEY(handle))
  			goto errout;
  
  		if (handle) {
  			ht_down = u32_lookup_ht(ht->tp_c, handle);
  
  			if (ht_down == NULL)
  				goto errout;
  			ht_down->refcnt++;
  		}
  
  		tcf_tree_lock(tp);
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
499
500
  		ht_old = n->ht_down;
  		n->ht_down = ht_down;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  		tcf_tree_unlock(tp);
47a1a1d4b   Patrick McHardy   pkt_sched: remove...
502
503
  		if (ht_old)
  			ht_old->refcnt--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
505
  	if (tb[TCA_U32_CLASSID]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
506
  		n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
509
510
  		tcf_bind_filter(tp, &n->res, base);
  	}
  
  #ifdef CONFIG_NET_CLS_IND
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
511
512
  	if (tb[TCA_U32_INDEV]) {
  		err = tcf_change_indev(tp, n->indev, tb[TCA_U32_INDEV]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
518
519
520
521
522
523
524
525
  		if (err < 0)
  			goto errout;
  	}
  #endif
  	tcf_exts_change(tp, &n->exts, &e);
  
  	return 0;
  errout:
  	tcf_exts_destroy(tp, &e);
  	return err;
  }
  
  static int u32_change(struct tcf_proto *tp, unsigned long base, u32 handle,
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
526
  		      struct nlattr **tca,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
530
531
532
  		      unsigned long *arg)
  {
  	struct tc_u_common *tp_c = tp->data;
  	struct tc_u_hnode *ht;
  	struct tc_u_knode *n;
  	struct tc_u32_sel *s;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
533
534
  	struct nlattr *opt = tca[TCA_OPTIONS];
  	struct nlattr *tb[TCA_U32_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
539
  	u32 htid;
  	int err;
  
  	if (opt == NULL)
  		return handle ? -EINVAL : 0;
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
540
  	err = nla_parse_nested(tb, TCA_U32_MAX, opt, u32_policy);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
541
542
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543

cc7ec456f   Eric Dumazet   net_sched: cleanups
544
545
  	n = (struct tc_u_knode *)*arg;
  	if (n) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
  		if (TC_U32_KEY(n->handle) == 0)
  			return -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
548
  		return u32_set_parms(tp, base, n->ht_up, n, tb, tca[TCA_RATE]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
550
  	if (tb[TCA_U32_DIVISOR]) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
551
  		unsigned int divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
555
556
557
558
559
560
561
  
  		if (--divisor > 0x100)
  			return -EINVAL;
  		if (TC_U32_KEY(handle))
  			return -EINVAL;
  		if (handle == 0) {
  			handle = gen_new_htid(tp->data);
  			if (handle == 0)
  				return -ENOMEM;
  		}
cc7ec456f   Eric Dumazet   net_sched: cleanups
562
  		ht = kzalloc(sizeof(*ht) + divisor*sizeof(void *), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
  		if (ht == NULL)
  			return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
565
  		ht->tp_c = tp_c;
e56cfad13   Jarek Poplawski   [NET_SCHED] cls_u...
566
  		ht->refcnt = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567
568
569
570
571
572
573
574
  		ht->divisor = divisor;
  		ht->handle = handle;
  		ht->prio = tp->prio;
  		ht->next = tp_c->hlist;
  		tp_c->hlist = ht;
  		*arg = (unsigned long)ht;
  		return 0;
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
575
  	if (tb[TCA_U32_HASH]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
576
  		htid = nla_get_u32(tb[TCA_U32_HASH]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
  		if (TC_U32_HTID(htid) == TC_U32_ROOT) {
  			ht = tp->root;
  			htid = ht->handle;
  		} else {
  			ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid));
  			if (ht == NULL)
  				return -EINVAL;
  		}
  	} else {
  		ht = tp->root;
  		htid = ht->handle;
  	}
  
  	if (ht->divisor < TC_U32_HASH(htid))
  		return -EINVAL;
  
  	if (handle) {
  		if (TC_U32_HTID(handle) && TC_U32_HTID(handle^htid))
  			return -EINVAL;
  		handle = htid | TC_U32_NODE(handle);
  	} else
  		handle = gen_new_kid(ht, htid);
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
599
  	if (tb[TCA_U32_SEL] == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
  		return -EINVAL;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
601
  	s = nla_data(tb[TCA_U32_SEL]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602

0da974f4f   Panagiotis Issaris   [NET]: Conversion...
603
  	n = kzalloc(sizeof(*n) + s->nkeys*sizeof(struct tc_u32_key), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
  	if (n == NULL)
  		return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
  #ifdef CONFIG_CLS_U32_PERF
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
607
  	n->pf = kzalloc(sizeof(struct tc_u32_pcnt) + s->nkeys*sizeof(u64), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
610
611
  	if (n->pf == NULL) {
  		kfree(n);
  		return -ENOBUFS;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
  #endif
  
  	memcpy(&n->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key));
  	n->ht_up = ht;
  	n->handle = handle;
b22680167   Radu Rendec   [PKT_SCHED] CLS_U...
617
  	n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
  
  #ifdef CONFIG_CLS_U32_MARK
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
620
  	if (tb[TCA_U32_MARK]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
  		struct tc_u32_mark *mark;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
622
  		mark = nla_data(tb[TCA_U32_MARK]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
625
626
  		memcpy(&n->mark, mark, sizeof(struct tc_u32_mark));
  		n->mark.success = 0;
  	}
  #endif
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
627
  	err = u32_set_parms(tp, base, ht, n, tb, tca[TCA_RATE]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
633
634
  	if (err == 0) {
  		struct tc_u_knode **ins;
  		for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next)
  			if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle))
  				break;
  
  		n->next = *ins;
6f5732142   Jarek Poplawski   pkt_sched: cls_u3...
635
  		tcf_tree_lock(tp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  		*ins = n;
6f5732142   Jarek Poplawski   pkt_sched: cls_u3...
637
  		tcf_tree_unlock(tp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
  
  		*arg = (unsigned long)n;
  		return 0;
  	}
  #ifdef CONFIG_CLS_U32_PERF
1ae39a430   Patrick McHardy   [NET_SCHED]: cls_...
643
  	kfree(n->pf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
645
646
647
648
649
650
651
652
653
  #endif
  	kfree(n);
  	return err;
  }
  
  static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg)
  {
  	struct tc_u_common *tp_c = tp->data;
  	struct tc_u_hnode *ht;
  	struct tc_u_knode *n;
cc7ec456f   Eric Dumazet   net_sched: cleanups
654
  	unsigned int h;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
  
  	if (arg->stop)
  		return;
  
  	for (ht = tp_c->hlist; ht; ht = ht->next) {
  		if (ht->prio != tp->prio)
  			continue;
  		if (arg->count >= arg->skip) {
  			if (arg->fn(tp, (unsigned long)ht, arg) < 0) {
  				arg->stop = 1;
  				return;
  			}
  		}
  		arg->count++;
  		for (h = 0; h <= ht->divisor; h++) {
  			for (n = ht->ht[h]; n; n = n->next) {
  				if (arg->count < arg->skip) {
  					arg->count++;
  					continue;
  				}
  				if (arg->fn(tp, (unsigned long)n, arg) < 0) {
  					arg->stop = 1;
  					return;
  				}
  				arg->count++;
  			}
  		}
  	}
  }
  
  static int u32_dump(struct tcf_proto *tp, unsigned long fh,
  		     struct sk_buff *skb, struct tcmsg *t)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
688
  	struct tc_u_knode *n = (struct tc_u_knode *)fh;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
689
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
691
692
693
694
  
  	if (n == NULL)
  		return skb->len;
  
  	t->tcm_handle = n->handle;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
695
696
697
  	nest = nla_nest_start(skb, TCA_OPTIONS);
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698
699
  
  	if (TC_U32_KEY(n->handle) == 0) {
cc7ec456f   Eric Dumazet   net_sched: cleanups
700
701
  		struct tc_u_hnode *ht = (struct tc_u_hnode *)fh;
  		u32 divisor = ht->divisor + 1;
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
702
  		NLA_PUT_U32(skb, TCA_U32_DIVISOR, divisor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
703
  	} else {
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
704
  		NLA_PUT(skb, TCA_U32_SEL,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
  			sizeof(n->sel) + n->sel.nkeys*sizeof(struct tc_u32_key),
  			&n->sel);
  		if (n->ht_up) {
  			u32 htid = n->handle & 0xFFFFF000;
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
709
  			NLA_PUT_U32(skb, TCA_U32_HASH, htid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
  		}
  		if (n->res.classid)
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
712
  			NLA_PUT_U32(skb, TCA_U32_CLASSID, n->res.classid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  		if (n->ht_down)
24beeab53   Patrick McHardy   [NET_SCHED]: Use ...
714
  			NLA_PUT_U32(skb, TCA_U32_LINK, n->ht_down->handle);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
  
  #ifdef CONFIG_CLS_U32_MARK
  		if (n->mark.val || n->mark.mask)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
718
  			NLA_PUT(skb, TCA_U32_MARK, sizeof(n->mark), &n->mark);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
  #endif
  
  		if (tcf_exts_dump(skb, &n->exts, &u32_ext_map) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
722
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
  
  #ifdef CONFIG_NET_CLS_IND
cc7ec456f   Eric Dumazet   net_sched: cleanups
725
  		if (strlen(n->indev))
57e1c487a   Patrick McHardy   [NET_SCHED]: Use ...
726
  			NLA_PUT_STRING(skb, TCA_U32_INDEV, n->indev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727
728
  #endif
  #ifdef CONFIG_CLS_U32_PERF
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
729
  		NLA_PUT(skb, TCA_U32_PCNT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
  		sizeof(struct tc_u32_pcnt) + n->sel.nkeys*sizeof(u64),
  			n->pf);
  #endif
  	}
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
734
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
736
  	if (TC_U32_KEY(n->handle))
  		if (tcf_exts_dump_stats(skb, &n->exts, &u32_ext_map) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
737
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
738
  	return skb->len;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
739
  nla_put_failure:
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
740
  	nla_nest_cancel(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
  	return -1;
  }
2eb9d75c7   Patrick McHardy   [NET_SCHED]: mark...
743
  static struct tcf_proto_ops cls_u32_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
  	.kind		=	"u32",
  	.classify	=	u32_classify,
  	.init		=	u32_init,
  	.destroy	=	u32_destroy,
  	.get		=	u32_get,
  	.put		=	u32_put,
  	.change		=	u32_change,
  	.delete		=	u32_delete,
  	.walk		=	u32_walk,
  	.dump		=	u32_dump,
  	.owner		=	THIS_MODULE,
  };
  
  static int __init init_u32(void)
  {
6ff9c3644   stephen hemminger   net sched: printk...
759
760
  	pr_info("u32 classifier
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
761
  #ifdef CONFIG_CLS_U32_PERF
6ff9c3644   stephen hemminger   net sched: printk...
762
763
  	pr_info("    Performance counters on
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
  #ifdef CONFIG_NET_CLS_IND
6ff9c3644   stephen hemminger   net sched: printk...
766
767
  	pr_info("    input device check on
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768
769
  #endif
  #ifdef CONFIG_NET_CLS_ACT
6ff9c3644   stephen hemminger   net sched: printk...
770
771
  	pr_info("    Actions configured
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
773
774
  #endif
  	return register_tcf_proto_ops(&cls_u32_ops);
  }
10297b993   YOSHIFUJI Hideaki   [NET] SCHED: Fix ...
775
  static void __exit exit_u32(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
777
778
779
780
781
782
  {
  	unregister_tcf_proto_ops(&cls_u32_ops);
  }
  
  module_init(init_u32)
  module_exit(exit_u32)
  MODULE_LICENSE("GPL");