ipv6: Refactor exception functions
[linux-2.6-microblaze.git] / net / ipv6 / route.c
index c52a7f4..8bfaa73 100644 (file)
@@ -1270,7 +1270,7 @@ static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
 {
        struct rt6_info *pcpu_rt, **p;
 
-       p = this_cpu_ptr(res->f6i->rt6i_pcpu);
+       p = this_cpu_ptr(res->nh->rt6i_pcpu);
        pcpu_rt = *p;
 
        if (pcpu_rt)
@@ -1291,7 +1291,7 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net,
        }
 
        dst_hold(&pcpu_rt->dst);
-       p = this_cpu_ptr(res->f6i->rt6i_pcpu);
+       p = this_cpu_ptr(res->nh->rt6i_pcpu);
        prev = cmpxchg(p, NULL, pcpu_rt);
        BUG_ON(prev);
 
@@ -1542,7 +1542,7 @@ out:
        return err;
 }
 
-void rt6_flush_exceptions(struct fib6_info *rt)
+static void fib6_nh_flush_exceptions(struct fib6_nh *nh, struct fib6_info *from)
 {
        struct rt6_exception_bucket *bucket;
        struct rt6_exception *rt6_ex;
@@ -1551,9 +1551,9 @@ void rt6_flush_exceptions(struct fib6_info *rt)
 
        spin_lock_bh(&rt6_exception_lock);
        /* Prevent rt6_insert_exception() to recreate the bucket list */
-       rt->exception_bucket_flushed = 1;
+       from->exception_bucket_flushed = 1;
 
-       bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
+       bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
                                    lockdep_is_held(&rt6_exception_lock));
        if (!bucket)
                goto out;
@@ -1569,6 +1569,11 @@ out:
        spin_unlock_bh(&rt6_exception_lock);
 }
 
+void rt6_flush_exceptions(struct fib6_info *f6i)
+{
+       fib6_nh_flush_exceptions(&f6i->fib6_nh, f6i);
+}
+
 /* Find cached rt in the hash table inside passed in rt
  * Caller has to hold rcu_read_lock()
  */
@@ -1615,19 +1620,14 @@ find_ex:
 }
 
 /* Remove the passed in cached rt from the hash table that contains it */
