Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / net / xfrm / xfrm_state.c
index 58f1f93..5dcc10b 100644 (file)
@@ -50,6 +50,7 @@ static DEFINE_SPINLOCK(xfrm_state_lock);
  * Main use is finding SA after policy selected tunnel or transport mode.
  * Also, it can be used by ah/esp icmp error handler to find offending SA.
  */
+static LIST_HEAD(xfrm_state_all);
 static struct hlist_head *xfrm_state_bydst __read_mostly;
 static struct hlist_head *xfrm_state_bysrc __read_mostly;
 static struct hlist_head *xfrm_state_byspi __read_mostly;
@@ -512,6 +513,7 @@ struct xfrm_state *xfrm_state_alloc(void)
        if (x) {
                atomic_set(&x->refcnt, 1);
                atomic_set(&x->tunnel_users, 0);
+               INIT_LIST_HEAD(&x->all);
                INIT_HLIST_NODE(&x->bydst);
                INIT_HLIST_NODE(&x->bysrc);
                INIT_HLIST_NODE(&x->byspi);
@@ -537,6 +539,10 @@ void __xfrm_state_destroy(struct xfrm_state *x)
 {
        BUG_TRAP(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);
        spin_unlock_bh(&xfrm_state_gc_lock);
@@ -913,6 +919,8 @@ static void __xfrm_state_insert(struct xfrm_state *x)
 
        x->genid = ++xfrm_state_genid;
 
+       list_add_tail(&x->all, &xfrm_state_all);
+
        h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
                          x->props.reqid, x->props.family);
        hlist_add_head(&x->bydst, xfrm_state_bydst+h);
@@ -1522,36 +1530,47 @@ unlock:
 }
 EXPORT_SYMBOL(xfrm_alloc_spi);
 
-int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*),
+int xfrm_state_walk(struct xfrm_state_walk *walk,
+                   int (*func)(struct xfrm_state *, int, void*),
                    void *data)
 {
-       int i;
-       struct xfrm_state *x, *last = NULL;
-       struct hlist_node *entry;
-       int count = 0;
+       struct xfrm_state *old, *x, *last = NULL;
        int err = 0;
 
+       if (walk->state == NULL && walk->count != 0)
+               return 0;
+
+       old = x = walk->state;
+       walk->state = NULL;
        spin_lock_bh(&xfrm_state_lock);
-       for (i = 0; i <= xfrm_state_hmask; i++) {
-               hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {
-                       if (!xfrm_id_proto_match(x->id.proto, proto))
-                               continue;
-                       if (last) {
-                               err = func(last, count, data);
-                               if (err)
-                                       goto out;
+       if (x == NULL)
+               x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
+       list_for_each_entry_from(x, &xfrm_state_all, all) {
+               if (x->km.state == XFRM_STATE_DEAD)
+                       continue;
+               if (!xfrm_id_proto_match(x->id.proto, walk->proto))
+                       continue;
+               if (last) {
+                       err = func(last, walk->count, data);
+                       if (err) {
+                               xfrm_state_hold(last);
+                               walk->state = last;
+                               goto out;
                        }
-                       last = x;
-                       count++;
                }
+               last = x;
+               walk->count++;
        }
-       if (count == 0) {
+       if (walk->count == 0) {
                err = -ENOENT;
                goto out;
        }
-       err = func(last, 0, data);
+       if (last)
+               err = func(last, 0, data);
 out:
        spin_unlock_bh(&xfrm_state_lock);
+       if (old != NULL)
+               xfrm_state_put(old);
        return err;
 }
 EXPORT_SYMBOL(xfrm_state_walk);