aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm/xfrm_state.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-09-09 22:58:29 -0400
committerDavid S. Miller <davem@davemloft.net>2008-09-09 22:58:29 -0400
commitabb81c4f3cb9b8d421f1e5474811ef1d461d341c (patch)
tree71a08df3a53eb9e19a62c673d77084e4d81f708b /net/xfrm/xfrm_state.c
parentdacc62dbf56e872ad96edde0393b9deb56d80cd5 (diff)
ipsec: Use RCU-like construct for saved state within a walk
Now that we save states within a walk we need synchronisation so that the list the saved state is on doesn't disappear from under us. As it stands this is done by keeping the state on the list which is bad because it gets in the way of the management of the state life-cycle. An alternative is to make our own pseudo-RCU system where we use counters to indicate which state can't be freed immediately as it may be referenced by an ongoing walk when that resumes. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/xfrm/xfrm_state.c')
-rw-r--r--net/xfrm/xfrm_state.c52
1 files changed, 39 insertions, 13 deletions
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 0a8f09c3144c..aaafcee02fc5 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -59,6 +59,11 @@ static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
59static unsigned int xfrm_state_num; 59static unsigned int xfrm_state_num;
60static unsigned int xfrm_state_genid; 60static unsigned int xfrm_state_genid;
61 61
62/* Counter indicating ongoing walk, protected by xfrm_state_lock. */
63static unsigned long xfrm_state_walk_ongoing;
64/* Counter indicating walk completion, protected by xfrm_cfg_mutex. */
65static unsigned long xfrm_state_walk_completed;
66
62static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); 67static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
63static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); 68static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
64 69
@@ -191,7 +196,8 @@ static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
191static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; 196static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
192 197
193static struct work_struct xfrm_state_gc_work; 198static struct work_struct xfrm_state_gc_work;
194static HLIST_HEAD(xfrm_state_gc_list); 199static LIST_HEAD(xfrm_state_gc_leftovers);
200static LIST_HEAD(xfrm_state_gc_list);
195static DEFINE_SPINLOCK(xfrm_state_gc_lock); 201static DEFINE_SPINLOCK(xfrm_state_gc_lock);
196 202
197int __xfrm_state_delete(struct xfrm_state *x); 203int __xfrm_state_delete(struct xfrm_state *x);
@@ -403,17 +409,22 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
403 409
404static void xfrm_state_gc_task(struct work_struct *data) 410static void xfrm_state_gc_task(struct work_struct *data)
405{ 411{
406 struct xfrm_state *x; 412 struct xfrm_state *x, *tmp;
407 struct hlist_node *entry, *tmp; 413 unsigned long completed;
408 struct hlist_head gc_list;
409 414
415 mutex_lock(&xfrm_cfg_mutex);
410 spin_lock_bh(&xfrm_state_gc_lock); 416 spin_lock_bh(&xfrm_state_gc_lock);
411 gc_list.first = xfrm_state_gc_list.first; 417 list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers);
412 INIT_HLIST_HEAD(&xfrm_state_gc_list);
413 spin_unlock_bh(&xfrm_state_gc_lock); 418 spin_unlock_bh(&xfrm_state_gc_lock);
414 419
415 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst) 420 completed = xfrm_state_walk_completed;
421 mutex_unlock(&xfrm_cfg_mutex);
422
423 list_for_each_entry_safe(x, tmp, &xfrm_state_gc_leftovers, gclist) {
424 if ((long)(x->lastused - completed) > 0)
425 break;
416 xfrm_state_gc_destroy(x); 426 xfrm_state_gc_destroy(x);
427 }
417 428
418 wake_up(&km_waitq); 429 wake_up(&km_waitq);
419} 430}
@@ -540,12 +551,8 @@ void __xfrm_state_destroy(struct xfrm_state *x)
540{ 551{
541 WARN_ON(x->km.state != XFRM_STATE_DEAD); 552 WARN_ON(x->km.state != XFRM_STATE_DEAD);
542 553
543 spin_lock_bh(&xfrm_state_lock);
544 list_del(&x->all);
545 spin_unlock_bh(&xfrm_state_lock);
546
547 spin_lock_bh(&xfrm_state_gc_lock); 554 spin_lock_bh(&xfrm_state_gc_lock);
548 hlist_add_head(&x->bydst, &xfrm_state_gc_list); 555 list_add_tail(&x->gclist, &xfrm_state_gc_list);
549 spin_unlock_bh(&xfrm_state_gc_lock); 556 spin_unlock_bh(&xfrm_state_gc_lock);
550 schedule_work(&xfrm_state_gc_work); 557 schedule_work(&xfrm_state_gc_work);
551} 558}
@@ -558,6 +565,8 @@ int __xfrm_state_delete(struct xfrm_state *x)
558 if (x->km.state != XFRM_STATE_DEAD) { 565 if (x->km.state != XFRM_STATE_DEAD) {
559 x->km.state = XFRM_STATE_DEAD; 566 x->km.state = XFRM_STATE_DEAD;
560 spin_lock(&xfrm_state_lock); 567 spin_lock(&xfrm_state_lock);
568 x->lastused = xfrm_state_walk_ongoing;
569 list_del_rcu(&x->all);
561 hlist_del(&x->bydst); 570 hlist_del(&x->bydst);
562 hlist_del(&x->bysrc); 571 hlist_del(&x->bysrc);
563 if (x->id.spi) 572 if (x->id.spi)
@@ -1574,6 +1583,7 @@ int xfrm_state_walk(struct xfrm_state_walk *walk,
1574 if (err) { 1583 if (err) {
1575 xfrm_state_hold(last); 1584 xfrm_state_hold(last);
1576 walk->state = last; 1585 walk->state = last;
1586 xfrm_state_walk_ongoing++;
1577 goto out; 1587 goto out;
1578 } 1588 }
1579 } 1589 }
@@ -1588,12 +1598,28 @@ int xfrm_state_walk(struct xfrm_state_walk *walk,
1588 err = func(last, 0, data); 1598 err = func(last, 0, data);
1589out: 1599out:
1590 spin_unlock_bh(&xfrm_state_lock); 1600 spin_unlock_bh(&xfrm_state_lock);
1591 if (old != NULL) 1601 if (old != NULL) {
1592 xfrm_state_put(old); 1602 xfrm_state_put(old);
1603 xfrm_state_walk_completed++;
1604 if (!list_empty(&xfrm_state_gc_leftovers))
1605 schedule_work(&xfrm_state_gc_work);
1606 }
1593 return err; 1607 return err;
1594} 1608}
1595EXPORT_SYMBOL(xfrm_state_walk); 1609EXPORT_SYMBOL(xfrm_state_walk);
1596 1610
1611void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1612{
1613 if (walk->state != NULL) {
1614 xfrm_state_put(walk->state);
1615 walk->state = NULL;
1616 xfrm_state_walk_completed++;
1617 if (!list_empty(&xfrm_state_gc_leftovers))
1618 schedule_work(&xfrm_state_gc_work);
1619 }
1620}
1621EXPORT_SYMBOL(xfrm_state_walk_done);
1622
1597 1623
1598void xfrm_replay_notify(struct xfrm_state *x, int event) 1624void xfrm_replay_notify(struct xfrm_state *x, int event)
1599{ 1625{