| 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 |
| |