int             (*dump)(struct sk_buff * skb, struct netlink_callback *cb);
        int             (*done)(struct netlink_callback *cb);
        int             family;
-       long            args[7];
+       long            args[6];
 };
 
 struct netlink_notify
 
       metrics. Plus, it will be made via sk->sk_dst_cache. Solved.
  */
 
+struct xfrm_state_walk {
+       struct list_head        all;
+       u8                      state;
+       union {
+               u8              dying;
+               u8              proto;
+       };
+       u32                     seq;
+};
+
 /* Full description of state of transformer. */
 struct xfrm_state
 {
-       struct list_head        all;
        union {
-               struct list_head        gclist;
+               struct hlist_node       gclist;
                struct hlist_node       bydst;
        };
        struct hlist_node       bysrc;
 
        u32                     genid;
 
-       /* Key manger bits */
-       struct {
-               u8              state;
-               u8              dying;
-               u32             seq;
-       } km;
+       /* Key manager bits */
+       struct xfrm_state_walk  km;
 
        /* Parameters of this state. */
        struct {
 
 #define XFRM_MAX_DEPTH         6
 
+struct xfrm_policy_walk_entry {
+       struct list_head        all;
+       u8                      dead;
+};
+
+struct xfrm_policy_walk {
+       struct xfrm_policy_walk_entry walk;
+       u8 type;
+       u32 seq;
+};
+
 struct xfrm_policy
 {
        struct xfrm_policy      *next;
-       struct list_head        bytype;
        struct hlist_node       bydst;
        struct hlist_node       byidx;
 
        struct xfrm_lifetime_cfg lft;
        struct xfrm_lifetime_cur curlft;
        struct dst_entry       *bundles;
-       u16                     family;
+       struct xfrm_policy_walk_entry walk;
        u8                      type;
        u8                      action;
        u8                      flags;
-       u8                      dead;
        u8                      xfrm_nr;
-       /* XXX 1 byte hole, try to pack */
+       u16                     family;
        struct xfrm_sec_ctx     *security;
        struct xfrm_tmpl        xfrm_vec[XFRM_MAX_DEPTH];
 };
        int priority;
 };
 
-struct xfrm_state_walk {
-       struct list_head list;
-       unsigned long genid;
-       struct xfrm_state *state;
-       int count;
-       u8 proto;
-};
-
-struct xfrm_policy_walk {
-       struct xfrm_policy *policy;
-       int count;
-       u8 type, cur_type;
-};
-
 extern void xfrm_init(void);
 extern void xfrm4_init(void);
 extern void xfrm_state_init(void);
 
 struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp);
 
-static inline void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)
-{
-       walk->cur_type = XFRM_POLICY_TYPE_MAIN;
-       walk->type = type;
-       walk->policy = NULL;
-       walk->count = 0;
-}
-
-static inline void xfrm_policy_walk_done(struct xfrm_policy_walk *walk)
-{
-       if (walk->policy != NULL) {
-               xfrm_pol_put(walk->policy);
-               walk->policy = NULL;
-       }
-}
-
+extern void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
 extern int xfrm_policy_walk(struct xfrm_policy_walk *walk,
        int (*func)(struct xfrm_policy *, int, int, void*), void *);
+extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk);
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
 struct xfrm_policy *xfrm_policy_bysel_ctx(u8 type, int dir,
                                          struct xfrm_selector *sel,
 
                        struct xfrm_policy_walk policy;
                        struct xfrm_state_walk  state;
                } u;
+               struct sk_buff  *skb;
        } dump;
 };
 
 static void pfkey_terminate_dump(struct pfkey_sock *pfk)
 {
        if (pfk->dump.dump) {
+               if (pfk->dump.skb) {
+                       kfree_skb(pfk->dump.skb);
+                       pfk->dump.skb = NULL;
+               }
                pfk->dump.done(pfk);
                pfk->dump.dump = NULL;
                pfk->dump.done = NULL;
 
 static int pfkey_do_dump(struct pfkey_sock *pfk)
 {
+       struct sadb_msg *hdr;
        int rc;
 
        rc = pfk->dump.dump(pfk);
        if (rc == -ENOBUFS)
                return 0;
 
+       if (pfk->dump.skb) {
+               if (!pfkey_can_dump(&pfk->sk))
+                       return 0;
+
+               hdr = (struct sadb_msg *) pfk->dump.skb->data;
+               hdr->sadb_msg_seq = 0;
+               hdr->sadb_msg_errno = rc;
+               pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
+                               &pfk->sk);
+               pfk->dump.skb = NULL;
+       }
+
        pfkey_terminate_dump(pfk);
        return rc;
 }
        out_hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto);
        out_hdr->sadb_msg_errno = 0;
        out_hdr->sadb_msg_reserved = 0;
