|
10 | 10 | #include <linux/skbuff.h>
|
11 | 11 | #include <linux/errno.h>
|
12 | 12 | #include <linux/slab.h>
|
| 13 | +#include <linux/rcupdate.h> |
13 | 14 | #include <net/act_api.h>
|
14 | 15 | #include <net/netlink.h>
|
15 | 16 | #include <net/pkt_cls.h>
|
@@ -313,6 +314,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
|
313 | 314 | struct tcindex_filter *f = NULL; /* make gcc behave */
|
314 | 315 | int err, balloc = 0;
|
315 | 316 | struct tcf_exts e;
|
| 317 | + bool update_h = false; |
316 | 318 |
|
317 | 319 | err = tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
|
318 | 320 | if (err < 0)
|
@@ -427,10 +429,13 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
|
427 | 429 | }
|
428 | 430 | }
|
429 | 431 |
|
430 |
| - if (cp->perfect) |
| 432 | + if (cp->perfect) { |
431 | 433 | r = cp->perfect + handle;
|
432 |
| - else |
433 |
| - r = tcindex_lookup(cp, handle) ? : &new_filter_result; |
| 434 | + } else { |
| 435 | + /* imperfect area is updated in-place using rcu */ |
| 436 | + update_h = !!tcindex_lookup(cp, handle); |
| 437 | + r = &new_filter_result; |
| 438 | + } |
434 | 439 |
|
435 | 440 | if (r == &new_filter_result) {
|
436 | 441 | f = kzalloc(sizeof(*f), GFP_KERNEL);
|
@@ -464,7 +469,28 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
|
464 | 469 |
|
465 | 470 | rcu_assign_pointer(tp->root, cp);
|
466 | 471 |
|
467 |
| - if (r == &new_filter_result) { |
| 472 | + if (update_h) { |
| 473 | + struct tcindex_filter __rcu **fp; |
| 474 | + struct tcindex_filter *cf; |
| 475 | + |
| 476 | + f->result.res = r->res; |
| 477 | + tcf_exts_change(&f->result.exts, &r->exts); |
| 478 | + |
| 479 | + /* imperfect area bucket */ |
| 480 | + fp = cp->h + (handle % cp->hash); |
| 481 | + |
| 482 | + /* lookup the filter, guaranteed to exist */ |
| 483 | + for (cf = rcu_dereference_bh_rtnl(*fp); cf; |
| 484 | + fp = &cf->next, cf = rcu_dereference_bh_rtnl(*fp)) |
| 485 | + if (cf->key == handle) |
| 486 | + break; |
| 487 | + |
| 488 | + f->next = cf->next; |
| 489 | + |
| 490 | + cf = rcu_replace_pointer(*fp, f, 1); |
| 491 | + tcf_exts_get_net(&cf->result.exts); |
| 492 | + tcf_queue_work(&cf->rwork, tcindex_destroy_fexts_work); |
| 493 | + } else if (r == &new_filter_result) { |
468 | 494 | struct tcindex_filter *nfp;
|
469 | 495 | struct tcindex_filter __rcu **fp;
|
470 | 496 |
|
|
0 commit comments