diff options
-rw-r--r-- | net/netfilter/nf_queue.c | 52 |
1 files changed, 29 insertions, 23 deletions
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index f402894d7e72..823fbf404566 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c | |||
@@ -17,7 +17,7 @@ | |||
17 | */ | 17 | */ |
18 | static struct nf_queue_handler *queue_handler[NPROTO]; | 18 | static struct nf_queue_handler *queue_handler[NPROTO]; |
19 | 19 | ||
20 | static DEFINE_RWLOCK(queue_handler_lock); | 20 | static DEFINE_MUTEX(queue_handler_mutex); |
21 | 21 | ||
22 | /* return EBUSY when somebody else is registered, return EEXIST if the | 22 | /* return EBUSY when somebody else is registered, return EEXIST if the |
23 | * same handler is registered, return 0 in case of success. */ | 23 | * same handler is registered, return 0 in case of success. */ |
@@ -28,16 +28,16 @@ int nf_register_queue_handler(int pf, struct nf_queue_handler *qh) | |||
28 | if (pf >= NPROTO) | 28 | if (pf >= NPROTO) |
29 | return -EINVAL; | 29 | return -EINVAL; |
30 | 30 | ||
31 | write_lock_bh(&queue_handler_lock); | 31 | mutex_lock(&queue_handler_mutex); |
32 | if (queue_handler[pf] == qh) | 32 | if (queue_handler[pf] == qh) |
33 | ret = -EEXIST; | 33 | ret = -EEXIST; |
34 | else if (queue_handler[pf]) | 34 | else if (queue_handler[pf]) |
35 | ret = -EBUSY; | 35 | ret = -EBUSY; |
36 | else { | 36 | else { |
37 | queue_handler[pf] = qh; | 37 | rcu_assign_pointer(queue_handler[pf], qh); |
38 | ret = 0; | 38 | ret = 0; |
39 | } | 39 | } |
40 | write_unlock_bh(&queue_handler_lock); | 40 | mutex_unlock(&queue_handler_mutex); |
41 | 41 | ||
42 | return ret; | 42 | return ret; |
43 | } | 43 | } |
@@ -49,14 +49,16 @@ int nf_unregister_queue_handler(int pf, struct nf_queue_handler *qh) | |||
49 | if (pf >= NPROTO) | 49 | if (pf >= NPROTO) |
50 | return -EINVAL; | 50 | return -EINVAL; |
51 | 51 | ||
52 | write_lock_bh(&queue_handler_lock); | 52 | mutex_lock(&queue_handler_mutex); |
53 | if (queue_handler[pf] != qh) { | 53 | if (queue_handler[pf] != qh) { |
54 | write_unlock_bh(&queue_handler_lock); | 54 | mutex_unlock(&queue_handler_mutex); |
55 | return -EINVAL; | 55 | return -EINVAL; |
56 | } | 56 | } |
57 | 57 | ||
58 | queue_handler[pf] = NULL; | 58 | rcu_assign_pointer(queue_handler[pf], NULL); |
59 | write_unlock_bh(&queue_handler_lock); | 59 | mutex_unlock(&queue_handler_mutex); |
60 | |||
61 | synchronize_rcu(); | ||
60 | 62 | ||
61 | return 0; | 63 | return 0; |
62 | } | 64 | } |
@@ -66,12 +68,14 @@ void nf_unregister_queue_handlers(struct nf_queue_handler *qh) | |||
66 | { | 68 | { |
67 | int pf; | 69 | int pf; |
68 | 70 | ||
69 | write_lock_bh(&queue_handler_lock); | 71 | mutex_lock(&queue_handler_mutex); |
70 | for (pf = 0; pf < NPROTO; pf++) { | 72 | for (pf = 0; pf < NPROTO; pf++) { |
71 | if (queue_handler[pf] == qh) | 73 | if (queue_handler[pf] == qh) |
72 | queue_handler[pf] = NULL; | 74 | rcu_assign_pointer(queue_handler[pf], NULL); |
73 | } | 75 | } |
74 | write_unlock_bh(&queue_handler_lock); | 76 | mutex_unlock(&queue_handler_mutex); |
77 | |||
78 | synchronize_rcu(); | ||
75 | } | 79 | } |
76 | EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers); | 80 | EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers); |
77 | 81 | ||
@@ -94,18 +98,21 @@ static int __nf_queue(struct sk_buff *skb, | |||
94 | struct net_device *physoutdev = NULL; | 98 | struct net_device *physoutdev = NULL; |
95 | #endif | 99 | #endif |
96 | struct nf_afinfo *afinfo; | 100 | struct nf_afinfo *afinfo; |
101 | struct nf_queue_handler *qh; | ||
97 | 102 | ||
98 | /* QUEUE == DROP if noone is waiting, to be safe. */ | 103 | /* QUEUE == DROP if noone is waiting, to be safe. */ |
99 | read_lock(&queue_handler_lock); | 104 | rcu_read_lock(); |
100 | if (!queue_handler[pf]) { | 105 | |
101 | read_unlock(&queue_handler_lock); | 106 | qh = rcu_dereference(queue_handler[pf]); |
107 | if (!qh) { | ||
108 | rcu_read_unlock(); | ||
102 | kfree_skb(skb); | 109 | kfree_skb(skb); |
103 | return 1; | 110 | return 1; |
104 | } | 111 | } |
105 | 112 | ||
106 | afinfo = nf_get_afinfo(pf); | 113 | afinfo = nf_get_afinfo(pf); |
107 | if (!afinfo) { | 114 | if (!afinfo) { |
108 | read_unlock(&queue_handler_lock); | 115 | rcu_read_unlock(); |
109 | kfree_skb(skb); | 116 | kfree_skb(skb); |
110 | return 1; | 117 | return 1; |
111 | } | 118 | } |
@@ -115,7 +122,7 @@ static int __nf_queue(struct sk_buff *skb, | |||
115 | if (net_ratelimit()) | 122 | if (net_ratelimit()) |
116 | printk(KERN_ERR "OOM queueing packet %p\n", | 123 | printk(KERN_ERR "OOM queueing packet %p\n", |
117 | skb); | 124 | skb); |
118 | read_unlock(&queue_handler_lock); | 125 | rcu_read_unlock(); |
119 | kfree_skb(skb); | 126 | kfree_skb(skb); |
120 | return 1; | 127 | return 1; |
121 | } | 128 | } |
@@ -125,7 +132,7 @@ static int __nf_queue(struct sk_buff *skb, | |||
125 | 132 | ||
126 | /* If it's going away, ignore hook. */ | 133 | /* If it's going away, ignore hook. */ |
127 | if (!try_module_get(info->elem->owner)) { | 134 | if (!try_module_get(info->elem->owner)) { |
128 | read_unlock(&queue_handler_lock); | 135 | rcu_read_unlock(); |
129 | kfree(info); | 136 | kfree(info); |
130 | return 0; | 137 | return 0; |
131 | } | 138 | } |
@@ -143,10 +150,9 @@ static int __nf_queue(struct sk_buff *skb, | |||
143 | } | 150 | } |
144 | #endif | 151 | #endif |
145 | afinfo->saveroute(skb, info); | 152 | afinfo->saveroute(skb, info); |
146 | status = queue_handler[pf]->outfn(skb, info, queuenum, | 153 | status = qh->outfn(skb, info, queuenum, qh->data); |
147 | queue_handler[pf]->data); | ||
148 | 154 | ||
149 | read_unlock(&queue_handler_lock); | 155 | rcu_read_unlock(); |
150 | 156 | ||
151 | if (status < 0) { | 157 | if (status < 0) { |
152 | /* James M doesn't say fuck enough. */ | 158 | /* James M doesn't say fuck enough. */ |
@@ -313,13 +319,13 @@ static int seq_show(struct seq_file *s, void *v) | |||
313 | loff_t *pos = v; | 319 | loff_t *pos = v; |
314 | struct nf_queue_handler *qh; | 320 | struct nf_queue_handler *qh; |
315 | 321 | ||
316 | read_lock_bh(&queue_handler_lock); | 322 | rcu_read_lock(); |
317 | qh = queue_handler[*pos]; | 323 | qh = rcu_dereference(queue_handler[*pos]); |
318 | if (!qh) | 324 | if (!qh) |
319 | ret = seq_printf(s, "%2lld NONE\n", *pos); | 325 | ret = seq_printf(s, "%2lld NONE\n", *pos); |
320 | else | 326 | else |
321 | ret = seq_printf(s, "%2lld %s\n", *pos, qh->name); | 327 | ret = seq_printf(s, "%2lld %s\n", *pos, qh->name); |
322 | read_unlock_bh(&queue_handler_lock); | 328 | rcu_read_unlock(); |
323 | 329 | ||
324 | return ret; | 330 | return ret; |
325 | } | 331 | } |