]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv4/route.c
[NETLINK]: Mark attribute construction exception unlikely
[net-next-2.6.git] / net / ipv4 / route.c
index 28484f396b048e1dd49210e5caffc24e29148f2b..fcae074b7ae4f83e87d8e41d6e48b356cea58c27 100644 (file)
@@ -92,6 +92,7 @@
 #include <linux/jhash.h>
 #include <linux/rcupdate.h>
 #include <linux/times.h>
+#include <net/dst.h>
 #include <net/net_namespace.h>
 #include <net/protocol.h>
 #include <net/ip.h>
@@ -132,13 +133,14 @@ static int ip_rt_mtu_expires              = 10 * 60 * HZ;
 static int ip_rt_min_pmtu              = 512 + 20 + 20;
 static int ip_rt_min_advmss            = 256;
 static int ip_rt_secret_interval       = 10 * 60 * HZ;
+static int ip_rt_flush_expected;
 static unsigned long rt_deadline;
 
 #define RTprint(a...)  printk(KERN_DEBUG a)
 
 static struct timer_list rt_flush_timer;
-static void rt_check_expire(struct work_struct *work);
-static DECLARE_DELAYED_WORK(expires_work, rt_check_expire);
+static void rt_worker_func(struct work_struct *work);
+static DECLARE_DELAYED_WORK(expires_work, rt_worker_func);
 static struct timer_list rt_secret_timer;
 
 /*
@@ -165,6 +167,7 @@ static struct dst_ops ipv4_dst_ops = {
        .negative_advice =      ipv4_negative_advice,
        .link_failure =         ipv4_link_failure,
        .update_pmtu =          ip_rt_update_pmtu,
+       .local_out =            ip_local_out,
        .entry_size =           sizeof(struct rtable),
 };
 
@@ -559,7 +562,36 @@ static inline int compare_keys(struct flowi *fl1, struct flowi *fl2)
                (fl1->iif ^ fl2->iif)) == 0;
 }
 
-static void rt_check_expire(struct work_struct *work)
+/*
+ * Perform a full scan of hash table and free all entries.
+ * Can be called by a softirq or a process.
+ * In the later case, we want to be reschedule if necessary
+ */
+static void rt_do_flush(int process_context)
+{
+       unsigned int i;
+       struct rtable *rth, *next;
+
+       for (i = 0; i <= rt_hash_mask; i++) {
+               if (process_context && need_resched())
+                       cond_resched();
+               rth = rt_hash_table[i].chain;
+               if (!rth)
+                       continue;
+
+               spin_lock_bh(rt_hash_lock_addr(i));
+               rth = rt_hash_table[i].chain;
+               rt_hash_table[i].chain = NULL;
+               spin_unlock_bh(rt_hash_lock_addr(i));
+
+               for (; rth; rth = next) {
+                       next = rth->u.dst.rt_next;
+                       rt_free(rth);
+               }
+       }
+}
+
+static void rt_check_expire(void)
 {
        static unsigned int rover;
        unsigned int i = rover, goal;
@@ -605,33 +637,33 @@ static void rt_check_expire(struct work_struct *work)
                spin_unlock_bh(rt_hash_lock_addr(i));
        }
        rover = i;
+}
+
+/*
+ * rt_worker_func() is run in process context.
+ * If a whole flush was scheduled, it is done.
+ * Else, we call rt_check_expire() to scan part of the hash table
+ */
+static void rt_worker_func(struct work_struct *work)
+{
+       if (ip_rt_flush_expected) {
+               ip_rt_flush_expected = 0;
+               rt_do_flush(1);
+       } else
+               rt_check_expire();
        schedule_delayed_work(&expires_work, ip_rt_gc_interval);
 }
 
 /* This can run from both BH and non-BH contexts, the latter
  * in the case of a forced flush event.
  */
-static void rt_run_flush(unsigned long dummy)
+static void rt_run_flush(unsigned long process_context)
 {
-       int i;
-       struct rtable *rth, *next;
-
        rt_deadline = 0;
 
        get_random_bytes(&rt_hash_rnd, 4);
 
-       for (i = rt_hash_mask; i >= 0; i--) {
-               spin_lock_bh(rt_hash_lock_addr(i));
-               rth = rt_hash_table[i].chain;
-               if (rth)
-                       rt_hash_table[i].chain = NULL;
-               spin_unlock_bh(rt_hash_lock_addr(i));
-
-               for (; rth; rth = next) {
-                       next = rth->u.dst.rt_next;
-                       rt_free(rth);
-               }
-       }
+       rt_do_flush(process_context);
 }
 
 static DEFINE_SPINLOCK(rt_flush_lock);
