diff options
Diffstat (limited to 'net/dccp/ccids/lib/tfrc_equation.c')
-rw-r--r-- | net/dccp/ccids/lib/tfrc_equation.c | 222 |
1 files changed, 139 insertions, 83 deletions
diff --git a/net/dccp/ccids/lib/tfrc_equation.c b/net/dccp/ccids/lib/tfrc_equation.c index 2601012383fb..ddac2c511e2f 100644 --- a/net/dccp/ccids/lib/tfrc_equation.c +++ b/net/dccp/ccids/lib/tfrc_equation.c | |||
@@ -18,10 +18,79 @@ | |||
18 | #include "tfrc.h" | 18 | #include "tfrc.h" |
19 | 19 | ||
20 | #define TFRC_CALC_X_ARRSIZE 500 | 20 | #define TFRC_CALC_X_ARRSIZE 500 |
21 | #define TFRC_CALC_X_SPLIT 50000 /* 0.05 * 1000000, details below */ | ||
22 | #define TFRC_SMALLEST_P (TFRC_CALC_X_SPLIT/TFRC_CALC_X_ARRSIZE) | ||
21 | 23 | ||
22 | #define TFRC_CALC_X_SPLIT 50000 | 24 | /* |
23 | /* equivalent to 0.05 */ | 25 | TFRC TCP Reno Throughput Equation Lookup Table for f(p) |
24 | 26 | ||
27 | The following two-column lookup table implements a part of the TCP throughput | ||
28 | equation from [RFC 3448, sec. 3.1]: | ||
29 | |||
30 | s | ||
31 | X_calc = -------------------------------------------------------------- | ||
32 | R * sqrt(2*b*p/3) + (3 * t_RTO * sqrt(3*b*p/8) * (p + 32*p^3)) | ||
33 | |||
34 | Where: | ||
35 | X is the transmit rate in bytes/second | ||
36 | s is the packet size in bytes | ||
37 | R is the round trip time in seconds | ||
38 | p is the loss event rate, between 0 and 1.0, of the number of loss | ||
39 | events as a fraction of the number of packets transmitted | ||
40 | t_RTO is the TCP retransmission timeout value in seconds | ||
41 | b is the number of packets acknowledged by a single TCP ACK | ||
42 | |||
43 | We can assume that b = 1 and t_RTO is 4 * R. The equation now becomes: | ||
44 | |||
45 | s | ||
46 | X_calc = ------------------------------------------------------- | ||
47 | R * sqrt(p*2/3) + (12 * R * sqrt(p*3/8) * (p + 32*p^3)) | ||
48 | |||
49 | which we can break down into: | ||
50 | |||
51 | s | ||
52 | X_calc = --------- | ||
53 | R * f(p) | ||
54 | |||
55 | where f(p) is given for 0 < p <= 1 by: | ||
56 | |||
57 | f(p) = sqrt(2*p/3) + 12 * sqrt(3*p/8) * (p + 32*p^3) | ||
58 | |||
59 | Since this is kernel code, floating-point arithmetic is avoided in favour of | ||
60 | integer arithmetic. This means that nearly all fractional parameters are | ||
61 | scaled by 1000000: | ||
62 | * the parameters p and R | ||
63 | * the return result f(p) | ||
64 | The lookup table therefore actually tabulates the following function g(q): | ||
65 | |||
66 | g(q) = 1000000 * f(q/1000000) | ||
67 | |||
68 | Hence, when p <= 1, q must be less than or equal to 1000000. To achieve finer | ||
69 | granularity for the practically more relevant case of small values of p (up to | ||
70 | 5%), the second column is used; the first one ranges up to 100%. This split | ||
71 | corresponds to the value of q = TFRC_CALC_X_SPLIT. At the same time this also | ||
72 | determines the smallest resolution possible with this lookup table: | ||
73 | |||
74 | TFRC_SMALLEST_P = TFRC_CALC_X_SPLIT / TFRC_CALC_X_ARRSIZE | ||
75 | |||
76 | The entire table is generated by: | ||
77 | for(i=0; i < TFRC_CALC_X_ARRSIZE; i++) { | ||
78 | lookup[i][0] = g((i+1) * 1000000/TFRC_CALC_X_ARRSIZE); | ||
79 | lookup[i][1] = g((i+1) * TFRC_CALC_X_SPLIT/TFRC_CALC_X_ARRSIZE); | ||
80 | } | ||
81 | |||
82 | With the given configuration, we have, with M = TFRC_CALC_X_ARRSIZE-1, | ||
83 | lookup[0][0] = g(1000000/(M+1)) = 1000000 * f(0.2%) | ||
84 | lookup[M][0] = g(1000000) = 1000000 * f(100%) | ||
85 | lookup[0][1] = g(TFRC_SMALLEST_P) = 1000000 * f(0.01%) | ||
86 | lookup[M][1] = g(TFRC_CALC_X_SPLIT) = 1000000 * f(5%) | ||
87 | |||
88 | In summary, the two columns represent f(p) for the following ranges: | ||
89 | * The first column is for 0.002 <= p <= 1.0 | ||
90 | * The second column is for 0.0001 <= p <= 0.05 | ||
91 | Where the columns overlap, the second (finer-grained) is given preference, | ||
92 | i.e. the first column is used only for p >= 0.05. | ||
93 | */ | ||
25 | static const u32 tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE][2] = { | 94 | static const u32 tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE][2] = { |
26 | { 37172, 8172 }, | 95 | { 37172, 8172 }, |
27 | { 53499, 11567 }, | 96 | { 53499, 11567 }, |
@@ -525,85 +594,69 @@ static const u32 tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE][2] = { | |||
525 | { 243315981, 271305 } | 594 | { 243315981, 271305 } |
526 | }; | 595 | }; |
527 | 596 | ||
528 | /* Calculate the send rate as per section 3.1 of RFC3448 | 597 | /* return largest index i such that fval <= lookup[i][small] */ |
529 | 598 | static inline u32 tfrc_binsearch(u32 fval, u8 small) | |
530 | Returns send rate in bytes per second | 599 | { |
531 | 600 | u32 try, low = 0, high = TFRC_CALC_X_ARRSIZE - 1; | |
532 | Integer maths and lookups are used as not allowed floating point in kernel | 601 | |
533 | 602 | while (low < high) { | |
534 | The function for Xcalc as per section 3.1 of RFC3448 is: | 603 | try = (low + high) / 2; |
535 | 604 | if (fval <= tfrc_calc_x_lookup[try][small]) | |
536 | X = s | 605 | high = try; |
537 | ------------------------------------------------------------- | 606 | else |
538 | R*sqrt(2*b*p/3) + (t_RTO * (3*sqrt(3*b*p/8) * p * (1+32*p^2))) | 607 | low = try + 1; |
539 | 608 | } | |
540 | where | 609 | return high; |
541 | X is the trasmit rate in bytes/second | 610 | } |
542 | s is the packet size in bytes | ||
543 | R is the round trip time in seconds | ||
544 | p is the loss event rate, between 0 and 1.0, of the number of loss events | ||
545 | as a fraction of the number of packets transmitted | ||
546 | t_RTO is the TCP retransmission timeout value in seconds | ||
547 | b is the number of packets acknowledged by a single TCP acknowledgement | ||
548 | |||
549 | we can assume that b = 1 and t_RTO is 4 * R. With this the equation becomes: | ||
550 | |||
551 | X = s | ||
552 | ----------------------------------------------------------------------- | ||
553 | R * sqrt(2 * p / 3) + (12 * R * (sqrt(3 * p / 8) * p * (1 + 32 * p^2))) | ||
554 | |||
555 | |||
556 | which we can break down into: | ||
557 | |||
558 | X = s | ||
559 | -------- | ||
560 | R * f(p) | ||
561 | |||
562 | where f(p) = sqrt(2 * p / 3) + (12 * sqrt(3 * p / 8) * p * (1 + 32 * p * p)) | ||
563 | |||
564 | Function parameters: | ||
565 | s - bytes | ||
566 | R - RTT in usecs | ||
567 | p - loss rate (decimal fraction multiplied by 1,000,000) | ||
568 | |||
569 | Returns Xcalc in bytes per second | ||
570 | |||
571 | DON'T alter this code unless you run test cases against it as the code | ||
572 | has been manipulated to stop underflow/overlow. | ||
573 | 611 | ||
574 | */ | 612 | /** |
613 | * tfrc_calc_x - Calculate the send rate as per section 3.1 of RFC3448 | ||
614 | * | ||
615 | * @s: packet size in bytes | ||
616 | * @R: RTT scaled by 1000000 (i.e., microseconds) | ||
617 | * @p: loss ratio estimate scaled by 1000000 | ||
618 | * Returns X_calc in bytes per second (not scaled). | ||
619 | * | ||
620 | * Note: DO NOT alter this code unless you run test cases against it, | ||
621 | * as the code has been optimized to stop underflow/overflow. | ||
622 | */ | ||
575 | u32 tfrc_calc_x(u16 s, u32 R, u32 p) | 623 | u32 tfrc_calc_x(u16 s, u32 R, u32 p) |
576 | { | 624 | { |
577 | int index; | 625 | int index; |
578 | u32 f; | 626 | u32 f; |
579 | u64 tmp1, tmp2; | 627 | u64 tmp1, tmp2; |
580 | 628 | ||
581 | if (p < TFRC_CALC_X_SPLIT) | 629 | /* check against invalid parameters and divide-by-zero */ |
582 | index = (p / (TFRC_CALC_X_SPLIT / TFRC_CALC_X_ARRSIZE)) - 1; | 630 | BUG_ON(p > 1000000); /* p must not exceed 100% */ |
583 | else | 631 | BUG_ON(p == 0); /* f(0) = 0, divide by zero */ |
584 | index = (p / (1000000 / TFRC_CALC_X_ARRSIZE)) - 1; | 632 | if (R == 0) { /* possible divide by zero */ |
633 | DCCP_CRIT("WARNING: RTT is 0, returning maximum X_calc."); | ||
634 | return ~0U; | ||
635 | } | ||
585 | 636 | ||
586 | if (index < 0) | 637 | if (p <= TFRC_CALC_X_SPLIT) { /* 0.0000 < p <= 0.05 */ |
587 | /* p should be 0 unless there is a bug in my code */ | 638 | if (p < TFRC_SMALLEST_P) { /* 0.0000 < p < 0.0001 */ |
588 | index = 0; | 639 | DCCP_WARN("Value of p (%d) below resolution. " |
640 | "Substituting %d\n", p, TFRC_SMALLEST_P); | ||
641 | index = 0; | ||
642 | } else /* 0.0001 <= p <= 0.05 */ | ||
643 | index = p/TFRC_SMALLEST_P - 1; | ||
589 | 644 | ||
590 | if (R == 0) { | 645 | f = tfrc_calc_x_lookup[index][1]; |
591 | DCCP_WARN("RTT==0, setting to 1\n"); | ||
592 | R = 1; /* RTT can't be zero or else divide by zero */ | ||
593 | } | ||
594 | 646 | ||
595 | BUG_ON(index >= TFRC_CALC_X_ARRSIZE); | 647 | } else { /* 0.05 < p <= 1.00 */ |
648 | index = p/(1000000/TFRC_CALC_X_ARRSIZE) - 1; | ||
596 | 649 | ||
597 | if (p >= TFRC_CALC_X_SPLIT) | ||
598 | f = tfrc_calc_x_lookup[index][0]; | 650 | f = tfrc_calc_x_lookup[index][0]; |
599 | else | 651 | } |
600 | f = tfrc_calc_x_lookup[index][1]; | ||
601 | 652 | ||
653 | /* The following computes X = s/(R*f(p)) in bytes per second. Since f(p) | ||
654 | * and R are both scaled by 1000000, we need to multiply by 1000000^2. | ||
655 | * ==> DO NOT alter this unless you test against overflow on 32 bit */ | ||
602 | tmp1 = ((u64)s * 100000000); | 656 | tmp1 = ((u64)s * 100000000); |
603 | tmp2 = ((u64)R * (u64)f); | 657 | tmp2 = ((u64)R * (u64)f); |
604 | do_div(tmp2, 10000); | 658 | do_div(tmp2, 10000); |
605 | do_div(tmp1, tmp2); | 659 | do_div(tmp1, tmp2); |
606 | /* Don't alter above math unless you test due to overflow on 32 bit */ | ||
607 | 660 | ||
608 | return (u32)tmp1; | 661 | return (u32)tmp1; |
609 | } | 662 | } |
@@ -611,33 +664,36 @@ u32 tfrc_calc_x(u16 s, u32 R, u32 p) | |||
611 | EXPORT_SYMBOL_GPL(tfrc_calc_x); | 664 | EXPORT_SYMBOL_GPL(tfrc_calc_x); |
612 | 665 | ||
613 | /* | 666 | /* |
614 | * args: fvalue - function value to match | 667 | * tfrc_calc_x_reverse_lookup - try to find p given f(p) |
615 | * returns: p closest to that value | ||
616 | * | 668 | * |
617 | * both fvalue and p are multiplied by 1,000,000 to use ints | 669 | * @fvalue: function value to match, scaled by 1000000 |
670 | * Returns closest match for p, also scaled by 1000000 | ||
618 | */ | 671 | */ |
619 | u32 tfrc_calc_x_reverse_lookup(u32 fvalue) | 672 | u32 tfrc_calc_x_reverse_lookup(u32 fvalue) |
620 | { | 673 | { |
621 | int ctr = 0; | 674 | int index; |
622 | int small; | ||
623 | 675 | ||
624 | if (fvalue < tfrc_calc_x_lookup[0][1]) | 676 | if (fvalue == 0) /* f(p) = 0 whenever p = 0 */ |
625 | return 0; | 677 | return 0; |
626 | 678 | ||
627 | if (fvalue <= tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE - 1][1]) | 679 | /* Error cases. */ |
628 | small = 1; | 680 | if (fvalue < tfrc_calc_x_lookup[0][1]) { |
629 | else if (fvalue > tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE - 1][0]) | 681 | DCCP_WARN("fvalue %d smaller than resolution\n", fvalue); |
682 | return tfrc_calc_x_lookup[0][1]; | ||
683 | } | ||
684 | if (fvalue > tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE - 1][0]) { | ||
685 | DCCP_WARN("fvalue %d exceeds bounds!\n", fvalue); | ||
630 | return 1000000; | 686 | return 1000000; |
631 | else | 687 | } |
632 | small = 0; | ||
633 | |||
634 | while (fvalue > tfrc_calc_x_lookup[ctr][small]) | ||
635 | ctr++; | ||
636 | 688 | ||
637 | if (small) | 689 | if (fvalue <= tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE - 1][1]) { |
638 | return TFRC_CALC_X_SPLIT * ctr / TFRC_CALC_X_ARRSIZE; | 690 | index = tfrc_binsearch(fvalue, 1); |
639 | else | 691 | return (index + 1) * TFRC_CALC_X_SPLIT / TFRC_CALC_X_ARRSIZE; |
640 | return 1000000 * ctr / TFRC_CALC_X_ARRSIZE; | 692 | } |
693 | |||
694 | /* else ... it must be in the coarse-grained column */ | ||
695 | index = tfrc_binsearch(fvalue, 0); | ||
696 | return (index + 1) * 1000000 / TFRC_CALC_X_ARRSIZE; | ||
641 | } | 697 | } |
642 | 698 | ||
643 | EXPORT_SYMBOL_GPL(tfrc_calc_x_reverse_lookup); | 699 | EXPORT_SYMBOL_GPL(tfrc_calc_x_reverse_lookup); |