-static int rt6_remove_exception_rt(struct rt6_info *rt)
+static int fib6_nh_remove_exception(const struct fib6_info *from, int plen,
+                                   const struct rt6_info *rt)
 {
+       const struct in6_addr *src_key = NULL;
        struct rt6_exception_bucket *bucket;
-       struct in6_addr *src_key = NULL;
        struct rt6_exception *rt6_ex;
-       struct fib6_info *from;
        int err;
 
-       from = rcu_dereference(rt->from);
-       if (!from ||
-           !(rt->rt6i_flags & RTF_CACHE))
-               return -EINVAL;
-
        if (!rcu_access_pointer(from->rt6i_exception_bucket))
                return -ENOENT;
 
@@ -1635,13 +1635,12 @@ static int rt6_remove_exception_rt(struct rt6_info *rt)
        bucket = rcu_dereference_protected(from->rt6i_exception_bucket,
                                    lockdep_is_held(&rt6_exception_lock));
 #ifdef CONFIG_IPV6_SUBTREES
-       /* rt6i_src.plen != 0 indicates 'from' is in subtree
-        * and exception table is indexed by a hash of
-        * both rt6i_dst and rt6i_src.
+       /* plen != 0 indicates 'from' is in subtree and exception
+        * table is indexed by a hash of both rt6i_dst and rt6i_src.
         * Otherwise, the exception table is indexed by
         * a hash of only rt6i_dst.
         */
-       if (from->fib6_src.plen)
+       if (plen)
                src_key = &rt->rt6i_src.addr;
 #endif
        rt6_ex = __rt6_find_exception_spinlock(&bucket,
@@ -1658,31 +1657,37 @@ static int rt6_remove_exception_rt(struct rt6_info *rt)
        return err;
 }
 
+static int rt6_remove_exception_rt(struct rt6_info *rt)
+{
+       struct fib6_info *from;
+
+       from = rcu_dereference(rt->from);
+       if (!from ||
+           !(rt->rt6i_flags & RTF_CACHE))
+               return -EINVAL;
+
+       return fib6_nh_remove_exception(from, from->fib6_src.plen, rt);
+}
+
 /* Find rt6_ex which contains the passed in rt cache and
  * refresh its stamp
  */
-static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
+static void fib6_nh_update_exception(const struct fib6_info *from, int plen,
+                                    const struct rt6_info *rt)
 {
+       const struct in6_addr *src_key = NULL;
        struct rt6_exception_bucket *bucket;
-       struct in6_addr *src_key = NULL;
        struct rt6_exception *rt6_ex;
-       struct fib6_info *from;
-
-       rcu_read_lock();
-       from = rcu_dereference(rt->from);
-       if (!from || !(rt->rt6i_flags & RTF_CACHE))
-               goto unlock;
 
        bucket = rcu_dereference(from->rt6i_exception_bucket);
 
 #ifdef CONFIG_IPV6_SUBTREES
-       /* rt6i_src.plen != 0 indicates 'from' is in subtree
-        * and exception table is indexed by a hash of
-        * both rt6i_dst and rt6i_src.
+       /* plen != 0 indicates 'from' is in subtree and exception
+        * table is indexed by a hash of both rt6i_dst and rt6i_src.
         * Otherwise, the exception table is indexed by
         * a hash of only rt6i_dst.
         */
-       if (from->fib6_src.plen)
+       if (plen)
                src_key = &rt->rt6i_src.addr;
 #endif
        rt6_ex = __rt6_find_exception_rcu(&bucket,
@@ -1690,7 +1695,19 @@ static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
                                          src_key);
        if (rt6_ex)
                rt6_ex->stamp = jiffies;
+}
+
+static void rt6_update_exception_stamp_rt(struct rt6_info *rt)
+{
+       struct fib6_info *from;
+
+       rcu_read_lock();
+
+       from = rcu_dereference(rt->from);
+       if (!from || !(rt->rt6i_flags & RTF_CACHE))
+               goto unlock;
 
+       fib6_nh_update_exception(from, from->fib6_src.plen, rt);
 unlock:
        rcu_read_unlock();
 }
@@ -1827,9 +1844,9 @@ static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
        gc_args->more++;
 }
 
-void rt6_age_exceptions(struct fib6_info *rt,
-                       struct fib6_gc_args *gc_args,
-                       unsigned long now)
+static void fib6_nh_age_exceptions(struct fib6_info *rt,
+                                  struct fib6_gc_args *gc_args,
+                                  unsigned long now)
 {
        struct rt6_exception_bucket *bucket;
        struct rt6_exception *rt6_ex;
@@ -1858,6 +1875,13 @@ void rt6_age_exceptions(struct fib6_info *rt,
        rcu_read_unlock_bh();
 }
 
+void rt6_age_exceptions(struct fib6_info *rt,
+                       struct fib6_gc_args *gc_args,
+                       unsigned long now)
+{
+       fib6_nh_age_exceptions(rt, gc_args, now);
+}
+
 /* must be called with rcu lock held */
 int fib6_table_lookup(struct net *net, struct fib6_table *table, int oif,
                      struct flowi6 *fl6, struct fib6_result *res, int strict)
@@ -3068,6 +3092,12 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
            !netif_carrier_ok(dev))
                fib6_nh->fib_nh_flags |= RTNH_F_LINKDOWN;
 
+       fib6_nh->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags);
+       if (!fib6_nh->rt6i_pcpu) {
+               err = -ENOMEM;
+               goto out;
+       }
+
        err = fib_nh_common_init(&fib6_nh->nh_common, cfg->fc_encap,
                                 cfg->fc_encap_type, cfg, gfp_flags, extack);
        if (err)
