diff options
Diffstat (limited to 'net/sched/cls_flow.c')
-rw-r--r-- | net/sched/cls_flow.c | 52 |
1 files changed, 43 insertions, 9 deletions
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 971b867e0484..8f63a1a94014 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c | |||
@@ -36,6 +36,8 @@ struct flow_filter { | |||
36 | struct list_head list; | 36 | struct list_head list; |
37 | struct tcf_exts exts; | 37 | struct tcf_exts exts; |
38 | struct tcf_ematch_tree ematches; | 38 | struct tcf_ematch_tree ematches; |
39 | struct timer_list perturb_timer; | ||
40 | u32 perturb_period; | ||
39 | u32 handle; | 41 | u32 handle; |
40 | 42 | ||
41 | u32 nkeys; | 43 | u32 nkeys; |
@@ -47,11 +49,9 @@ struct flow_filter { | |||
47 | u32 addend; | 49 | u32 addend; |
48 | u32 divisor; | 50 | u32 divisor; |
49 | u32 baseclass; | 51 | u32 baseclass; |
52 | u32 hashrnd; | ||
50 | }; | 53 | }; |
51 | 54 | ||
52 | static u32 flow_hashrnd __read_mostly; | ||
53 | static int flow_hashrnd_initted __read_mostly; | ||
54 | |||
55 | static const struct tcf_ext_map flow_ext_map = { | 55 | static const struct tcf_ext_map flow_ext_map = { |
56 | .action = TCA_FLOW_ACT, | 56 | .action = TCA_FLOW_ACT, |
57 | .police = TCA_FLOW_POLICE, | 57 | .police = TCA_FLOW_POLICE, |
@@ -348,7 +348,7 @@ static int flow_classify(struct sk_buff *skb, struct tcf_proto *tp, | |||
348 | } | 348 | } |
349 | 349 | ||
350 | if (f->mode == FLOW_MODE_HASH) | 350 | if (f->mode == FLOW_MODE_HASH) |
351 | classid = jhash2(keys, f->nkeys, flow_hashrnd); | 351 | classid = jhash2(keys, f->nkeys, f->hashrnd); |
352 | else { | 352 | else { |
353 | classid = keys[0]; | 353 | classid = keys[0]; |
354 | classid = (classid & f->mask) ^ f->xor; | 354 | classid = (classid & f->mask) ^ f->xor; |
@@ -369,6 +369,15 @@ static int flow_classify(struct sk_buff *skb, struct tcf_proto *tp, | |||
369 | return -1; | 369 | return -1; |
370 | } | 370 | } |
371 | 371 | ||
372 | static void flow_perturbation(unsigned long arg) | ||
373 | { | ||
374 | struct flow_filter *f = (struct flow_filter *)arg; | ||
375 | |||
376 | get_random_bytes(&f->hashrnd, 4); | ||
377 | if (f->perturb_period) | ||
378 | mod_timer(&f->perturb_timer, jiffies + f->perturb_period); | ||
379 | } | ||
380 | |||
372 | static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { | 381 | static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { |
373 | [TCA_FLOW_KEYS] = { .type = NLA_U32 }, | 382 | [TCA_FLOW_KEYS] = { .type = NLA_U32 }, |
374 | [TCA_FLOW_MODE] = { .type = NLA_U32 }, | 383 | [TCA_FLOW_MODE] = { .type = NLA_U32 }, |
@@ -381,6 +390,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { | |||
381 | [TCA_FLOW_ACT] = { .type = NLA_NESTED }, | 390 | [TCA_FLOW_ACT] = { .type = NLA_NESTED }, |
382 | [TCA_FLOW_POLICE] = { .type = NLA_NESTED }, | 391 | [TCA_FLOW_POLICE] = { .type = NLA_NESTED }, |
383 | [TCA_FLOW_EMATCHES] = { .type = NLA_NESTED }, | 392 | [TCA_FLOW_EMATCHES] = { .type = NLA_NESTED }, |
393 | [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, | ||
384 | }; | 394 | }; |
385 | 395 | ||
386 | static int flow_change(struct tcf_proto *tp, unsigned long base, | 396 | static int flow_change(struct tcf_proto *tp, unsigned long base, |
@@ -394,6 +404,7 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, | |||
394 | struct tcf_exts e; | 404 | struct tcf_exts e; |
395 | struct tcf_ematch_tree t; | 405 | struct tcf_ematch_tree t; |
396 | unsigned int nkeys = 0; | 406 | unsigned int nkeys = 0; |
407 | unsigned int perturb_period = 0; | ||
397 | u32 baseclass = 0; | 408 | u32 baseclass = 0; |
398 | u32 keymask = 0; | 409 | u32 keymask = 0; |
399 | u32 mode; | 410 | u32 mode; |
@@ -442,6 +453,14 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, | |||
442 | mode = nla_get_u32(tb[TCA_FLOW_MODE]); | 453 | mode = nla_get_u32(tb[TCA_FLOW_MODE]); |
443 | if (mode != FLOW_MODE_HASH && nkeys > 1) | 454 | if (mode != FLOW_MODE_HASH && nkeys > 1) |
444 | goto err2; | 455 | goto err2; |
456 | |||
457 | if (mode == FLOW_MODE_HASH) | ||
458 | perturb_period = f->perturb_period; | ||
459 | if (tb[TCA_FLOW_PERTURB]) { | ||
460 | if (mode != FLOW_MODE_HASH) | ||
461 | goto err2; | ||
462 | perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; | ||
463 | } | ||
445 | } else { | 464 | } else { |
446 | err = -EINVAL; | 465 | err = -EINVAL; |
447 | if (!handle) | 466 | if (!handle) |
@@ -455,6 +474,12 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, | |||
455 | if (mode != FLOW_MODE_HASH && nkeys > 1) | 474 | if (mode != FLOW_MODE_HASH && nkeys > 1) |
456 | goto err2; | 475 | goto err2; |
457 | 476 | ||
477 | if (tb[TCA_FLOW_PERTURB]) { | ||
478 | if (mode != FLOW_MODE_HASH) | ||
479 | goto err2; | ||
480 | perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; | ||
481 | } | ||
482 | |||
458 | if (TC_H_MAJ(baseclass) == 0) | 483 | if (TC_H_MAJ(baseclass) == 0) |
459 | baseclass = TC_H_MAKE(tp->q->handle, baseclass); | 484 | baseclass = TC_H_MAKE(tp->q->handle, baseclass); |
460 | if (TC_H_MIN(baseclass) == 0) | 485 | if (TC_H_MIN(baseclass) == 0) |
@@ -467,6 +492,11 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, | |||
467 | 492 | ||
468 | f->handle = handle; | 493 | f->handle = handle; |
469 | f->mask = ~0U; | 494 | f->mask = ~0U; |
495 | |||
496 | get_random_bytes(&f->hashrnd, 4); | ||
497 | f->perturb_timer.function = flow_perturbation; | ||
498 | f->perturb_timer.data = (unsigned long)f; | ||
499 | init_timer_deferrable(&f->perturb_timer); | ||
470 | } | 500 | } |
471 | 501 | ||
472 | tcf_exts_change(tp, &f->exts, &e); | 502 | tcf_exts_change(tp, &f->exts, &e); |
@@ -495,6 +525,11 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, | |||
495 | if (baseclass) | 525 | if (baseclass) |
496 | f->baseclass = baseclass; | 526 | f->baseclass = baseclass; |
497 | 527 | ||
528 | f->perturb_period = perturb_period; | ||
529 | del_timer(&f->perturb_timer); | ||
530 | if (perturb_period) | ||
531 | mod_timer(&f->perturb_timer, jiffies + perturb_period); | ||
532 | |||
498 | if (*arg == 0) | 533 | if (*arg == 0) |
499 | list_add_tail(&f->list, &head->filters); | 534 | list_add_tail(&f->list, &head->filters); |
500 | 535 | ||
@@ -512,6 +547,7 @@ err1: | |||
512 | 547 | ||
513 | static void flow_destroy_filter(struct tcf_proto *tp, struct flow_filter *f) | 548 | static void flow_destroy_filter(struct tcf_proto *tp, struct flow_filter *f) |
514 | { | 549 | { |
550 | del_timer_sync(&f->perturb_timer); | ||
515 | tcf_exts_destroy(tp, &f->exts); | 551 | tcf_exts_destroy(tp, &f->exts); |
516 | tcf_em_tree_destroy(tp, &f->ematches); | 552 | tcf_em_tree_destroy(tp, &f->ematches); |
517 | kfree(f); | 553 | kfree(f); |
@@ -532,11 +568,6 @@ static int flow_init(struct tcf_proto *tp) | |||
532 | { | 568 | { |
533 | struct flow_head *head; | 569 | struct flow_head *head; |
534 | 570 | ||
535 | if (!flow_hashrnd_initted) { | ||
536 | get_random_bytes(&flow_hashrnd, 4); | ||
537 | flow_hashrnd_initted = 1; | ||
538 | } | ||
539 | |||
540 | head = kzalloc(sizeof(*head), GFP_KERNEL); | 571 | head = kzalloc(sizeof(*head), GFP_KERNEL); |
541 | if (head == NULL) | 572 | if (head == NULL) |
542 | return -ENOBUFS; | 573 | return -ENOBUFS; |
@@ -605,6 +636,9 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh, | |||
605 | if (f->baseclass) | 636 | if (f->baseclass) |
606 | NLA_PUT_U32(skb, TCA_FLOW_BASECLASS, f->baseclass); | 637 | NLA_PUT_U32(skb, TCA_FLOW_BASECLASS, f->baseclass); |
607 | 638 | ||
639 | if (f->perturb_period) | ||
640 | NLA_PUT_U32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ); | ||
641 | |||
608 | if (tcf_exts_dump(skb, &f->exts, &flow_ext_map) < 0) | 642 | if (tcf_exts_dump(skb, &f->exts, &flow_ext_map) < 0) |
609 | goto nla_put_failure; | 643 | goto nla_put_failure; |
610 | #ifdef CONFIG_NET_EMATCH | 644 | #ifdef CONFIG_NET_EMATCH |