netfilter: revised locking for x_tables
[linux-2.6.git] / net / netfilter / x_tables.c
index 509a956..150e5cf 100644 (file)
@@ -625,20 +625,6 @@ void xt_free_table_info(struct xt_table_info *info)
 }
 EXPORT_SYMBOL(xt_free_table_info);
 
-void xt_table_entry_swap_rcu(struct xt_table_info *oldinfo,
-                            struct xt_table_info *newinfo)
-{
-       unsigned int cpu;
-
-       for_each_possible_cpu(cpu) {
-               void *p = oldinfo->entries[cpu];
-               rcu_assign_pointer(oldinfo->entries[cpu], newinfo->entries[cpu]);
-               newinfo->entries[cpu] = p;
-       }
-
-}
-EXPORT_SYMBOL_GPL(xt_table_entry_swap_rcu);
-
 /* Find table by name, grabs mutex & ref.  Returns ERR_PTR() on error. */
 struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
                                    const char *name)
@@ -676,32 +662,43 @@ void xt_compat_unlock(u_int8_t af)
 EXPORT_SYMBOL_GPL(xt_compat_unlock);
 #endif
 
+DEFINE_PER_CPU(struct xt_info_lock, xt_info_locks);
+EXPORT_PER_CPU_SYMBOL_GPL(xt_info_locks);
+
+
 struct xt_table_info *
 xt_replace_table(struct xt_table *table,
              unsigned int num_counters,
              struct xt_table_info *newinfo,
              int *error)
 {
-       struct xt_table_info *oldinfo, *private;
+       struct xt_table_info *private;
 
        /* Do the substitution. */
-       mutex_lock(&table->lock);
+       local_bh_disable();
        private = table->private;
+
        /* Check inside lock: is the old number correct? */
        if (num_counters != private->number) {
                duprintf("num_counters != table->private->number (%u/%u)\n",
                         num_counters, private->number);
-               mutex_unlock(&table->lock);
+               local_bh_enable();
                *error = -EAGAIN;
                return NULL;
        }
-       oldinfo = private;
-       rcu_assign_pointer(table->private, newinfo);
-       newinfo->initial_entries = oldinfo->initial_entries;
-       mutex_unlock(&table->lock);
 
-       synchronize_net();
-       return oldinfo;
+       table->private = newinfo;
+       newinfo->initial_entries = private->initial_entries;
+
+       /*
+        * Even though table entries have now been swapped, other CPU's
+        * may still be using the old entries. This is okay, because
+        * resynchronization happens because of the locking done
+        * during the get_counters() routine.
+        */
+       local_bh_enable();
+
+       return private;
 }
 EXPORT_SYMBOL_GPL(xt_replace_table);
 
@@ -734,7 +731,6 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table *table,
 
        /* Simplifies replace_table code. */
        table->private = bootstrap;
-       mutex_init(&table->lock);
 
        if (!xt_replace_table(table, 0, newinfo, &ret))
                goto unlock;
@@ -1147,7 +1143,14 @@ static struct pernet_operations xt_net_ops = {
 
 static int __init xt_init(void)
 {
-       int i, rv;
+       unsigned int i;
+       int rv;
+
+       for_each_possible_cpu(i) {
+               struct xt_info_lock *lock = &per_cpu(xt_info_locks, i);
+               spin_lock_init(&lock->lock);
+               lock->readers = 0;
+       }
 
        xt = kmalloc(sizeof(struct xt_af) * NFPROTO_NUMPROTO, GFP_KERNEL);
        if (!xt)