[IPV4] ROUTE: Convert rt_hash_lock_init() macro into function
[safe/jmp/linux-2.6] / net / ipv4 / route.c
index a21021b..d95e48e 100644 (file)
@@ -133,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;
 
 /*
@@ -234,16 +235,25 @@ struct rt_hash_bucket {
 
 static spinlock_t      *rt_hash_locks;
 # define rt_hash_lock_addr(slot) &rt_hash_locks[(slot) & (RT_HASH_LOCK_SZ - 1)]
-# define rt_hash_lock_init()   { \
-               int i; \
-               rt_hash_locks = kmalloc(sizeof(spinlock_t) * RT_HASH_LOCK_SZ, GFP_KERNEL); \
-               if (!rt_hash_locks) panic("IP: failed to allocate rt_hash_locks\n"); \
-               for (i = 0; i < RT_HASH_LOCK_SZ; i++) \
-                       spin_lock_init(&rt_hash_locks[i]); \
-               }
+
+static __init void rt_hash_lock_init(void)
+{
+       int i;
+
+       rt_hash_locks = kmalloc(sizeof(spinlock_t) * RT_HASH_LOCK_SZ,
+                       GFP_KERNEL);
+       if (!rt_hash_locks)
+               panic("IP: failed to allocate rt_hash_locks\n");
+
+       for (i = 0; i < RT_HASH_LOCK_SZ; i++)
+               spin_lock_init(&rt_hash_locks[i]);
+}
 #else
 # define rt_hash_lock_addr(slot) NULL
-# define rt_hash_lock_init()
+
+static inline void rt_hash_lock_init(void)
+{
+}
 #endif
 
 static struct rt_hash_bucket   *rt_hash_table;
@@ -480,6 +490,83 @@ static const struct file_operations rt_cpu_seq_fops = {
        .release = seq_release,
 };
 
+#ifdef CONFIG_NET_CLS_ROUTE
+static int ip_rt_acct_read(char *buffer, char **start, off_t offset,
+                          int length, int *eof, void *data)
+{
+       unsigned int i;
+
+       if ((offset & 3) || (length & 3))
+               return -EIO;
+
+       if (offset >= sizeof(struct ip_rt_acct) * 256) {
+               *eof = 1;
+               return 0;
+       }
+
+       if (offset + length >= sizeof(struct ip_rt_acct) * 256) {
+               length = sizeof(struct ip_rt_acct) * 256 - offset;
+               *eof = 1;
+       }
+
+       offset /= sizeof(u32);
+
+       if (length > 0) {
+               u32 *dst = (u32 *) buffer;
+
+               *start = buffer;
+               memset(dst, 0, length);
+
+               for_each_possible_cpu(i) {
+                       unsigned int j;
+                       u32 *src;
+
+                       src = ((u32 *) per_cpu_ptr(ip_rt_acct, i)) + offset;
+                       for (j = 0; j < length/4; j++)
+                               dst[j] += src[j];
+               }
+       }
+       return length;
+}
+#endif
+
+static __init int ip_rt_proc_init(struct net *net)
+{
+       struct proc_dir_entry *pde;
+
+       pde = proc_net_fops_create(net, "rt_cache", S_IRUGO,
+                       &rt_cache_seq_fops);
+       if (!pde)
+               goto err1;
+
+       pde = create_proc_entry("rt_cache", S_IRUGO, net->proc_net_stat);
+       if (!pde)
+               goto err2;
+
+       pde->proc_fops = &rt_cpu_seq_fops;
+
+#ifdef CONFIG_NET_CLS_ROUTE
+       pde = create_proc_read_entry("rt_acct", 0, net->proc_net,
+                       ip_rt_acct_read, NULL);
+       if (!pde)
+               goto err3;
+#endif
+       return 0;
+
+#ifdef CONFIG_NET_CLS_ROUTE
+err3:
+       remove_proc_entry("rt_cache", net->proc_net_stat);
+#endif
+err2:
+       remove_proc_entry("rt_cache", net->proc_net);
+err1:
+       return -ENOMEM;
+}
+#else
+static inline int ip_rt_proc_init(struct net *net)
+{
+       return 0;
+}
 #endif /* CONFIG_PROC_FS */
 
 static __inline__ void rt_free(struct rtable *rt)
@@ -561,7 +648,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;
@@ -607,33 +723,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);
@@ -667,7 +783,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;
        }
 
@@ -678,12 +794,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);
 }
 
 /*
@@ -2527,6 +2648,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;
@@ -2536,6 +2658,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;
@@ -2606,7 +2731,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;
 
@@ -2859,48 +2984,6 @@ ctl_table ipv4_route_table[] = {
 
 #ifdef CONFIG_NET_CLS_ROUTE
 struct ip_rt_acct *ip_rt_acct __read_mostly;
-
-/* IP route accounting ptr for this logical cpu number. */
-#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,
-                          int length, int *eof, void *data)
-{
-       unsigned int i;
-
-       if ((offset & 3) || (length & 3))
-               return -EIO;
-
-       if (offset >= sizeof(struct ip_rt_acct) * 256) {
-               *eof = 1;
-               return 0;
-       }
-
-       if (offset + length >= sizeof(struct ip_rt_acct) * 256) {
-               length = sizeof(struct ip_rt_acct) * 256 - offset;
-               *eof = 1;
-       }
-
-       offset /= sizeof(u32);
-
-       if (length > 0) {
-               u32 *dst = (u32 *) buffer;
-
-               *start = buffer;
-               memset(dst, 0, length);
-
-               for_each_possible_cpu(i) {
-                       unsigned int j;
-                       u32 *src = ((u32 *) IP_RT_ACCT_CPU(i)) + offset;
-
-                       for (j = 0; j < length/4; j++)
-                               dst[j] += src[j];
-               }
-       }
-       return length;
-}
-#endif /* CONFIG_PROC_FS */
 #endif /* CONFIG_NET_CLS_ROUTE */
 
 static __initdata unsigned long rhash_entries;
@@ -2964,20 +3047,8 @@ int __init ip_rt_init(void)
                ip_rt_secret_interval;
        add_timer(&rt_secret_timer);
 
-#ifdef CONFIG_PROC_FS
-       {
-       struct proc_dir_entry *rtstat_pde = NULL; /* keep gcc happy */
-       if (!proc_net_fops_create(&init_net, "rt_cache", S_IRUGO, &rt_cache_seq_fops) ||
-           !(rtstat_pde = create_proc_entry("rt_cache", S_IRUGO,
-                                            init_net.proc_net_stat))) {
-               return -ENOMEM;
-       }
-       rtstat_pde->proc_fops = &rt_cpu_seq_fops;
-       }
-#ifdef CONFIG_NET_CLS_ROUTE
-       create_proc_read_entry("rt_acct", 0, init_net.proc_net, ip_rt_acct_read, NULL);
-#endif
-#endif
+       if (ip_rt_proc_init(&init_net))
+               printk(KERN_ERR "Unable to create route proc files\n");
 #ifdef CONFIG_XFRM
        xfrm_init();
        xfrm4_init();