/* Full description of state of transformer. */
struct xfrm_state
{
- /* Note: bydst is re-used during gc */
struct list_head all;
- struct hlist_node bydst;
+ union {
+ struct list_head gclist;
+ struct hlist_node bydst;
+ };
struct hlist_node bysrc;
struct hlist_node byspi;
walk->count = 0;
}
-static inline void xfrm_state_walk_done(struct xfrm_state_walk *walk)
-{
- if (walk->state != NULL) {
- xfrm_state_put(walk->state);
- walk->state = NULL;
- }
-}
-
extern int xfrm_state_walk(struct xfrm_state_walk *walk,
int (*func)(struct xfrm_state *, int, void*), void *);
+extern void xfrm_state_walk_done(struct xfrm_state_walk *walk);
extern struct xfrm_state *xfrm_state_alloc(void);
extern struct xfrm_state *xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
struct flowi *fl, struct xfrm_tmpl *tmpl,
static unsigned int xfrm_state_num;
static unsigned int xfrm_state_genid;
+/* Counter indicating ongoing walk, protected by xfrm_state_lock. */
+static unsigned long xfrm_state_walk_ongoing;
+/* Counter indicating walk completion, protected by xfrm_cfg_mutex. */
+static unsigned long xfrm_state_walk_completed;
+
static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
static struct work_struct xfrm_state_gc_work;
-static HLIST_HEAD(xfrm_state_gc_list);
+static LIST_HEAD(xfrm_state_gc_leftovers);
+static LIST_HEAD(xfrm_state_gc_list);
static DEFINE_SPINLOCK(xfrm_state_gc_lock);
int __xfrm_state_delete(struct xfrm_state *x);
static void xfrm_state_gc_task(struct work_struct *data)
{
- struct xfrm_state *x;
- struct hlist_node *entry, *tmp;
- struct hlist_head gc_list;
+ struct xfrm_state *x, *tmp;
+ unsigned long completed;
+ mutex_lock(&xfrm_cfg_mutex);
spin_lock_bh(&xfrm_state_gc_lock);
- gc_list.first = xfrm_state_gc_list.first;
- INIT_HLIST_HEAD(&xfrm_state_gc_list);
+ list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers);
spin_unlock_bh(&xfrm_state_gc_lock);
- hlist_for_each_entry_safe(x, entry, tmp, &gc_list, bydst)
+ completed = xfrm_state_walk_completed;
+ mutex_unlock(&xfrm_cfg_mutex);
+
+ list_for_each_entry_safe(x, tmp, &xfrm_state_gc_leftovers, gclist) {
+ if ((long)(x->lastused - completed) > 0)
+ break;
xfrm_state_gc_destroy(x);
+ }
wake_up(&km_waitq);
}
{
WARN_ON(x->km.state != XFRM_STATE_DEAD);
- spin_lock_bh(&xfrm_state_lock);
- list_del(&x->all);
- spin_unlock_bh(&xfrm_state_lock);
-
spin_lock_bh(&xfrm_state_gc_lock);
- hlist_add_head(&x->bydst, &xfrm_state_gc_list);
+ list_add_tail(&x->gclist, &xfrm_state_gc_list);
spin_unlock_bh(&xfrm_state_gc_lock);
schedule_work(&xfrm_state_gc_work);
}
if (x->km.state != XFRM_STATE_DEAD) {
x->km.state = XFRM_STATE_DEAD;
spin_lock(&xfrm_state_lock);
+ x->lastused = xfrm_state_walk_ongoing;
+ list_del_rcu(&x->all);
hlist_del(&x->bydst);
hlist_del(&x->bysrc);
if (x->id.spi)
if (err) {
xfrm_state_hold(last);
walk->state = last;
+ xfrm_state_walk_ongoing++;
goto out;
}
}
err = func(last, 0, data);
out:
spin_unlock_bh(&xfrm_state_lock);
- if (old != NULL)
+ if (old != NULL) {
xfrm_state_put(old);
+ xfrm_state_walk_completed++;
+ if (!list_empty(&xfrm_state_gc_leftovers))
+ schedule_work(&xfrm_state_gc_work);
+ }
return err;
}
EXPORT_SYMBOL(xfrm_state_walk);
+void xfrm_state_walk_done(struct xfrm_state_walk *walk)
+{
+ if (walk->state != NULL) {
+ xfrm_state_put(walk->state);
+ walk->state = NULL;
+ xfrm_state_walk_completed++;
+ if (!list_empty(&xfrm_state_gc_leftovers))
+ schedule_work(&xfrm_state_gc_work);
+ }
+}
+EXPORT_SYMBOL(xfrm_state_walk_done);
+
void xfrm_replay_notify(struct xfrm_state *x, int event)
{