aboutsummaryrefslogtreecommitdiffstats
path: root/net/xfrm/xfrm_state.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-10-01 10:03:24 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-01 10:03:24 -0400
commit12a169e7d8f4b1c95252d8b04ed0f1033ed7cfe2 (patch)
tree9630d7798d4fdfc06d6001ccd057aff68f39f908 /net/xfrm/xfrm_state.c
parentb262e60309e1b0eb25d300c7e739427d5316abb1 (diff)
ipsec: Put dumpers on the dump list
Herbert Xu came up with the idea and the original patch to make xfrm_state dump list contain also dumpers: As it is we go to extraordinary lengths to ensure that states don't go away while dumpers go to sleep. It's much easier if we just put the dumpers themselves on the list since they can't go away while they're going. I've also changed the order of addition on new states to prevent a never-ending dump. Timo Teräs improved the patch to apply cleanly to latest tree, modified iteration code to be more readable by using a common struct for entries in the list, implemented the same idea for xfrm_policy dumping and moved the af_key specific "last" entry caching to af_key. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Timo Teras <timo.teras@iki.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/xfrm/xfrm_state.c')
-rw-r--r--net/xfrm/xfrm_state.c109
1 files changed, 36 insertions, 73 deletions
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 053970e8765d..747fd8c291a7 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -59,14 +59,6 @@ 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
67/* List of outstanding state walks used to set the completed counter. */
68static LIST_HEAD(xfrm_state_walks);
69
70static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); 62static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
71static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); 63static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
72 64
@@ -199,8 +191,7 @@ static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
199static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; 191static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
200 192
201static struct work_struct xfrm_state_gc_work; 193static struct work_struct xfrm_state_gc_work;
202static LIST_HEAD(xfrm_state_gc_leftovers); 194static HLIST_HEAD(xfrm_state_gc_list);
203static LIST_HEAD(xfrm_state_gc_list);
204static DEFINE_SPINLOCK(xfrm_state_gc_lock); 195static DEFINE_SPINLOCK(xfrm_state_gc_lock);
205 196
206int __xfrm_state_delete(struct xfrm_state *x); 197int __xfrm_state_delete(struct xfrm_state *x);
@@ -412,23 +403,16 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
412 403
413static void xfrm_state_gc_task(struct work_struct *data) 404static void xfrm_state_gc_task(struct work_struct *data)
414{ 405{
415 struct xfrm_state *x, *tmp; 406 struct xfrm_state *x;
416 unsigned long completed; 407 struct hlist_node *entry, *tmp;
408 struct hlist_head gc_list;
417 409
418 mutex_lock(&xfrm_cfg_mutex);
419 spin_lock_bh(&xfrm_state_gc_lock); 410 spin_lock_bh(&xfrm_state_gc_lock);
420 list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers); 411 hlist_move_list(&xfrm_state_gc_list, &gc_list);
421 spin_unlock_bh(&xfrm_state_gc_lock); 412 spin_unlock_bh(&xfrm_state_gc_lock);
422 413
423 completed = xfrm_state_walk_completed; 414 hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
424 mutex_unlock(&xfrm_cfg_mutex);
425
426 list_for_each_entry_safe(x, tmp, &xfrm_state_gc_leftovers, gclist) {
427 if ((long)(x->lastused - completed) > 0)
428 break;
429 list_del(&x->gclist);
430 xfrm_state_gc_destroy(x); 415 xfrm_state_gc_destroy(x);
431 }
432 416
433 wake_up(&km_waitq); 417 wake_up(&km_waitq);
434} 418}
@@ -529,7 +513,7 @@ struct xfrm_state *xfrm_state_alloc(void)
529 if (x) { 513 if (x) {
530 atomic_set(&x->refcnt, 1); 514 atomic_set(&x->refcnt, 1);
531 atomic_set(&x->tunnel_users, 0); 515 atomic_set(&x->tunnel_users, 0);
532 INIT_LIST_HEAD(&x->all); 516 INIT_LIST_HEAD(&x->km.all);
533 INIT_HLIST_NODE(&x->bydst); 517 INIT_HLIST_NODE(&x->bydst);
534 INIT_HLIST_NODE(&x->bysrc); 518 INIT_HLIST_NODE(&x->bysrc);
535 INIT_HLIST_NODE(&x->byspi); 519 INIT_HLIST_NODE(&x->byspi);
@@ -556,7 +540,7 @@ void __xfrm_state_destroy(struct xfrm_state *x)
556 WARN_ON(x->km.state != XFRM_STATE_DEAD); 540 WARN_ON(x->km.state != XFRM_STATE_DEAD);
557 541
558 spin_lock_bh(&xfrm_state_gc_lock); 542 spin_lock_bh(&xfrm_state_gc_lock);
559 list_add_tail(&x->gclist, &xfrm_state_gc_list); 543 hlist_add_head(&x->gclist, &xfrm_state_gc_list);
560 spin_unlock_bh(&xfrm_state_gc_lock); 544 spin_unlock_bh(&xfrm_state_gc_lock);
561 schedule_work(&xfrm_state_gc_work); 545 schedule_work(&xfrm_state_gc_work);
562} 546}
@@ -569,8 +553,7 @@ int __xfrm_state_delete(struct xfrm_state *x)
569 if (x->km.state != XFRM_STATE_DEAD) { 553 if (x->km.state != XFRM_STATE_DEAD) {
570 x->km.state = XFRM_STATE_DEAD; 554 x->km.state = XFRM_STATE_DEAD;
571 spin_lock(&xfrm_state_lock); 555 spin_lock(&xfrm_state_lock);
572 x->lastused = xfrm_state_walk_ongoing; 556 list_del(&x->km.all);
573 list_del_rcu(&x->all);
574 hlist_del(&x->bydst); 557 hlist_del(&x->bydst);
575 hlist_del(&x->bysrc); 558 hlist_del(&x->bysrc);
576 if (x->id.spi) 559 if (x->id.spi)
@@ -871,7 +854,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
871 854
872 if (km_query(x, tmpl, pol) == 0) { 855 if (km_query(x, tmpl, pol) == 0) {
873 x->km.state = XFRM_STATE_ACQ; 856 x->km.state = XFRM_STATE_ACQ;
874 list_add_tail(&x->all, &xfrm_state_all); 857 list_add(&x->km.all, &xfrm_state_all);
875 hlist_add_head(&x->bydst, xfrm_state_bydst+h); 858 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
876 h = xfrm_src_hash(daddr, saddr, family); 859 h = xfrm_src_hash(daddr, saddr, family);
877 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 860 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
@@ -940,7 +923,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
940 923
941 x->genid = ++xfrm_state_genid; 924 x->genid = ++xfrm_state_genid;
942 925
943 list_add_tail(&x->all, &xfrm_state_all); 926 list_add(&x->km.all, &xfrm_state_all);
944 927
945 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr, 928 h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
946 x->props.reqid, x->props.family); 929 x->props.reqid, x->props.family);
@@ -1069,7 +1052,7 @@ static struct xfrm_state *__find_acq_core(unsigned short family, u8 mode, u32 re
1069 xfrm_state_hold(x); 1052 xfrm_state_hold(x);
1070 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ; 1053 x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
1071 add_timer(&x->timer); 1054 add_timer(&x->timer);
1072 list_add_tail(&x->all, &xfrm_state_all); 1055 list_add(&x->km.all, &xfrm_state_all);
1073 hlist_add_head(&x->bydst, xfrm_state_bydst+h); 1056 hlist_add_head(&x->bydst, xfrm_state_bydst+h);
1074 h = xfrm_src_hash(daddr, saddr, family); 1057 h = xfrm_src_hash(daddr, saddr, family);
1075 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h); 1058 hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
@@ -1566,79 +1549,59 @@ int xfrm_state_walk(struct xfrm_state_walk *walk,
1566 int (*func)(struct xfrm_state *, int, void*), 1549 int (*func)(struct xfrm_state *, int, void*),
1567 void *data) 1550 void *data)
1568{ 1551{
1569 struct xfrm_state *old, *x, *last = NULL; 1552 struct xfrm_state *state;
1553 struct xfrm_state_walk *x;
1570 int err = 0; 1554 int err = 0;
1571 1555
1572 if (walk->state == NULL && walk->count != 0) 1556 if (walk->seq != 0 && list_empty(&walk->all))
1573 return 0; 1557 return 0;
1574 1558
1575 old = x = walk->state;
1576 walk->state = NULL;
1577 spin_lock_bh(&xfrm_state_lock); 1559 spin_lock_bh(&xfrm_state_lock);
1578 if (x == NULL) 1560 if (list_empty(&walk->all))
1579 x = list_first_entry(&xfrm_state_all, struct xfrm_state, all); 1561 x = list_first_entry(&xfrm_state_all, struct xfrm_state_walk, all);
1562 else
1563 x = list_entry(&walk->all, struct xfrm_state_walk, all);
1580 list_for_each_entry_from(x, &xfrm_state_all, all) { 1564 list_for_each_entry_from(x, &xfrm_state_all, all) {
1581 if (x->km.state == XFRM_STATE_DEAD) 1565 if (x->state == XFRM_STATE_DEAD)
1582 continue; 1566 continue;
1583 if (!xfrm_id_proto_match(x->id.proto, walk->proto)) 1567 state = container_of(x, struct xfrm_state, km);
1568 if (!xfrm_id_proto_match(state->id.proto, walk->proto))
1584 continue; 1569 continue;
1585 if (last) { 1570 err = func(state, walk->seq, data);
1586 err = func(last, walk->count, data); 1571 if (err) {
1587 if (err) { 1572 list_move_tail(&walk->all, &x->all);
1588 xfrm_state_hold(last); 1573 goto out;
1589 walk->state = last;
1590 goto out;
1591 }
1592 } 1574 }
1593 last = x; 1575 walk->seq++;
1594 walk->count++;
1595 } 1576 }
1596 if (walk->count == 0) { 1577 if (walk->seq == 0) {
1597 err = -ENOENT; 1578 err = -ENOENT;
1598 goto out; 1579 goto out;
1599 } 1580 }
1600 if (last) 1581 list_del_init(&walk->all);
1601 err = func(last, 0, data);
1602out: 1582out:
1603 spin_unlock_bh(&xfrm_state_lock); 1583 spin_unlock_bh(&xfrm_state_lock);
1604 if (old != NULL)
1605 xfrm_state_put(old);
1606 return err; 1584 return err;
1607} 1585}
1608EXPORT_SYMBOL(xfrm_state_walk); 1586EXPORT_SYMBOL(xfrm_state_walk);
1609 1587
1610void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto) 1588void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1611{ 1589{
1590 INIT_LIST_HEAD(&walk->all);
1612 walk->proto = proto; 1591 walk->proto = proto;
1613 walk->state = NULL; 1592 walk->state = XFRM_STATE_DEAD;
1614 walk->count = 0; 1593 walk->seq = 0;
1615 list_add_tail(&walk->list, &xfrm_state_walks);
1616 walk->genid = ++xfrm_state_walk_ongoing;
1617} 1594}
1618EXPORT_SYMBOL(xfrm_state_walk_init); 1595EXPORT_SYMBOL(xfrm_state_walk_init);
1619 1596
1620void xfrm_state_walk_done(struct xfrm_state_walk *walk) 1597void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1621{ 1598{
1622 struct list_head *prev; 1599 if (list_empty(&walk->all))
1623
1624 if (walk->state != NULL) {
1625 xfrm_state_put(walk->state);
1626 walk->state = NULL;
1627 }
1628
1629 prev = walk->list.prev;
1630 list_del(&walk->list);
1631
1632 if (prev != &xfrm_state_walks) {
1633 list_entry(prev, struct xfrm_state_walk, list)->genid =
1634 walk->genid;
1635 return; 1600 return;
1636 }
1637
1638 xfrm_state_walk_completed = walk->genid;
1639 1601
1640 if (!list_empty(&xfrm_state_gc_leftovers)) 1602 spin_lock_bh(&xfrm_state_lock);
1641 schedule_work(&xfrm_state_gc_work); 1603 list_del(&walk->all);
1604 spin_lock_bh(&xfrm_state_lock);
1642} 1605}
1643EXPORT_SYMBOL(xfrm_state_walk_done); 1606EXPORT_SYMBOL(xfrm_state_walk_done);
1644 1607