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 | |
| 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>
| -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] = { |
