aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2014-01-21 20:29:41 -0500
committerDavid S. Miller <davem@davemloft.net>2014-01-22 02:17:20 -0500
commit809fa972fd90ff27225294b17a027e908b2d7b7a (patch)
tree3bb15ec5b897df4ea197339478bb5d76049a2761
parent89770b0a69ee0e0e5e99c722192d535115f73778 (diff)
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>
-rw-r--r--drivers/net/bonding/bond_main.c24
-rw-r--r--drivers/net/bonding/bond_netlink.c4
-rw-r--r--drivers/net/bonding/bond_options.c15
-rw-r--r--drivers/net/bonding/bond_sysfs.c5
-rw-r--r--drivers/net/bonding/bonding.h3
-rw-r--r--include/linux/flex_array.h3
-rw-r--r--include/linux/reciprocal_div.h39
-rw-r--r--include/linux/slab_def.h4
-rw-r--r--include/net/red.h3
-rw-r--r--lib/flex_array.c7
-rw-r--r--lib/reciprocal_div.c24
-rw-r--r--net/sched/sch_netem.c6
12 files changed, 88 insertions, 49 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 3220b488dd1e..f100bd958b88 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -79,7 +79,6 @@
79#include <net/pkt_sched.h> 79#include <net/pkt_sched.h>
80#include <linux/rculist.h> 80#include <linux/rculist.h>
81#include <net/flow_keys.h> 81#include <net/flow_keys.h>
82#include <linux/reciprocal_div.h>
83#include "bonding.h" 82#include "bonding.h"
84#include "bond_3ad.h" 83#include "bond_3ad.h"
85#include "bond_alb.h" 84#include "bond_alb.h"
@@ -3596,8 +3595,9 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
3596 */ 3595 */
3597static u32 bond_rr_gen_slave_id(struct bonding *bond) 3596static u32 bond_rr_gen_slave_id(struct bonding *bond)
3598{ 3597{
3599 int packets_per_slave = bond->params.packets_per_slave;
3600 u32 slave_id; 3598 u32 slave_id;
3599 struct reciprocal_value reciprocal_packets_per_slave;
3600 int packets_per_slave = bond->params.packets_per_slave;
3601 3601
3602 switch (packets_per_slave) { 3602 switch (packets_per_slave) {
3603 case 0: 3603 case 0:
@@ -3607,8 +3607,10 @@ static u32 bond_rr_gen_slave_id(struct bonding *bond)
3607 slave_id = bond->rr_tx_counter; 3607 slave_id = bond->rr_tx_counter;
3608 break; 3608 break;
3609 default: 3609 default:
3610 reciprocal_packets_per_slave =
3611 bond->params.reciprocal_packets_per_slave;
3610 slave_id = reciprocal_divide(bond->rr_tx_counter, 3612 slave_id = reciprocal_divide(bond->rr_tx_counter,
3611 packets_per_slave); 3613 reciprocal_packets_per_slave);
3612 break; 3614 break;
3613 } 3615 }
3614 bond->rr_tx_counter++; 3616 bond->rr_tx_counter++;
@@ -4343,10 +4345,18 @@ static int bond_check_params(struct bond_params *params)
4343 params->resend_igmp = resend_igmp; 4345 params->resend_igmp = resend_igmp;
4344 params->min_links = min_links; 4346 params->min_links = min_links;
4345 params->lp_interval = lp_interval; 4347 params->lp_interval = lp_interval;
4346 if (packets_per_slave > 1) 4348 params->packets_per_slave = packets_per_slave;
4347 params->packets_per_slave = reciprocal_value(packets_per_slave); 4349 if (packets_per_slave > 0) {
4348 else 4350 params->reciprocal_packets_per_slave =
4349 params->packets_per_slave = packets_per_slave; 4351 reciprocal_value(packets_per_slave);
4352 } else {
4353 /* reciprocal_packets_per_slave is unused if
4354 * packets_per_slave is 0 or 1, just initialize it
4355 */
4356 params->reciprocal_packets_per_slave =
4357 (struct reciprocal_value) { 0 };
4358 }
4359
4350 if (primary) { 4360 if (primary) {
4351 strncpy(params->primary, primary, IFNAMSIZ); 4361 strncpy(params->primary, primary, IFNAMSIZ);
4352 params->primary[IFNAMSIZ - 1] = 0; 4362 params->primary[IFNAMSIZ - 1] = 0;
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index 21c648854a8c..e8526552790c 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -19,7 +19,6 @@
19#include <linux/if_ether.h> 19#include <linux/if_ether.h>
20#include <net/netlink.h> 20#include <net/netlink.h>
21#include <net/rtnetlink.h> 21#include <net/rtnetlink.h>
22#include <linux/reciprocal_div.h>
23#include "bonding.h" 22#include "bonding.h"
24 23
25int bond_get_slave(struct net_device *slave_dev, struct sk_buff *skb) 24int bond_get_slave(struct net_device *slave_dev, struct sk_buff *skb)
@@ -452,9 +451,6 @@ static int bond_fill_info(struct sk_buff *skb,
452 goto nla_put_failure; 451 goto nla_put_failure;
453 452
454 packets_per_slave = bond->params.packets_per_slave; 453 packets_per_slave = bond->params.packets_per_slave;
455 if (packets_per_slave > 1)
456 packets_per_slave = reciprocal_value(packets_per_slave);
457
458 if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE, 454 if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE,
459 packets_per_slave)) 455 packets_per_slave))
460 goto nla_put_failure; 456 goto nla_put_failure;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 945a6668da83..85e434886f2e 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -16,7 +16,6 @@
16#include <linux/netdevice.h> 16#include <linux/netdevice.h>
17#include <linux/rwlock.h> 17#include <linux/rwlock.h>
18#include <linux/rcupdate.h> 18#include <linux/rcupdate.h>
19#include <linux/reciprocal_div.h>
20#include "bonding.h" 19#include "bonding.h"
21 20
22int bond_option_mode_set(struct bonding *bond, int mode) 21int bond_option_mode_set(struct bonding *bond, int mode)
@@ -671,11 +670,17 @@ int bond_option_packets_per_slave_set(struct bonding *bond,
671 pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n", 670 pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n",
672 bond->dev->name); 671 bond->dev->name);
673 672
674 if (packets_per_slave > 1) 673 bond->params.packets_per_slave = packets_per_slave;
675 bond->params.packets_per_slave = 674 if (packets_per_slave > 0) {
675 bond->params.reciprocal_packets_per_slave =
676 reciprocal_value(packets_per_slave); 676 reciprocal_value(packets_per_slave);
677 else 677 } else {
678 bond->params.packets_per_slave = packets_per_slave; 678 /* reciprocal_packets_per_slave is unused if
679 * packets_per_slave is 0 or 1, just initialize it
680 */
681 bond->params.reciprocal_packets_per_slave =
682 (struct reciprocal_value) { 0 };
683 }
679 684
680 return 0; 685 return 0;
681} 686}
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 011f163c2c67..c083e9a66ece 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -39,7 +39,6 @@
39#include <net/net_namespace.h> 39#include <net/net_namespace.h>
40#include <net/netns/generic.h> 40#include <net/netns/generic.h>
41#include <linux/nsproxy.h> 41#include <linux/nsproxy.h>
42#include <linux/reciprocal_div.h>
43 42
44#include "bonding.h" 43#include "bonding.h"
45 44
@@ -1374,10 +1373,6 @@ static ssize_t bonding_show_packets_per_slave(struct device *d,
1374{ 1373{
1375 struct bonding *bond = to_bond(d); 1374 struct bonding *bond = to_bond(d);
1376 unsigned int packets_per_slave = bond->params.packets_per_slave; 1375 unsigned int packets_per_slave = bond->params.packets_per_slave;
1377
1378 if (packets_per_slave > 1)
1379 packets_per_slave = reciprocal_value(packets_per_slave);
1380
1381 return sprintf(buf, "%u\n", packets_per_slave); 1376 return sprintf(buf, "%u\n", packets_per_slave);
1382} 1377}
1383 1378
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 8a935f8f2b3c..0a616c41dc94 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -23,6 +23,8 @@
23#include <linux/netpoll.h> 23#include <linux/netpoll.h>
24#include <linux/inetdevice.h> 24#include <linux/inetdevice.h>
25#include <linux/etherdevice.h> 25#include <linux/etherdevice.h>
26#include <linux/reciprocal_div.h>
27
26#include "bond_3ad.h" 28#include "bond_3ad.h"
27#include "bond_alb.h" 29#include "bond_alb.h"
28 30
@@ -171,6 +173,7 @@ struct bond_params {
171 int resend_igmp; 173 int resend_igmp;
172 int lp_interval; 174 int lp_interval;
173 int packets_per_slave; 175 int packets_per_slave;
176 struct reciprocal_value reciprocal_packets_per_slave;
174}; 177};
175 178
176struct bond_parm_tbl { 179struct bond_parm_tbl {
diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h
index 6843cf193a44..b6efb0c64408 100644
--- a/include/linux/flex_array.h
+++ b/include/linux/flex_array.h
@@ -2,6 +2,7 @@
2#define _FLEX_ARRAY_H 2#define _FLEX_ARRAY_H
3 3
4#include <linux/types.h> 4#include <linux/types.h>
5#include <linux/reciprocal_div.h>
5#include <asm/page.h> 6#include <asm/page.h>
6 7
7#define FLEX_ARRAY_PART_SIZE PAGE_SIZE 8#define FLEX_ARRAY_PART_SIZE PAGE_SIZE
@@ -22,7 +23,7 @@ struct flex_array {
22 int element_size; 23 int element_size;
23 int total_nr_elements; 24 int total_nr_elements;
24 int elems_per_part; 25 int elems_per_part;
25 u32 reciprocal_elems; 26 struct reciprocal_value reciprocal_elems;
26 struct flex_array_part *parts[]; 27 struct flex_array_part *parts[];
27 }; 28 };
28 /* 29 /*
diff --git a/include/linux/reciprocal_div.h b/include/linux/reciprocal_div.h
index f9c90b33285b..8c5a3fb6c6c5 100644
--- a/include/linux/reciprocal_div.h
+++ b/include/linux/reciprocal_div.h
@@ -4,29 +4,32 @@
4#include <linux/types.h> 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 11 * The assembler implementation from Agner Fog, which this code is
10 * and B is a known value (but not known at compile time) 12 * based on, can be found here:
13 * http://www.agner.org/optimize/asmlib.zip
11 * 14 *
12 * The math principle used is : 15 * This optimization for A/B is helpful if the divisor B is mostly
13 * Let RECIPROCAL_VALUE(B) be (((1LL << 32) + (B - 1))/ B) 16 * runtime invariant. The reciprocal of B is calculated in the
14 * Then A / B = (u32)(((u64)(A) * (R)) >> 32) 17 * slow-path with reciprocal_value(). The fast-path can then just use
15 * 18 * a much faster multiplication operation with a variable dividend A
16 * This replaces a divide by a multiply (and a shift), and 19 * to calculate the division A/B.
17 * is generally less expensive in CPU cycles.
18 */ 20 */
19 21
20/* 22struct reciprocal_value {
21 * Computes the reciprocal value (R) for the value B of the divisor. 23 u32 m;
22 * Should not be called before each reciprocal_divide(), 24 u8 sh1, sh2;
23 * or else the performance is slower than a normal divide. 25};
24 */
25extern u32 reciprocal_value(u32 B);
26 26
27struct reciprocal_value reciprocal_value(u32 d);
27 28
28static inline u32 reciprocal_divide(u32 A, u32 R) 29static 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 */
diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h
index 09bfffb08a56..96e8abae19a9 100644
--- a/include/linux/slab_def.h
+++ b/include/linux/slab_def.h
@@ -1,6 +1,8 @@
1#ifndef _LINUX_SLAB_DEF_H 1#ifndef _LINUX_SLAB_DEF_H
2#define _LINUX_SLAB_DEF_H 2#define _LINUX_SLAB_DEF_H
3 3
4#include <linux/reciprocal_div.h>
5
4/* 6/*
5 * Definitions unique to the original Linux SLAB allocator. 7 * Definitions unique to the original Linux SLAB allocator.
6 */ 8 */
@@ -12,7 +14,7 @@ struct kmem_cache {
12 unsigned int shared; 14 unsigned int shared;
13 15
14 unsigned int size; 16 unsigned int size;
15 u32 reciprocal_buffer_size; 17 struct reciprocal_value reciprocal_buffer_size;
16/* 2) touched by every alloc & free from the backend */ 18/* 2) touched by every alloc & free from the backend */
17 19
18 unsigned int flags; /* constant flags */ 20 unsigned int flags; /* constant flags */
diff --git a/include/net/red.h b/include/net/red.h
index 168bb2f495f2..76e0b5f922c6 100644
--- a/include/net/red.h
+++ b/include/net/red.h
@@ -130,7 +130,8 @@ struct red_parms {
130 u32 qth_max; /* Max avg length threshold: Wlog scaled */ 130 u32 qth_max; /* Max avg length threshold: Wlog scaled */
131 u32 Scell_max; 131 u32 Scell_max;
132 u32 max_P; /* probability, [0 .. 1.0] 32 scaled */ 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 u32 qth_delta; /* max_th - min_th */ 135 u32 qth_delta; /* max_th - min_th */
135 u32 target_min; /* min_th + 0.4*(max_th - min_th) */ 136 u32 target_min; /* min_th + 0.4*(max_th - min_th) */
136 u32 target_max; /* min_th + 0.6*(max_th - min_th) */ 137 u32 target_max; /* min_th + 0.6*(max_th - min_th) */
diff --git a/lib/flex_array.c b/lib/flex_array.c
index 6948a6692fc4..2eed22fa507c 100644
--- a/lib/flex_array.c
+++ b/lib/flex_array.c
@@ -90,8 +90,8 @@ struct flex_array *flex_array_alloc(int element_size, unsigned int total,
90{ 90{
91 struct flex_array *ret; 91 struct flex_array *ret;
92 int elems_per_part = 0; 92 int elems_per_part = 0;
93 int reciprocal_elems = 0;
94 int max_size = 0; 93 int max_size = 0;
94 struct reciprocal_value reciprocal_elems = { 0 };
95 95
96 if (element_size) { 96 if (element_size) {
97 elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size); 97 elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
@@ -119,6 +119,11 @@ EXPORT_SYMBOL(flex_array_alloc);
119static int fa_element_to_part_nr(struct flex_array *fa, 119static int fa_element_to_part_nr(struct flex_array *fa,
120 unsigned int element_nr) 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 return reciprocal_divide(element_nr, fa->reciprocal_elems); 127 return reciprocal_divide(element_nr, fa->reciprocal_elems);
123} 128}
124 129
diff --git a/lib/reciprocal_div.c b/lib/reciprocal_div.c
index 75510e94f7d0..464152410c51 100644
--- a/lib/reciprocal_div.c
+++ b/lib/reciprocal_div.c
@@ -1,11 +1,27 @@
1#include <linux/kernel.h>
1#include <asm/div64.h> 2#include <asm/div64.h>
2#include <linux/reciprocal_div.h> 3#include <linux/reciprocal_div.h>
3#include <linux/export.h> 4#include <linux/export.h>
4 5
5u32 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
11struct reciprocal_value reciprocal_value(u32 d)
6{ 12{
7 u64 val = (1LL << 32) + (k - 1); 13 struct reciprocal_value R;
8 do_div(val, k); 14 u64 m;
9 return (u32)val; 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}
11EXPORT_SYMBOL(reciprocal_value); 27EXPORT_SYMBOL(reciprocal_value);
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index a2bfc371b44a..de1059af6da1 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -91,7 +91,7 @@ struct netem_sched_data {
91 u64 rate; 91 u64 rate;
92 s32 packet_overhead; 92 s32 packet_overhead;
93 u32 cell_size; 93 u32 cell_size;
94 u32 cell_size_reciprocal; 94 struct reciprocal_value cell_size_reciprocal;
95 s32 cell_overhead; 95 s32 cell_overhead;
96 96
97 struct crndstate { 97 struct crndstate {
@@ -725,9 +725,11 @@ static void get_rate(struct Qdisc *sch, const struct nlattr *attr)
725 q->rate = r->rate; 725 q->rate = r->rate;
726 q->packet_overhead = r->packet_overhead; 726 q->packet_overhead = r->packet_overhead;
727 q->cell_size = r->cell_size; 727 q->cell_size = r->cell_size;
728 q->cell_overhead = r->cell_overhead;
728 if (q->cell_size) 729 if (q->cell_size)
729 q->cell_size_reciprocal = reciprocal_value(q->cell_size); 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
733static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr) 735static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)