-       out_hdr->sadb_msg_seq = count;
+       out_hdr->sadb_msg_seq = count + 1;
        out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
-       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk);
+
+       if (pfk->dump.skb)
+               pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
+                               &pfk->sk);
+       pfk->dump.skb = out_skb;
+
        return 0;
 }
 
        return 0;
 
 out:
-       xp->dead = 1;
+       xp->walk.dead = 1;
        xfrm_policy_destroy(xp);
        return err;
 }
        out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
        out_hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
        out_hdr->sadb_msg_errno = 0;
-       out_hdr->sadb_msg_seq = count;
+       out_hdr->sadb_msg_seq = count + 1;
        out_hdr->sadb_msg_pid = pfk->dump.msg_pid;
-       pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, &pfk->sk);
+
+       if (pfk->dump.skb)
+               pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
+                               &pfk->sk);
+       pfk->dump.skb = out_skb;
+
        return 0;
 }
 
 
 
 static DEFINE_RWLOCK(xfrm_policy_lock);
 
-static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX];
+static struct list_head xfrm_policy_all;
 unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
 EXPORT_SYMBOL(xfrm_policy_count);
 
 
        read_lock(&xp->lock);
 
-       if (xp->dead)
+       if (xp->walk.dead)
                goto out;
 
        dir = xfrm_policy_id2dir(xp->index);
        policy = kzalloc(sizeof(struct xfrm_policy), gfp);
 
        if (policy) {
-               INIT_LIST_HEAD(&policy->bytype);
+               INIT_LIST_HEAD(&policy->walk.all);
                INIT_HLIST_NODE(&policy->bydst);
                INIT_HLIST_NODE(&policy->byidx);
                rwlock_init(&policy->lock);
 
 void xfrm_policy_destroy(struct xfrm_policy *policy)
 {
-       BUG_ON(!policy->dead);
+       BUG_ON(!policy->walk.dead);
 
        BUG_ON(policy->bundles);
 
        if (del_timer(&policy->timer))
                BUG();
 
-       write_lock_bh(&xfrm_policy_lock);
-       list_del(&policy->bytype);
-       write_unlock_bh(&xfrm_policy_lock);
-
        security_xfrm_policy_free(policy->security);
        kfree(policy);
 }
        int dead;
 
        write_lock_bh(&policy->lock);
-       dead = policy->dead;
-       policy->dead = 1;
+       dead = policy->walk.dead;
+       policy->walk.dead = 1;
        write_unlock_bh(&policy->lock);
 
        if (unlikely(dead)) {
        if (delpol) {
                hlist_del(&delpol->bydst);
                hlist_del(&delpol->byidx);
+               list_del(&delpol->walk.all);
                xfrm_policy_count[dir]--;
        }
        policy->index = delpol ? delpol->index : xfrm_gen_index(policy->type, dir);
        policy->curlft.use_time = 0;
        if (!mod_timer(&policy->timer, jiffies + HZ))
                xfrm_pol_hold(policy);
-       list_add_tail(&policy->bytype, &xfrm_policy_bytype[policy->type]);
+       list_add(&policy->walk.all, &xfrm_policy_all);
        write_unlock_bh(&xfrm_policy_lock);
 
        if (delpol)
                                }
                                hlist_del(&pol->bydst);
                                hlist_del(&pol->byidx);
+                               list_del(&pol->walk.all);
                                xfrm_policy_count[dir]--;
                        }
                        ret = pol;
                                }
                                hlist_del(&pol->bydst);
                                hlist_del(&pol->byidx);
