aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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 /drivers
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>
Diffstat (limited to 'drivers')
-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
5 files changed, 30 insertions, 21 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 {