diff options
-rw-r--r-- | include/net/xfrm.h | 15 | ||||
-rw-r--r-- | net/xfrm/xfrm_state.c | 52 |
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. */ |
121 | struct xfrm_state | 121 | struct 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 | ||
1289 | static 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 | |||
1297 | extern int xfrm_state_walk(struct xfrm_state_walk *walk, | 1291 | extern 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 *); |
1293 | extern void xfrm_state_walk_done(struct xfrm_state_walk *walk); | ||
1299 | extern struct xfrm_state *xfrm_state_alloc(void); | 1294 | extern struct xfrm_state *xfrm_state_alloc(void); |
1300 | extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr, | 1295 | extern 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; | |||
59 | static unsigned int xfrm_state_num; | 59 | static unsigned int xfrm_state_num; |
60 | static unsigned int xfrm_state_genid; | 60 | static unsigned int xfrm_state_genid; |
61 | 61 | ||
62 | /* Counter indicating ongoing walk, protected by xfrm_state_lock. */ | ||
63 | static unsigned long xfrm_state_walk_ongoing; | ||
64 | /* Counter indicating walk completion, protected by xfrm_cfg_mutex. */ | ||
65 | static unsigned long xfrm_state_walk_completed; | ||
66 | |||
62 | static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); | 67 | static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); |
63 | static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); | 68 | static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); |
64 | 69 | ||
@@ -191,7 +196,8 @@ static DEFINE_RWLOCK(xfrm_state_afinfo_lock); | |||
191 | static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; | 196 | static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; |
192 | 197 | ||
193 | static struct work_struct xfrm_state_gc_work; | 198 | static struct work_struct xfrm_state_gc_work; |
194 | static HLIST_HEAD(xfrm_state_gc_list); | 199 | static LIST_HEAD(xfrm_state_gc_leftovers); |
200 | static LIST_HEAD(xfrm_state_gc_list); | ||
195 | static DEFINE_SPINLOCK(xfrm_state_gc_lock); | 201 | static DEFINE_SPINLOCK(xfrm_state_gc_lock); |
196 | 202 | ||
197 | int __xfrm_state_delete(struct xfrm_state *x); | 203 | int __xfrm_state_delete(struct xfrm_state *x); |
@@ -403,17 +409,22 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x) | |||
403 | 409 | ||
404 | static void xfrm_state_gc_task(struct work_struct *data) | 410 | static 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); |
1589 | out: | 1599 | out: |
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 | } |
1595 | EXPORT_SYMBOL(xfrm_state_walk); | 1609 | EXPORT_SYMBOL(xfrm_state_walk); |
1596 | 1610 | ||
1611 | void 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 | } | ||
1621 | EXPORT_SYMBOL(xfrm_state_walk_done); | ||
1622 | |||
1597 | 1623 | ||
1598 | void xfrm_replay_notify(struct xfrm_state *x, int event) | 1624 | void xfrm_replay_notify(struct xfrm_state *x, int event) |
1599 | { | 1625 | { |