[NETFILTER]: nf_conntrack: use RCU for conntrack hash
[safe/jmp/linux-2.6] / net / ipv4 / netfilter / nf_conntrack_l3proto_ipv4_compat.c
index 14a93a7..0ee87ed 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/percpu.h>
+#include <net/net_namespace.h>
 
 #include <linux/netfilter.h>
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_l4proto.h>
 #include <net/netfilter/nf_conntrack_expect.h>
 
-#if 0
-#define DEBUGP printk
-#else
-#define DEBUGP(format, args...)
-#endif
-
 #ifdef CONFIG_NF_CT_ACCT
 static unsigned int
 seq_print_counters(struct seq_file *s,
@@ -41,35 +36,38 @@ struct ct_iter_state {
        unsigned int bucket;
 };
 
-static struct list_head *ct_get_first(struct seq_file *seq)
+static struct hlist_node *ct_get_first(struct seq_file *seq)
 {
        struct ct_iter_state *st = seq->private;
+       struct hlist_node *n;
 
        for (st->bucket = 0;
             st->bucket < nf_conntrack_htable_size;
             st->bucket++) {
-               if (!list_empty(&nf_conntrack_hash[st->bucket]))
-                       return nf_conntrack_hash[st->bucket].next;
+               n = rcu_dereference(nf_conntrack_hash[st->bucket].first);
+               if (n)
+                       return n;
        }
        return NULL;
 }
 
-static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head)
+static struct hlist_node *ct_get_next(struct seq_file *seq,
+                                     struct hlist_node *head)
 {
        struct ct_iter_state *st = seq->private;
 
-       head = head->next;
-       while (head == &nf_conntrack_hash[st->bucket]) {
+       head = rcu_dereference(head->next);
+       while (head == NULL) {
                if (++st->bucket >= nf_conntrack_htable_size)
                        return NULL;
-               head = nf_conntrack_hash[st->bucket].next;
+               head = rcu_dereference(nf_conntrack_hash[st->bucket].first);
        }
        return head;
 }
 
-static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
+static struct hlist_node *ct_get_idx(struct seq_file *seq, loff_t pos)
 {
-       struct list_head *head = ct_get_first(seq);
+       struct hlist_node *head = ct_get_first(seq);
 
        if (head)
                while (pos && (head = ct_get_next(seq, head)))
@@ -78,8 +76,9 @@ static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
 }
 
 static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
+       __acquires(RCU)
 {
-       read_lock_bh(&nf_conntrack_lock);
+       rcu_read_lock();
        return ct_get_idx(seq, *pos);
 }
 
@@ -90,8 +89,9 @@ static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
 }
 
 static void ct_seq_stop(struct seq_file *s, void *v)
+       __releases(RCU)
 {
-       read_unlock_bh(&nf_conntrack_lock);
+       rcu_read_unlock();
 }
 
 static int ct_seq_show(struct seq_file *s, void *v)
@@ -125,10 +125,7 @@ static int ct_seq_show(struct seq_file *s, void *v)
                      ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
                return -ENOSPC;
 
-       if (l3proto->print_conntrack(s, ct))
-               return -ENOSPC;
-
-       if (l4proto->print_conntrack(s, ct))
+       if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
                return -ENOSPC;
 
        if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
@@ -169,7 +166,7 @@ static int ct_seq_show(struct seq_file *s, void *v)
        return 0;
 }
 
