Skip to content

Commit

Permalink
bonding: Fix jiffies overflow problems (again)
Browse files Browse the repository at this point in the history
The time_before_eq()/time_after_eq() functions operate on unsigned
long and only work if the difference between the two compared values
is smaller than half the range of unsigned long (31 bits on i386).

Some of the variables (slave->jiffies, dev->trans_start, dev->last_rx)
used by bonding store a copy of jiffies and may not be updated for a
long time. With HZ=1000, time_before_eq()/time_after_eq() will start
giving bad results after ~25 days.

jiffies will never be before slave->jiffies, dev->trans_start,
dev->last_rx by more than possibly a couple ticks caused by preemption
of this code. This allows us to detect/prevent these overflows by
replacing time_before_eq()/time_after_eq() with time_in_range().

Signed-off-by: Jiri Bohac <jbohac@suse.cz>
Signed-off-by: Jean Delvare <jdelvare@suse.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jiri Bohac authored and David S. Miller committed Sep 7, 2010
1 parent c4433be commit cb32f2a
Showing 1 changed file with 39 additions and 17 deletions.
56 changes: 39 additions & 17 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2797,9 +2797,15 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
* so it can wait
*/
bond_for_each_slave(bond, slave, i) {
unsigned long trans_start = dev_trans_start(slave->dev);

if (slave->link != BOND_LINK_UP) {
if (time_before_eq(jiffies, dev_trans_start(slave->dev) + delta_in_ticks) &&
time_before_eq(jiffies, slave->dev->last_rx + delta_in_ticks)) {
if (time_in_range(jiffies,
trans_start - delta_in_ticks,
trans_start + delta_in_ticks) &&
time_in_range(jiffies,
slave->dev->last_rx - delta_in_ticks,
slave->dev->last_rx + delta_in_ticks)) {

slave->link = BOND_LINK_UP;
slave->state = BOND_STATE_ACTIVE;
Expand Down Expand Up @@ -2827,8 +2833,12 @@ void bond_loadbalance_arp_mon(struct work_struct *work)
* when the source ip is 0, so don't take the link down
* if we don't know our ip yet
*/
if (time_after_eq(jiffies, dev_trans_start(slave->dev) + 2*delta_in_ticks) ||
(time_after_eq(jiffies, slave->dev->last_rx + 2*delta_in_ticks))) {
if (!time_in_range(jiffies,
trans_start - delta_in_ticks,
trans_start + 2 * delta_in_ticks) ||
!time_in_range(jiffies,
slave->dev->last_rx - delta_in_ticks,
slave->dev->last_rx + 2 * delta_in_ticks)) {

slave->link = BOND_LINK_DOWN;
slave->state = BOND_STATE_BACKUP;
Expand Down Expand Up @@ -2883,13 +2893,16 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
{
struct slave *slave;
int i, commit = 0;
unsigned long trans_start;

bond_for_each_slave(bond, slave, i) {
slave->new_link = BOND_LINK_NOCHANGE;

if (slave->link != BOND_LINK_UP) {
if (time_before_eq(jiffies, slave_last_rx(bond, slave) +
delta_in_ticks)) {
if (time_in_range(jiffies,
slave_last_rx(bond, slave) - delta_in_ticks,
slave_last_rx(bond, slave) + delta_in_ticks)) {

slave->new_link = BOND_LINK_UP;
commit++;
}
Expand All @@ -2902,8 +2915,9 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
* active. This avoids bouncing, as the last receive
* times need a full ARP monitor cycle to be updated.
*/
if (!time_after_eq(jiffies, slave->jiffies +
2 * delta_in_ticks))
if (time_in_range(jiffies,
slave->jiffies - delta_in_ticks,
slave->jiffies + 2 * delta_in_ticks))
continue;

/*
Expand All @@ -2921,8 +2935,10 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
*/
if (slave->state == BOND_STATE_BACKUP &&
!bond->current_arp_slave &&
time_after(jiffies, slave_last_rx(bond, slave) +
3 * delta_in_ticks)) {
!time_in_range(jiffies,
slave_last_rx(bond, slave) - delta_in_ticks,
slave_last_rx(bond, slave) + 3 * delta_in_ticks)) {

slave->new_link = BOND_LINK_DOWN;
commit++;
}
Expand All @@ -2933,11 +2949,15 @@ static int bond_ab_arp_inspect(struct bonding *bond, int delta_in_ticks)
* - (more than 2*delta since receive AND
* the bond has an IP address)
*/
trans_start = dev_trans_start(slave->dev);
if ((slave->state == BOND_STATE_ACTIVE) &&
(time_after_eq(jiffies, dev_trans_start(slave->dev) +
2 * delta_in_ticks) ||
(time_after_eq(jiffies, slave_last_rx(bond, slave)
+ 2 * delta_in_ticks)))) {
(!time_in_range(jiffies,
trans_start - delta_in_ticks,
trans_start + 2 * delta_in_ticks) ||
!time_in_range(jiffies,
slave_last_rx(bond, slave) - delta_in_ticks,
slave_last_rx(bond, slave) + 2 * delta_in_ticks))) {

slave->new_link = BOND_LINK_DOWN;
commit++;
}
Expand All @@ -2956,17 +2976,19 @@ static void bond_ab_arp_commit(struct bonding *bond, int delta_in_ticks)
{
struct slave *slave;
int i;
unsigned long trans_start;

bond_for_each_slave(bond, slave, i) {
switch (slave->new_link) {
case BOND_LINK_NOCHANGE:
continue;

case BOND_LINK_UP:
trans_start = dev_trans_start(slave->dev);
if ((!bond->curr_active_slave &&
time_before_eq(jiffies,
dev_trans_start(slave->dev) +
delta_in_ticks)) ||
time_in_range(jiffies,
trans_start - delta_in_ticks,
trans_start + delta_in_ticks)) ||
bond->curr_active_slave != slave) {
slave->link = BOND_LINK_UP;
bond->current_arp_slave = NULL;
Expand Down

0 comments on commit cb32f2a

Please sign in to comment.