+                               list_del(&pol->walk.all);
                                xfrm_policy_count[dir]--;
                        }
                        ret = pol;
                                        continue;
                                hlist_del(&pol->bydst);
                                hlist_del(&pol->byidx);
+                               list_del(&pol->walk.all);
                                write_unlock_bh(&xfrm_policy_lock);
 
                                xfrm_audit_policy_delete(pol, 1,
                     int (*func)(struct xfrm_policy *, int, int, void*),
                     void *data)
 {
-       struct xfrm_policy *old, *pol, *last = NULL;
+       struct xfrm_policy *pol;
+       struct xfrm_policy_walk_entry *x;
        int error = 0;
 
        if (walk->type >= XFRM_POLICY_TYPE_MAX &&
            walk->type != XFRM_POLICY_TYPE_ANY)
                return -EINVAL;
 
-       if (walk->policy == NULL && walk->count != 0)
+       if (list_empty(&walk->walk.all) && walk->seq != 0)
                return 0;
 
-       old = pol = walk->policy;
-       walk->policy = NULL;
-       read_lock_bh(&xfrm_policy_lock);
-
-       for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) {
-               if (walk->type != walk->cur_type &&
-                   walk->type != XFRM_POLICY_TYPE_ANY)
+       write_lock_bh(&xfrm_policy_lock);
+       if (list_empty(&walk->walk.all))
+               x = list_first_entry(&xfrm_policy_all, struct xfrm_policy_walk_entry, all);
+       else
+               x = list_entry(&walk->walk.all, struct xfrm_policy_walk_entry, all);
+       list_for_each_entry_from(x, &xfrm_policy_all, all) {
+               if (x->dead)
                        continue;
-
-               if (pol == NULL) {
-                       pol = list_first_entry(&xfrm_policy_bytype[walk->cur_type],
-                                              struct xfrm_policy, bytype);
-               }
-               list_for_each_entry_from(pol, &xfrm_policy_bytype[walk->cur_type], bytype) {
-                       if (pol->dead)
-                               continue;
-                       if (last) {
-                               error = func(last, xfrm_policy_id2dir(last->index),
-                                            walk->count, data);
-                               if (error) {
-                                       xfrm_pol_hold(last);
-                                       walk->policy = last;
-                                       goto out;
-                               }
-                       }
-                       last = pol;
-                       walk->count++;
+               pol = container_of(x, struct xfrm_policy, walk);
+               if (walk->type != XFRM_POLICY_TYPE_ANY &&
+                   walk->type != pol->type)
+                       continue;
+               error = func(pol, xfrm_policy_id2dir(pol->index),
+                            walk->seq, data);
+               if (error) {
+                       list_move_tail(&walk->walk.all, &x->all);
+                       goto out;
                }
-               pol = NULL;
+               walk->seq++;
        }
-       if (walk->count == 0) {
+       if (walk->seq == 0) {
                error = -ENOENT;
                goto out;
        }
-       if (last)
-               error = func(last, xfrm_policy_id2dir(last->index), 0, data);
+       list_del_init(&walk->walk.all);
 out:
-       read_unlock_bh(&xfrm_policy_lock);
-       if (old != NULL)
-               xfrm_pol_put(old);
+       write_unlock_bh(&xfrm_policy_lock);
        return error;
 }
 EXPORT_SYMBOL(xfrm_policy_walk);
 
+void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type)
+{
+       INIT_LIST_HEAD(&walk->walk.all);
+       walk->walk.dead = 1;
+       walk->type = type;
+       walk->seq = 0;
+}
+EXPORT_SYMBOL(xfrm_policy_walk_init);
+
+void xfrm_policy_walk_done(struct xfrm_policy_walk *walk)
+{
+       if (list_empty(&walk->walk.all))
+               return;
+
+       write_lock_bh(&xfrm_policy_lock);
+       list_del(&walk->walk.all);
+       write_unlock_bh(&xfrm_policy_lock);
+}
+EXPORT_SYMBOL(xfrm_policy_walk_done);
+
 /*
  * Find policy to apply to this flow.
  *
        struct hlist_head *chain = policy_hash_bysel(&pol->selector,
                                                     pol->family, dir);
 
-       list_add_tail(&pol->bytype, &xfrm_policy_bytype[pol->type]);
+       list_add(&pol->walk.all, &xfrm_policy_all);
        hlist_add_head(&pol->bydst, chain);
        hlist_add_head(&pol->byidx, xfrm_policy_byidx+idx_hash(pol->index));
        xfrm_policy_count[dir]++;
 
        hlist_del(&pol->bydst);
        hlist_del(&pol->byidx);
+       list_del(&pol->walk.all);
        xfrm_policy_count[dir]--;
 
        return pol;
 
                for (pi = 0; pi < npols; pi++) {
                        read_lock_bh(&pols[pi]->lock);
-                       pol_dead |= pols[pi]->dead;
+                       pol_dead |= pols[pi]->walk.dead;
                        read_unlock_bh(&pols[pi]->lock);
                }
 
                        panic("XFRM: failed to allocate bydst hash\n");
        }
 
-       for (dir = 0; dir < XFRM_POLICY_TYPE_MAX; dir++)
-               INIT_LIST_HEAD(&xfrm_policy_bytype[dir]);
-
+       INIT_LIST_HEAD(&xfrm_policy_all);
        INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
        register_netdevice_notifier(&xfrm_dev_notifier);
 }
        int i, j, n = 0;
 
        write_lock_bh(&pol->lock);
-       if (unlikely(pol->dead)) {
+       if (unlikely(pol->walk.dead)) {
                /* target policy has been deleted */
                write_unlock_bh(&pol->lock);
                return -ENOENT;
 
 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;
-
-/* List of outstanding state walks used to set the completed counter.  */
-static LIST_HEAD(xfrm_state_walks);
-
 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 LIST_HEAD(xfrm_state_gc_leftovers);
-static LIST_HEAD(xfrm_state_gc_list);
+static HLIST_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, *tmp;
-       unsigned long completed;
+       struct xfrm_state *x;
+       struct hlist_node *entry, *tmp;
+       struct hlist_head gc_list;
 
-       mutex_lock(&xfrm_cfg_mutex);
        spin_lock_bh(&xfrm_state_gc_lock);
-       list_splice_tail_init(&xfrm_state_gc_list, &xfrm_state_gc_leftovers);
+       hlist_move_list(&xfrm_state_gc_list, &gc_list);
        spin_unlock_bh(&xfrm_state_gc_lock);
 
-       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;
-               list_del(&x->gclist);
+       hlist_for_each_entry_safe(x, entry, tmp, &gc_list, gclist)
                xfrm_state_gc_destroy(x);
-       }
 
        wake_up(&km_waitq);
 }
        if (x) {
                atomic_set(&x->refcnt, 1);
                atomic_set(&x->tunnel_users, 0);
-               INIT_LIST_HEAD(&x->all);
+               INIT_LIST_HEAD(&x->km.all);
                INIT_HLIST_NODE(&x->bydst);
                INIT_HLIST_NODE(&x->bysrc);
                INIT_HLIST_NODE(&x->byspi);
        WARN_ON(x->km.state != XFRM_STATE_DEAD);
 
        spin_lock_bh(&xfrm_state_gc_lock);
-       list_add_tail(&x->gclist, &xfrm_state_gc_list);
+       hlist_add_head(&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);
+               list_del(&x->km.all);
                hlist_del(&x->bydst);
                hlist_del(&x->bysrc);
                if (x->id.spi)
 
                if (km_query(x, tmpl, pol) == 0) {
                        x->km.state = XFRM_STATE_ACQ;
-                       list_add_tail(&x->all, &xfrm_state_all);
+                       list_add(&x->km.all, &xfrm_state_all);
                        hlist_add_head(&x->bydst, xfrm_state_bydst+h);
                        h = xfrm_src_hash(daddr, saddr, family);
                        hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
 
        x->genid = ++xfrm_state_genid;
 
-       list_add_tail(&x->all, &xfrm_state_all);
+       list_add(&x->km.all, &xfrm_state_all);
 
        h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
                          x->props.reqid, x->props.family);
                xfrm_state_hold(x);
                x->timer.expires = jiffies + sysctl_xfrm_acq_expires*HZ;
                add_timer(&x->timer);
-               list_add_tail(&x->all, &xfrm_state_all);
+               list_add(&x->km.all, &xfrm_state_all);
                hlist_add_head(&x->bydst, xfrm_state_bydst+h);
                h = xfrm_src_hash(daddr, saddr, family);
                hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);
                    int (*func)(struct xfrm_state *, int, void*),
                    void *data)
 {
-       struct xfrm_state *old, *x, *last = NULL;
+       struct xfrm_state *state;
+       struct xfrm_state_walk *x;
        int err = 0;
 
-       if (walk->state == NULL && walk->count != 0)
+       if (walk->seq != 0 && list_empty(&walk->all))
                return 0;
 
-       old = x = walk->state;
-       walk->state = NULL;
        spin_lock_bh(&xfrm_state_lock);
-       if (x == NULL)
-               x = list_first_entry(&xfrm_state_all, struct xfrm_state, all);
+       if (list_empty(&walk->all))
+               x = list_first_entry(&xfrm_state_all, struct xfrm_state_walk, all);
+       else
+               x = list_entry(&walk->all, struct xfrm_state_walk, all);
        list_for_each_entry_from(x, &xfrm_state_all, all) {
-               if (x->km.state == XFRM_STATE_DEAD)
+               if (x->state == XFRM_STATE_DEAD)
                        continue;
-               if (!xfrm_id_proto_match(x->id.proto, walk->proto))
+               state = container_of(x, struct xfrm_state, km);
+               if (!xfrm_id_proto_match(state->id.proto, walk->proto))
                        continue;
-               if (last) {
-                       err = func(last, walk->count, data);
-                       if (err) {
-                               xfrm_state_hold(last);
-                               walk->state = last;
-                               goto out;
-                       }
+               err = func(state, walk->seq, data);
+               if (err) {
+                       list_move_tail(&walk->all, &x->all);
+                       goto out;
                }
-               last = x;
-               walk->count++;
+               walk->seq++;
        }
-       if (walk->count == 0) {
+       if (walk->seq == 0) {
                err = -ENOENT;
                goto out;
        }
-       if (last)
-               err = func(last, 0, data);
+       list_del_init(&walk->all);
 out:
        spin_unlock_bh(&xfrm_state_lock);
-       if (old != NULL)
-               xfrm_state_put(old);
        return err;
 }
 EXPORT_SYMBOL(xfrm_state_walk);
 
 void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto)
 {
+       INIT_LIST_HEAD(&walk->all);
        walk->proto = proto;
-       walk->state = NULL;
-       walk->count = 0;
-       list_add_tail(&walk->list, &xfrm_state_walks);
-       walk->genid = ++xfrm_state_walk_ongoing;
+       walk->state = XFRM_STATE_DEAD;
+       walk->seq = 0;
 }
 EXPORT_SYMBOL(xfrm_state_walk_init);
 
 void xfrm_state_walk_done(struct xfrm_state_walk *walk)
 {
-       struct list_head *prev;
-
-       if (walk->state != NULL) {
-               xfrm_state_put(walk->state);
-               walk->state = NULL;
-       }
-
-       prev = walk->list.prev;
-       list_del(&walk->list);
-
-       if (prev != &xfrm_state_walks) {
-               list_entry(prev, struct xfrm_state_walk, list)->genid =
-                       walk->genid;
+       if (list_empty(&walk->all))
                return;
-       }
-
-       xfrm_state_walk_completed = walk->genid;
 
-       if (!list_empty(&xfrm_state_gc_leftovers))
-               schedule_work(&xfrm_state_gc_work);
+       spin_lock_bh(&xfrm_state_lock);
+       list_del(&walk->all);
+       spin_lock_bh(&xfrm_state_lock);
 }
 EXPORT_SYMBOL(xfrm_state_walk_done);
 
 
        return xp;
  error:
        *errp = err;
-       xp->dead = 1;
+       xp->walk.dead = 1;
        xfrm_policy_destroy(xp);
        return NULL;
 }
                return -ENOENT;
 
        read_lock(&xp->lock);
-       if (xp->dead) {
+       if (xp->walk.dead) {
                read_unlock(&xp->lock);
                goto out;
        }