aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/sched/cls_fw.c111
1 files changed, 77 insertions, 34 deletions
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 861b03ccfed0..006b45a67fdd 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -33,17 +33,20 @@
33 33
34struct fw_head { 34struct fw_head {
35 u32 mask; 35 u32 mask;
36 struct fw_filter *ht[HTSIZE]; 36 struct fw_filter __rcu *ht[HTSIZE];
37 struct rcu_head rcu;
37}; 38};
38 39
39struct fw_filter { 40struct fw_filter {
40 struct fw_filter *next; 41 struct fw_filter __rcu *next;
41 u32 id; 42 u32 id;
42 struct tcf_result res; 43 struct tcf_result res;
43#ifdef CONFIG_NET_CLS_IND 44#ifdef CONFIG_NET_CLS_IND
44 int ifindex; 45 int ifindex;
45#endif /* CONFIG_NET_CLS_IND */ 46#endif /* CONFIG_NET_CLS_IND */
46 struct tcf_exts exts; 47 struct tcf_exts exts;
48 struct tcf_proto *tp;
49 struct rcu_head rcu;
47}; 50};
48 51
49static u32 fw_hash(u32 handle) 52static u32 fw_hash(u32 handle)
@@ -56,14 +59,16 @@ static u32 fw_hash(u32 handle)
56static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, 59static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp,
57 struct tcf_result *res) 60 struct tcf_result *res)
58{ 61{
59 struct fw_head *head = tp->root; 62 struct fw_head *head = rcu_dereference_bh(tp->root);
60 struct fw_filter *f; 63 struct fw_filter *f;
61 int r; 64 int r;
62 u32 id = skb->mark; 65 u32 id = skb->mark;
63 66
64 if (head != NULL) { 67 if (head != NULL) {
65 id &= head->mask; 68 id &= head->mask;
66 for (f = head->ht[fw_hash(id)]; f; f = f->next) { 69
70 for (f = rcu_dereference_bh(head->ht[fw_hash(id)]); f;
71 f = rcu_dereference_bh(f->next)) {
67 if (f->id == id) { 72 if (f->id == id) {
68 *res = f->res; 73 *res = f->res;
69#ifdef CONFIG_NET_CLS_IND 74#ifdef CONFIG_NET_CLS_IND
@@ -92,13 +97,14 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp,
92 97
93static unsigned long fw_get(struct tcf_proto *tp, u32 handle) 98static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
94{ 99{
95 struct fw_head *head = tp->root; 100 struct fw_head *head = rtnl_dereference(tp->root);
96 struct fw_filter *f; 101 struct fw_filter *f;
97 102
98 if (head == NULL) 103 if (head == NULL)
99 return 0; 104 return 0;
100 105
101 for (f = head->ht[fw_hash(handle)]; f; f = f->next) { 106 f = rtnl_dereference(head->ht[fw_hash(handle)]);
107 for (; f; f = rtnl_dereference(f->next)) {
102 if (f->id == handle) 108 if (f->id == handle)
103 return (unsigned long)f; 109 return (unsigned long)f;
104 } 110 }
@@ -114,8 +120,11 @@ static int fw_init(struct tcf_proto *tp)
114 return 0; 120 return 0;
115} 121}
116 122
117static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f) 123static void fw_delete_filter(struct rcu_head *head)
118{ 124{
125 struct fw_filter *f = container_of(head, struct fw_filter, rcu);
126 struct tcf_proto *tp = f->tp;
127
119 tcf_unbind_filter(tp, &f->res); 128 tcf_unbind_filter(tp, &f->res);
120 tcf_exts_destroy(tp, &f->exts); 129 tcf_exts_destroy(tp, &f->exts);
121 kfree(f); 130 kfree(f);
@@ -123,7 +132,7 @@ static void fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f)
123 132
124static void fw_destroy(struct tcf_proto *tp) 133static void fw_destroy(struct tcf_proto *tp)
125{ 134{
126 struct fw_head *head = tp->root; 135 struct fw_head *head = rtnl_dereference(tp->root);
127 struct fw_filter *f; 136 struct fw_filter *f;
128 int h; 137 int h;
129 138
@@ -131,29 +140,33 @@ static void fw_destroy(struct tcf_proto *tp)
131 return; 140 return;
132 141
133 for (h = 0; h < HTSIZE; h++) { 142 for (h = 0; h < HTSIZE; h++) {
134 while ((f = head->ht[h]) != NULL) { 143 while ((f = rtnl_dereference(head->ht[h])) != NULL) {
135 head->ht[h] = f->next; 144 RCU_INIT_POINTER(head->ht[h],
136 fw_delete_filter(tp, f); 145 rtnl_dereference(f->next));
146 call_rcu(&f->rcu, fw_delete_filter);
137 } 147 }
138 } 148 }
139 kfree(head); 149 RCU_INIT_POINTER(tp->root, NULL);
150 kfree_rcu(head, rcu);
140} 151}
141 152
142static int fw_delete(struct tcf_proto *tp, unsigned long arg) 153static int fw_delete(struct tcf_proto *tp, unsigned long arg)
143{ 154{
144 struct fw_head *head = tp->root; 155 struct fw_head *head = rtnl_dereference(tp->root);
145 struct fw_filter *f = (struct fw_filter *)arg; 156 struct fw_filter *f = (struct fw_filter *)arg;
146 struct fw_filter **fp; 157 struct fw_filter __rcu **fp;
158 struct fw_filter *pfp;
147 159
148 if (head == NULL || f == NULL) 160 if (head == NULL || f == NULL)
149 goto out; 161 goto out;
150 162
151 for (fp = &head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) { 163 fp = &head->ht[fw_hash(f->id)];
152 if (*fp == f) { 164
153 tcf_tree_lock(tp); 165 for (pfp = rtnl_dereference(*fp); pfp;
154 *fp = f->next; 166 fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
155 tcf_tree_unlock(tp); 167 if (pfp == f) {
156 fw_delete_filter(tp, f); 168 RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
169 call_rcu(&f->rcu, fw_delete_filter);
157 return 0; 170 return 0;
158 } 171 }
159 } 172 }
@@ -171,7 +184,7 @@ static int
171fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, 184fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
172 struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr) 185 struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr)
173{ 186{
174 struct fw_head *head = tp->root; 187 struct fw_head *head = rtnl_dereference(tp->root);
175 struct tcf_exts e; 188 struct tcf_exts e;
176 u32 mask; 189 u32 mask;
177 int err; 190 int err;
@@ -220,7 +233,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
220 struct nlattr **tca, 233 struct nlattr **tca,
221 unsigned long *arg, bool ovr) 234 unsigned long *arg, bool ovr)
222{ 235{
223 struct fw_head *head = tp->root; 236 struct fw_head *head = rtnl_dereference(tp->root);
224 struct fw_filter *f = (struct fw_filter *) *arg; 237 struct fw_filter *f = (struct fw_filter *) *arg;
225 struct nlattr *opt = tca[TCA_OPTIONS]; 238 struct nlattr *opt = tca[TCA_OPTIONS];
226 struct nlattr *tb[TCA_FW_MAX + 1]; 239 struct nlattr *tb[TCA_FW_MAX + 1];
@@ -233,10 +246,42 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
233 if (err < 0) 246 if (err < 0)
234 return err; 247 return err;
235 248
236 if (f != NULL) { 249 if (f) {
250 struct fw_filter *pfp, *fnew;
251 struct fw_filter __rcu **fp;
252
237 if (f->id != handle && handle) 253 if (f->id != handle && handle)
238 return -EINVAL; 254 return -EINVAL;
239 return fw_change_attrs(net, tp, f, tb, tca, base, ovr); 255
256 fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
257 if (!fnew)
258 return -ENOBUFS;
259
260 fnew->id = f->id;
261 fnew->res = f->res;
262#ifdef CONFIG_NET_CLS_IND
263 fnew->ifindex = f->ifindex;
264#endif /* CONFIG_NET_CLS_IND */
265 fnew->tp = f->tp;
266
267 err = fw_change_attrs(net, tp, fnew, tb, tca, base, ovr);
268 if (err < 0) {
269 kfree(fnew);
270 return err;
271 }
272
273 fp = &head->ht[fw_hash(fnew->id)];
274 for (pfp = rtnl_dereference(*fp); pfp;
275 fp = &pfp->next, pfp = rtnl_dereference(*fp))
276 if (pfp == f)
277 break;
278
279 RCU_INIT_POINTER(fnew->next, rtnl_dereference(pfp->next));
280 rcu_assign_pointer(*fp, fnew);
281 call_rcu(&f->rcu, fw_delete_filter);
282
283 *arg = (unsigned long)fnew;
284 return err;
240 } 285 }
241 286
242 if (!handle) 287 if (!handle)
@@ -252,9 +297,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
252 return -ENOBUFS; 297 return -ENOBUFS;
253 head->mask = mask; 298 head->mask = mask;
254 299
255 tcf_tree_lock(tp); 300 rcu_assign_pointer(tp->root, head);
256 tp->root = head;
257 tcf_tree_unlock(tp);
258 } 301 }
259 302
260 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); 303 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
@@ -263,15 +306,14 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
263 306
264 tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE); 307 tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE);
265 f->id = handle; 308 f->id = handle;
309 f->tp = tp;
266 310
267 err = fw_change_attrs(net, tp, f, tb, tca, base, ovr); 311 err = fw_change_attrs(net, tp, f, tb, tca, base, ovr);
268 if (err < 0) 312 if (err < 0)
269 goto errout; 313 goto errout;
270 314
271 f->next = head->ht[fw_hash(handle)]; 315 RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]);
272 tcf_tree_lock(tp); 316 rcu_assign_pointer(head->ht[fw_hash(handle)], f);
273 head->ht[fw_hash(handle)] = f;
274 tcf_tree_unlock(tp);
275 317
276 *arg = (unsigned long)f; 318 *arg = (unsigned long)f;
277 return 0; 319 return 0;
@@ -283,7 +325,7 @@ errout:
283 325
284static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) 326static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
285{ 327{
286 struct fw_head *head = tp->root; 328 struct fw_head *head = rtnl_dereference(tp->root);
287 int h; 329 int h;
288 330
289 if (head == NULL) 331 if (head == NULL)
@@ -295,7 +337,8 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
295 for (h = 0; h < HTSIZE; h++) { 337 for (h = 0; h < HTSIZE; h++) {
296 struct fw_filter *f; 338 struct fw_filter *f;
297 339
298 for (f = head->ht[h]; f; f = f->next) { 340 for (f = rtnl_dereference(head->ht[h]); f;
341 f = rtnl_dereference(f->next)) {
299 if (arg->count < arg->skip) { 342 if (arg->count < arg->skip) {
300 arg->count++; 343 arg->count++;
301 continue; 344 continue;
@@ -312,7 +355,7 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg)
312static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, 355static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
313 struct sk_buff *skb, struct tcmsg *t) 356 struct sk_buff *skb, struct tcmsg *t)
314{ 357{
315 struct fw_head *head = tp->root; 358 struct fw_head *head = rtnl_dereference(tp->root);
316 struct fw_filter *f = (struct fw_filter *)fh; 359 struct fw_filter *f = (struct fw_filter *)fh;
317 unsigned char *b = skb_tail_pointer(skb); 360 unsigned char *b = skb_tail_pointer(skb);
318 struct nlattr *nest; 361 struct nlattr *nest;