diff options
Diffstat (limited to 'net/sched/cls_basic.c')
-rw-r--r-- | net/sched/cls_basic.c | 37 |
1 files changed, 22 insertions, 15 deletions
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index d89ebafd2239..cfeb6f158566 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c | |||
@@ -17,13 +17,14 @@ | |||
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/rtnetlink.h> | 18 | #include <linux/rtnetlink.h> |
19 | #include <linux/skbuff.h> | 19 | #include <linux/skbuff.h> |
20 | #include <linux/idr.h> | ||
20 | #include <net/netlink.h> | 21 | #include <net/netlink.h> |
21 | #include <net/act_api.h> | 22 | #include <net/act_api.h> |
22 | #include <net/pkt_cls.h> | 23 | #include <net/pkt_cls.h> |
23 | 24 | ||
24 | struct basic_head { | 25 | struct basic_head { |
25 | u32 hgenerator; | ||
26 | struct list_head flist; | 26 | struct list_head flist; |
27 | struct idr handle_idr; | ||
27 | struct rcu_head rcu; | 28 | struct rcu_head rcu; |
28 | }; | 29 | }; |
29 | 30 | ||
@@ -78,6 +79,7 @@ static int basic_init(struct tcf_proto *tp) | |||
78 | if (head == NULL) | 79 | if (head == NULL) |
79 | return -ENOBUFS; | 80 | return -ENOBUFS; |
80 | INIT_LIST_HEAD(&head->flist); | 81 | INIT_LIST_HEAD(&head->flist); |
82 | idr_init(&head->handle_idr); | ||
81 | rcu_assign_pointer(tp->root, head); | 83 | rcu_assign_pointer(tp->root, head); |
82 | return 0; | 84 | return 0; |
83 | } | 85 | } |
@@ -99,8 +101,10 @@ static void basic_destroy(struct tcf_proto *tp) | |||
99 | list_for_each_entry_safe(f, n, &head->flist, link) { | 101 | list_for_each_entry_safe(f, n, &head->flist, link) { |
100 | list_del_rcu(&f->link); | 102 | list_del_rcu(&f->link); |
101 | tcf_unbind_filter(tp, &f->res); | 103 | tcf_unbind_filter(tp, &f->res); |
104 | idr_remove_ext(&head->handle_idr, f->handle); | ||
102 | call_rcu(&f->rcu, basic_delete_filter); | 105 | call_rcu(&f->rcu, basic_delete_filter); |
103 | } | 106 | } |
107 | idr_destroy(&head->handle_idr); | ||
104 | kfree_rcu(head, rcu); | 108 | kfree_rcu(head, rcu); |
105 | } | 109 | } |
106 | 110 | ||
@@ -111,6 +115,7 @@ static int basic_delete(struct tcf_proto *tp, void *arg, bool *last) | |||
111 | 115 | ||
112 | list_del_rcu(&f->link); | 116 | list_del_rcu(&f->link); |
113 | tcf_unbind_filter(tp, &f->res); | 117 | tcf_unbind_filter(tp, &f->res); |
118 | idr_remove_ext(&head->handle_idr, f->handle); | ||
114 | call_rcu(&f->rcu, basic_delete_filter); | 119 | call_rcu(&f->rcu, basic_delete_filter); |
115 | *last = list_empty(&head->flist); | 120 | *last = list_empty(&head->flist); |
116 | return 0; | 121 | return 0; |
@@ -154,6 +159,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, | |||
154 | struct nlattr *tb[TCA_BASIC_MAX + 1]; | 159 | struct nlattr *tb[TCA_BASIC_MAX + 1]; |
155 | struct basic_filter *fold = (struct basic_filter *) *arg; | 160 | struct basic_filter *fold = (struct basic_filter *) *arg; |
156 | struct basic_filter *fnew; | 161 | struct basic_filter *fnew; |
162 | unsigned long idr_index; | ||
157 | 163 | ||
158 | if (tca[TCA_OPTIONS] == NULL) | 164 | if (tca[TCA_OPTIONS] == NULL) |
159 | return -EINVAL; | 165 | return -EINVAL; |
@@ -179,30 +185,31 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, | |||
179 | err = -EINVAL; | 185 | err = -EINVAL; |
180 | if (handle) { | 186 | if (handle) { |
181 | fnew->handle = handle; | 187 | fnew->handle = handle; |
182 | } else if (fold) { | 188 | if (!fold) { |
183 | fnew->handle = fold->handle; | 189 | err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index, |
190 | handle, handle + 1, GFP_KERNEL); | ||
191 | if (err) | ||
192 | goto errout; | ||
193 | } | ||
184 | } else { | 194 | } else { |
185 | unsigned int i = 0x80000000; | 195 | err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index, |
186 | do { | 196 | 1, 0x7FFFFFFF, GFP_KERNEL); |
187 | if (++head->hgenerator == 0x7FFFFFFF) | 197 | if (err) |
188 | head->hgenerator = 1; | ||
189 | } while (--i > 0 && basic_get(tp, head->hgenerator)); | ||
190 | |||
191 | if (i <= 0) { | ||
192 | pr_err("Insufficient number of handles\n"); | ||
193 | goto errout; | 198 | goto errout; |
194 | } | 199 | fnew->handle = idr_index; |
195 | |||
196 | fnew->handle = head->hgenerator; | ||
197 | } | 200 | } |
198 | 201 | ||
199 | err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr); | 202 | err = basic_set_parms(net, tp, fnew, base, tb, tca[TCA_RATE], ovr); |
200 | if (err < 0) | 203 | if (err < 0) { |
204 | if (!fold) | ||
205 | idr_remove_ext(&head->handle_idr, fnew->handle); | ||
201 | goto errout; | 206 | goto errout; |
207 | } | ||
202 | 208 | ||
203 | *arg = fnew; | 209 | *arg = fnew; |
204 | 210 | ||
205 | if (fold) { | 211 | if (fold) { |
212 | idr_replace_ext(&head->handle_idr, fnew, fnew->handle); | ||
206 | list_replace_rcu(&fold->link, &fnew->link); | 213 | list_replace_rcu(&fold->link, &fnew->link); |
207 | tcf_unbind_filter(tp, &fold->res); | 214 | tcf_unbind_filter(tp, &fold->res); |
208 | call_rcu(&fold->rcu, basic_delete_filter); | 215 | call_rcu(&fold->rcu, basic_delete_filter); |