Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

reciprocal_divide: update/correction of the algorithm

Jakub Zawadzki noticed that some divisions by reciprocal_divide()
were not correct [1][2], which he could also show with BPF code
after divisions are transformed into reciprocal_value() for runtime
invariance which can be passed to reciprocal_divide() later on;
reverse in BPF dump ended up with a different, off-by-one K in
some situations.

This has been fixed by Eric Dumazet in commit aee636c4809fa5
("bpf: do not use reciprocal divide"). This follow-up patch
improves reciprocal_value() and reciprocal_divide() to work in
all cases by using Granlund and Montgomery method, so that also
future use is safe and without any non-obvious side-effects.
Known problems with the old implementation were that division by 1
always returned 0 and some off-by-ones when the dividend and divisor
where very large. This seemed to not be problematic with its
current users, as far as we can tell. Eric Dumazet checked for
the slab usage, we cannot surely say so in the case of flex_array.
Still, in order to fix that, we propose an extension from the
original implementation from commit 6a2d7a955d8d resp. [3][4],
by using the algorithm proposed in "Division by Invariant Integers
Using Multiplication" [5], Torbjörn Granlund and Peter L.
Montgomery, that is, pseudocode for q = n/d where q, n, d is in
u32 universe:

1) Initialization:

int l = ceil(log_2 d)
uword m' = floor((1<<32)*((1<<l)-d)/d)+1
int sh_1 = min(l,1)
int sh_2 = max(l-1,0)

2) For q = n/d, all uword:

uword t = (n*m')>>32
q = (t+((n-t)>>sh_1))>>sh_2

The assembler implementation from Agner Fog [6] also helped a lot
while implementing. We have tested the implementation on x86_64,
ppc64, i686, s390x; on x86_64/haswell we're still half the latency
compared to normal divide.

Joint work with Daniel Borkmann.

[1] http://www.wireshark.org/~darkjames/reciprocal-buggy.c
[2] http://www.wireshark.org/~darkjames/set-and-dump-filter-k-bug.c
[3] https://gmplib.org/~tege/division-paper.pdf
[4] http://homepage.cs.uiowa.edu/~jones/bcd/divide.html
[5] http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.2556
[6] http://www.agner.org/optimize/asmlib.zip

Reported-by: Jakub Zawadzki <darkjames-ws@darkjames.pl>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Austin S Hemmelgarn <ahferroin7@gmail.com>
Cc: linux-kernel@vger.kernel.org
Cc: Jesse Gross <jesse@nicira.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Stephen Hemminger <stephen@networkplumber.org>
Cc: Matt Mackall <mpm@selenic.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: Andy Gospodarek <andy@greyhouse.net>
Cc: Veaceslav Falico <vfalico@redhat.com>
Cc: Jay Vosburgh <fubar@us.ibm.com>
Cc: Jakub Zawadzki <darkjames-ws@darkjames.pl>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Hannes Frederic Sowa and committed by
David S. Miller
809fa972 89770b0a

+88 -49
+17 -7
drivers/net/bonding/bond_main.c
··· 79 79 #include <net/pkt_sched.h> 80 80 #include <linux/rculist.h> 81 81 #include <net/flow_keys.h> 82 - #include <linux/reciprocal_div.h> 83 82 #include "bonding.h" 84 83 #include "bond_3ad.h" 85 84 #include "bond_alb.h" ··· 3595 3596 */ 3596 3597 static u32 bond_rr_gen_slave_id(struct bonding *bond) 3597 3598 { 3598 - int packets_per_slave = bond->params.packets_per_slave; 3599 3599 u32 slave_id; 3600 + struct reciprocal_value reciprocal_packets_per_slave; 3601 + int packets_per_slave = bond->params.packets_per_slave; 3600 3602 3601 3603 switch (packets_per_slave) { 3602 3604 case 0: ··· 3607 3607 slave_id = bond->rr_tx_counter; 3608 3608 break; 3609 3609 default: 3610 + reciprocal_packets_per_slave = 3611 + bond->params.reciprocal_packets_per_slave; 3610 3612 slave_id = reciprocal_divide(bond->rr_tx_counter, 3611 - packets_per_slave); 3613 + reciprocal_packets_per_slave); 3612 3614 break; 3613 3615 } 3614 3616 bond->rr_tx_counter++; ··· 4345 4343 params->resend_igmp = resend_igmp; 4346 4344 params->min_links = min_links; 4347 4345 params->lp_interval = lp_interval; 4348 - if (packets_per_slave > 1) 4349 - params->packets_per_slave = reciprocal_value(packets_per_slave); 4350 - else 4351 - params->packets_per_slave = packets_per_slave; 4346 + params->packets_per_slave = packets_per_slave; 4347 + if (packets_per_slave > 0) { 4348 + params->reciprocal_packets_per_slave = 4349 + reciprocal_value(packets_per_slave); 4350 + } else { 4351 + /* reciprocal_packets_per_slave is unused if 4352 + * packets_per_slave is 0 or 1, just initialize it 4353 + */ 4354 + params->reciprocal_packets_per_slave = 4355 + (struct reciprocal_value) { 0 }; 4356 + } 4357 + 4352 4358 if (primary) { 4353 4359 strncpy(params->primary, primary, IFNAMSIZ); 4354 4360 params->primary[IFNAMSIZ - 1] = 0;
-4
drivers/net/bonding/bond_netlink.c
··· 19 19 #include <linux/if_ether.h> 20 20 #include <net/netlink.h> 21 21 #include <net/rtnetlink.h> 22 - #include <linux/reciprocal_div.h> 23 22 #include "bonding.h" 24 23 25 24 int bond_get_slave(struct net_device *slave_dev, struct sk_buff *skb) ··· 451 452 goto nla_put_failure; 452 453 453 454 packets_per_slave = bond->params.packets_per_slave; 454 - if (packets_per_slave > 1) 455 - packets_per_slave = reciprocal_value(packets_per_slave); 456 - 457 455 if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE, 458 456 packets_per_slave)) 459 457 goto nla_put_failure;
+10 -5
drivers/net/bonding/bond_options.c
··· 16 16 #include <linux/netdevice.h> 17 17 #include <linux/rwlock.h> 18 18 #include <linux/rcupdate.h> 19 - #include <linux/reciprocal_div.h> 20 19 #include "bonding.h" 21 20 22 21 int bond_option_mode_set(struct bonding *bond, int mode) ··· 670 671 pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n", 671 672 bond->dev->name); 672 673 673 - if (packets_per_slave > 1) 674 - bond->params.packets_per_slave = 674 + bond->params.packets_per_slave = packets_per_slave; 675 + if (packets_per_slave > 0) { 676 + bond->params.reciprocal_packets_per_slave = 675 677 reciprocal_value(packets_per_slave); 676 - else 677 - bond->params.packets_per_slave = packets_per_slave; 678 + } else { 679 + /* reciprocal_packets_per_slave is unused if 680 + * packets_per_slave is 0 or 1, just initialize it 681 + */ 682 + bond->params.reciprocal_packets_per_slave = 683 + (struct reciprocal_value) { 0 }; 684 + } 678 685 679 686 return 0; 680 687 }
-5
drivers/net/bonding/bond_sysfs.c
··· 39 39 #include <net/net_namespace.h> 40 40 #include <net/netns/generic.h> 41 41 #include <linux/nsproxy.h> 42 - #include <linux/reciprocal_div.h> 43 42 44 43 #include "bonding.h" 45 44 ··· 1373 1374 { 1374 1375 struct bonding *bond = to_bond(d); 1375 1376 unsigned int packets_per_slave = bond->params.packets_per_slave; 1376 - 1377 - if (packets_per_slave > 1) 1378 - packets_per_slave = reciprocal_value(packets_per_slave); 1379 - 1380 1377 return sprintf(buf, "%u\n", packets_per_slave); 1381 1378 } 1382 1379
+3
drivers/net/bonding/bonding.h
··· 23 23 #include <linux/netpoll.h> 24 24 #include <linux/inetdevice.h> 25 25 #include <linux/etherdevice.h> 26 + #include <linux/reciprocal_div.h> 27 + 26 28 #include "bond_3ad.h" 27 29 #include "bond_alb.h" 28 30 ··· 173 171 int resend_igmp; 174 172 int lp_interval; 175 173 int packets_per_slave; 174 + struct reciprocal_value reciprocal_packets_per_slave; 176 175 }; 177 176 178 177 struct bond_parm_tbl {
+2 -1
include/linux/flex_array.h
··· 2 2 #define _FLEX_ARRAY_H 3 3 4 4 #include <linux/types.h> 5 + #include <linux/reciprocal_div.h> 5 6 #include <asm/page.h> 6 7 7 8 #define FLEX_ARRAY_PART_SIZE PAGE_SIZE ··· 23 22 int element_size; 24 23 int total_nr_elements; 25 24 int elems_per_part; 26 - u32 reciprocal_elems; 25 + struct reciprocal_value reciprocal_elems; 27 26 struct flex_array_part *parts[]; 28 27 }; 29 28 /*
+21 -18
include/linux/reciprocal_div.h
··· 4 4 #include <linux/types.h> 5 5 6 6 /* 7 - * This file describes reciprocical division. 7 + * This algorithm is based on the paper "Division by Invariant 8 + * Integers Using Multiplication" by Torbjörn Granlund and Peter 9 + * L. Montgomery. 8 10 * 9 - * This optimizes the (A/B) problem, when A and B are two u32 10 - * and B is a known value (but not known at compile time) 11 + * The assembler implementation from Agner Fog, which this code is 12 + * based on, can be found here: 13 + * http://www.agner.org/optimize/asmlib.zip 11 14 * 12 - * The math principle used is : 13 - * Let RECIPROCAL_VALUE(B) be (((1LL << 32) + (B - 1))/ B) 14 - * Then A / B = (u32)(((u64)(A) * (R)) >> 32) 15 - * 16 - * This replaces a divide by a multiply (and a shift), and 17 - * is generally less expensive in CPU cycles. 15 + * This optimization for A/B is helpful if the divisor B is mostly 16 + * runtime invariant. The reciprocal of B is calculated in the 17 + * slow-path with reciprocal_value(). The fast-path can then just use 18 + * a much faster multiplication operation with a variable dividend A 19 + * to calculate the division A/B. 18 20 */ 19 21 20 - /* 21 - * Computes the reciprocal value (R) for the value B of the divisor. 22 - * Should not be called before each reciprocal_divide(), 23 - * or else the performance is slower than a normal divide. 24 - */ 25 - extern u32 reciprocal_value(u32 B); 22 + struct reciprocal_value { 23 + u32 m; 24 + u8 sh1, sh2; 25 + }; 26 26 27 + struct reciprocal_value reciprocal_value(u32 d); 27 28 28 - static inline u32 reciprocal_divide(u32 A, u32 R) 29 + static inline u32 reciprocal_divide(u32 a, struct reciprocal_value R) 29 30 { 30 - return (u32)(((u64)A * R) >> 32); 31 + u32 t = (u32)(((u64)a * R.m) >> 32); 32 + return (t + ((a - t) >> R.sh1)) >> R.sh2; 31 33 } 32 - #endif 34 + 35 + #endif /* _LINUX_RECIPROCAL_DIV_H */
+3 -1
include/linux/slab_def.h
··· 1 1 #ifndef _LINUX_SLAB_DEF_H 2 2 #define _LINUX_SLAB_DEF_H 3 3 4 + #include <linux/reciprocal_div.h> 5 + 4 6 /* 5 7 * Definitions unique to the original Linux SLAB allocator. 6 8 */ ··· 14 12 unsigned int shared; 15 13 16 14 unsigned int size; 17 - u32 reciprocal_buffer_size; 15 + struct reciprocal_value reciprocal_buffer_size; 18 16 /* 2) touched by every alloc & free from the backend */ 19 17 20 18 unsigned int flags; /* constant flags */
+2 -1
include/net/red.h
··· 130 130 u32 qth_max; /* Max avg length threshold: Wlog scaled */ 131 131 u32 Scell_max; 132 132 u32 max_P; /* probability, [0 .. 1.0] 32 scaled */ 133 - u32 max_P_reciprocal; /* reciprocal_value(max_P / qth_delta) */ 133 + /* reciprocal_value(max_P / qth_delta) */ 134 + struct reciprocal_value max_P_reciprocal; 134 135 u32 qth_delta; /* max_th - min_th */ 135 136 u32 target_min; /* min_th + 0.4*(max_th - min_th) */ 136 137 u32 target_max; /* min_th + 0.6*(max_th - min_th) */
+6 -1
lib/flex_array.c
··· 90 90 { 91 91 struct flex_array *ret; 92 92 int elems_per_part = 0; 93 - int reciprocal_elems = 0; 94 93 int max_size = 0; 94 + struct reciprocal_value reciprocal_elems = { 0 }; 95 95 96 96 if (element_size) { 97 97 elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size); ··· 119 119 static int fa_element_to_part_nr(struct flex_array *fa, 120 120 unsigned int element_nr) 121 121 { 122 + /* 123 + * if element_size == 0 we don't get here, so we never touch 124 + * the zeroed fa->reciprocal_elems, which would yield invalid 125 + * results 126 + */ 122 127 return reciprocal_divide(element_nr, fa->reciprocal_elems); 123 128 } 124 129
+20 -4
lib/reciprocal_div.c
··· 1 + #include <linux/kernel.h> 1 2 #include <asm/div64.h> 2 3 #include <linux/reciprocal_div.h> 3 4 #include <linux/export.h> 4 5 5 - u32 reciprocal_value(u32 k) 6 + /* 7 + * For a description of the algorithm please have a look at 8 + * include/linux/reciprocal_div.h 9 + */ 10 + 11 + struct reciprocal_value reciprocal_value(u32 d) 6 12 { 7 - u64 val = (1LL << 32) + (k - 1); 8 - do_div(val, k); 9 - return (u32)val; 13 + struct reciprocal_value R; 14 + u64 m; 15 + int l; 16 + 17 + l = fls(d - 1); 18 + m = ((1ULL << 32) * ((1ULL << l) - d)); 19 + do_div(m, d); 20 + ++m; 21 + R.m = (u32)m; 22 + R.sh1 = min(l, 1); 23 + R.sh2 = max(l - 1, 0); 24 + 25 + return R; 10 26 } 11 27 EXPORT_SYMBOL(reciprocal_value);
+4 -2
net/sched/sch_netem.c
··· 91 91 u64 rate; 92 92 s32 packet_overhead; 93 93 u32 cell_size; 94 - u32 cell_size_reciprocal; 94 + struct reciprocal_value cell_size_reciprocal; 95 95 s32 cell_overhead; 96 96 97 97 struct crndstate { ··· 725 725 q->rate = r->rate; 726 726 q->packet_overhead = r->packet_overhead; 727 727 q->cell_size = r->cell_size; 728 + q->cell_overhead = r->cell_overhead; 728 729 if (q->cell_size) 729 730 q->cell_size_reciprocal = reciprocal_value(q->cell_size); 730 - q->cell_overhead = r->cell_overhead; 731 + else 732 + q->cell_size_reciprocal = (struct reciprocal_value) { 0 }; 731 733 } 732 734 733 735 static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)