BUG/MINOR: quic: Lack of precision when computing K (cubic only cc)

K cubic variable is stored in ms. But it was a formula with the second as unit
for the window difference parameter which was used to compute K without
considering the loss of information. Then the result was converted in ms (K *= 1000).
This leaded to a lack of precision and multiples of 1000 as values.

To fix this, use the same formula but with the window difference in ms as parameter
passed to the cubic function and remove the conversion.

Must be backported as far as 2.6.

(cherry picked from commit a6d40e09f7c9ebb99d5f08630a6482fbb7d04d26)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 3b51c3db6f62e40b42ef537b6a3dabffad760d7c)
[wt: adj ctx]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit 642bddc3eb5d0494b82430aa17ae1f5e633e02d3)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/quic_cc_cubic.c b/src/quic_cc_cubic.c
index da6029d..fd7a514 100644
--- a/src/quic_cc_cubic.c
+++ b/src/quic_cc_cubic.c
@@ -237,13 +237,24 @@
 			c->W_target = path->cwnd;
 		}
 		else {
+			uint64_t wnd_diff;
+
 			/* K value computing (in seconds):
 			 * K = cubic_root((W_max - cwnd_epoch)/C) (Figure 2)
-			 * Note that K is stored in milliseconds.
+			 * Note that K is stored in milliseconds and that
+			 * 8000 * 125000 = 1000^3.
+			 *
+			 * Supporting 2^40 windows, shifted by 10, leaves ~13 bits of unused
+			 * precision. We exploit this precision for our NS conversion by
+			 * multiplying by 8000 without overflowing, then later by 125000
+			 * after the divide so that we limit the precision loss to the minimum
+			 * before the cubic_root() call."
 			 */
-			c->K = cubic_root(((c->last_w_max - path->cwnd) << CUBIC_SCALE_FACTOR_SHIFT) / (CUBIC_C_SCALED * path->mtu));
-			/* Convert to miliseconds. */
-			c->K *= 1000;
+			wnd_diff = (c->last_w_max - path->cwnd) << CUBIC_SCALE_FACTOR_SHIFT;
+			wnd_diff *= 8000ULL;
+			wnd_diff /= CUBIC_C_SCALED * path->mtu;
+			wnd_diff *= 125000ULL;
+			c->K = cubic_root(wnd_diff);
 			c->W_target = c->last_w_max;
 		}