Skip to content

Commit

Permalink
xfrm: SAD entries do not expire correctly after suspend-resume
Browse files Browse the repository at this point in the history
  This fixes the following bug in the current implementation of
net/xfrm: SAD entries timeouts do not count the time spent by the machine 
in the suspended state. This leads to the connectivity problems because 
after resuming local machine thinks that the SAD entry is still valid, while 
it has already been expired on the remote server.

  The cause of this is very simple: the timeouts in the net/xfrm are bound to 
the old mod_timer() timers. This patch reassigns them to the
CLOCK_REALTIME hrtimer.

  I have been using this version of the patch for a few months on my
machines without any problems. Also run a few stress tests w/o any
issues.

  This version of the patch uses tasklet_hrtimer by Peter Zijlstra
(commit 9ba5f0).

  This patch is against 2.6.31.4. Please CC me.

Signed-off-by: Yury Polyanskiy <polyanskiy@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Yury Polyanskiy authored and David S. Miller committed Nov 9, 2009
1 parent 7a50a24 commit 9e0d57f
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 14 deletions.
5 changes: 4 additions & 1 deletion include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <net/route.h>
#include <net/ipv6.h>
#include <net/ip6_fib.h>

#include <linux/interrupt.h>

#ifdef CONFIG_XFRM_STATISTICS
#include <net/snmp.h>
#endif
Expand Down Expand Up @@ -198,7 +201,7 @@ struct xfrm_state {
struct xfrm_stats stats;

struct xfrm_lifetime_cur curlft;
struct timer_list timer;
struct tasklet_hrtimer mtimer;

/* Last used time */
unsigned long lastused;
Expand Down
30 changes: 17 additions & 13 deletions net/xfrm/xfrm_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include <linux/cache.h>
#include <linux/audit.h>
#include <asm/uaccess.h>
#include <linux/ktime.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>

#include "xfrm_hash.h"

Expand Down Expand Up @@ -352,7 +355,7 @@ static void xfrm_put_mode(struct xfrm_mode *mode)

static void xfrm_state_gc_destroy(struct xfrm_state *x)
{
del_timer_sync(&x->timer);
tasklet_hrtimer_cancel(&x->mtimer);
del_timer_sync(&x->rtimer);
kfree(x->aalg);
kfree(x->ealg);
Expand Down Expand Up @@ -398,9 +401,10 @@ static inline unsigned long make_jiffies(long secs)
return secs*HZ;
}

static void xfrm_timer_handler(unsigned long data)
static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
{
struct xfrm_state *x = (struct xfrm_state*)data;
struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer);
struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer);
struct net *net = xs_net(x);
unsigned long now = get_seconds();
long next = LONG_MAX;
Expand Down Expand Up @@ -451,8 +455,9 @@ static void xfrm_timer_handler(unsigned long data)
if (warn)
km_state_expired(x, 0, 0);
resched:
if (next != LONG_MAX)
mod_timer(&x->timer, jiffies + make_jiffies(next));
if (next != LONG_MAX){
tasklet_hrtimer_start(&x->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL);
}

goto out;

Expand All @@ -474,6 +479,7 @@ static void xfrm_timer_handler(unsigned long data)

out:
spin_unlock(&x->lock);
return HRTIMER_NORESTART;
}

static void xfrm_replay_timer_handler(unsigned long data);
Expand All @@ -492,7 +498,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
INIT_HLIST_NODE(&x->bydst);
INIT_HLIST_NODE(&x->bysrc);
INIT_HLIST_NODE(&x->byspi);
setup_timer(&x->timer, xfrm_timer_handler, (unsigned long)x);
tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler, CLOCK_REALTIME, HRTIMER_MODE_ABS);
setup_timer(&x->rtimer, xfrm_replay_timer_handler,
(unsigned long)x);
x->curlft.add_time = get_seconds();
Expand Down Expand Up @@ -843,8 +849,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
}
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
add_timer(&x->timer);
tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
net->xfrm.state_num++;
xfrm_hash_grow_check(net, x->bydst.next != NULL);
} else {
Expand Down Expand Up @@ -921,7 +926,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
}

mod_timer(&x->timer, jiffies + HZ);
tasklet_hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
if (x->replay_maxage)
mod_timer(&x->rtimer, jiffies + x->replay_maxage);

Expand Down Expand Up @@ -1019,8 +1024,7 @@ static struct xfrm_state *__find_acq_core(struct net *net, unsigned short family
x->props.reqid = reqid;
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
xfrm_state_hold(x);
x->timer.expires = jiffies + net->xfrm.sysctl_acq_expires*HZ;
add_timer(&x->timer);
tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
list_add(&x->km.all, &net->xfrm.state_all);
hlist_add_head(&x->bydst, net->xfrm.state_bydst+h);
h = xfrm_src_hash(net, daddr, saddr, family);
Expand Down Expand Up @@ -1300,7 +1304,7 @@ int xfrm_state_update(struct xfrm_state *x)
memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
x1->km.dying = 0;

mod_timer(&x1->timer, jiffies + HZ);
tasklet_hrtimer_start(&x1->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
if (x1->curlft.use_time)
xfrm_state_check_expire(x1);

Expand All @@ -1325,7 +1329,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
if (x->curlft.bytes >= x->lft.hard_byte_limit ||
x->curlft.packets >= x->lft.hard_packet_limit) {
x->km.state = XFRM_STATE_EXPIRED;
mod_timer(&x->timer, jiffies);
tasklet_hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL);
return -EINVAL;
}

Expand Down

0 comments on commit 9e0d57f

Please sign in to comment.