blob: 3d44dcecebce69c53abe52e5a821d8e701da96c5 [file] [log] [blame]
From da72b214fd92086186c95fa11bc35ffd36a14044 Mon Sep 17 00:00:00 2001
From: Thomas Gleixner <tglx@linutronix.de>
Date: Sun, 4 Oct 2009 16:58:07 +0200
Subject: [PATCH] net: Fix netfilter percpu assumptions for real
commit 00ef66ebb37437ca205d06224e7b956d205f7886 in tip.
commit 21ece08 (net: fix the xtables smp_processor_id assumptions for
-rt) fixed only half of the problem. The filter functions might run in
thread context and can be preempted and migrated on -RT.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index 5c588ca..dcc03d7 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -482,22 +482,35 @@ DECLARE_PER_CPU(struct xt_info_lock, xt_info_locks);
* _Only_ that special combination of being per-cpu and never getting
* re-entered asynchronously means that the count is safe.
*/
-static inline void xt_info_rdlock_bh(void)
+static inline int xt_info_rdlock_bh(void)
{
struct xt_info_lock *lock;
+ int cpu;
local_bh_disable();
- lock = &__raw_get_cpu_var(xt_info_locks);
- if (likely(!lock->readers++))
+ preempt_disable_rt();
+ cpu = smp_processor_id();
+ lock = &per_cpu(xt_info_locks, cpu);
+ if (likely(!lock->readers++)) {
+ preempt_enable_rt();
spin_lock(&lock->lock);
+ } else
+ preempt_enable_rt();
+ return cpu;
}
-static inline void xt_info_rdunlock_bh(void)
+static inline void xt_info_rdunlock_bh(int cpu)
{
- struct xt_info_lock *lock = &__raw_get_cpu_var(xt_info_locks);
+ struct xt_info_lock *lock = &per_cpu(xt_info_locks, cpu);
- if (likely(!--lock->readers))
+ preempt_disable_rt();
+
+ if (likely(!--lock->readers)) {
+ preempt_enable_rt();
spin_unlock(&lock->lock);
+ } else
+ preempt_enable_rt();
+
local_bh_enable();
}
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 9438d18..4afb6c8 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -266,6 +266,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
void *table_base;
const struct xt_table_info *private;
struct xt_target_param tgpar;
+ int cpu;
if (!pskb_may_pull(skb, arp_hdr_len(skb->dev)))
return NF_DROP;
@@ -273,9 +274,9 @@ unsigned int arpt_do_table(struct sk_buff *skb,
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
- xt_info_rdlock_bh();
+ cpu = xt_info_rdlock_bh();
private = table->private;
- table_base = private->entries[raw_smp_processor_id()];
+ table_base = private->entries[cpu];
e = get_entry(table_base, private->hook_entry[hook]);
back = get_entry(table_base, private->underflow[hook]);
@@ -346,7 +347,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
/* Verdict */
break;
} while (!hotdrop);
- xt_info_rdunlock_bh();
+ xt_info_rdunlock_bh(cpu);
if (hotdrop)
return NF_DROP;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 83501d8..1c33431 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -335,6 +335,7 @@ ipt_do_table(struct sk_buff *skb,
const struct xt_table_info *private;
struct xt_match_param mtpar;
struct xt_target_param tgpar;
+ int cpu;
/* Initialization */
ip = ip_hdr(skb);
@@ -355,9 +356,9 @@ ipt_do_table(struct sk_buff *skb,
mtpar.hooknum = tgpar.hooknum = hook;
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
- xt_info_rdlock_bh();
+ cpu = xt_info_rdlock_bh();
private = table->private;
- table_base = private->entries[raw_smp_processor_id()];
+ table_base = private->entries[cpu];
e = get_entry(table_base, private->hook_entry[hook]);
@@ -447,7 +448,7 @@ ipt_do_table(struct sk_buff *skb,
/* Verdict */
break;
} while (!hotdrop);
- xt_info_rdunlock_bh();
+ xt_info_rdunlock_bh(cpu);
#ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT;
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 66c78d0..6d5b78b 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -364,6 +364,7 @@ ip6t_do_table(struct sk_buff *skb,
const struct xt_table_info *private;
struct xt_match_param mtpar;
struct xt_target_param tgpar;
+ int cpu;
/* Initialization */
indev = in ? in->name : nulldevname;
@@ -382,9 +383,9 @@ ip6t_do_table(struct sk_buff *skb,
IP_NF_ASSERT(table->valid_hooks & (1 << hook));
- xt_info_rdlock_bh();
+ cpu = xt_info_rdlock_bh();
private = table->private;
- table_base = private->entries[raw_smp_processor_id()];
+ table_base = private->entries[cpu];
e = get_entry(table_base, private->hook_entry[hook]);
@@ -478,7 +479,7 @@ ip6t_do_table(struct sk_buff *skb,
#ifdef CONFIG_NETFILTER_DEBUG
tb_comefrom = NETFILTER_LINK_POISON;
#endif
- xt_info_rdunlock_bh();
+ xt_info_rdunlock_bh(cpu);
#ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT;
--
1.7.1.1