diff options
author | Cong Wang <cwang@twopensource.com> | 2014-07-17 20:34:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-20 23:49:17 -0400 |
commit | 7801db8aec957fa6610efe0ee26a6c8bc0f1d73b (patch) | |
tree | 24c7a78ca03e5c6eff1b2b23d281d83c02e72788 /net | |
parent | c2a6c7813f1ffae636e369b5d7011c9f518d3cd9 (diff) |
net_sched: avoid generating same handle for u32 filters
When kernel generates a handle for a u32 filter, it tries to start
from the max in the bucket. So when we have a filter with the max (fff)
handle, it will cause kernel always generates the same handle for new
filters. This can be shown by the following command:
tc qdisc add dev eth0 ingress
tc filter add dev eth0 parent ffff: protocol ip pref 770 handle 800::fff u32 match ip protocol 1 0xff
tc filter add dev eth0 parent ffff: protocol ip pref 770 u32 match ip protocol 1 0xff
...
we will get some u32 filters with same handle:
# tc filter show dev eth0 parent ffff:
filter protocol ip pref 770 u32
filter protocol ip pref 770 u32 fh 800: ht divisor 1
filter protocol ip pref 770 u32 fh 800::fff order 4095 key ht 800 bkt 0
match 00010000/00ff0000 at 8
filter protocol ip pref 770 u32 fh 800::fff order 4095 key ht 800 bkt 0
match 00010000/00ff0000 at 8
filter protocol ip pref 770 u32 fh 800::fff order 4095 key ht 800 bkt 0
match 00010000/00ff0000 at 8
filter protocol ip pref 770 u32 fh 800::fff order 4095 key ht 800 bkt 0
match 00010000/00ff0000 at 8
handles should be unique. This patch fixes it by looking up a bitmap,
so that can guarantee the handle is as unique as possible. For compatibility,
we still start from 0x800.
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Cong Wang <cwang@twopensource.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/sched/cls_u32.c | 19 |
1 files changed, 14 insertions, 5 deletions
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index c39b583ace32..70c0be8d0121 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/errno.h> | 38 | #include <linux/errno.h> |
39 | #include <linux/rtnetlink.h> | 39 | #include <linux/rtnetlink.h> |
40 | #include <linux/skbuff.h> | 40 | #include <linux/skbuff.h> |
41 | #include <linux/bitmap.h> | ||
41 | #include <net/netlink.h> | 42 | #include <net/netlink.h> |
42 | #include <net/act_api.h> | 43 | #include <net/act_api.h> |
43 | #include <net/pkt_cls.h> | 44 | #include <net/pkt_cls.h> |
@@ -460,17 +461,25 @@ static int u32_delete(struct tcf_proto *tp, unsigned long arg) | |||
460 | return 0; | 461 | return 0; |
461 | } | 462 | } |
462 | 463 | ||
464 | #define NR_U32_NODE (1<<12) | ||
463 | static u32 gen_new_kid(struct tc_u_hnode *ht, u32 handle) | 465 | static u32 gen_new_kid(struct tc_u_hnode *ht, u32 handle) |
464 | { | 466 | { |
465 | struct tc_u_knode *n; | 467 | struct tc_u_knode *n; |
466 | unsigned int i = 0x7FF; | 468 | unsigned long i; |
469 | unsigned long *bitmap = kzalloc(BITS_TO_LONGS(NR_U32_NODE) * sizeof(unsigned long), | ||
470 | GFP_KERNEL); | ||
471 | if (!bitmap) | ||
472 | return handle | 0xFFF; | ||
467 | 473 | ||
468 | for (n = ht->ht[TC_U32_HASH(handle)]; n; n = n->next) | 474 | for (n = ht->ht[TC_U32_HASH(handle)]; n; n = n->next) |
469 | if (i < TC_U32_NODE(n->handle)) | 475 | set_bit(TC_U32_NODE(n->handle), bitmap); |
470 | i = TC_U32_NODE(n->handle); | ||
471 | i++; | ||
472 | 476 | ||
473 | return handle | (i > 0xFFF ? 0xFFF : i); | 477 | i = find_next_zero_bit(bitmap, NR_U32_NODE, 0x800); |
478 | if (i >= NR_U32_NODE) | ||
479 | i = find_next_zero_bit(bitmap, NR_U32_NODE, 1); | ||
480 | |||
481 | kfree(bitmap); | ||
482 | return handle | (i >= NR_U32_NODE ? 0xFFF : i); | ||
474 | } | 483 | } |
475 | 484 | ||
476 | static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { | 485 | static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = { |