]> err.no Git - linux-2.6/blobdiff - net/xfrm/xfrm_policy.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / net / xfrm / xfrm_policy.c
index d83227baaa093f1103e3593bfa3c62df6c8e09ed..bae94a8031a290d5138cb99c60f007026c11d548 100644 (file)
@@ -46,6 +46,7 @@ EXPORT_SYMBOL(xfrm_cfg_mutex);
 
 static DEFINE_RWLOCK(xfrm_policy_lock);
 
+static struct list_head xfrm_policy_bytype[XFRM_POLICY_TYPE_MAX];
 unsigned int xfrm_policy_count[XFRM_POLICY_MAX*2];
 EXPORT_SYMBOL(xfrm_policy_count);
 
@@ -208,6 +209,7 @@ struct xfrm_policy *xfrm_policy_alloc(gfp_t gfp)
        policy = kzalloc(sizeof(struct xfrm_policy), gfp);
 
        if (policy) {
+               INIT_LIST_HEAD(&policy->bytype);
                INIT_HLIST_NODE(&policy->bydst);
                INIT_HLIST_NODE(&policy->byidx);
                rwlock_init(&policy->lock);
@@ -221,7 +223,7 @@ EXPORT_SYMBOL(xfrm_policy_alloc);
 
 /* Destroy xfrm_policy: descendant resources must be released to this moment. */
 
-void __xfrm_policy_destroy(struct xfrm_policy *policy)
+void xfrm_policy_destroy(struct xfrm_policy *policy)
 {
        BUG_ON(!policy->dead);
 
@@ -230,10 +232,14 @@ void __xfrm_policy_destroy(struct xfrm_policy *policy)
        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);
        kfree(policy);
 }
-EXPORT_SYMBOL(__xfrm_policy_destroy);
+EXPORT_SYMBOL(xfrm_policy_destroy);
 
 static void xfrm_policy_gc_kill(struct xfrm_policy *policy)
 {
@@ -331,15 +337,31 @@ static void xfrm_dst_hash_transfer(struct hlist_head *list,
                                   struct hlist_head *ndsttable,
                                   unsigned int nhashmask)
 {
-       struct hlist_node *entry, *tmp;
+       struct hlist_node *entry, *tmp, *entry0 = NULL;
        struct xfrm_policy *pol;
+       unsigned int h0 = 0;
 
+redo:
        hlist_for_each_entry_safe(pol, entry, tmp, list, bydst) {
                unsigned int h;
 
                h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
                                pol->family, nhashmask);
-               hlist_add_head(&pol->bydst, ndsttable+h);
+               if (!entry0) {
+                       hlist_del(entry);
+                       hlist_add_head(&pol->bydst, ndsttable+h);
+                       h0 = h;
+               } else {
+                       if (h != h0)
+                               continue;
+                       hlist_del(entry);
+                       hlist_add_after(entry0, &pol->bydst);
+               }
+               entry0 = entry;
+       }
+       if (!hlist_empty(list)) {
+               entry0 = NULL;
+               goto redo;
        }
 }
 
@@ -568,6 +590,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
        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]);
        write_unlock_bh(&xfrm_policy_lock);
 
        if (delpol)
@@ -806,57 +829,60 @@ out:
 }
 EXPORT_SYMBOL(xfrm_policy_flush);
 
-int xfrm_policy_walk(u8 type, int (*func)(struct xfrm_policy *, int, int, void*),
+int xfrm_policy_walk(struct xfrm_policy_walk *walk,
+                    int (*func)(struct xfrm_policy *, int, int, void*),
                     void *data)
 {
-       struct xfrm_policy *pol, *last = NULL;
-       struct hlist_node *entry;
-       int dir, last_dir = 0, count, error;
+       struct xfrm_policy *old, *pol, *last = NULL;
+       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)
+               return 0;
+
+       old = pol = walk->policy;
+       walk->policy = NULL;
        read_lock_bh(&xfrm_policy_lock);
-       count = 0;
 
-       for (dir = 0; dir < 2*XFRM_POLICY_MAX; dir++) {
-               struct hlist_head *table = xfrm_policy_bydst[dir].table;
-               int i;
+       for (; walk->cur_type < XFRM_POLICY_TYPE_MAX; walk->cur_type++) {
+               if (walk->type != walk->cur_type &&
+                   walk->type != XFRM_POLICY_TYPE_ANY)
+                       continue;
 
-               hlist_for_each_entry(pol, entry,
-                                    &xfrm_policy_inexact[dir], bydst) {
-                       if (pol->type != type)
+               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, last_dir % XFRM_POLICY_MAX,
-                                            count, data);
-                               if (error)
+                               error = func(last, xfrm_policy_id2dir(last->index),
+                                            walk->count, data);
+                               if (error) {
+                                       xfrm_pol_hold(last);
+                                       walk->policy = last;
                                        goto out;
-                       }
-                       last = pol;
-                       last_dir = dir;
-                       count++;
-               }
-               for (i = xfrm_policy_bydst[dir].hmask; i >= 0; i--) {
-                       hlist_for_each_entry(pol, entry, table + i, bydst) {
-                               if (pol->type != type)
-                                       continue;
-                               if (last) {
-                                       error = func(last, last_dir % XFRM_POLICY_MAX,
-                                                    count, data);
-                                       if (error)
-                                               goto out;
                                }
-                               last = pol;
-                               last_dir = dir;
-                               count++;
                        }
+                       last = pol;
+                       walk->count++;
                }
+               pol = NULL;
        }
-       if (count == 0) {
+       if (walk->count == 0) {
                error = -ENOENT;
                goto out;
        }
-       error = func(last, last_dir % XFRM_POLICY_MAX, 0, data);
+       if (last)
+               error = func(last, xfrm_policy_id2dir(last->index), 0, data);
 out:
        read_unlock_bh(&xfrm_policy_lock);
+       if (old != NULL)
+               xfrm_pol_put(old);
        return error;
 }
 EXPORT_SYMBOL(xfrm_policy_walk);
@@ -2349,6 +2375,9 @@ static void __init xfrm_policy_init(void)
                        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_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task);
        register_netdevice_notifier(&xfrm_dev_notifier);
 }
@@ -2367,8 +2396,8 @@ void __init xfrm_init(void)
 }
 
 #ifdef CONFIG_AUDITSYSCALL
-static inline void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
-                                               struct audit_buffer *audit_buf)
+static void xfrm_audit_common_policyinfo(struct xfrm_policy *xp,
+                                        struct audit_buffer *audit_buf)
 {
        struct xfrm_sec_ctx *ctx = xp->security;
        struct xfrm_selector *sel = &xp->selector;