@@ -665,7 +697,7 @@ void rt_cache_flush(int delay)
 
        if (delay <= 0) {
                spin_unlock_bh(&rt_flush_lock);
-               rt_run_flush(0);
+               rt_run_flush(user_mode);
                return;
        }
 
@@ -676,12 +708,17 @@ void rt_cache_flush(int delay)
        spin_unlock_bh(&rt_flush_lock);
 }
 
+/*
+ * We change rt_hash_rnd and ask next rt_worker_func() invocation
+ * to perform a flush in process context
+ */
 static void rt_secret_rebuild(unsigned long dummy)
 {
-       unsigned long now = jiffies;
-
-       rt_cache_flush(0);
-       mod_timer(&rt_secret_timer, now + ip_rt_secret_interval);
+       get_random_bytes(&rt_hash_rnd, 4);
+       ip_rt_flush_expected = 1;
+       cancel_delayed_work(&expires_work);
+       schedule_delayed_work(&expires_work, HZ/10);
+       mod_timer(&rt_secret_timer, jiffies + ip_rt_secret_interval);
 }
 
 /*
@@ -2357,12 +2394,6 @@ static struct dst_ops ipv4_dst_blackhole_ops = {
 };
 
 
-static int ipv4_blackhole_output(struct sk_buff *skb)
-{
-       kfree_skb(skb);
-       return 0;
-}
-
 static int ipv4_dst_blackhole(struct rtable **rp, struct flowi *flp, struct sock *sk)
 {
        struct rtable *ort = *rp;
@@ -2374,8 +2405,8 @@ static int ipv4_dst_blackhole(struct rtable **rp, struct flowi *flp, struct sock
 
                atomic_set(&new->__refcnt, 1);
                new->__use = 1;
-               new->input = ipv4_blackhole_output;
-               new->output = ipv4_blackhole_output;
+               new->input = dst_discard;
+               new->output = dst_discard;
                memcpy(new->metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32));
 
                new->dev = ort->u.dst.dev;
@@ -2531,6 +2562,7 @@ nla_put_failure:
 
 static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
+       struct net *net = in_skb->sk->sk_net;
        struct rtmsg *rtm;
        struct nlattr *tb[RTA_MAX+1];
        struct rtable *rt = NULL;
@@ -2540,6 +2572,9 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
        int err;
        struct sk_buff *skb;
 
+       if (net != &init_net)
+               return -EINVAL;
+
        err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy);
        if (err < 0)
                goto errout;
@@ -2610,7 +2645,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
        if (err <= 0)
                goto errout_free;
 
-       err = rtnl_unicast(skb, NETLINK_CB(in_skb).pid);
+       err = rtnl_unicast(skb, &init_net, NETLINK_CB(in_skb).pid);
 errout:
        return err;
 
@@ -2862,12 +2897,10 @@ ctl_table ipv4_route_table[] = {
 #endif
 
 #ifdef CONFIG_NET_CLS_ROUTE
-struct ip_rt_acct *ip_rt_acct;
-
-/* This code sucks.  But you should have seen it before! --RR */
+struct ip_rt_acct *ip_rt_acct __read_mostly;
 
 /* IP route accounting ptr for this logical cpu number. */
-#define IP_RT_ACCT_CPU(i) (ip_rt_acct + i * 256)
+#define IP_RT_ACCT_CPU(cpu) (per_cpu_ptr(ip_rt_acct, cpu))
 
 #ifdef CONFIG_PROC_FS
 static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
@@ -2927,16 +2960,9 @@ int __init ip_rt_init(void)
                             (jiffies ^ (jiffies >> 7)));
 
 #ifdef CONFIG_NET_CLS_ROUTE
-       {
-       int order;
-       for (order = 0;
-            (PAGE_SIZE << order) < 256 * sizeof(struct ip_rt_acct) * NR_CPUS; order++)
-               /* NOTHING */;
-       ip_rt_acct = (struct ip_rt_acct *)__get_free_pages(GFP_KERNEL, order);
+       ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct));
        if (!ip_rt_acct)
                panic("IP: failed to allocate ip_rt_acct\n");
-       memset(ip_rt_acct, 0, PAGE_SIZE << order);
-       }
 #endif
 
        ipv4_dst_ops.kmem_cachep =
@@ -2964,10 +2990,8 @@ int __init ip_rt_init(void)
        devinet_init();
        ip_fib_init();
 
-       init_timer(&rt_flush_timer);
-       rt_flush_timer.function = rt_run_flush;
-       init_timer(&rt_secret_timer);
-       rt_secret_timer.function = rt_secret_rebuild;
+       setup_timer(&rt_flush_timer, rt_run_flush, 0);
+       setup_timer(&rt_secret_timer, rt_secret_rebuild, 0);
 
        /* All the timers, started at system startup tend
           to synchronize. Perturb it a bit.