17
17
#include <linux/errno.h>
18
18
#include <linux/rtnetlink.h>
19
19
#include <linux/skbuff.h>
20
+ #include <linux/idr.h>
20
21
#include <net/netlink.h>
21
22
#include <net/act_api.h>
22
23
#include <net/pkt_cls.h>
23
24
24
25
struct basic_head {
25
- u32 hgenerator ;
26
26
struct list_head flist ;
27
+ struct idr handle_idr ;
27
28
struct rcu_head rcu ;
28
29
};
29
30
@@ -78,6 +79,7 @@ static int basic_init(struct tcf_proto *tp)
78
79
if (head == NULL )
79
80
return - ENOBUFS ;
80
81
INIT_LIST_HEAD (& head -> flist );
82
+ idr_init (& head -> handle_idr );
81
83
rcu_assign_pointer (tp -> root , head );
82
84
return 0 ;
83
85
}
@@ -99,8 +101,10 @@ static void basic_destroy(struct tcf_proto *tp)
99
101
list_for_each_entry_safe (f , n , & head -> flist , link ) {
100
102
list_del_rcu (& f -> link );
101
103
tcf_unbind_filter (tp , & f -> res );
104
+ idr_remove_ext (& head -> handle_idr , f -> handle );
102
105
call_rcu (& f -> rcu , basic_delete_filter );
103
106
}
107
+ idr_destroy (& head -> handle_idr );
104
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
116
list_del_rcu (& f -> link );
113
117
tcf_unbind_filter (tp , & f -> res );
118
+ idr_remove_ext (& head -> handle_idr , f -> handle );
114
119
call_rcu (& f -> rcu , basic_delete_filter );
115
120
* last = list_empty (& head -> flist );
116
121
return 0 ;
@@ -154,6 +159,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
154
159
struct nlattr * tb [TCA_BASIC_MAX + 1 ];
155
160
struct basic_filter * fold = (struct basic_filter * ) * arg ;
156
161
struct basic_filter * fnew ;
162
+ unsigned long idr_index ;
157
163
158
164
if (tca [TCA_OPTIONS ] == NULL )
159
165
return - EINVAL ;
@@ -179,30 +185,31 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
179
185
err = - EINVAL ;
180
186
if (handle ) {
181
187
fnew -> handle = handle ;
182
- } else if (fold ) {
183
- fnew -> handle = fold -> handle ;
188
+ if (!fold ) {
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
194
} else {
185
- unsigned int i = 0x80000000 ;
186
- do {
187
- if (++ head -> hgenerator == 0x7FFFFFFF )
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" );
195
+ err = idr_alloc_ext (& head -> handle_idr , fnew , & idr_index ,
196
+ 1 , 0x7FFFFFFF , GFP_KERNEL );
197
+ if (err )
193
198
goto errout ;
194
- }
195
-
196
- fnew -> handle = head -> hgenerator ;
199
+ fnew -> handle = idr_index ;
197
200
}
198
201
199
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
206
goto errout ;
207
+ }
202
208
203
209
* arg = fnew ;
204
210
205
211
if (fold ) {
212
+ idr_replace_ext (& head -> handle_idr , fnew , fnew -> handle );
206
213
list_replace_rcu (& fold -> link , & fnew -> link );
207
214
tcf_unbind_filter (tp , & fold -> res );
208
215
call_rcu (& fold -> rcu , basic_delete_filter );
0 commit comments