aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/xfrm.h15
-rw-r--r--net/xfrm/xfrm_state.c52
2 files changed, 44 insertions, 23 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 2933d7474a79..4bb94992b5fa 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -120,9 +120,11 @@ extern struct mutex xfrm_cfg_mutex;
120/* Full description of state of transformer. */ 120/* Full description of state of transformer. */
121struct xfrm_state 121struct xfrm_state
122{ 122{
123 /* Note: bydst is re-used during gc */
124 struct list_head all; 123 struct list_head all;
125 struct hlist_node bydst; 124 union {
125 struct list_head gclist;
126 struct hlist_node bydst;
127 };
126 struct hlist_node bysrc; 128 struct hlist_node bysrc;
127 struct hlist_node byspi; 129 struct hlist_node byspi;
128 130
@@ -1286,16 +1288,9 @@ static inline void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
1286 walk->count = 0; 1288 walk->count = 0;
1287} 1289}
1288 1290
1289static inline void xfrm_state_walk_done(struct xfrm_state_walk *walk)
1290{
1291 if (walk->state != NULL) {
1292 xfrm_state_put(walk->state);
1293 walk->state = NULL;
1294 }
1295}
1296
1297extern int xfrm_state_walk(struct xfrm_state_walk *walk, 1291extern int xfrm_state_walk(struct xfrm_state_walk *walk,
1298 int (*func)(struct xfrm_state *, int, void*), void *); 1292 int (*func)(struct xfrm_state *, int, void*), void *);
1293extern void xfrm_state_walk_done(struct xfrm_state_walk *walk);
1299extern struct xfrm_state *xfrm_state_alloc(void); 1294extern struct xfrm_state *xfrm_state_alloc(void);
1300extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, 1295extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
1301 struct flowi *fl, struct xfrm_tmpl *tmpl, 1296 struct flowi *fl, struct xfrm_tmpl *tmpl,
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{