|
@@ -253,9 +253,9 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
|
|
indev = in ? in->name : nulldevname;
|
|
|
outdev = out ? out->name : nulldevname;
|
|
|
|
|
|
- rcu_read_lock_bh();
|
|
|
- private = rcu_dereference(table->private);
|
|
|
- table_base = rcu_dereference(private->entries[smp_processor_id()]);
|
|
|
+ xt_info_rdlock_bh();
|
|
|
+ private = table->private;
|
|
|
+ table_base = private->entries[smp_processor_id()];
|
|
|
|
|
|
e = get_entry(table_base, private->hook_entry[hook]);
|
|
|
back = get_entry(table_base, private->underflow[hook]);
|
|
@@ -273,6 +273,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
|
|
|
|
|
hdr_len = sizeof(*arp) + (2 * sizeof(struct in_addr)) +
|
|
|
(2 * skb->dev->addr_len);
|
|
|
+
|
|
|
ADD_COUNTER(e->counters, hdr_len, 1);
|
|
|
|
|
|
t = arpt_get_target(e);
|
|
@@ -328,8 +329,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
|
|
e = (void *)e + e->next_offset;
|
|
|
}
|
|
|
} while (!hotdrop);
|
|
|
-
|
|
|
- rcu_read_unlock_bh();
|
|
|
+ xt_info_rdunlock_bh();
|
|
|
|
|
|
if (hotdrop)
|
|
|
return NF_DROP;
|
|
@@ -711,9 +711,12 @@ static void get_counters(const struct xt_table_info *t,
|
|
|
/* Instead of clearing (by a previous call to memset())
|
|
|
* the counters and using adds, we set the counters
|
|
|
* with data used by 'current' CPU
|
|
|
- * We dont care about preemption here.
|
|
|
+ *
|
|
|
+ * Bottom half has to be disabled to prevent deadlock
|
|
|
+ * if new softirq were to run and call ipt_do_table
|
|
|
*/
|
|
|
- curcpu = raw_smp_processor_id();
|
|
|
+ local_bh_disable();
|
|
|
+ curcpu = smp_processor_id();
|
|
|
|
|
|
i = 0;
|
|
|
ARPT_ENTRY_ITERATE(t->entries[curcpu],
|
|
@@ -726,73 +729,22 @@ static void get_counters(const struct xt_table_info *t,
|
|
|
if (cpu == curcpu)
|
|
|
continue;
|
|
|
i = 0;
|
|
|
+ xt_info_wrlock(cpu);
|
|
|
ARPT_ENTRY_ITERATE(t->entries[cpu],
|
|
|
t->size,
|
|
|
add_entry_to_counter,
|
|
|
counters,
|
|
|
&i);
|
|
|
+ xt_info_wrunlock(cpu);
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* We're lazy, and add to the first CPU; overflow works its fey magic
|
|
|
- * and everything is OK. */
|
|
|
-static int
|
|
|
-add_counter_to_entry(struct arpt_entry *e,
|
|
|
- const struct xt_counters addme[],
|
|
|
- unsigned int *i)
|
|
|
-{
|
|
|
- ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
|
|
|
-
|
|
|
- (*i)++;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Take values from counters and add them back onto the current cpu */
|
|
|
-static void put_counters(struct xt_table_info *t,
|
|
|
- const struct xt_counters counters[])
|
|
|
-{
|
|
|
- unsigned int i, cpu;
|
|
|
-
|
|
|
- local_bh_disable();
|
|
|
- cpu = smp_processor_id();
|
|
|
- i = 0;
|
|
|
- ARPT_ENTRY_ITERATE(t->entries[cpu],
|
|
|
- t->size,
|
|
|
- add_counter_to_entry,
|
|
|
- counters,
|
|
|
- &i);
|
|
|
local_bh_enable();
|
|
|
}
|
|
|
|
|
|
-static inline int
|
|
|
-zero_entry_counter(struct arpt_entry *e, void *arg)
|
|
|
-{
|
|
|
- e->counters.bcnt = 0;
|
|
|
- e->counters.pcnt = 0;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info)
|
|
|
-{
|
|
|
- unsigned int cpu;
|
|
|
- const void *loc_cpu_entry = info->entries[raw_smp_processor_id()];
|
|
|
-
|
|
|
- memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
|
|
|
- for_each_possible_cpu(cpu) {
|
|
|
- memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size);
|
|
|
- ARPT_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size,
|
|
|
- zero_entry_counter, NULL);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static struct xt_counters *alloc_counters(struct xt_table *table)
|
|
|
{
|
|
|
unsigned int countersize;
|
|
|
struct xt_counters *counters;
|
|
|
struct xt_table_info *private = table->private;
|
|
|
- struct xt_table_info *info;
|
|
|
|
|
|
/* We need atomic snapshot of counters: rest doesn't change
|
|
|
* (other than comefrom, which userspace doesn't care
|
|
@@ -802,30 +754,11 @@ static struct xt_counters *alloc_counters(struct xt_table *table)
|
|
|
counters = vmalloc_node(countersize, numa_node_id());
|
|
|
|
|
|
if (counters == NULL)
|
|
|
- goto nomem;
|
|
|
-
|
|
|
- info = xt_alloc_table_info(private->size);
|
|
|
- if (!info)
|
|
|
- goto free_counters;
|
|
|
-
|
|
|
- clone_counters(info, private);
|
|
|
-
|
|
|
- mutex_lock(&table->lock);
|
|
|
- xt_table_entry_swap_rcu(private, info);
|
|
|
- synchronize_net(); /* Wait until smoke has cleared */
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- get_counters(info, counters);
|
|
|
- put_counters(private, counters);
|
|
|
- mutex_unlock(&table->lock);
|
|
|
-
|
|
|
- xt_free_table_info(info);
|
|
|
+ get_counters(private, counters);
|
|
|
|
|
|
return counters;
|
|
|
-
|
|
|
- free_counters:
|
|
|
- vfree(counters);
|
|
|
- nomem:
|
|
|
- return ERR_PTR(-ENOMEM);
|
|
|
}
|
|
|
|
|
|
static int copy_entries_to_user(unsigned int total_size,
|
|
@@ -1094,8 +1027,9 @@ static int __do_replace(struct net *net, const char *name,
|
|
|
(newinfo->number <= oldinfo->initial_entries))
|
|
|
module_put(t->me);
|
|
|
|
|
|
- /* Get the old counters. */
|
|
|
+ /* Get the old counters, and synchronize with replace */
|
|
|
get_counters(oldinfo, counters);
|
|
|
+
|
|
|
/* Decrease module usage counts and free resource */
|
|
|
loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
|
|
|
ARPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
|
|
@@ -1165,10 +1099,23 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/* We're lazy, and add to the first CPU; overflow works its fey magic
|
|
|
+ * and everything is OK. */
|
|
|
+static int
|
|
|
+add_counter_to_entry(struct arpt_entry *e,
|
|
|
+ const struct xt_counters addme[],
|
|
|
+ unsigned int *i)
|
|
|
+{
|
|
|
+ ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
|
|
|
+
|
|
|
+ (*i)++;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int do_add_counters(struct net *net, void __user *user, unsigned int len,
|
|
|
int compat)
|
|
|
{
|
|
|
- unsigned int i;
|
|
|
+ unsigned int i, curcpu;
|
|
|
struct xt_counters_info tmp;
|
|
|
struct xt_counters *paddc;
|
|
|
unsigned int num_counters;
|
|
@@ -1224,26 +1171,26 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len,
|
|
|
goto free;
|
|
|
}
|
|
|
|
|
|
- mutex_lock(&t->lock);
|
|
|
+ local_bh_disable();
|
|
|
private = t->private;
|
|
|
if (private->number != num_counters) {
|
|
|
ret = -EINVAL;
|
|
|
goto unlock_up_free;
|
|
|
}
|
|
|
|
|
|
- preempt_disable();
|
|
|
i = 0;
|
|
|
/* Choose the copy that is on our node */
|
|
|
- loc_cpu_entry = private->entries[smp_processor_id()];
|
|
|
+ curcpu = smp_processor_id();
|
|
|
+ loc_cpu_entry = private->entries[curcpu];
|
|
|
+ xt_info_wrlock(curcpu);
|
|
|
ARPT_ENTRY_ITERATE(loc_cpu_entry,
|
|
|
private->size,
|
|
|
add_counter_to_entry,
|
|
|
paddc,
|
|
|
&i);
|
|
|
- preempt_enable();
|
|
|
+ xt_info_wrunlock(curcpu);
|
|
|
unlock_up_free:
|
|
|
- mutex_unlock(&t->lock);
|
|
|
-
|
|
|
+ local_bh_enable();
|
|
|
xt_table_unlock(t);
|
|
|
module_put(t->me);
|
|
|
free:
|