[IPV4]: Remove unused ip_options->is_data.
[safe/jmp/linux-2.6] / net / ipv4 / fib_hash.c
index 499522f..8d58d85 100644 (file)
@@ -52,6 +52,7 @@ struct fib_node {
        struct hlist_node       fn_hash;
        struct list_head        fn_alias;
        __be32                  fn_key;
+       struct fib_alias        fn_embedded_alias;
 };
 
 struct fn_zone {
@@ -193,10 +194,13 @@ static inline void fn_free_node(struct fib_node * f)
        kmem_cache_free(fn_hash_kmem, f);
 }
 
-static inline void fn_free_alias(struct fib_alias *fa)
+static inline void fn_free_alias(struct fib_alias *fa, struct fib_node *f)
 {
        fib_release_info(fa->fa_info);
-       kmem_cache_free(fn_alias_kmem, fa);
+       if (fa == &f->fn_embedded_alias)
+               fa->fa_info = NULL;
+       else
+               kmem_cache_free(fn_alias_kmem, fa);
 }
 
 static struct fn_zone *
@@ -368,7 +372,8 @@ static struct fib_node *fib_find_node(struct fn_zone *fz, __be32 key)
 static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
 {
        struct fn_hash *table = (struct fn_hash *) tb->tb_data;
-       struct fib_node *new_f, *f;
+       struct fib_node *new_f = NULL;
+       struct fib_node *f;
        struct fib_alias *fa, *new_fa;
        struct fn_zone *fz;
        struct fib_info *fi;
@@ -420,19 +425,43 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
 
        if (fa && fa->fa_tos == tos &&
            fa->fa_info->fib_priority == fi->fib_priority) {
-               struct fib_alias *fa_orig;
+               struct fib_alias *fa_first, *fa_match;
 
                err = -EEXIST;
                if (cfg->fc_nlflags & NLM_F_EXCL)
                        goto out;
 
+               /* We have 2 goals:
+                * 1. Find exact match for type, scope, fib_info to avoid
+                * duplicate routes
+                * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
+                */
+               fa_match = NULL;
+               fa_first = fa;
+               fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
+               list_for_each_entry_continue(fa, &f->fn_alias, fa_list) {
+                       if (fa->fa_tos != tos)
+                               break;
+                       if (fa->fa_info->fib_priority != fi->fib_priority)
+                               break;
+                       if (fa->fa_type == cfg->fc_type &&
+                           fa->fa_scope == cfg->fc_scope &&
+                           fa->fa_info == fi) {
+                               fa_match = fa;
+                               break;
+                       }
+               }
+
                if (cfg->fc_nlflags & NLM_F_REPLACE) {
                        struct fib_info *fi_drop;
                        u8 state;
 
-                       if (fi->fib_treeref > 1)
+                       fa = fa_first;
+                       if (fa_match) {
+                               if (fa == fa_match)
+                                       err = 0;
                                goto out;
-
+                       }
                        write_lock_bh(&fib_hash_lock);
                        fi_drop = fa->fa_info;
                        fa->fa_info = fi;
@@ -455,20 +484,11 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
                 * uses the same scope, type, and nexthop
                 * information.
                 */
-               fa_orig = fa;
-               fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
-               list_for_each_entry_continue(fa, &f->fn_alias, fa_list) {
-                       if (fa->fa_tos != tos)
-                               break;
-                       if (fa->fa_info->fib_priority != fi->fib_priority)
-                               break;
-                       if (fa->fa_type == cfg->fc_type &&
-                           fa->fa_scope == cfg->fc_scope &&
-                           fa->fa_info == fi)
-                               goto out;
-               }
+               if (fa_match)
+                       goto out;
+
                if (!(cfg->fc_nlflags & NLM_F_APPEND))
-                       fa = fa_orig;
+                       fa = fa_first;
        }
 
        err = -ENOENT;
@@ -476,15 +496,11 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
                goto out;
 
        err = -ENOBUFS;
-       new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
-       if (new_fa == NULL)
-               goto out;
 
-       new_f = NULL;
        if (!f) {
-               new_f = kmem_cache_alloc(fn_hash_kmem, GFP_KERNEL);
+               new_f = kmem_cache_zalloc(fn_hash_kmem, GFP_KERNEL);
                if (new_f == NULL)
-                       goto out_free_new_fa;
+                       goto out;
 
                INIT_HLIST_NODE(&new_f->fn_hash);
                INIT_LIST_HEAD(&new_f->fn_alias);
@@ -492,6 +508,12 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
                f = new_f;
        }
 
+       new_fa = &f->fn_embedded_alias;
+       if (new_fa->fa_info != NULL) {
+               new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
+               if (new_fa == NULL)
+                       goto out;
+       }
        new_fa->fa_info = fi;
        new_fa->fa_tos = tos;
        new_fa->fa_type = cfg->fc_type;
@@ -518,9 +540,9 @@ static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
                  &cfg->fc_nlinfo, 0);
        return 0;
 
-out_free_new_fa:
-       kmem_cache_free(fn_alias_kmem, new_fa);
 out:
+       if (new_f)
+               kmem_cache_free(fn_hash_kmem, new_f);
        fib_release_info(fi);
        return err;
 }
@@ -595,7 +617,7 @@ static int fn_hash_delete(struct fib_table *tb, struct fib_config *cfg)
 
                if (fa->fa_state & FA_S_ACCESSED)
                        rt_cache_flush(-1);
-               fn_free_alias(fa);
+               fn_free_alias(fa, f);
                if (kill_fn) {
                        fn_free_node(f);
                        fz->fz_nent--;
@@ -631,7 +653,7 @@ static int fn_flush_list(struct fn_zone *fz, int idx)
                                fib_hash_genid++;
                                write_unlock_bh(&fib_hash_lock);
 
-                               fn_free_alias(fa);
+                               fn_free_alias(fa, f);
                                found++;
                        }
                }
@@ -750,10 +772,10 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
 void __init fib_hash_init(void)
 {
        fn_hash_kmem = kmem_cache_create("ip_fib_hash", sizeof(struct fib_node),
-                                        0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+                                        0, SLAB_PANIC, NULL);
 
        fn_alias_kmem = kmem_cache_create("ip_fib_alias", sizeof(struct fib_alias),
-                                         0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+                                         0, SLAB_PANIC, NULL);
 
 }