-static struct seq_operations ct_seq_ops = {
+static const struct seq_operations ct_seq_ops = {
        .start = ct_seq_start,
        .next  = ct_seq_next,
        .stop  = ct_seq_stop,
@@ -178,26 +175,11 @@ static struct seq_operations ct_seq_ops = {
 
 static int ct_open(struct inode *inode, struct file *file)
 {
-       struct seq_file *seq;
-       struct ct_iter_state *st;
-       int ret;
-
-       st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
-       if (st == NULL)
-               return -ENOMEM;
-       ret = seq_open(file, &ct_seq_ops);
-       if (ret)
-               goto out_free;
-       seq          = file->private_data;
-       seq->private = st;
-       memset(st, 0, sizeof(struct ct_iter_state));
-       return ret;
-out_free:
-       kfree(st);
-       return ret;
+       return seq_open_private(file, &ct_seq_ops,
+                       sizeof(struct ct_iter_state));
 }
 
-static struct file_operations ct_file_ops = {
+static const struct file_operations ct_file_ops = {
        .owner   = THIS_MODULE,
        .open    = ct_open,
        .read    = seq_read,
@@ -206,47 +188,72 @@ static struct file_operations ct_file_ops = {
 };
 
 /* expects */
-static void *exp_seq_start(struct seq_file *s, loff_t *pos)
+struct ct_expect_iter_state {
+       unsigned int bucket;
+};
+
+static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
 {
-       struct list_head *e = &nf_conntrack_expect_list;
-       loff_t i;
+       struct ct_expect_iter_state *st = seq->private;
+       struct hlist_node *n;
 
-       /* strange seq_file api calls stop even if we fail,
-        * thus we need to grab lock since stop unlocks */
-       read_lock_bh(&nf_conntrack_lock);
+       for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
+               n = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
+               if (n)
+                       return n;
+       }
+       return NULL;
+}
 
-       if (list_empty(e))
-               return NULL;
+static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
+                                            struct hlist_node *head)
+{
+       struct ct_expect_iter_state *st = seq->private;
 
-       for (i = 0; i <= *pos; i++) {
-               e = e->next;
-               if (e == &nf_conntrack_expect_list)
+       head = rcu_dereference(head->next);
+       while (head == NULL) {
+               if (++st->bucket >= nf_ct_expect_hsize)
                        return NULL;
+               head = rcu_dereference(nf_ct_expect_hash[st->bucket].first);
        }
-       return e;
+       return head;
 }
 
-static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
+static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
 {
-       struct list_head *e = v;
+       struct hlist_node *head = ct_expect_get_first(seq);
 
-       ++*pos;
-       e = e->next;
+       if (head)
+               while (pos && (head = ct_expect_get_next(seq, head)))
+                       pos--;
+       return pos ? NULL : head;
+}
 
-       if (e == &nf_conntrack_expect_list)
-               return NULL;
+static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
+       __acquires(RCU)
+{
+       rcu_read_lock();
+       return ct_expect_get_idx(seq, *pos);
+}
 
-       return e;
+static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       (*pos)++;
+       return ct_expect_get_next(seq, v);
 }
 
-static void exp_seq_stop(struct seq_file *s, void *v)
+static void exp_seq_stop(struct seq_file *seq, void *v)
+       __releases(RCU)
 {
-       read_unlock_bh(&nf_conntrack_lock);
+       rcu_read_unlock();
 }
 
 static int exp_seq_show(struct seq_file *s, void *v)
 {
-       struct nf_conntrack_expect *exp = v;
+       struct nf_conntrack_expect *exp;
+       struct hlist_node *n = v;
+
+       exp = hlist_entry(n, struct nf_conntrack_expect, hnode);
 
        if (exp->tuple.src.l3num != AF_INET)
                return 0;
@@ -266,7 +273,7 @@ static int exp_seq_show(struct seq_file *s, void *v)
        return seq_putc(s, '\n');
 }
 
-static struct seq_operations exp_seq_ops = {
+static const struct seq_operations exp_seq_ops = {
        .start = exp_seq_start,
        .next = exp_seq_next,
        .stop = exp_seq_stop,
@@ -275,15 +282,16 @@ static struct seq_operations exp_seq_ops = {
 
 static int exp_open(struct inode *inode, struct file *file)
 {
-       return seq_open(file, &exp_seq_ops);
+       return seq_open_private(file, &exp_seq_ops,
+                       sizeof(struct ct_expect_iter_state));
 }
 
-static struct file_operations ip_exp_file_ops = {
+static const struct file_operations ip_exp_file_ops = {
        .owner   = THIS_MODULE,
        .open    = exp_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
-       .release = seq_release
+       .release = seq_release_private,
 };
 
 static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
@@ -354,7 +362,7 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v)
        return 0;
 }
 
-static struct seq_operations ct_cpu_seq_ops = {
+static const struct seq_operations ct_cpu_seq_ops = {
        .start  = ct_cpu_seq_start,
        .next   = ct_cpu_seq_next,
        .stop   = ct_cpu_seq_stop,
@@ -366,7 +374,7 @@ static int ct_cpu_seq_open(struct inode *inode, struct file *file)
        return seq_open(file, &ct_cpu_seq_ops);
 }
 
-static struct file_operations ct_cpu_seq_fops = {
+static const struct file_operations ct_cpu_seq_fops = {
        .owner   = THIS_MODULE,
        .open    = ct_cpu_seq_open,
        .read    = seq_read,
@@ -378,16 +386,16 @@ int __init nf_conntrack_ipv4_compat_init(void)
 {
        struct proc_dir_entry *proc, *proc_exp, *proc_stat;
 
-       proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops);
+       proc = proc_net_fops_create(&init_net, "ip_conntrack", 0440, &ct_file_ops);
        if (!proc)
                goto err1;
 
-       proc_exp = proc_net_fops_create("ip_conntrack_expect", 0440,
+       proc_exp = proc_net_fops_create(&init_net, "ip_conntrack_expect", 0440,
                                        &ip_exp_file_ops);
        if (!proc_exp)
                goto err2;
 
-       proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, proc_net_stat);
+       proc_stat = create_proc_entry("ip_conntrack", S_IRUGO, init_net.proc_net_stat);
        if (!proc_stat)
                goto err3;
 
@@ -397,16 +405,16 @@ int __init nf_conntrack_ipv4_compat_init(void)
        return 0;
 
 err3:
-       proc_net_remove("ip_conntrack_expect");
+       proc_net_remove(&init_net, "ip_conntrack_expect");
 err2:
-       proc_net_remove("ip_conntrack");
+       proc_net_remove(&init_net, "ip_conntrack");
 err1:
        return -ENOMEM;
 }
 
 void __exit nf_conntrack_ipv4_compat_fini(void)
 {
-       remove_proc_entry("ip_conntrack", proc_net_stat);
-       proc_net_remove("ip_conntrack_expect");
-       proc_net_remove("ip_conntrack");
+       remove_proc_entry("ip_conntrack", init_net.proc_net_stat);
+       proc_net_remove(&init_net, "ip_conntrack_expect");
+       proc_net_remove(&init_net, "ip_conntrack");
 }