@@ -3092,6 +3122,25 @@ out:
 
 void fib6_nh_release(struct fib6_nh *fib6_nh)
 {
+       if (fib6_nh->rt6i_pcpu) {
+               int cpu;
+
+               for_each_possible_cpu(cpu) {
+                       struct rt6_info **ppcpu_rt;
+                       struct rt6_info *pcpu_rt;
+
+                       ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
+                       pcpu_rt = *ppcpu_rt;
+                       if (pcpu_rt) {
+                               dst_dev_put(&pcpu_rt->dst);
+                               dst_release(&pcpu_rt->dst);
+                               *ppcpu_rt = NULL;
+                       }
+               }
+
+               free_percpu(fib6_nh->rt6i_pcpu);
+       }
+
        fib_nh_common_release(&fib6_nh->nh_common);
 }
 
@@ -4173,9 +4222,36 @@ void rt6_disable_ip(struct net_device *dev, unsigned long event)
 struct rt6_mtu_change_arg {
        struct net_device *dev;
        unsigned int mtu;
+       struct fib6_info *f6i;
 };
 
-static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
+static int fib6_nh_mtu_change(struct fib6_info *f6i, void *_arg)
+{
+       struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *)_arg;
+       struct fib6_nh *nh = &f6i->fib6_nh;
+
+       /* For administrative MTU increase, there is no way to discover
+        * IPv6 PMTU increase, so PMTU increase should be updated here.
+        * Since RFC 1981 doesn't include administrative MTU increase
+        * update PMTU increase is a MUST. (i.e. jumbo frame)
+        */
+       if (nh->fib_nh_dev == arg->dev) {
+               struct inet6_dev *idev = __in6_dev_get(arg->dev);
+               u32 mtu = f6i->fib6_pmtu;
+
+               if (mtu >= arg->mtu ||
+                   (mtu < arg->mtu && mtu == idev->cnf.mtu6))
+                       fib6_metric_set(f6i, RTAX_MTU, arg->mtu);
+
+               spin_lock_bh(&rt6_exception_lock);
+               rt6_exceptions_update_pmtu(idev, f6i, arg->mtu);
+               spin_unlock_bh(&rt6_exception_lock);
+       }
+
+       return 0;
+}
+
+static int rt6_mtu_change_route(struct fib6_info *f6i, void *p_arg)
 {
        struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg;
        struct inet6_dev *idev;
@@ -4190,24 +4266,11 @@ static int rt6_mtu_change_route(struct fib6_info *rt, void *p_arg)
        if (!idev)
                return 0;
 
-       /* For administrative MTU increase, there is no way to discover
-          IPv6 PMTU increase, so PMTU increase should be updated here.
-          Since RFC 1981 doesn't include administrative MTU increase
-          update PMTU increase is a MUST. (i.e. jumbo frame)
-        */
-       if (rt->fib6_nh.fib_nh_dev == arg->dev &&
-           !fib6_metric_locked(rt, RTAX_MTU)) {
-               u32 mtu = rt->fib6_pmtu;
-
-               if (mtu >= arg->mtu ||
-                   (mtu < arg->mtu && mtu == idev->cnf.mtu6))
-                       fib6_metric_set(rt, RTAX_MTU, arg->mtu);
+       if (fib6_metric_locked(f6i, RTAX_MTU))
+               return 0;
 
-               spin_lock_bh(&rt6_exception_lock);
-               rt6_exceptions_update_pmtu(idev, rt, arg->mtu);
-               spin_unlock_bh(&rt6_exception_lock);
-       }
-       return 0;
+       arg->f6i = f6i;
+       return fib6_nh_mtu_change(f6i, arg);
 }
 
 void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
@@ -4221,6 +4284,7 @@ void rt6_mtu_change(struct net_device *dev, unsigned int mtu)
 }
 
 static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
+       [RTA_UNSPEC]            = { .strict_start_type = RTA_DPORT + 1 },
        [RTA_GATEWAY]           = { .len = sizeof(struct in6_addr) },
        [RTA_PREFSRC]           = { .len = sizeof(struct in6_addr) },
        [RTA_OIF]               = { .type = NLA_U32 },