diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-07-18 10:08:07 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-07-18 10:08:07 -0400 |
commit | 6b75e3e8d664a9a1b99d31a7f4976ae70d1d090a (patch) | |
tree | 940d95c340df22db092062bdaea599e9afc4fcf9 /net | |
parent | 131ad62d8fc06d9d0a5c61d9526876352c2f2bbd (diff) |
netfilter: nfnetlink: add RCU in nfnetlink_rcv_msg()
Goal of this patch is to permit nfnetlink providers not mandate
nfnl_mutex being held while nfnetlink_rcv_msg() calls them.
If struct nfnl_callback contains a non NULL call_rcu(), then
nfnetlink_rcv_msg() will use it instead of call() field, holding
rcu_read_lock instead of nfnl_mutex
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Florian Westphal <fw@strlen.de>
CC: Eric Leblond <eric@regit.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/nfnetlink.c | 40 |
1 files changed, 30 insertions, 10 deletions
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index b4a4532823e8..1905976b5135 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c | |||
@@ -37,7 +37,7 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER); | |||
37 | 37 | ||
38 | static char __initdata nfversion[] = "0.30"; | 38 | static char __initdata nfversion[] = "0.30"; |
39 | 39 | ||
40 | static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT]; | 40 | static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT]; |
41 | static DEFINE_MUTEX(nfnl_mutex); | 41 | static DEFINE_MUTEX(nfnl_mutex); |
42 | 42 | ||
43 | void nfnl_lock(void) | 43 | void nfnl_lock(void) |
@@ -59,7 +59,7 @@ int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) | |||
59 | nfnl_unlock(); | 59 | nfnl_unlock(); |
60 | return -EBUSY; | 60 | return -EBUSY; |
61 | } | 61 | } |
62 | subsys_table[n->subsys_id] = n; | 62 | rcu_assign_pointer(subsys_table[n->subsys_id], n); |
63 | nfnl_unlock(); | 63 | nfnl_unlock(); |
64 | 64 | ||
65 | return 0; | 65 | return 0; |
@@ -71,7 +71,7 @@ int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) | |||
71 | nfnl_lock(); | 71 | nfnl_lock(); |
72 | subsys_table[n->subsys_id] = NULL; | 72 | subsys_table[n->subsys_id] = NULL; |
73 | nfnl_unlock(); | 73 | nfnl_unlock(); |
74 | 74 | synchronize_rcu(); | |
75 | return 0; | 75 | return 0; |
76 | } | 76 | } |
77 | EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister); | 77 | EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister); |
@@ -83,7 +83,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t | |||
83 | if (subsys_id >= NFNL_SUBSYS_COUNT) | 83 | if (subsys_id >= NFNL_SUBSYS_COUNT) |
84 | return NULL; | 84 | return NULL; |
85 | 85 | ||
86 | return subsys_table[subsys_id]; | 86 | return rcu_dereference(subsys_table[subsys_id]); |
87 | } | 87 | } |
88 | 88 | ||
89 | static inline const struct nfnl_callback * | 89 | static inline const struct nfnl_callback * |
@@ -139,21 +139,27 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
139 | 139 | ||
140 | type = nlh->nlmsg_type; | 140 | type = nlh->nlmsg_type; |
141 | replay: | 141 | replay: |
142 | rcu_read_lock(); | ||
142 | ss = nfnetlink_get_subsys(type); | 143 | ss = nfnetlink_get_subsys(type); |
143 | if (!ss) { | 144 | if (!ss) { |
144 | #ifdef CONFIG_MODULES | 145 | #ifdef CONFIG_MODULES |
145 | nfnl_unlock(); | 146 | rcu_read_unlock(); |
146 | request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); | 147 | request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); |
147 | nfnl_lock(); | 148 | rcu_read_lock(); |
148 | ss = nfnetlink_get_subsys(type); | 149 | ss = nfnetlink_get_subsys(type); |
149 | if (!ss) | 150 | if (!ss) |
150 | #endif | 151 | #endif |
152 | { | ||
153 | rcu_read_unlock(); | ||
151 | return -EINVAL; | 154 | return -EINVAL; |
155 | } | ||
152 | } | 156 | } |
153 | 157 | ||
154 | nc = nfnetlink_find_client(type, ss); | 158 | nc = nfnetlink_find_client(type, ss); |
155 | if (!nc) | 159 | if (!nc) { |
160 | rcu_read_unlock(); | ||
156 | return -EINVAL; | 161 | return -EINVAL; |
162 | } | ||
157 | 163 | ||
158 | { | 164 | { |
159 | int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); | 165 | int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); |
@@ -167,7 +173,23 @@ replay: | |||
167 | if (err < 0) | 173 | if (err < 0) |
168 | return err; | 174 | return err; |
169 | 175 | ||
170 | err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda); | 176 | if (nc->call_rcu) { |
177 | err = nc->call_rcu(net->nfnl, skb, nlh, | ||
178 | (const struct nlattr **)cda); | ||
179 | rcu_read_unlock(); | ||
180 | } else { | ||
181 | rcu_read_unlock(); | ||
182 | nfnl_lock(); | ||
183 | if (rcu_dereference_protected( | ||
184 | subsys_table[NFNL_SUBSYS_ID(type)], | ||
185 | lockdep_is_held(&nfnl_mutex)) != ss || | ||
186 | nfnetlink_find_client(type, ss) != nc) | ||
187 | err = -EAGAIN; | ||
188 | else | ||
189 | err = nc->call(net->nfnl, skb, nlh, | ||
190 | (const struct nlattr **)cda); | ||
191 | nfnl_unlock(); | ||
192 | } | ||
171 | if (err == -EAGAIN) | 193 | if (err == -EAGAIN) |
172 | goto replay; | 194 | goto replay; |
173 | return err; | 195 | return err; |
@@ -176,9 +198,7 @@ replay: | |||
176 | 198 | ||
177 | static void nfnetlink_rcv(struct sk_buff *skb) | 199 | static void nfnetlink_rcv(struct sk_buff *skb) |
178 | { | 200 | { |
179 | nfnl_lock(); | ||
180 | netlink_rcv_skb(skb, &nfnetlink_rcv_msg); | 201 | netlink_rcv_skb(skb, &nfnetlink_rcv_msg); |
181 | nfnl_unlock(); | ||
182 | } | 202 | } |
183 | 203 | ||
184 | static int __net_init nfnetlink_net_init(struct net *net) | 204 | static int __net_init nfnetlink_net_init(struct net *net) |